klavier_core/
tuple.rs

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: _ } => &notes[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// 256th note = 1
133// 128th = 2
134// 64th = 4
135// 32th = 8
136// 16th = 16
137// 8th = 32
138// Quarter = 64
139// Half = 128
140// Whole = 256
141#[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(&note0));
228        assert!(g0.contains(&note1));
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}