Skip to content

Commit 7ce4f09

Browse files
authored
Merge pull request #486 from b-ma/fix/delay-node
Fix - DelayNode ring buffer size
2 parents 87d3eae + 79e7d59 commit 7ce4f09

File tree

1 file changed

+144
-100
lines changed

1 file changed

+144
-100
lines changed

src/node/delay.rs

Lines changed: 144 additions & 100 deletions
Original file line numberDiff line numberDiff line change
@@ -197,24 +197,14 @@ impl DelayNode {
197197
"NotSupportedError - maxDelayTime MUST be greater than zero and less than three minutes",
198198
);
199199

200-
// we internally clamp max delay to quantum duration because the current
201-
// implementation doesn't allow sub-quantum delays. Later, this will
202-
// ensure that even if the declared max_delay_time and max_delay are smaller
203-
// than quantum duration, the node, if found in a loop, will gracefully
204-
// fallback to the clamped behavior. (e.g. we ensure that ring buffer size
205-
// is always >= 2)
206-
let quantum_duration = 1. / sample_rate * RENDER_QUANTUM_SIZE as f64;
207-
let max_delay_time = options.max_delay_time.max(quantum_duration);
208-
209-
// allocate large enough buffer to store all delayed samples
210-
//
211-
// we add 1 here so that in edge cases where num_samples is a multiple of
212-
// RENDER_QUANTUM_SIZE and delay_time == max_delay_time we are sure to
213-
// enough room for history. (see. test_max_delay_multiple_of_quantum_size)
214-
let num_samples = max_delay_time * sample_rate + 1.;
200+
// Allocate large enough ring buffer to store all delayed samples.
201+
// We add one extra slot in the ring buffer so that reader never reads the
202+
// same entry in history as the writer, even if `delay_time == max_delay_time`
203+
// of if `max_delay_time < quantum duration`
204+
let max_delay_time = options.max_delay_time;
215205
let num_quanta =
216-
(num_samples.ceil() as usize + RENDER_QUANTUM_SIZE - 1) / RENDER_QUANTUM_SIZE;
217-
let ring_buffer = Vec::with_capacity(num_quanta);
206+
(max_delay_time * sample_rate / RENDER_QUANTUM_SIZE as f64).ceil() as usize;
207+
let ring_buffer = Vec::with_capacity(num_quanta + 1);
218208

219209
let shared_ring_buffer = Rc::new(RefCell::new(ring_buffer));
220210
let shared_ring_buffer_clone = Rc::clone(&shared_ring_buffer);
@@ -707,60 +697,59 @@ mod tests {
707697
}
708698

709699
#[test]
710-
fn test_sub_sample_accurate() {
711-
{
712-
let delay_in_samples = 128.5;
713-
let sample_rate = 48_000.;
714-
let mut context = OfflineAudioContext::new(1, 256, sample_rate);
700+
fn test_sub_sample_accurate_1() {
701+
let delay_in_samples = 128.5;
702+
let sample_rate = 48_000.;
703+
let mut context = OfflineAudioContext::new(1, 256, sample_rate);
715704

716-
let delay = context.create_delay(2.);
717-
delay.delay_time.set_value(delay_in_samples / sample_rate);
718-
delay.connect(&context.destination());
705+
let delay = context.create_delay(2.);
706+
delay.delay_time.set_value(delay_in_samples / sample_rate);
707+
delay.connect(&context.destination());
719708

720-
let mut dirac = context.create_buffer(1, 1, sample_rate);
721-
dirac.copy_to_channel(&[1.], 0);
709+
let mut dirac = context.create_buffer(1, 1, sample_rate);
710+
dirac.copy_to_channel(&[1.], 0);
722711

723-
let mut src = context.create_buffer_source();
724-
src.connect(&delay);
725-
src.set_buffer(dirac);
726-
src.start_at(0.);
712+
let mut src = context.create_buffer_source();
713+
src.connect(&delay);
714+
src.set_buffer(dirac);
715+
src.start_at(0.);
727716

728-
let result = context.start_rendering_sync();
729-
let channel = result.get_channel_data(0);
717+
let result = context.start_rendering_sync();
718+
let channel = result.get_channel_data(0);
730719

731-
let mut expected = vec![0.; 256];
732-
expected[128] = 0.5;
733-
expected[129] = 0.5;
720+
let mut expected = vec![0.; 256];
721+
expected[128] = 0.5;
722+
expected[129] = 0.5;
734723

735-
assert_float_eq!(channel[..], expected[..], abs_all <= 0.00001);
736-
}
724+
assert_float_eq!(channel[..], expected[..], abs_all <= 0.00001);
725+
}
737726

738-
{
739-
let delay_in_samples = 128.8;
740-
let sample_rate = 48_000.;
741-
let mut context = OfflineAudioContext::new(1, 256, sample_rate);
727+
#[test]
728+
fn test_sub_sample_accurate_2() {
729+
let delay_in_samples = 128.8;
730+
let sample_rate = 48_000.;
731+
let mut context = OfflineAudioContext::new(1, 256, sample_rate);
742732

743-
let delay = context.create_delay(2.);
744-
delay.delay_time.set_value(delay_in_samples / sample_rate);
745-
delay.connect(&context.destination());
733+
let delay = context.create_delay(2.);
734+
delay.delay_time.set_value(delay_in_samples / sample_rate);
735+
delay.connect(&context.destination());
746736

747-
let mut dirac = context.create_buffer(1, 1, sample_rate);
748-
dirac.copy_to_channel(&[1.], 0);
737+
let mut dirac = context.create_buffer(1, 1, sample_rate);
738+
dirac.copy_to_channel(&[1.], 0);
749739

750-
let mut src = context.create_buffer_source();
751-
src.connect(&delay);
752-
src.set_buffer(dirac);
753-
src.start_at(0.);
740+
let mut src = context.create_buffer_source();
741+
src.connect(&delay);
742+
src.set_buffer(dirac);
743+
src.start_at(0.);
754744

755-
let result = context.start_rendering_sync();
756-
let channel = result.get_channel_data(0);
745+
let result = context.start_rendering_sync();
746+
let channel = result.get_channel_data(0);
757747

758-
let mut expected = vec![0.; 256];
759-
expected[128] = 0.2;
760-
expected[129] = 0.8;
748+
let mut expected = vec![0.; 256];
749+
expected[128] = 0.2;
750+
expected[129] = 0.8;
761751

762-
assert_float_eq!(channel[..], expected[..], abs_all <= 1e-5);
763-
}
752+
assert_float_eq!(channel[..], expected[..], abs_all <= 1e-5);
764753
}
765754

766755
#[test]
@@ -935,6 +924,60 @@ mod tests {
935924
assert_float_eq!(channel[..], expected[..], abs_all <= 0.);
936925
}
937926

927+
// reproduce wpt tests from
928+
// - the-delaynode-interface/delaynode-max-default-delay.html
929+
// - the-delaynode-interface/delaynode-max-nondefault-delay.html
930+
#[test]
931+
fn test_max_delay() {
932+
use std::f32::consts::PI;
933+
934+
for &delay_time_seconds in [1., 1.5].iter() {
935+
let sample_rate = 44100.0;
936+
let render_length = 4 * sample_rate as usize;
937+
938+
let mut context = OfflineAudioContext::new(1, render_length, sample_rate);
939+
940+
// create 2 seconds tone buffer at 20Hz
941+
let tone_frequency = 20.;
942+
let tone_length_seconds = 2.;
943+
let tone_length = tone_length_seconds as usize * sample_rate as usize;
944+
let mut tone_buffer = context.create_buffer(1, tone_length, sample_rate);
945+
let tone_data = tone_buffer.get_channel_data_mut(0);
946+
947+
for (i, s) in tone_data.iter_mut().enumerate() {
948+
*s = (tone_frequency * 2.0 * PI * i as f32 / sample_rate).sin();
949+
}
950+
951+
let mut buffer_source = context.create_buffer_source();
952+
buffer_source.set_buffer(tone_buffer.clone());
953+
954+
let delay = context.create_delay(delay_time_seconds); // max delay defaults to 1 second
955+
delay.delay_time.set_value(delay_time_seconds as f32);
956+
957+
buffer_source.connect(&delay);
958+
delay.connect(&context.destination());
959+
buffer_source.start_at(0.);
960+
961+
let output = context.start_rendering_sync();
962+
let source = tone_buffer.get_channel_data(0);
963+
let rendered = output.get_channel_data(0);
964+
965+
let delay_time_frames = (delay_time_seconds * sample_rate as f64) as usize;
966+
let tone_length_frames = (tone_length_seconds * sample_rate as f64) as usize;
967+
968+
for (i, s) in rendered.iter().enumerate() {
969+
if i < delay_time_frames {
970+
assert_eq!(*s, 0.);
971+
} else if i >= delay_time_frames && i < delay_time_frames + tone_length_frames {
972+
let j = i - delay_time_frames;
973+
assert_eq!(*s, source[j]);
974+
} else {
975+
assert_eq!(*s, 0.);
976+
}
977+
}
978+
}
979+
}
980+
938981
#[test]
939982
fn test_max_delay_smaller_than_quantum_size() {
940983
// regression test that even if the declared max_delay_time is smaller than
@@ -975,64 +1018,65 @@ mod tests {
9751018
}
9761019
}
9771020

1021+
// test_max_delay_multiple_of_quantum_size_x
1022+
// are regression test that delay node has always enough internal buffer size
1023+
// when max_delay is a multiple of quantum size and delay == max_delay.
1024+
// This bug only occurs when the Writer is called before than the Reader,
1025+
// which is the case when not in a loop
9781026
#[test]
979-
fn test_max_delay_multiple_of_quantum_size() {
980-
// regression test that delay node has always enough internal buffer size
981-
// when max_delay is a multiple of quantum size and delay == max_delay.
982-
// This bug only occurs when the Writer is called before than the Reader,
983-
// which is the case when not in a loop
984-
1027+
fn test_max_delay_multiple_of_quantum_size_1() {
9851028
// set delay and max delay time exactly 1 render quantum
986-
{
987-
let sample_rate = 48_000.;
988-
let mut context = OfflineAudioContext::new(1, 256, sample_rate);
1029+
let sample_rate = 48_000.;
1030+
let mut context = OfflineAudioContext::new(1, 256, sample_rate);
9891031

990-
let delay = context.create_delay(1.);
991-
delay.delay_time.set_value(128. / sample_rate);
992-
delay.connect(&context.destination());
1032+
let max_delay = 128. / sample_rate;
1033+
let delay = context.create_delay(max_delay.into());
1034+
delay.delay_time.set_value(max_delay);
1035+
delay.connect(&context.destination());
9931036

994-
let mut dirac = context.create_buffer(1, 1, sample_rate);
995-
dirac.copy_to_channel(&[1.], 0);
1037+
let mut dirac = context.create_buffer(1, 1, sample_rate);
1038+
dirac.copy_to_channel(&[1.], 0);
9961039

997-
let mut src = context.create_buffer_source();
998-
src.connect(&delay);
999-
src.set_buffer(dirac);
1000-
src.start_at(0.);
1040+
let mut src = context.create_buffer_source();
1041+
src.connect(&delay);
1042+
src.set_buffer(dirac);
1043+
src.start_at(0.);
10011044

1002-
let result = context.start_rendering_sync();
1003-
let channel = result.get_channel_data(0);
1045+
let result = context.start_rendering_sync();
1046+
let channel = result.get_channel_data(0);
10041047

1005-
let mut expected = vec![0.; 256];
1006-
expected[128] = 1.;
1048+
let mut expected = vec![0.; 256];
1049+
expected[128] = 1.;
10071050

1008-
assert_float_eq!(channel[..], expected[..], abs_all <= 1e-5);
1009-
}
1051+
assert_float_eq!(channel[..], expected[..], abs_all <= 1e-5);
1052+
}
10101053

1054+
#[test]
1055+
fn test_max_delay_multiple_of_quantum_size_2() {
10111056
// set delay and max delay time exactly 2 render quantum
1012-
{
1013-
let sample_rate = 48_000.;
1014-
let mut context = OfflineAudioContext::new(1, 3 * 128, sample_rate);
1057+
let sample_rate = 48_000.;
1058+
let mut context = OfflineAudioContext::new(1, 3 * 128, sample_rate);
10151059

1016-
let delay = context.create_delay(2.);
1017-
delay.delay_time.set_value(128. * 2. / sample_rate);
1018-
delay.connect(&context.destination());
1060+
let max_delay = 128. * 2. / sample_rate;
1061+
let delay = context.create_delay(max_delay.into());
1062+
delay.delay_time.set_value(max_delay);
1063+
delay.connect(&context.destination());
10191064

1020-
let mut dirac = context.create_buffer(1, 1, sample_rate);
1021-
dirac.copy_to_channel(&[1.], 0);
1065+
let mut dirac = context.create_buffer(1, 1, sample_rate);
1066+
dirac.copy_to_channel(&[1.], 0);
10221067

1023-
let mut src = context.create_buffer_source();
1024-
src.connect(&delay);
1025-
src.set_buffer(dirac);
1026-
src.start_at(0.);
1068+
let mut src = context.create_buffer_source();
1069+
src.connect(&delay);
1070+
src.set_buffer(dirac);
1071+
src.start_at(0.);
10271072

1028-
let result = context.start_rendering_sync();
1029-
let channel = result.get_channel_data(0);
1073+
let result = context.start_rendering_sync();
1074+
let channel = result.get_channel_data(0);
10301075

1031-
let mut expected = vec![0.; 3 * 128];
1032-
expected[256] = 1.;
1076+
let mut expected = vec![0.; 3 * 128];
1077+
expected[256] = 1.;
10331078

1034-
assert_float_eq!(channel[..], expected[..], abs_all <= 1e-5);
1035-
}
1079+
assert_float_eq!(channel[..], expected[..], abs_all <= 1e-5);
10361080
}
10371081

10381082
#[test]

0 commit comments

Comments
 (0)