mirror of
https://github.com/ZDoom/fluidsynth.git
synced 2025-02-23 12:21:39 +00:00
Modify sequencer to (optionally) use sample timer instead of system timer
This commit is contained in:
parent
95e985b4c9
commit
7993412330
5 changed files with 194 additions and 18 deletions
|
@ -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);
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
|
|
Loading…
Reference in a new issue