1 /*  RetroArch - A frontend for libretro.
2  *  Copyright (C) 2018 The RetroArch team
3  *
4  *  RetroArch is free software: you can redistribute it and/or modify it under the terms
5  *  of the GNU General Public License as published by the Free Software Found-
6  *  ation, either version 3 of the License, or (at your option) any later version.
7  *
8  *  RetroArch is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
9  *  without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
10  *  PURPOSE.  See the GNU General Public License for more details.
11  *
12  *  You should have received a copy of the GNU General Public License along with RetroArch.
13  *  If not, see <http://www.gnu.org/licenses/>.
14  */
15 
16 #include <alsa/asoundlib.h>
17 
18 #include <libretro.h>
19 #include <verbosity.h>
20 #include <lists/string_list.h>
21 #include <string/stdstring.h>
22 
23 #include "../midi_driver.h"
24 
25 typedef struct
26 {
27    snd_seq_t *seq;
28    snd_seq_addr_t in;
29    snd_seq_addr_t in_dest;
30    snd_seq_addr_t out;
31    snd_seq_addr_t out_src;
32    int out_queue;
33    snd_seq_real_time_t out_ev_time;  /* time of the last output event */
34 } alsa_midi_t;
35 
36 static const snd_seq_event_type_t alsa_midi_ev_map[8] =
37 {
38    SND_SEQ_EVENT_NOTEOFF,
39    SND_SEQ_EVENT_NOTEON,
40    SND_SEQ_EVENT_KEYPRESS,
41    SND_SEQ_EVENT_CONTROLLER,
42    SND_SEQ_EVENT_PGMCHANGE,
43    SND_SEQ_EVENT_CHANPRESS,
44    SND_SEQ_EVENT_PITCHBEND,
45    SND_SEQ_EVENT_SYSEX
46 };
47 
alsa_midi_get_avail_ports(struct string_list * ports,unsigned caps)48 static bool alsa_midi_get_avail_ports(struct string_list *ports, unsigned caps)
49 {
50    int r;
51    snd_seq_t *seq;
52    snd_seq_client_info_t *client_info;
53    snd_seq_port_info_t *port_info;
54    union string_list_elem_attr attr = {0};
55 
56    snd_seq_client_info_alloca(&client_info);
57    snd_seq_port_info_alloca(&port_info);
58 
59    r = snd_seq_open(&seq, "default", SND_SEQ_OPEN_DUPLEX, SND_SEQ_NONBLOCK);
60    if (r < 0)
61    {
62       RARCH_ERR("[MIDI]: snd_seq_open failed with error %d.\n", r);
63       return false;
64    }
65 
66    snd_seq_client_info_set_client(client_info, -1);
67 
68    while (snd_seq_query_next_client(seq, client_info) == 0)
69    {
70       int client = snd_seq_client_info_get_client(client_info);
71 
72       snd_seq_port_info_set_client(port_info, client);
73       snd_seq_port_info_set_port(port_info, -1);
74 
75       while (snd_seq_query_next_port(seq, port_info) == 0)
76       {
77          unsigned port_caps = snd_seq_port_info_get_capability(port_info);
78          unsigned port_type = snd_seq_port_info_get_type(port_info);
79 
80          if ((port_type & SND_SEQ_PORT_TYPE_MIDI_GENERIC) &&
81                (port_caps & caps) == caps)
82          {
83             const char *port_name = snd_seq_port_info_get_name(port_info);
84 
85             if (!string_list_append(ports, port_name, attr))
86             {
87                RARCH_ERR("[MIDI]: string_list_append failed.\n");
88                snd_seq_close(seq);
89 
90                return false;
91             }
92          }
93       }
94    }
95 
96    snd_seq_close(seq);
97 
98    return true;
99 }
100 
alsa_midi_get_port(snd_seq_t * seq,const char * name,unsigned caps,snd_seq_addr_t * addr)101 static bool alsa_midi_get_port(snd_seq_t *seq, const char *name, unsigned caps,
102       snd_seq_addr_t *addr)
103 {
104    snd_seq_client_info_t *client_info;
105    snd_seq_port_info_t *port_info;
106 
107    snd_seq_client_info_alloca(&client_info);
108    snd_seq_port_info_alloca(&port_info);
109 
110    snd_seq_client_info_set_client(client_info, -1);
111    while (snd_seq_query_next_client(seq, client_info) == 0)
112    {
113       int client_id = snd_seq_client_info_get_client(client_info);
114 
115       snd_seq_port_info_set_client(port_info, client_id);
116       snd_seq_port_info_set_port(port_info, -1);
117 
118       while (snd_seq_query_next_port(seq, port_info) == 0)
119       {
120          unsigned port_caps = snd_seq_port_info_get_capability(port_info);
121          unsigned type      = snd_seq_port_info_get_type(port_info);
122 
123          if ((type & SND_SEQ_PORT_TYPE_MIDI_GENERIC) && (port_caps & caps) == caps)
124          {
125             const char *port_name = snd_seq_port_info_get_name(port_info);
126 
127             if (string_is_equal(port_name, name))
128             {
129                addr->client = client_id;
130                addr->port   = snd_seq_port_info_get_port(port_info);
131 
132                return true;
133             }
134          }
135       }
136    }
137 
138    return false;
139 }
140 
alsa_midi_get_avail_inputs(struct string_list * inputs)141 static bool alsa_midi_get_avail_inputs(struct string_list *inputs)
142 {
143    return alsa_midi_get_avail_ports(inputs, SND_SEQ_PORT_CAP_READ |
144          SND_SEQ_PORT_CAP_SUBS_READ);
145 }
146 
alsa_midi_get_avail_outputs(struct string_list * outputs)147 static bool alsa_midi_get_avail_outputs(struct string_list *outputs)
148 {
149    return alsa_midi_get_avail_ports(outputs, SND_SEQ_PORT_CAP_WRITE |
150          SND_SEQ_PORT_CAP_SUBS_WRITE);
151 }
152 
alsa_midi_free(void * p)153 static void alsa_midi_free(void *p)
154 {
155    alsa_midi_t *d = (alsa_midi_t*)p;
156 
157    if (d)
158    {
159       if (d->seq)
160          snd_seq_close(d->seq);
161       free(d);
162    }
163 }
164 
alsa_midi_set_input(void * p,const char * input)165 static bool alsa_midi_set_input(void *p, const char *input)
166 {
167    int r;
168    snd_seq_port_subscribe_t *sub;
169    alsa_midi_t *d = (alsa_midi_t*)p;
170 
171    if (!input)
172    {
173       if (d->in_dest.port >= 0)
174       {
175          snd_seq_delete_simple_port(d->seq, d->in_dest.port);
176          d->in_dest.port = -1;
177       }
178 
179       return true;
180    }
181 
182    if (!alsa_midi_get_port(d->seq, input, SND_SEQ_PORT_CAP_READ |
183          SND_SEQ_PORT_CAP_SUBS_READ, &d->in))
184       return false;
185 
186    r = snd_seq_create_simple_port(d->seq, "in", SND_SEQ_PORT_CAP_WRITE |
187          SND_SEQ_PORT_CAP_SUBS_WRITE, SND_SEQ_PORT_TYPE_APPLICATION);
188    if (r < 0)
189    {
190       RARCH_ERR("[MIDI]: snd_seq_create_simple_port failed with error %d.\n", r);
191       return false;
192    }
193 
194    d->in_dest.client = snd_seq_client_id(d->seq);
195    d->in_dest.port   = r;
196 
197    snd_seq_port_subscribe_alloca(&sub);
198    snd_seq_port_subscribe_set_sender(sub, &d->in);
199    snd_seq_port_subscribe_set_dest(sub, &d->in_dest);
200    r = snd_seq_subscribe_port(d->seq, sub);
201    if (r < 0)
202       RARCH_ERR("[MIDI]: snd_seq_subscribe_port failed with error %d.\n", r);
203 
204    return r >= 0;
205 }
206 
alsa_midi_set_output(void * p,const char * output)207 static bool alsa_midi_set_output(void *p, const char *output)
208 {
209    int r;
210    alsa_midi_t *d = (alsa_midi_t*)p;
211 
212    if (!output)
213    {
214       if (d->out_queue >= 0)
215       {
216          snd_seq_stop_queue(d->seq, d->out_queue, NULL);
217          snd_seq_free_queue(d->seq, d->out_queue);
218          d->out_queue = -1;
219       }
220       if (d->out_src.port >= 0)
221       {
222          snd_seq_delete_simple_port(d->seq, d->out_src.port);
223          d->out_src.port = -1;
224       }
225 
226       return true;
227    }
228 
229    if (!alsa_midi_get_port(d->seq, output, SND_SEQ_PORT_CAP_WRITE |
230          SND_SEQ_PORT_CAP_SUBS_WRITE, &d->out))
231       return false;
232 
233    r = snd_seq_create_simple_port(d->seq, "out", SND_SEQ_PORT_CAP_READ |
234          SND_SEQ_PORT_CAP_SUBS_READ, SND_SEQ_PORT_TYPE_APPLICATION);
235    if (r < 0)
236    {
237       RARCH_ERR("[MIDI]: snd_seq_create_simple_port failed with error %d.\n", r);
238       return false;
239    }
240 
241    d->out_src.client = snd_seq_client_id(d->seq);
242    d->out_src.port   = r;
243 
244    r = snd_seq_connect_to(d->seq, d->out_src.port, d->out.client, d->out.port);
245    if (r < 0)
246    {
247       RARCH_ERR("[MIDI]: snd_seq_connect_to failed with error %d.\n", r);
248       return false;
249    }
250 
251    d->out_queue = snd_seq_alloc_queue(d->seq);
252    if (d->out_queue < 0)
253    {
254       RARCH_ERR("[MIDI]: snd_seq_alloc_queue failed with error %d.\n", d->out_queue);
255       return false;
256    }
257 
258    r = snd_seq_start_queue(d->seq, d->out_queue, NULL);
259    if (r < 0)
260    {
261        RARCH_ERR("[MIDI]: snd_seq_start_queue failed with error %d.\n", r);
262        return false;
263    }
264 
265    return true;
266 }
267 
alsa_midi_init(const char * input,const char * output)268 static void *alsa_midi_init(const char *input, const char *output)
269 {
270    int r;
271    bool err       = false;
272    alsa_midi_t *d = (alsa_midi_t*)calloc(sizeof(alsa_midi_t), 1);
273 
274    if (!d)
275    {
276       RARCH_ERR("[MIDI]: Out of memory.\n");
277       return NULL;
278    }
279 
280    d->in_dest.port = -1;
281    d->out_src.port = -1;
282    d->out_queue    = -1;
283 
284    r = snd_seq_open(&d->seq, "default", SND_SEQ_OPEN_DUPLEX, SND_SEQ_NONBLOCK);
285    if (r < 0)
286    {
287       RARCH_ERR("[MIDI]: snd_seq_open failed with error %d.\n", r);
288       err = true;
289    }
290    else if (!alsa_midi_set_input(d, input))
291       err = true;
292    else if (!alsa_midi_set_output(d, output))
293       err = true;
294 
295    if (err)
296    {
297       alsa_midi_free(d);
298       d = NULL;
299    }
300 
301    return d;
302 }
303 
alsa_midi_read(void * p,midi_event_t * event)304 static bool alsa_midi_read(void *p, midi_event_t *event)
305 {
306    int r;
307    snd_seq_event_t *ev;
308    alsa_midi_t *d = (alsa_midi_t*)p;
309 
310    r = snd_seq_event_input(d->seq, &ev);
311    if (r < 0)
312    {
313 #ifdef DEBUG
314       if (r != -EAGAIN)
315          RARCH_ERR("[MIDI]: snd_seq_event_input failed with error %d.\n", r);
316 #endif
317       return false;
318    }
319 
320    if (ev->type == SND_SEQ_EVENT_NOTEOFF)
321    {
322       event->data[0]   = 0x80 | ev->data.note.channel;
323       event->data[1]   = ev->data.note.note;
324       event->data[2]   = ev->data.note.velocity;
325       event->data_size = 3;
326    }
327    else if (ev->type == SND_SEQ_EVENT_NOTEON)
328    {
329       event->data[0]   = 0x90 | ev->data.note.channel;
330       event->data[1]   = ev->data.note.note;
331       event->data[2]   = ev->data.note.velocity;
332       event->data_size = 3;
333    }
334    else if (ev->type == SND_SEQ_EVENT_KEYPRESS)
335    {
336       event->data[0]   = 0xA0 | ev->data.note.channel;
337       event->data[1]   = ev->data.note.note;
338       event->data[2]   = ev->data.note.velocity;
339       event->data_size = 3;
340    }
341    else if (ev->type == SND_SEQ_EVENT_CONTROLLER)
342    {
343       event->data[0]   = 0xB0 | ev->data.control.channel;
344       event->data[1]   = ev->data.control.param;
345       event->data[2]   = ev->data.control.value;
346       event->data_size = 3;
347    }
348    else if (ev->type == SND_SEQ_EVENT_PGMCHANGE)
349    {
350       event->data[0]   = 0xC0 | ev->data.control.channel;
351       event->data[1]   = ev->data.control.value;
352       event->data_size = 2;
353    }
354    else if (ev->type == SND_SEQ_EVENT_CHANPRESS)
355    {
356       event->data[0]   = 0xD0 | ev->data.control.channel;
357       event->data[1]   = ev->data.control.value;
358       event->data_size = 2;
359    }
360    else if (ev->type == SND_SEQ_EVENT_PITCHBEND)
361    {
362       event->data[0]   = 0xE0 | ev->data.control.channel;
363       event->data[1]   = ev->data.control.value & 127;
364       event->data[2]   = ev->data.control.value >> 7;
365       event->data_size = 3;
366    }
367    else if (ev->type == SND_SEQ_EVENT_SYSEX)
368    {
369       if (ev->data.ext.len <= event->data_size)
370       {
371          size_t i;
372          uint8_t *ev_data = (uint8_t*)ev->data.ext.ptr;
373 
374          for (i = 0; i < ev->data.ext.len; ++i)
375             event->data[i] = ev_data[i];
376 
377          event->data_size = ev->data.ext.len;
378       }
379 #ifdef DEBUG
380       else
381       {
382          RARCH_ERR("[MIDI]: SysEx event too big.\n");
383          r = -1;
384       }
385 #endif
386    }
387    else
388       r = -1;
389 
390    event->delta_time = 0;
391    snd_seq_free_event(ev);
392 
393    return r >= 0;
394 }
395 
alsa_midi_write(void * p,const midi_event_t * event)396 static bool alsa_midi_write(void *p, const midi_event_t *event)
397 {
398    int r;
399    snd_seq_event_t ev;
400    alsa_midi_t *d = (alsa_midi_t*)p;
401 
402    ev.type  = alsa_midi_ev_map[(event->data[0] >> 4) & 7];
403    ev.flags = SND_SEQ_TIME_STAMP_REAL | SND_SEQ_TIME_MODE_ABS;
404    ev.queue = d->out_queue;
405    ev.time.time.tv_sec  = d->out_ev_time.tv_sec + event->delta_time / 1000000;
406    ev.time.time.tv_nsec = d->out_ev_time.tv_nsec +
407          (event->delta_time % 1000000) * 1000;
408 
409    if (ev.time.time.tv_nsec >= 1000000000)
410    {
411        ev.time.time.tv_sec  += 1;
412        ev.time.time.tv_nsec -= 1000000000;
413    }
414    ev.source.port = d->out_src.port;
415    ev.dest.client = SND_SEQ_ADDRESS_SUBSCRIBERS;
416 
417    if (ev.type == SND_SEQ_EVENT_NOTEOFF || ev.type == SND_SEQ_EVENT_NOTEON ||
418          ev.type == SND_SEQ_EVENT_KEYPRESS)
419    {
420       ev.data.note.channel  = event->data[0] & 0x0F;
421       ev.data.note.note     = event->data[1];
422       ev.data.note.velocity = event->data[2];
423    }
424    else if (ev.type == SND_SEQ_EVENT_CONTROLLER)
425    {
426       ev.data.control.channel = event->data[0] & 0x0F;
427       ev.data.control.param   = event->data[1];
428       ev.data.control.value   = event->data[2];
429    }
430    else if (ev.type == SND_SEQ_EVENT_PGMCHANGE ||
431          ev.type == SND_SEQ_EVENT_CHANPRESS)
432    {
433       ev.data.control.channel = event->data[0] & 0x0F;
434       ev.data.control.value   = event->data[1];
435    }
436    else if (ev.type == SND_SEQ_EVENT_PITCHBEND)
437    {
438       ev.data.control.channel = event->data[0] & 0x0F;
439       ev.data.control.value   = (event->data[1] | (event->data[2] << 7)) - 0x2000;
440    }
441    else if (ev.type == SND_SEQ_EVENT_SYSEX)
442    {
443       ev.flags |= SND_SEQ_EVENT_LENGTH_VARIABLE;
444       ev.data.ext.ptr = event->data;
445       ev.data.ext.len = event->data_size;
446    }
447 
448    r = snd_seq_event_output(d->seq, &ev);
449 #ifdef DEBUG
450    if (r < 0)
451       RARCH_ERR("[MIDI]: snd_seq_event_output failed with error %d.\n", r);
452 #endif
453 
454    d->out_ev_time.tv_sec  = ev.time.time.tv_sec;
455    d->out_ev_time.tv_nsec = ev.time.time.tv_nsec;
456 
457    return r >= 0;
458 }
459 
alsa_midi_flush(void * p)460 static bool alsa_midi_flush(void *p)
461 {
462    int r;
463    alsa_midi_t *d = (alsa_midi_t*)p;
464 
465    r = snd_seq_drain_output(d->seq);
466 #ifdef DEBUG
467    if (r < 0)
468       RARCH_ERR("[MIDI]: snd_seq_drain_output failed with error %d.\n", r);
469 #endif
470 
471    return r == 0;
472 }
473 
474 midi_driver_t midi_alsa = {
475    "alsa",
476    alsa_midi_get_avail_inputs,
477    alsa_midi_get_avail_outputs,
478    alsa_midi_init,
479    alsa_midi_free,
480    alsa_midi_set_input,
481    alsa_midi_set_output,
482    alsa_midi_read,
483    alsa_midi_write,
484    alsa_midi_flush
485 };
486