2003-03-11 16:56:45 +00:00
|
|
|
/* FluidSynth - A Software Synthesizer
|
|
|
|
*
|
|
|
|
* Copyright (C) 2003 Peter Hanappe and others.
|
|
|
|
*
|
|
|
|
* This library is free software; you can redistribute it and/or
|
2017-07-12 17:45:23 +02:00
|
|
|
* modify it under the terms of the GNU Lesser General Public License
|
2017-07-12 17:53:03 +02:00
|
|
|
* as published by the Free Software Foundation; either version 2.1 of
|
2003-03-11 16:56:45 +00:00
|
|
|
* 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
|
2017-07-12 17:45:23 +02:00
|
|
|
* Lesser General Public License for more details.
|
2007-03-04 16:45:05 +00:00
|
|
|
*
|
2017-07-12 17:54:54 +02:00
|
|
|
* You should have received a copy of the GNU Lesser General Public
|
2003-03-11 16:56:45 +00:00
|
|
|
* License along with this library; if not, write to the Free
|
2011-08-15 12:57:10 +00:00
|
|
|
* Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
|
|
|
* 02110-1301, USA
|
2003-03-11 16:56:45 +00:00
|
|
|
*/
|
|
|
|
|
|
|
|
/* fluid_alsa.c
|
|
|
|
*
|
|
|
|
* Driver for the Advanced Linux Sound Architecture
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "fluid_synth.h"
|
|
|
|
#include "fluid_midi.h"
|
|
|
|
#include "fluid_adriver.h"
|
|
|
|
#include "fluid_mdriver.h"
|
|
|
|
#include "fluid_settings.h"
|
|
|
|
|
|
|
|
#if ALSA_SUPPORT
|
2003-03-12 00:58:20 +00:00
|
|
|
|
|
|
|
#define ALSA_PCM_NEW_HW_PARAMS_API
|
2003-03-11 16:56:45 +00:00
|
|
|
#include <alsa/asoundlib.h>
|
|
|
|
#include <fcntl.h>
|
|
|
|
#include <unistd.h>
|
|
|
|
#include <errno.h>
|
|
|
|
#include <sys/poll.h>
|
2010-10-08 08:15:13 +00:00
|
|
|
#include <glib.h>
|
2003-03-11 16:56:45 +00:00
|
|
|
|
|
|
|
#include "config.h"
|
|
|
|
|
2006-02-19 10:01:58 +00:00
|
|
|
#include "fluid_lash.h"
|
2003-03-11 16:56:45 +00:00
|
|
|
|
|
|
|
#define FLUID_ALSA_DEFAULT_MIDI_DEVICE "default"
|
|
|
|
#define FLUID_ALSA_DEFAULT_SEQ_DEVICE "default"
|
|
|
|
|
|
|
|
#define BUFFER_LENGTH 512
|
|
|
|
|
2004-03-29 10:05:18 +00:00
|
|
|
/** fluid_alsa_audio_driver_t
|
2007-03-04 16:45:05 +00:00
|
|
|
*
|
2003-03-12 00:58:20 +00:00
|
|
|
* This structure should not be accessed directly. Use audio port
|
2007-03-04 16:45:05 +00:00
|
|
|
* functions instead.
|
2003-03-12 00:58:20 +00:00
|
|
|
*/
|
|
|
|
typedef struct {
|
|
|
|
fluid_audio_driver_t driver;
|
|
|
|
snd_pcm_t* pcm;
|
|
|
|
fluid_audio_func_t callback;
|
|
|
|
void* data;
|
|
|
|
int buffer_size;
|
2009-10-30 21:49:54 +00:00
|
|
|
fluid_thread_t *thread;
|
2003-03-12 00:58:20 +00:00
|
|
|
int cont;
|
|
|
|
} fluid_alsa_audio_driver_t;
|
|
|
|
|
|
|
|
|
|
|
|
fluid_audio_driver_t* new_fluid_alsa_audio_driver(fluid_settings_t* settings,
|
|
|
|
fluid_synth_t* synth);
|
|
|
|
fluid_audio_driver_t* new_fluid_alsa_audio_driver2(fluid_settings_t* settings,
|
|
|
|
fluid_audio_func_t func, void* data);
|
|
|
|
|
|
|
|
int delete_fluid_alsa_audio_driver(fluid_audio_driver_t* p);
|
|
|
|
void fluid_alsa_audio_driver_settings(fluid_settings_t* settings);
|
2017-10-26 18:08:10 +02:00
|
|
|
static fluid_thread_return_t fluid_alsa_audio_run_float(void* d);
|
|
|
|
static fluid_thread_return_t fluid_alsa_audio_run_s16(void* d);
|
2003-03-12 00:58:20 +00:00
|
|
|
|
|
|
|
|
|
|
|
struct fluid_alsa_formats_t {
|
|
|
|
char* name;
|
|
|
|
snd_pcm_format_t format;
|
|
|
|
snd_pcm_access_t access;
|
2009-10-30 21:49:54 +00:00
|
|
|
fluid_thread_func_t run;
|
2003-03-12 00:58:20 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
struct fluid_alsa_formats_t fluid_alsa_formats[] = {
|
2007-03-04 16:45:05 +00:00
|
|
|
{ "s16, rw, interleaved",
|
|
|
|
SND_PCM_FORMAT_S16,
|
|
|
|
SND_PCM_ACCESS_RW_INTERLEAVED,
|
2003-03-12 00:58:20 +00:00
|
|
|
fluid_alsa_audio_run_s16 },
|
2007-03-04 16:45:05 +00:00
|
|
|
{ "float, rw, non interleaved",
|
|
|
|
SND_PCM_FORMAT_FLOAT,
|
|
|
|
SND_PCM_ACCESS_RW_NONINTERLEAVED,
|
2003-04-03 21:32:56 +00:00
|
|
|
fluid_alsa_audio_run_float },
|
2003-03-12 00:58:20 +00:00
|
|
|
{ NULL, 0, 0, NULL }
|
|
|
|
};
|
|
|
|
|
|
|
|
|
2003-03-11 16:56:45 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* fluid_alsa_rawmidi_driver_t
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
typedef struct {
|
|
|
|
fluid_midi_driver_t driver;
|
|
|
|
snd_rawmidi_t *rawmidi_in;
|
|
|
|
struct pollfd *pfd;
|
|
|
|
int npfd;
|
2009-10-30 21:49:54 +00:00
|
|
|
fluid_thread_t *thread;
|
2017-10-28 12:57:47 +02:00
|
|
|
fluid_atomic_int_t should_quit;
|
2003-03-11 16:56:45 +00:00
|
|
|
unsigned char buffer[BUFFER_LENGTH];
|
|
|
|
fluid_midi_parser_t* parser;
|
|
|
|
} fluid_alsa_rawmidi_driver_t;
|
|
|
|
|
|
|
|
|
2007-03-04 16:45:05 +00:00
|
|
|
fluid_midi_driver_t* new_fluid_alsa_rawmidi_driver(fluid_settings_t* settings,
|
|
|
|
handle_midi_event_func_t handler,
|
2003-03-12 00:58:20 +00:00
|
|
|
void* event_handler_data);
|
2003-03-11 16:56:45 +00:00
|
|
|
|
|
|
|
int delete_fluid_alsa_rawmidi_driver(fluid_midi_driver_t* p);
|
2017-10-26 18:08:10 +02:00
|
|
|
static fluid_thread_return_t fluid_alsa_midi_run(void* d);
|
2003-03-11 16:56:45 +00:00
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* fluid_alsa_seq_driver_t
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
typedef struct {
|
|
|
|
fluid_midi_driver_t driver;
|
|
|
|
snd_seq_t *seq_handle;
|
|
|
|
struct pollfd *pfd;
|
|
|
|
int npfd;
|
2009-10-30 21:49:54 +00:00
|
|
|
fluid_thread_t *thread;
|
2017-10-28 12:57:47 +02:00
|
|
|
fluid_atomic_int_t should_quit;
|
2007-03-04 16:45:05 +00:00
|
|
|
int port_count;
|
2003-03-11 16:56:45 +00:00
|
|
|
} fluid_alsa_seq_driver_t;
|
|
|
|
|
2007-03-04 16:45:05 +00:00
|
|
|
fluid_midi_driver_t* new_fluid_alsa_seq_driver(fluid_settings_t* settings,
|
|
|
|
handle_midi_event_func_t handler,
|
2003-03-11 16:56:45 +00:00
|
|
|
void* data);
|
|
|
|
int delete_fluid_alsa_seq_driver(fluid_midi_driver_t* p);
|
2017-10-26 18:08:10 +02:00
|
|
|
static fluid_thread_return_t fluid_alsa_seq_run(void* d);
|
2003-03-11 16:56:45 +00:00
|
|
|
|
2003-03-12 00:58:20 +00:00
|
|
|
/**************************************************************
|
|
|
|
*
|
|
|
|
* Alsa audio driver
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
|
|
|
void fluid_alsa_audio_driver_settings(fluid_settings_t* settings)
|
|
|
|
{
|
|
|
|
fluid_settings_register_str(settings, "audio.alsa.device", "default", 0, NULL, NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2007-03-04 16:45:05 +00:00
|
|
|
fluid_audio_driver_t*
|
2003-03-12 00:58:20 +00:00
|
|
|
new_fluid_alsa_audio_driver(fluid_settings_t* settings,
|
|
|
|
fluid_synth_t* synth)
|
|
|
|
{
|
2006-12-10 16:02:04 +00:00
|
|
|
return new_fluid_alsa_audio_driver2(settings, NULL, synth);
|
2003-03-12 00:58:20 +00:00
|
|
|
}
|
|
|
|
|
2007-03-04 16:45:05 +00:00
|
|
|
fluid_audio_driver_t*
|
2003-03-12 00:58:20 +00:00
|
|
|
new_fluid_alsa_audio_driver2(fluid_settings_t* settings,
|
|
|
|
fluid_audio_func_t func, void* data)
|
|
|
|
{
|
|
|
|
fluid_alsa_audio_driver_t* dev;
|
|
|
|
double sample_rate;
|
|
|
|
int periods, period_size;
|
2009-09-22 07:04:07 +00:00
|
|
|
char* device = NULL;
|
Added public API functions: fluid_synth_sysex, fluid_synth_activate_key_tuning, fluid_synth_activate_octave_tuning, fluid_synth_tune_notes, fluid_synth_activate_tuning and fluid_synth_deactivate_tuning.
Added audio.realtime, audio.realtime-prio, midi.realtime and midi.realtime-prio (only ALSA updated to use them at this point).
Fixed bug in new_fluid_channel() where tuning field was not initialized.
fluid_settings.h had wrong field order in prototypes for fluid_settings_register_num and fluid_settings_register_int.
Added multi core support: "synth.cpu-cores" setting, fluid_synth_core_thread_func() and many core_ variables to fluid_synth_t.
Switched fluid_mutex_t back to a normal non-recursive mutex and added fluid_rec_mutex_t.
Added fluid_cond_t thread synchronization stuff.
Added fluid_cond_mutex_t which is a dynamically allocated regular mutex for use with fluid_cond_t.
fluid_settings_t and fluid_synth_t are now using fluid_rec_mutex_t (same as before, just name change).
Added platform specific fluid_thread_self_set_prio() functions to fluid_sys.c for activating high priority for the calling thread.
Modified new_fluid_thread() to take a prio and prio_level parameters.
Added missing fluid_atomic_pointer_set().
fluid_voice_write() changed to only take a voice audio buffer to render to, mixing is done separately with new fluid_voice_mix().
fluid_voice_write() now returns the count of samples rendered.
fluid_voice_effects() split into fluid_voice_filter() and fluid_voice_mix().
Added dsp_buf_count field to fluid_voice_t to keep track of last count of samples rendered to dsp_buf.
fluid_voice_get_channel() converted to a macro.
Added FLUID_DEFAULT_AUDIO_RT_PRIO and FLUID_DEFAULT_MIDI_RT_PRIO in fluidsynth_priv.h, set to 90 and 80 respectively which get used for audio.realtime-prio and midi.realtime-prio (was 90 and 90 before).
2009-09-29 21:40:28 +00:00
|
|
|
int realtime_prio = 0;
|
2003-03-12 00:58:20 +00:00
|
|
|
int i, err, dir = 0;
|
|
|
|
snd_pcm_hw_params_t* hwparams;
|
|
|
|
snd_pcm_sw_params_t* swparams = NULL;
|
|
|
|
snd_pcm_uframes_t uframes;
|
|
|
|
unsigned int tmp;
|
|
|
|
|
|
|
|
dev = FLUID_NEW(fluid_alsa_audio_driver_t);
|
|
|
|
if (dev == NULL) {
|
|
|
|
FLUID_LOG(FLUID_ERR, "Out of memory");
|
2007-03-04 16:45:05 +00:00
|
|
|
return NULL;
|
2003-03-12 00:58:20 +00:00
|
|
|
}
|
|
|
|
FLUID_MEMSET(dev, 0, sizeof(fluid_alsa_audio_driver_t));
|
|
|
|
|
|
|
|
fluid_settings_getint(settings, "audio.periods", &periods);
|
|
|
|
fluid_settings_getint(settings, "audio.period-size", &period_size);
|
|
|
|
fluid_settings_getnum(settings, "synth.sample-rate", &sample_rate);
|
Added public API functions: fluid_synth_sysex, fluid_synth_activate_key_tuning, fluid_synth_activate_octave_tuning, fluid_synth_tune_notes, fluid_synth_activate_tuning and fluid_synth_deactivate_tuning.
Added audio.realtime, audio.realtime-prio, midi.realtime and midi.realtime-prio (only ALSA updated to use them at this point).
Fixed bug in new_fluid_channel() where tuning field was not initialized.
fluid_settings.h had wrong field order in prototypes for fluid_settings_register_num and fluid_settings_register_int.
Added multi core support: "synth.cpu-cores" setting, fluid_synth_core_thread_func() and many core_ variables to fluid_synth_t.
Switched fluid_mutex_t back to a normal non-recursive mutex and added fluid_rec_mutex_t.
Added fluid_cond_t thread synchronization stuff.
Added fluid_cond_mutex_t which is a dynamically allocated regular mutex for use with fluid_cond_t.
fluid_settings_t and fluid_synth_t are now using fluid_rec_mutex_t (same as before, just name change).
Added platform specific fluid_thread_self_set_prio() functions to fluid_sys.c for activating high priority for the calling thread.
Modified new_fluid_thread() to take a prio and prio_level parameters.
Added missing fluid_atomic_pointer_set().
fluid_voice_write() changed to only take a voice audio buffer to render to, mixing is done separately with new fluid_voice_mix().
fluid_voice_write() now returns the count of samples rendered.
fluid_voice_effects() split into fluid_voice_filter() and fluid_voice_mix().
Added dsp_buf_count field to fluid_voice_t to keep track of last count of samples rendered to dsp_buf.
fluid_voice_get_channel() converted to a macro.
Added FLUID_DEFAULT_AUDIO_RT_PRIO and FLUID_DEFAULT_MIDI_RT_PRIO in fluidsynth_priv.h, set to 90 and 80 respectively which get used for audio.realtime-prio and midi.realtime-prio (was 90 and 90 before).
2009-09-29 21:40:28 +00:00
|
|
|
fluid_settings_dupstr(settings, "audio.alsa.device", &device); /* ++ dup device name */
|
|
|
|
fluid_settings_getint (settings, "audio.realtime-prio", &realtime_prio);
|
|
|
|
|
2003-03-12 00:58:20 +00:00
|
|
|
dev->data = data;
|
|
|
|
dev->callback = func;
|
|
|
|
dev->cont = 1;
|
|
|
|
dev->buffer_size = period_size;
|
|
|
|
|
|
|
|
/* Open the PCM device */
|
2009-09-22 07:04:07 +00:00
|
|
|
if ((err = snd_pcm_open(&dev->pcm, device ? device : "default",
|
|
|
|
SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK)) != 0) {
|
2003-03-12 00:58:20 +00:00
|
|
|
if (err == -EBUSY) {
|
2009-09-22 07:04:07 +00:00
|
|
|
FLUID_LOG(FLUID_ERR, "The \"%s\" audio device is used by another application",
|
|
|
|
device ? device : "default");
|
2003-03-12 00:58:20 +00:00
|
|
|
goto error_recovery;
|
2003-11-26 22:07:01 +00:00
|
|
|
} else {
|
2009-09-22 07:04:07 +00:00
|
|
|
FLUID_LOG(FLUID_ERR, "Failed to open the \"%s\" audio device",
|
|
|
|
device ? device : "default");
|
2003-11-26 22:07:01 +00:00
|
|
|
goto error_recovery;
|
2003-03-12 00:58:20 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
snd_pcm_hw_params_alloca(&hwparams);
|
|
|
|
snd_pcm_sw_params_alloca(&swparams);
|
|
|
|
|
|
|
|
/* Set hardware parameters. We continue trying access methods and
|
|
|
|
sample formats until we have one that works. For example, if
|
|
|
|
memory mapped access fails we try regular IO methods. (not
|
|
|
|
finished, yet). */
|
|
|
|
|
|
|
|
for (i = 0; fluid_alsa_formats[i].name != NULL; i++) {
|
|
|
|
|
|
|
|
snd_pcm_hw_params_any(dev->pcm, hwparams);
|
|
|
|
|
2006-11-21 20:04:31 +00:00
|
|
|
if (snd_pcm_hw_params_set_access(dev->pcm, hwparams, fluid_alsa_formats[i].access) < 0) {
|
2003-03-12 00:58:20 +00:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2006-11-21 20:04:31 +00:00
|
|
|
if (snd_pcm_hw_params_set_format(dev->pcm, hwparams, fluid_alsa_formats[i].format) < 0) {
|
2003-03-12 00:58:20 +00:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2006-11-21 20:04:31 +00:00
|
|
|
if ((err = snd_pcm_hw_params_set_channels(dev->pcm, hwparams, 2)) < 0) {
|
2003-08-19 20:57:00 +00:00
|
|
|
FLUID_LOG(FLUID_ERR, "Failed to set the channels: %s",
|
|
|
|
snd_strerror (err));
|
2007-03-04 16:45:05 +00:00
|
|
|
goto error_recovery;
|
2003-03-12 00:58:20 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
tmp = (unsigned int) sample_rate;
|
2006-11-21 20:04:31 +00:00
|
|
|
if ((err = snd_pcm_hw_params_set_rate_near(dev->pcm, hwparams, &tmp, NULL)) < 0) {
|
2003-08-19 20:57:00 +00:00
|
|
|
FLUID_LOG(FLUID_ERR, "Failed to set the sample rate: %s",
|
|
|
|
snd_strerror (err));
|
2006-11-21 20:04:31 +00:00
|
|
|
goto error_recovery;
|
2003-03-12 00:58:20 +00:00
|
|
|
}
|
2006-11-21 20:04:31 +00:00
|
|
|
|
|
|
|
if (tmp != sample_rate) {
|
2003-03-12 00:58:20 +00:00
|
|
|
/* There's currently no way to change the sampling rate of the
|
|
|
|
synthesizer after it's been created. */
|
2017-10-28 13:36:32 +02:00
|
|
|
FLUID_LOG(FLUID_WARN, "Requested sample rate of %u, got %u instead, "
|
2009-04-15 04:29:11 +00:00
|
|
|
"synthesizer likely out of tune!", (unsigned int) sample_rate, tmp);
|
2003-03-12 00:58:20 +00:00
|
|
|
}
|
|
|
|
|
2006-11-21 20:04:31 +00:00
|
|
|
uframes = period_size;
|
|
|
|
if (snd_pcm_hw_params_set_period_size_near(dev->pcm, hwparams, &uframes, &dir) < 0) {
|
2003-03-12 00:58:20 +00:00
|
|
|
FLUID_LOG(FLUID_ERR, "Failed to set the period size");
|
2007-03-04 16:45:05 +00:00
|
|
|
goto error_recovery;
|
2003-03-12 00:58:20 +00:00
|
|
|
}
|
|
|
|
if (uframes != (unsigned long) period_size) {
|
2007-03-04 16:45:05 +00:00
|
|
|
FLUID_LOG(FLUID_WARN, "Requested a period size of %d, got %d instead",
|
2003-03-12 00:58:20 +00:00
|
|
|
period_size, (int) uframes);
|
|
|
|
dev->buffer_size = (int) uframes;
|
2006-11-21 20:04:31 +00:00
|
|
|
period_size = uframes; /* period size is used below, so set it to the real value */
|
2003-03-12 00:58:20 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
tmp = periods;
|
2006-11-21 20:04:31 +00:00
|
|
|
if (snd_pcm_hw_params_set_periods_near(dev->pcm, hwparams, &tmp, &dir) < 0) {
|
2003-03-12 00:58:20 +00:00
|
|
|
FLUID_LOG(FLUID_ERR, "Failed to set the number of periods");
|
2007-03-04 16:45:05 +00:00
|
|
|
goto error_recovery;
|
2003-03-12 00:58:20 +00:00
|
|
|
}
|
|
|
|
if (tmp != (unsigned int) periods) {
|
2007-03-04 16:45:05 +00:00
|
|
|
FLUID_LOG(FLUID_WARN, "Requested %d periods, got %d instead",
|
2003-03-12 00:58:20 +00:00
|
|
|
periods, (int) tmp);
|
|
|
|
}
|
2007-03-04 16:45:05 +00:00
|
|
|
|
2006-11-21 20:04:31 +00:00
|
|
|
if (snd_pcm_hw_params(dev->pcm, hwparams) < 0) {
|
2003-03-12 00:58:20 +00:00
|
|
|
FLUID_LOG(FLUID_WARN, "Audio device hardware configuration failed");
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (fluid_alsa_formats[i].name == NULL) {
|
|
|
|
FLUID_LOG(FLUID_ERR, "Failed to find a workable audio format");
|
2007-03-04 16:45:05 +00:00
|
|
|
goto error_recovery;
|
2003-03-12 00:58:20 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Set the software params */
|
|
|
|
snd_pcm_sw_params_current(dev->pcm, swparams);
|
2006-11-21 20:04:31 +00:00
|
|
|
|
2003-03-12 00:58:20 +00:00
|
|
|
if (snd_pcm_sw_params_set_start_threshold(dev->pcm, swparams, period_size) != 0) {
|
2006-11-21 20:04:31 +00:00
|
|
|
FLUID_LOG(FLUID_ERR, "Failed to set start threshold.");
|
2003-03-12 00:58:20 +00:00
|
|
|
}
|
2006-11-21 20:04:31 +00:00
|
|
|
|
2006-11-23 18:49:36 +00:00
|
|
|
if (snd_pcm_sw_params_set_avail_min(dev->pcm, swparams, period_size) != 0) {
|
2003-03-12 00:58:20 +00:00
|
|
|
FLUID_LOG(FLUID_ERR, "Software setup for minimum available frames failed.");
|
|
|
|
}
|
2006-11-21 20:04:31 +00:00
|
|
|
|
2003-03-12 00:58:20 +00:00
|
|
|
if (snd_pcm_sw_params(dev->pcm, swparams) != 0) {
|
|
|
|
FLUID_LOG(FLUID_ERR, "Software setup failed.");
|
|
|
|
}
|
|
|
|
|
2009-11-15 02:34:24 +00:00
|
|
|
if (snd_pcm_nonblock(dev->pcm, 0) != 0) {
|
|
|
|
FLUID_LOG(FLUID_ERR, "Failed to set the audio device to blocking mode");
|
|
|
|
goto error_recovery;
|
|
|
|
}
|
2003-03-12 00:58:20 +00:00
|
|
|
|
|
|
|
/* Create the audio thread */
|
2013-08-19 23:10:43 +00:00
|
|
|
dev->thread = new_fluid_thread ("alsa-audio", fluid_alsa_formats[i].run, dev, realtime_prio, FALSE);
|
2007-03-04 16:45:05 +00:00
|
|
|
|
2009-10-30 21:49:54 +00:00
|
|
|
if (!dev->thread)
|
2003-03-12 00:58:20 +00:00
|
|
|
goto error_recovery;
|
|
|
|
|
2009-09-22 07:04:07 +00:00
|
|
|
if (device) FLUID_FREE (device); /* -- free device name */
|
|
|
|
|
2003-03-12 00:58:20 +00:00
|
|
|
return (fluid_audio_driver_t*) dev;
|
|
|
|
|
|
|
|
error_recovery:
|
2009-09-22 07:04:07 +00:00
|
|
|
if (device) FLUID_FREE (device); /* -- free device name */
|
2003-03-12 00:58:20 +00:00
|
|
|
delete_fluid_alsa_audio_driver((fluid_audio_driver_t*) dev);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
int delete_fluid_alsa_audio_driver(fluid_audio_driver_t* p)
|
|
|
|
{
|
|
|
|
fluid_alsa_audio_driver_t* dev = (fluid_alsa_audio_driver_t*) p;
|
2007-03-04 16:45:05 +00:00
|
|
|
|
2003-03-12 00:58:20 +00:00
|
|
|
if (dev == NULL) {
|
|
|
|
return FLUID_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
dev->cont = 0;
|
|
|
|
|
2009-10-30 21:49:54 +00:00
|
|
|
if (dev->thread)
|
|
|
|
fluid_thread_join (dev->thread);
|
2003-03-12 00:58:20 +00:00
|
|
|
|
2009-11-15 02:34:24 +00:00
|
|
|
if (dev->pcm)
|
2006-11-21 20:04:31 +00:00
|
|
|
snd_pcm_close (dev->pcm);
|
2003-03-12 00:58:20 +00:00
|
|
|
|
2004-03-29 10:05:18 +00:00
|
|
|
FLUID_FREE(dev);
|
|
|
|
|
2003-03-12 00:58:20 +00:00
|
|
|
return FLUID_OK;
|
|
|
|
}
|
|
|
|
|
2006-12-10 16:02:04 +00:00
|
|
|
/* handle error after an ALSA write call */
|
|
|
|
static int fluid_alsa_handle_write_error (snd_pcm_t *pcm, int errval)
|
|
|
|
{
|
|
|
|
switch (errval)
|
|
|
|
{
|
|
|
|
case -EAGAIN:
|
|
|
|
snd_pcm_wait(pcm, 1);
|
|
|
|
break;
|
2017-06-30 08:14:09 +02:00
|
|
|
// on some BSD variants ESTRPIPE is defined as EPIPE.
|
|
|
|
// not sure why, maybe because this version of alsa doesnt support
|
2017-06-30 08:19:29 +02:00
|
|
|
// suspending pcm streams. anyway, since EPIPE seems to be more
|
2017-06-30 08:14:09 +02:00
|
|
|
// likely than ESTRPIPE, so ifdef it out in case.
|
2017-07-05 08:18:45 +02:00
|
|
|
#if ESTRPIPE == EPIPE
|
|
|
|
#warning "ESTRPIPE defined as EPIPE. This may cause trouble with ALSA playback."
|
|
|
|
#else
|
2017-06-30 08:14:09 +02:00
|
|
|
case -ESTRPIPE:
|
|
|
|
if (snd_pcm_resume(pcm) != 0) {
|
|
|
|
FLUID_LOG(FLUID_ERR, "Failed to resume the audio device");
|
|
|
|
return FLUID_FAILED;
|
|
|
|
}
|
|
|
|
#endif
|
2017-07-24 22:32:54 +02:00
|
|
|
/* fall through ... */
|
|
|
|
/* ... since the stream got resumed, but still has to be prepared */
|
2006-12-10 16:02:04 +00:00
|
|
|
case -EPIPE:
|
|
|
|
case -EBADFD:
|
|
|
|
if (snd_pcm_prepare(pcm) != 0) {
|
|
|
|
FLUID_LOG(FLUID_ERR, "Failed to prepare the audio device");
|
|
|
|
return FLUID_FAILED;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
FLUID_LOG(FLUID_ERR, "The audio device error: %s", snd_strerror(errval));
|
|
|
|
return FLUID_FAILED;
|
|
|
|
}
|
|
|
|
|
|
|
|
return FLUID_OK;
|
|
|
|
}
|
|
|
|
|
2017-10-26 18:08:10 +02:00
|
|
|
static fluid_thread_return_t fluid_alsa_audio_run_float (void *d)
|
2003-03-12 00:58:20 +00:00
|
|
|
{
|
|
|
|
fluid_alsa_audio_driver_t* dev = (fluid_alsa_audio_driver_t*) d;
|
2006-11-21 20:04:31 +00:00
|
|
|
fluid_synth_t *synth = (fluid_synth_t *)(dev->data);
|
2003-03-12 00:58:20 +00:00
|
|
|
float* left;
|
|
|
|
float* right;
|
|
|
|
float* handle[2];
|
|
|
|
int n, buffer_size, offset;
|
|
|
|
|
|
|
|
buffer_size = dev->buffer_size;
|
|
|
|
|
|
|
|
left = FLUID_ARRAY(float, buffer_size);
|
|
|
|
right = FLUID_ARRAY(float, buffer_size);
|
|
|
|
|
|
|
|
if ((left == NULL) || (right == NULL)) {
|
|
|
|
FLUID_LOG(FLUID_ERR, "Out of memory.");
|
2017-06-25 10:03:16 +02:00
|
|
|
goto error_recovery;
|
2003-03-12 00:58:20 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (snd_pcm_prepare(dev->pcm) != 0) {
|
|
|
|
FLUID_LOG(FLUID_ERR, "Failed to prepare the audio device");
|
2007-03-04 16:45:05 +00:00
|
|
|
goto error_recovery;
|
2003-03-12 00:58:20 +00:00
|
|
|
}
|
|
|
|
|
2006-12-10 16:02:04 +00:00
|
|
|
/* use separate loops depending on if callback supplied or not (overkill?) */
|
|
|
|
if (dev->callback)
|
|
|
|
{
|
|
|
|
while (dev->cont) {
|
|
|
|
handle[0] = left;
|
|
|
|
handle[1] = right;
|
2003-03-12 00:58:20 +00:00
|
|
|
|
2006-12-10 16:02:04 +00:00
|
|
|
(*dev->callback)(synth, buffer_size, 0, NULL, 2, handle);
|
2003-03-12 00:58:20 +00:00
|
|
|
|
2006-12-10 16:02:04 +00:00
|
|
|
offset = 0;
|
|
|
|
while (offset < buffer_size) {
|
|
|
|
handle[0] = left + offset;
|
|
|
|
handle[1] = right + offset;
|
2006-11-21 20:04:31 +00:00
|
|
|
|
2006-12-10 16:02:04 +00:00
|
|
|
n = snd_pcm_writen(dev->pcm, (void *)handle, buffer_size - offset);
|
2003-03-12 00:58:20 +00:00
|
|
|
|
2006-12-10 16:02:04 +00:00
|
|
|
if (n < 0) /* error occurred? */
|
|
|
|
{
|
2007-01-14 17:48:51 +00:00
|
|
|
if (fluid_alsa_handle_write_error (dev->pcm, n) != FLUID_OK)
|
2006-12-10 16:02:04 +00:00
|
|
|
goto error_recovery;
|
|
|
|
} else offset += n; /* no error occurred */
|
|
|
|
} /* while (offset < buffer_size) */
|
|
|
|
} /* while (dev->cont) */
|
|
|
|
}
|
|
|
|
else /* no user audio callback (faster) */
|
|
|
|
{
|
|
|
|
while (dev->cont) {
|
|
|
|
fluid_synth_write_float(dev->data, buffer_size, left, 0, 1, right, 0, 1);
|
2003-03-12 00:58:20 +00:00
|
|
|
|
2006-12-10 16:02:04 +00:00
|
|
|
offset = 0;
|
|
|
|
while (offset < buffer_size) {
|
|
|
|
handle[0] = left + offset;
|
|
|
|
handle[1] = right + offset;
|
2003-03-12 00:58:20 +00:00
|
|
|
|
2006-12-10 16:02:04 +00:00
|
|
|
n = snd_pcm_writen(dev->pcm, (void *)handle, buffer_size - offset);
|
2003-03-12 00:58:20 +00:00
|
|
|
|
2006-12-10 16:02:04 +00:00
|
|
|
if (n < 0) /* error occurred? */
|
2006-11-21 20:04:31 +00:00
|
|
|
{
|
2007-01-14 17:48:51 +00:00
|
|
|
if (fluid_alsa_handle_write_error (dev->pcm, n) != FLUID_OK)
|
2006-11-21 20:04:31 +00:00
|
|
|
goto error_recovery;
|
2006-12-10 16:02:04 +00:00
|
|
|
} else offset += n; /* no error occurred */
|
|
|
|
} /* while (offset < buffer_size) */
|
|
|
|
} /* while (dev->cont) */
|
2003-03-12 00:58:20 +00:00
|
|
|
}
|
2006-12-10 16:02:04 +00:00
|
|
|
|
2003-03-12 00:58:20 +00:00
|
|
|
error_recovery:
|
2007-03-04 16:45:05 +00:00
|
|
|
|
2003-03-12 00:58:20 +00:00
|
|
|
FLUID_FREE(left);
|
|
|
|
FLUID_FREE(right);
|
2017-10-26 18:08:10 +02:00
|
|
|
|
|
|
|
return FLUID_THREAD_RETURN_VALUE;
|
2003-03-12 00:58:20 +00:00
|
|
|
}
|
|
|
|
|
2017-10-26 18:08:10 +02:00
|
|
|
static fluid_thread_return_t fluid_alsa_audio_run_s16 (void *d)
|
2003-03-12 00:58:20 +00:00
|
|
|
{
|
|
|
|
fluid_alsa_audio_driver_t* dev = (fluid_alsa_audio_driver_t*) d;
|
|
|
|
float* left;
|
|
|
|
float* right;
|
|
|
|
short* buf;
|
|
|
|
float* handle[2];
|
2009-10-07 05:20:55 +00:00
|
|
|
int n, buffer_size, offset;
|
2003-03-12 00:58:20 +00:00
|
|
|
|
|
|
|
buffer_size = dev->buffer_size;
|
|
|
|
|
|
|
|
left = FLUID_ARRAY(float, buffer_size);
|
|
|
|
right = FLUID_ARRAY(float, buffer_size);
|
|
|
|
buf = FLUID_ARRAY(short, 2 * buffer_size);
|
|
|
|
|
|
|
|
if ((left == NULL) || (right == NULL) || (buf == NULL)) {
|
|
|
|
FLUID_LOG(FLUID_ERR, "Out of memory.");
|
2017-06-25 10:03:16 +02:00
|
|
|
goto error_recovery;
|
2003-03-12 00:58:20 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
handle[0] = left;
|
|
|
|
handle[1] = right;
|
|
|
|
|
|
|
|
if (snd_pcm_prepare(dev->pcm) != 0) {
|
|
|
|
FLUID_LOG(FLUID_ERR, "Failed to prepare the audio device");
|
2007-03-04 16:45:05 +00:00
|
|
|
goto error_recovery;
|
2003-03-12 00:58:20 +00:00
|
|
|
}
|
|
|
|
|
2007-09-20 07:47:38 +00:00
|
|
|
/* use separate loops depending on if callback supplied or not */
|
2006-12-10 16:02:04 +00:00
|
|
|
if (dev->callback)
|
|
|
|
{
|
2007-09-20 07:47:38 +00:00
|
|
|
int dither_index = 0;
|
2003-03-12 00:58:20 +00:00
|
|
|
|
2007-09-20 07:47:38 +00:00
|
|
|
while (dev->cont)
|
|
|
|
{
|
|
|
|
(*dev->callback)(dev->data, buffer_size, 0, NULL, 2, handle);
|
2003-03-12 00:58:20 +00:00
|
|
|
|
2007-09-20 07:47:38 +00:00
|
|
|
/* convert floating point data to 16 bit (with dithering) */
|
|
|
|
fluid_synth_dither_s16 (&dither_index, buffer_size, left, right,
|
|
|
|
buf, 0, 2, buf, 1, 2);
|
2006-12-10 16:02:04 +00:00
|
|
|
offset = 0;
|
2007-09-20 07:47:38 +00:00
|
|
|
while (offset < buffer_size)
|
|
|
|
{
|
|
|
|
n = snd_pcm_writei (dev->pcm, (void*) (buf + 2 * offset),
|
|
|
|
buffer_size - offset);
|
2006-12-10 16:02:04 +00:00
|
|
|
if (n < 0) /* error occurred? */
|
|
|
|
{
|
2007-01-14 17:48:51 +00:00
|
|
|
if (fluid_alsa_handle_write_error (dev->pcm, n) != FLUID_OK)
|
2006-12-10 16:02:04 +00:00
|
|
|
goto error_recovery;
|
2007-09-20 07:47:38 +00:00
|
|
|
}
|
|
|
|
else offset += n; /* no error occurred */
|
2006-12-10 16:02:04 +00:00
|
|
|
} /* while (offset < buffer_size) */
|
|
|
|
} /* while (dev->cont) */
|
|
|
|
}
|
2007-09-20 07:47:38 +00:00
|
|
|
else /* no user audio callback, dev->data is the synth instance */
|
2006-12-10 16:02:04 +00:00
|
|
|
{
|
2007-09-20 07:47:38 +00:00
|
|
|
fluid_synth_t* synth = (fluid_synth_t *)(dev->data);
|
|
|
|
|
|
|
|
while (dev->cont)
|
|
|
|
{
|
|
|
|
fluid_synth_write_s16 (synth, buffer_size, buf, 0, 2, buf, 1, 2);
|
2003-03-12 00:58:20 +00:00
|
|
|
|
2006-12-10 16:02:04 +00:00
|
|
|
offset = 0;
|
2007-09-20 07:47:38 +00:00
|
|
|
while (offset < buffer_size)
|
|
|
|
{
|
|
|
|
n = snd_pcm_writei (dev->pcm, (void*) (buf + 2 * offset),
|
|
|
|
buffer_size - offset);
|
2003-03-12 00:58:20 +00:00
|
|
|
|
2006-12-10 16:02:04 +00:00
|
|
|
if (n < 0) /* error occurred? */
|
2006-11-21 20:04:31 +00:00
|
|
|
{
|
2007-01-14 17:48:51 +00:00
|
|
|
if (fluid_alsa_handle_write_error (dev->pcm, n) != FLUID_OK)
|
2006-11-21 20:04:31 +00:00
|
|
|
goto error_recovery;
|
2007-09-20 07:47:38 +00:00
|
|
|
}
|
|
|
|
else offset += n; /* no error occurred */
|
2006-12-10 16:02:04 +00:00
|
|
|
} /* while (offset < buffer_size) */
|
|
|
|
} /* while (dev->cont) */
|
2003-03-12 00:58:20 +00:00
|
|
|
}
|
2006-11-21 20:04:31 +00:00
|
|
|
|
2003-03-12 00:58:20 +00:00
|
|
|
error_recovery:
|
2007-03-04 16:45:05 +00:00
|
|
|
|
2003-03-12 00:58:20 +00:00
|
|
|
FLUID_FREE(left);
|
|
|
|
FLUID_FREE(right);
|
|
|
|
FLUID_FREE(buf);
|
2017-10-26 18:08:10 +02:00
|
|
|
|
|
|
|
return FLUID_THREAD_RETURN_VALUE;
|
2003-03-12 00:58:20 +00:00
|
|
|
}
|
|
|
|
|
2003-03-11 16:56:45 +00:00
|
|
|
|
|
|
|
/**************************************************************
|
|
|
|
*
|
|
|
|
* Alsa MIDI driver
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
|
void fluid_alsa_rawmidi_driver_settings(fluid_settings_t* settings)
|
|
|
|
{
|
|
|
|
fluid_settings_register_str(settings, "midi.alsa.device", "default", 0, NULL, NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* new_fluid_alsa_rawmidi_driver
|
|
|
|
*/
|
2007-03-04 16:45:05 +00:00
|
|
|
fluid_midi_driver_t*
|
|
|
|
new_fluid_alsa_rawmidi_driver(fluid_settings_t* settings,
|
|
|
|
handle_midi_event_func_t handler,
|
2003-03-11 16:56:45 +00:00
|
|
|
void* data)
|
|
|
|
{
|
|
|
|
int i, err;
|
|
|
|
fluid_alsa_rawmidi_driver_t* dev;
|
Added public API functions: fluid_synth_sysex, fluid_synth_activate_key_tuning, fluid_synth_activate_octave_tuning, fluid_synth_tune_notes, fluid_synth_activate_tuning and fluid_synth_deactivate_tuning.
Added audio.realtime, audio.realtime-prio, midi.realtime and midi.realtime-prio (only ALSA updated to use them at this point).
Fixed bug in new_fluid_channel() where tuning field was not initialized.
fluid_settings.h had wrong field order in prototypes for fluid_settings_register_num and fluid_settings_register_int.
Added multi core support: "synth.cpu-cores" setting, fluid_synth_core_thread_func() and many core_ variables to fluid_synth_t.
Switched fluid_mutex_t back to a normal non-recursive mutex and added fluid_rec_mutex_t.
Added fluid_cond_t thread synchronization stuff.
Added fluid_cond_mutex_t which is a dynamically allocated regular mutex for use with fluid_cond_t.
fluid_settings_t and fluid_synth_t are now using fluid_rec_mutex_t (same as before, just name change).
Added platform specific fluid_thread_self_set_prio() functions to fluid_sys.c for activating high priority for the calling thread.
Modified new_fluid_thread() to take a prio and prio_level parameters.
Added missing fluid_atomic_pointer_set().
fluid_voice_write() changed to only take a voice audio buffer to render to, mixing is done separately with new fluid_voice_mix().
fluid_voice_write() now returns the count of samples rendered.
fluid_voice_effects() split into fluid_voice_filter() and fluid_voice_mix().
Added dsp_buf_count field to fluid_voice_t to keep track of last count of samples rendered to dsp_buf.
fluid_voice_get_channel() converted to a macro.
Added FLUID_DEFAULT_AUDIO_RT_PRIO and FLUID_DEFAULT_MIDI_RT_PRIO in fluidsynth_priv.h, set to 90 and 80 respectively which get used for audio.realtime-prio and midi.realtime-prio (was 90 and 90 before).
2009-09-29 21:40:28 +00:00
|
|
|
int realtime_prio = 0;
|
2003-03-11 16:56:45 +00:00
|
|
|
int count;
|
|
|
|
struct pollfd *pfd = NULL;
|
|
|
|
char* device = NULL;
|
|
|
|
|
|
|
|
/* not much use doing anything */
|
|
|
|
if (handler == NULL) {
|
|
|
|
FLUID_LOG(FLUID_ERR, "Invalid argument");
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* allocate the device */
|
|
|
|
dev = FLUID_NEW(fluid_alsa_rawmidi_driver_t);
|
|
|
|
if (dev == NULL) {
|
|
|
|
FLUID_LOG(FLUID_ERR, "Out of memory");
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
FLUID_MEMSET(dev, 0, sizeof(fluid_alsa_rawmidi_driver_t));
|
|
|
|
|
|
|
|
dev->driver.handler = handler;
|
|
|
|
dev->driver.data = data;
|
|
|
|
|
|
|
|
/* allocate one event to store the input data */
|
|
|
|
dev->parser = new_fluid_midi_parser();
|
|
|
|
if (dev->parser == NULL) {
|
|
|
|
FLUID_LOG(FLUID_ERR, "Out of memory");
|
|
|
|
goto error_recovery;
|
|
|
|
}
|
|
|
|
|
Added public API functions: fluid_synth_sysex, fluid_synth_activate_key_tuning, fluid_synth_activate_octave_tuning, fluid_synth_tune_notes, fluid_synth_activate_tuning and fluid_synth_deactivate_tuning.
Added audio.realtime, audio.realtime-prio, midi.realtime and midi.realtime-prio (only ALSA updated to use them at this point).
Fixed bug in new_fluid_channel() where tuning field was not initialized.
fluid_settings.h had wrong field order in prototypes for fluid_settings_register_num and fluid_settings_register_int.
Added multi core support: "synth.cpu-cores" setting, fluid_synth_core_thread_func() and many core_ variables to fluid_synth_t.
Switched fluid_mutex_t back to a normal non-recursive mutex and added fluid_rec_mutex_t.
Added fluid_cond_t thread synchronization stuff.
Added fluid_cond_mutex_t which is a dynamically allocated regular mutex for use with fluid_cond_t.
fluid_settings_t and fluid_synth_t are now using fluid_rec_mutex_t (same as before, just name change).
Added platform specific fluid_thread_self_set_prio() functions to fluid_sys.c for activating high priority for the calling thread.
Modified new_fluid_thread() to take a prio and prio_level parameters.
Added missing fluid_atomic_pointer_set().
fluid_voice_write() changed to only take a voice audio buffer to render to, mixing is done separately with new fluid_voice_mix().
fluid_voice_write() now returns the count of samples rendered.
fluid_voice_effects() split into fluid_voice_filter() and fluid_voice_mix().
Added dsp_buf_count field to fluid_voice_t to keep track of last count of samples rendered to dsp_buf.
fluid_voice_get_channel() converted to a macro.
Added FLUID_DEFAULT_AUDIO_RT_PRIO and FLUID_DEFAULT_MIDI_RT_PRIO in fluidsynth_priv.h, set to 90 and 80 respectively which get used for audio.realtime-prio and midi.realtime-prio (was 90 and 90 before).
2009-09-29 21:40:28 +00:00
|
|
|
fluid_settings_getint (settings, "midi.realtime-prio", &realtime_prio);
|
|
|
|
|
2003-03-11 16:56:45 +00:00
|
|
|
/* get the device name. if none is specified, use the default device. */
|
2009-09-22 07:04:07 +00:00
|
|
|
fluid_settings_dupstr(settings, "midi.alsa.device", &device); /* ++ alloc device name */
|
2003-03-11 16:56:45 +00:00
|
|
|
|
|
|
|
/* open the hardware device. only use midi in. */
|
2009-09-22 07:04:07 +00:00
|
|
|
if ((err = snd_rawmidi_open(&dev->rawmidi_in, NULL, device ? device : "default",
|
|
|
|
SND_RAWMIDI_NONBLOCK)) < 0) {
|
2003-03-11 16:56:45 +00:00
|
|
|
FLUID_LOG(FLUID_ERR, "Error opening ALSA raw MIDI port");
|
|
|
|
goto error_recovery;
|
|
|
|
}
|
|
|
|
|
2009-10-30 21:49:54 +00:00
|
|
|
snd_rawmidi_nonblock(dev->rawmidi_in, 1);
|
|
|
|
|
2003-03-11 16:56:45 +00:00
|
|
|
/* get # of MIDI file descriptors */
|
|
|
|
count = snd_rawmidi_poll_descriptors_count(dev->rawmidi_in);
|
|
|
|
if (count > 0) { /* make sure there are some */
|
|
|
|
pfd = FLUID_MALLOC(sizeof (struct pollfd) * count);
|
|
|
|
dev->pfd = FLUID_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;
|
2007-03-04 16:45:05 +00:00
|
|
|
dev->pfd[dev->npfd].events = POLLIN;
|
|
|
|
dev->pfd[dev->npfd].revents = 0;
|
2003-03-11 16:56:45 +00:00
|
|
|
dev->npfd++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
FLUID_FREE(pfd);
|
|
|
|
|
2017-10-28 12:57:47 +02:00
|
|
|
fluid_atomic_int_set(&dev->should_quit, 0);
|
2003-03-11 16:56:45 +00:00
|
|
|
|
2009-10-30 21:49:54 +00:00
|
|
|
/* create the MIDI thread */
|
2013-08-19 23:10:43 +00:00
|
|
|
dev->thread = new_fluid_thread ("alsa-midi-raw", fluid_alsa_midi_run, dev, realtime_prio, FALSE);
|
2003-06-10 10:04:01 +00:00
|
|
|
|
2009-10-30 21:49:54 +00:00
|
|
|
if (!dev->thread)
|
|
|
|
goto error_recovery;
|
2009-09-22 07:04:07 +00:00
|
|
|
|
|
|
|
if (device) FLUID_FREE (device); /* -- free device name */
|
|
|
|
|
2003-03-11 16:56:45 +00:00
|
|
|
return (fluid_midi_driver_t*) dev;
|
2007-03-04 16:45:05 +00:00
|
|
|
|
2003-03-11 16:56:45 +00:00
|
|
|
error_recovery:
|
2009-09-22 07:04:07 +00:00
|
|
|
if (device) FLUID_FREE (device); /* -- free device name */
|
2003-03-11 16:56:45 +00:00
|
|
|
delete_fluid_alsa_rawmidi_driver((fluid_midi_driver_t*) dev);
|
|
|
|
return NULL;
|
2007-03-04 16:45:05 +00:00
|
|
|
|
2003-03-11 16:56:45 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* delete_fluid_alsa_rawmidi_driver
|
|
|
|
*/
|
2007-03-04 16:45:05 +00:00
|
|
|
int
|
2003-03-11 16:56:45 +00:00
|
|
|
delete_fluid_alsa_rawmidi_driver(fluid_midi_driver_t* p)
|
|
|
|
{
|
|
|
|
fluid_alsa_rawmidi_driver_t* dev;
|
|
|
|
|
|
|
|
dev = (fluid_alsa_rawmidi_driver_t*) p;
|
|
|
|
if (dev == NULL) {
|
|
|
|
return FLUID_OK;
|
|
|
|
}
|
|
|
|
|
2009-10-30 21:49:54 +00:00
|
|
|
/* cancel the thread and wait for it before cleaning up */
|
2017-10-28 12:57:47 +02:00
|
|
|
fluid_atomic_int_set(&dev->should_quit, 1);
|
2003-03-11 16:56:45 +00:00
|
|
|
|
2009-10-30 21:49:54 +00:00
|
|
|
if (dev->thread)
|
|
|
|
fluid_thread_join (dev->thread);
|
|
|
|
|
2003-03-11 16:56:45 +00:00
|
|
|
if (dev->rawmidi_in) {
|
|
|
|
snd_rawmidi_close(dev->rawmidi_in);
|
|
|
|
}
|
|
|
|
if (dev->parser != NULL) {
|
|
|
|
delete_fluid_midi_parser(dev->parser);
|
|
|
|
}
|
|
|
|
FLUID_FREE(dev);
|
|
|
|
return FLUID_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* fluid_alsa_midi_run
|
|
|
|
*/
|
2017-10-26 18:08:10 +02:00
|
|
|
fluid_thread_return_t
|
2003-03-11 16:56:45 +00:00
|
|
|
fluid_alsa_midi_run(void* d)
|
|
|
|
{
|
|
|
|
fluid_midi_event_t* evt;
|
|
|
|
fluid_alsa_rawmidi_driver_t* dev = (fluid_alsa_rawmidi_driver_t*) d;
|
2009-10-30 21:49:54 +00:00
|
|
|
int n, i;
|
2003-03-11 16:56:45 +00:00
|
|
|
|
|
|
|
/* go into a loop until someone tells us to stop */
|
2017-10-28 12:57:47 +02:00
|
|
|
while (!fluid_atomic_int_get(&dev->should_quit)) {
|
2003-03-11 16:56:45 +00:00
|
|
|
|
|
|
|
/* is there something to read? */
|
|
|
|
n = poll(dev->pfd, dev->npfd, 100); /* use a 100 milliseconds timeout */
|
|
|
|
if (n < 0) {
|
|
|
|
perror("poll");
|
|
|
|
} else if (n > 0) {
|
|
|
|
|
|
|
|
/* read new data */
|
|
|
|
n = snd_rawmidi_read(dev->rawmidi_in, dev->buffer, BUFFER_LENGTH);
|
|
|
|
if ((n < 0) && (n != -EAGAIN)) {
|
|
|
|
FLUID_LOG(FLUID_ERR, "Failed to read the midi input");
|
2017-10-28 12:57:47 +02:00
|
|
|
fluid_atomic_int_set(&dev->should_quit, 1);
|
2003-03-11 16:56:45 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* let the parser convert the data into events */
|
|
|
|
for (i = 0; i < n; i++) {
|
|
|
|
evt = fluid_midi_parser_parse(dev->parser, dev->buffer[i]);
|
|
|
|
if (evt != NULL) {
|
|
|
|
(*dev->driver.handler)(dev->driver.data, evt);
|
|
|
|
}
|
|
|
|
}
|
2006-11-21 20:04:31 +00:00
|
|
|
}
|
2003-03-11 16:56:45 +00:00
|
|
|
}
|
2017-10-26 18:08:10 +02:00
|
|
|
|
|
|
|
return FLUID_THREAD_RETURN_VALUE;
|
2003-03-11 16:56:45 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**************************************************************
|
|
|
|
*
|
|
|
|
* Alsa sequencer
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
|
void fluid_alsa_seq_driver_settings(fluid_settings_t* settings)
|
|
|
|
{
|
|
|
|
fluid_settings_register_str(settings, "midi.alsa_seq.device", "default", 0, NULL, NULL);
|
|
|
|
fluid_settings_register_str(settings, "midi.alsa_seq.id", "pid", 0, NULL, NULL);
|
|
|
|
}
|
|
|
|
|
2004-05-05 20:27:22 +00:00
|
|
|
|
|
|
|
static char* fluid_alsa_seq_full_id(char* id, char* buf, int len)
|
|
|
|
{
|
|
|
|
if (id != NULL) {
|
|
|
|
if (FLUID_STRCMP(id, "pid") == 0) {
|
2017-10-15 18:42:44 +02:00
|
|
|
FLUID_SNPRINTF (buf, len, "FLUID Synth (%d)", getpid());
|
2004-05-05 20:27:22 +00:00
|
|
|
} else {
|
2017-10-15 18:42:44 +02:00
|
|
|
FLUID_SNPRINTF (buf, len, "FLUID Synth (%s)", id);
|
2004-05-05 20:27:22 +00:00
|
|
|
}
|
|
|
|
} else {
|
2017-10-15 18:42:44 +02:00
|
|
|
FLUID_SNPRINTF (buf, len, "FLUID Synth");
|
2004-05-05 20:27:22 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return buf;
|
|
|
|
}
|
|
|
|
|
|
|
|
static char* fluid_alsa_seq_full_name(char* id, int port, char* buf, int len)
|
2007-03-04 16:45:05 +00:00
|
|
|
{
|
2004-05-05 20:27:22 +00:00
|
|
|
if (id != NULL) {
|
|
|
|
if (FLUID_STRCMP(id, "pid") == 0) {
|
2017-10-15 18:42:44 +02:00
|
|
|
FLUID_SNPRINTF (buf, len, "Synth input port (%d:%d)", getpid(), port);
|
2004-05-05 20:27:22 +00:00
|
|
|
} else {
|
2017-10-15 18:42:44 +02:00
|
|
|
FLUID_SNPRINTF (buf, len, "Synth input port (%s:%d)", id, port);
|
2004-05-05 20:27:22 +00:00
|
|
|
}
|
|
|
|
} else {
|
2017-10-15 18:42:44 +02:00
|
|
|
FLUID_SNPRINTF (buf, len, "Synth input port");
|
2004-05-05 20:27:22 +00:00
|
|
|
}
|
|
|
|
return buf;
|
|
|
|
}
|
|
|
|
|
2017-10-26 15:51:34 +02:00
|
|
|
// Connect available ALSA MIDI inputs to port_info
|
|
|
|
static void fluid_alsa_seq_autoconnect(fluid_alsa_seq_driver_t* dev, const snd_seq_port_info_t *port_info)
|
|
|
|
{
|
|
|
|
snd_seq_t *seq = dev->seq_handle;
|
|
|
|
snd_seq_port_subscribe_t *subs;
|
|
|
|
snd_seq_client_info_t *cinfo;
|
|
|
|
snd_seq_port_info_t *pinfo;
|
|
|
|
|
|
|
|
snd_seq_port_subscribe_alloca(&subs);
|
|
|
|
snd_seq_client_info_alloca(&cinfo);
|
|
|
|
snd_seq_port_info_alloca(&pinfo);
|
|
|
|
|
|
|
|
snd_seq_client_info_set_client(cinfo, -1);
|
|
|
|
while (snd_seq_query_next_client(seq, cinfo) >= 0) {
|
|
|
|
const snd_seq_addr_t *dest = snd_seq_port_info_get_addr(port_info);
|
|
|
|
|
|
|
|
snd_seq_port_info_set_client(pinfo, snd_seq_client_info_get_client(cinfo));
|
|
|
|
snd_seq_port_info_set_port(pinfo, -1);
|
|
|
|
while (snd_seq_query_next_port(seq, pinfo) >= 0) {
|
|
|
|
const unsigned int needed_type = SND_SEQ_PORT_TYPE_MIDI_GENERIC;
|
|
|
|
const unsigned int needed_cap = SND_SEQ_PORT_CAP_READ|SND_SEQ_PORT_CAP_SUBS_READ;
|
|
|
|
const snd_seq_addr_t *sender = snd_seq_port_info_get_addr(pinfo);
|
|
|
|
const char *pname = snd_seq_port_info_get_name(pinfo);
|
|
|
|
|
|
|
|
if ((snd_seq_port_info_get_type(pinfo) & needed_type) != needed_type)
|
|
|
|
continue;
|
|
|
|
if ((snd_seq_port_info_get_capability(pinfo) & needed_cap) != needed_cap)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
snd_seq_port_subscribe_set_sender(subs, sender);
|
|
|
|
snd_seq_port_subscribe_set_dest(subs, dest);
|
|
|
|
|
|
|
|
if (snd_seq_get_port_subscription(seq, subs) == 0) {
|
|
|
|
FLUID_LOG(FLUID_WARN, "Connection %s is already subscribed\n", pname);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if (snd_seq_subscribe_port(seq, subs) < 0) {
|
|
|
|
FLUID_LOG(FLUID_ERR, "Connection of %s failed (%s)\n", pname, snd_strerror(errno));
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
FLUID_LOG(FLUID_INFO, "Connection of %s succeeded\n", pname);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2004-05-05 20:27:22 +00:00
|
|
|
|
2003-03-11 16:56:45 +00:00
|
|
|
/*
|
|
|
|
* new_fluid_alsa_seq_driver
|
|
|
|
*/
|
2007-03-04 16:45:05 +00:00
|
|
|
fluid_midi_driver_t*
|
|
|
|
new_fluid_alsa_seq_driver(fluid_settings_t* settings,
|
2003-03-11 16:56:45 +00:00
|
|
|
handle_midi_event_func_t handler, void* data)
|
|
|
|
{
|
|
|
|
int i, err;
|
2017-10-25 16:51:29 +02:00
|
|
|
int autoconn_inputs = 0;
|
2003-03-11 16:56:45 +00:00
|
|
|
fluid_alsa_seq_driver_t* dev;
|
Added public API functions: fluid_synth_sysex, fluid_synth_activate_key_tuning, fluid_synth_activate_octave_tuning, fluid_synth_tune_notes, fluid_synth_activate_tuning and fluid_synth_deactivate_tuning.
Added audio.realtime, audio.realtime-prio, midi.realtime and midi.realtime-prio (only ALSA updated to use them at this point).
Fixed bug in new_fluid_channel() where tuning field was not initialized.
fluid_settings.h had wrong field order in prototypes for fluid_settings_register_num and fluid_settings_register_int.
Added multi core support: "synth.cpu-cores" setting, fluid_synth_core_thread_func() and many core_ variables to fluid_synth_t.
Switched fluid_mutex_t back to a normal non-recursive mutex and added fluid_rec_mutex_t.
Added fluid_cond_t thread synchronization stuff.
Added fluid_cond_mutex_t which is a dynamically allocated regular mutex for use with fluid_cond_t.
fluid_settings_t and fluid_synth_t are now using fluid_rec_mutex_t (same as before, just name change).
Added platform specific fluid_thread_self_set_prio() functions to fluid_sys.c for activating high priority for the calling thread.
Modified new_fluid_thread() to take a prio and prio_level parameters.
Added missing fluid_atomic_pointer_set().
fluid_voice_write() changed to only take a voice audio buffer to render to, mixing is done separately with new fluid_voice_mix().
fluid_voice_write() now returns the count of samples rendered.
fluid_voice_effects() split into fluid_voice_filter() and fluid_voice_mix().
Added dsp_buf_count field to fluid_voice_t to keep track of last count of samples rendered to dsp_buf.
fluid_voice_get_channel() converted to a macro.
Added FLUID_DEFAULT_AUDIO_RT_PRIO and FLUID_DEFAULT_MIDI_RT_PRIO in fluidsynth_priv.h, set to 90 and 80 respectively which get used for audio.realtime-prio and midi.realtime-prio (was 90 and 90 before).
2009-09-29 21:40:28 +00:00
|
|
|
int realtime_prio = 0;
|
2003-03-11 16:56:45 +00:00
|
|
|
int count;
|
|
|
|
struct pollfd *pfd = NULL;
|
2009-09-22 07:04:07 +00:00
|
|
|
char *device = NULL;
|
|
|
|
char *id = NULL;
|
|
|
|
char *portname = NULL;
|
2003-03-11 16:56:45 +00:00
|
|
|
char full_id[64];
|
|
|
|
char full_name[64];
|
2004-05-05 20:27:22 +00:00
|
|
|
snd_seq_port_info_t *port_info = NULL;
|
|
|
|
int midi_channels;
|
2003-03-11 16:56:45 +00:00
|
|
|
|
|
|
|
/* not much use doing anything */
|
|
|
|
if (handler == NULL) {
|
|
|
|
FLUID_LOG(FLUID_ERR, "Invalid argument");
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* allocate the device */
|
|
|
|
dev = FLUID_NEW(fluid_alsa_seq_driver_t);
|
|
|
|
if (dev == NULL) {
|
|
|
|
FLUID_LOG(FLUID_ERR, "Out of memory");
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
FLUID_MEMSET(dev, 0, sizeof(fluid_alsa_seq_driver_t));
|
|
|
|
dev->driver.data = data;
|
|
|
|
dev->driver.handler = handler;
|
|
|
|
|
Added public API functions: fluid_synth_sysex, fluid_synth_activate_key_tuning, fluid_synth_activate_octave_tuning, fluid_synth_tune_notes, fluid_synth_activate_tuning and fluid_synth_deactivate_tuning.
Added audio.realtime, audio.realtime-prio, midi.realtime and midi.realtime-prio (only ALSA updated to use them at this point).
Fixed bug in new_fluid_channel() where tuning field was not initialized.
fluid_settings.h had wrong field order in prototypes for fluid_settings_register_num and fluid_settings_register_int.
Added multi core support: "synth.cpu-cores" setting, fluid_synth_core_thread_func() and many core_ variables to fluid_synth_t.
Switched fluid_mutex_t back to a normal non-recursive mutex and added fluid_rec_mutex_t.
Added fluid_cond_t thread synchronization stuff.
Added fluid_cond_mutex_t which is a dynamically allocated regular mutex for use with fluid_cond_t.
fluid_settings_t and fluid_synth_t are now using fluid_rec_mutex_t (same as before, just name change).
Added platform specific fluid_thread_self_set_prio() functions to fluid_sys.c for activating high priority for the calling thread.
Modified new_fluid_thread() to take a prio and prio_level parameters.
Added missing fluid_atomic_pointer_set().
fluid_voice_write() changed to only take a voice audio buffer to render to, mixing is done separately with new fluid_voice_mix().
fluid_voice_write() now returns the count of samples rendered.
fluid_voice_effects() split into fluid_voice_filter() and fluid_voice_mix().
Added dsp_buf_count field to fluid_voice_t to keep track of last count of samples rendered to dsp_buf.
fluid_voice_get_channel() converted to a macro.
Added FLUID_DEFAULT_AUDIO_RT_PRIO and FLUID_DEFAULT_MIDI_RT_PRIO in fluidsynth_priv.h, set to 90 and 80 respectively which get used for audio.realtime-prio and midi.realtime-prio (was 90 and 90 before).
2009-09-29 21:40:28 +00:00
|
|
|
fluid_settings_getint (settings, "midi.realtime-prio", &realtime_prio);
|
|
|
|
|
2003-03-11 16:56:45 +00:00
|
|
|
/* get the device name. if none is specified, use the default device. */
|
2017-09-21 22:09:24 +02:00
|
|
|
if (fluid_settings_dupstr(settings, "midi.alsa_seq.device", &device) != FLUID_OK) /* ++ alloc device name */
|
2009-09-22 07:04:07 +00:00
|
|
|
goto error_recovery;
|
|
|
|
|
2017-09-21 22:09:24 +02:00
|
|
|
if (fluid_settings_dupstr(settings, "midi.alsa_seq.id", &id) != FLUID_OK) /* ++ alloc id string */
|
2009-09-22 07:04:07 +00:00
|
|
|
goto error_recovery;
|
2003-03-11 16:56:45 +00:00
|
|
|
|
2004-05-05 20:27:22 +00:00
|
|
|
if (id == NULL) {
|
2009-09-22 07:04:07 +00:00
|
|
|
id = FLUID_MALLOC (32);
|
|
|
|
if (!id)
|
|
|
|
{
|
|
|
|
FLUID_LOG(FLUID_ERR, "Out of memory");
|
|
|
|
goto error_recovery;
|
|
|
|
}
|
|
|
|
|
|
|
|
sprintf(id, "%d", getpid());
|
2004-05-05 20:27:22 +00:00
|
|
|
}
|
|
|
|
|
2008-09-15 04:17:24 +00:00
|
|
|
/* get the midi portname */
|
2009-09-22 07:04:07 +00:00
|
|
|
fluid_settings_dupstr(settings, "midi.portname", &portname);
|
|
|
|
if (portname && FLUID_STRLEN (portname) == 0)
|
|
|
|
{
|
|
|
|
FLUID_FREE (portname); /* -- free port name */
|
|
|
|
portname = NULL;
|
|
|
|
}
|
2008-09-15 04:17:24 +00:00
|
|
|
|
2006-11-21 20:04:31 +00:00
|
|
|
/* open the sequencer INPUT only */
|
2009-09-22 07:04:07 +00:00
|
|
|
err = snd_seq_open(&dev->seq_handle, device ? device : "default", SND_SEQ_OPEN_INPUT, 0);
|
2004-05-05 20:27:22 +00:00
|
|
|
if (err < 0) {
|
2003-03-11 16:56:45 +00:00
|
|
|
FLUID_LOG(FLUID_ERR, "Error opening ALSA sequencer");
|
|
|
|
goto error_recovery;
|
|
|
|
}
|
|
|
|
|
2009-10-30 21:49:54 +00:00
|
|
|
snd_seq_nonblock (dev->seq_handle, 1);
|
|
|
|
|
2003-03-11 16:56:45 +00:00
|
|
|
/* get # of MIDI file descriptors */
|
|
|
|
count = snd_seq_poll_descriptors_count(dev->seq_handle, POLLIN);
|
|
|
|
if (count > 0) { /* make sure there are some */
|
|
|
|
pfd = FLUID_MALLOC(sizeof (struct pollfd) * count);
|
|
|
|
dev->pfd = FLUID_MALLOC(sizeof (struct pollfd) * count);
|
|
|
|
/* grab file descriptor POLL info structures */
|
|
|
|
count = snd_seq_poll_descriptors(dev->seq_handle, pfd, count, POLLIN);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* 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;
|
2007-03-04 16:45:05 +00:00
|
|
|
dev->pfd[dev->npfd].events = POLLIN;
|
|
|
|
dev->pfd[dev->npfd].revents = 0;
|
2003-03-11 16:56:45 +00:00
|
|
|
dev->npfd++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
FLUID_FREE(pfd);
|
2007-03-04 16:45:05 +00:00
|
|
|
|
2003-03-11 16:56:45 +00:00
|
|
|
/* set the client name */
|
2008-09-15 04:17:24 +00:00
|
|
|
if (!portname) {
|
|
|
|
snd_seq_set_client_name(dev->seq_handle, fluid_alsa_seq_full_id(id, full_id, 64));
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
snd_seq_set_client_name(dev->seq_handle, portname);
|
|
|
|
}
|
2004-05-05 20:27:22 +00:00
|
|
|
|
|
|
|
|
|
|
|
/* create the ports */
|
|
|
|
snd_seq_port_info_alloca(&port_info);
|
|
|
|
FLUID_MEMSET(port_info, 0, snd_seq_port_info_sizeof());
|
|
|
|
|
|
|
|
fluid_settings_getint(settings, "synth.midi-channels", &midi_channels);
|
|
|
|
dev->port_count = midi_channels / 16;
|
|
|
|
|
2007-03-04 16:45:05 +00:00
|
|
|
snd_seq_port_info_set_capability(port_info,
|
|
|
|
SND_SEQ_PORT_CAP_WRITE |
|
2004-05-05 20:27:22 +00:00
|
|
|
SND_SEQ_PORT_CAP_SUBS_WRITE);
|
2007-03-04 16:45:05 +00:00
|
|
|
snd_seq_port_info_set_type(port_info,
|
2017-07-05 18:43:58 +02:00
|
|
|
SND_SEQ_PORT_TYPE_MIDI_GM |
|
|
|
|
SND_SEQ_PORT_TYPE_SYNTHESIZER |
|
|
|
|
SND_SEQ_PORT_TYPE_APPLICATION |
|
|
|
|
SND_SEQ_PORT_TYPE_MIDI_GENERIC);
|
2004-05-05 20:27:22 +00:00
|
|
|
snd_seq_port_info_set_midi_channels(port_info, 16);
|
|
|
|
snd_seq_port_info_set_port_specified(port_info, 1);
|
|
|
|
|
|
|
|
for (i = 0; i < dev->port_count; i++) {
|
2003-03-11 16:56:45 +00:00
|
|
|
|
2008-09-15 04:17:24 +00:00
|
|
|
if (!portname) {
|
|
|
|
snd_seq_port_info_set_name(port_info, fluid_alsa_seq_full_name(id, i, full_name, 64));
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
snd_seq_port_info_set_name(port_info, portname);
|
|
|
|
}
|
2004-05-05 20:27:22 +00:00
|
|
|
snd_seq_port_info_set_port(port_info, i);
|
|
|
|
|
|
|
|
err = snd_seq_create_port(dev->seq_handle, port_info);
|
|
|
|
if (err < 0) {
|
2003-03-11 16:56:45 +00:00
|
|
|
FLUID_LOG(FLUID_ERR, "Error creating ALSA sequencer port");
|
|
|
|
goto error_recovery;
|
|
|
|
}
|
2004-05-05 20:27:22 +00:00
|
|
|
}
|
|
|
|
|
2017-10-26 15:56:27 +02:00
|
|
|
fluid_settings_getint(settings, "midi.autoconnect", &autoconn_inputs);
|
2016-05-02 12:20:34 +02:00
|
|
|
if (autoconn_inputs)
|
2017-10-26 15:51:34 +02:00
|
|
|
fluid_alsa_seq_autoconnect(dev, port_info);
|
2016-05-02 12:20:34 +02:00
|
|
|
|
2006-02-19 10:01:58 +00:00
|
|
|
/* tell the lash server our client id */
|
|
|
|
#ifdef LASH_ENABLED
|
2004-05-05 20:27:22 +00:00
|
|
|
{
|
2006-02-19 10:01:58 +00:00
|
|
|
int enable_lash = 0;
|
|
|
|
fluid_settings_getint (settings, "lash.enable", &enable_lash);
|
|
|
|
if (enable_lash)
|
|
|
|
fluid_lash_alsa_client_id (fluid_lash_client, snd_seq_client_id (dev->seq_handle));
|
2004-05-05 20:27:22 +00:00
|
|
|
}
|
2006-02-19 10:01:58 +00:00
|
|
|
#endif /* LASH_ENABLED */
|
2003-03-11 16:56:45 +00:00
|
|
|
|
2017-10-28 12:57:47 +02:00
|
|
|
fluid_atomic_int_set(&dev->should_quit, 0);
|
2003-03-11 16:56:45 +00:00
|
|
|
|
2009-10-30 21:49:54 +00:00
|
|
|
/* create the MIDI thread */
|
2013-08-19 23:10:43 +00:00
|
|
|
dev->thread = new_fluid_thread ("alsa-midi-seq", fluid_alsa_seq_run, dev, realtime_prio, FALSE);
|
2009-09-22 07:04:07 +00:00
|
|
|
|
|
|
|
if (portname) FLUID_FREE (portname);
|
|
|
|
if (id) FLUID_FREE (id);
|
|
|
|
if (device) FLUID_FREE (device);
|
|
|
|
|
2003-03-11 16:56:45 +00:00
|
|
|
return (fluid_midi_driver_t*) dev;
|
|
|
|
|
|
|
|
error_recovery:
|
2009-09-22 07:04:07 +00:00
|
|
|
|
|
|
|
if (portname) FLUID_FREE (portname);
|
|
|
|
if (id) FLUID_FREE (id);
|
|
|
|
if (device) FLUID_FREE (device);
|
|
|
|
|
2003-03-11 16:56:45 +00:00
|
|
|
delete_fluid_alsa_seq_driver((fluid_midi_driver_t*) dev);
|
2009-09-22 07:04:07 +00:00
|
|
|
|
2003-03-11 16:56:45 +00:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* delete_fluid_alsa_seq_driver
|
|
|
|
*/
|
2007-03-04 16:45:05 +00:00
|
|
|
int
|
2003-03-11 16:56:45 +00:00
|
|
|
delete_fluid_alsa_seq_driver(fluid_midi_driver_t* p)
|
|
|
|
{
|
|
|
|
fluid_alsa_seq_driver_t* dev;
|
|
|
|
|
|
|
|
dev = (fluid_alsa_seq_driver_t*) p;
|
|
|
|
if (dev == NULL) {
|
|
|
|
return FLUID_OK;
|
|
|
|
}
|
|
|
|
|
2009-10-30 21:49:54 +00:00
|
|
|
/* cancel the thread and wait for it before cleaning up */
|
2017-10-28 12:57:47 +02:00
|
|
|
fluid_atomic_int_set(&dev->should_quit, 1);
|
2003-03-11 16:56:45 +00:00
|
|
|
|
2009-10-30 21:49:54 +00:00
|
|
|
if (dev->thread)
|
|
|
|
fluid_thread_join (dev->thread);
|
|
|
|
|
2003-03-11 16:56:45 +00:00
|
|
|
if (dev->seq_handle) {
|
|
|
|
snd_seq_close(dev->seq_handle);
|
|
|
|
}
|
2006-11-23 18:49:36 +00:00
|
|
|
|
|
|
|
if (dev->pfd) FLUID_FREE (dev->pfd);
|
|
|
|
|
2003-03-11 16:56:45 +00:00
|
|
|
FLUID_FREE(dev);
|
|
|
|
return FLUID_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* fluid_alsa_seq_run
|
|
|
|
*/
|
2017-10-26 18:08:10 +02:00
|
|
|
fluid_thread_return_t
|
2003-03-11 16:56:45 +00:00
|
|
|
fluid_alsa_seq_run(void* d)
|
|
|
|
{
|
2006-11-23 18:49:36 +00:00
|
|
|
int n, ev;
|
2003-03-11 16:56:45 +00:00
|
|
|
snd_seq_event_t *seq_ev;
|
|
|
|
fluid_midi_event_t evt;
|
|
|
|
fluid_alsa_seq_driver_t* dev = (fluid_alsa_seq_driver_t*) d;
|
|
|
|
|
|
|
|
/* go into a loop until someone tells us to stop */
|
2017-10-28 12:57:47 +02:00
|
|
|
while (!fluid_atomic_int_get(&dev->should_quit)) {
|
2003-03-11 16:56:45 +00:00
|
|
|
|
|
|
|
/* is there something to read? */
|
2006-11-21 20:04:31 +00:00
|
|
|
n = poll(dev->pfd, dev->npfd, 100); /* use a 100 milliseconds timeout */
|
2003-03-11 16:56:45 +00:00
|
|
|
if (n < 0) {
|
|
|
|
perror("poll");
|
2006-11-21 20:04:31 +00:00
|
|
|
} else if (n > 0) { /* check for pending events */
|
2006-11-23 18:49:36 +00:00
|
|
|
do
|
2003-03-11 16:56:45 +00:00
|
|
|
{
|
2006-11-23 18:49:36 +00:00
|
|
|
ev = snd_seq_event_input(dev->seq_handle, &seq_ev); /* read the events */
|
|
|
|
|
2009-10-30 21:49:54 +00:00
|
|
|
if (ev == -EAGAIN) break;
|
|
|
|
|
2006-11-23 18:49:36 +00:00
|
|
|
/* Negative value indicates an error, ignore interrupted system call
|
|
|
|
* (-EPERM) and input event buffer overrun (-ENOSPC) */
|
|
|
|
if (ev < 0)
|
|
|
|
{ /* FIXME - report buffer overrun? */
|
|
|
|
if (ev != -EPERM && ev != -ENOSPC)
|
|
|
|
{
|
|
|
|
FLUID_LOG(FLUID_ERR, "Error while reading ALSA sequencer (code=%d)", ev);
|
2017-10-28 12:57:47 +02:00
|
|
|
fluid_atomic_int_set(&dev->should_quit, 1);
|
2006-11-23 18:49:36 +00:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
2006-11-21 20:04:31 +00:00
|
|
|
|
2006-11-23 18:49:36 +00:00
|
|
|
switch (seq_ev->type)
|
2003-03-11 16:56:45 +00:00
|
|
|
{
|
|
|
|
case SND_SEQ_EVENT_NOTEON:
|
|
|
|
evt.type = NOTE_ON;
|
2004-05-05 20:27:22 +00:00
|
|
|
evt.channel = seq_ev->dest.port * 16 + seq_ev->data.note.channel;
|
2003-03-11 16:56:45 +00:00
|
|
|
evt.param1 = seq_ev->data.note.note;
|
|
|
|
evt.param2 = seq_ev->data.note.velocity;
|
|
|
|
break;
|
|
|
|
case SND_SEQ_EVENT_NOTEOFF:
|
|
|
|
evt.type = NOTE_OFF;
|
2004-05-05 20:27:22 +00:00
|
|
|
evt.channel = seq_ev->dest.port * 16 + seq_ev->data.note.channel;
|
2003-03-11 16:56:45 +00:00
|
|
|
evt.param1 = seq_ev->data.note.note;
|
|
|
|
evt.param2 = seq_ev->data.note.velocity;
|
|
|
|
break;
|
|
|
|
case SND_SEQ_EVENT_KEYPRESS:
|
|
|
|
evt.type = KEY_PRESSURE;
|
2004-05-05 20:27:22 +00:00
|
|
|
evt.channel = seq_ev->dest.port * 16 + seq_ev->data.note.channel;
|
2003-03-11 16:56:45 +00:00
|
|
|
evt.param1 = seq_ev->data.note.note;
|
|
|
|
evt.param2 = seq_ev->data.note.velocity;
|
|
|
|
break;
|
|
|
|
case SND_SEQ_EVENT_CONTROLLER:
|
|
|
|
evt.type = CONTROL_CHANGE;
|
2004-05-05 20:27:22 +00:00
|
|
|
evt.channel = seq_ev->dest.port * 16 + seq_ev->data.control.channel;
|
2003-03-11 16:56:45 +00:00
|
|
|
evt.param1 = seq_ev->data.control.param;
|
|
|
|
evt.param2 = seq_ev->data.control.value;
|
|
|
|
break;
|
|
|
|
case SND_SEQ_EVENT_PITCHBEND:
|
|
|
|
evt.type = PITCH_BEND;
|
2004-05-05 20:27:22 +00:00
|
|
|
evt.channel = seq_ev->dest.port * 16 + seq_ev->data.control.channel;
|
2003-03-11 16:56:45 +00:00
|
|
|
|
|
|
|
/* ALSA pitch bend is -8192 - 8191, we adjust it here */
|
|
|
|
evt.param1 = seq_ev->data.control.value + 8192;
|
|
|
|
break;
|
|
|
|
case SND_SEQ_EVENT_PGMCHANGE:
|
|
|
|
evt.type = PROGRAM_CHANGE;
|
2004-05-05 20:27:22 +00:00
|
|
|
evt.channel = seq_ev->dest.port * 16 + seq_ev->data.control.channel;
|
2003-03-11 16:56:45 +00:00
|
|
|
evt.param1 = seq_ev->data.control.value;
|
|
|
|
break;
|
|
|
|
case SND_SEQ_EVENT_CHANPRESS:
|
|
|
|
evt.type = CHANNEL_PRESSURE;
|
2004-05-05 20:27:22 +00:00
|
|
|
evt.channel = seq_ev->dest.port * 16 + seq_ev->data.control.channel;
|
2003-03-11 16:56:45 +00:00
|
|
|
evt.param1 = seq_ev->data.control.value;
|
|
|
|
break;
|
2009-10-12 19:18:06 +00:00
|
|
|
case SND_SEQ_EVENT_SYSEX:
|
|
|
|
if (seq_ev->data.ext.len < 2) continue;
|
|
|
|
|
|
|
|
fluid_midi_event_set_sysex (&evt, (char *)(seq_ev->data.ext.ptr) + 1,
|
|
|
|
seq_ev->data.ext.len - 2, FALSE);
|
|
|
|
break;
|
2003-03-11 16:56:45 +00:00
|
|
|
default:
|
|
|
|
continue; /* unhandled event, next loop iteration */
|
|
|
|
}
|
|
|
|
|
2006-11-23 18:49:36 +00:00
|
|
|
/* send the events to the next link in the chain */
|
|
|
|
(*dev->driver.handler)(dev->driver.data, &evt);
|
2003-03-11 16:56:45 +00:00
|
|
|
}
|
2006-11-23 18:49:36 +00:00
|
|
|
while (ev > 0);
|
2006-11-21 20:04:31 +00:00
|
|
|
} /* if poll() > 0 */
|
2010-10-08 08:15:13 +00:00
|
|
|
} /* while (!dev->should_quit) */
|
2017-10-26 18:08:10 +02:00
|
|
|
|
|
|
|
return FLUID_THREAD_RETURN_VALUE;
|
2003-03-11 16:56:45 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#endif /* #if ALSA_SUPPORT */
|