Skip to main content

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