makepad_platform/os/linux/
alsa_midi.rs

1#![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        // alright lets send some midi.
28        // send some midi here
29        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    //event_sender: mpsc::Sender<AlsaMidiEvent>,
51    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        // Subscribe to the announce port.
180        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        //println!("HERE!");
202        
203        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                //println!("GOT PORT {} {} {}", port_name, is_input, is_output);
248                //let name = format!("{} {}", client_name, port_name);
249                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 (watch_sender, watch_receiver) = mpsc::channel();
286        // let _ = watch_sender.send(AlsaMidiEvent::UpdateDevices);
287        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            //event_sender: watch_sender.clone(),
293            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        //let in_client = midi_access_clone.lock().unwrap().client.as_ref().unwrap().in_client.clone();
302        
303        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                            // make sure our eventloop runs
366                            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                    // send to port
385                    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        // enable the ones we use
426        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        // disable the ones not in the set
434        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        //self.event_sender.send(AlsaMidiEvent::UseMidiOutputs(ports.to_vec())).unwrap();
444    }
445    
446    pub fn use_midi_inputs(&mut self, ports: &[MidiPortId]) {
447        if self.client.is_err() {
448            return
449        }
450        // enable the ones we use
451        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        // disable the ones not in the set
459        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        // alright lets disconnect all midi ports
475        for port in &mut self.ports {
476            unsafe {port.unsubscribe(self.client.as_ref().unwrap())};
477        }
478        // replace the ports
479        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}