diff --git a/crates/dsp/src/offline.rs b/crates/dsp/src/offline.rs index a5be2cd6..89e1e588 100644 --- a/crates/dsp/src/offline.rs +++ b/crates/dsp/src/offline.rs @@ -465,7 +465,8 @@ pub enum SpeedRampChange { #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum SpeedRampSelection { /// The result is as long as the input. Two thirds of the input are played: the first two - /// thirds for Speed Up, the last two thirds for Slow Down. + /// thirds for Slow Down, the last two thirds for Speed Up, so each joins its neighbour + /// without a jump. FitTo, /// All of the input is played, and the result is 150 % of its length. Extend, @@ -482,7 +483,7 @@ pub fn speed_ramp(ch: &[Vec], change: SpeedRampChange, selection: SpeedRamp SpeedRampSelection::Extend => ((len * 1.5).round(), 0.0, len), SpeedRampSelection::FitTo => { let played = len * 2.0 / 3.0; - (len, if change == SpeedRampChange::SlowDown { len - played } else { 0.0 }, played) + (len, if change == SpeedRampChange::SlowDown { 0.0 } else { len - played }, played) } }; let out_len = (total as usize).min(MAX_OFFLINE_SAMPLES); diff --git a/crates/dsp/tests/it/speed_ramp.rs b/crates/dsp/tests/it/speed_ramp.rs index 0d035418..a62661cd 100644 --- a/crates/dsp/tests/it/speed_ramp.rs +++ b/crates/dsp/tests/it/speed_ramp.rs @@ -24,21 +24,22 @@ fn speed_ramp_fit_to_keeps_the_length_and_plays_two_thirds() { let x = vec![ramp(len)]; let slow = offline::speed_ramp(&x, SpeedRampChange::SlowDown, SpeedRampSelection::FitTo, false); assert_eq!(slow[0].len(), len); - // Slow Down plays the last two thirds: it starts at normal speed one third in and stops at the end. + // Slow Down plays the first two thirds: it starts at normal speed at the start and stops at the end. for u in [0.0, 0.25, 0.5, 0.75, 0.9] { let n = (u * len as f64) as usize; - near(slow[0][n], 1.0 / 3.0 + 2.0 / 3.0 * slow_down_pos(u), &format!("slow down at {u}")); + near(slow[0][n], 2.0 / 3.0 * slow_down_pos(u), &format!("slow down at {u}")); } - let step = |a: &[f32], n: usize| f64::from(a[n + 1] - a[n]) * len as f64; + // The speed over 50 samples: a one-sample step inside the input also shows the interpolation's ripple. + let step = |a: &[f32], n: usize| f64::from(a[n + 50] - a[n]) * len as f64 / 50.0; assert!((step(&slow[0], 100) - 1.0).abs() < 0.01, "slow down starts at normal speed: {}", step(&slow[0], 100)); assert!(step(&slow[0], len - 200) < 0.02, "slow down ends near a stop: {}", step(&slow[0], len - 200)); - // Speed Up plays the first two thirds: it starts from a stop and reaches normal speed at the end. + // Speed Up plays the last two thirds: it starts from a stop and reaches normal speed at the end. let fast = offline::speed_ramp(&x, SpeedRampChange::SpeedUp, SpeedRampSelection::FitTo, false); assert_eq!(fast[0].len(), len); for u in [0.1, 0.25, 0.5, 0.75, 0.99] { let n = (u * len as f64) as usize; - near(fast[0][n], 2.0 / 3.0 * speed_up_pos(u), &format!("speed up at {u}")); + near(fast[0][n], 1.0 / 3.0 + 2.0 / 3.0 * speed_up_pos(u), &format!("speed up at {u}")); } assert!(step(&fast[0], 200) < 0.02, "speed up starts near a stop: {}", step(&fast[0], 200)); assert!((step(&fast[0], len - 100) - 1.0).abs() < 0.01, "speed up ends at normal speed: {}", step(&fast[0], len - 100)); diff --git a/crates/engine/src/cmd/audiosuite.rs b/crates/engine/src/cmd/audiosuite.rs index f77681c6..52f70964 100644 --- a/crates/engine/src/cmd/audiosuite.rs +++ b/crates/engine/src/cmd/audiosuite.rs @@ -274,10 +274,10 @@ mod tests { (c.start, c.length, c.name, audio) }; // The defaults: Slow Down, Fit To, Fades On. Halfway through, the read position is 11/16 of the - // way through the last two thirds, and the gain is the speed there, 3/4. + // way through the first two thirds, and the gain is the speed there, 3/4. let (start, length, name, audio) = run(json!({})); assert_eq!((start, length, name.as_str()), (480, 4800, "ramp-speeramp")); - let want = (1.0 / 3.0 + 2.0 / 3.0 * 11.0 / 16.0) * 0.75; + let want = (2.0 / 3.0 * 11.0 / 16.0) * 0.75; assert!((audio[2400] - want).abs() < 2e-3, "slow down halfway: got {}, want {want}", audio[2400]); assert!(audio[4790].abs() < 0.01, "the fade-out ends near silence, got {}", audio[4790]); // Speed Up with Extend and no fades: all of the clip is played over 150 % of its length, and the