diff --git a/crates/engine/src/cmd/event_more.rs b/crates/engine/src/cmd/event_more.rs index 783ddcf5..7e7d3e86 100644 --- a/crates/engine/src/cmd/event_more.rs +++ b/crates/engine/src/cmd/event_more.rs @@ -578,8 +578,8 @@ fn extract_chords(e: &mut Engine, p: &Value) -> Result { let base = s.tempo.samples_to_ticks(c.start, s.sample_rate); let end = s.tempo.samples_to_ticks(c.end(), s.sample_rate); for n in &sequence.notes { - let a = base + n.start; - let b = (base + n.end()).min(end); + let a = base.saturating_add(n.start); + let b = base.saturating_add(n.end()).min(end); if b > a { notes.push((a, b, n.pitch)); } @@ -967,6 +967,17 @@ mod tests { assert!(r["bpm"].as_f64().is_some()); } + #[test] + fn extract_chords_survives_huge_note_start() { + let (mut e, t) = midi_engine(); + let r = e.execute("midi.new_clip", &json!({"track": t.0, "start": 48_000, "end": 96_000})).unwrap(); + let clip = r["clip"].as_u64().unwrap(); + e.execute("midi.note_add", &json!({"clip": clip, "pitch": 60, "start_ticks": i64::MAX, "length_ticks": 480})).unwrap(); + e.execute("edit.select", &json!({"tracks": [t.0], "start": 0, "end": 96_000})).unwrap(); + let r = e.execute("event.extract_chords", &json!({})); + assert!(r.is_ok(), "{r:?}"); + } + #[test] fn retro_record_huge_start_ticks_stay_in_range() { for tk in [i64::MAX, 1_000_000_000_000_000] {