diff --git a/fluidsynth/include/fluidsynth/event.h b/fluidsynth/include/fluidsynth/event.h index 086a8780..db76d2ba 100644 --- a/fluidsynth/include/fluidsynth/event.h +++ b/fluidsynth/include/fluidsynth/event.h @@ -55,6 +55,7 @@ enum fluid_seq_event_type { FLUID_SEQ_CHORUSSEND, /**< Chorus send set event */ FLUID_SEQ_TIMER, /**< Timer event (DOCME) */ FLUID_SEQ_ANYCONTROLCHANGE, /**< DOCME (used for remove_events only) */ + FLUID_SEQ_UNREGISTERING, /**< Called when a sequencer client is being unregistered. @since 1.1.0 */ FLUID_SEQ_LASTEVENT /**< Defines the count of event enums */ }; @@ -101,6 +102,9 @@ FLUIDSYNTH_API void fluid_event_chorus_send(fluid_event_t* evt, int channel, sho /* Only for removing events */ FLUIDSYNTH_API void fluid_event_any_control_change(fluid_event_t* evt, int channel); +/* Only when unregistering clients */ +FLUIDSYNTH_API void fluid_event_unregistering(fluid_event_t* evt); + /* Accessing event data */ FLUIDSYNTH_API int fluid_event_get_type(fluid_event_t* evt); FLUIDSYNTH_API short fluid_event_get_source(fluid_event_t* evt); diff --git a/fluidsynth/include/fluidsynth/seq.h b/fluidsynth/include/fluidsynth/seq.h index 25ab0598..574cc456 100644 --- a/fluidsynth/include/fluidsynth/seq.h +++ b/fluidsynth/include/fluidsynth/seq.h @@ -32,10 +32,14 @@ typedef void (*fluid_event_callback_t)(unsigned int time, fluid_event_t* event, /** Allocate a new sequencer structure */ FLUIDSYNTH_API fluid_sequencer_t* new_fluid_sequencer(void); +FLUIDSYNTH_API fluid_sequencer_t* new_fluid_sequencer2(int useSystemTimer); /** Free the sequencer structure */ FLUIDSYNTH_API void delete_fluid_sequencer(fluid_sequencer_t* seq); + +FLUIDSYNTH_API int fluid_sequencer_get_useSystemTimer(fluid_sequencer_t* seq); + /** clients can be sources or destinations of events. These functions ensure a unique ID for any source or dest, for filtering purposes. sources only dont need to register a callback. @@ -61,7 +65,8 @@ FLUIDSYNTH_API char* fluid_sequencer_get_client_name(fluid_sequencer_t* seq, int /** Returns 1 if client is a destination (has a callback) */ FLUIDSYNTH_API int fluid_sequencer_client_is_dest(fluid_sequencer_t* seq, int id); - +/** Advance sequencer. Do not use if you created the sequencer with useSystemTimer enabled. */ +FLUIDSYNTH_API void fluid_sequencer_process(fluid_sequencer_t* seq, unsigned int msec); /** Sending an event immediately. */ FLUIDSYNTH_API void fluid_sequencer_send_now(fluid_sequencer_t* seq, fluid_event_t* evt); diff --git a/fluidsynth/src/fluid_event.c b/fluidsynth/src/fluid_event.c index aaa7465e..50c22922 100644 --- a/fluidsynth/src/fluid_event.c +++ b/fluidsynth/src/fluid_event.c @@ -399,6 +399,18 @@ fluid_event_chorus_send(fluid_event_t* evt, int channel, short val) } +/** + * Set a sequencer event to be an unregistering event. + * @param evt Sequencer event structure + */ +void +fluid_event_unregistering(fluid_event_t* evt) +{ + evt->type = FLUID_SEQ_UNREGISTERING; +} + + + /* * Accessing event data */ diff --git a/fluidsynth/src/fluid_seq.c b/fluidsynth/src/fluid_seq.c index 7a8dd858..3fe62902 100644 --- a/fluidsynth/src/fluid_seq.c +++ b/fluidsynth/src/fluid_seq.c @@ -42,6 +42,8 @@ /* Private data for SEQUENCER */ struct _fluid_sequencer_t { unsigned int startMs; + unsigned int currentMs; + gboolean useSystemTimer; double scale; // ticks per second fluid_list_t* clients; short clientsID; @@ -82,8 +84,26 @@ int _fluid_seq_queue_process(void* data, unsigned int msec); // callback from ti /* API implementation */ +/** + * Legacy call to new_fluid_sequencer to provide backwards compatibility. + * Please use new_fluid_sequencer2 instead. + * @deprecated + */ fluid_sequencer_t* new_fluid_sequencer() +{ + return new_fluid_sequencer2(1); +} + +/** + * Create a new sequencer object. + * @param useSystemTimer if this parameter is non-zero, sequencer will advance + * at the rate of the computer's system clock. If zero, call fluid_sequencer_process + * to advance the sequencer. + * @since 1.1.0 + */ +fluid_sequencer_t* +new_fluid_sequencer2(int useSystemTimer) { fluid_sequencer_t* seq; @@ -96,7 +116,8 @@ new_fluid_sequencer() FLUID_MEMSET(seq, 0, sizeof(fluid_sequencer_t)); seq->scale = 1000; // default value - seq->startMs = fluid_curtime(); + seq->useSystemTimer = useSystemTimer ? TRUE : FALSE; + seq->startMs = seq->useSystemTimer ? fluid_curtime() : 0; seq->clients = NULL; seq->clientsID = 0; @@ -129,10 +150,15 @@ delete_fluid_sequencer(fluid_sequencer_t* seq) return; } + /* cleanup clients */ + while (seq->clients) { + fluid_sequencer_client_t *client = (fluid_sequencer_client_t*)seq->clients->data; + fluid_sequencer_unregister_client(seq, client->id); + } + _fluid_seq_queue_end(seq); - /* cleanup clients */ - if (seq->clients) { +/* if (seq->clients) { fluid_list_t *tmp = seq->clients; while (tmp != NULL) { fluid_sequencer_client_t *client = (fluid_sequencer_client_t*)tmp->data; @@ -141,7 +167,7 @@ delete_fluid_sequencer(fluid_sequencer_t* seq) } delete_fluid_list(seq->clients); seq->clients = NULL; - } + }*/ #if FLUID_SEQ_WITH_TRACE if (seq->tracebuf != NULL) @@ -152,6 +178,17 @@ delete_fluid_sequencer(fluid_sequencer_t* seq) FLUID_FREE(seq); } +/** + * @return 1 if system timers are enabled, or 0 if system timers are disabled. + * @since 1.1.0 + */ +int +fluid_sequencer_get_useSystemTimer(fluid_sequencer_t* seq) +{ + return seq->useSystemTimer ? 1 : 0; +} + + #if FLUID_SEQ_WITH_TRACE /* trace */ @@ -232,22 +269,35 @@ short fluid_sequencer_register_client(fluid_sequencer_t* seq, char* name, void fluid_sequencer_unregister_client(fluid_sequencer_t* seq, short id) { fluid_list_t *tmp; + fluid_event_t* evt; if (seq->clients == NULL) return; + evt = new_fluid_event(); + if (evt != NULL) { + fluid_event_unregistering(evt); + fluid_event_set_dest(evt, id); + } + tmp = seq->clients; while (tmp) { fluid_sequencer_client_t *client = (fluid_sequencer_client_t*)tmp->data; if (client->id == id) { + /* What should we really do if evt is null due to out-of-memory? */ + if (client->callback != NULL && evt != NULL) + (client->callback)(fluid_sequencer_get_tick(seq), + evt, seq, client->data); if (client->name) FLUID_FREE(client->name); seq->clients = fluid_list_remove_link(seq->clients, tmp); delete1_fluid_list(tmp); + delete_fluid_event(evt); return; } tmp = tmp->next; } + delete_fluid_event(evt); return; } @@ -368,7 +418,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 absMs = fluid_curtime(); + unsigned int absMs = seq->useSystemTimer ? fluid_curtime() : seq->currentMs; double nowFloat; unsigned int now; nowFloat = ((double)(absMs - seq->startMs))*seq->scale/1000.0f; @@ -414,7 +464,9 @@ void fluid_sequencer_set_time_scale(fluid_sequencer_t* seq, double scale) } /* re-start timer */ - seq->timer = new_fluid_timer((int)(1000/seq->scale), _fluid_seq_queue_process, (void *)seq, 1, 0); + if (seq->useSystemTimer) { + seq->timer = new_fluid_timer((int)(1000/seq->scale), _fluid_seq_queue_process, (void *)seq, 1, 0); + } } } @@ -534,8 +586,10 @@ _fluid_seq_queue_init(fluid_sequencer_t* seq, int maxEvents) fluid_mutex_init(seq->mutex); /* start timer */ - seq->timer = new_fluid_timer((int)(1000/seq->scale), _fluid_seq_queue_process, - (void *)seq, 1, 0); + if (seq->useSystemTimer) { + seq->timer = new_fluid_timer((int)(1000/seq->scale), _fluid_seq_queue_process, + (void *)seq, 1, 0); + } return (0); } @@ -638,6 +692,19 @@ int _fluid_seq_queue_process(void* data, unsigned int msec) { fluid_sequencer_t* seq = (fluid_sequencer_t *)data; + fluid_sequencer_process(seq, msec); + /* continue timer */ + return 1; +} + +/** + * Advance sequencer. Do not use if you created the sequencer with useSystemTimer enabled. + * @param msec the number of milliseconds (compared to when the sequencer was created). + * @since 1.1.0 + */ +void +fluid_sequencer_process(fluid_sequencer_t* seq, unsigned int msec) +{ /* process prequeue */ fluid_evt_entry* tmp; @@ -666,10 +733,9 @@ _fluid_seq_queue_process(void* data, unsigned int msec) } /* send queued events */ + seq->currentMs = msec; _fluid_seq_queue_send_queued_events(seq); - /* continue timer */ - return 1; } diff --git a/fluidsynth/src/fluid_seqbind.c b/fluidsynth/src/fluid_seqbind.c index 3702d3f3..d19c5abd 100644 --- a/fluidsynth/src/fluid_seqbind.c +++ b/fluidsynth/src/fluid_seqbind.c @@ -28,25 +28,107 @@ */ #include "fluidsynth_priv.h" +#include "fluid_synth.h" /*************************************************************** * * SEQUENCER BINDING */ +struct _fluid_seqbind_t { + fluid_synth_t* synth; + fluid_sequencer_t* seq; + fluid_sample_timer_t* sample_timer; + short client_id; +}; +typedef struct _fluid_seqbind_t fluid_seqbind_t; + + +int fluid_seqbind_timer_callback(void* data, unsigned int msec); void fluid_seq_fluidsynth_callback(unsigned int time, fluid_event_t* event, fluid_sequencer_t* seq, void* data); -/* registering the synth */ -short fluid_sequencer_register_fluidsynth(fluid_sequencer_t* seq, fluid_synth_t* synth) +/** + * Proper cleanup of the seqbind struct. + */ +void +delete_fluid_seqbind(fluid_seqbind_t* seqbind) { - /* register fluidsynth itself */ - return fluid_sequencer_register_client(seq, "fluidsynth", fluid_seq_fluidsynth_callback, (void *)synth); + if (seqbind == NULL) { + return; + } + + if ((seqbind->client_id != -1) && (seqbind->seq != NULL)) { + fluid_sequencer_unregister_client(seqbind->seq, seqbind->client_id); + seqbind->client_id = -1; + } + + if ((seqbind->sample_timer != NULL) && (seqbind->synth != NULL)) { + delete_fluid_sample_timer(seqbind->synth, seqbind->sample_timer); + seqbind->sample_timer = NULL; + } + + FLUID_FREE(seqbind); } -/* the callback itself */ -void fluid_seq_fluidsynth_callback(unsigned int time, fluid_event_t* evt, fluid_sequencer_t* seq, void* data) +/** + * Registers fluidsynth as a client of the given sequencer. + * The fluidsynth is registered with the name "fluidsynth". + * @returns the fluidsynth destID in the sequencer, or -1 if function failed. + */ +short +fluid_sequencer_register_fluidsynth(fluid_sequencer_t* seq, fluid_synth_t* synth) { - fluid_synth_t* synth = (fluid_synth_t *)data; + fluid_seqbind_t* seqbind; + + seqbind = FLUID_NEW(fluid_seqbind_t); + if (seqbind == NULL) { + fluid_log(FLUID_PANIC, "sequencer: Out of memory\n"); + return -1; + } + + seqbind->synth = synth; + seqbind->seq = seq; + seqbind->sample_timer = NULL; + seqbind->client_id = -1; + + /* set up the sample timer */ + if (!fluid_sequencer_get_useSystemTimer(seq)) { + seqbind->sample_timer = + new_fluid_sample_timer(synth, fluid_seqbind_timer_callback, (void *) seqbind); + if (seqbind->sample_timer == NULL) { + fluid_log(FLUID_PANIC, "sequencer: Out of memory\n"); + delete_fluid_seqbind(seqbind); + return -1; + } + } + + /* register fluidsynth itself */ + seqbind->client_id = + fluid_sequencer_register_client(seq, "fluidsynth", fluid_seq_fluidsynth_callback, (void *)seqbind); + if (seqbind->client_id == -1) { + delete_fluid_seqbind(seqbind); + return -1; + } + + return seqbind->client_id; +} + +/* Callback for sample timer */ +int +fluid_seqbind_timer_callback(void* data, unsigned int msec) +{ + fluid_seqbind_t* seqbind = (fluid_seqbind_t *) data; + fluid_sequencer_process(seqbind->seq, msec); + return 1; +} + +/* Callback for midi events */ +void +fluid_seq_fluidsynth_callback(unsigned int time, fluid_event_t* evt, fluid_sequencer_t* seq, void* data) +{ + fluid_synth_t* synth; + fluid_seqbind_t* seqbind = (fluid_seqbind_t *) data; + synth = seqbind->synth; switch (fluid_event_get_type(evt)) { @@ -147,6 +229,13 @@ void fluid_seq_fluidsynth_callback(unsigned int time, fluid_event_t* evt, fluid_ } break; + case FLUID_SEQ_UNREGISTERING: /* free ourselves */ + { + seqbind->client_id = -1; /* avoid recursive call to fluid_sequencer_unregister_client */ + delete_fluid_seqbind(seqbind); + } + break; + case FLUID_SEQ_TIMER: /* nothing in fluidsynth */ break;