Modify sequencer to (optionally) use sample timer instead of system timer

This commit is contained in:
David Henningsson 2009-05-03 11:32:44 +00:00
parent 95e985b4c9
commit 7993412330
5 changed files with 194 additions and 18 deletions

View file

@ -55,6 +55,7 @@ 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_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 */
}; };
@ -101,6 +102,9 @@ FLUIDSYNTH_API void fluid_event_chorus_send(fluid_event_t* evt, int channel, sho
/* 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);
/* Only when unregistering clients */
FLUIDSYNTH_API void fluid_event_unregistering(fluid_event_t* evt);
/* Accessing event data */ /* Accessing event data */
FLUIDSYNTH_API int fluid_event_get_type(fluid_event_t* evt); FLUIDSYNTH_API int fluid_event_get_type(fluid_event_t* evt);
FLUIDSYNTH_API short fluid_event_get_source(fluid_event_t* evt); FLUIDSYNTH_API short fluid_event_get_source(fluid_event_t* evt);

View file

@ -32,10 +32,14 @@ typedef void (*fluid_event_callback_t)(unsigned int time, fluid_event_t* event,
/** Allocate a new sequencer structure */ /** Allocate a new sequencer structure */
FLUIDSYNTH_API fluid_sequencer_t* new_fluid_sequencer(void); FLUIDSYNTH_API fluid_sequencer_t* new_fluid_sequencer(void);
FLUIDSYNTH_API fluid_sequencer_t* new_fluid_sequencer2(int useSystemTimer);
/** Free the sequencer structure */ /** Free the sequencer structure */
FLUIDSYNTH_API void delete_fluid_sequencer(fluid_sequencer_t* seq); 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 /** clients can be sources or destinations of events. These functions ensure a unique ID for any
source or dest, for filtering purposes. source or dest, for filtering purposes.
sources only dont need to register a callback. 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) */ /** Returns 1 if client is a destination (has a callback) */
FLUIDSYNTH_API int fluid_sequencer_client_is_dest(fluid_sequencer_t* seq, int id); 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. */ /** Sending an event immediately. */
FLUIDSYNTH_API void fluid_sequencer_send_now(fluid_sequencer_t* seq, fluid_event_t* evt); FLUIDSYNTH_API void fluid_sequencer_send_now(fluid_sequencer_t* seq, fluid_event_t* evt);

View file

@ -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 * Accessing event data
*/ */

View file

@ -42,6 +42,8 @@
/* 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;
gboolean useSystemTimer;
double scale; // ticks per second double scale; // ticks per second
fluid_list_t* clients; fluid_list_t* clients;
short clientsID; short clientsID;
@ -82,8 +84,26 @@ int _fluid_seq_queue_process(void* data, unsigned int msec); // callback from ti
/* API implementation */ /* API implementation */
/**
* Legacy call to new_fluid_sequencer to provide backwards compatibility.
* Please use new_fluid_sequencer2 instead.
* @deprecated
*/
fluid_sequencer_t* fluid_sequencer_t*
new_fluid_sequencer() 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; fluid_sequencer_t* seq;
@ -96,7 +116,8 @@ new_fluid_sequencer()
FLUID_MEMSET(seq, 0, sizeof(fluid_sequencer_t)); FLUID_MEMSET(seq, 0, sizeof(fluid_sequencer_t));
seq->scale = 1000; // default value 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->clients = NULL;
seq->clientsID = 0; seq->clientsID = 0;
@ -129,10 +150,15 @@ delete_fluid_sequencer(fluid_sequencer_t* seq)
return; 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); _fluid_seq_queue_end(seq);
/* cleanup clients */ /* if (seq->clients) {
if (seq->clients) {
fluid_list_t *tmp = seq->clients; fluid_list_t *tmp = seq->clients;
while (tmp != NULL) { while (tmp != NULL) {
fluid_sequencer_client_t *client = (fluid_sequencer_client_t*)tmp->data; 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); delete_fluid_list(seq->clients);
seq->clients = NULL; seq->clients = NULL;
} }*/
#if FLUID_SEQ_WITH_TRACE #if FLUID_SEQ_WITH_TRACE
if (seq->tracebuf != NULL) if (seq->tracebuf != NULL)
@ -152,6 +178,17 @@ delete_fluid_sequencer(fluid_sequencer_t* seq)
FLUID_FREE(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 #if FLUID_SEQ_WITH_TRACE
/* 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) void fluid_sequencer_unregister_client(fluid_sequencer_t* seq, short id)
{ {
fluid_list_t *tmp; fluid_list_t *tmp;
fluid_event_t* evt;
if (seq->clients == NULL) return; 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; tmp = seq->clients;
while (tmp) { while (tmp) {
fluid_sequencer_client_t *client = (fluid_sequencer_client_t*)tmp->data; fluid_sequencer_client_t *client = (fluid_sequencer_client_t*)tmp->data;
if (client->id == id) { 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) if (client->name)
FLUID_FREE(client->name); FLUID_FREE(client->name);
seq->clients = fluid_list_remove_link(seq->clients, tmp); seq->clients = fluid_list_remove_link(seq->clients, tmp);
delete1_fluid_list(tmp); delete1_fluid_list(tmp);
delete_fluid_event(evt);
return; return;
} }
tmp = tmp->next; tmp = tmp->next;
} }
delete_fluid_event(evt);
return; 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 fluid_sequencer_get_tick(fluid_sequencer_t* seq)
{ {
unsigned int absMs = fluid_curtime(); unsigned int absMs = seq->useSystemTimer ? fluid_curtime() : 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;
@ -414,7 +464,9 @@ void fluid_sequencer_set_time_scale(fluid_sequencer_t* seq, double scale)
} }
/* re-start timer */ /* 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); fluid_mutex_init(seq->mutex);
/* start timer */ /* start timer */
seq->timer = new_fluid_timer((int)(1000/seq->scale), _fluid_seq_queue_process, if (seq->useSystemTimer) {
(void *)seq, 1, 0); seq->timer = new_fluid_timer((int)(1000/seq->scale), _fluid_seq_queue_process,
(void *)seq, 1, 0);
}
return (0); return (0);
} }
@ -638,6 +692,19 @@ int
_fluid_seq_queue_process(void* data, unsigned int msec) _fluid_seq_queue_process(void* data, unsigned int msec)
{ {
fluid_sequencer_t* seq = (fluid_sequencer_t *)data; 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 */ /* process prequeue */
fluid_evt_entry* tmp; fluid_evt_entry* tmp;
@ -666,10 +733,9 @@ _fluid_seq_queue_process(void* data, unsigned int msec)
} }
/* send queued events */ /* send queued events */
seq->currentMs = msec;
_fluid_seq_queue_send_queued_events(seq); _fluid_seq_queue_send_queued_events(seq);
/* continue timer */
return 1;
} }

View file

@ -28,25 +28,107 @@
*/ */
#include "fluidsynth_priv.h" #include "fluidsynth_priv.h"
#include "fluid_synth.h"
/*************************************************************** /***************************************************************
* *
* SEQUENCER BINDING * 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); 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 */ if (seqbind == NULL) {
return fluid_sequencer_register_client(seq, "fluidsynth", fluid_seq_fluidsynth_callback, (void *)synth); 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)) { switch (fluid_event_get_type(evt)) {
@ -147,6 +229,13 @@ void fluid_seq_fluidsynth_callback(unsigned int time, fluid_event_t* evt, fluid_
} }
break; 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: case FLUID_SEQ_TIMER:
/* nothing in fluidsynth */ /* nothing in fluidsynth */
break; break;