Skip to main content

web_audio_api/
events.rs

1#[cfg(feature = "diagnostics")]
2use crate::context::AudioContextDiagnostics;
3use crate::context::ConcreteBaseAudioContext;
4use crate::context::{AudioContextState, AudioNodeId};
5use crate::{AudioBuffer, AudioRenderCapacityEvent};
6
7use std::any::Any;
8use std::collections::HashMap;
9use std::hash::Hash;
10use std::ops::ControlFlow;
11use std::sync::{Arc, Mutex};
12
13use crossbeam_channel::Receiver;
14
15/// The Event interface
16#[derive(Debug, Clone)]
17#[non_exhaustive]
18pub struct Event {
19    pub type_: &'static str,
20}
21
22#[derive(Hash, Eq, PartialEq, Debug)]
23pub(crate) enum EventType {
24    Ended(AudioNodeId),
25    SinkChange,
26    StateChange,
27    RenderCapacity,
28    ProcessorError(AudioNodeId),
29    #[cfg(feature = "diagnostics")]
30    Diagnostics,
31    Message(AudioNodeId),
32    Complete,
33    AudioProcessing(AudioNodeId),
34}
35
36/// The Error Event interface
37#[non_exhaustive]
38#[derive(Debug)]
39pub struct ErrorEvent {
40    /// The error message
41    pub message: String,
42    /// The object with which panic was originally invoked.
43    pub error: Box<dyn Any + Send>,
44    /// Inherits from this base Event
45    pub event: Event,
46}
47
48/// The AudioProcessingEvent interface
49#[non_exhaustive]
50#[derive(Debug)]
51pub struct AudioProcessingEvent {
52    /// The input buffer
53    pub input_buffer: AudioBuffer,
54    /// The output buffer
55    pub output_buffer: AudioBuffer,
56    /// The time when the audio will be played in the same time coordinate system as the
57    /// AudioContext's currentTime.
58    pub playback_time: f64,
59    pub(crate) registration: Option<(ConcreteBaseAudioContext, AudioNodeId)>,
60}
61
62impl Drop for AudioProcessingEvent {
63    fn drop(&mut self) {
64        if let Some((context, id)) = self.registration.take() {
65            let wrapped = crate::message::ControlMessage::NodeMessage {
66                id,
67                msg: llq::Node::new(Box::new(self.output_buffer.clone())),
68            };
69            context.send_control_msg(wrapped);
70        }
71    }
72}
73
74/// The OfflineAudioCompletionEvent Event interface
75#[non_exhaustive]
76#[derive(Debug)]
77pub struct OfflineAudioCompletionEvent {
78    /// The rendered AudioBuffer
79    pub rendered_buffer: AudioBuffer,
80    /// Inherits from this base Event
81    pub event: Event,
82}
83
84#[derive(Debug)]
85pub(crate) enum EventPayload {
86    None,
87    RenderCapacity(AudioRenderCapacityEvent),
88    ProcessorError(ErrorEvent),
89    #[cfg(feature = "diagnostics")]
90    Diagnostics(AudioContextDiagnostics),
91    Message(Box<dyn Any + Send + 'static>),
92    AudioContextState(AudioContextState),
93    Complete(AudioBuffer),
94    AudioProcessing(AudioProcessingEvent),
95}
96
97#[derive(Debug)]
98pub(crate) struct EventDispatch {
99    type_: EventType,
100    payload: EventPayload,
101}
102
103impl EventDispatch {
104    pub fn ended(id: AudioNodeId) -> Self {
105        EventDispatch {
106            type_: EventType::Ended(id),
107            payload: EventPayload::None,
108        }
109    }
110
111    pub fn sink_change() -> Self {
112        EventDispatch {
113            type_: EventType::SinkChange,
114            payload: EventPayload::None,
115        }
116    }
117
118    pub fn state_change(state: AudioContextState) -> Self {
119        EventDispatch {
120            type_: EventType::StateChange,
121            payload: EventPayload::AudioContextState(state),
122        }
123    }
124
125    pub fn render_capacity(value: AudioRenderCapacityEvent) -> Self {
126        EventDispatch {
127            type_: EventType::RenderCapacity,
128            payload: EventPayload::RenderCapacity(value),
129        }
130    }
131
132    pub fn processor_error(id: AudioNodeId, value: ErrorEvent) -> Self {
133        EventDispatch {
134            type_: EventType::ProcessorError(id),
135            payload: EventPayload::ProcessorError(value),
136        }
137    }
138
139    #[cfg(feature = "diagnostics")]
140    pub fn diagnostics(value: AudioContextDiagnostics) -> Self {
141        EventDispatch {
142            type_: EventType::Diagnostics,
143            payload: EventPayload::Diagnostics(value),
144        }
145    }
146
147    pub fn message(id: AudioNodeId, value: Box<dyn Any + Send + 'static>) -> Self {
148        EventDispatch {
149            type_: EventType::Message(id),
150            payload: EventPayload::Message(value),
151        }
152    }
153
154    pub fn complete(buffer: AudioBuffer) -> Self {
155        EventDispatch {
156            type_: EventType::Complete,
157            payload: EventPayload::Complete(buffer),
158        }
159    }
160
161    pub fn audio_processing(id: AudioNodeId, value: AudioProcessingEvent) -> Self {
162        EventDispatch {
163            type_: EventType::AudioProcessing(id),
164            payload: EventPayload::AudioProcessing(value),
165        }
166    }
167}
168
169pub(crate) enum EventHandler {
170    Once(Box<dyn FnOnce(EventPayload) + Send + 'static>),
171    Multiple(Box<dyn FnMut(EventPayload) + Send + 'static>),
172}
173
174#[derive(Clone)]
175pub(crate) struct EventLoop {
176    event_recv: Receiver<EventDispatch>,
177    event_handlers: Arc<Mutex<HashMap<EventType, EventHandler>>>,
178}
179
180impl EventLoop {
181    pub fn new(event_recv: Receiver<EventDispatch>) -> Self {
182        Self {
183            event_recv,
184            event_handlers: Default::default(),
185        }
186    }
187
188    fn handle_event(&self, mut event: EventDispatch) -> ControlFlow<()> {
189        // Terminate the event loop when the audio context is closing
190        let mut result = ControlFlow::Continue(());
191        if matches!(
192            event.payload,
193            EventPayload::AudioContextState(AudioContextState::Closed)
194        ) {
195            event.payload = EventPayload::None; // the statechange handler takes no argument
196            result = ControlFlow::Break(());
197        }
198
199        // In unit tests, we rethrow panics to avoid missing critical node exceptions
200        // https://github.com/orottier/web-audio-api-rs/issues/522
201        #[cfg(test)]
202        if let EventPayload::ProcessorError(e) = event.payload {
203            panic!("Rethrowing exception during tests: {:?}", e);
204        }
205
206        let mut event_handler_lock = self.event_handlers.lock().unwrap();
207        let callback_option = event_handler_lock.remove(&event.type_);
208        drop(event_handler_lock); // release Mutex while running callback
209
210        if let Some(callback) = callback_option {
211            match callback {
212                EventHandler::Once(f) => (f)(event.payload),
213                EventHandler::Multiple(mut f) => {
214                    (f)(event.payload);
215                    self.event_handlers
216                        .lock()
217                        .unwrap()
218                        .insert(event.type_, EventHandler::Multiple(f));
219                }
220            };
221        }
222
223        result
224    }
225
226    #[inline(always)]
227    pub fn handle_pending_events(&self) -> bool {
228        let mut events_were_handled = false;
229        // try_iter will yield all pending events, but does not block
230        for event in self.event_recv.try_iter() {
231            // we can ignore the return value, it is only useful in the event thread
232            let _ = self.handle_event(event);
233            events_were_handled = true;
234        }
235        events_were_handled
236    }
237
238    pub fn run_in_thread(&self) {
239        log::debug!("Entering event thread");
240
241        // split borrows to help compiler
242        let self_clone = self.clone();
243
244        std::thread::spawn(move || {
245            // This thread is dedicated to event handling, so we can block
246            for event in self_clone.event_recv.iter() {
247                let result = self_clone.handle_event(event);
248                if result.is_break() {
249                    break;
250                }
251            }
252
253            log::debug!("Event loop has terminated");
254        });
255    }
256
257    pub fn set_handler(&self, event: EventType, callback: EventHandler) {
258        self.event_handlers.lock().unwrap().insert(event, callback);
259    }
260
261    pub fn clear_handler(&self, event: EventType) {
262        self.event_handlers.lock().unwrap().remove(&event);
263    }
264}