currawong_core/
midi.rs

1use crate::{
2    keyboard::KeyEvent,
3    music::{self, Note},
4    signal::{Sf64, Signal, SignalCtx},
5};
6pub use midly::{
7    num::{u14, u4, u7},
8    MidiMessage,
9};
10use midly::{MetaMessage, PitchBend, Timing, TrackEvent, TrackEventKind};
11use std::{
12    cell::{Cell, RefCell},
13    rc::Rc,
14};
15
16#[derive(Debug, Clone, Copy)]
17pub struct MidiEvent {
18    pub channel: u4,
19    pub message: MidiMessage,
20}
21
22#[derive(Debug, Clone, Default)]
23pub struct MidiEvents {
24    events: Vec<MidiEvent>,
25}
26
27impl MidiEvents {
28    fn add(&mut self, midi_event: MidiEvent) {
29        self.events.push(midi_event);
30    }
31
32    pub fn for_each_event<F: FnMut(MidiEvent)>(&self, f: F) {
33        self.events.iter().cloned().for_each(f)
34    }
35
36    pub fn for_each_message_on_channel<F: FnMut(MidiMessage)>(&self, channel: u8, mut f: F) {
37        self.for_each_event(|event| {
38            if event.channel.as_int() == channel {
39                f(event.message);
40            }
41        })
42    }
43
44    pub fn filter_channel(&self, channel: u8) -> MidiMessages {
45        let mut midi_messages = MidiMessages::default();
46        self.for_each_message_on_channel(channel, |message| midi_messages.add(message));
47        midi_messages
48    }
49}
50
51#[derive(Debug, Clone, Default)]
52pub struct MidiMessages {
53    messages: Vec<MidiMessage>,
54}
55
56impl MidiMessages {
57    fn add(&mut self, midi_message: MidiMessage) {
58        self.messages.push(midi_message);
59    }
60
61    pub fn for_each<F: FnMut(MidiMessage)>(&self, f: F) {
62        self.messages.iter().cloned().for_each(f)
63    }
64}
65
66fn u7_to_01(u7: u7) -> f64 {
67    u7.as_int() as f64 / 127.0
68}
69
70fn signal_u7_to_01(s: &Signal<u7>) -> Sf64 {
71    s.map(u7_to_01)
72}
73
74pub struct MidiControllerTableRaw {
75    values: Vec<Signal<u7>>,
76}
77
78#[derive(Clone)]
79pub struct MidiControllerTable {
80    values_01: Vec<Sf64>,
81}
82
83impl MidiControllerTableRaw {
84    pub fn get(&self, i: u8) -> Signal<u7> {
85        self.values[i as usize].clone()
86    }
87
88    pub fn to_controller_table(&self) -> MidiControllerTable {
89        MidiControllerTable {
90            values_01: self.values.iter().map(signal_u7_to_01).collect(),
91        }
92    }
93}
94
95impl MidiControllerTable {
96    pub fn get_01(&self, i: u8) -> Sf64 {
97        self.values_01[i as usize].clone()
98    }
99
100    pub fn volume(&self) -> Sf64 {
101        self.get_01(7)
102    }
103
104    pub fn modulation(&self) -> Sf64 {
105        self.get_01(1)
106    }
107}
108
109/// A type that can produce a stream of midi events such as a midi file or device
110pub trait MidiEventSource {
111    fn for_each_new_event<F>(&mut self, ctx: &SignalCtx, f: F)
112    where
113        F: FnMut(MidiEvent);
114}
115
116pub struct TrackEventSource {
117    track: Vec<TrackEvent<'static>>,
118    timing: Timing,
119    tick_duration_s: f64,
120    next_index: usize,
121    next_tick: u32,
122    current_time_s: f64,
123    next_tick_time_s: f64,
124}
125
126impl TrackEventSource {
127    pub fn new<'a>(track: &[TrackEvent<'a>], timing: Timing, default_s_per_beat: f64) -> Self {
128        let track = track
129            .into_iter()
130            .map(|event| event.to_static())
131            .collect::<Vec<_>>();
132        let tick_duration_s = match timing {
133            Timing::Metrical(ticks_per_beat) => {
134                default_s_per_beat / (ticks_per_beat.as_int() as f64)
135            }
136            Timing::Timecode(frames_per_second, ticks_per_frame) => {
137                1.0 / (frames_per_second.as_f32() as f64 * ticks_per_frame as f64)
138            }
139        };
140        Self {
141            track,
142            timing,
143            tick_duration_s,
144            next_index: 0,
145            next_tick: 0,
146            current_time_s: 0.0,
147            next_tick_time_s: 0.0,
148        }
149    }
150}
151
152impl MidiEventSource for TrackEventSource {
153    fn for_each_new_event<F>(&mut self, ctx: &SignalCtx, mut f: F)
154    where
155        F: FnMut(MidiEvent),
156    {
157        let time_since_prev_tick_s = 1.0 / ctx.sample_rate_hz;
158        self.current_time_s += time_since_prev_tick_s;
159        while self.next_tick_time_s < self.current_time_s {
160            self.next_tick_time_s += self.tick_duration_s;
161            while let Some(event) = self.track.get(self.next_index) {
162                let tick = self.next_tick;
163                if event.delta == tick {
164                    self.next_tick = 0;
165                    self.next_index += 1;
166                    match event.kind {
167                        TrackEventKind::Midi { channel, message } => {
168                            f(MidiEvent { channel, message })
169                        }
170                        TrackEventKind::Meta(MetaMessage::Tempo(us_per_beat)) => {
171                            if let Timing::Metrical(ticks_per_beat) = self.timing {
172                                self.tick_duration_s = us_per_beat.as_int() as f64
173                                    / (ticks_per_beat.as_int() as f64 * 1_000_000.0);
174                            }
175                        }
176                        _ => (),
177                    }
178                } else {
179                    break;
180                }
181            }
182            self.next_tick += 1;
183        }
184    }
185}
186
187pub fn event_source_to_signal(event_source: impl MidiEventSource + 'static) -> Signal<MidiEvents> {
188    let event_source = RefCell::new(event_source);
189    Signal::from_fn(move |ctx| {
190        let mut midi_events = MidiEvents::default();
191        event_source
192            .borrow_mut()
193            .for_each_new_event(ctx, |midi_event| midi_events.add(midi_event));
194        midi_events
195    })
196}
197
198impl Signal<MidiEvents> {
199    pub fn filter_channel(&self, channel: u8) -> Signal<MidiMessages> {
200        self.map(move |events| events.filter_channel(channel))
201    }
202}
203
204pub fn event_source_to_signal_single_channel(
205    event_source: impl MidiEventSource + 'static,
206    channel: u8,
207) -> Signal<MidiMessages> {
208    let event_source = RefCell::new(event_source);
209    Signal::from_fn(move |ctx| {
210        let mut midi_messages = MidiMessages::default();
211        event_source
212            .borrow_mut()
213            .for_each_new_event(ctx, |midi_event| {
214                if midi_event.channel == channel {
215                    midi_messages.add(midi_event.message);
216                }
217            });
218        midi_messages
219    })
220}
221
222fn midi_note_message_to_key_event(key: u7, vel: u7, pressed: bool) -> KeyEvent {
223    KeyEvent {
224        note: Note::from_midi_index(key),
225        velocity_01: u7_to_01(vel),
226        pressed,
227    }
228}
229
230fn midi_pitch_bend_to_pitch_bend_multiplier_hz(midi_pitch_bend: u14) -> f64 {
231    music::TONE_RATIO
232        .powf(((midi_pitch_bend.as_int() as i32 - 0x2000) as f64 * 2.0) / 0x3FFF as f64)
233}
234
235impl Signal<MidiMessages> {
236    pub fn key_events(&self) -> Signal<Vec<KeyEvent>> {
237        self.map(|messages| {
238            let mut ret = Vec::new();
239            messages.for_each(|message| match message {
240                MidiMessage::NoteOn { key, vel } => {
241                    ret.push(midi_note_message_to_key_event(key, vel, true))
242                }
243                MidiMessage::NoteOff { key, vel } => {
244                    ret.push(midi_note_message_to_key_event(key, vel, false))
245                }
246                _ => (),
247            });
248            ret
249        })
250    }
251
252    pub fn pitch_bend_multiplier_hz(&self) -> Sf64 {
253        let state = Rc::new(Cell::new(1.0));
254        self.map({
255            let state = Rc::clone(&state);
256            move |messages| {
257                messages.for_each(|message| match message {
258                    MidiMessage::PitchBend {
259                        bend: PitchBend(pitch_bend),
260                    } => state.set(midi_pitch_bend_to_pitch_bend_multiplier_hz(pitch_bend)),
261                    _ => (),
262                });
263            }
264        })
265        .then({
266            let state = Rc::clone(&state);
267            move || state.get()
268        })
269    }
270
271    pub fn controller_table(&self) -> MidiControllerTable {
272        let table = Rc::new(RefCell::new(vec![0.0; 128]));
273        let update_table = Signal::from_fn({
274            let table = Rc::clone(&table);
275            let signal = self.clone();
276            move |ctx| {
277                let mut table = table.borrow_mut();
278                signal.sample(ctx).for_each(|message| match message {
279                    MidiMessage::Controller { controller, value } => {
280                        table[controller.as_int() as usize] = value.as_int() as f64 / 127.0;
281                    }
282                    _ => (),
283                });
284            }
285        });
286        MidiControllerTable {
287            values_01: (0..128)
288                .map(|i| {
289                    update_table.map({
290                        let table = Rc::clone(&table);
291                        move |()| table.borrow()[i]
292                    })
293                })
294                .collect(),
295        }
296    }
297}