Skip to content

Commit 84f136b

Browse files
committed
wip: fix #525
1 parent 3fed970 commit 84f136b

File tree

1 file changed

+170
-11
lines changed

1 file changed

+170
-11
lines changed

src/node/audio_buffer_source.rs

Lines changed: 170 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -411,6 +411,7 @@ impl AudioBufferSourceRenderer {
411411
if self.loop_state.start < 0. {
412412
self.loop_state.start = 0.;
413413
}
414+
414415
if self.loop_state.start > duration {
415416
self.loop_state.start = duration;
416417
}
@@ -455,8 +456,8 @@ impl AudioProcessor for AudioBufferSourceRenderer {
455456
let buffer = match &self.buffer {
456457
None => {
457458
output.make_silent();
458-
// #462 like the above arm, we can safely return tail_time false if this node has
459-
// no buffer set.
459+
// #462 like the above arm, we can safely return tail_time false
460+
// if this node has no buffer set.
460461
return false;
461462
}
462463
Some(b) => b,
@@ -468,6 +469,8 @@ impl AudioProcessor for AudioBufferSourceRenderer {
468469
end: loop_end,
469470
} = self.loop_state;
470471

472+
println!("infos: {loop_start}, {loop_end}");
473+
471474
// these will only be used if `loop_` is true, so no need for `Option`
472475
let mut actual_loop_start = 0.;
473476
let mut actual_loop_end = 0.;
@@ -479,6 +482,7 @@ impl AudioProcessor for AudioBufferSourceRenderer {
479482
let computed_playback_rate = (playback_rate * (detune / 1200.).exp2()) as f64;
480483

481484
let buffer_duration = buffer.duration();
485+
let buffer_length = buffer.length();
482486
// multiplier to be applied on `position` to tackle possible difference
483487
// between the context and buffer sample rates. As this is an edge case,
484488
// we just linearly interpolate, thus favoring performance vs quality
@@ -637,6 +641,11 @@ impl AudioProcessor for AudioBufferSourceRenderer {
637641
for (i, playback_info) in playback_infos.iter_mut().enumerate() {
638642
let current_time = block_time + i as f64 * dt;
639643

644+
// Handle following cases:
645+
// - we are before start time
646+
// - we are after stop time
647+
// - explicit duration (in buffer time reference) has been given and we have reached it
648+
// Note that checking against buffer duration is done below to handle looping
640649
if current_time < self.start_time
641650
|| current_time >= self.stop_time
642651
|| self.render_state.buffer_time_elapsed >= self.duration
@@ -696,10 +705,15 @@ impl AudioProcessor for AudioBufferSourceRenderer {
696705
let prev_frame_index = playhead_floored as usize; // can't be < 0.
697706
let k = (playhead - playhead_floored) as f32;
698707

699-
*playback_info = Some(PlaybackInfo {
700-
prev_frame_index,
701-
k,
702-
});
708+
// Due to how buffer_time is computed, we can still run into
709+
// floating point errors and try to access a non existing index
710+
// cf. test_end_of_file_slow_track_2
711+
if prev_frame_index < buffer_length {
712+
*playback_info = Some(PlaybackInfo {
713+
prev_frame_index,
714+
k,
715+
});
716+
}
703717
}
704718

705719
let time_incr = dt * computed_playback_rate;
@@ -726,10 +740,31 @@ impl AudioProcessor for AudioBufferSourceRenderer {
726740
}) => {
727741
// `prev_frame_index` cannot be out of bounds
728742
let prev_sample = buffer_channel[*prev_frame_index];
743+
744+
// @todo - stiching between loop points
729745
let next_sample = match buffer_channel.get(prev_frame_index + 1)
730746
{
731747
Some(val) => *val,
732-
None => 0.,
748+
None => {
749+
// @todo - this works only in "normal" case
750+
// - check invalid loop points
751+
// - playback_rate < 0.
752+
//
753+
// find first sample >= to start loop point
754+
if is_looping {
755+
let start_playhead = actual_loop_start * sample_rate;
756+
let start_index = if start_playhead.floor() == start_playhead {
757+
start_playhead as usize
758+
} else {
759+
start_playhead as usize + 1
760+
};
761+
println!("{actual_loop_start}, {actual_loop_end}");
762+
763+
buffer_channel[start_index]
764+
} else {
765+
0.
766+
}
767+
}
733768
};
734769

735770
(1. - k).mul_add(prev_sample, k * next_sample)
@@ -1395,6 +1430,45 @@ mod tests {
13951430
}
13961431
}
13971432

1433+
#[test]
1434+
fn test_slow_track_loop_mono() {
1435+
let sample_rate = 48_000.;
1436+
let len = RENDER_QUANTUM_SIZE * 4;
1437+
1438+
for buffer_len in [
1439+
RENDER_QUANTUM_SIZE / 2 - 1,
1440+
RENDER_QUANTUM_SIZE / 2,
1441+
RENDER_QUANTUM_SIZE / 2 + 1,
1442+
RENDER_QUANTUM_SIZE - 1,
1443+
RENDER_QUANTUM_SIZE,
1444+
RENDER_QUANTUM_SIZE + 1,
1445+
RENDER_QUANTUM_SIZE * 2 - 1,
1446+
RENDER_QUANTUM_SIZE * 2,
1447+
RENDER_QUANTUM_SIZE * 2 + 1,
1448+
] {
1449+
let mut context = OfflineAudioContext::new(1, len, sample_rate);
1450+
1451+
let mut dirac = context.create_buffer(1, buffer_len, sample_rate);
1452+
dirac.copy_to_channel(&[1.], 0);
1453+
1454+
let mut src = context.create_buffer_source();
1455+
src.connect(&context.destination());
1456+
src.set_loop(true);
1457+
src.set_buffer(dirac);
1458+
src.start_at(1. / sample_rate as f64);
1459+
1460+
let result = context.start_rendering_sync();
1461+
let channel = result.get_channel_data(0);
1462+
1463+
let mut expected = vec![0.; len];
1464+
for i in (1..len).step_by(buffer_len) {
1465+
expected[i] = 1.;
1466+
}
1467+
1468+
assert_float_eq!(channel[..], expected[..], abs_all <= 1e-9);
1469+
}
1470+
}
1471+
13981472
#[test]
13991473
fn test_fast_track_loop_stereo() {
14001474
let sample_rate = 48_000.;
@@ -1426,9 +1500,12 @@ mod tests {
14261500

14271501
let mut expected_left: Vec<f32> = vec![0.; len];
14281502
let mut expected_right = vec![0.; len];
1429-
for i in (0..len - 1).step_by(buffer_len) {
1503+
for i in (0..len).step_by(buffer_len) {
14301504
expected_left[i] = 1.;
1431-
expected_right[i + 1] = 1.;
1505+
1506+
if i < expected_right.len() - 1 {
1507+
expected_right[i + 1] = 1.;
1508+
}
14321509
}
14331510

14341511
assert_float_eq!(
@@ -1444,10 +1521,62 @@ mod tests {
14441521
}
14451522
}
14461523

1524+
#[test]
1525+
fn test_slow_track_loop_stereo() {
1526+
let sample_rate = 48_000.;
1527+
let len = RENDER_QUANTUM_SIZE * 4;
1528+
1529+
for buffer_len in [
1530+
RENDER_QUANTUM_SIZE / 2 - 1,
1531+
RENDER_QUANTUM_SIZE / 2,
1532+
RENDER_QUANTUM_SIZE / 2 + 1,
1533+
RENDER_QUANTUM_SIZE - 1,
1534+
RENDER_QUANTUM_SIZE,
1535+
RENDER_QUANTUM_SIZE + 1,
1536+
RENDER_QUANTUM_SIZE * 2 - 1,
1537+
RENDER_QUANTUM_SIZE * 2,
1538+
RENDER_QUANTUM_SIZE * 2 + 1,
1539+
] {
1540+
let mut context = OfflineAudioContext::new(2, len, sample_rate);
1541+
let mut dirac = context.create_buffer(2, buffer_len, sample_rate);
1542+
dirac.copy_to_channel(&[1.], 0);
1543+
dirac.copy_to_channel(&[0., 1.], 1);
1544+
1545+
let mut src = context.create_buffer_source();
1546+
src.connect(&context.destination());
1547+
src.set_loop(true);
1548+
src.set_buffer(dirac);
1549+
src.start_at(1. / sample_rate as f64);
1550+
1551+
let result = context.start_rendering_sync();
1552+
1553+
let mut expected_left: Vec<f32> = vec![0.; len];
1554+
let mut expected_right = vec![0.; len];
1555+
for i in (1..len).step_by(buffer_len) {
1556+
expected_left[i] = 1.;
1557+
1558+
if i < expected_right.len() - 1 {
1559+
expected_right[i + 1] = 1.;
1560+
}
1561+
}
1562+
1563+
assert_float_eq!(
1564+
result.get_channel_data(0)[..],
1565+
expected_left[..],
1566+
abs_all <= 1e-9
1567+
);
1568+
assert_float_eq!(
1569+
result.get_channel_data(1)[..],
1570+
expected_right[..],
1571+
abs_all <= 1e-9
1572+
);
1573+
}
1574+
}
1575+
14471576
#[test]
14481577
fn test_loop_hangs() {
14491578
let sample_rate = 48_000.;
1450-
let length = sample_rate as usize;
1579+
let length = sample_rate as usize / 10;
14511580
let mut context = OfflineAudioContext::new(1, length, sample_rate);
14521581

14531582
let mut buffer = context.create_buffer(1, 500, sample_rate);
@@ -1468,7 +1597,7 @@ mod tests {
14681597
let channel = result.get_channel_data(0);
14691598

14701599
assert_float_eq!(channel[0], 1.0, abs_all <= 0.);
1471-
assert_float_eq!(channel[1..], [0.; 48_000 - 1][..], abs_all <= 0.);
1600+
assert_float_eq!(channel[1..], vec![0.; length - 1][..], abs_all <= 0.);
14721601
}
14731602

14741603
#[test]
@@ -1501,6 +1630,36 @@ mod tests {
15011630
assert_float_eq!(channel[..], expected[..], abs_all <= 0.);
15021631
}
15031632

1633+
#[test]
1634+
// regression test for #452
1635+
// - fals track
1636+
// - duration not set so `self.duration` is `f64::MAX`
1637+
// - stop time is > buffer length
1638+
fn test_end_of_file_slow_track_2() {
1639+
let sample_rate = 48_000.;
1640+
let mut context = OfflineAudioContext::new(1, RENDER_QUANTUM_SIZE, sample_rate);
1641+
1642+
let mut buffer = context.create_buffer(1, 5, sample_rate);
1643+
let data = vec![1.; 1];
1644+
buffer.copy_to_channel(&data, 0);
1645+
1646+
let mut src = context.create_buffer_source();
1647+
src.connect(&context.destination());
1648+
src.set_buffer(buffer);
1649+
// play in fast track
1650+
src.start_at(1. / sample_rate as f64);
1651+
// stop after end of buffer but before the end of render quantum
1652+
src.stop_at(125. / sample_rate as f64);
1653+
1654+
let result = context.start_rendering_sync();
1655+
let channel = result.get_channel_data(0);
1656+
1657+
let mut expected = vec![0.; 128];
1658+
expected[1] = 1.;
1659+
1660+
assert_float_eq!(channel[..], expected[..], abs_all <= 0.);
1661+
}
1662+
15041663
#[test]
15051664
fn test_loop_no_restart_suspend() {
15061665
let sample_rate = 48_000.;

0 commit comments

Comments
 (0)