1use std::{rc::Rc, ops::Index, collections::HashSet};
2
3use super::{note::Note, have_start_tick::HaveBaseStartTick, duration::{Duration, Denominator}};
4use gcd::Gcd;
5
6pub fn tuplize(notes: Vec<Rc<Note>>) -> Vec<Rc<Note>> {
7 if notes.is_empty() { return vec![]; }
8 let work = &mut notes.clone();
9 let (min_unit, sorted) = sort_by_start_tick(work);
10
11 if let Some(min_unit) = min_unit {
12 let start_tick = work[0].base_start_tick();
13 let (total_tick, total_unit) = total_tick_unit(&sorted);
14 let denominator = total_unit / min_unit;
15 if denominator < 2 { return notes; }
16 let total_tick = total_tick / denominator * 2;
17 let denominator = Denominator::from_value(denominator as u8).unwrap();
18 let mut u = 0;
19 let mut ret = Vec::with_capacity(work.len());
20
21 for e in sorted.iter() {
22 match e {
23 TupleElem::None => {},
24 TupleElem::Some { start_tick: _, notes, min_duration: _ } => {
25 for n in notes.iter() {
26 let mut note = (**n).clone();
27 note.base_start_tick = start_tick + (total_tick * u / total_unit);
28 note.duration = n.duration.with_denominator(denominator);
29 u += numerator_unit(note.duration);
30 ret.push(Rc::new(note));
31 }
32 },
33 }
34 }
35
36 ret
37 } else {
38 vec![]
39 }
40}
41enum TupleElem {
42 None,
43 Some {
44 start_tick: u32,
45 notes: Vec<Rc<Note>>,
46 min_duration: Duration,
47 }
48}
49enum SingleOrDouble<T> {
50 Single(T),
51 Double(T, T),
52}
53
54impl Index<usize> for TupleElem {
55 type Output = Rc<Note>;
56
57 fn index(&self, index: usize) -> &Self::Output {
58 match self {
59 TupleElem::None => panic!("Index(={}) out of bounds.", index),
60 TupleElem::Some { start_tick: _, notes, min_duration: _ } => ¬es[index],
61 }
62 }
63}
64
65impl TupleElem {
66 #[cfg(test)]
67 fn len(&self) -> usize {
68 match self {
69 TupleElem::None => 0,
70 TupleElem::Some { start_tick: _, notes, min_duration: _ } => notes.len(),
71 }
72 }
73
74 #[cfg(test)]
75 fn contains(&self, note: &Rc<Note>) -> bool {
76 match self {
77 TupleElem::None => false,
78 TupleElem::Some { start_tick: _, notes, min_duration: _ } => notes.contains(note),
79 }
80 }
81
82 fn tick_length(&self) -> u32 {
83 match self {
84 TupleElem::None => 0,
85 TupleElem::Some { start_tick: _, notes: _, min_duration } => min_duration.tick_length(),
86 }
87 }
88
89 fn unit(&self) -> Option<u32> {
90 match self {
91 TupleElem::None => None,
92 TupleElem::Some { start_tick: _, notes: _, min_duration } => Some(numerator_unit(*min_duration)),
93 }
94 }
95
96 fn add(self, note: Rc<Note>) -> SingleOrDouble<Self> {
97 let duration = note.duration.with_denominator(Denominator::from_value(2).unwrap());
98 match self {
99 TupleElem::None =>
100 SingleOrDouble::Single(TupleElem::Some {
101 start_tick: note.base_start_tick(),
102 min_duration: duration,
103 notes: vec![note],
104 }),
105 TupleElem::Some { start_tick, mut notes, min_duration } => {
106 if start_tick != note.base_start_tick() {
107 SingleOrDouble::Double(
108 TupleElem::Some {
109 start_tick, notes, min_duration
110 },
111 TupleElem::Some {
112 start_tick: note.base_start_tick(),
113 min_duration: duration,
114 notes: vec![note],
115 }
116 )
117 } else {
118 notes.push(note.clone());
119 SingleOrDouble::Single(
120 TupleElem::Some {
121 start_tick,
122 min_duration: min_duration.min(duration),
123 notes,
124 }
125 )
126 }
127 }
128 }
129 }
130}
131
132#[inline]
142fn numerator_unit(dur: Duration) -> u32 {
143 let len: u32 = 1 << (8 - dur.numerator.ord());
144 len + (len - (len >> dur.dots.value()))
145}
146
147#[inline]
148fn gcd_units(units: HashSet<u32>) -> Option<u32> {
149 units.iter().copied().reduce(|u0, u1| u0.gcd(u1))
150}
151
152fn sort_by_start_tick(notes: &mut [Rc<Note>]) -> (Option<u32>, Vec<TupleElem>) {
153 if notes.is_empty() { return (None, vec![]) }
154
155 notes.sort_by_key(|note0| note0.base_start_tick());
156 let mut ret: Vec<TupleElem> = vec![];
157 let mut cur = TupleElem::None;
158 let mut numerator_units = HashSet::new();
159
160 for note in notes.iter() {
161 numerator_units.insert(numerator_unit(note.duration));
162 match cur.add(note.clone()) {
163 SingleOrDouble::Single(e) => {
164 cur = e;
165 },
166 SingleOrDouble::Double(e0, e1) => {
167 ret.push(e0);
168 cur = e1;
169 },
170 }
171 }
172
173 ret.push(cur);
174 (
175 gcd_units(numerator_units),
176 ret
177 )
178}
179
180fn total_tick_unit(elements: &[TupleElem]) -> (u32, u32) {
181 let mut tick = 0;
182 let mut unit: u32 = 0;
183
184 for e in elements.iter() {
185 tick += e.tick_length();
186 unit += e.unit().unwrap_or(0);
187 }
188
189 (tick, unit)
190}
191
192#[cfg(test)]
193mod tests {
194 use std::rc::Rc;
195 use crate::{note::Note, pitch::Pitch, solfa::Solfa, octave::Octave, sharp_flat::SharpFlat, duration::{Duration, Numerator, Denominator, Dots}};
196 use super::{numerator_unit, tuplize};
197
198 #[test]
199 fn sort_by_start_tick() {
200 let note0 = Rc::new(Note {
201 pitch: Pitch::new(Solfa::A, Octave::Oct3, SharpFlat::Null),
202 duration: Duration::new(Numerator::Quarter, Denominator::from_value(2).unwrap(), Dots::ZERO),
203 ..Default::default()
204 });
205
206 let note1 = Rc::new(Note {
207 pitch: Pitch::new(Solfa::A, Octave::Oct3, SharpFlat::Null),
208 duration: Duration::new(Numerator::Half, Denominator::from_value(2).unwrap(), Dots::ZERO),
209 ..Default::default()
210 });
211
212 let note2 = Rc::new(Note {
213 base_start_tick: 100,
214 pitch: Pitch::new(Solfa::A, Octave::Oct3, SharpFlat::Null),
215 duration: Duration::new(Numerator::Quarter, Denominator::from_value(2).unwrap(), Dots::ZERO),
216 ..Default::default()
217 });
218
219 let mut notes: [Rc<Note>; 3] = [note2.clone(), note1.clone(), note0.clone()];
220 let (min_unit, sorted) = super::sort_by_start_tick(&mut notes);
221
222 assert_eq!(min_unit.unwrap(), 64);
223 assert_eq!(sorted.len(), 2);
224
225 let g0 = &sorted[0];
226 assert_eq!(g0.len(), 2);
227 assert!(g0.contains(¬e0));
228 assert!(g0.contains(¬e1));
229
230 assert_eq!(sorted[1][0], note2);
231 }
232
233 #[test]
234 fn unit() {
235 assert_eq!(
236 2,
237 numerator_unit(
238 Duration::new(Numerator::N128th, Denominator::from_value(2).unwrap(), Dots::ZERO)
239 )
240 );
241 assert_eq!(
242 2,
243 numerator_unit(
244 Duration::new(Numerator::N128th, Denominator::from_value(5).unwrap(), Dots::ZERO)
245 )
246 );
247 assert_eq!(
248 3,
249 numerator_unit(
250 Duration::new(Numerator::N128th, Denominator::from_value(2).unwrap(), Dots::ONE)
251 )
252 );
253 assert_eq!(
254 6,
255 numerator_unit(
256 Duration::new(Numerator::N64th, Denominator::from_value(2).unwrap(), Dots::ONE)
257 )
258 );
259 }
260
261 fn note(tick: u32, pitch: Pitch, duration: Duration) -> Rc<Note> {
262 Rc::new(Note {
263 base_start_tick: tick, pitch, duration,
264 ..Default::default()
265 })
266 }
267
268 #[test]
269 fn tuplize_3_eigth() {
270 let note0 = note(
271 10,
272 Pitch::new(Solfa::A, Octave::Oct3, SharpFlat::Null),
273 Duration::new(Numerator::N8th, Denominator::from_value(2).unwrap(), Dots::ZERO)
274 );
275 let note1 = note(
276 20,
277 Pitch::new(Solfa::A, Octave::Oct3, SharpFlat::Null),
278 Duration::new(Numerator::N8th, Denominator::from_value(2).unwrap(), Dots::ZERO)
279 );
280 let note2 = note(
281 50,
282 Pitch::new(Solfa::A, Octave::Oct3, SharpFlat::Null),
283 Duration::new(Numerator::N8th, Denominator::from_value(2).unwrap(), Dots::ZERO)
284 );
285
286 let result = tuplize(vec![note0, note1, note2]);
287 assert_eq!(result[0].base_start_tick, 10);
288 assert_eq!(result[0].duration, Duration::new(Numerator::N8th, Denominator::from_value(3).unwrap(), Dots::ZERO));
289
290 assert_eq!(result[1].base_start_tick, 10 + 80);
291 assert_eq!(result[1].duration, Duration::new(Numerator::N8th, Denominator::from_value(3).unwrap(), Dots::ZERO));
292
293 assert_eq!(result[2].base_start_tick, 10 + 80 * 2);
294 assert_eq!(result[2].duration, Duration::new(Numerator::N8th, Denominator::from_value(3).unwrap(), Dots::ZERO));
295 }
296
297 #[test]
298 fn tuplize_3_eigth_again() {
299 let note0 = note(
300 0,
301 Pitch::new(Solfa::A, Octave::Oct3, SharpFlat::Null),
302 Duration::new(Numerator::N8th, Denominator::from_value(3).unwrap(), Dots::ZERO)
303 );
304 let note1 = note(
305 80,
306 Pitch::new(Solfa::A, Octave::Oct3, SharpFlat::Null),
307 Duration::new(Numerator::N8th, Denominator::from_value(3).unwrap(), Dots::ZERO)
308 );
309 let note2 = note(
310 160,
311 Pitch::new(Solfa::A, Octave::Oct3, SharpFlat::Null),
312 Duration::new(Numerator::N8th, Denominator::from_value(3).unwrap(), Dots::ZERO)
313 );
314
315 let result = tuplize(vec![note0, note1, note2]);
316 assert_eq!(result[0].base_start_tick, 0);
317 assert_eq!(result[0].duration, Duration::new(Numerator::N8th, Denominator::from_value(3).unwrap(), Dots::ZERO));
318
319 assert_eq!(result[1].base_start_tick, 80);
320 assert_eq!(result[1].duration, Duration::new(Numerator::N8th, Denominator::from_value(3).unwrap(), Dots::ZERO));
321
322 assert_eq!(result[2].base_start_tick, 160);
323 assert_eq!(result[2].duration, Duration::new(Numerator::N8th, Denominator::from_value(3).unwrap(), Dots::ZERO));
324 }
325}