web_audio_api/
lib.rs

1#![doc = include_str!("../README.md")]
2#![warn(rust_2018_idioms)]
3#![warn(rust_2021_compatibility)]
4#![warn(clippy::missing_panics_doc)]
5#![warn(clippy::clone_on_ref_ptr)]
6#![deny(trivial_numeric_casts)]
7#![deny(missing_debug_implementations)]
8
9use std::error::Error;
10use std::sync::atomic::{AtomicU32, AtomicU64, Ordering};
11
12/// Render quantum size, the audio graph is rendered in blocks of RENDER_QUANTUM_SIZE samples
13/// see. <https://webaudio.github.io/web-audio-api/#render-quantum>
14pub(crate) const RENDER_QUANTUM_SIZE: usize = 128;
15
16/// Maximum number of channels for audio processing
17pub const MAX_CHANNELS: usize = 32;
18
19mod buffer;
20pub use buffer::*;
21
22mod capacity;
23pub use capacity::*;
24
25pub mod context;
26
27pub mod media_devices;
28pub mod media_recorder;
29pub mod media_streams;
30
31pub mod node;
32
33mod events;
34pub use events::*;
35
36mod message_port;
37pub use message_port::MessagePort;
38
39mod param;
40pub use param::*;
41
42mod periodic_wave;
43pub use periodic_wave::*;
44
45mod render;
46
47mod spatial;
48pub use spatial::AudioListener;
49
50mod io;
51
52mod analysis;
53mod message;
54
55mod decoding;
56
57mod media_element;
58pub use media_element::MediaElement;
59
60mod resampling;
61pub mod worklet;
62
63#[repr(transparent)]
64pub(crate) struct AtomicF32 {
65    bits: AtomicU32,
66}
67
68impl std::fmt::Debug for AtomicF32 {
69    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
70        f.write_fmt(format_args!("{}", self.load(Ordering::Relaxed)))
71    }
72}
73
74impl AtomicF32 {
75    #[must_use]
76    pub fn new(value: f32) -> Self {
77        Self {
78            bits: AtomicU32::new(value.to_bits()),
79        }
80    }
81
82    #[must_use]
83    pub fn load(&self, ordering: Ordering) -> f32 {
84        f32::from_bits(self.bits.load(ordering))
85    }
86
87    pub fn store(&self, value: f32, ordering: Ordering) {
88        self.bits.store(value.to_bits(), ordering);
89    }
90}
91
92/// Atomic float 64, only `load` and `store` are supported, no arithmetic
93#[repr(transparent)]
94pub(crate) struct AtomicF64 {
95    bits: AtomicU64,
96}
97
98impl std::fmt::Debug for AtomicF64 {
99    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
100        f.write_fmt(format_args!("{}", self.load(Ordering::Relaxed)))
101    }
102}
103
104impl AtomicF64 {
105    #[must_use]
106    pub fn new(value: f64) -> Self {
107        Self {
108            bits: AtomicU64::new(value.to_bits()),
109        }
110    }
111
112    #[must_use]
113    pub fn load(&self, ordering: Ordering) -> f64 {
114        f64::from_bits(self.bits.load(ordering))
115    }
116
117    pub fn store(&self, value: f64, ordering: Ordering) {
118        self.bits.store(value.to_bits(), ordering);
119    }
120}
121
122/// Assert that the given sample rate is valid.
123///
124/// Note that in practice sample rates should stand between 8000Hz (lower bound for
125/// voice based applications, e.g. see phone bandwidth) and 96000Hz (for very high
126/// quality audio applications and spectrum manipulation).
127/// Most common sample rates for musical applications are 44100 and 48000.
128///
129/// - see <https://webaudio.github.io/web-audio-api/#dom-baseaudiocontext-samplerate>
130/// > An implementation MUST support sample rates in at least the range 8000 to 96000.
131///
132/// # Panics
133///
134/// This function will panic if:
135/// - the given sample rate is lower than 2000 or greater than 384000
136///
137#[track_caller]
138#[inline(always)]
139pub(crate) fn assert_valid_sample_rate(sample_rate: f32) {
140    // Arbitrary cutoffs defined as:
141    // min_sample_rate = min_required_in_spec / 4
142    // max_sample_rate = max_required_in_spec * 4
143    let min_sample_rate = 2_000.;
144    let max_sample_rate = 384_000.;
145
146    assert!(
147        sample_rate >= min_sample_rate && sample_rate <= max_sample_rate,
148        "NotSupportedError - Invalid sample rate: {:?}, should be in the range [{:?}, {:?}]",
149        sample_rate,
150        min_sample_rate,
151        max_sample_rate,
152    );
153}
154
155/// Assert that the given number of channels is valid.
156///
157/// # Panics
158///
159/// This function will panic if:
160/// - the given number of channels is outside the [1, 32] range,
161///   32 being defined by the MAX_CHANNELS constant.
162///
163#[track_caller]
164#[inline(always)]
165pub(crate) fn assert_valid_number_of_channels(number_of_channels: usize) {
166    assert!(
167        number_of_channels > 0 && number_of_channels <= MAX_CHANNELS,
168        "NotSupportedError - Invalid number of channels: {:?} is outside range [1, {:?}]",
169        number_of_channels,
170        MAX_CHANNELS
171    );
172}
173
174/// Assert that the given channel number is valid according to the number of channels
175/// of an Audio asset (e.g. [`AudioBuffer`]).
176///
177/// # Panics
178///
179/// This function will panic if:
180/// - the given channel number is greater than or equal to the given number of channels.
181///
182#[track_caller]
183#[inline(always)]
184pub(crate) fn assert_valid_channel_number(channel_number: usize, number_of_channels: usize) {
185    assert!(
186        channel_number < number_of_channels,
187        "IndexSizeError - Invalid channel number {:?} (number of channels: {:?})",
188        channel_number,
189        number_of_channels
190    );
191}
192
193/// Assert that the given value number is a valid buffer length, i.e. greater than zero
194///
195/// # Panics
196///
197/// This function will panic if:
198/// - the given value is not lower than or equal to zero
199///
200#[track_caller]
201#[inline(always)]
202pub(crate) fn assert_valid_buffer_length(length: usize) {
203    assert!(
204        length > 0,
205        "NotSupportedError - Invalid length: {:?} is less than or equal to minimum bound (0)",
206        length,
207    );
208}
209
210/// Assert that the given value number is a valid time information, i.e. greater
211/// than or equal to zero and finite.
212///
213/// # Panics
214///
215/// This function will panic if:
216/// - the given value is not finite and lower than zero
217///
218#[track_caller]
219#[inline(always)]
220pub(crate) fn assert_valid_time_value(value: f64) {
221    assert!(
222        value.is_finite(),
223        "TypeError - The provided time value is non-finite.",
224    );
225
226    assert!(
227        value >= 0.,
228        "RangeError - The provided time value ({:?}) cannot be negative",
229        value
230    );
231}
232
233pub(crate) trait AudioBufferIter: Iterator<Item = FallibleBuffer> + Send + 'static {}
234
235impl<M: Iterator<Item = FallibleBuffer> + Send + 'static> AudioBufferIter for M {}
236
237type FallibleBuffer = Result<AudioBuffer, Box<dyn Error + Send + Sync>>;
238
239#[cfg(test)]
240mod tests {
241    use float_eq::assert_float_eq;
242
243    use super::*;
244
245    #[test]
246    fn test_atomic_f64() {
247        let f = AtomicF64::new(2.0);
248        assert_float_eq!(f.load(Ordering::SeqCst), 2.0, abs <= 0.);
249
250        f.store(3.0, Ordering::SeqCst);
251        assert_float_eq!(f.load(Ordering::SeqCst), 3.0, abs <= 0.);
252    }
253
254    #[test]
255    #[should_panic]
256    fn test_invalid_sample_rate_too_small() {
257        // invalid lower value used in wpt check
258        // <the-audio-api/the-audiocontext-interface/audiocontextoptions.html>
259        assert_valid_sample_rate(1.);
260    }
261
262    #[test]
263    #[should_panic]
264    fn test_invalid_sample_rate_too_big() {
265        // invalid upper value used in wpt check
266        // <the-audio-api/the-audiocontext-interface/audiocontextoptions.html>
267        assert_valid_sample_rate(1_000_000.);
268    }
269
270    #[test]
271    fn test_valid_sample_rate() {
272        assert_valid_sample_rate(48000.);
273    }
274
275    #[test]
276    #[should_panic]
277    fn test_invalid_number_of_channels_min() {
278        assert_valid_number_of_channels(0);
279    }
280
281    #[test]
282    #[should_panic]
283    fn test_invalid_number_of_channels_max() {
284        assert_valid_number_of_channels(33);
285    }
286
287    #[test]
288    fn test_valid_number_of_channels() {
289        assert_valid_number_of_channels(1);
290        assert_valid_number_of_channels(32);
291    }
292
293    #[test]
294    #[should_panic]
295    fn test_invalid_time_value_non_finite() {
296        assert_valid_time_value(f64::NAN);
297    }
298
299    #[test]
300    #[should_panic]
301    fn test_invalid_time_value_negative() {
302        assert_valid_time_value(-1.);
303    }
304
305    #[test]
306    fn test_valid_time_value() {
307        assert_valid_time_value(0.);
308        assert_valid_time_value(1.);
309    }
310}