-
Notifications
You must be signed in to change notification settings - Fork 275
Draft of an optimized pausing architecture #791
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Conversation
`Source`s in rodio get queried by the `OutputStream`. That means that the stream calls the next method on a `Source`. Only then can the `Source` do something, like call next on a `Source` it wraps. For pausing we want to turn this flow around, we want to affect the `OutputStream` from a `Source` (possibly wrapped by many others). To do so we need to get access to the `OutputStream` in a `Source`. We do so by making the `OutputStream` call `set_pause_handle(<some handle>)` on each `Source` added to it. During that call of `set_pause_handle` each of those sources calls `set_pause_handle` on which ever source the wrap. This goes on all through the `Source`s tree. A `Pausable` source keeps a copy of the pause handle passed to it when `set_pause_handle` got called on it. When it needs to pause the stream it can simply call `pause()` on its stored pause handle. There will be multiple types of pause handles: - One which simply (un)pauses the `cpal::Stream`. It is passed to all the sources through `set_pause_handle` on `OutputStream` creation. - One made for `Mixer`. It is passed to all streams added to the `Mixer`. This wraps whichever pause handle was set on the mixer. It only calls pause on that handle when all streams in the mixer have called pause on it. - Maybe more I did not think off?
Note: another thing to think about. What should a paused source do when next is called while its paused?
I'm leaning towards panic. We need to think about the possibility for race conditions here though. Especially if we start moving the pause handle out of the sources themselves (like a |
Heh, I understand this took you a few hours. Architecture looks extensible and the code is well laid out. As a mental model I'm thinking how much sense it makes that a
I think that latter use case would definitely be something that users would be expecting. Heck, I know that this point about CPU usage came up in a similar context in librespot when the users pause their app and not the source. Also, users may not want auto-pausing, because it may trigger behavior downstream the sink that they don't want. Like a DAC switching off its outputs or dropping signal lock. Have you considered doing it the other way around? Having the output stream (optionally) switching into "paused" mode when its last source was exhausted? Switching it on again when a mixer or queue signals it has appended new inputs? |
|
||
fn set_pause_handle(&mut self, pause_handle: crate::source::PauseHandle) { | ||
// TODO create MixerController in pausable. It will only set paused on this | ||
// pause handle when all sources in the mixer have been paused |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
have been paused or exhausted.
We already have the
Good point, outputs switching off would be annoying. Maybe we could present some options:
I failed to write it out but that is part of what I planned for the pause_handle. I've added a comment to the code skeleton noting that for mixer. |
Source
s in rodio get queried by theOutputStream
. That means that the stream calls the next method on aSource
. Only then can theSource
do something, like call next on aSource
it wraps.For pausing we want to turn this flow around, we want to affect the
OutputStream
from aSource
(possibly wrapped by many others).To do so we need to get access to the
OutputStream
in aSource
. We do so by making theOutputStream
callset_pause_handle(<some handle>)
on eachSource
added to it. During that call ofset_pause_handle
each of those sources callsset_pause_handle
on which ever source the wrap. This goes on all through theSource
s tree.A
Pausable
source keeps a copy of the pause handle passed to it whenset_pause_handle
got called on it. When it needs to pause the stream it can simply callpause()
on its stored pause handle.There will be multiple types of pause handles:
cpal::Stream
. It is passed to all the sources throughset_pause_handle
onOutputStream
creation.Mixer
. It is passed to all streams added to theMixer
. This wraps whichever pause handle was set on the mixer. It only calls pause on that handle when all streams in the mixer have called pause on it.