Enable sequencer to accept all types of events that MIDI threads can generate. Change behavior of fluid_sequencer_send_at to ensure thread safety.

This commit is contained in:
David Henningsson 2009-06-04 20:31:06 +00:00
parent a8bffeb370
commit e25765e61b
7 changed files with 145 additions and 16 deletions

View file

@ -45,7 +45,7 @@ enum fluid_seq_event_type {
FLUID_SEQ_PROGRAMCHANGE, /**< Program change message */ FLUID_SEQ_PROGRAMCHANGE, /**< Program change message */
FLUID_SEQ_PROGRAMSELECT, /**< Program select message (DOCME) */ FLUID_SEQ_PROGRAMSELECT, /**< Program select message (DOCME) */
FLUID_SEQ_PITCHBEND, /**< Pitch bend message */ FLUID_SEQ_PITCHBEND, /**< Pitch bend message */
FLUID_SEQ_PITCHWHHELSENS, /**< Pitch wheel sensitivity set message */ FLUID_SEQ_PITCHWHHELSENS, /**< Pitch wheel sensitivity set message TODO: Correct spelling of this event? */
FLUID_SEQ_MODULATION, /**< Modulation controller event */ FLUID_SEQ_MODULATION, /**< Modulation controller event */
FLUID_SEQ_SUSTAIN, /**< Sustain controller event */ FLUID_SEQ_SUSTAIN, /**< Sustain controller event */
FLUID_SEQ_CONTROLCHANGE, /**< MIDI control change event */ FLUID_SEQ_CONTROLCHANGE, /**< MIDI control change event */
@ -55,6 +55,8 @@ enum fluid_seq_event_type {
FLUID_SEQ_CHORUSSEND, /**< Chorus send set event */ FLUID_SEQ_CHORUSSEND, /**< Chorus send set event */
FLUID_SEQ_TIMER, /**< Timer event (DOCME) */ FLUID_SEQ_TIMER, /**< Timer event (DOCME) */
FLUID_SEQ_ANYCONTROLCHANGE, /**< DOCME (used for remove_events only) */ FLUID_SEQ_ANYCONTROLCHANGE, /**< DOCME (used for remove_events only) */
FLUID_SEQ_CHANNELPRESSURE, /**< Channel aftertouch event @since 1.1.0 */
FLUID_SEQ_SYSTEMRESET, /**< System reset event @since 1.1.0 */
FLUID_SEQ_UNREGISTERING, /**< Called when a sequencer client is being unregistered. @since 1.1.0 */ FLUID_SEQ_UNREGISTERING, /**< Called when a sequencer client is being unregistered. @since 1.1.0 */
FLUID_SEQ_LASTEVENT /**< Defines the count of event enums */ FLUID_SEQ_LASTEVENT /**< Defines the count of event enums */
}; };
@ -71,7 +73,7 @@ FLUIDSYNTH_API void fluid_event_set_dest(fluid_event_t* evt, short dest);
FLUIDSYNTH_API void fluid_event_timer(fluid_event_t* evt, void* data); FLUIDSYNTH_API void fluid_event_timer(fluid_event_t* evt, void* data);
/* Note events */ /* Note events */
FLUIDSYNTH_API void fluid_event_note(fluid_event_t* evt, int channel, FLUIDSYNTH_API void fluid_event_nfluid_event_set_timeote(fluid_event_t* evt, int channel,
short key, short vel, short key, short vel,
unsigned int duration); unsigned int duration);
@ -99,6 +101,10 @@ FLUIDSYNTH_API void fluid_event_volume(fluid_event_t* evt, int channel, short va
FLUIDSYNTH_API void fluid_event_reverb_send(fluid_event_t* evt, int channel, short val); FLUIDSYNTH_API void fluid_event_reverb_send(fluid_event_t* evt, int channel, short val);
FLUIDSYNTH_API void fluid_event_chorus_send(fluid_event_t* evt, int channel, short val); FLUIDSYNTH_API void fluid_event_chorus_send(fluid_event_t* evt, int channel, short val);
FLUIDSYNTH_API void fluid_event_channel_pressure(fluid_event_t* evt, int channel, short val);
FLUIDSYNTH_API void fluid_event_system_reset(fluid_event_t* evt);
/* Only for removing events */ /* Only for removing events */
FLUIDSYNTH_API void fluid_event_any_control_change(fluid_event_t* evt, int channel); FLUIDSYNTH_API void fluid_event_any_control_change(fluid_event_t* evt, int channel);

View file

@ -35,6 +35,8 @@ extern "C" {
FLUIDSYNTH_API FLUIDSYNTH_API
short fluid_sequencer_register_fluidsynth(fluid_sequencer_t* seq, fluid_synth_t* synth); short fluid_sequencer_register_fluidsynth(fluid_sequencer_t* seq, fluid_synth_t* synth);
int
fluid_sequencer_add_midi_event_to_buffer(void* data, fluid_midi_event_t* event);
#ifdef __cplusplus #ifdef __cplusplus

View file

@ -107,6 +107,9 @@ FLUIDSYNTH_API int fluid_synth_get_pitch_wheel_sens(fluid_synth_t* synth, int ch
/** Send a program change message. Returns 0 if no error occurred, -1 otherwise. */ /** Send a program change message. Returns 0 if no error occurred, -1 otherwise. */
FLUIDSYNTH_API int fluid_synth_program_change(fluid_synth_t* synth, int chan, int program); FLUIDSYNTH_API int fluid_synth_program_change(fluid_synth_t* synth, int chan, int program);
/** Send a channel aftertouch message. Returns 0 if no error occurred, -1 otherwise. */
FLUIDSYNTH_API int fluid_synth_channel_pressure(fluid_synth_t* synth, int chan, int val);
/** Select a bank. Returns 0 if no error occurred, -1 otherwise. */ /** Select a bank. Returns 0 if no error occurred, -1 otherwise. */
FLUIDSYNTH_API FLUIDSYNTH_API
int fluid_synth_bank_select(fluid_synth_t* synth, int chan, unsigned int bank); int fluid_synth_bank_select(fluid_synth_t* synth, int chan, unsigned int bank);

View file

@ -38,6 +38,17 @@
/* Event alloc/free */ /* Event alloc/free */
void
fluid_event_clear(fluid_event_t* evt)
{
FLUID_MEMSET(evt, 0, sizeof(fluid_event_t));
// by default, no type
evt->dest = -1;
evt->src = -1;
evt->type = -1;
}
/** /**
* Create a new sequencer event structure. * Create a new sequencer event structure.
* @return New sequencer event structure or NULL if out of memory * @return New sequencer event structure or NULL if out of memory
@ -52,13 +63,7 @@ new_fluid_event()
fluid_log(FLUID_PANIC, "event: Out of memory\n"); fluid_log(FLUID_PANIC, "event: Out of memory\n");
return NULL; return NULL;
} }
fluid_event_clear(evt);
FLUID_MEMSET(evt, 0, sizeof(fluid_event_t));
// by default, no type
evt->dest = -1;
evt->src = -1;
evt->type = -1;
return(evt); return(evt);
} }
@ -402,6 +407,7 @@ fluid_event_chorus_send(fluid_event_t* evt, int channel, short val)
/** /**
* Set a sequencer event to be an unregistering event. * Set a sequencer event to be an unregistering event.
* @param evt Sequencer event structure * @param evt Sequencer event structure
* @since 1.1.0
*/ */
void void
fluid_event_unregistering(fluid_event_t* evt) fluid_event_unregistering(fluid_event_t* evt)
@ -409,6 +415,34 @@ fluid_event_unregistering(fluid_event_t* evt)
evt->type = FLUID_SEQ_UNREGISTERING; evt->type = FLUID_SEQ_UNREGISTERING;
} }
/**
* Set a sequencer event to be a channel-wide aftertouch event.
* @param evt Sequencer event structure
* @param channel MIDI channel number
* @param val Aftertouch amount (0-127)
* @since 1.1.0
*/
void
fluid_event_channel_pressure(fluid_event_t* evt, int channel, short val)
{
evt->type = FLUID_SEQ_CHANNELPRESSURE;
evt->channel = channel;
if (val < 0) val = 0;
if (val > 127) val = 127;
evt->value = val;
}
/**
* Set a sequencer event to be a midi system reset event.
* @param evt Sequencer event structure
* @since 1.1.0
*/
void
fluid_event_system_reset(fluid_event_t* evt)
{
evt->type = FLUID_SEQ_SYSTEMRESET;
}
/* /*

View file

@ -46,6 +46,8 @@ struct _fluid_event_t {
unsigned int fluid_event_get_time(fluid_event_t* evt); unsigned int fluid_event_get_time(fluid_event_t* evt);
void fluid_event_set_time(fluid_event_t* evt, unsigned int time); void fluid_event_set_time(fluid_event_t* evt, unsigned int time);
void fluid_event_clear(fluid_event_t* evt);
/* private data for sorter + heap */ /* private data for sorter + heap */
enum fluid_evt_entry_type { enum fluid_evt_entry_type {
FLUID_EVT_ENTRY_INSERT = 0, FLUID_EVT_ENTRY_INSERT = 0,

View file

@ -42,7 +42,7 @@
/* Private data for SEQUENCER */ /* Private data for SEQUENCER */
struct _fluid_sequencer_t { struct _fluid_sequencer_t {
unsigned int startMs; unsigned int startMs;
unsigned int currentMs; gint currentMs;
gboolean useSystemTimer; gboolean useSystemTimer;
double scale; // ticks per second double scale; // ticks per second
fluid_list_t* clients; fluid_list_t* clients;
@ -379,6 +379,7 @@ void fluid_sequencer_send_now(fluid_sequencer_t* seq, fluid_event_t* evt)
} }
} }
int int
fluid_sequencer_send_at(fluid_sequencer_t* seq, fluid_event_t* evt, unsigned int time, int absolute) fluid_sequencer_send_at(fluid_sequencer_t* seq, fluid_event_t* evt, unsigned int time, int absolute)
{ {
@ -392,16 +393,17 @@ fluid_sequencer_send_at(fluid_sequencer_t* seq, fluid_event_t* evt, unsigned int
fluid_event_set_time(evt, time); fluid_event_set_time(evt, time);
/* process late */ /* process late */
if (time < now) { /* Commented out for thread safety - send_at must go via the queue */
/* if (time < now) {
fluid_sequencer_send_now(seq, evt); fluid_sequencer_send_now(seq, evt);
return 0; return 0;
} }*/
/* process now */ /* process now */
if (time == now) { /* if (time == now) {
fluid_sequencer_send_now(seq, evt); fluid_sequencer_send_now(seq, evt);
return 0; return 0;
} }*/
/* queue for processing later */ /* queue for processing later */
return _fluid_seq_queue_pre_insert(seq, evt); return _fluid_seq_queue_pre_insert(seq, evt);
@ -419,7 +421,7 @@ fluid_sequencer_remove_events(fluid_sequencer_t* seq, short source, short dest,
**************************************/ **************************************/
unsigned int fluid_sequencer_get_tick(fluid_sequencer_t* seq) unsigned int fluid_sequencer_get_tick(fluid_sequencer_t* seq)
{ {
unsigned int absMs = seq->useSystemTimer ? fluid_curtime() : seq->currentMs; unsigned int absMs = seq->useSystemTimer ? (int) fluid_curtime() : g_atomic_int_get(&seq->currentMs);
double nowFloat; double nowFloat;
unsigned int now; unsigned int now;
nowFloat = ((double)(absMs - seq->startMs))*seq->scale/1000.0f; nowFloat = ((double)(absMs - seq->startMs))*seq->scale/1000.0f;
@ -763,7 +765,7 @@ fluid_sequencer_process(fluid_sequencer_t* seq, unsigned int msec)
} }
/* send queued events */ /* send queued events */
seq->currentMs = msec; g_atomic_int_set(&seq->currentMs, msec);
_fluid_seq_queue_send_queued_events(seq); _fluid_seq_queue_send_queued_events(seq);
} }

View file

@ -29,6 +29,8 @@
#include "fluidsynth_priv.h" #include "fluidsynth_priv.h"
#include "fluid_synth.h" #include "fluid_synth.h"
#include "fluid_midi.h"
#include "fluid_event_priv.h"
/*************************************************************** /***************************************************************
* *
@ -229,6 +231,18 @@ fluid_seq_fluidsynth_callback(unsigned int time, fluid_event_t* evt, fluid_seque
} }
break; break;
case FLUID_SEQ_CHANNELPRESSURE:
{
fluid_synth_channel_pressure(synth, fluid_event_get_channel(evt), fluid_event_get_value(evt));
}
break;
case FLUID_SEQ_SYSTEMRESET:
{
fluid_synth_system_reset(synth);
}
break;
case FLUID_SEQ_UNREGISTERING: /* free ourselves */ case FLUID_SEQ_UNREGISTERING: /* free ourselves */
{ {
seqbind->client_id = -1; /* avoid recursive call to fluid_sequencer_unregister_client */ seqbind->client_id = -1; /* avoid recursive call to fluid_sequencer_unregister_client */
@ -245,4 +259,70 @@ fluid_seq_fluidsynth_callback(unsigned int time, fluid_event_t* evt, fluid_seque
} }
} }
static int get_fluidsynth_dest(fluid_sequencer_t* seq)
{
int i, id;
char* name;
int j = fluid_sequencer_count_clients(seq);
for (i = 0; i < j; i++) {
id = fluid_sequencer_get_client_id(seq, i);
name = fluid_sequencer_get_client_name(seq, id);
if (strcmp(name, "fluidsynth") == 0) {
return id;
}
}
return -1;
}
/**
* Transforms an incoming midi event (from a midi driver or midi router) to a
* sequencer event and adds it to the sequencer queue for sending as soon as possible.
* @param data the sequencer, must be a valid fluid_sequencer_t
* @param event midi event
* @return FLUID_OK or FLUID_FAILED
* @since 1.1.0
*/
int
fluid_sequencer_add_midi_event_to_buffer(void* data, fluid_midi_event_t* event)
{
fluid_event_t evt;
fluid_sequencer_t* seq = (fluid_sequencer_t*) data;
int chan = fluid_midi_event_get_channel(event);
fluid_event_clear(&evt);
fluid_event_set_time(&evt, fluid_sequencer_get_tick(seq));
fluid_event_set_dest(&evt, get_fluidsynth_dest(seq));
switch (fluid_midi_event_get_type(event)) {
case NOTE_OFF:
fluid_event_noteoff(&evt, chan, fluid_midi_event_get_key(event));
break;
case NOTE_ON:
fluid_event_noteon(&evt, fluid_midi_event_get_channel(event),
fluid_midi_event_get_key(event), fluid_midi_event_get_velocity(event));
break;
case CONTROL_CHANGE:
fluid_event_control_change(&evt, chan, fluid_midi_event_get_control(event),
fluid_midi_event_get_value(event));
break;
case PROGRAM_CHANGE:
fluid_event_program_change(&evt, chan, fluid_midi_event_get_program(event));
break;
case PITCH_BEND:
fluid_event_pitch_bend(&evt, chan, fluid_midi_event_get_pitch(event));
break;
case CHANNEL_PRESSURE:
fluid_event_channel_pressure(&evt, chan, fluid_midi_event_get_program(event));
break;
case MIDI_SYSTEM_RESET:
fluid_event_system_reset(&evt);
break;
default: /* Not yet implemented */
return FLUID_FAILED;
}
/* Schedule for sending at next call to fluid_sequencer_process */
return fluid_sequencer_send_at(seq, &evt, 0, 0);
}