diff --git a/crates/time/src/format.rs b/crates/time/src/format.rs index a5f4a1a..b595031 100644 --- a/crates/time/src/format.rs +++ b/crates/time/src/format.rs @@ -143,6 +143,15 @@ mod tests { assert_eq!(format_position(12345, TimeFormat::Samples, sr, &map, rate, 0), "12345"); } + #[test] + fn minimum_bar_parses_without_overflow() { + let (sr, map, rate) = ctx(); + let p = |t: &str| parse_position(t, TimeFormat::BarsBeats, sr, &map, rate, 0); + assert!(p("-9223372036854775808|01|000").is_ok_and(|s| s < 0)); + assert_eq!(p("2|3|000").ok(), Some(144_000)); + assert_eq!(p("0|1|000").ok(), Some(-96_000)); + } + #[test] fn parse_round_trips() { let (sr, map, rate) = ctx(); diff --git a/crates/time/src/tempo.rs b/crates/time/src/tempo.rs index 5b87a3a..b9948f9 100644 --- a/crates/time/src/tempo.rs +++ b/crates/time/src/tempo.rs @@ -200,7 +200,7 @@ impl TempoMap { let mut cur_tick = 0i64; let first = self.meter_at_tick(0); if bar < 1 { - return (bar - 1).saturating_mul(first.ticks_per_bar()); + return bar.saturating_sub(1).saturating_mul(first.ticks_per_bar()); } for (i, m) in self.meters.iter().enumerate() { let next_tick = self.meters.get(i + 1).map(|n| n.tick); @@ -288,6 +288,13 @@ mod tests { assert_eq!(m.bar_beat_at(24_000, sr), BarBeat { bar: 1, beat: 2, tick: 0 }); } + #[test] + fn minimum_bar_saturates_instead_of_overflowing() { + let m = TempoMap::default(); + assert_eq!(m.bar_start_tick(i64::MIN), i64::MIN); + assert_eq!(m.bar_start_tick(0), -TICKS_PER_QUARTER * 4); + } + #[test] fn tempo_change_is_integrated() { let mut m = TempoMap::default();