Skip to content

Commit b0b03a6

Browse files
committed
Emit statechange event for OfflineAudioContext renderin
Previously this was not emitted because the OfflineAudioContext has no event loop running. By overriding the trait we do make this happen. TODO: if you call offline_context.base().set_onstatechange we run the wrong handler code stil..
1 parent a835a9d commit b0b03a6

File tree

1 file changed

+55
-8
lines changed

1 file changed

+55
-8
lines changed

src/context/offline.rs

Lines changed: 55 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -48,14 +48,28 @@ struct OfflineAudioContextRenderer {
4848
suspend_callbacks: Vec<(usize, Box<OfflineAudioContextCallback>)>,
4949
/// channel to listen for `resume` calls on a suspended context
5050
resume_receiver: mpsc::Receiver<()>,
51-
/// event handler for oncomplete event
51+
/// event handler for statechange event
52+
onstatechange_handler: Option<Box<dyn FnMut(Event) + Send + 'static>>,
53+
/// event handler for complete event
5254
oncomplete_handler: Option<Box<dyn FnOnce(OfflineAudioCompletionEvent) + Send + 'static>>,
5355
}
5456

5557
impl BaseAudioContext for OfflineAudioContext {
5658
fn base(&self) -> &ConcreteBaseAudioContext {
5759
&self.base
5860
}
61+
62+
fn set_onstatechange<F: FnMut(Event) + Send + 'static>(&self, callback: F) {
63+
if let Some(renderer) = self.renderer.lock().unwrap().as_mut() {
64+
renderer.onstatechange_handler = Some(Box::new(callback));
65+
}
66+
}
67+
68+
fn clear_onstatechange(&self) {
69+
if let Some(renderer) = self.renderer.lock().unwrap().as_mut() {
70+
renderer.onstatechange_handler = None;
71+
}
72+
}
5973
}
6074

6175
impl OfflineAudioContext {
@@ -114,6 +128,7 @@ impl OfflineAudioContext {
114128
suspend_promises: Vec::new(),
115129
suspend_callbacks: Vec::new(),
116130
resume_receiver,
131+
onstatechange_handler: None,
117132
oncomplete_handler: None,
118133
};
119134

@@ -147,15 +162,20 @@ impl OfflineAudioContext {
147162
let OfflineAudioContextRenderer {
148163
renderer,
149164
suspend_callbacks,
150-
mut oncomplete_handler,
165+
oncomplete_handler,
166+
mut onstatechange_handler,
151167
..
152168
} = renderer;
153169

154170
self.base.set_state(AudioContextState::Running);
171+
Self::emit_statechange(&mut onstatechange_handler);
172+
155173
let result = renderer.render_audiobuffer_sync(self.length, suspend_callbacks, self);
174+
156175
self.base.set_state(AudioContextState::Closed);
176+
Self::emit_statechange(&mut onstatechange_handler);
157177

158-
Self::emit_oncomplete(&mut oncomplete_handler, &result);
178+
Self::emit_complete(oncomplete_handler, &result);
159179

160180
result
161181
}
@@ -184,28 +204,31 @@ impl OfflineAudioContext {
184204
renderer,
185205
suspend_promises,
186206
resume_receiver,
187-
mut oncomplete_handler,
207+
oncomplete_handler,
208+
mut onstatechange_handler,
188209
..
189210
} = renderer;
190211

191212
self.base.set_state(AudioContextState::Running);
213+
Self::emit_statechange(&mut onstatechange_handler);
192214

193215
let result = renderer
194216
.render_audiobuffer(self.length, suspend_promises, resume_receiver)
195217
.await;
196218

197219
self.base.set_state(AudioContextState::Closed);
220+
Self::emit_statechange(&mut onstatechange_handler);
198221

199-
Self::emit_oncomplete(&mut oncomplete_handler, &result);
222+
Self::emit_complete(oncomplete_handler, &result);
200223

201224
result
202225
}
203226

204-
fn emit_oncomplete(
205-
oncomplete_handler: &mut Option<Box<dyn FnOnce(OfflineAudioCompletionEvent) + Send>>,
227+
fn emit_complete(
228+
oncomplete_handler: Option<Box<dyn FnOnce(OfflineAudioCompletionEvent) + Send>>,
206229
result: &AudioBuffer,
207230
) {
208-
if let Some(callback) = oncomplete_handler.take() {
231+
if let Some(callback) = oncomplete_handler {
209232
let event = OfflineAudioCompletionEvent {
210233
rendered_buffer: result.clone(),
211234
event: Event { type_: "complete" },
@@ -214,6 +237,15 @@ impl OfflineAudioContext {
214237
}
215238
}
216239

240+
fn emit_statechange(onstatechange_handler: &mut Option<Box<dyn FnMut(Event) + Send>>) {
241+
if let Some(callback) = onstatechange_handler.as_mut() {
242+
let event = Event {
243+
type_: "statechange",
244+
};
245+
(callback)(event);
246+
}
247+
}
248+
217249
/// get the length of rendering audio buffer
218250
// false positive: OfflineAudioContext is not const
219251
#[allow(clippy::missing_const_for_fn, clippy::unused_self)]
@@ -540,6 +572,21 @@ mod tests {
540572
context.suspend_sync(0.0, |_| ());
541573
}
542574

575+
#[test]
576+
fn test_onstatechange() {
577+
let mut context = OfflineAudioContext::new(2, 555, 44_100.);
578+
579+
let changed = Arc::new(AtomicBool::new(false));
580+
let changed_clone = Arc::clone(&changed);
581+
context.set_onstatechange(move |_event| {
582+
changed_clone.store(true, Ordering::Relaxed);
583+
});
584+
585+
let _ = context.start_rendering_sync();
586+
587+
assert!(changed.load(Ordering::Relaxed));
588+
}
589+
543590
#[test]
544591
fn test_oncomplete() {
545592
let mut context = OfflineAudioContext::new(2, 555, 44_100.);

0 commit comments

Comments
 (0)