makepad_platform/
midi.rs

1use {
2    crate::{
3        os::{OsMidiOutput,OsMidiInput},
4        makepad_live_id::{LiveId, FromLiveId},
5    }
6};
7
8#[derive(Clone, Debug)]
9pub struct MidiPortsEvent {
10    pub descs: Vec<MidiPortDesc>,
11}
12
13impl MidiPortsEvent {
14    pub fn all_inputs(&self) -> Vec<MidiPortId> {
15        let mut out = Vec::new();
16        for d in &self.descs {
17            if d.port_type.is_input() {
18                out.push(d.port_id);
19            }
20        }
21        out
22    }
23    pub fn all_outputs(&self) -> Vec<MidiPortId> {
24        let mut out = Vec::new();
25        for d in &self.descs {
26            if d.port_type.is_output() {
27                out.push(d.port_id);
28            }
29        }
30        out
31    }
32}
33
34impl std::fmt::Display for MidiPortsEvent {
35    fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result {
36        write!(f, "MIDI ports:\n").unwrap();
37        for desc in &self.descs {
38            if desc.port_type.is_input() {
39                write!(f, "[Input] {}\n", desc.name).unwrap()
40            }
41            else {
42                write!(f, "[Output] {}\n", desc.name).unwrap()
43            }
44        }
45        Ok(())
46    }
47}
48
49impl std::fmt::Debug for MidiPortDesc {
50    fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result {
51        f.debug_tuple("name").field(&self.name).finish()
52    }
53}
54
55#[derive(Default)]
56pub struct MidiInput(pub (crate) Option<OsMidiInput>);
57unsafe impl Send for MidiInput {}
58
59impl MidiInput {
60    pub fn receive(&mut self) -> Option<(MidiPortId, MidiData)> {
61        self.0.as_mut().unwrap().receive()
62    }
63}
64
65#[derive(Default)]
66pub struct MidiOutput(pub (crate) Option<OsMidiOutput>);
67unsafe impl Send for MidiOutput {}
68
69impl MidiOutput {
70    pub fn send(&self, port: Option<MidiPortId>, data: MidiData) {
71        let output = self.0.as_ref().unwrap();
72        output.send(port, data);
73    } 
74}
75
76#[derive(Clone, Copy, Debug, PartialEq)] 
77pub struct MidiData {
78    pub data: [u8; 3],
79}
80
81impl std::convert::From<u32> for MidiData {
82    fn from(data: u32) -> Self {
83        MidiData {
84            data: [((data >> 16) & 0xff) as u8, ((data >> 8) & 0xff) as u8, ((data >> 0) & 0xff) as u8]
85        }
86    } 
87}  
88
89#[derive(Clone, Copy, Debug, PartialEq)]
90pub enum MidiPortType {
91    Input,
92    Output,
93}
94
95impl MidiPortType {
96    pub fn is_input(&self) -> bool {
97        match self {
98            Self::Input => true,
99            _ => false
100        }
101    }
102    pub fn is_output(&self) -> bool {
103        match self {
104            Self::Output => true,
105            _ => false
106        }
107    }
108}
109
110#[derive(Clone, Debug, Default, Eq, Hash, Copy, PartialEq, FromLiveId)]
111pub struct MidiPortId(pub LiveId);
112
113#[derive(Clone, PartialEq)]
114pub struct MidiPortDesc {
115    pub name: String,
116    pub port_id: MidiPortId,
117    pub port_type: MidiPortType,
118}
119
120
121#[derive(Clone, Copy, Debug)]
122pub struct MidiNote {
123    pub is_on: bool,
124    pub channel: u8,
125    pub note_number: u8,
126    pub velocity: u8,
127}
128
129impl Into<MidiData> for MidiNote {
130    fn into(self) -> MidiData {
131        MidiData {
132            data: [
133                (if self.is_on {0x9}else {0x8} << 4) | self.channel,
134                self.note_number,
135                self.velocity
136            ]
137        }
138    }
139}
140
141
142#[derive(Clone, Copy, Debug)]
143pub struct MidiAftertouch {
144    pub channel: u8,
145    pub note_number: u8,
146    pub velocity: u8
147}
148
149impl Into<MidiData> for MidiAftertouch {
150    fn into(self) -> MidiData {
151        MidiData {
152            data: [
153                0xA0 | self.channel,
154                self.note_number,
155                self.velocity
156            ]
157        }
158    }
159}
160
161#[derive(Clone, Copy, Debug)]
162pub struct MidiControlChange {
163    pub channel: u8,
164    pub param: u8,
165    pub value: u8,
166}
167
168impl Into<MidiData> for MidiControlChange {
169    fn into(self) -> MidiData {
170        MidiData {
171            data: [
172                0xB0 | self.channel,
173                self.param,
174                self.value
175            ]
176        }
177    }
178}
179
180
181#[derive(Clone, Copy, Debug)]
182pub struct MidiProgramChange {
183    pub channel: u8,
184    pub hi: u8,
185    pub lo: u8
186}
187
188impl Into<MidiData> for MidiProgramChange {
189    fn into(self) -> MidiData {
190        MidiData {
191            data: [
192                0xC0 | self.channel,
193                self.hi,
194                self.lo
195            ]
196        }
197    }
198}
199
200
201#[derive(Clone, Copy, Debug)]
202pub struct MidiChannelAftertouch {
203    pub channel: u8,
204    pub value: u16
205}
206
207impl Into<MidiData> for MidiChannelAftertouch {
208    fn into(self) -> MidiData {
209        MidiData {
210            data: [
211                0xD0 | self.channel,
212                (((self.value as u32)>>7)&0x7f) as u8,
213                ((self.value as u32)&0x7f) as u8,
214            ]
215        }
216    }
217}
218
219
220#[derive(Clone, Copy, Debug)]
221pub struct MidiPitchBend {
222    pub channel: u8,
223    pub bend: u16,
224}
225
226impl Into<MidiData> for MidiPitchBend {
227    fn into(self) -> MidiData {
228        MidiData {
229            data: [
230                0xE0 | self.channel,
231                (((self.bend as u32)>>7)&0x7f) as u8,
232                ((self.bend as u32)&0x7f) as u8,
233            ]
234        }
235    }
236}
237
238#[derive(Clone, Copy, Debug)]
239pub struct MidiSystem {
240    pub channel: u8,
241    pub hi: u8,
242    pub lo: u8
243}
244
245impl Into<MidiData> for MidiSystem {
246    fn into(self) -> MidiData {
247        MidiData {
248            data: [
249                0xF0 | self.channel,
250                self.hi,
251                self.lo
252            ]
253        }
254    }
255}
256
257#[derive(Clone, Copy, Debug)]
258pub enum MidiEvent {
259    Note(MidiNote),
260    Aftertouch(MidiAftertouch),
261    ControlChange(MidiControlChange),
262    ProgramChange(MidiProgramChange),
263    PitchBend(MidiPitchBend),
264    ChannelAftertouch(MidiChannelAftertouch),
265    System(MidiSystem),
266    Unknown(MidiData)
267}
268
269impl MidiEvent {
270    pub fn on_note(&self) -> Option<MidiNote> {
271        match self {
272            Self::Note(note) => Some(*note),
273            _ => None
274        }
275    }
276}
277
278impl MidiData {
279    pub fn status(&self) -> u8 {
280        self.data[0] >> 4
281    }
282    pub fn channel(&self) -> u8 {
283        self.data[0] & 0xf
284    }
285    
286    pub fn decode(&self) -> MidiEvent {
287        let status = self.status();
288        let channel = self.channel();
289        match status {
290            0x8 | 0x9 => {
291                let velocity = self.data[2];
292                let is_on = if status == 0x8 || velocity == 0 {
293                    false
294                } else {
295                    true
296                };
297                MidiEvent::Note(MidiNote {
298                    is_on,
299                    channel,
300                    note_number: self.data[1],
301                    velocity,
302                })
303            },
304            0xA => MidiEvent::Aftertouch(MidiAftertouch {
305                channel,
306                note_number: self.data[1],
307                velocity: self.data[2],
308            }),
309            0xB => MidiEvent::ControlChange(MidiControlChange {
310                channel,
311                param: self.data[1],
312                value: self.data[2]
313            }),
314            0xC => MidiEvent::ProgramChange(MidiProgramChange {
315                channel,
316                hi: self.data[1],
317                lo: self.data[2]
318            }),
319            0xD => MidiEvent::ChannelAftertouch(MidiChannelAftertouch {
320                channel,
321                value: ((self.data[1] as u16) << 7) | self.data[2] as u16,
322            }),
323            0xE => MidiEvent::PitchBend(MidiPitchBend {
324                channel,
325                bend: ((self.data[1] as u16) << 7) | self.data[2] as u16,
326            }),
327            0xF => MidiEvent::System(MidiSystem {
328                channel,
329                hi: self.data[1],
330                lo: self.data[2]
331            }),
332            _ => MidiEvent::Unknown(*self)
333        }
334    }
335}