8d41a79e19376982af957c03a2c2f6ad26d8e516
[aubio.git] / src / midi_alsa_seq.c
1 /* 
2  * Copyright 2004 Paul Brossier
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Library General Public License
6  * as published by the Free Software Foundation; either version 2 of
7  * the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful, but
10  * WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Library General Public License for more details.
13  *  
14  * You should have received a copy of the GNU Library General Public
15  * License along with this library; if not, write to the Free
16  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
17  * 02111-1307, USA
18  */
19
20 /* this file originally taken from Fluidsynth, Peter Hanappe and others. */
21
22 /** \file
23  * Midi driver for the Advanced Linux Sound Architecture (sequencer mode)
24  */
25
26
27 #include "aubio_priv.h"
28 #include "midi.h"
29 #include "midi_event.h"
30 #include "midi_parser.h"
31 #include "midi_driver.h"
32 #include "config.h"
33
34 #if ALSA_SUPPORT
35
36 #define ALSA_PCM_NEW_HW_PARAMS_API
37 #include <alsa/asoundlib.h>
38 #include <pthread.h>
39 #include <fcntl.h>
40 #include <unistd.h>
41 #include <sys/poll.h>
42 /* #include <errno.h> //perror is in stdio.h */
43
44
45 #ifdef LADCCA_SUPPORT
46 #include <ladcca/ladcca.h>
47 extern cca_client_t * aubio_cca_client;
48 #endif /* LADCCA_SUPPORT */
49
50 #define AUBIO_ALSA_DEFAULT_SEQ_DEVICE   "default"
51
52 #define AUBIO_ALSA_BUFFER_LENGTH 512
53
54 /* SCHED_FIFO priorities for ALSA threads (see pthread_attr_setschedparam) */
55 #define ALSA_RAWMIDI_SCHED_PRIORITY 90
56 #define ALSA_SEQ_SCHED_PRIORITY 90
57
58
59 /** aubio_alsa_seq_driver_t */
60 typedef struct {
61     aubio_midi_driver_t driver;
62     snd_seq_t *seq_handle;
63     int seq_port;
64     struct pollfd *pfd;
65     int npfd;
66     pthread_t thread;
67     int status;
68 } aubio_alsa_seq_driver_t;
69
70 aubio_midi_driver_t* new_aubio_alsa_seq_driver(//aubio_settings_t* settings, 
71         handle_midi_event_func_t handler, 
72         void* data);
73 int del_aubio_alsa_seq_driver(aubio_midi_driver_t* p);
74 static void* aubio_alsa_seq_run(void* d);
75
76 //void aubio_alsa_seq_driver_settings(aubio_settings_t* settings)
77 //{
78 //  aubio_settings_register_str(settings, "midi.alsa_seq.device", "default", 0, NULL, NULL);
79 //  aubio_settings_register_str(settings, "midi.alsa_seq.id", "pid", 0, NULL, NULL);
80 //}
81
82 /** new_aubio_alsa_seq_driver */
83 aubio_midi_driver_t*  new_aubio_alsa_seq_driver(//aubio_settings_t* settings, 
84         handle_midi_event_func_t handler, void* data)
85 {
86     int i, err;                     
87     aubio_alsa_seq_driver_t* dev;   /**< object to return */
88     pthread_attr_t attr;            /**< sequencer thread */
89     int sched = SCHED_FIFO;         /**< default scheduling policy */
90     struct sched_param priority;    /**< scheduling priority settings */
91     int count;                      /**< number of MIDI file descriptors */
92     struct pollfd *pfd = NULL;      /**< poll file descriptor array (copied in dev->pfd) */
93     char* device = NULL;            /**< the device name */
94     char* id = NULL;
95     char full_id[64];
96     char full_name[64];
97
98     /* not much use doing anything */
99     if (handler == NULL) {
100         AUBIO_ERR( "Invalid argument");
101         return NULL;
102     }
103
104     /* allocate the device */
105     dev = AUBIO_NEW(aubio_alsa_seq_driver_t);
106     if (dev == NULL) {
107         AUBIO_ERR( "Out of memory");
108         return NULL;
109     }
110     AUBIO_MEMSET(dev, 0, sizeof(aubio_alsa_seq_driver_t));
111     dev->seq_port = -1;
112     dev->driver.data = data;
113     dev->driver.handler = handler;
114
115     /* get the device name. if none is specified, use the default device. */
116     //aubio_settings_getstr(settings, "midi.alsa_seq.device", &device);
117     if (device == NULL) {
118         device = "default";
119     }
120
121     /* open the sequencer INPUT only, non-blocking */
122     //if ((err = snd_seq_open(&dev->seq_handle, device, SND_SEQ_OPEN_INPUT,
123     if ((err = snd_seq_open(&dev->seq_handle, device, SND_SEQ_OPEN_DUPLEX,
124                     SND_SEQ_NONBLOCK)) < 0) {
125         AUBIO_ERR( "Error opening ALSA sequencer");
126         goto error_recovery;
127     }
128
129     /* tell the ladcca server our client id */
130 #ifdef LADCCA_SUPPORT
131     {
132         int enable_ladcca = 1;
133         //aubio_settings_getint (settings, "ladcca.enable", &enable_ladcca);
134         if (enable_ladcca)
135             cca_alsa_client_id (aubio_cca_client, snd_seq_client_id (dev->seq_handle));
136     }
137 #endif /* LADCCA_SUPPORT */
138
139     /* get # of MIDI file descriptors */
140     count = snd_seq_poll_descriptors_count(dev->seq_handle, POLLIN);
141     if (count > 0) {        /* make sure there are some */
142         pfd = AUBIO_MALLOC(sizeof (struct pollfd) * count);
143         dev->pfd = AUBIO_MALLOC(sizeof (struct pollfd) * count);
144         /* grab file descriptor POLL info structures */
145         count = snd_seq_poll_descriptors(dev->seq_handle, pfd, count, POLLIN);
146     }
147
148     for (i = 0; i < count; i++) {        /* loop over file descriptors */
149         /* copy the input FDs */
150         if (pfd[i].events & POLLIN) { /* use only the input FDs */
151             dev->pfd[dev->npfd].fd = pfd[i].fd;
152             dev->pfd[dev->npfd].events = POLLIN; 
153             dev->pfd[dev->npfd].revents = 0; 
154             dev->npfd++;
155         }
156     }
157     AUBIO_FREE(pfd);
158
159     //aubio_settings_getstr(settings, "midi.alsa_seq.id", &id);
160
161     if (id != NULL) {
162         if (AUBIO_STRCMP(id, "pid") == 0) {
163             snprintf(full_id, 64, "aubio (%d)", getpid());
164             snprintf(full_name, 64, "aubio_port (%d)", getpid());
165         } else {
166             snprintf(full_id, 64, "aubio (%s)", id);
167             snprintf(full_name, 64, "aubio_port (%s)", id);
168         }
169     } else {
170         snprintf(full_id, 64, "aubio");
171         snprintf(full_name, 64, "aubio_port");
172     }
173
174     /* set the client name */
175     snd_seq_set_client_name (dev->seq_handle, full_id);
176
177     if ((dev->seq_port = snd_seq_create_simple_port (dev->seq_handle,
178                     full_name,
179                     SND_SEQ_PORT_CAP_WRITE | SND_SEQ_PORT_CAP_SUBS_WRITE |
180                     SND_SEQ_PORT_CAP_READ | SND_SEQ_PORT_CAP_SUBS_READ | 
181                     SND_SEQ_PORT_CAP_DUPLEX,
182                     SND_SEQ_PORT_TYPE_APPLICATION)) < 0)
183     {
184         AUBIO_ERR( "Error creating ALSA sequencer port");
185         goto error_recovery;
186     }
187
188     dev->status = AUBIO_MIDI_READY;
189
190     /* create the midi thread */
191     if (pthread_attr_init(&attr)) {
192         AUBIO_ERR( "Couldn't initialize midi thread attributes");
193         goto error_recovery;
194     }
195
196     /* use fifo scheduling. if it fails, use default scheduling. */
197     while (1) {
198         err = pthread_attr_setschedpolicy(&attr, sched);
199         if (err) {
200             AUBIO_MSG( "Couldn't set high priority scheduling for the MIDI input");
201             if (sched == SCHED_FIFO) {
202                 sched = SCHED_OTHER;
203                 continue;
204             } else {
205                 AUBIO_ERR( "Couldn't set scheduling policy.");
206                 goto error_recovery;
207             }
208         }
209
210         /* SCHED_FIFO will not be active without setting the priority */
211         priority.sched_priority = (sched == SCHED_FIFO) ? ALSA_SEQ_SCHED_PRIORITY : 0;
212         pthread_attr_setschedparam (&attr, &priority);
213
214         err = pthread_create(&dev->thread, &attr, aubio_alsa_seq_run, (void*) dev);
215         if (err) {
216             AUBIO_ERR( "Couldn't set high priority scheduling for the MIDI input");
217             if (sched == SCHED_FIFO) {
218                 sched = SCHED_OTHER;
219                 continue;
220             } else {
221                 //AUBIO_LOG(AUBIO_PANIC, "Couldn't create the midi thread.");
222                 AUBIO_ERR( "Couldn't create the midi thread.");
223                 goto error_recovery;
224             }
225         }
226         break;
227     }
228     return (aubio_midi_driver_t*) dev;
229
230
231 error_recovery:
232     del_aubio_alsa_seq_driver((aubio_midi_driver_t*) dev);
233     return NULL;
234 }
235
236 /** del_aubio_alsa_seq_driver */
237 int del_aubio_alsa_seq_driver(aubio_midi_driver_t* p)
238 {
239     aubio_alsa_seq_driver_t* dev;
240
241     dev = (aubio_alsa_seq_driver_t*) p;
242     if (dev == NULL) {
243         return AUBIO_OK;
244     }
245
246     dev->status = AUBIO_MIDI_DONE;
247
248     /* cancel the thread and wait for it before cleaning up */
249     if (dev->thread) {
250         if (pthread_cancel(dev->thread)) {
251             AUBIO_ERR( "Failed to cancel the midi thread");
252             return AUBIO_FAIL;
253         }
254         if (pthread_join(dev->thread, NULL)) {
255             AUBIO_ERR( "Failed to join the midi thread");
256             return AUBIO_FAIL;
257         }
258     }
259     if (dev->seq_port >= 0) {
260         snd_seq_delete_simple_port (dev->seq_handle, dev->seq_port);
261     }
262     if (dev->seq_handle) {
263         snd_seq_drain_output(dev->seq_handle);
264         snd_seq_close(dev->seq_handle);
265     }
266     AUBIO_FREE(dev);
267     return AUBIO_OK;
268 }
269
270 /** aubio_alsa_seq_run */
271 void* aubio_alsa_seq_run(void* d)
272 {
273     int n;//, i;
274     snd_seq_event_t *seq_ev;
275     aubio_midi_event_t evt;
276     aubio_alsa_seq_driver_t* dev = (aubio_alsa_seq_driver_t*) d;
277
278     /* make sure the other threads can cancel this thread any time */
279     if (pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL)) {
280         AUBIO_ERR( "Failed to set the cancel state of the midi thread");
281         pthread_exit(NULL);
282     }
283     if (pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL)) {
284         AUBIO_ERR( "Failed to set the cancel state of the midi thread");
285         pthread_exit(NULL);
286     }
287
288     /* go into a loop until someone tells us to stop */
289     dev->status = AUBIO_MIDI_LISTENING;
290     while (dev->status == AUBIO_MIDI_LISTENING) {
291
292         /* is there something to read? */
293         n = poll(dev->pfd, dev->npfd, 1); /* use a 1 milliseconds timeout */
294         if (n < 0) {
295             perror("poll");
296         } else if (n > 0) {
297
298             /* read new events from the midi input port */
299             while ((n = snd_seq_event_input(dev->seq_handle, &seq_ev)) >= 0)
300             {
301                 switch (seq_ev->type)
302                 {
303                     case SND_SEQ_EVENT_NOTEON:
304                         evt.type = NOTE_ON;
305                         evt.channel = seq_ev->data.note.channel;
306                         evt.param1 = seq_ev->data.note.note;
307                         evt.param2 = seq_ev->data.note.velocity;
308                         break;
309                     case SND_SEQ_EVENT_NOTEOFF:
310                         evt.type = NOTE_OFF;
311                         evt.channel = seq_ev->data.note.channel;
312                         evt.param1 = seq_ev->data.note.note;
313                         evt.param2 = seq_ev->data.note.velocity;
314                         break;
315                     case SND_SEQ_EVENT_KEYPRESS:
316                         evt.type = KEY_PRESSURE;
317                         evt.channel = seq_ev->data.note.channel;
318                         evt.param1 = seq_ev->data.note.note;
319                         evt.param2 = seq_ev->data.note.velocity;
320                         break;
321                     case SND_SEQ_EVENT_CONTROLLER:
322                         evt.type = CONTROL_CHANGE;
323                         evt.channel = seq_ev->data.control.channel;
324                         evt.param1 = seq_ev->data.control.param;
325                         evt.param2 = seq_ev->data.control.value;
326                         break;
327                     case SND_SEQ_EVENT_PITCHBEND:
328                         evt.type = PITCH_BEND;
329                         evt.channel = seq_ev->data.control.channel;
330                         /* ALSA pitch bend is -8192 - 8191, we adjust it here */
331                         evt.param1 = seq_ev->data.control.value + 8192;
332                         break;
333                     case SND_SEQ_EVENT_PGMCHANGE:
334                         evt.type = PROGRAM_CHANGE;
335                         evt.channel = seq_ev->data.control.channel;
336                         evt.param1 = seq_ev->data.control.value;
337                         break;
338                     case SND_SEQ_EVENT_CHANPRESS:
339                         evt.type = CHANNEL_PRESSURE;
340                         evt.channel = seq_ev->data.control.channel;
341                         evt.param1 = seq_ev->data.control.value;
342                         break;
343                     default:
344                         continue;        /* unhandled event, next loop iteration */
345                 }
346
347                 /* send the events to the next link in the chain */
348                 (*dev->driver.handler)(dev->driver.data, &evt);
349                 
350                 /* dump input on output */
351                 //snd_seq_ev_set_source(new_ev, dev->seq_port);
352                 //snd_seq_ev_set_dest(seq_ev,dev->seq_handle,dev->seq_client);
353                 //snd_seq_ev_set_subs(new_ev);
354                 //snd_seq_ev_set_direct(new_ev);
355                 //snd_seq_event_output(dev->seq_handle, new_ev);
356                 //snd_seq_drain_output(dev->seq_handle);
357
358             }
359         }
360
361         if ((n < 0) && (n != -EAGAIN)) {
362             AUBIO_ERR( "Error occured while reading ALSA sequencer events");
363             dev->status = AUBIO_MIDI_DONE;
364         }
365
366 //        /* added by piem to handle new data to output */
367 //        while (/* get new data, but from where ??? (n = snd_seq_event_output(dev->seq_handle, seq_ev)) >= 0*/ )
368 //        {
369 //            /* dump input on output */
370 //            snd_seq_ev_set_source(new_ev, dev->seq_port);
371 //            //snd_seq_ev_set_dest(seq_ev,dev->seq_handle,dev->seq_client);
372 //            snd_seq_ev_set_subs(new_ev);
373 //            snd_seq_ev_set_direct(new_ev);
374 //            snd_seq_event_output(dev->seq_handle, new_ev);
375 //            snd_seq_drain_output(dev->seq_handle);
376 //        }
377
378     }
379     pthread_exit(NULL);
380 }
381
382
383 snd_seq_event_t ev;
384
385 void aubio_midi_direct_output(aubio_midi_driver_t * d, aubio_midi_event_t * event) 
386 {
387     aubio_alsa_seq_driver_t* dev = (aubio_alsa_seq_driver_t*) d;
388     /*
389     if (pthread_join(dev->thread, NULL)) {
390         AUBIO_ERR( "Failed to join the midi thread");
391     }
392     */
393     switch(event->type) 
394     {
395         case NOTE_ON:
396             ev.type = SND_SEQ_EVENT_NOTEON;
397             ev.data.note.channel  = event->channel;
398             ev.data.note.note     = event->param1;
399             ev.data.note.velocity = event->param2;
400             //AUBIO_ERR( "NOTE_ON %d\n", event->param1);
401             break;
402         case NOTE_OFF:
403             ev.type = SND_SEQ_EVENT_NOTEOFF;
404             ev.data.note.channel  = event->channel;
405             ev.data.note.note     = event->param1;
406             ev.data.note.velocity = event->param2;
407             //AUBIO_ERR( "NOTE_OFF %d\n", event->param1);
408             break;
409         default:
410             break;
411     }
412     if (ev.type == SND_SEQ_EVENT_NOTEOFF || ev.type == SND_SEQ_EVENT_NOTEON ) { 
413         snd_seq_ev_set_subs(&ev);
414         snd_seq_ev_set_direct(&ev);
415         snd_seq_ev_set_source(&ev, dev->seq_port);
416         snd_seq_event_output_direct(dev->seq_handle, &ev);
417     }
418     /*
419     if (pthread_detach(dev->thread)) {
420         AUBIO_ERR( "Failed to leave the midi thread");
421     }
422     */
423 }
424
425 #endif /* #if ALSA_SUPPORT */