1use super::{Instrument, MAX_CHANNELS, Pitch, ScoreLimits};
3use crate::{Error, Limits, Result};
4
5pub const MAX_TEMPO_CHANGES: usize = 1024;
7
8#[derive(Clone, Copy, Debug, PartialEq)]
10pub struct TempoChange {
11 pub tick: u64,
12 pub bpm: f64,
14}
15
16#[derive(Clone, Copy, Debug)]
17struct Rational {
18 num: u128,
19 den: u128,
20}
21fn gcd(mut a: u128, mut b: u128) -> u128 {
22 while b != 0 {
23 (a, b) = (b, a % b);
24 }
25 a
26}
27fn timing_overflow() -> Error {
28 Error::limit("Exact prepared timing exceeds checked u128/u64 bounds")
29}
30impl Rational {
31 fn new(num: u128, den: u128) -> Self {
32 let g = gcd(num, den);
33 Self {
34 num: num / g,
35 den: den / g,
36 }
37 }
38 fn duration(ticks: u64, rate: u32, bpm: u32, tpq: u16) -> Self {
39 Self::new(
41 ticks as u128 * rate as u128 * 60000,
42 bpm as u128 * tpq as u128,
43 )
44 }
45 fn add(self, other: Self) -> Result<Self> {
46 let g = gcd(self.den, other.den);
47 let a = other.den / g;
48 let b = self.den / g;
49 let num = self
50 .num
51 .checked_mul(a)
52 .and_then(|n| n.checked_add(other.num.checked_mul(b)?))
53 .ok_or_else(timing_overflow)?;
54 let den = self.den.checked_mul(a).ok_or_else(timing_overflow)?;
55 Ok(Self::new(num, den))
56 }
57 fn frame(self) -> Result<u64> {
58 crate::timing::rounded_ratio(self.num, self.den).ok_or_else(timing_overflow)
59 }
60}
61
62#[derive(Clone, Debug)]
63struct Segment {
64 tick: u64,
65 bpm: u32,
66 start: Rational,
67 frame: u64,
68 beat_numerator: u128,
69}
70
71#[derive(Clone, Debug)]
81pub struct TempoMap {
82 rate: u32,
83 tpq: u16,
84 segments: Vec<Segment>,
85}
86impl TempoMap {
87 pub fn new(sample_rate: u32, ticks_per_quarter: u16, changes: &[TempoChange]) -> Result<Self> {
88 Limits::default().check(0, 1, sample_rate)?;
89 if changes.len() > MAX_TEMPO_CHANGES {
90 return Err(Error::limit("Too many prepared tempo changes"));
91 }
92 if changes.is_empty()
93 || changes[0].tick != 0
94 || !(1..=960).contains(&ticks_per_quarter)
95 || changes.windows(2).any(|p| p[0].tick >= p[1].tick)
96 {
97 return Err(Error::invalid(
98 "Tempo changes must start at zero and increase; TPQ must be 1..=960",
99 ));
100 }
101 let mut segments: Vec<Segment> = Vec::new();
102 segments
103 .try_reserve_exact(changes.len())
104 .map_err(|_| Error::limit("Cannot reserve tempo map"))?;
105 for change in changes {
106 let bpm = crate::timing::milli_bpm(change.bpm)?;
107 let (start, beat_numerator) = if let Some(previous) = segments.last() {
108 let start = previous.start.add(Rational::duration(
109 change.tick - previous.tick,
110 sample_rate,
111 previous.bpm,
112 ticks_per_quarter,
113 ))?;
114 let frame = start.frame()?;
115 (
116 start,
117 previous.beat_numerator
118 + (frame - previous.frame) as u128 * previous.bpm as u128,
119 )
120 } else {
121 (Rational::new(0, 1), 0)
122 };
123 segments.push(Segment {
124 tick: change.tick,
125 bpm,
126 start,
127 frame: start.frame()?,
128 beat_numerator,
129 });
130 }
131 Ok(Self {
132 rate: sample_rate,
133 tpq: ticks_per_quarter,
134 segments,
135 })
136 }
137 pub fn sample_rate(&self) -> u32 {
138 self.rate
139 }
140 pub fn ticks_per_quarter(&self) -> u16 {
141 self.tpq
142 }
143 pub fn frame_at(&self, tick: u64) -> Result<u64> {
147 let s = &self.segments[self.segments.partition_point(|s| s.tick <= tick) - 1];
148 s.start
149 .add(Rational::duration(
150 tick - s.tick,
151 self.rate,
152 s.bpm,
153 self.tpq,
154 ))?
155 .frame()
156 }
157 pub fn bpm_at(&self, tick: u64) -> f64 {
159 self.segments[self.segments.partition_point(|s| s.tick <= tick) - 1].bpm as f64 / 1000.0
160 }
161 pub(super) fn last_change_frame(&self) -> u64 {
162 self.segments.last().unwrap().frame
163 }
164 pub(super) fn clock(&self, frame: u64) -> BeatClock {
165 let s = &self.segments[self.segments.partition_point(|s| s.frame <= frame) - 1];
167 BeatClock {
168 numerator: s.beat_numerator + (frame - s.frame) as u128 * s.bpm as u128,
169 denominator: self.rate as u128 * 60000,
170 }
171 }
172}
173
174#[derive(Clone, Copy, Debug)]
176pub(super) struct BeatClock {
177 pub numerator: u128,
178 pub denominator: u128,
179}
180
181#[derive(Clone, Copy, Debug)]
185pub struct FrameNote {
186 pub on: u64,
187 pub off: u64,
188 pub pitch: Pitch,
189 pub instrument: usize,
190 pub volume: u8,
192 pub velocity: u8,
194}
195impl FrameNote {
196 pub fn new(on: u64, off: u64, pitch: Pitch, instrument: usize) -> Self {
197 Self {
198 on,
199 off,
200 pitch,
201 instrument,
202 volume: 15,
203 velocity: 127,
204 }
205 }
206}
207
208#[derive(Clone, Debug, Default)]
213pub struct FrameScore {
214 pub instruments: Vec<Instrument>,
215 pub tracks: Vec<Vec<FrameNote>>,
216 pub end_frame: u64,
219}
220impl FrameScore {
221 pub(super) fn validate(&self, map: &TempoMap) -> Result<()> {
222 let limits = ScoreLimits::default();
223 if self.tracks.len() > MAX_CHANNELS || self.instruments.len() > 256 {
224 return Err(Error::limit(
225 "Prepared synth needs at most eight tracks and 256 instruments",
226 ));
227 }
228 if self.end_frame as u128 > map.rate as u128 * 1200 {
229 return Err(Error::limit("Prepared synth body exceeds 1200 seconds"));
230 }
231 if map.last_change_frame() > self.end_frame {
232 return Err(Error::invalid(
233 "Tempo changes extend beyond the prepared body",
234 ));
235 }
236 let mut notes = 0usize;
237 for track in &self.tracks {
238 notes = notes
239 .checked_add(track.len())
240 .ok_or_else(|| Error::limit("Prepared note count overflow"))?;
241 if notes > limits.max_notes {
242 return Err(Error::limit("Too many prepared notes"));
243 }
244 if track.windows(2).any(|p| p[0].on > p[1].on) {
245 return Err(Error::invalid("Prepared notes must be onset-ordered"));
246 }
247 for note in track {
248 if note.on >= note.off
249 || note.off > self.end_frame
250 || note.instrument >= self.instruments.len()
251 || note.volume > 15
252 || note.velocity > 127
253 {
254 return Err(Error::invalid(
255 "Invalid prepared note bounds, instrument, volume or velocity",
256 ));
257 }
258 }
259 }
260 for instrument in &self.instruments {
261 instrument.validate()?;
262 }
263 Ok(())
264 }
265}