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
65pub struct MidiOutput(pub (crate) Option<OsMidiOutput>);
66unsafe impl Send for MidiOutput {}
67
68impl MidiOutput {
69 pub fn send(&self, port: Option<MidiPortId>, data: MidiData) {
70 let output = self.0.as_ref().unwrap();
71 output.send(port, data);
72 }
73}
74
75#[derive(Clone, Copy, Debug, PartialEq)]
76pub struct MidiData {
77 pub data: [u8; 3],
78}
79
80impl std::convert::From<u32> for MidiData {
81 fn from(data: u32) -> Self {
82 MidiData {
83 data: [((data >> 16) & 0xff) as u8, ((data >> 8) & 0xff) as u8, ((data >> 0) & 0xff) as u8]
84 }
85 }
86}
87
88#[derive(Clone, Copy, Debug, PartialEq)]
89pub enum MidiPortType {
90 Input,
91 Output,
92}
93
94impl MidiPortType {
95 pub fn is_input(&self) -> bool {
96 match self {
97 Self::Input => true,
98 _ => false
99 }
100 }
101 pub fn is_output(&self) -> bool {
102 match self {
103 Self::Output => true,
104 _ => false
105 }
106 }
107}
108
109#[derive(Clone, Debug, Default, Eq, Hash, Copy, PartialEq, FromLiveId)]
110pub struct MidiPortId(pub LiveId);
111
112#[derive(Clone, PartialEq)]
113pub struct MidiPortDesc {
114 pub name: String,
115 pub port_id: MidiPortId,
116 pub port_type: MidiPortType,
117}
118
119
120#[derive(Clone, Copy, Debug)]
121pub struct MidiNote {
122 pub is_on: bool,
123 pub channel: u8,
124 pub note_number: u8,
125 pub velocity: u8,
126}
127
128impl Into<MidiData> for MidiNote {
129 fn into(self) -> MidiData {
130 MidiData {
131 data: [
132 (if self.is_on {0x9}else {0x8} << 4) | self.channel,
133 self.note_number,
134 self.velocity
135 ]
136 }
137 }
138}
139
140
141#[derive(Clone, Copy, Debug)]
142pub struct MidiAftertouch {
143 pub channel: u8,
144 pub note_number: u8,
145 pub velocity: u8
146}
147
148impl Into<MidiData> for MidiAftertouch {
149 fn into(self) -> MidiData {
150 MidiData {
151 data: [
152 0xA0 | self.channel,
153 self.note_number,
154 self.velocity
155 ]
156 }
157 }
158}
159
160#[derive(Clone, Copy, Debug)]
161pub struct MidiControlChange {
162 pub channel: u8,
163 pub param: u8,
164 pub value: u8,
165}
166
167impl Into<MidiData> for MidiControlChange {
168 fn into(self) -> MidiData {
169 MidiData {
170 data: [
171 0xB0 | self.channel,
172 self.param,
173 self.value
174 ]
175 }
176 }
177}
178
179
180#[derive(Clone, Copy, Debug)]
181pub struct MidiProgramChange {
182 pub channel: u8,
183 pub hi: u8,
184 pub lo: u8
185}
186
187impl Into<MidiData> for MidiProgramChange {
188 fn into(self) -> MidiData {
189 MidiData {
190 data: [
191 0xC0 | self.channel,
192 self.hi,
193 self.lo
194 ]
195 }
196 }
197}
198
199
200#[derive(Clone, Copy, Debug)]
201pub struct MidiChannelAftertouch {
202 pub channel: u8,
203 pub value: u16
204}
205
206impl Into<MidiData> for MidiChannelAftertouch {
207 fn into(self) -> MidiData {
208 MidiData {
209 data: [
210 0xD0 | self.channel,
211 (((self.value as u32)>>7)&0x7f) as u8,
212 ((self.value as u32)&0x7f) as u8,
213 ]
214 }
215 }
216}
217
218
219#[derive(Clone, Copy, Debug)]
220pub struct MidiPitchBend {
221 pub channel: u8,
222 pub bend: u16,
223}
224
225impl Into<MidiData> for MidiPitchBend {
226 fn into(self) -> MidiData {
227 MidiData {
228 data: [
229 0xE0 | self.channel,
230 (((self.bend as u32)>>7)&0x7f) as u8,
231 ((self.bend as u32)&0x7f) as u8,
232 ]
233 }
234 }
235}
236
237#[derive(Clone, Copy, Debug)]
238pub struct MidiSystem {
239 pub channel: u8,
240 pub hi: u8,
241 pub lo: u8
242}
243
244impl Into<MidiData> for MidiSystem {
245 fn into(self) -> MidiData {
246 MidiData {
247 data: [
248 0xF0 | self.channel,
249 self.hi,
250 self.lo
251 ]
252 }
253 }
254}
255
256#[derive(Clone, Copy, Debug)]
257pub enum MidiEvent {
258 Note(MidiNote),
259 Aftertouch(MidiAftertouch),
260 ControlChange(MidiControlChange),
261 ProgramChange(MidiProgramChange),
262 PitchBend(MidiPitchBend),
263 ChannelAftertouch(MidiChannelAftertouch),
264 System(MidiSystem),
265 Unknown(MidiData)
266}
267
268impl MidiEvent {
269 pub fn on_note(&self) -> Option<MidiNote> {
270 match self {
271 Self::Note(note) => Some(*note),
272 _ => None
273 }
274 }
275}
276
277impl MidiData {
278 pub fn status(&self) -> u8 {
279 self.data[0] >> 4
280 }
281 pub fn channel(&self) -> u8 {
282 self.data[0] & 0xf
283 }
284
285 pub fn decode(&self) -> MidiEvent {
286 let status = self.status();
287 let channel = self.channel();
288 match status {
289 0x8 | 0x9 => MidiEvent::Note(MidiNote {
290 is_on: status == 0x9,
291 channel,
292 note_number: self.data[1],
293 velocity: self.data[2]
294 }),
295 0xA => MidiEvent::Aftertouch(MidiAftertouch {
296 channel,
297 note_number: self.data[1],
298 velocity: self.data[2],
299 }),
300 0xB => MidiEvent::ControlChange(MidiControlChange {
301 channel,
302 param: self.data[1],
303 value: self.data[2]
304 }),
305 0xC => MidiEvent::ProgramChange(MidiProgramChange {
306 channel,
307 hi: self.data[1],
308 lo: self.data[2]
309 }),
310 0xD => MidiEvent::ChannelAftertouch(MidiChannelAftertouch {
311 channel,
312 value: ((self.data[1] as u16) << 7) | self.data[2] as u16,
313 }),
314 0xE => MidiEvent::PitchBend(MidiPitchBend {
315 channel,
316 bend: ((self.data[1] as u16) << 7) | self.data[2] as u16,
317 }),
318 0xF => MidiEvent::System(MidiSystem {
319 channel,
320 hi: self.data[1],
321 lo: self.data[2]
322 }),
323 _ => MidiEvent::Unknown(*self)
324 }
325 }
326}