midi_toolkit/sequence/conversion/
notes_to_events.rs

1use std::collections::BinaryHeap;
2
3use crate::gen_iter::GenIter;
4
5use crate::{
6    events::{Event, NoteOffEvent},
7    notes::MIDINote,
8    num::MIDINum,
9    sequence::event::Delta,
10    unwrap,
11};
12
13/// A temporary struct for ordering note off events in a binary heap.
14struct NoteOffHolder<D: MIDINum>(Delta<D, NoteOffEvent>);
15
16impl<D: MIDINum> NoteOffHolder<D> {
17    fn new(delta: D, event: NoteOffEvent) -> Self {
18        Self(Delta::new(delta, event))
19    }
20
21    fn into_event(self) -> Delta<D, Event> {
22        Delta::new(self.0.delta, Event::NoteOff(self.0.event))
23    }
24}
25
26impl<D: MIDINum> PartialEq for NoteOffHolder<D> {
27    fn eq(&self, other: &Self) -> bool {
28        self.0.delta == other.0.delta
29    }
30}
31impl<D: MIDINum> Eq for NoteOffHolder<D> {}
32
33impl<D: MIDINum> Ord for NoteOffHolder<D> {
34    fn cmp(&self, other: &Self) -> std::cmp::Ordering {
35        self.0
36            .delta
37            .partial_cmp(&other.0.delta)
38            .unwrap_or(std::cmp::Ordering::Equal)
39            .reverse()
40    }
41}
42
43impl<D: MIDINum> PartialOrd for NoteOffHolder<D> {
44    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
45        Some(self.cmp(other))
46    }
47}
48
49/// Takes a note iterator and converts it to a note event iterator.
50/// Effectively flattening the notes into an event sequence.
51pub fn notes_to_events<D: MIDINum, N: MIDINote<D>, Err>(
52    iter: impl Iterator<Item = Result<N, Err>> + Sized,
53) -> impl Iterator<Item = Result<Delta<D, Event>, Err>> {
54    GenIter(
55        #[coroutine]
56        move || {
57            let mut note_offs = BinaryHeap::<NoteOffHolder<D>>::new();
58
59            let mut prev_time = D::zero();
60
61            for note in iter {
62                let note = unwrap!(note);
63
64                while let Some(e) = note_offs.peek() {
65                    if e.0.delta <= note.start() {
66                        let holder = note_offs.pop().unwrap();
67                        let mut e = holder.into_event();
68                        let time = e.delta;
69                        e.delta -= prev_time;
70                        prev_time = time;
71                        yield Ok(e);
72                    } else {
73                        break;
74                    }
75                }
76
77                yield Ok(Event::new_delta_note_on_event(
78                    note.start() - prev_time,
79                    note.channel(),
80                    note.key(),
81                    note.velocity(),
82                ));
83
84                prev_time = note.start();
85
86                let time = note.end();
87                let off = NoteOffEvent::new(note.channel(), note.key());
88                let holder = NoteOffHolder::new(time, off);
89
90                note_offs.push(holder);
91            }
92
93            while let Some(holder) = note_offs.pop() {
94                let mut e = holder.into_event();
95                let time = e.delta;
96                e.delta -= prev_time;
97                prev_time = time;
98                yield Ok(e);
99            }
100        },
101    )
102}
103
104#[cfg(test)]
105mod tests {
106    use crate::{
107        events::Event,
108        notes::Note,
109        pipe,
110        sequence::{conversion::notes_to_events, to_vec_result, wrap_ok},
111    };
112
113    #[test]
114    fn convert_notes_to_events() {
115        let events = vec![
116            Note {
117                start: 100.0f64,
118                channel: 0,
119                key: 64,
120                velocity: 127,
121                len: 80.0,
122            },
123            Note {
124                start: 130.0,
125                channel: 0,
126                key: 64,
127                velocity: 127,
128                len: 130.0,
129            },
130            Note {
131                start: 260.0,
132                channel: 1,
133                key: 64,
134                velocity: 127,
135                len: 80.0,
136            },
137        ];
138
139        let changed = pipe! {
140            events.into_iter()
141            |>wrap_ok()
142            |>notes_to_events()
143            |>to_vec_result().unwrap()
144        };
145
146        let expected = vec![
147            Event::new_delta_note_on_event(100.0f64, 0, 64, 127),
148            Event::new_delta_note_on_event(30.0f64, 0, 64, 127),
149            Event::new_delta_note_off_event(50.0f64, 0, 64),
150            Event::new_delta_note_off_event(80.0f64, 0, 64),
151            Event::new_delta_note_on_event(0.0f64, 1, 64, 127),
152            Event::new_delta_note_off_event(80.0f64, 1, 64),
153        ];
154
155        assert_eq!(changed, expected);
156    }
157}