Skip to content

Commit 44ec8ef

Browse files
authored
Merge pull request #417 from b-ma/fix/scheduled-source-node-start-stop
Assert `AudioScheduledSourceNode` node start and stop arguments are valid
2 parents 585f8aa + b5613c8 commit 44ec8ef

File tree

6 files changed

+94
-31
lines changed

6 files changed

+94
-31
lines changed

src/lib.rs

Lines changed: 43 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -185,8 +185,8 @@ pub(crate) fn assert_valid_number_of_channels(number_of_channels: usize) {
185185
}
186186
}
187187

188-
/// Assert that the given channel number is valid according the number of channel
189-
/// of an Audio asset (e.g. [`AudioBuffer`])
188+
/// Assert that the given channel number is valid according to the number of channels
189+
/// of an Audio asset (e.g. [`AudioBuffer`]).
190190
///
191191
/// # Panics
192192
///
@@ -204,6 +204,29 @@ pub(crate) fn assert_valid_channel_number(channel_number: usize, number_of_chann
204204
}
205205
}
206206

207+
/// Assert that the given value number is a valid time information, i.e. greater
208+
/// than or equal to zero and finite.
209+
///
210+
/// # Panics
211+
///
212+
/// This function will panic if:
213+
/// - the given value is not finite and lower than zero
214+
///
215+
#[track_caller]
216+
#[inline(always)]
217+
pub(crate) fn assert_valid_time_value(value: f64) {
218+
if !value.is_finite() {
219+
panic!("TypeError - The provided time value is non-finite.");
220+
}
221+
222+
if value < 0. {
223+
panic!(
224+
"RangeError - The provided time value ({:?}) cannot be negative",
225+
value
226+
);
227+
}
228+
}
229+
207230
pub(crate) trait AudioBufferIter: Iterator<Item = FallibleBuffer> + Send + 'static {}
208231

209232
impl<M: Iterator<Item = FallibleBuffer> + Send + 'static> AudioBufferIter for M {}
@@ -265,4 +288,22 @@ mod tests {
265288
assert_valid_number_of_channels(1);
266289
assert_valid_number_of_channels(32);
267290
}
291+
292+
#[test]
293+
#[should_panic]
294+
fn test_invalid_time_value_non_finite() {
295+
assert_valid_time_value(f64::NAN);
296+
}
297+
298+
#[test]
299+
#[should_panic]
300+
fn test_invalid_time_value_negative() {
301+
assert_valid_time_value(-1.);
302+
}
303+
304+
#[test]
305+
fn test_valid_time_value() {
306+
assert_valid_time_value(0.);
307+
assert_valid_time_value(1.);
308+
}
268309
}

src/node/audio_buffer_source.rs

Lines changed: 16 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ use crate::buffer::AudioBuffer;
66
use crate::context::{AudioContextRegistration, AudioParamId, BaseAudioContext};
77
use crate::param::{AudioParam, AudioParamDescriptor, AutomationRate};
88
use crate::render::{AudioParamValues, AudioProcessor, AudioRenderQuantum, RenderScope};
9-
use crate::{AtomicF64, RENDER_QUANTUM_SIZE};
9+
use crate::{assert_valid_time_value, AtomicF64, RENDER_QUANTUM_SIZE};
1010

1111
use super::{AudioNode, AudioScheduledSourceNode, ChannelConfig};
1212

@@ -147,6 +147,7 @@ impl AudioScheduledSourceNode for AudioBufferSourceNode {
147147
}
148148

149149
fn stop_at(&mut self, when: f64) {
150+
assert_valid_time_value(when);
150151
assert!(
151152
self.source_started,
152153
"InvalidStateError cannot stop before start"
@@ -251,10 +252,14 @@ impl AudioBufferSourceNode {
251252
///
252253
/// Panics if the source was already started
253254
pub fn start_at_with_offset_and_duration(&mut self, start: f64, offset: f64, duration: f64) {
255+
assert_valid_time_value(start);
256+
assert_valid_time_value(offset);
257+
assert_valid_time_value(duration);
254258
assert!(
255259
!self.source_started,
256260
"InvalidStateError: Cannot call `start` twice"
257261
);
262+
258263
self.source_started = true;
259264

260265
let control = ControlMessage::StartWithOffsetAndDuration(start, offset, duration);
@@ -1014,23 +1019,25 @@ mod tests {
10141019
}
10151020

10161021
#[test]
1017-
fn test_schedule_in_the_past() {
1022+
fn test_start_in_the_past() {
10181023
let sample_rate = 48000.;
1019-
let mut context = OfflineAudioContext::new(1, RENDER_QUANTUM_SIZE, sample_rate);
1024+
let mut context = OfflineAudioContext::new(1, 2 * RENDER_QUANTUM_SIZE, sample_rate);
10201025

10211026
let mut dirac = context.create_buffer(1, 1, sample_rate);
10221027
dirac.copy_to_channel(&[1.], 0);
10231028

1024-
let mut src = context.create_buffer_source();
1025-
src.connect(&context.destination());
1026-
src.set_buffer(dirac);
1027-
src.start_at(-1.);
1029+
context.suspend_sync((128. / sample_rate).into(), |context| {
1030+
let mut src = context.create_buffer_source();
1031+
src.connect(&context.destination());
1032+
src.set_buffer(dirac);
1033+
src.start_at(0.);
1034+
});
10281035

10291036
let result = context.start_rendering_sync();
10301037
let channel = result.get_channel_data(0);
10311038

1032-
let mut expected = vec![0.; RENDER_QUANTUM_SIZE];
1033-
expected[0] = 1.;
1039+
let mut expected = vec![0.; 2 * RENDER_QUANTUM_SIZE];
1040+
expected[128] = 1.;
10341041

10351042
assert_float_eq!(channel[..], expected[..], abs_all <= 0.);
10361043
}

src/node/constant_source.rs

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ use std::any::Any;
33
use crate::context::{AudioContextRegistration, AudioParamId, BaseAudioContext};
44
use crate::param::{AudioParam, AudioParamDescriptor, AutomationRate};
55
use crate::render::{AudioParamValues, AudioProcessor, AudioRenderQuantum, RenderScope};
6-
use crate::RENDER_QUANTUM_SIZE;
6+
use crate::{assert_valid_time_value, RENDER_QUANTUM_SIZE};
77

88
use super::{AudioNode, AudioScheduledSourceNode, ChannelConfig};
99

@@ -100,6 +100,7 @@ impl AudioScheduledSourceNode for ConstantSourceNode {
100100
}
101101

102102
fn start_at(&mut self, when: f64) {
103+
assert_valid_time_value(when);
103104
self.registration.post_message(Schedule::Start(when));
104105
}
105106

@@ -109,6 +110,7 @@ impl AudioScheduledSourceNode for ConstantSourceNode {
109110
}
110111

111112
fn stop_at(&mut self, when: f64) {
113+
assert_valid_time_value(when);
112114
self.registration.post_message(Schedule::Stop(when));
113115
}
114116
}
@@ -268,16 +270,20 @@ mod tests {
268270

269271
#[test]
270272
fn test_start_in_the_past() {
271-
let mut context = OfflineAudioContext::new(1, 128, 48000.);
273+
let sample_rate = 48000.;
274+
let mut context = OfflineAudioContext::new(1, 2 * 128, sample_rate);
272275

273-
let mut src = context.create_constant_source();
274-
src.connect(&context.destination());
275-
src.start_at(-1.);
276+
context.suspend_sync((128. / sample_rate).into(), |context| {
277+
let mut src = context.create_constant_source();
278+
src.connect(&context.destination());
279+
src.start_at(0.);
280+
});
276281

277282
let buffer = context.start_rendering_sync();
278283
let channel = buffer.get_channel_data(0);
279284

280285
// 1rst block should be silence
281-
assert_float_eq!(channel[0..128], vec![1.; 128][..], abs_all <= 0.);
286+
assert_float_eq!(channel[0..128], vec![0.; 128][..], abs_all <= 0.);
287+
assert_float_eq!(channel[128..], vec![1.; 128][..], abs_all <= 0.);
282288
}
283289
}

src/node/oscillator.rs

Lines changed: 19 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ use crate::context::{AudioContextRegistration, AudioParamId, BaseAudioContext};
55
use crate::param::{AudioParam, AudioParamDescriptor, AutomationRate};
66
use crate::render::{AudioParamValues, AudioProcessor, AudioRenderQuantum, RenderScope};
77
use crate::PeriodicWave;
8-
use crate::RENDER_QUANTUM_SIZE;
8+
use crate::{assert_valid_time_value, RENDER_QUANTUM_SIZE};
99

1010
use super::{
1111
precomputed_sine_table, AudioNode, AudioScheduledSourceNode, ChannelConfig,
@@ -160,6 +160,7 @@ impl AudioScheduledSourceNode for OscillatorNode {
160160
}
161161

162162
fn start_at(&mut self, when: f64) {
163+
assert_valid_time_value(when);
163164
self.registration.post_message(Schedule::Start(when));
164165
}
165166

@@ -169,6 +170,7 @@ impl AudioScheduledSourceNode for OscillatorNode {
169170
}
170171

171172
fn stop_at(&mut self, when: f64) {
173+
assert_valid_time_value(when);
172174
self.registration.post_message(Schedule::Stop(when));
173175
}
174176
}
@@ -1147,15 +1149,18 @@ mod tests {
11471149
}
11481150

11491151
#[test]
1150-
fn osc_schedule_in_past() {
1152+
fn test_start_in_the_past() {
11511153
let freq = 8910.1;
11521154
let sample_rate = 44_100;
11531155

11541156
let mut context = OfflineAudioContext::new(1, sample_rate, sample_rate as f32);
1155-
let mut osc = context.create_oscillator();
1156-
osc.connect(&context.destination());
1157-
osc.frequency().set_value(freq);
1158-
osc.start_at(-1.);
1157+
1158+
context.suspend_sync(128. / sample_rate as f64, move |context| {
1159+
let mut osc = context.create_oscillator();
1160+
osc.connect(&context.destination());
1161+
osc.frequency().set_value(freq);
1162+
osc.start_at(0.);
1163+
});
11591164

11601165
let output = context.start_rendering_sync();
11611166
let result = output.get_channel_data(0);
@@ -1164,10 +1169,14 @@ mod tests {
11641169
let mut phase: f64 = 0.;
11651170
let phase_incr = freq as f64 / sample_rate as f64;
11661171

1167-
for _i in 0..sample_rate {
1168-
let sample = (phase * 2. * PI).sin();
1169-
expected.push(sample as f32);
1170-
phase += phase_incr;
1172+
for i in 0..sample_rate {
1173+
if i < 128 {
1174+
expected.push(0.);
1175+
} else {
1176+
let sample = (phase * 2. * PI).sin();
1177+
expected.push(sample as f32);
1178+
phase += phase_incr;
1179+
}
11711180
}
11721181

11731182
assert_float_eq!(result[..], expected[..], abs_all <= 1e-5);

src/node/panner.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -174,7 +174,7 @@ enum ControlMessage {
174174
#[inline(always)]
175175
fn assert_valid_channel_count(count: usize) {
176176
if count > 2 {
177-
panic!("NotSupportedError: PannerNode channel count cannot be greater than two");
177+
panic!("NotSupportedError - PannerNode channel count cannot be greater than two");
178178
}
179179
}
180180

@@ -189,7 +189,7 @@ fn assert_valid_channel_count(count: usize) {
189189
#[inline(always)]
190190
fn assert_valid_channel_count_mode(mode: ChannelCountMode) {
191191
if mode == ChannelCountMode::Max {
192-
panic!("NotSupportedError: PannerNode channel count mode cannot be set to max");
192+
panic!("NotSupportedError - PannerNode channel count mode cannot be set to max");
193193
}
194194
}
195195

src/node/stereo_panner.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ impl Default for StereoPannerOptions {
4444
#[inline(always)]
4545
fn assert_valid_channel_count(count: usize) {
4646
if count > 2 {
47-
panic!("NotSupportedError: StereoPannerNode channel count cannot be greater than two");
47+
panic!("NotSupportedError - StereoPannerNode channel count cannot be greater than two");
4848
}
4949
}
5050

@@ -59,7 +59,7 @@ fn assert_valid_channel_count(count: usize) {
5959
#[inline(always)]
6060
fn assert_valid_channel_count_mode(mode: ChannelCountMode) {
6161
if mode == ChannelCountMode::Max {
62-
panic!("NotSupportedError: StereoPannerNode channel count mode cannot be set to max");
62+
panic!("NotSupportedError - StereoPannerNode channel count mode cannot be set to max");
6363
}
6464
}
6565

0 commit comments

Comments
 (0)