Merge pull request #577 from Silvertree2010/fix/h772-midi-notes-move-overflows-panics

Saturate midi.notes_move tick and pitch arithmetic
This commit is contained in:
Brandon Thomas
2026-10-19 02:54:16 -04:00
committed by GitHub
+17 -2
View File
@@ -126,8 +126,8 @@ fn notes_move(e: &mut Engine, p: &Value) -> Result<Value> {
let seq = seq_mut(e, id, "midi.notes_move")?;
for (i, n) in seq.notes.iter_mut().enumerate() {
if idx.contains(&i) {
n.start = (n.start + dt).max(0);
n.pitch = (i64::from(n.pitch) + ds).clamp(0, 127) as u8;
n.start = n.start.saturating_add(dt).max(0);
n.pitch = i64::from(n.pitch).saturating_add(ds).clamp(0, 127) as u8;
}
}
seq.sort();
@@ -157,6 +157,21 @@ mod tests {
assert!(e.execute("midi.note_edit", &json!({"clip": clip, "index": 99})).is_err());
}
#[test]
fn notes_move_huge_params_do_not_overflow() {
let mut e = Engine::default();
let t = e.session_mut().add_track(TrackKind::Instrument, ChannelFormat::Stereo, Some("Keys"));
let r = e.execute("midi.new_clip", &json!({"track": t.0})).unwrap();
let clip = r["clip"].as_u64().unwrap();
e.execute("midi.note_add", &json!({"clip": clip, "pitch": 60, "start_ticks": 480})).unwrap();
e.execute("midi.notes_move", &json!({"clip": clip, "indices": [0], "ticks": i64::MAX})).unwrap();
let n = e.execute("midi.notes", &json!({"clip": clip})).unwrap();
assert_eq!(n["notes"][0]["start"], i64::MAX);
e.execute("midi.notes_move", &json!({"clip": clip, "indices": [0], "semitones": i64::MAX})).unwrap();
let n = e.execute("midi.notes", &json!({"clip": clip})).unwrap();
assert_eq!(n["notes"][0]["pitch"], 127);
}
#[test]
fn rejects_audio_tracks() {
let mut e = Engine::default();