/** * 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 memory\n"); return FLUID_FAILED; } nameCopy = FLUID_STRDUP(name); if (nameCopy == NULL) { fluid_log(FLUID_PANIC, "sequencer: Out of memory\n"); FLUID_FREE(client); 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); }
fluid_evt_heap_t* _fluid_evt_heap_init(int nbEvents) { #ifdef HEAP_WITH_DYNALLOC int i; fluid_evt_heap_t* heap; fluid_evt_entry *tmp; heap = FLUID_NEW(fluid_evt_heap_t); if (heap == NULL) { fluid_log(FLUID_PANIC, "sequencer: Out of memory\n"); return NULL; } heap->freelist = NULL; fluid_mutex_init(heap->mutex); /* LOCK */ fluid_mutex_lock(heap->mutex); /* Allocate the event entries */ for (i = 0; i < nbEvents; i++) { tmp = FLUID_NEW(fluid_evt_entry); tmp->next = heap->freelist; heap->freelist = tmp; } /* UNLOCK */ fluid_mutex_unlock(heap->mutex); #else int i; fluid_evt_heap_t* heap; int siz = 2*sizeof(fluid_evt_entry *) + sizeof(fluid_evt_entry)*nbEvents; heap = (fluid_evt_heap_t *)FLUID_MALLOC(siz); if (heap == NULL) { fluid_log(FLUID_PANIC, "sequencer: Out of memory\n"); return NULL; } FLUID_MEMSET(heap, 0, siz); /* link all heap events */ { fluid_evt_entry *tmp = &(heap->pool); for (i = 0 ; i < nbEvents - 1 ; i++) tmp[i].next = &(tmp[i+1]); tmp[nbEvents-1].next = NULL; /* set head & tail */ heap->tail = &(tmp[nbEvents-1]); heap->head = &(heap->pool); } #endif return (heap); }
/* 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 events\n"); 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; }
/* 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 events\n"); 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); }
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 memory\n"); 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); }
void Chorus::update() { /* The modulating LFO goes through a full period every x samples: */ modulation_period_samples = lrint(sample_rate / speed_Hz); /* The variation in delay time is x: */ int modulation_depth_samples = (int) (depth_ms / 1000.0 /* convert modulation depth in ms to s*/ * sample_rate); if (modulation_depth_samples > MAX_SAMPLES) { fluid_log(FLUID_WARN, "chorus: Too high depth. Setting it to max (%d).", MAX_SAMPLES); modulation_depth_samples = MAX_SAMPLES; } /* initialize LFO table */ if (type == FLUID_CHORUS_MOD_SINE) sine(lookup_tab, modulation_period_samples, modulation_depth_samples); else if (type == FLUID_CHORUS_MOD_TRIANGLE) triangle(lookup_tab, modulation_period_samples, modulation_depth_samples); else { type = FLUID_CHORUS_MOD_SINE; sine(lookup_tab, modulation_period_samples, modulation_depth_samples); } for (int i = 0; i < number_blocks; i++) { /* Set the phase of the chorus blocks equally spaced */ phase[i] = (int) ((double) modulation_period_samples * (double) i / (double) number_blocks); } /* Start of the circular buffer */ counter = 0; }
/** * 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 memory\n"); 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 memory\n"); 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 memory\n"); return NULL; } seq->traceptr = seq->tracebuf; #endif return(seq); }
/** * Create a new sequencer event structure. * @return New sequencer event structure or NULL if out of memory */ fluid_event_t* new_fluid_event() { fluid_event_t* evt; evt = FLUID_NEW(fluid_event_t); if (evt == NULL) { fluid_log(FLUID_PANIC, "event: Out of memory\n"); return NULL; } fluid_event_clear(evt); return(evt); }
/** * Registers a synthesizer as a destination client of the given sequencer. * The \a synth is registered with the name "fluidsynth". * @param seq Sequencer instance * @param synth Synthesizer instance * @returns Sequencer client ID, or #FLUID_FAILED on error. */ short fluid_sequencer_register_fluidsynth (fluid_sequencer_t* seq, fluid_synth_t* synth) { fluid_seqbind_t* seqbind; seqbind = FLUID_NEW(fluid_seqbind_t); if (seqbind == NULL) { fluid_log(FLUID_PANIC, "sequencer: Out of memory\n"); return FLUID_FAILED; } seqbind->synth = synth; seqbind->seq = seq; seqbind->sample_timer = NULL; seqbind->client_id = -1; /* set up the sample timer */ if (!fluid_sequencer_get_use_system_timer(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 FLUID_FAILED; } } /* 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 FLUID_FAILED; } return seqbind->client_id; }
/** * 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 : %f\n", 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); } } }