-
Notifications
You must be signed in to change notification settings - Fork 16
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
Prevent out-of-order arrival of audio node settings #311
Conversation
This comment was marked as outdated.
This comment was marked as outdated.
src/context/mod.rs
Outdated
/// | ||
/// The message will be handled by | ||
/// [`AudioProcessor::onmessage`](crate::render::AudioProcessor::onmessage). | ||
pub fn post_message(&self, msg: Box<dyn std::any::Any + Send + 'static>) { |
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.
A de-allocation will happen every time this method is called (we're consuming the Box by value). I'm not sure how we could prevent it - except for shipping the box back to the control thread when we are done with it..
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.
I implemented the shipping back pattern for Moire based on llq
: https://codeberg.org/moire/moire/src/branch/main/crates/ctrl/src/circular_fifo.rs. Not sure if this is suitable here.
The other option would be to hand over handled messages to basedrop
from the real-time thread.
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.
It makes me think we could return the renderers back to the control thread as well when they are removed from the graph?
Let me know if I'm wrong or badly understand something, but then the memory would be cleared within the control thread and it would also open the door for implementing some pools of renderers?
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.
This is tricky. The renderer may have stored/cached AudioRenderQuanta (e.g. DelayRenderer) which are not Send
so are not allowed to be moved outside of the render thread.
A generic system to decommision AudioProcessors and control messages without touching the global allocator is something worth to explore though.
I will create a separate issue, also taking the recommendations of @uklotzde into account
Actually, regarding the spec, this could be explicitly modelled according to the AudioWorklet MessagePort interface, isn't it? related specs: |
This comment was marked as outdated.
This comment was marked as outdated.
This comment was marked as outdated.
This comment was marked as outdated.
This comment was marked as outdated.
This comment was marked as outdated.
Hah, I had not realized it was actually this close. The only difference is that the spec allows for bi-directional comms. |
Otherwise, AudioParam automation events will be routed to a non-yet existing processor
This comment was marked as outdated.
This comment was marked as outdated.
This comment was marked as outdated.
This comment was marked as outdated.
This comment was marked as outdated.
This comment was marked as outdated.
@b-ma @uklotzde Apologies for the large PR. But this should lay the groundwork to prevent out of order processing of changes to the audio graph. Fixes #311 Nodes to rewrite still (in a new PR):
For further consideration
|
This comment was marked as outdated.
This comment was marked as outdated.
src/control.rs
Outdated
pub fn get_start_at(&self) -> f64 { | ||
self.inner.start.load(Ordering::Acquire) | ||
impl Controller { | ||
pub fn shared(&self) -> Arc<ControllerShared> { |
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.
Only return a reference in such situations. The caller knows better if and when to clone it. The caller might even decide to obtain only a weak reference.
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.
Thanks, I have changed it to return &Arc
/// [`AudioContextRegistration::post_message`](crate::context::AudioContextRegistration::post_message). | ||
/// This will not be necessary for most processors. | ||
#[allow(unused_variables)] | ||
fn onmessage(&mut self, msg: Box<dyn Any + Send + 'static>) { |
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.
Why onmessage
instead of on_message
? This looks odd. Either change the name or leave a comment that explains why this naming is the right choice here.
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.
Odd indeed, it's from https://developer.mozilla.org/en-US/docs/Web/API/MessagePort/message_event
I added a comment to clarify this
|
Hey, looks pretty good to me Just one thing I don't really understand: why did you keep the Let me know if I can help fixing the other nodes! |
Good point. It can be inlined into audio_buffer_source.rs. I will create a separate issue for it because I'm already quite tired from this PR :) |
Huhu I get it, I will try to propose something these next days |
This is the direction I am thinking of now, to prevent settings passed via atomics 'overtaking' settings passed by the various message channels. Fixes #297
It adds a new method to the public API of the
AudioProcessor
:fn onmessage(&mut self, msg: Box<dyn std::any::Any + Send + 'static>)
And to the mysterious
AudioContextRegistration
:pub fn post_message(&self, msg: Box<dyn std::any::Any + Send + 'static>)
But the
AudioProcessor::onmessage
fn comes with a default no-op implementation so I think it will not really complicate things. The AudioProcessor interface is not part of the specification anyway (just modeled after AudioWorkletProcessor ) and only advanced users will encounter it.I have rewritten the AudioParam and OscillatorNode to use it so far. All other nodes still need to be ported. In the end, none of the nodes should have their own crossbeam sender/receiver.
Besides fixing the arrival order, I also expect a modest performance gain because nodes do not need to poll for changed settings on each render quantum. Instead the render thread pushes them on demand.
Some tests are still failing. I need to rethink the way we write the AudioParam test suite. And there are some subtle issues with the
AudioListener
which is only loaded lazily which confuses the AudioParam events we ship.