rodio: Cannot restart stopped sink
Hi there! Thank you for this awesome library. While exploring the API I stumbled over the following behavior, which I believe is a bug:
A Sink no longer plays newly appended sounds after .stop() has been called on the Sink.
Looking at the sources, it seems that .stop() sets Controls.stopped to true, but I could not find anything that would set it back to false other than constructing a new Sink.
About this issue
- Original URL
- State: open
- Created 6 years ago
- Reactions: 4
- Comments: 15 (3 by maintainers)
Commits related to this issue
- Broken attempt at clearing sink This'll never work because sink is apparently not intended to be used: https://github.com/RustAudio/rodio/issues/171 Looks like sink will need reimplementing in korama... — committed to geokala/korama by deleted user 3 years ago
- Proof of Concept. it actually fucking works. it's 5 am please send help. really don't care for Rodio though... https://github.com/RustAudio/rodio/issues/171 this was opened >3 years ago and they STI... — committed to Beinsezii/ompl by Beinsezii 3 years ago
- Fix Sink playback after stop (#464) Addressing issues #462 and #315 and #171 Given how the sink's queue is drained on stop I made some modifications in sink.append(). I added a check to ensure the... — committed to RustAudio/rodio by RottenFishbone a year ago
I implore you to revisit that design decision. I think this is needlessly convoluted. Besides, when do you ever want to just call something on an object which makes it unusable but doesn’t drop it?
stop()should work like you would expect and according to the current docs: Clear the queue and reset the sink.Shouldn’t
.stop()actually behave in an intuitive manner if Sink is supposed to “make it easier to get started with rodio”? Clearing the queue of sounds should be basic functionality. And to be honest many apps would not need to look any further than Sink if a.clear()like function was implemented. At least it should be made absolutely clear that.stop()makes a sink useless, even though it is redundant considering.drop()exists…I’m sorry if I sound a bit annoyed (because I am), I just spent a solid hour looking at the docs before coming here. I don’t agree with that design decision, but if we shall stick with it, it should be abundantly clear in the docs how
.stop()works.So can we talk about why the API is designed in such way? It seems counter-intuitive that you get an unusable sink after you call .stop() on it. Am I supposed to create a new sink every time I want to restart playback? If so, why even expose .stop() method, why not just Drop, since the Sink is useless after calling it anyway?
Well okay then. While I can’t say I agree with this, it seems like the most reasonable way to do this. The reason why I think it’s unreasonable is because the Sink struct hides some of the lower level implementation details which are then exposed to the user who wants to reimplement their own version of Sink, just because they need this .clear() functionality.
Additionaly, I would like to ask you to reflect this intention in the docs. I for one tend to take libraries mostly as an opaque object that I shouldn’t mess with if it isn’t really important/broken. This breaks the illusion and I think it’s best to inform the user.
So, I’m trying to work through a solution right now. My idea is to have another
impl Iteratorstruct likeStoppablefor sources, and to make itSkippable. The iterator’snext()will run the inner iterator’snext()method ifskipis false, and consume the entire iterator if it is true. This would make the sink move on to the next source on its list, or stop if it runs out.The sink would have a
skip()method which sets the current iterator’sSkippablestruct field to true, and theclear()method would simplyskip()as many times as there are sources, and thenpause()itself so it outputs zeros.How does this sound so far? I struggle with handling some of the concurrency bits, but this seems like a reasonable approach to take, and would create two new useful methods instead of just the one.
@TheEdward162 These are excellent questions.
Working around the
stop==dropissue in ggez had me do some ugly things with context and default devices, that will probably bite someone in the future. This could have been easily avoided with different stop semantics.I’m wondering now, since
.stop()apparently needs to behave like adrop(), couldn’t we get another function (e.g.sink.clear()) that just clears the queue while leaving the sink operational?I think I’m misunderstanding something obvious. To clarify, if I stop one sound and then
appenda different one I’m not supposed to hear it? In other words I cannot reuse aSink? (I thought the purpose ofSink::stop()was emptying the queue so that other sounds could be played.)Sorry if my questions are annoying. I’m somewhat confused at the moment 😃