source: ext/midi/midi_alsa_raw.c @ b60dd4ae

feature/autosinkfeature/constantqfeature/pitchshiftfeature/pydocstringsfeature/timestretchpitchshiftsamplertimestretchyinfft+
Last change on this file since b60dd4ae was b60dd4ae, checked in by Paul Brossier <piem@altern.org>, 16 years ago

moved midi functions to ext/

  • Property mode set to 100644
File size: 9.1 KB
Line 
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
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
33#if ALSA_SUPPORT
34
35#define ALSA_PCM_NEW_HW_PARAMS_API
36#include <alsa/asoundlib.h>
37#include <pthread.h>
38#include <fcntl.h>
39#include <unistd.h>
40#include <sys/poll.h>
41/* #include <errno.h> //perror is in stdio.h */
42
43#include "config.h"
44
45#define AUBIO_ALSA_DEFAULT_MIDI_DEVICE  "default"
46
47/** \bug double define? */
48#define AUBIO_ALSA_BUFFER_LENGTH 512
49
50/* SCHED_FIFO priorities for ALSA threads (see pthread_attr_setschedparam) */
51#define ALSA_RAWMIDI_SCHED_PRIORITY 90
52#define ALSA_SEQ_SCHED_PRIORITY 90
53
54/** aubio_midi_alsa_raw_driver_t */
55typedef struct {
56  aubio_midi_driver_t driver;
57  snd_rawmidi_t *rawmidi_in;
58  snd_rawmidi_t *rawmidi_out;
59  struct pollfd *pfd;
60  int npfd;
61  pthread_t thread;
62  int status;
63  unsigned char buffer[AUBIO_ALSA_BUFFER_LENGTH];
64  aubio_midi_parser_t* parser;
65} aubio_midi_alsa_raw_driver_t;
66
67aubio_midi_driver_t* new_aubio_midi_alsa_raw_driver(//aubio_settings_t* settings,
68    handle_midi_event_func_t handler, 
69    void* event_handler_data);
70int del_aubio_midi_alsa_raw_driver(aubio_midi_driver_t* p);
71static void* aubio_midi_alsa_raw_run(void* d);
72
73
74/**************************************************************
75 *
76 *        Alsa MIDI driver
77 *
78 */
79
80//void aubio_midi_alsa_raw_driver_settings(aubio_settings_t* settings)
81//{
82//  aubio_settings_register_str(settings, "midi.alsa.device", "default", 0, NULL, NULL);
83//}
84
85/** new_aubio_midi_alsa_raw_driver */
86aubio_midi_driver_t* new_aubio_midi_alsa_raw_driver(
87    //aubio_settings_t* settings,
88    handle_midi_event_func_t handler, 
89    void* data)
90{
91  int i, err;
92  aubio_midi_alsa_raw_driver_t* dev;
93  pthread_attr_t attr;
94  int sched = SCHED_FIFO;
95  struct sched_param priority;
96  int count;
97  struct pollfd *pfd = NULL;
98  char* device = NULL;
99
100  /* not much use doing anything */
101  if (handler == NULL) {
102    AUBIO_ERR("Invalid argument");
103    return NULL;
104  }
105
106  /* allocate the device */
107  dev = AUBIO_NEW(aubio_midi_alsa_raw_driver_t);
108  if (dev == NULL) {
109    AUBIO_ERR( "Out of memory");
110    return NULL;
111  }
112  AUBIO_MEMSET(dev, 0, sizeof(aubio_midi_alsa_raw_driver_t));
113
114  dev->driver.handler = handler;
115  dev->driver.data = data;
116
117  /* allocate one event to store the input data */
118  dev->parser = new_aubio_midi_parser();
119  if (dev->parser == NULL) {
120    AUBIO_ERR( "Out of memory");
121    goto error_recovery;
122  }
123
124  /* get the device name. if none is specified, use the default device. */
125  //aubio_settings_getstr(settings, "midi.alsa.device", &device);
126  if (device == NULL) {
127    device = "default";
128  }
129
130  /* open the hardware device. only use midi in. */
131  if ((err = snd_rawmidi_open(&dev->rawmidi_in, NULL, device, SND_RAWMIDI_NONBLOCK)) < 0) {
132  //if ((err = snd_rawmidi_open(&dev->rawmidi_in, &dev->rawmidi_out, device, SND_RAWMIDI_NONBLOCK)) < 0) {
133    AUBIO_ERR( "Error opening ALSA raw MIDI IN port");
134    goto error_recovery;
135  }
136
137  /* get # of MIDI file descriptors */
138  count = snd_rawmidi_poll_descriptors_count(dev->rawmidi_in);
139  if (count > 0) {              /* make sure there are some */
140    pfd = AUBIO_MALLOC(sizeof (struct pollfd) * count);
141    dev->pfd = AUBIO_MALLOC(sizeof (struct pollfd) * count);
142    /* grab file descriptor POLL info structures */
143    count = snd_rawmidi_poll_descriptors(dev->rawmidi_in, pfd, count);
144  }
145
146  /* copy the input FDs */
147  for (i = 0; i < count; i++) {         /* loop over file descriptors */
148    if (pfd[i].events & POLLIN) { /* use only the input FDs */
149      dev->pfd[dev->npfd].fd = pfd[i].fd;
150      dev->pfd[dev->npfd].events = POLLIN; 
151      dev->pfd[dev->npfd].revents = 0; 
152      dev->npfd++;
153    }
154  }
155  AUBIO_FREE(pfd);
156
157
158 
159  dev->status = AUBIO_MIDI_READY;
160
161  /* create the midi thread */
162  if (pthread_attr_init(&attr)) {
163    AUBIO_ERR( "Couldn't initialize midi thread attributes");
164    goto error_recovery;
165  }
166
167  /* Was: "use fifo scheduling. if it fails, use default scheduling." */
168  /* Now normal scheduling is used by default for the MIDI thread. The reason is,
169   * that fluidsynth works better with low latencies under heavy load, if only the
170   * audio thread is prioritized.
171   * With MIDI at ordinary priority, that could result in individual notes being played
172   * a bit late. On the other hand, if the audio thread is delayed, an audible dropout
173   * is the result.
174   * To reproduce this: Edirol UA-1 USB-MIDI interface, four buffers
175   * with 45 samples each (roughly 4 ms latency), ravewave soundfont. -MN
176   */ 
177
178  /* Not so sure anymore. We're losing MIDI data, if we can't keep up with
179   * the speed it is generated. */
180  /* AUBIO_MSG("Note: High-priority scheduling for the MIDI thread was intentionally disabled.");
181      sched=SCHED_OTHER;*/
182
183  while (1) {
184    err = pthread_attr_setschedpolicy(&attr, sched);
185    if (err) {
186      //AUBIO_LOG(AUBIO_WARN, "Couldn't set high priority scheduling for the MIDI input");
187      AUBIO_MSG( "Couldn't set high priority scheduling for the MIDI input");
188      if (sched == SCHED_FIFO) {
189        sched = SCHED_OTHER;
190        continue;
191      } else {
192        AUBIO_ERR( "Couldn't set scheduling policy.");
193        goto error_recovery;
194      }
195    }
196
197    /* SCHED_FIFO will not be active without setting the priority */
198    priority.sched_priority = (sched == SCHED_FIFO) ? 
199      ALSA_RAWMIDI_SCHED_PRIORITY : 0;
200    pthread_attr_setschedparam (&attr, &priority);
201    err = pthread_create(&dev->thread, &attr, aubio_midi_alsa_raw_run, (void*) dev);
202    if (err) {
203      AUBIO_MSG( "Couldn't set high priority scheduling for the MIDI input");
204      if (sched == SCHED_FIFO) {
205        sched = SCHED_OTHER;
206        continue;
207      } else {
208        AUBIO_ERR( "Couldn't create the midi thread.");
209        goto error_recovery;
210      }
211    }
212    break;
213  } 
214  return (aubio_midi_driver_t*) dev;
215
216error_recovery:
217  del_aubio_midi_alsa_raw_driver((aubio_midi_driver_t*) dev);
218  return NULL;
219
220}
221
222/** del_aubio_midi_alsa_raw_driver */
223int del_aubio_midi_alsa_raw_driver(aubio_midi_driver_t* p)
224{
225  aubio_midi_alsa_raw_driver_t* dev;
226
227  dev = (aubio_midi_alsa_raw_driver_t*) p;
228  if (dev == NULL) {
229    return AUBIO_OK;
230  }
231
232  dev->status = AUBIO_MIDI_DONE;
233
234  /* cancel the thread and wait for it before cleaning up */
235  if (dev->thread) {
236    if (pthread_cancel(dev->thread)) {
237      AUBIO_ERR( "Failed to cancel the midi thread");
238      return AUBIO_FAIL;
239    }
240    if (pthread_join(dev->thread, NULL)) {
241      AUBIO_ERR( "Failed to join the midi thread");
242      return AUBIO_FAIL;
243    }
244  }
245  if (dev->rawmidi_in) {
246    snd_rawmidi_drain(dev->rawmidi_in);
247    snd_rawmidi_close(dev->rawmidi_in);
248  }
249  if (dev->rawmidi_out) {
250    snd_rawmidi_drain(dev->rawmidi_out);
251    snd_rawmidi_close(dev->rawmidi_in);
252  }
253  if (dev->parser != NULL) {
254    del_aubio_midi_parser(dev->parser);
255  }
256  AUBIO_FREE(dev);
257  return AUBIO_OK;
258}
259
260/** aubio_midi_alsa_raw_run */
261void * aubio_midi_alsa_raw_run(void* d)
262{
263  int n, i;
264  aubio_midi_event_t* evt;
265  aubio_midi_alsa_raw_driver_t* dev = (aubio_midi_alsa_raw_driver_t*) d;
266
267  /* make sure the other threads can cancel this thread any time */
268  if (pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL)) {
269    AUBIO_ERR( "Failed to set the cancel state of the midi thread");
270    pthread_exit(NULL);
271  }
272  if (pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL)) {
273    AUBIO_ERR( "Failed to set the cancel state of the midi thread");
274    pthread_exit(NULL);
275  }
276
277  /* go into a loop until someone tells us to stop */
278  dev->status = AUBIO_MIDI_LISTENING;
279  while (dev->status == AUBIO_MIDI_LISTENING) {
280
281    /* is there something to read? */
282    /* use a 100 milliseconds timeout */
283    n = poll(dev->pfd, dev->npfd, 100); 
284    if (n < 0) {
285      perror("poll");
286    } else if (n > 0) {
287
288      /* read new data */
289      n = snd_rawmidi_read(dev->rawmidi_in, dev->buffer, 
290          AUBIO_ALSA_BUFFER_LENGTH);
291      if ((n < 0) && (n != -EAGAIN)) {
292        AUBIO_ERR( "Failed to read the midi input");
293        dev->status = AUBIO_MIDI_DONE;
294      }
295
296      /* let the parser convert the data into events */
297      for (i = 0; i < n; i++) {
298        evt = aubio_midi_parser_parse(dev->parser, dev->buffer[i]);
299        if (evt != NULL) {
300          (*dev->driver.handler)(dev->driver.data, evt);
301        }
302      }
303    };
304  }
305  pthread_exit(NULL);
306}
307
308#endif /* #if ALSA_SUPPORT */
Note: See TracBrowser for help on using the repository browser.