use record_player::{AcousticConfig, ScratchAcousticDsp};
fn simulate_spinback(
grip: f64,
slipmat_response: f64,
dwell_seconds: f64,
finger_rate: f64,
reported_rate_lags: bool,
) {
let sr = 48_000.0;
let mut dsp = ScratchAcousticDsp::new_native(sr, AcousticConfig::default()).unwrap();
let source = vec![0.25_f32; 48_000 * 30];
dsp.replace_window_native(&[source.as_slice()], sr, None)
.unwrap();
dsp.set_effects(false, false);
let _ = slipmat_response; dsp.start();
dsp.set_transport(false, 1.0, 0.0, 0.0);
for _ in 0..(48_000 / 256) {
dsp.render(256, 1);
}
let read_position = dsp.position();
dsp.render((sr * 0.015) as u32, 1);
dsp.set_transport(true, 1.0, 0.0, grip);
dsp.set_motion(read_position, 0.0, 0.22);
let event_frames = (sr / 120.0) as u32;
let dwell_events = (dwell_seconds * 120.0).round() as usize;
let mut host_position = read_position;
for _ in 0..dwell_events {
dsp.render(event_frames, 1);
dsp.set_transport(true, 1.0, 0.0, grip);
dsp.set_motion(host_position, 0.0, 0.0);
}
let contact_position = dsp.position();
println!(
"grip={grip} slipmat={slipmat_response} dwell={dwell_seconds}s finger_rate={finger_rate} lag={reported_rate_lags}"
);
println!(
" forward creep after contact: {:.0} frames ({:.1} ms of audio)",
contact_position - read_position,
(contact_position - read_position) / 48.0
);
let frames_per_event = finger_rate * sr / 120.0;
let mut reversal_ms: Option<f64> = None;
let mut half_speed_ms: Option<f64> = None;
let mut last_position = contact_position;
for event in 1..=120 {
host_position -= frames_per_event;
let reported_rate = if reported_rate_lags && event <= 12 {
-finger_rate * f64::from(event) / 12.0
} else {
-finger_rate
};
dsp.set_transport(true, 1.0, reported_rate, grip);
dsp.set_motion(host_position, reported_rate, 0.0);
dsp.render(event_frames, 1);
let position = dsp.position();
let rate = (position - last_position) / f64::from(event_frames);
let ms = event as f64 * 1_000.0 / 120.0;
if reversal_ms.is_none() && position < last_position {
reversal_ms = Some(ms);
}
if half_speed_ms.is_none() && rate <= -0.5 * finger_rate {
half_speed_ms = Some(ms);
}
if event <= 24 {
println!(
" t={ms:6.1}ms rendered_rate={rate:8.4} pos-contact={:9.1} recoveries={}",
position - contact_position,
0
);
}
last_position = position;
}
println!(
" => reversal after {:?} ms, half finger speed after {:?} ms\n",
reversal_ms, half_speed_ms
);
}
#[test]
fn spinback_latency_report() {
simulate_spinback(1.0, 1.0, 0.0, 1.0, false);
simulate_spinback(1.0, 1.0, 0.0, 1.0, true);
simulate_spinback(1.0, 1.0, 0.0, 0.15, true);
simulate_spinback(1.0, 1.0, 0.25, 1.0, true);
simulate_spinback(0.45, 1.0, 0.0, 1.0, false);
simulate_spinback(0.45, 0.5, 0.0, 1.0, false);
simulate_spinback(0.45, 0.0, 0.0, 1.0, false);
}