fluid_seq.c
上传用户:tjmskj2
上传日期:2020-08-17
资源大小:577k
文件大小:30k
- /* FluidSynth - A Software Synthesizer
- *
- * Copyright (C) 2003 Peter Hanappe and others.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Library General Public License
- * as published by the Free Software Foundation; either version 2 of
- * the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Library General Public License for more details.
- *
- * You should have received a copy of the GNU Library General Public
- * License along with this library; if not, write to the Free
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
- * 02111-1307, USA
- */
- /*
- 2002 : API design by Peter Hanappe and Antoine Schmitt
- August 2002 : Implementation by Antoine Schmitt as@gratin.org
- as part of the infiniteCD author project
- http://www.infiniteCD.org/
- */
- #include "fluid_event_priv.h"
- #include "fluidsynth_priv.h" // FLUID_NEW, etc
- #include "fluid_sys.h" // timer, threads, etc...
- #include "fluid_list.h"
- /***************************************************************
- *
- * SEQUENCER
- */
- #define FLUID_SEQUENCER_EVENTS_MAX 1000
- /* Private data for SEQUENCER */
- struct _fluid_sequencer_t {
- unsigned int startMs;
- gint currentMs;
- gboolean useSystemTimer;
- double scale; // ticks per second
- fluid_list_t* clients;
- short clientsID;
- /* for queue + heap */
- fluid_evt_entry* preQueue;
- fluid_evt_entry* preQueueLast;
- fluid_timer_t* timer;
- int queue0StartTime;
- short prevCellNb;
- fluid_evt_entry* queue0[256][2];
- fluid_evt_entry* queue1[255][2];
- fluid_evt_entry* queueLater;
- fluid_evt_heap_t* heap;
- fluid_mutex_t mutex;
- #if FLUID_SEQ_WITH_TRACE
- char *tracebuf;
- char *traceptr;
- int tracelen;
- #endif
- };
- /* Private data for clients */
- typedef struct _fluid_sequencer_client_t {
- short id;
- char* name;
- fluid_event_callback_t callback;
- void* data;
- } fluid_sequencer_client_t;
- /* prototypes */
- static short _fluid_seq_queue_init(fluid_sequencer_t* seq, int nbEvents);
- static void _fluid_seq_queue_end(fluid_sequencer_t* seq);
- static short _fluid_seq_queue_pre_insert(fluid_sequencer_t* seq, fluid_event_t * evt);
- static void _fluid_seq_queue_pre_remove(fluid_sequencer_t* seq, short src, short dest, int type);
- static int _fluid_seq_queue_process(void* data, unsigned int msec); // callback from timer
- static void _fluid_seq_queue_insert_entry(fluid_sequencer_t* seq, fluid_evt_entry * evtentry);
- static void _fluid_seq_queue_remove_entries_matching(fluid_sequencer_t* seq, fluid_evt_entry* temp);
- static void _fluid_seq_queue_send_queued_events(fluid_sequencer_t* seq);
- static void _fluid_free_evt_queue(fluid_evt_entry** first, fluid_evt_entry** last);
- /* API implementation */
- /**
- * Create a new sequencer object which uses the system timer. Use
- * new_fluid_sequencer2() to specify whether the system timer or
- * fluid_sequencer_process() is used to advance the sequencer.
- * @return New sequencer instance
- */
- fluid_sequencer_t*
- new_fluid_sequencer (void)
- {
- return new_fluid_sequencer2 (TRUE);
- }
- /**
- * Create a new sequencer object.
- * @param use_system_timer If TRUE, sequencer will advance at the rate of the
- * system clock. If FALSE, call fluid_sequencer_process() to advance
- * the sequencer.
- * @return New sequencer instance
- * @since 1.1.0
- */
- fluid_sequencer_t*
- new_fluid_sequencer2 (int use_system_timer)
- {
- fluid_sequencer_t* seq;
- seq = FLUID_NEW(fluid_sequencer_t);
- if (seq == NULL) {
- fluid_log(FLUID_PANIC, "sequencer: Out of memoryn");
- return NULL;
- }
- FLUID_MEMSET(seq, 0, sizeof(fluid_sequencer_t));
- seq->scale = 1000; // default value
- seq->useSystemTimer = use_system_timer ? TRUE : FALSE;
- seq->startMs = seq->useSystemTimer ? fluid_curtime() : 0;
- seq->clients = NULL;
- seq->clientsID = 0;
- if (-1 == _fluid_seq_queue_init(seq, FLUID_SEQUENCER_EVENTS_MAX)) {
- FLUID_FREE(seq);
- fluid_log(FLUID_PANIC, "sequencer: Out of memoryn");
- return NULL;
- }
- #if FLUID_SEQ_WITH_TRACE
- seq->tracelen = 1024*100;
- seq->tracebuf = (char *)FLUID_MALLOC(seq->tracelen);
- if (seq->tracebuf == NULL) {
- _fluid_seq_queue_end(seq);
- FLUID_FREE(seq);
- fluid_log(FLUID_PANIC, "sequencer: Out of memoryn");
- return NULL;
- }
- seq->traceptr = seq->tracebuf;
- #endif
- return(seq);
- }
- /**
- * Free a sequencer object.
- * @param seq Sequencer to delete
- */
- void
- delete_fluid_sequencer (fluid_sequencer_t* seq)
- {
- if (seq == NULL) {
- 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);
- /* if (seq->clients) {
- fluid_list_t *tmp = seq->clients;
- while (tmp != NULL) {
- fluid_sequencer_client_t *client = (fluid_sequencer_client_t*)tmp->data;
- if (client->name) FLUID_FREE(client->name);
- tmp = tmp->next;
- }
- delete_fluid_list(seq->clients);
- seq->clients = NULL;
- }*/
- #if FLUID_SEQ_WITH_TRACE
- if (seq->tracebuf != NULL)
- FLUID_FREE(seq->tracebuf);
- seq->tracebuf = NULL;
- #endif
- FLUID_FREE(seq);
- }
- /**
- * Check if a sequencer is using the system timer or not.
- * @param seq Sequencer object
- * @return TRUE if system timer is being used, FALSE otherwise.
- * @since 1.1.0
- */
- int
- fluid_sequencer_get_use_system_timer (fluid_sequencer_t* seq)
- {
- return seq->useSystemTimer ? 1 : 0;
- }
- #if FLUID_SEQ_WITH_TRACE
- /* trace */
- void
- fluid_seq_dotrace(fluid_sequencer_t* seq, char *fmt, ...)
- {
- va_list args;
- int len, remain = seq->tracelen - (seq->traceptr - seq->tracebuf);
- if (remain <= 0) return;
- va_start (args, fmt);
- len = vsnprintf(seq->traceptr, remain, fmt, args);
- va_end (args);
- if (len > 0) {
- if (len <= remain) {
- // all written, with 0 at end
- seq->traceptr += len;
- } else {
- // not enough room, set to end
- seq->traceptr = seq->tracebuf + seq->tracelen;
- }
- }
- return;
- }
- /**
- * Clear sequencer trace buffer.
- * @param seq Sequencer object
- */
- void
- fluid_seq_cleartrace(fluid_sequencer_t* seq)
- {
- seq->traceptr = seq->tracebuf;
- }
- /**
- * Get sequencer trace buffer.
- * @param seq Sequencer object
- */
- char *
- fluid_seq_gettrace(fluid_sequencer_t* seq)
- {
- return seq->tracebuf;
- }
- #else
- void fluid_seq_dotrace(fluid_sequencer_t* seq, char *fmt, ...) {}
- #endif // FLUID_SEQ_WITH_TRACE
- /* clients */
- /**
- * Register a sequencer client.
- * @param seq Sequencer object
- * @param name Name of sequencer client
- * @param callback Sequencer client callback or NULL for a source client.
- * @param data User data to pass to the a callback
- * @return Unique sequencer ID or #FLUID_FAILED on error
- *
- * Clients can be sources or destinations of events. Sources don't need to
- * register a callback.
- */
- short
- fluid_sequencer_register_client (fluid_sequencer_t* seq, const char *name,
- fluid_event_callback_t callback, void* data)
- {
- fluid_sequencer_client_t * client;
- char * nameCopy;
- client = FLUID_NEW(fluid_sequencer_client_t);
- if (client == NULL) {
- fluid_log(FLUID_PANIC, "sequencer: Out of memoryn");
- return FLUID_FAILED;
- }
- nameCopy = FLUID_STRDUP(name);
- if (nameCopy == NULL) {
- fluid_log(FLUID_PANIC, "sequencer: Out of memoryn");
- return FLUID_FAILED;
- }
- seq->clientsID++;
- client->name = nameCopy;
- client->id = seq->clientsID;
- client->callback = callback;
- client->data = data;
- seq->clients = fluid_list_append(seq->clients, (void *)client);
- return (client->id);
- }
- /**
- * Unregister a previously registered client.
- * @param seq Sequencer object
- * @param id Client ID as returned by fluid_sequencer_register_client().
- */
- 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);
- FLUID_FREE(client);
- delete_fluid_event(evt);
- return;
- }
- tmp = tmp->next;
- }
- delete_fluid_event(evt);
- return;
- }
- /**
- * Count a sequencers registered clients.
- * @param seq Sequencer object
- * @return Count of sequencer clients.
- */
- int
- fluid_sequencer_count_clients(fluid_sequencer_t* seq)
- {
- if (seq->clients == NULL)
- return 0;
- return fluid_list_size(seq->clients);
- }
- /**
- * Get a client ID from its index (order in which it was registered).
- * @param seq Sequencer object
- * @param index Index of register client
- * @return Client ID or #FLUID_FAILED if not found
- */
- short fluid_sequencer_get_client_id (fluid_sequencer_t* seq, int index)
- {
- fluid_list_t *tmp = fluid_list_nth(seq->clients, index);
- if (tmp == NULL) {
- return FLUID_FAILED;
- } else {
- fluid_sequencer_client_t *client = (fluid_sequencer_client_t*)tmp->data;
- return client->id;
- }
- }
- /**
- * Get the name of a registered client.
- * @param seq Sequencer object
- * @param id Client ID
- * @return Client name or NULL if not found. String is internal and should not
- * be modified or freed.
- */
- char *
- fluid_sequencer_get_client_name(fluid_sequencer_t* seq, int id)
- {
- fluid_list_t *tmp;
- if (seq->clients == NULL)
- return NULL;
- tmp = seq->clients;
- while (tmp) {
- fluid_sequencer_client_t *client = (fluid_sequencer_client_t*)tmp->data;
- if (client->id == id)
- return client->name;
- tmp = tmp->next;
- }
- return NULL;
- }
- /**
- * Check if a client is a destination client.
- * @param seq Sequencer object
- * @param id Client ID
- * @return TRUE if client is a destination client, FALSE otherwise or if not found
- */
- int
- fluid_sequencer_client_is_dest(fluid_sequencer_t* seq, int id)
- {
- fluid_list_t *tmp;
- if (seq->clients == NULL) return FALSE;
- tmp = seq->clients;
- while (tmp) {
- fluid_sequencer_client_t *client = (fluid_sequencer_client_t*)tmp->data;
- if (client->id == id)
- return (client->callback != NULL);
- tmp = tmp->next;
- }
- return FALSE;
- }
- /**
- * Send an event immediately.
- * @param seq Sequencer object
- * @param evt Event to send (copied)
- */
- /* Event not actually copied, but since its used immediately it virtually is. */
- void
- fluid_sequencer_send_now(fluid_sequencer_t* seq, fluid_event_t* evt)
- {
- short destID = fluid_event_get_dest(evt);
- /* find callback */
- fluid_list_t *tmp = seq->clients;
- while (tmp) {
- fluid_sequencer_client_t *dest = (fluid_sequencer_client_t*)tmp->data;
- if (dest->id == destID) {
- if (dest->callback)
- (dest->callback)(fluid_sequencer_get_tick(seq),
- evt, seq, dest->data);
- return;
- }
- tmp = tmp->next;
- }
- }
- /**
- * Schedule an event for sending at a later time.
- * @param seq Sequencer object
- * @param evt Event to send
- * @param time Time value in ticks (in milliseconds with the default time scale of 1000).
- * @param absolute TRUE if a time is absolute sequencer time (time since sequencer
- * creation), FALSE if relative to current time.
- * @return #FLUID_OK on success, #FLUID_FAILED otherwise
- */
- int
- fluid_sequencer_send_at (fluid_sequencer_t* seq, fluid_event_t* evt,
- unsigned int time, int absolute)
- {
- unsigned int now = fluid_sequencer_get_tick(seq);
- /* set absolute */
- if (!absolute)
- time = now + time;
- /* time stamp event */
- fluid_event_set_time(evt, time);
- /* queue for processing later */
- return _fluid_seq_queue_pre_insert(seq, evt);
- }
- /**
- * Remove events from the event queue.
- * @param seq Sequencer object
- * @param source Source client ID to match or -1 for wildcard
- * @param dest Destination client ID to match or -1 for wildcard
- * @param type Event type to match or -1 for wildcard (#fluid_seq_event_type)
- */
- void
- fluid_sequencer_remove_events (fluid_sequencer_t* seq, short source,
- short dest, int type)
- {
- _fluid_seq_queue_pre_remove(seq, source, dest, type);
- }
- /*************************************
- time
- **************************************/
- /**
- * Get the current tick of a sequencer.
- * @param seq Sequencer object
- * @return Current tick value
- */
- unsigned int
- fluid_sequencer_get_tick (fluid_sequencer_t* seq)
- {
- unsigned int absMs = seq->useSystemTimer ? (int) fluid_curtime() : g_atomic_int_get(&seq->currentMs);
- double nowFloat;
- unsigned int now;
- nowFloat = ((double)(absMs - seq->startMs))*seq->scale/1000.0f;
- now = nowFloat;
- return now;
- }
- /**
- * Set the time scale of a sequencer.
- * @param seq Sequencer object
- * @param scale Sequencer scale value in ticks per second
- * (default is 1000 for 1 tick per millisecond, max is 1000.0)
- *
- * If there are already scheduled events in the sequencer and the scale is changed
- * the events are adjusted accordingly.
- */
- void
- fluid_sequencer_set_time_scale (fluid_sequencer_t* seq, double scale)
- {
- if (scale <= 0) {
- fluid_log(FLUID_WARN, "sequencer: scale <= 0 : %fn", scale);
- return;
- }
- if (scale > 1000.0)
- // Otherwise : problems with the timer = 0ms...
- scale = 1000.0;
- if (seq->scale != scale) {
- double oldScale = seq->scale;
- // stop timer
- if (seq->timer) {
- delete_fluid_timer(seq->timer);
- seq->timer = NULL;
- }
- seq->scale = scale;
- // change start0 so that cellNb is preserved
- seq->queue0StartTime = (seq->queue0StartTime + seq->prevCellNb)*(seq->scale/oldScale) - seq->prevCellNb;
- // change all preQueue events for new scale
- {
- fluid_evt_entry* tmp;
- tmp = seq->preQueue;
- while (tmp) {
- if (tmp->entryType == FLUID_EVT_ENTRY_INSERT)
- tmp->evt.time = tmp->evt.time*seq->scale/oldScale;
- tmp = tmp->next;
- }
- }
- /* re-start timer */
- if (seq->useSystemTimer) {
- seq->timer = new_fluid_timer((int)(1000/seq->scale), _fluid_seq_queue_process, (void *)seq, TRUE, FALSE, TRUE);
- }
- }
- }
- /**
- * Get a sequencer's time scale.
- * @param seq Sequencer object.
- * @return Time scale value in ticks per second.
- */
- double
- fluid_sequencer_get_time_scale(fluid_sequencer_t* seq)
- {
- return seq->scale;
- }
- /**********************
- the queue
- **********************/
- /*
- The queue stores all future events to be processed.
- Data structures
- There is a heap, allocated at init time, for managing a pool
- of event entries, that is description of an event, its time,
- and whether it is a normal event or a removal command.
- The queue is separated in two arrays, and a list. The first
- array 'queue0' corresponds to the events to be sent in the
- next 256 ticks (0 to 255), the second array 'queue1' contains
- the events to be send from now+256 to now+65535. The list
- called 'queueLater' contains the events to be sent later than
- that. In each array, one cell contains a list of events having
- the same time (in the queue0 array), or the same time/256 (in
- the queue1 array), and a pointer to the last event in the list
- of the cell so as to be able to insert fast at the end of the
- list (i.e. a cell = 2 pointers). The 'queueLater' list is
- ordered by time and by post time. This way, inserting 'soon'
- events is fast (below 65535 ticks, that is about 1 minute if 1
- tick=1ms). Inserting later events is more slow, but this is a
- realtime engine, isn't it ?
- The queue0 starts at queue0StartTime. When 256 ticks have
- elapsed, the queue0 array is emptied, and the first cell of
- the queue1 array is expanded in the queue0 array, according to
- the time of each event. The queue1 array is shifted to the
- left, and the first events of the queueLater list are inserte
- in the last cell of the queue1 array.
- We remember the previously managed cell in queue0 in the
- prevCellNb variable. When processing the current cell, we
- process the events in between (late events).
- Functions
- The main thread functions first get an event entry from the
- heap, and copy the given event into it, then merely enqueue it
- in a preQueue. This is in order to protect the data structure:
- everything is managed in the callback (thread or interrupt,
- depending on the architecture).
- All queue data structure management is done in a timer
- callback: '_fluid_seq_queue_process'. The
- _fluid_seq_queue_process function first process the preQueue,
- inserting or removing event entrys from the queue, then
- processes the queue, by sending events ready to be sent at the
- current time.
- Critical sections between the main thread (or app) and the
- sequencer thread (or interrupt) are:
- - the heap management (if two threads get a free event at the
- same time)
- - the preQueue access.
- These are really small and fast sections (merely a pointer or
- two changing value). They are not protected by a mutex for now
- (August 2002). Waiting for crossplatform mutex solutions. When
- changing this code, beware that the
- _fluid_seq_queue_pre_insert function may be called by the
- callback of the queue thread (ex : a note event inserts a
- noteoff event).
- */
- /********************/
- /* API */
- /********************/
- static short
- _fluid_seq_queue_init(fluid_sequencer_t* seq, int maxEvents)
- {
- seq->heap = _fluid_evt_heap_init(maxEvents);
- if (seq->heap == NULL) {
- fluid_log(FLUID_PANIC, "sequencer: Out of memoryn");
- return -1;
- }
- seq->preQueue = NULL;
- seq->preQueueLast = NULL;
- FLUID_MEMSET(seq->queue0, 0, 2*256*sizeof(fluid_evt_entry *));
- FLUID_MEMSET(seq->queue1, 0, 2*255*sizeof(fluid_evt_entry *));
- seq->queueLater = NULL;
- seq->queue0StartTime = fluid_sequencer_get_tick(seq);
- seq->prevCellNb = -1;
- fluid_mutex_init(seq->mutex);
- /* start timer */
- if (seq->useSystemTimer) {
- seq->timer = new_fluid_timer((int)(1000/seq->scale), _fluid_seq_queue_process,
- (void *)seq, TRUE, FALSE, TRUE);
- }
- return (0);
- }
- static void
- _fluid_seq_queue_end(fluid_sequencer_t* seq)
- {
- int i;
- /* free all remaining events */
- _fluid_free_evt_queue(&seq->preQueue, &seq->preQueueLast);
- for (i = 0; i < 256; i++)
- _fluid_free_evt_queue(&(seq->queue0[i][0]), &(seq->queue0[i][1]));
- for (i = 0; i < 255; i++)
- _fluid_free_evt_queue(&(seq->queue1[i][0]), &(seq->queue1[i][1]));
- _fluid_free_evt_queue(&seq->queueLater, NULL);
- if (seq->timer) {
- delete_fluid_timer(seq->timer);
- seq->timer = NULL;
- }
- if (seq->heap) {
- _fluid_evt_heap_free(seq->heap);
- seq->heap = NULL;
- }
- fluid_mutex_destroy(seq->mutex);
- }
- /********************/
- /* queue management */
- /********************/
- /* Create event_entry and append to the preQueue.
- * May be called from the main thread (usually) but also recursively
- * from the queue thread, when a callback itself does an insert... */
- static short
- _fluid_seq_queue_pre_insert(fluid_sequencer_t* seq, fluid_event_t * evt)
- {
- fluid_evt_entry * evtentry = _fluid_seq_heap_get_free(seq->heap);
- if (evtentry == NULL) {
- /* should not happen */
- fluid_log(FLUID_PANIC, "sequencer: no more free eventsn");
- return -1;
- }
- evtentry->next = NULL;
- evtentry->entryType = FLUID_EVT_ENTRY_INSERT;
- FLUID_MEMCPY(&(evtentry->evt), evt, sizeof(fluid_event_t));
- fluid_mutex_lock(seq->mutex);
- /* append to preQueue */
- if (seq->preQueueLast) {
- seq->preQueueLast->next = evtentry;
- } else {
- seq->preQueue = evtentry;
- }
- seq->preQueueLast = evtentry;
- fluid_mutex_unlock(seq->mutex);
- return (0);
- }
- /* Create event_entry and append to the preQueue.
- * May be called from the main thread (usually) but also recursively
- * from the queue thread, when a callback itself does an insert... */
- static void
- _fluid_seq_queue_pre_remove(fluid_sequencer_t* seq, short src, short dest, int type)
- {
- fluid_evt_entry * evtentry = _fluid_seq_heap_get_free(seq->heap);
- if (evtentry == NULL) {
- /* should not happen */
- fluid_log(FLUID_PANIC, "sequencer: no more free eventsn");
- return;
- }
- evtentry->next = NULL;
- evtentry->entryType = FLUID_EVT_ENTRY_REMOVE;
- {
- fluid_event_t* evt = &(evtentry->evt);
- fluid_event_set_source(evt, src);
- fluid_event_set_source(evt, src);
- fluid_event_set_dest(evt, dest);
- evt->type = type;
- }
- fluid_mutex_lock(seq->mutex);
- /* append to preQueue */
- if (seq->preQueueLast) {
- seq->preQueueLast->next = evtentry;
- } else {
- seq->preQueue = evtentry;
- }
- seq->preQueueLast = evtentry;
- fluid_mutex_unlock(seq->mutex);
- return;
- }
- static void
- _fluid_free_evt_queue(fluid_evt_entry** first, fluid_evt_entry** last)
- {
- fluid_evt_entry* tmp2;
- fluid_evt_entry* tmp = *first;
- while (tmp != NULL) {
- tmp2 = tmp->next;
- FLUID_FREE(tmp);
- tmp = tmp2;
- }
- *first = NULL;
- if (last != NULL) {
- *last = NULL;
- }
- }
- /* Callback from timer (may be in a different thread, or in an interrupt) */
- static 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 a sequencer that isn't using the system timer.
- * @param seq Sequencer object
- * @param msec Time to advance sequencer to (absolute time since sequencer start).
- * @since 1.1.0
- */
- void
- fluid_sequencer_process(fluid_sequencer_t* seq, unsigned int msec)
- {
- /* process prequeue */
- fluid_evt_entry* tmp;
- fluid_evt_entry* next;
- fluid_mutex_lock(seq->mutex);
- /* get the preQueue */
- tmp = seq->preQueue;
- seq->preQueue = NULL;
- seq->preQueueLast = NULL;
- fluid_mutex_unlock(seq->mutex);
- /* walk all the preQueue and process them in order : inserts and removes */
- while (tmp) {
- next = tmp->next;
- if (tmp->entryType == FLUID_EVT_ENTRY_REMOVE) {
- _fluid_seq_queue_remove_entries_matching(seq, tmp);
- } else {
- _fluid_seq_queue_insert_entry(seq, tmp);
- }
- tmp = next;
- }
- /* send queued events */
- g_atomic_int_set(&seq->currentMs, msec);
- _fluid_seq_queue_send_queued_events(seq);
- }
- #if 0
- static void
- _fluid_seq_queue_print_later(fluid_sequencer_t* seq)
- {
- int count = 0;
- fluid_evt_entry* tmp = seq->queueLater;
- printf("queueLater:n");
- while (tmp) {
- unsigned int delay = tmp->evt.time - seq->queue0StartTime;
- printf("queueLater: Delay = %in", delay);
- tmp = tmp->next;
- count++;
- }
- printf("queueLater: Total of %i eventsn", count);
- }
- #endif
- static void
- _fluid_seq_queue_insert_queue0(fluid_sequencer_t* seq, fluid_evt_entry* tmp, int cell)
- {
- if (seq->queue0[cell][1] == NULL) {
- seq->queue0[cell][1] = seq->queue0[cell][0] = tmp;
- } else {
- seq->queue0[cell][1]->next = tmp;
- seq->queue0[cell][1] = tmp;
- }
- tmp->next = NULL;
- }
- static void
- _fluid_seq_queue_insert_queue1(fluid_sequencer_t* seq, fluid_evt_entry* tmp, int cell)
- {
- if (seq->queue1[cell][1] == NULL) {
- seq->queue1[cell][1] = seq->queue1[cell][0] = tmp;
- } else {
- seq->queue1[cell][1]->next = tmp;
- seq->queue1[cell][1] = tmp;
- }
- tmp->next = NULL;
- }
- static void
- _fluid_seq_queue_insert_queue_later(fluid_sequencer_t* seq, fluid_evt_entry* evtentry)
- {
- fluid_evt_entry* prev;
- fluid_evt_entry* tmp;
- unsigned int time = evtentry->evt.time;
- /* insert in 'queueLater', after the ones that have the same
- * time */
- /* first? */
- if ((seq->queueLater == NULL)
- || (seq->queueLater->evt.time > time)) {
- evtentry->next = seq->queueLater;
- seq->queueLater = evtentry;
- return;
- }
- /* walk queueLater */
- /* this is the only slow thing : if the event is more
- than 65535 ticks after the current time */
- prev = seq->queueLater;
- tmp = prev->next;
- while (tmp) {
- if (tmp->evt.time > time) {
- /* insert before tmp */
- evtentry->next = tmp;
- prev->next = evtentry;
- return;
- }
- prev = tmp;
- tmp = prev->next;
- }
- /* last */
- evtentry->next = NULL;
- prev->next = evtentry;
- }
- static void
- _fluid_seq_queue_insert_entry(fluid_sequencer_t* seq, fluid_evt_entry * evtentry)
- {
- /* time is relative to seq origin, in ticks */
- fluid_event_t * evt = &(evtentry->evt);
- unsigned int time = evt->time;
- unsigned int delay;
- if (seq->queue0StartTime > 0) {
- /* queue0StartTime could be < 0 if the scale changed a
- lot early, breaking the following comparison
- */
- if (time < (unsigned int)seq->queue0StartTime) {
- /* we are late, send now */
- fluid_sequencer_send_now(seq, evt);
- _fluid_seq_heap_set_free(seq->heap, evtentry);
- return;
- }
- }
- if (seq->prevCellNb >= 0) {
- /* prevCellNb could be -1 is seq was just started - unlikely */
- /* prevCellNb can also be -1 if cellNb was reset to 0 in
- _fluid_seq_queue_send_queued_events() */
- if (time <= (unsigned int)(seq->queue0StartTime + seq->prevCellNb)) {
- /* we are late, send now */
- fluid_sequencer_send_now(seq, evt);
- _fluid_seq_heap_set_free(seq->heap, evtentry);
- return;
- }
- }
- delay = time - seq->queue0StartTime;
- if (delay > 65535) {
- _fluid_seq_queue_insert_queue_later(seq, evtentry);
- } else if (delay > 255) {
- _fluid_seq_queue_insert_queue1(seq, evtentry, delay/256 - 1);
- } else {
- _fluid_seq_queue_insert_queue0(seq, evtentry, delay);
- }
- }
- static int
- _fluid_seq_queue_matchevent(fluid_event_t* evt, int templType, short templSrc, short templDest)
- {
- int eventType;
- if (templSrc != -1 && templSrc != fluid_event_get_source(evt))
- return 0;
- if (templDest != -1 && templDest != fluid_event_get_dest(evt))
- return 0;
- if (templType == -1)
- return 1;
- eventType = fluid_event_get_type(evt);
- if (templType == eventType)
- return 1;
- if (templType == FLUID_SEQ_ANYCONTROLCHANGE)
- if (eventType == FLUID_SEQ_PITCHBEND ||
- eventType == FLUID_SEQ_MODULATION ||
- eventType == FLUID_SEQ_SUSTAIN ||
- eventType == FLUID_SEQ_PAN ||
- eventType == FLUID_SEQ_VOLUME ||
- eventType == FLUID_SEQ_REVERBSEND ||
- eventType == FLUID_SEQ_CONTROLCHANGE ||
- eventType == FLUID_SEQ_CHORUSSEND)
- return 1;
- return 0;
- }
- static void
- _fluid_seq_queue_remove_entries_matching(fluid_sequencer_t* seq, fluid_evt_entry* templ)
- {
- /* we walk everything : this is slow, but that is life */
- int i, type;
- short src, dest;
- src = templ->evt.src;
- dest = templ->evt.dest;
- type = templ->evt.type;
- /* we can set it free now */
- _fluid_seq_heap_set_free(seq->heap, templ);
- /* queue0 */
- for (i = 0 ; i < 256 ; i++) {
- fluid_evt_entry* tmp = seq->queue0[i][0];
- fluid_evt_entry* prev = NULL;
- while (tmp) {
- /* remove and/or walk */
- if (_fluid_seq_queue_matchevent((&tmp->evt), type, src, dest)) {
- /* remove */
- if (prev) {
- prev->next = tmp->next;
- if (tmp == seq->queue0[i][1]) // last one in list
- seq->queue0[i][1] = prev;
- _fluid_seq_heap_set_free(seq->heap, tmp);
- tmp = prev->next;
- } else {
- /* first one in list */
- seq->queue0[i][0] = tmp->next;
- if (tmp == seq->queue0[i][1]) // last one in list
- seq->queue0[i][1] = NULL;
- _fluid_seq_heap_set_free(seq->heap, tmp);
- tmp = seq->queue0[i][0];
- }
- } else {
- prev = tmp;
- tmp = prev->next;
- }
- }
- }
- /* queue1 */
- for (i = 0 ; i < 255 ; i++) {
- fluid_evt_entry* tmp = seq->queue1[i][0];
- fluid_evt_entry* prev = NULL;
- while (tmp) {
- if (_fluid_seq_queue_matchevent((&tmp->evt), type, src, dest)) {
- /* remove */
- if (prev) {
- prev->next = tmp->next;
- if (tmp == seq->queue1[i][1]) // last one in list
- seq->queue1[i][1] = prev;
- _fluid_seq_heap_set_free(seq->heap, tmp);
- tmp = prev->next;
- } else {
- /* first one in list */
- seq->queue1[i][0] = tmp->next;
- if (tmp == seq->queue1[i][1]) // last one in list
- seq->queue1[i][1] = NULL;
- _fluid_seq_heap_set_free(seq->heap, tmp);
- tmp = seq->queue1[i][0];
- }
- } else {
- prev = tmp;
- tmp = prev->next;
- }
- }
- }
- /* queueLater */
- {
- fluid_evt_entry* tmp = seq->queueLater;
- fluid_evt_entry* prev = NULL;
- while (tmp) {
- if (_fluid_seq_queue_matchevent((&tmp->evt), type, src, dest)) {
- /* remove */
- if (prev) {
- prev->next = tmp->next;
- _fluid_seq_heap_set_free(seq->heap, tmp);
- tmp = prev->next;
- } else {
- seq->queueLater = tmp->next;
- _fluid_seq_heap_set_free(seq->heap, tmp);
- tmp = seq->queueLater;
- }
- } else {
- prev = tmp;
- tmp = prev->next;
- }
- }
- }
- }
- static void
- _fluid_seq_queue_send_cell_events(fluid_sequencer_t* seq, int cellNb)
- {
- fluid_evt_entry* next;
- fluid_evt_entry* tmp;
- tmp = seq->queue0[cellNb][0];
- while (tmp) {
- fluid_sequencer_send_now(seq, &(tmp->evt));
- next = tmp->next;
- _fluid_seq_heap_set_free(seq->heap, tmp);
- tmp = next;
- }
- seq->queue0[cellNb][0] = NULL;
- seq->queue0[cellNb][1] = NULL;
- }
- static void
- _fluid_seq_queue_slide(fluid_sequencer_t* seq)
- {
- short i;
- fluid_evt_entry* next;
- fluid_evt_entry* tmp;
- int count = 0;
- /* do the slide */
- seq->queue0StartTime += 256;
- /* sort all queue1[0] into queue0 according to new queue0StartTime */
- tmp = seq->queue1[0][0];
- while (tmp) {
- unsigned int delay = tmp->evt.time - seq->queue0StartTime;
- next = tmp->next;
- if (delay > 255) {
- /* should not happen !! */
- /* append it to queue1[1] */
- _fluid_seq_queue_insert_queue1(seq, tmp, 1);
- } else {
- _fluid_seq_queue_insert_queue0(seq, tmp, delay);
- }
- tmp = next;
- count++;
- }
- /* slide all queue1[i] into queue1[i-1] */
- for (i = 1 ; i < 255 ; i++) {
- seq->queue1[i-1][0] = seq->queue1[i][0];
- seq->queue1[i-1][1] = seq->queue1[i][1];
- }
- seq->queue1[254][0] = NULL;
- seq->queue1[254][1] = NULL;
- /* append queueLater to queue1[254] */
- count = 0;
- tmp = seq->queueLater;
- while (tmp) {
- unsigned int delay = tmp->evt.time - seq->queue0StartTime;
- if (delay > 65535) {
- break;
- }
- next = tmp->next;
- /* append it */
- _fluid_seq_queue_insert_queue1(seq, tmp, 254);
- tmp = next;
- count++;
- }
- seq->queueLater = tmp;
- }
- static void
- _fluid_seq_queue_send_queued_events(fluid_sequencer_t* seq)
- {
- unsigned int nowTicks = fluid_sequencer_get_tick(seq);
- short cellNb;
- cellNb = seq->prevCellNb + 1;
- while (cellNb <= (int)(nowTicks - seq->queue0StartTime)) {
- if (cellNb == 256) {
- cellNb = 0;
- _fluid_seq_queue_slide(seq);
- } /* slide */
- /* process queue0[cellNb] */
- _fluid_seq_queue_send_cell_events(seq, cellNb);
- /* next cell */
- cellNb++;
- }
- seq->prevCellNb = cellNb - 1;
- }