Added events for bank select and SoundFont ID select and a payload structure for MIDI channel and integer values.

Return queue process is now a thread, instead of a timer, which blocks until signaled by synthesis thread.
fluid_synth_one_block() signals return queue process if any events are pending.
Program change messages are now queued via return event queue if they occur in synthesis context.
Changed fluid_synth_cc() to always queue when in non-synthesis context, no more exceptions.
fluid_synth_bank_select() and fluid_synth_sfont_select() now queue if called out of synthesis context.
Removed checks for synth->state == FLUID_SYNTH_PLAYING, since those functions shouldn't be getting called after state is changed to STOPPED (in delete_fluid_synth).
This commit is contained in:
Josh Green 2009-11-17 08:13:23 +00:00
parent 7758f5a630
commit 35f5a739a2
3 changed files with 196 additions and 98 deletions

View file

@ -40,7 +40,9 @@ enum fluid_event_queue_elem
FLUID_EVENT_QUEUE_ELEM_FREE_PRESET, /**< Free preset return event. Uses pval field of event value */
FLUID_EVENT_QUEUE_ELEM_SET_TUNING, /**< Set tuning event. Uses set_tuning field of event value */
FLUID_EVENT_QUEUE_ELEM_REPL_TUNING, /**< Replace tuning event. Uses repl_tuning field of event value */
FLUID_EVENT_QUEUE_ELEM_UNREF_TUNING /**< Unref tuning return event. Uses unref_tuning field of event value */
FLUID_EVENT_QUEUE_ELEM_UNREF_TUNING, /**< Unref tuning return event. Uses unref_tuning field of event value */
FLUID_EVENT_QUEUE_ELEM_BANK_SELECT, /**< MIDI bank select on a channel. Uses chan_int field. */
FLUID_EVENT_QUEUE_ELEM_SFONT_ID /**< SoundFont ID select on a channel. Uses chan_int field. */
};
/**
@ -117,6 +119,14 @@ typedef struct
int count; /**< Number of times to unref */
} fluid_event_unref_tuning_t;
/**
* Structure for an integer parameter sent to a MIDI channel (bank or SoundFont ID for example).
*/
typedef struct
{
int channel;
int val;
} fluid_event_channel_int_t;
/**
* Event queue element structure.
@ -135,6 +145,7 @@ typedef struct
fluid_event_set_tuning_t set_tuning; /**< If type == FLUID_EVENT_QUEUE_ELEM_SET_TUNING */
fluid_event_repl_tuning_t repl_tuning; /**< If type == FLUID_EVENT_QUEUE_ELEM_REPL_TUNING */
fluid_event_unref_tuning_t unref_tuning; /**< If type == FLUID_EVENT_QUEUE_ELEM_UNREF_TUNING */
fluid_event_channel_int_t chan_int; /**< If type == FLUID_EVENT_QUEUE_ELEM_BANK_SELECT || type == FLUID_EVENT_QUEUE_ELEM_SFONT_ID */
double dval; /**< A floating point payload value */
int ival; /**< An integer payload value */
void *pval; /**< A pointer payload value */

View file

@ -44,19 +44,21 @@ extern int feenableexcept (int excepts);
static void fluid_synth_init(void);
static int fluid_synth_return_event_process_callback (void* data, unsigned int msec);
static void fluid_synth_return_event_process_thread (void* data);
static fluid_event_queue_t *fluid_synth_get_event_queue (fluid_synth_t* synth);
static int fluid_synth_queue_midi_event (fluid_synth_t* synth, int type, int chan,
int param1, int param2);
static int fluid_synth_queue_gen_event (fluid_synth_t* synth, int chan,
int param, float value, int absolute);
static int fluid_synth_queue_int_event (fluid_synth_t* synth, int type, int val);
static int fluid_synth_queue_chan_int_event (fluid_synth_t* synth, int type,
int chan, int val);
static void fluid_synth_thread_queue_destroy_notify (void *data);
static int fluid_synth_noteon_LOCAL(fluid_synth_t* synth, int chan, int key,
int vel);
static int fluid_synth_noteoff_LOCAL(fluid_synth_t* synth, int chan, int key);
static int fluid_synth_damp_voices_LOCAL(fluid_synth_t* synth, int chan);
static int fluid_synth_cc_real(fluid_synth_t* synth, int channum, int num, int noqueue);
static int fluid_synth_cc_LOCAL(fluid_synth_t* synth, int channum, int num);
static int fluid_synth_update_device_id (fluid_synth_t *synth, char *name,
int value);
static int fluid_synth_sysex_midi_tuning (fluid_synth_t *synth, const char *data,
@ -82,6 +84,10 @@ fluid_synth_get_preset(fluid_synth_t* synth, unsigned int sfontnum,
static fluid_preset_t*
fluid_synth_get_preset_by_sfont_name(fluid_synth_t* synth, const char *sfontname,
unsigned int banknum, unsigned int prognum);
static void fluid_synth_bank_select_LOCAL (fluid_synth_t *synth, int chan,
unsigned int bank);
static void fluid_synth_sfont_select_LOCAL (fluid_synth_t *synth, int chan,
unsigned int sfont_id);
static void fluid_synth_update_presets(fluid_synth_t* synth);
static int fluid_synth_update_gain(fluid_synth_t* synth,
@ -517,6 +523,8 @@ new_fluid_synth(fluid_settings_t *settings)
fluid_private_init(synth->thread_queues);
synth->return_queue = fluid_event_queue_new (FLUID_MAX_RETURN_EVENTS);
synth->return_queue_mutex = new_fluid_cond_mutex ();
synth->return_queue_cond = new_fluid_cond ();
if (synth->return_queue == NULL) {
FLUID_LOG(FLUID_ERR, "Out of memory");
@ -774,9 +782,9 @@ new_fluid_synth(fluid_settings_t *settings)
/* FIXME */
synth->start = fluid_curtime();
/* Spawn low priority periodic timer thread to process synth thread return events */
synth->return_queue_timer = new_fluid_timer (200, fluid_synth_return_event_process_callback,
synth, TRUE, TRUE, FALSE);
/* Spawn a thread to process synth thread return events */
synth->return_queue_thread = new_fluid_thread (fluid_synth_return_event_process_thread,
synth, 0, FALSE);
return synth;
error_recovery:
@ -785,64 +793,83 @@ new_fluid_synth(fluid_settings_t *settings)
}
/* Callback to process synthesis thread return events */
static int
fluid_synth_return_event_process_callback (void* data, unsigned int msec)
static void
fluid_synth_return_event_process_thread (void* data)
{
fluid_synth_t *synth = data;
fluid_event_queue_elem_t *event;
fluid_preset_t *preset;
fluid_sfont_t *sfont;
while ((event = fluid_event_queue_get_outptr (synth->return_queue)))
{
switch (event->type)
/* Loop while synth is PLAYING */
do
{ /* Block until we have some work to do or synth stops playing */
fluid_cond_mutex_lock (synth->return_queue_mutex);
while (!(event = fluid_event_queue_get_outptr (synth->return_queue))
&& fluid_atomic_int_get (&synth->state) == FLUID_SYNTH_PLAYING)
fluid_cond_wait (synth->return_queue_cond, synth->return_queue_mutex);
fluid_cond_mutex_unlock (synth->return_queue_mutex);
if (!event) break; /* No event means synth stopped playing */
/* Loop while there are return events */
do
{
case FLUID_EVENT_QUEUE_ELEM_REVERB: /* Sync reverb shadow variables */
if (event->reverb.set & FLUID_REVMODEL_SET_ROOMSIZE)
fluid_atomic_float_set (&synth->reverb_roomsize, event->reverb.roomsize);
switch (event->type)
{
case FLUID_EVENT_QUEUE_ELEM_REVERB: /* Sync reverb shadow variables */
if (event->reverb.set & FLUID_REVMODEL_SET_ROOMSIZE)
fluid_atomic_float_set (&synth->reverb_roomsize, event->reverb.roomsize);
if (event->reverb.set & FLUID_REVMODEL_SET_DAMPING)
fluid_atomic_float_set (&synth->reverb_damping, event->reverb.damping);
if (event->reverb.set & FLUID_REVMODEL_SET_DAMPING)
fluid_atomic_float_set (&synth->reverb_damping, event->reverb.damping);
if (event->reverb.set & FLUID_REVMODEL_SET_WIDTH)
fluid_atomic_float_set (&synth->reverb_width, event->reverb.width);
if (event->reverb.set & FLUID_REVMODEL_SET_WIDTH)
fluid_atomic_float_set (&synth->reverb_width, event->reverb.width);
if (event->reverb.set & FLUID_REVMODEL_SET_LEVEL)
fluid_atomic_float_set (&synth->reverb_level, event->reverb.level);
break;
case FLUID_EVENT_QUEUE_ELEM_CHORUS: /* Sync chorus shadow variables */
if (event->chorus.set & FLUID_CHORUS_SET_NR)
fluid_atomic_int_set (&synth->chorus_nr, event->chorus.nr);
if (event->reverb.set & FLUID_REVMODEL_SET_LEVEL)
fluid_atomic_float_set (&synth->reverb_level, event->reverb.level);
break;
case FLUID_EVENT_QUEUE_ELEM_CHORUS: /* Sync chorus shadow variables */
if (event->chorus.set & FLUID_CHORUS_SET_NR)
fluid_atomic_int_set (&synth->chorus_nr, event->chorus.nr);
if (event->chorus.set & FLUID_CHORUS_SET_LEVEL)
fluid_atomic_float_set (&synth->chorus_level, event->chorus.level);
if (event->chorus.set & FLUID_CHORUS_SET_LEVEL)
fluid_atomic_float_set (&synth->chorus_level, event->chorus.level);
if (event->chorus.set & FLUID_CHORUS_SET_SPEED)
fluid_atomic_float_set (&synth->chorus_speed, event->chorus.speed);
if (event->chorus.set & FLUID_CHORUS_SET_SPEED)
fluid_atomic_float_set (&synth->chorus_speed, event->chorus.speed);
if (event->chorus.set & FLUID_CHORUS_SET_DEPTH)
fluid_atomic_float_set (&synth->chorus_depth, event->chorus.depth);
if (event->chorus.set & FLUID_CHORUS_SET_DEPTH)
fluid_atomic_float_set (&synth->chorus_depth, event->chorus.depth);
if (event->chorus.set & FLUID_CHORUS_SET_TYPE)
fluid_atomic_int_set (&synth->chorus_type, event->chorus.type);
break;
case FLUID_EVENT_QUEUE_ELEM_FREE_PRESET: /* Preset free event */
preset = (fluid_preset_t *)(event->pval);
sfont = preset->sfont;
if (event->chorus.set & FLUID_CHORUS_SET_TYPE)
fluid_atomic_int_set (&synth->chorus_type, event->chorus.type);
break;
case FLUID_EVENT_QUEUE_ELEM_FREE_PRESET: /* Preset free event */
preset = (fluid_preset_t *)(event->pval);
sfont = preset->sfont;
/* Delete presets under mutex lock, to protect chan->shadow_preset */
fluid_rec_mutex_lock (synth->mutex);
delete_fluid_preset (preset);
fluid_rec_mutex_unlock (synth->mutex);
/* Delete presets under mutex lock, to protect chan->shadow_preset */
fluid_rec_mutex_lock (synth->mutex);
delete_fluid_preset (preset);
fluid_rec_mutex_unlock (synth->mutex);
fluid_synth_sfont_unref (synth, sfont); /* -- unref preset's SoundFont */
break;
fluid_synth_sfont_unref (synth, sfont); /* -- unref preset's SoundFont */
break;
case FLUID_EVENT_QUEUE_ELEM_MIDI:
if (event->midi.type == PROGRAM_CHANGE)
fluid_synth_program_change (synth, event->midi.channel, event->midi.param1);
break;
}
fluid_event_queue_next_outptr (synth->return_queue);
}
fluid_event_queue_next_outptr (synth->return_queue);
while ((event = fluid_event_queue_get_outptr (synth->return_queue)));
}
return TRUE; /* Keep this timer callback active */
while (fluid_atomic_int_get (&synth->state) == FLUID_SYNTH_PLAYING);
}
/**
@ -868,16 +895,28 @@ delete_fluid_synth(fluid_synth_t* synth)
fluid_profiling_print();
synth->state = FLUID_SYNTH_STOPPED;
/* Stop return event queue thread, and process remaining events */
if (synth->return_queue_timer)
delete_fluid_timer (synth->return_queue_timer);
if (synth->return_queue_thread)
{ /* Signal the return queue thread to cause it to exit */
fluid_cond_mutex_lock (synth->return_queue_mutex);
fluid_atomic_int_set (&synth->state, FLUID_SYNTH_STOPPED);
fluid_cond_signal (synth->return_queue_cond);
fluid_cond_mutex_unlock (synth->return_queue_mutex);
if (synth->return_queue) {
fluid_synth_return_event_process_callback(synth, 0);
fluid_event_queue_free(synth->return_queue);
fluid_thread_join (synth->return_queue_thread);
delete_fluid_thread (synth->return_queue_thread);
}
else fluid_atomic_int_set (&synth->state, FLUID_SYNTH_STOPPED);
if (synth->return_queue)
fluid_event_queue_free(synth->return_queue);
if (synth->return_queue_mutex)
delete_fluid_cond_mutex (synth->return_queue_mutex);
if (synth->return_queue_cond)
delete_fluid_cond (synth->return_queue_cond);
/* Free multi-core resources (if multi-core enabled) */
if (synth->cores > 1)
@ -1224,6 +1263,32 @@ fluid_synth_queue_int_event (fluid_synth_t* synth, int type, int val)
return FLUID_OK;
}
/**
* Queues an event with a channel and integer value payload.
* @param synth FluidSynth instance
* @param type Event type (#fluid_event_queue_elem)
* @param chan MIDI channel
* @param val Event value
* @return FLUID_OK on success, FLUID_FAILED otherwise
*/
static int
fluid_synth_queue_chan_int_event (fluid_synth_t* synth, int type, int chan, int val)
{
fluid_event_queue_t *queue;
fluid_event_queue_elem_t *event;
event = fluid_synth_get_event_elem (synth, &queue);
if (!event) return FLUID_FAILED;
event->type = type;
event->chan_int.channel = chan;
event->chan_int.val = val;
fluid_event_queue_next_inptr (queue);
return FLUID_OK;
}
/* Gets called when a thread ends, which has been assigned a queue */
static void
fluid_synth_thread_queue_destroy_notify (void *data)
@ -1382,41 +1447,21 @@ fluid_synth_cc(fluid_synth_t* synth, int chan, int num, int val)
fluid_channel_set_cc (synth->channel[chan], num, val);
fluid_synth_cc_real (synth, chan, num, FALSE);
if (fluid_synth_should_queue (synth))
return fluid_synth_queue_midi_event (synth, CONTROL_CHANGE, chan, num, 0);
else fluid_synth_cc_LOCAL (synth, chan, num);
return FLUID_OK;
}
/* Local synthesis thread variant of MIDI CC set function. */
static int
fluid_synth_cc_real (fluid_synth_t* synth, int channum, int num, int noqueue)
fluid_synth_cc_LOCAL (fluid_synth_t* synth, int channum, int num)
{
fluid_channel_t* chan = synth->channel[channum];
int nrpn_select, nrpn_active = 0;
int rpn_msb = 0, rpn_lsb = 0;
int nrpn_select;
int value;
/* nrpn_active, rpn_msb and rpn_lsb may get used multiple times, read them
* once atomically if they are needed */
if (num == DATA_ENTRY_MSB)
{
nrpn_active = fluid_atomic_int_get (&chan->nrpn_active);
if (!nrpn_active)
{
rpn_msb = fluid_channel_get_cc (chan, RPN_MSB);
rpn_lsb = fluid_channel_get_cc (chan, RPN_LSB);
}
}
/* Check if event needs to be queued. Bank select messages and tuning
* bank/program select RPN messages are not queued. */
if (!noqueue && fluid_synth_should_queue (synth)
&& num != BANK_SELECT_MSB && num != BANK_SELECT_LSB
&& !(num == DATA_ENTRY_MSB && !nrpn_active && rpn_msb == 0
&& (rpn_lsb == RPN_TUNING_PROGRAM_CHANGE || rpn_lsb == RPN_TUNING_BANK_SELECT)))
return fluid_synth_queue_midi_event (synth, CONTROL_CHANGE, channum, num, 0);
value = fluid_channel_get_cc (chan, num);
switch (num) {
@ -1443,7 +1488,7 @@ fluid_synth_cc_real (fluid_synth_t* synth, int channum, int num, int noqueue)
{
int data = (value << 7) + fluid_channel_get_cc (chan, DATA_ENTRY_LSB);
if (nrpn_active) /* NRPN is active? */
if (fluid_atomic_int_get (&chan->nrpn_active)) /* NRPN is active? */
{ /* SontFont 2.01 NRPN Message (Sect. 9.6, p. 74) */
if ((fluid_channel_get_cc (chan, NRPN_MSB) == 120)
&& (fluid_channel_get_cc (chan, NRPN_LSB) < 100))
@ -1459,9 +1504,9 @@ fluid_synth_cc_real (fluid_synth_t* synth, int channum, int num, int noqueue)
fluid_atomic_int_set (&chan->nrpn_select, 0); /* Reset to 0 */
}
}
else if (rpn_msb == 0) /* RPN is active: MSB = 0? */
else if (fluid_channel_get_cc (chan, RPN_MSB) == 0) /* RPN is active: MSB = 0? */
{
switch (rpn_lsb)
switch (fluid_channel_get_cc (chan, RPN_LSB))
{
case RPN_PITCH_BEND_RANGE: /* Set bend range in semitones */
fluid_channel_set_pitch_wheel_sensitivity (synth->channel[channum], value);
@ -2263,12 +2308,34 @@ fluid_synth_program_change(fluid_synth_t* synth, int chan, int prognum)
{
fluid_preset_t* preset = NULL;
fluid_channel_t* channel;
fluid_event_queue_elem_t *event;
int subst_bank, subst_prog, banknum;
fluid_return_val_if_fail (synth != NULL, FLUID_FAILED);
fluid_return_val_if_fail (chan >= 0 && chan < synth->midi_channels, FLUID_FAILED);
fluid_return_val_if_fail (prognum >= 0 && prognum <= FLUID_NUM_PROGRAMS, FLUID_FAILED);
/* If this is the synthesis thread, send program change via return queue, to
* avoid mutex locks and other realtime naughties */
if (fluid_synth_is_synth_thread (synth))
{
event = fluid_event_queue_get_inptr (synth->return_queue);
if (!event)
{
FLUID_LOG (FLUID_ERR, "Synth return event queue full");
return FLUID_FAILED;
}
event->type = FLUID_EVENT_QUEUE_ELEM_MIDI;
event->midi.type = PROGRAM_CHANGE;
event->midi.param1 = prognum;
event->midi.channel = chan;
fluid_event_queue_next_inptr (synth->return_queue);
return FLUID_OK;
}
channel = synth->channel[chan];
fluid_channel_get_sfont_bank_prog(channel, NULL, &banknum, NULL);
@ -2337,10 +2404,21 @@ fluid_synth_bank_select(fluid_synth_t* synth, int chan, unsigned int bank)
fluid_return_val_if_fail (synth != NULL, FLUID_FAILED);
fluid_return_val_if_fail (chan >= 0 && chan < synth->midi_channels, FLUID_FAILED);
fluid_channel_set_sfont_bank_prog(synth->channel[chan], -1, bank, -1);
if (fluid_synth_should_queue (synth))
return fluid_synth_queue_chan_int_event (synth, FLUID_EVENT_QUEUE_ELEM_BANK_SELECT,
chan, bank);
else fluid_synth_bank_select_LOCAL (synth, chan, bank);
return FLUID_OK;
}
/* Local synthesis thread variant of bank select */
static void
fluid_synth_bank_select_LOCAL (fluid_synth_t *synth, int chan, unsigned int bank)
{
fluid_channel_set_sfont_bank_prog (synth->channel[chan], -1, bank, -1);
}
/**
* Set SoundFont ID on a MIDI channel.
* @param synth FluidSynth instance
@ -2354,10 +2432,21 @@ fluid_synth_sfont_select(fluid_synth_t* synth, int chan, unsigned int sfont_id)
fluid_return_val_if_fail (synth != NULL, FLUID_FAILED);
fluid_return_val_if_fail (chan >= 0 && chan < synth->midi_channels, FLUID_FAILED);
fluid_channel_set_sfont_bank_prog(synth->channel[chan], sfont_id, -1, -1);
if (fluid_synth_should_queue (synth))
return fluid_synth_queue_chan_int_event (synth, FLUID_EVENT_QUEUE_ELEM_SFONT_ID,
chan, sfont_id);
else fluid_synth_sfont_select_LOCAL (synth, chan, sfont_id);
return FLUID_OK;
}
/* Local synthesis thread variant of SoundFont ID select */
static void
fluid_synth_sfont_select_LOCAL (fluid_synth_t *synth, int chan, unsigned int sfont_id)
{
fluid_channel_set_sfont_bank_prog(synth->channel[chan], sfont_id, -1, -1);
}
/**
* Get current SoundFont ID, bank number and program number for a MIDI channel.
* @param synth FluidSynth instance
@ -2674,10 +2763,6 @@ fluid_synth_nwrite_float(fluid_synth_t* synth, int len,
int i, num, available, count, bytes;
float cpu_load;
/* make sure we're playing */
if (synth->state != FLUID_SYNTH_PLAYING)
return FLUID_OK;
/* First, take what's still available in the buffer */
count = 0;
num = synth->cur;
@ -2788,10 +2873,6 @@ fluid_synth_write_float(fluid_synth_t* synth, int len,
double time = fluid_utime();
float cpu_load;
/* make sure we're playing */
if (synth->state != FLUID_SYNTH_PLAYING)
return FLUID_OK;
l = synth->cur;
for (i = 0, j = loff, k = roff; i < len; i++, l++, j += lincr, k += rincr) {
@ -2884,11 +2965,6 @@ fluid_synth_write_s16(fluid_synth_t* synth, int len,
float cpu_load;
fluid_profile_ref_var (prof_ref);
/* make sure we're playing */
if (synth->state != FLUID_SYNTH_PLAYING) {
return 0;
}
cur = synth->cur;
for (i = 0, j = loff, k = roff; i < len; i++, cur++, j += lincr, k += rincr) {
@ -3211,6 +3287,10 @@ got_voice: /* We got a voice to process */
fluid_check_fpe("LADSPA");
#endif
/* Signal return queue thread if there are any events pending */
if (fluid_atomic_int_get (&synth->return_queue->count) > 0)
fluid_cond_signal (synth->return_queue_cond);
synth->ticks += FLUID_BUFSIZE;
/* Testcase, that provokes a denormal floating point error */
@ -3323,8 +3403,7 @@ fluid_synth_process_event_queue_LOCAL (fluid_synth_t *synth,
event->midi.param1);
break;
case CONTROL_CHANGE:
/* Pass TRUE for noqueue, since event should never get re-queued */
fluid_synth_cc_real (synth, event->midi.channel, event->midi.param1, TRUE);
fluid_synth_cc_LOCAL (synth, event->midi.channel, event->midi.param1);
break;
case MIDI_SYSTEM_RESET:
fluid_synth_system_reset_LOCAL (synth);
@ -3381,6 +3460,12 @@ fluid_synth_process_event_queue_LOCAL (fluid_synth_t *synth,
event->repl_tuning.new_tuning,
event->repl_tuning.apply, TRUE);
break;
case FLUID_EVENT_QUEUE_ELEM_BANK_SELECT:
fluid_synth_bank_select_LOCAL (synth, event->chan_int.channel, event->chan_int.val);
break;
case FLUID_EVENT_QUEUE_ELEM_SFONT_ID:
fluid_synth_sfont_select_LOCAL (synth, event->chan_int.channel, event->chan_int.val);
break;
}
fluid_event_queue_next_outptr (queue);

View file

@ -158,7 +158,9 @@ struct _fluid_synth_t
fluid_rec_mutex_t mutex; /**< Lock for multi-thread sensitive variables (not used by synthesis process) */
fluid_list_t *queue_pool; /**< List of event queues whose threads have been destroyed and which can be re-used */
fluid_event_queue_t *return_queue; /**< Event queue for events from synthesis thread to non-synthesis threads (memory frees, etc) */
fluid_timer_t *return_queue_timer; /**< Timer thread to process return event queue */
fluid_thread_t *return_queue_thread; /**< Return event queue processing thread */
fluid_cond_mutex_t *return_queue_mutex; /**< Mutex for return queue condition */
fluid_cond_t *return_queue_cond; /**< Return queue thread synchronization condition */
fluid_settings_t* settings; /**< the synthesizer settings */
int device_id; /**< Device ID used for SYSEX messages */