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