ref: dde06ad6e5f5aece9077aad8b8d8de87e8630136
dir: /ext/midi/midi_alsa_raw.c/
/*
* Copyright 2004 Paul Brossier
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public License
* as published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the Free
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
* 02111-1307, USA
*/
/* this file originally taken from Fluidsynth, Peter Hanappe and others. */
/** \file
* Midi driver for the Advanced Linux Sound Architecture
*/
#include "aubio_priv.h"
#include "midi.h"
#include "midi_event.h"
#include "midi_parser.h"
#include "midi_driver.h"
#if ALSA_SUPPORT
#define ALSA_PCM_NEW_HW_PARAMS_API
#include <alsa/asoundlib.h>
#include <pthread.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/poll.h>
/* #include <errno.h> //perror is in stdio.h */
#include "config.h"
#define AUBIO_ALSA_DEFAULT_MIDI_DEVICE "default"
/** \bug double define? */
#define AUBIO_ALSA_BUFFER_LENGTH 512
/* SCHED_FIFO priorities for ALSA threads (see pthread_attr_setschedparam) */
#define ALSA_RAWMIDI_SCHED_PRIORITY 90
#define ALSA_SEQ_SCHED_PRIORITY 90
/** aubio_midi_alsa_raw_driver_t */
typedef struct {
aubio_midi_driver_t driver;
snd_rawmidi_t *rawmidi_in;
snd_rawmidi_t *rawmidi_out;
struct pollfd *pfd;
int npfd;
pthread_t thread;
int status;
unsigned char buffer[AUBIO_ALSA_BUFFER_LENGTH];
aubio_midi_parser_t* parser;
} aubio_midi_alsa_raw_driver_t;
aubio_midi_driver_t* new_aubio_midi_alsa_raw_driver(//aubio_settings_t* settings,
handle_midi_event_func_t handler,
void* event_handler_data);
int del_aubio_midi_alsa_raw_driver(aubio_midi_driver_t* p);
static void* aubio_midi_alsa_raw_run(void* d);
/**************************************************************
*
* Alsa MIDI driver
*
*/
//void aubio_midi_alsa_raw_driver_settings(aubio_settings_t* settings)
//{
// aubio_settings_register_str(settings, "midi.alsa.device", "default", 0, NULL, NULL);
//}
/** new_aubio_midi_alsa_raw_driver */
aubio_midi_driver_t* new_aubio_midi_alsa_raw_driver(
//aubio_settings_t* settings,
handle_midi_event_func_t handler,
void* data)
{
int i, err;
aubio_midi_alsa_raw_driver_t* dev;
pthread_attr_t attr;
int sched = SCHED_FIFO;
struct sched_param priority;
int count;
struct pollfd *pfd = NULL;
char* device = NULL;
/* not much use doing anything */
if (handler == NULL) {
AUBIO_ERR("Invalid argument");
return NULL;
}
/* allocate the device */
dev = AUBIO_NEW(aubio_midi_alsa_raw_driver_t);
if (dev == NULL) {
AUBIO_ERR( "Out of memory");
return NULL;
}
AUBIO_MEMSET(dev, 0, sizeof(aubio_midi_alsa_raw_driver_t));
dev->driver.handler = handler;
dev->driver.data = data;
/* allocate one event to store the input data */
dev->parser = new_aubio_midi_parser();
if (dev->parser == NULL) {
AUBIO_ERR( "Out of memory");
goto error_recovery;
}
/* get the device name. if none is specified, use the default device. */
//aubio_settings_getstr(settings, "midi.alsa.device", &device);
if (device == NULL) {
device = "default";
}
/* open the hardware device. only use midi in. */
if ((err = snd_rawmidi_open(&dev->rawmidi_in, NULL, device, SND_RAWMIDI_NONBLOCK)) < 0) {
//if ((err = snd_rawmidi_open(&dev->rawmidi_in, &dev->rawmidi_out, device, SND_RAWMIDI_NONBLOCK)) < 0) {
AUBIO_ERR( "Error opening ALSA raw MIDI IN port");
goto error_recovery;
}
/* get # of MIDI file descriptors */
count = snd_rawmidi_poll_descriptors_count(dev->rawmidi_in);
if (count > 0) { /* make sure there are some */
pfd = AUBIO_MALLOC(sizeof (struct pollfd) * count);
dev->pfd = AUBIO_MALLOC(sizeof (struct pollfd) * count);
/* grab file descriptor POLL info structures */
count = snd_rawmidi_poll_descriptors(dev->rawmidi_in, pfd, count);
}
/* copy the input FDs */
for (i = 0; i < count; i++) { /* loop over file descriptors */
if (pfd[i].events & POLLIN) { /* use only the input FDs */
dev->pfd[dev->npfd].fd = pfd[i].fd;
dev->pfd[dev->npfd].events = POLLIN;
dev->pfd[dev->npfd].revents = 0;
dev->npfd++;
}
}
AUBIO_FREE(pfd);
dev->status = AUBIO_MIDI_READY;
/* create the midi thread */
if (pthread_attr_init(&attr)) {
AUBIO_ERR( "Couldn't initialize midi thread attributes");
goto error_recovery;
}
/* Was: "use fifo scheduling. if it fails, use default scheduling." */
/* Now normal scheduling is used by default for the MIDI thread. The reason is,
* that fluidsynth works better with low latencies under heavy load, if only the
* audio thread is prioritized.
* With MIDI at ordinary priority, that could result in individual notes being played
* a bit late. On the other hand, if the audio thread is delayed, an audible dropout
* is the result.
* To reproduce this: Edirol UA-1 USB-MIDI interface, four buffers
* with 45 samples each (roughly 4 ms latency), ravewave soundfont. -MN
*/
/* Not so sure anymore. We're losing MIDI data, if we can't keep up with
* the speed it is generated. */
/* AUBIO_MSG("Note: High-priority scheduling for the MIDI thread was intentionally disabled.");
sched=SCHED_OTHER;*/
while (1) {
err = pthread_attr_setschedpolicy(&attr, sched);
if (err) {
//AUBIO_LOG(AUBIO_WARN, "Couldn't set high priority scheduling for the MIDI input");
AUBIO_MSG( "Couldn't set high priority scheduling for the MIDI input");
if (sched == SCHED_FIFO) {
sched = SCHED_OTHER;
continue;
} else {
AUBIO_ERR( "Couldn't set scheduling policy.");
goto error_recovery;
}
}
/* SCHED_FIFO will not be active without setting the priority */
priority.sched_priority = (sched == SCHED_FIFO) ?
ALSA_RAWMIDI_SCHED_PRIORITY : 0;
pthread_attr_setschedparam (&attr, &priority);
err = pthread_create(&dev->thread, &attr, aubio_midi_alsa_raw_run, (void*) dev);
if (err) {
AUBIO_MSG( "Couldn't set high priority scheduling for the MIDI input");
if (sched == SCHED_FIFO) {
sched = SCHED_OTHER;
continue;
} else {
AUBIO_ERR( "Couldn't create the midi thread.");
goto error_recovery;
}
}
break;
}
return (aubio_midi_driver_t*) dev;
error_recovery:
del_aubio_midi_alsa_raw_driver((aubio_midi_driver_t*) dev);
return NULL;
}
/** del_aubio_midi_alsa_raw_driver */
int del_aubio_midi_alsa_raw_driver(aubio_midi_driver_t* p)
{
aubio_midi_alsa_raw_driver_t* dev;
dev = (aubio_midi_alsa_raw_driver_t*) p;
if (dev == NULL) {
return AUBIO_OK;
}
dev->status = AUBIO_MIDI_DONE;
/* cancel the thread and wait for it before cleaning up */
if (dev->thread) {
if (pthread_cancel(dev->thread)) {
AUBIO_ERR( "Failed to cancel the midi thread");
return AUBIO_FAIL;
}
if (pthread_join(dev->thread, NULL)) {
AUBIO_ERR( "Failed to join the midi thread");
return AUBIO_FAIL;
}
}
if (dev->rawmidi_in) {
snd_rawmidi_drain(dev->rawmidi_in);
snd_rawmidi_close(dev->rawmidi_in);
}
if (dev->rawmidi_out) {
snd_rawmidi_drain(dev->rawmidi_out);
snd_rawmidi_close(dev->rawmidi_in);
}
if (dev->parser != NULL) {
del_aubio_midi_parser(dev->parser);
}
AUBIO_FREE(dev);
return AUBIO_OK;
}
/** aubio_midi_alsa_raw_run */
void * aubio_midi_alsa_raw_run(void* d)
{
int n, i;
aubio_midi_event_t* evt;
aubio_midi_alsa_raw_driver_t* dev = (aubio_midi_alsa_raw_driver_t*) d;
/* make sure the other threads can cancel this thread any time */
if (pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL)) {
AUBIO_ERR( "Failed to set the cancel state of the midi thread");
pthread_exit(NULL);
}
if (pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL)) {
AUBIO_ERR( "Failed to set the cancel state of the midi thread");
pthread_exit(NULL);
}
/* go into a loop until someone tells us to stop */
dev->status = AUBIO_MIDI_LISTENING;
while (dev->status == AUBIO_MIDI_LISTENING) {
/* is there something to read? */
/* use a 100 milliseconds timeout */
n = poll(dev->pfd, dev->npfd, 100);
if (n < 0) {
perror("poll");
} else if (n > 0) {
/* read new data */
n = snd_rawmidi_read(dev->rawmidi_in, dev->buffer,
AUBIO_ALSA_BUFFER_LENGTH);
if ((n < 0) && (n != -EAGAIN)) {
AUBIO_ERR( "Failed to read the midi input");
dev->status = AUBIO_MIDI_DONE;
}
/* let the parser convert the data into events */
for (i = 0; i < n; i++) {
evt = aubio_midi_parser_parse(dev->parser, dev->buffer[i]);
if (evt != NULL) {
(*dev->driver.handler)(dev->driver.data, evt);
}
}
};
}
pthread_exit(NULL);
}
#endif /* #if ALSA_SUPPORT */