Skip to content

Commit 30c2926

Browse files
committed
Further code cleanup now the OfflineAudioContext has an event loop
And add tests for async rendering events
1 parent 551a0c7 commit 30c2926

File tree

3 files changed

+83
-61
lines changed

3 files changed

+83
-61
lines changed

src/context/offline.rs

Lines changed: 66 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,11 @@ use std::sync::{Arc, Mutex};
55

66
use crate::buffer::AudioBuffer;
77
use crate::context::{AudioContextState, BaseAudioContext, ConcreteBaseAudioContext};
8+
use crate::events::{
9+
Event, EventDispatch, EventHandler, EventPayload, EventType, OfflineAudioCompletionEvent,
10+
};
811
use crate::render::RenderThread;
912
use crate::{assert_valid_sample_rate, RENDER_QUANTUM_SIZE};
10-
use crate::{Event, OfflineAudioCompletionEvent};
1113

1214
use crate::events::EventLoop;
1315
use futures_channel::{mpsc, oneshot};
@@ -49,10 +51,6 @@ struct OfflineAudioContextRenderer {
4951
suspend_callbacks: Vec<(usize, Box<OfflineAudioContextCallback>)>,
5052
/// channel to listen for `resume` calls on a suspended context
5153
resume_receiver: mpsc::Receiver<()>,
52-
/// event handler for statechange event
53-
onstatechange_handler: Option<Box<dyn FnMut(Event) + Send + 'static>>,
54-
/// event handler for complete event
55-
oncomplete_handler: Option<Box<dyn FnOnce(OfflineAudioCompletionEvent) + Send + 'static>>,
5654
/// event loop to run after each render quantum
5755
event_loop: EventLoop,
5856
}
@@ -61,18 +59,6 @@ impl BaseAudioContext for OfflineAudioContext {
6159
fn base(&self) -> &ConcreteBaseAudioContext {
6260
&self.base
6361
}
64-
65-
fn set_onstatechange<F: FnMut(Event) + Send + 'static>(&self, callback: F) {
66-
if let Some(renderer) = self.renderer.lock().unwrap().as_mut() {
67-
renderer.onstatechange_handler = Some(Box::new(callback));
68-
}
69-
}
70-
71-
fn clear_onstatechange(&self) {
72-
if let Some(renderer) = self.renderer.lock().unwrap().as_mut() {
73-
renderer.onstatechange_handler = None;
74-
}
75-
}
7662
}
7763

7864
impl OfflineAudioContext {
@@ -138,8 +124,6 @@ impl OfflineAudioContext {
138124
suspend_promises: Vec::new(),
139125
suspend_callbacks: Vec::new(),
140126
resume_receiver,
141-
onstatechange_handler: None,
142-
oncomplete_handler: None,
143127
event_loop,
144128
};
145129

@@ -170,24 +154,25 @@ impl OfflineAudioContext {
170154
.unwrap()
171155
.take()
172156
.expect("InvalidStateError - Cannot call `startRendering` twice");
157+
173158
let OfflineAudioContextRenderer {
174159
renderer,
175160
suspend_callbacks,
176-
oncomplete_handler,
177-
mut onstatechange_handler,
178161
event_loop,
179162
..
180163
} = renderer;
181164

182165
self.base.set_state(AudioContextState::Running);
183-
Self::emit_statechange(&mut onstatechange_handler);
184166

185-
let result = renderer.render_audiobuffer_sync(self, suspend_callbacks, event_loop);
167+
let result = renderer.render_audiobuffer_sync(self, suspend_callbacks, &event_loop);
186168

187169
self.base.set_state(AudioContextState::Closed);
188-
Self::emit_statechange(&mut onstatechange_handler);
170+
let _ = self
171+
.base
172+
.send_event(EventDispatch::complete(result.clone()));
189173

190-
Self::emit_complete(oncomplete_handler, &result);
174+
// spin the event loop once more to handle the statechange/complete events
175+
event_loop.handle_pending_events();
191176

192177
result
193178
}
@@ -216,48 +201,27 @@ impl OfflineAudioContext {
216201
renderer,
217202
suspend_promises,
218203
resume_receiver,
219-
oncomplete_handler,
220-
mut onstatechange_handler,
204+
event_loop,
221205
..
222206
} = renderer;
223207

224208
self.base.set_state(AudioContextState::Running);
225-
Self::emit_statechange(&mut onstatechange_handler);
226209

227210
let result = renderer
228-
.render_audiobuffer(self.length, suspend_promises, resume_receiver)
211+
.render_audiobuffer(self.length, suspend_promises, resume_receiver, &event_loop)
229212
.await;
230213

231214
self.base.set_state(AudioContextState::Closed);
232-
Self::emit_statechange(&mut onstatechange_handler);
215+
let _ = self
216+
.base
217+
.send_event(EventDispatch::complete(result.clone()));
233218

234-
Self::emit_complete(oncomplete_handler, &result);
219+
// spin the event loop once more to handle the statechange/complete events
220+
event_loop.handle_pending_events();
235221

236222
result
237223
}
238224

239-
fn emit_complete(
240-
oncomplete_handler: Option<Box<dyn FnOnce(OfflineAudioCompletionEvent) + Send>>,
241-
result: &AudioBuffer,
242-
) {
243-
if let Some(callback) = oncomplete_handler {
244-
let event = OfflineAudioCompletionEvent {
245-
rendered_buffer: result.clone(),
246-
event: Event { type_: "complete" },
247-
};
248-
(callback)(event);
249-
}
250-
}
251-
252-
fn emit_statechange(onstatechange_handler: &mut Option<Box<dyn FnMut(Event) + Send>>) {
253-
if let Some(callback) = onstatechange_handler.as_mut() {
254-
let event = Event {
255-
type_: "statechange",
256-
};
257-
(callback)(event);
258-
}
259-
}
260-
261225
/// get the length of rendering audio buffer
262226
// false positive: OfflineAudioContext is not const
263227
#[allow(clippy::missing_const_for_fn, clippy::unused_self)]
@@ -434,17 +398,24 @@ impl OfflineAudioContext {
434398
&self,
435399
callback: F,
436400
) {
437-
if let Some(renderer) = self.renderer.lock().unwrap().as_mut() {
438-
renderer.oncomplete_handler = Some(Box::new(callback));
439-
}
401+
let callback = move |v| match v {
402+
EventPayload::Complete(v) => {
403+
let event = OfflineAudioCompletionEvent {
404+
rendered_buffer: v,
405+
event: Event { type_: "complete" },
406+
};
407+
callback(event)
408+
}
409+
_ => unreachable!(),
410+
};
411+
412+
self.base()
413+
.set_event_handler(EventType::Complete, EventHandler::Once(Box::new(callback)));
440414
}
441415

442416
/// Unset the callback to run when the rendering has completed
443-
#[allow(clippy::missing_panics_doc)]
444417
pub fn clear_oncomplete(&self) {
445-
if let Some(renderer) = self.renderer.lock().unwrap().as_mut() {
446-
renderer.oncomplete_handler = None;
447-
}
418+
self.base().clear_event_handler(EventType::Complete);
448419
}
449420
}
450421

@@ -599,6 +570,23 @@ mod tests {
599570
assert!(changed.load(Ordering::Relaxed));
600571
}
601572

573+
#[test]
574+
fn test_onstatechange_async() {
575+
use futures::executor;
576+
577+
let 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 _ = executor::block_on(context.start_rendering());
586+
587+
assert!(changed.load(Ordering::Relaxed));
588+
}
589+
602590
#[test]
603591
fn test_oncomplete() {
604592
let mut context = OfflineAudioContext::new(2, 555, 44_100.);
@@ -614,4 +602,22 @@ mod tests {
614602

615603
assert!(complete.load(Ordering::Relaxed));
616604
}
605+
606+
#[test]
607+
fn test_oncomplete_async() {
608+
use futures::executor;
609+
610+
let context = OfflineAudioContext::new(2, 555, 44_100.);
611+
612+
let complete = Arc::new(AtomicBool::new(false));
613+
let complete_clone = Arc::clone(&complete);
614+
context.set_oncomplete(move |event| {
615+
assert_eq!(event.rendered_buffer.length(), 555);
616+
complete_clone.store(true, Ordering::Relaxed);
617+
});
618+
619+
let _ = executor::block_on(context.start_rendering());
620+
621+
assert!(complete.load(Ordering::Relaxed));
622+
}
617623
}

src/events.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ pub(crate) enum EventType {
2525
ProcessorError(AudioNodeId),
2626
Diagnostics,
2727
Message(AudioNodeId),
28+
Complete,
2829
}
2930

3031
/// The Error Event interface
@@ -57,6 +58,7 @@ pub(crate) enum EventPayload {
5758
Diagnostics(Vec<u8>),
5859
Message(Box<dyn Any + Send + 'static>),
5960
AudioContextState(AudioContextState),
61+
Complete(AudioBuffer),
6062
}
6163

6264
#[derive(Debug)]
@@ -114,6 +116,13 @@ impl EventDispatch {
114116
payload: EventPayload::Message(value),
115117
}
116118
}
119+
120+
pub fn complete(buffer: AudioBuffer) -> Self {
121+
EventDispatch {
122+
type_: EventType::Complete,
123+
payload: EventPayload::Complete(buffer),
124+
}
125+
}
117126
}
118127

119128
pub(crate) enum EventHandler {

src/render/thread.rs

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -236,7 +236,7 @@ impl RenderThread {
236236
mut self,
237237
context: &mut OfflineAudioContext,
238238
mut suspend_callbacks: Vec<(usize, Box<OfflineAudioContextCallback>)>,
239-
event_loop: EventLoop,
239+
event_loop: &EventLoop,
240240
) -> AudioBuffer {
241241
let length = context.length();
242242

@@ -284,6 +284,7 @@ impl RenderThread {
284284
length: usize,
285285
mut suspend_callbacks: Vec<(usize, oneshot::Sender<()>)>,
286286
mut resume_receiver: mpsc::Receiver<()>,
287+
event_loop: &EventLoop,
287288
) -> AudioBuffer {
288289
let options = AudioBufferOptions {
289290
number_of_channels: self.number_of_channels,
@@ -309,6 +310,12 @@ impl RenderThread {
309310
}
310311

311312
self.render_offline_quantum(&mut buffer);
313+
314+
let events_were_handled = event_loop.handle_pending_events();
315+
if events_were_handled {
316+
// Handle any control messages that may have been submitted by the handler
317+
self.handle_control_messages();
318+
}
312319
}
313320

314321
buffer

0 commit comments

Comments
 (0)