Skip to content
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

Set BaseAudioContext::state by render thread to signal device readiness #419

Merged
merged 9 commits into from
Jan 4, 2024

Conversation

orottier
Copy link
Owner

@orottier orottier commented Jan 2, 2024

- A context is instantiated in Suspended state
- Online context will change to Running at the first render quantum
- Online context will change to Closed when the render thread is dropped after a `close` call
- Online context suspend/resume state is still managed by the control thread - TODO
- Offline context will change to Running after startRendering
- Offline context will set state to suspended when suspending, running when resumed
- Offline context will set change to closed when rendering has finished

Fixes #410 #416 #101

It probably also fixes ircam-ismm/node-web-audio-api#61 (comment)

One thing that I found that is more a concern on the rust side, is that the first "resume" statechange event is sometimes triggered, sometimes not. I think this some kind of race condition but this is a bit weird from a user perspective.

Todo

  • find a solution for online AudioContext resume/suspend to signal device 'readiness'. Possible solution: first pause audio rendering by sending a ctrl msg to suspend the audio graph. When that message is handled, then emit the state_change event and update the state and suspend the cpal stream. Similar setup for resuming.
  • close_sync on online AudioContext is not truly sync. We don't guarantee the operation has finished before the function returns. Solution: use a channel to communicate if we are done
  • implement oncomplete for offline context - tricky! to be addressed in Emit oncomplete event for OfflineAudioContext #411
  • add async methods for suspend/resume/close on online AudioContext

Fixes #410

Except for suspending/resuming, I don't have a solution for that yet.

In summary:
- A context is instantiated in Suspended state
- Online context will change to Running at the first render quantum
- Online context will change to Closed when the render thread is dropped
  after a `close` call
- Online context suspend/resume state is still managed by the control
  thread - TODO
- Offline context will change to Running after startRendering
@orottier orottier requested a review from b-ma January 2, 2024 19:46
@@ -333,14 +334,14 @@ impl ConcreteBaseAudioContext {
/// Returns state of current context
#[must_use]
pub(super) fn state(&self) -> AudioContextState {
self.inner.state.load(Ordering::SeqCst).into()
self.inner.state.load(Ordering::Acquire).into()
}

/// Updates state of current context
pub(super) fn set_state(&self, state: AudioContextState) {
let current_state = self.state();
if current_state != state {
Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A race condition may occur in concurrent calls to this method (e.g. making a massive amount of suspend calls) causing the statechange event to be sent too many times. This could be fixed with https://doc.rust-lang.org/std/sync/atomic/struct.AtomicU64.html#method.compare_exchange but this method will probably be refactored away once we solve how to make suspend_sync better

@b-ma
Copy link
Collaborator

b-ma commented Jan 3, 2024

Cool! Looks all good to me

This will be very simple to implement #411 from this point, I was unsure of the behaviour of await

@orottier
Copy link
Owner Author

orottier commented Jan 3, 2024

This will be very simple to implement #411 from this point

Ahah, that is what you think (and I thought), but this is especially tricky because we don't spawn an event loop in the case of the OfflineAudioContext. Something to reconsider maybe. In any case we can't use the existing methods to spawn the complete event. I tried but it does not emit the event properly.

@b-ma
Copy link
Collaborator

b-ma commented Jan 3, 2024

Ah damn I didn't realized that...

Something to reconsider maybe.

Indeed, and their will probably be another issue after that I just discovered yesterday... :) As we send the ended event for AudioScheduledSourceNode in the next render block, it might never be triggered in offline contexts if the rendering finishes on this block (there is a wpt test failling and I was thinking that was the actual issue)

@orottier
Copy link
Owner Author

orottier commented Jan 3, 2024

Indeed that will also be an issue. But to be fair, these events simply don't make sense on an OfflineAudioContext because there is not actionable thing you can do with them.
What information does it give you that a AudioBufferSourceNode has ended when the offline context is rendering? You don't have time to modify the audio graph at that point anyway because the rendering will continue while you handle the event...
I think the only sensible (but deprecated) event for OfflineAudioContext is oncomplete. All the others (statechange, ended, processorerror) could be informational but not actionable..

@b-ma
Copy link
Collaborator

b-ma commented Jan 3, 2024

Hehe yup you are right, I guess the most important use case is actually testing and, eventually, some kind of monitoring (I do my best here :)...

@orottier
Copy link
Owner Author

orottier commented Jan 3, 2024

Ready for merging

// Then, ask to resume rendering via a control message
let (sender, receiver) = oneshot::channel();
let notify = OneshotNotify::Async(sender);
let suspend_msg = ControlMessage::Resume { notify };
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suspend_msg is a bit misleading, should be resume_msg?

// First, stop rendering via a control message
let (sender, receiver) = oneshot::channel();
let notify = OneshotNotify::Async(sender);
let suspend_msg = ControlMessage::Close { notify };
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same close_msg?

@b-ma
Copy link
Collaborator

b-ma commented Jan 3, 2024

Nice!

@orottier orottier merged commit d2e782c into main Jan 4, 2024
3 checks passed
@orottier orottier deleted the feature/device-readiness branch January 4, 2024 10:02
@orottier
Copy link
Owner Author

orottier commented Jan 4, 2024

Released in v0.41

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Improve BaseAudioContext onChangeState to signal device readiness
2 participants