makepad_platform/os/linux/
alsa_midi.rs1#![allow(non_upper_case_globals)]
2use {
3 std::sync::{Arc, Mutex, mpsc},
4 std::ffi::CStr,
5 std::os::raw::{
6 c_uint,
7 },
8 super::{
9 alsa_sys::*,
10 alsa_audio::AlsaError,
11 },
12 crate::{
13 makepad_live_id::*,
14 midi::*,
15 thread::SignalToUI,
16 }
17};
18
19
20#[derive(Clone)]
21pub struct OsMidiOutput(pub (crate) Arc<Mutex<AlsaMidiAccess >>);
22
23pub struct OsMidiInput(mpsc::Receiver<(MidiPortId, MidiData) >);
24
25impl OsMidiOutput {
26 pub fn send(&self, port_id: Option<MidiPortId>, d: MidiData) {
27 let _ = self.0.lock().unwrap().send_midi(port_id, d);
30 }
31}
32
33impl OsMidiInput {
34 pub fn receive(&mut self) -> Option<(MidiPortId, MidiData)> {
35 if let Ok((port_id, data)) = self.0.try_recv() {
36 return Some((port_id, data))
37 }
38 None
39 }
40}
41
42type InputSenders = Arc<Mutex<Vec<mpsc::Sender<(MidiPortId, MidiData) >> >>;
43
44#[derive(Clone)]
45pub struct AlsaMidiOutput {
46}
47
48pub struct AlsaMidiAccess {
49 input_senders: InputSenders,
50 ports: Vec<AlsaMidiPort>,
52 client: Result<AlsaClient, AlsaError>,
53}
54
55
56
57macro_rules!alsa_error {
58 ( $ call: expr) => {
59 AlsaError::from(stringify!( $ call), $ call)
60 }
61}
62
63#[derive(Clone)]
64struct AlsaClientPtr(pub *mut snd_seq_t);
65unsafe impl Send for AlsaClientPtr {}
66
67#[derive(Clone)]
68struct AlsaMidiSendPtr(pub *mut snd_midi_event_t);
69unsafe impl Send for AlsaMidiSendPtr {}
70
71
72struct AlsaClient {
73 in_client: AlsaClientPtr,
74 in_client_id: i32,
75 in_port_id: i32,
76 midi_send: AlsaMidiSendPtr,
77 out_client: AlsaClientPtr,
78 out_client_id: i32,
79 out_port_id: i32,
80}
81
82const kRequiredInputPortCaps: c_uint =
83SND_SEQ_PORT_CAP_READ | SND_SEQ_PORT_CAP_SUBS_READ;
84const kRequiredOutputPortCaps: c_uint =
85SND_SEQ_PORT_CAP_WRITE | SND_SEQ_PORT_CAP_SUBS_WRITE;
86const kCreateOutputPortCaps: c_uint =
87SND_SEQ_PORT_CAP_READ | SND_SEQ_PORT_CAP_NO_EXPORT;
88const kCreateInputPortCaps: c_uint =
89SND_SEQ_PORT_CAP_WRITE | SND_SEQ_PORT_CAP_NO_EXPORT;
90const kCreatePortType: c_uint =
91SND_SEQ_PORT_TYPE_MIDI_GENERIC | SND_SEQ_PORT_TYPE_APPLICATION;
92
93#[derive(Clone)]
94pub struct AlsaMidiPort {
95 client_id: i32,
96 port_id: i32,
97 subscribed: bool,
98 desc: MidiPortDesc
99}
100
101impl AlsaMidiPort {
102
103 unsafe fn subscribe(&mut self, client: &AlsaClient) {
104 if !self.subscribed {
105 self.subscribed = true;
106 self.config_port(client, true);
107 }
108 }
109
110 unsafe fn unsubscribe(&mut self, client: &AlsaClient) {
111 if self.subscribed {
112 self.subscribed = false;
113 self.config_port(client, false);
114 }
115 }
116
117 unsafe fn config_port(&self, client: &AlsaClient, subscribe: bool) {
118 let mut subs: *mut snd_seq_port_subscribe_t = 0 as *mut _;
119 snd_seq_port_subscribe_malloc(&mut subs);
120
121 if self.desc.port_type.is_input() {
122 let sender = snd_seq_addr_t {
123 client: self.client_id as _,
124 port: self.port_id as _
125 };
126 snd_seq_port_subscribe_set_sender(subs, &sender);
127 let dest = snd_seq_addr_t {
128 client: client.in_client_id as _,
129 port: client.in_port_id as _
130 };
131 snd_seq_port_subscribe_set_dest(subs, &dest);
132 if subscribe {
133 alsa_error!(snd_seq_subscribe_port(client.in_client.0, subs)).unwrap();
134 }
135 else {
136 snd_seq_unsubscribe_port(client.in_client.0, subs);
137 }
138 }
139 else {
140 let sender = snd_seq_addr_t {
141 client: client.out_client_id as _,
142 port: client.out_port_id as _
143 };
144 snd_seq_port_subscribe_set_sender(subs, &sender);
145 let dest = snd_seq_addr_t {
146 client: self.client_id as _,
147 port: self.port_id as _
148 };
149 snd_seq_port_subscribe_set_dest(subs, &dest);
150 if subscribe {
151 alsa_error!(snd_seq_subscribe_port(client.in_client.0, subs)).unwrap();
152 }
153 else {
154 snd_seq_unsubscribe_port(client.in_client.0, subs);
155 }
156 }
157 }
158}
159
160impl AlsaClient {
161 unsafe fn new() -> Result<AlsaClient, AlsaError> {
162 let mut in_client: *mut snd_seq_t = 0 as *mut _;
163 alsa_error!(snd_seq_open(&mut in_client, "default\0".as_ptr(), SND_SEQ_OPEN_INPUT, 0)) ?;
164 alsa_error!(snd_seq_set_client_name(in_client, "Makepad Midi In\0".as_ptr())) ?;
165 let in_client_id = snd_seq_client_id(in_client);
166
167 let mut out_client: *mut snd_seq_t = 0 as *mut _;
168 alsa_error!(snd_seq_open(&mut out_client, "default\0".as_ptr(), SND_SEQ_OPEN_OUTPUT, 0)) ?;
169 alsa_error!(snd_seq_set_client_name(out_client, "Makepad Midi Out\0".as_ptr())) ?;
170 let out_client_id = snd_seq_client_id(out_client);
171
172 let in_port_id = alsa_error!(snd_seq_create_simple_port(
173 in_client,
174 "Makepad Midi In\0".as_ptr(),
175 kCreateInputPortCaps,
176 kCreatePortType
177 )) ?;
178
179 let mut subs: *mut snd_seq_port_subscribe_t = 0 as *mut _;
181 alsa_error!(snd_seq_port_subscribe_malloc(&mut subs)) ?;
182 let announce_sender = snd_seq_addr_t {
183 client: SND_SEQ_CLIENT_SYSTEM,
184 port: SND_SEQ_PORT_SYSTEM_ANNOUNCE
185 };
186 let announce_dest = snd_seq_addr_t {
187 client: in_client_id as _,
188 port: in_port_id as _
189 };
190 snd_seq_port_subscribe_set_sender(subs, &announce_sender);
191 snd_seq_port_subscribe_set_dest(subs, &announce_dest);
192
193 alsa_error!(snd_seq_subscribe_port(in_client, subs)) ?;
194
195 let out_port_id = alsa_error!(snd_seq_create_simple_port(
196 out_client,
197 "Makepad Midi Out\0".as_ptr(),
198 kCreateOutputPortCaps,
199 SND_SEQ_PORT_TYPE_APPLICATION
200 )) ?;
201 let mut midi_send: *mut snd_midi_event_t = 0 as * mut _;
204 alsa_error!(snd_midi_event_new(256, &mut midi_send))?;
205
206 Ok(AlsaClient {
207 in_client: AlsaClientPtr(in_client),
208 in_client_id,
209 in_port_id,
210 midi_send: AlsaMidiSendPtr(midi_send),
211 out_client: AlsaClientPtr(out_client),
212 out_client_id,
213 out_port_id,
214 })
215 }
216
217 unsafe fn enumerate_ports(&self) -> Result<Vec<AlsaMidiPort>, AlsaError> {
218
219 let mut client_info: *mut snd_seq_client_info_t = 0 as *mut _;
220 alsa_error!(snd_seq_client_info_malloc(&mut client_info)) ?;
221
222 let mut port_info: *mut snd_seq_port_info_t = 0 as *mut _;
223 alsa_error!(snd_seq_port_info_malloc(&mut port_info)) ?;
224
225 snd_seq_client_info_set_client(client_info, -1);
226 let mut out_ports = Vec::new();
227
228 while snd_seq_query_next_client(self.in_client.0, client_info) == 0 {
229 let client_id = snd_seq_client_info_get_client(client_info);
230 if client_id == self.in_client_id || client_id == self.out_client_id {
231 continue;
232 }
233
234 snd_seq_port_info_set_client(port_info, client_id);
235 snd_seq_port_info_set_port(port_info, -1);
236 let client_name = CStr::from_ptr(snd_seq_client_info_get_name(client_info)).to_str().unwrap().to_string();
237 let _client_type = snd_seq_client_info_get_type(client_info);
238 if client_name == "System" {
239 continue;
240 }
241 while snd_seq_query_next_port(self.in_client.0, port_info) == 0 {
242 let addr: *const snd_seq_addr_t = snd_seq_port_info_get_addr(port_info);
243 let caps = snd_seq_port_info_get_capability(port_info);
244 let port_name = CStr::from_ptr(snd_seq_port_info_get_name(port_info)).to_str().unwrap().to_string();
245 let is_input = (caps & kRequiredInputPortCaps) == kRequiredInputPortCaps;
246 let is_output = (caps & kRequiredOutputPortCaps) == kRequiredOutputPortCaps;
247 if is_input {
250 out_ports.push(AlsaMidiPort {
251 client_id,
252 subscribed: false,
253 port_id: (*addr).port as _,
254 desc: MidiPortDesc {
255 port_id: LiveId::from_str(&format!("{} input", port_name)).into(),
256 name: port_name.clone(),
257 port_type: MidiPortType::Input
258 }
259 })
260 }
261 if is_output {
262 out_ports.push(AlsaMidiPort {
263 client_id,
264 subscribed: false,
265 port_id: (*addr).port as _,
266 desc: MidiPortDesc {
267 port_id: LiveId::from_str(&format!("{} output", port_name)).into(),
268 name: port_name,
269 port_type: MidiPortType::Output
270 }
271 })
272 }
273 }
274 }
275 Ok(out_ports)
276 }
277}
278
279impl AlsaMidiAccess {
280
281 pub fn new(change_signal: SignalToUI) -> Arc<Mutex<Self >> {
282
283 change_signal.set();
284
285 let input_senders = InputSenders::default();
288
289 let midi_access = Arc::new(Mutex::new(Self {
290 client: unsafe {AlsaClient::new()},
291 ports: Vec::new(),
292 input_senders: input_senders.clone(),
294 }));
295
296 let midi_access_clone = midi_access.clone();
297 let change_signal_clone = change_signal.clone();
298 if midi_access_clone.lock().unwrap().client.as_ref().is_err(){
299 return midi_access;
300 }
301 std::thread::spawn(move || unsafe {
304 let in_client = midi_access_clone.lock().unwrap().client.as_ref().unwrap().in_client.clone();
305 loop {
306 let mut ev: *mut snd_seq_event_t = 0 as *mut _;
307 snd_seq_event_input(in_client.0, &mut ev);
308 let msg = match (*ev).type_ {
309 SND_SEQ_EVENT_PORT_SUBSCRIBED |
310 SND_SEQ_EVENT_PORT_UNSUBSCRIBED |
311 SND_SEQ_EVENT_CLIENT_CHANGE |
312 SND_SEQ_EVENT_CLIENT_START |
313 SND_SEQ_EVENT_CLIENT_EXIT => None,
314 SND_SEQ_EVENT_PORT_CHANGE |
315 SND_SEQ_EVENT_PORT_START |
316 SND_SEQ_EVENT_PORT_EXIT => {
317 change_signal_clone.set();
318 None
319 },
320 SND_SEQ_EVENT_NOTEON |
321 SND_SEQ_EVENT_NOTEOFF => Some(MidiNote {
322 is_on: (*ev).type_ == SND_SEQ_EVENT_NOTEON,
323 channel: (*ev).data.note.channel,
324 note_number: (*ev).data.note.note,
325 velocity: (*ev).data.note.velocity
326 }.into()),
327 SND_SEQ_EVENT_KEYPRESS => Some(MidiAftertouch {
328 channel: (*ev).data.note.channel,
329 note_number: (*ev).data.note.note,
330 velocity: (*ev).data.note.velocity
331 }.into()),
332 SND_SEQ_EVENT_CONTROLLER => Some(MidiControlChange {
333 channel: (*ev).data.control.channel,
334 param: (*ev).data.control.param as _,
335 value: (*ev).data.control.value as _
336 }.into()),
337 SND_SEQ_EVENT_PGMCHANGE => Some(MidiProgramChange {
338 channel: (*ev).data.control.channel,
339 hi: (*ev).data.control.param as _,
340 lo: (*ev).data.control.value as _
341 }.into()),
342 SND_SEQ_EVENT_CHANPRESS => Some(MidiChannelAftertouch {
343 channel: (*ev).data.control.channel,
344 value: (8192 + (*ev).data.control.value) as _
345 }.into()),
346 SND_SEQ_EVENT_PITCHBEND => Some(MidiPitchBend {
347 channel: (*ev).data.control.channel,
348 bend: (8192 + (*ev).data.control.value) as _
349 }.into()),
350 x => {
351 println!("Unknown alsa midi event {}", x);
352 None
353 }
354 };
355 if let Some(msg) = msg {
356 if let Some(port_id) = midi_access_clone.lock().unwrap().find_port(
357 (*ev).source.client as i32,
358 (*ev).source.port as i32
359 ) {
360 let mut senders = input_senders.lock().unwrap();
361 senders.retain( | s | {
362 s.send((port_id, msg)).is_ok()
363 });
364 if senders.len()>0 {
365 SignalToUI::set_ui_signal();
367 }
368 }
369 }
370 }
371 });
372 change_signal.set();
373 midi_access
374 }
375
376 pub fn send_midi(&mut self, port_id: Option<MidiPortId>, d: MidiData) {
377 if self.client.is_err() {
378 return
379 }
380 let client = self.client.as_ref().unwrap();
381 unsafe {
382 for port in &self.ports {
383 if port_id.is_none() || Some(port.desc.port_id) == port_id {
384 let mut event: snd_seq_event_t = std::mem::zeroed();
386 snd_midi_event_reset_encode(client.midi_send.0);
387 let r = snd_midi_event_encode(client.midi_send.0, d.data.as_ptr(), 3, &mut event);
388 if r!= 1{
389 panic!("Unexpected result");
390 }
391 event.source.port = port.port_id as _;
392 event.dest.client = SND_SEQ_ADDRESS_SUBSCRIBERS as _;
393 event.dest.port = SND_SEQ_ADDRESS_UNKNOWN as _;
394 event.queue = SND_SEQ_QUEUE_DIRECT as _;
395 snd_seq_event_output_direct(client.out_client.0, &mut event);
396 }
397 }
398 }
399 }
400
401 pub fn find_port(&self, client_id: i32, port_id: i32) -> Option<MidiPortId> {
402 for port in &self.ports {
403 if port.client_id == client_id && port.port_id == port_id {
404 return Some(port.desc.port_id)
405 }
406 }
407 None
408 }
409
410 pub fn create_midi_input(&self) -> MidiInput {
411 let senders = self.input_senders.clone();
412 let (send, recv) = mpsc::channel();
413 senders.lock().unwrap().push(send);
414 MidiInput(Some(OsMidiInput(recv)))
415 }
416
417 pub fn midi_reset(&mut self) {
418 self.get_updated_descs();
419 }
420
421 pub fn use_midi_outputs(&mut self, ports: &[MidiPortId]) {
422 if self.client.is_err() {
423 return
424 }
425 for port_id in ports {
427 if let Some(port) = self.ports.iter_mut().find( | p | p.desc.port_id == *port_id && p.desc.port_type.is_output()) {
428 unsafe {
429 port.subscribe(self.client.as_ref().unwrap())
430 }
431 }
432 }
433 for port in &mut self.ports {
435 if ports.iter().find( | p | **p == port.desc.port_id).is_none() {
436 if port.desc.port_type.is_output() {
437 unsafe {
438 port.unsubscribe(self.client.as_ref().unwrap())
439 }
440 }
441 }
442 }
443 }
445
446 pub fn use_midi_inputs(&mut self, ports: &[MidiPortId]) {
447 if self.client.is_err() {
448 return
449 }
450 for port_id in ports {
452 if let Some(port) = self.ports.iter_mut().find( | p | p.desc.port_id == *port_id && p.desc.port_type.is_input()) {
453 unsafe {
454 port.subscribe(self.client.as_ref().unwrap())
455 }
456 }
457 }
458 for port in &mut self.ports {
460 if ports.iter().find( | p | **p == port.desc.port_id).is_none() {
461 if port.desc.port_type.is_input() {
462 unsafe {
463 port.unsubscribe(self.client.as_ref().unwrap())
464 }
465 }
466 }
467 }
468 }
469
470 pub fn get_updated_descs(&mut self) -> Vec<MidiPortDesc> {
471 if self.client.is_err() {
472 return Vec::new();
473 }
474 for port in &mut self.ports {
476 unsafe {port.unsubscribe(self.client.as_ref().unwrap())};
477 }
478 self.ports = if let Ok(client) = &self.client {
480 unsafe {client.enumerate_ports().unwrap()}
481 }
482 else {
483 Vec::new()
484 };
485 let mut descs = Vec::new();
486 for port in &self.ports {
487 descs.push(port.desc.clone());
488 }
489 descs
490 }
491
492}