/* 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; }
/** * 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 */ fluid_atomic_int_set(&seq->currentMs, msec); _fluid_seq_queue_send_queued_events(seq); }
void _fluid_evt_heap_free(fluid_evt_heap_t* heap) { #ifdef HEAP_WITH_DYNALLOC fluid_evt_entry *tmp, *next; /* LOCK */ fluid_mutex_lock(heap->mutex); tmp = heap->freelist; while (tmp) { next = tmp->next; FLUID_FREE(tmp); tmp = next; } /* UNLOCK */ fluid_mutex_unlock(heap->mutex); fluid_mutex_destroy(heap->mutex); FLUID_FREE(heap); #else FLUID_FREE(heap); #endif }
/* 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); }
/** * Add a rule to a MIDI router. * @param router MIDI router * @param rule Rule to add (used directly and should not be accessed again following a * successful call to this function). * @param type The type of rule to add (#fluid_midi_router_rule_type) * @return #FLUID_OK on success, #FLUID_FAILED otherwise (invalid rule for example) * @since 1.1.0 */ int fluid_midi_router_add_rule (fluid_midi_router_t *router, fluid_midi_router_rule_t *rule, int type) { fluid_midi_router_rule_t *free_rules, *next_rule; fluid_return_val_if_fail (router != NULL, FLUID_FAILED); fluid_return_val_if_fail (rule != NULL, FLUID_FAILED); fluid_return_val_if_fail (type >= 0 && type < FLUID_MIDI_ROUTER_RULE_COUNT, FLUID_FAILED); fluid_mutex_lock (router->rules_mutex); /* ++ lock */ /* Take over free rules list, if any (to free outside of lock) */ free_rules = router->free_rules; router->free_rules = NULL; rule->next = router->rules[type]; router->rules[type] = rule; fluid_mutex_unlock (router->rules_mutex); /* -- unlock */ /* Free any deactivated rules which were waiting for events and are now done */ for (; free_rules; free_rules = next_rule) { next_rule = free_rules->next; FLUID_FREE (free_rules); } return FLUID_OK; }
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); }
/** * Clear all rules in a MIDI router. Such a router will drop all events until * rules are added. * @param router Router to clear all rules from * @return #FLUID_OK on success, #FLUID_FAILED otherwise * @since 1.1.0 */ int fluid_midi_router_clear_rules (fluid_midi_router_t *router) { fluid_midi_router_rule_t *del_rules[FLUID_MIDI_ROUTER_RULE_COUNT]; fluid_midi_router_rule_t *rule, *next_rule, *prev_rule; int i; fluid_return_val_if_fail (router != NULL, FLUID_FAILED); fluid_mutex_lock (router->rules_mutex); /* ++ lock */ for (i = 0; i < FLUID_MIDI_ROUTER_RULE_COUNT; i++) { del_rules[i] = NULL; prev_rule = NULL; /* Process existing rules */ for (rule = router->rules[i]; rule; rule = next_rule) { next_rule = rule->next; if (rule->pending_events == 0) /* Rule has no pending events? */ { /* Remove rule from rule list */ if (prev_rule) prev_rule->next = next_rule; else if (rule == router->rules[i]) router->rules[i] = next_rule; /* Prepend to delete list */ rule->next = del_rules[i]; del_rules[i] = rule; } else { rule->waiting = TRUE; /* Pending events, mark as waiting */ prev_rule = rule; } } } fluid_mutex_unlock (router->rules_mutex); /* -- unlock */ /* Free old rules outside of lock */ for (i = 0; i < FLUID_MIDI_ROUTER_RULE_COUNT; i++) { for (rule = del_rules[i]; rule; rule = next_rule) { next_rule = rule->next; FLUID_FREE (rule); } } return FLUID_OK; }
int fluid_samplecache_load(SFData *sf, unsigned int sample_start, unsigned int sample_end, int sample_type, int try_mlock, short **sample_data, char **sample_data24) { fluid_samplecache_entry_t *entry; int ret; fluid_mutex_lock(samplecache_mutex); entry = get_samplecache_entry(sf, sample_start, sample_end, sample_type); if (entry == NULL) { entry = new_samplecache_entry(sf, sample_start, sample_end, sample_type); if (entry == NULL) { ret = -1; goto unlock_exit; } samplecache_list = fluid_list_prepend(samplecache_list, entry); } if (try_mlock && !entry->mlocked) { /* Lock the memory to disable paging. It's okay if this fails. It * probably means that the user doesn't have the required permission. */ if (fluid_mlock(entry->sample_data, entry->sample_count * sizeof(short)) == 0) { if (entry->sample_data24 != NULL) { entry->mlocked = (fluid_mlock(entry->sample_data24, entry->sample_count) == 0); } else { entry->mlocked = TRUE; } if (!entry->mlocked) { fluid_munlock(entry->sample_data, entry->sample_count * sizeof(short)); FLUID_LOG(FLUID_WARN, "Failed to pin the sample data to RAM; swapping is possible."); } } } entry->num_references++; *sample_data = entry->sample_data; *sample_data24 = entry->sample_data24; ret = entry->sample_count; unlock_exit: fluid_mutex_unlock(samplecache_mutex); return ret; }
int fluid_samplecache_unload(const short *sample_data) { fluid_list_t *entry_list; fluid_samplecache_entry_t *entry; int ret; fluid_mutex_lock(samplecache_mutex); entry_list = samplecache_list; while (entry_list) { entry = (fluid_samplecache_entry_t *)fluid_list_get(entry_list); if (sample_data == entry->sample_data) { entry->num_references--; if (entry->num_references == 0) { if (entry->mlocked) { fluid_munlock(entry->sample_data, entry->sample_count * sizeof(short)); if (entry->sample_data24 != NULL) { fluid_munlock(entry->sample_data24, entry->sample_count); } } samplecache_list = fluid_list_remove(samplecache_list, entry); delete_samplecache_entry(entry); } ret = FLUID_OK; goto unlock_exit; } entry_list = fluid_list_next(entry_list); } FLUID_LOG(FLUID_ERR, "Trying to free sample data not found in cache."); ret = FLUID_FAILED; unlock_exit: fluid_mutex_unlock(samplecache_mutex); return ret; }
fluid_evt_entry* _fluid_seq_heap_get_free(fluid_evt_heap_t* heap) { #ifdef HEAP_WITH_DYNALLOC fluid_evt_entry* evt = NULL; /* LOCK */ fluid_mutex_lock(heap->mutex); #if !defined(MACOS9) if (heap->freelist == NULL) { heap->freelist = FLUID_NEW(fluid_evt_entry); if (heap->freelist != NULL) { heap->freelist->next = NULL; } } #endif evt = heap->freelist; if (evt != NULL) { heap->freelist = heap->freelist->next; evt->next = NULL; } /* UNLOCK */ fluid_mutex_unlock(heap->mutex); return evt; #else fluid_evt_entry* evt; if (heap->head == NULL) return NULL; /* take from head of the heap */ /* critical - should threadlock ? */ evt = heap->head; heap->head = heap->head->next; return evt; #endif }
void _fluid_seq_heap_set_free(fluid_evt_heap_t* heap, fluid_evt_entry* evt) { #ifdef HEAP_WITH_DYNALLOC /* LOCK */ fluid_mutex_lock(heap->mutex); evt->next = heap->freelist; heap->freelist = evt; /* UNLOCK */ fluid_mutex_unlock(heap->mutex); #else /* append to the end of the heap */ /* critical - should threadlock ? */ heap->tail->next = evt; heap->tail = evt; evt->next = NULL; #endif }
/** * Handle a MIDI event through a MIDI router instance. * @param data MIDI router instance #fluid_midi_router_t, its a void * so that * this function can be used as a callback for other subsystems * (new_fluid_midi_driver() for example). * @param event MIDI event to handle * @return #FLUID_OK on success, #FLUID_FAILED otherwise * * Purpose: The midi router is called for each event, that is received * via the 'physical' midi input. Each event can trigger an arbitrary number * of generated events (one for each rule that matches). * * In default mode, a noteon event is just forwarded to the synth's 'noteon' function, * a 'CC' event to the synth's 'CC' function and so on. * * The router can be used to: * - filter messages (for example: Pass sustain pedal CCs only to selected channels) * - split the keyboard (noteon with notenr < x: to ch 1, >x to ch 2) * - layer sounds (for each noteon received on ch 1, create a noteon on ch1, ch2, ch3,...) * - velocity scaling (for each noteon event, scale the velocity by 1.27 to give DX7 users * a chance) * - velocity switching ("v <=100: Angel Choir; V > 100: Hell's Bells") * - get rid of aftertouch * - ... */ int fluid_midi_router_handle_midi_event (void* data, fluid_midi_event_t* event) { fluid_midi_router_t* router = (fluid_midi_router_t *)data; fluid_midi_router_rule_t **rulep, *rule, *next_rule, *prev_rule = NULL; int event_has_par2 = 0; /* Flag, indicates that current event needs two parameters */ int par1_max = 127; /* Range limit for par1 */ int par2_max = 127; /* Range limit for par2 */ int ret_val = FLUID_OK; int chan; /* Channel of the generated event */ int par1; /* par1 of the generated event */ int par2; int event_par1; int event_par2; fluid_midi_event_t new_event; /* Some keyboards report noteoff through a noteon event with vel=0. * Convert those to noteoff to ease processing. */ if (event->type == NOTE_ON && event->param2 == 0) { event->type = NOTE_OFF; event->param2 = 127; /* Release velocity */ } fluid_mutex_lock (router->rules_mutex); /* ++ lock rules */ /* Depending on the event type, choose the correct list of rules. */ switch (event->type) { case NOTE_ON: rulep = &router->rules[FLUID_MIDI_ROUTER_RULE_NOTE]; event_has_par2 = 1; break; case NOTE_OFF: rulep = &router->rules[FLUID_MIDI_ROUTER_RULE_NOTE]; event_has_par2 = 1; break; case CONTROL_CHANGE: rulep = &router->rules[FLUID_MIDI_ROUTER_RULE_CC]; event_has_par2 = 1; break; case PROGRAM_CHANGE: rulep = &router->rules[FLUID_MIDI_ROUTER_RULE_PROG_CHANGE]; break; case PITCH_BEND: rulep = &router->rules[FLUID_MIDI_ROUTER_RULE_PITCH_BEND]; par1_max = 16383; break; case CHANNEL_PRESSURE: rulep = &router->rules[FLUID_MIDI_ROUTER_RULE_CHANNEL_PRESSURE]; break; case KEY_PRESSURE: rulep = &router->rules[FLUID_MIDI_ROUTER_RULE_KEY_PRESSURE]; event_has_par2 = 1; break; case MIDI_SYSTEM_RESET: case MIDI_SYSEX: ret_val = router->event_handler (router->event_handler_data,event); fluid_mutex_unlock (router->rules_mutex); /* -- unlock rules */ return ret_val; default: rulep = NULL; /* Event will not be passed on */ break; } /* Loop over rules in the list, looking for matches for this event. */ for (rule = rulep ? *rulep : NULL; rule; prev_rule = rule, rule = next_rule) { event_par1 = (int)event->param1; event_par2 = (int)event->param2; next_rule = rule->next; /* Rule may get removed from list, so get next here */ /* Channel window */ if (rule->chan_min > rule->chan_max) { /* Inverted rule: Exclude everything between max and min (but not min/max) */ if (event->channel > rule->chan_max && event->channel < rule->chan_min) continue; } else /* Normal rule: Exclude everything < max or > min (but not min/max) */ { if (event->channel > rule->chan_max || event->channel < rule->chan_min) continue; } /* Par 1 window */ if (rule->par1_min > rule->par1_max) { /* Inverted rule: Exclude everything between max and min (but not min/max) */ if (event_par1 > rule->par1_max && event_par1 < rule->par1_min) continue; } else /* Normal rule: Exclude everything < max or > min (but not min/max)*/ { if (event_par1 > rule->par1_max || event_par1 < rule->par1_min) continue; } /* Par 2 window (only applies to event types, which have 2 pars) * For noteoff events, velocity switching doesn't make any sense. * Velocity scaling might be useful, though. */ if (event_has_par2 && event->type != NOTE_OFF) { if (rule->par2_min > rule->par2_max) { /* Inverted rule: Exclude everything between max and min (but not min/max) */ if (event_par2 > rule->par2_max && event_par2 < rule->par2_min) continue; } else /* Normal rule: Exclude everything < max or > min (but not min/max)*/ { if (event_par2 > rule->par2_max || event_par2 < rule->par2_min) continue; } } /* Channel scaling / offset * Note: rule->chan_mul will probably be 0 or 1. If it's 0, input from all * input channels is mapped to the same synth channel. */ chan = (int)((fluid_real_t)event->channel * (fluid_real_t)rule->chan_mul + (fluid_real_t)rule->chan_add + 0.5); /* Par 1 scaling / offset */ par1 = (int)((fluid_real_t)event_par1 * (fluid_real_t)rule->par1_mul + (fluid_real_t)rule->par1_add + 0.5); /* Par 2 scaling / offset, if applicable */ if (event_has_par2) par2 = (int)((fluid_real_t)event_par2 * (fluid_real_t)rule->par2_mul + (fluid_real_t)rule->par2_add + 0.5); else par2 = 0; /* Channel range limiting */ if (chan < 0) chan = 0; else if (chan >= router->nr_midi_channels) chan = router->nr_midi_channels - 1; /* Par1 range limiting */ if (par1 < 0) par1 = 0; else if (par1 > par1_max) par1 = par1_max; /* Par2 range limiting */ if (event_has_par2) { if (par2 < 0) par2 = 0; else if (par2 > par2_max) par2 = par2_max; } /* At this point we have to create an event of event->type on 'chan' with par1 (maybe par2). * We keep track on the state of noteon and sustain pedal events. If the application tries * to delete a rule, it will only be fully removed, if pending noteoff / pedal off events have * arrived. In the meantime while waiting, it will only let through 'negative' events * (noteoff or pedal up). */ if (event->type == NOTE_ON || (event->type == CONTROL_CHANGE && par1 == SUSTAIN_SWITCH && par2 >= 64)) { /* Noteon or sustain pedal down event generated */ if (rule->keys_cc[par1] == 0) { rule->keys_cc[par1] = 1; rule->pending_events++; } } else if (event->type == NOTE_OFF || (event->type == CONTROL_CHANGE && par1 == SUSTAIN_SWITCH && par2 < 64)) { /* Noteoff or sustain pedal up event generated */ if (rule->keys_cc[par1] > 0) { rule->keys_cc[par1] = 0; rule->pending_events--; /* Rule is waiting for negative event to be destroyed? */ if (rule->waiting) { if (rule->pending_events == 0) { /* Remove rule from rule list */ if (prev_rule) prev_rule->next = next_rule; else *rulep = next_rule; /* Add to free list */ rule->next = router->free_rules; router->free_rules = rule; rule = prev_rule; /* Set rule to previous rule, which gets assigned to the next prev_rule value (in for() statement) */ } goto send_event; /* Pass the event to complete the cycle */ } } } /* Rule is still waiting for negative event? (note off or pedal up) */ if (rule->waiting) continue; /* Skip (rule is inactive except for matching negative event) */ send_event: /* At this point it is decided, what is sent to the synth. * Create a new event and make the appropriate call */ fluid_midi_event_set_type (&new_event, event->type); fluid_midi_event_set_channel (&new_event, chan); new_event.param1 = par1; new_event.param2 = par2; /* FIXME - What should be done on failure? For now continue to process events, but return failure to caller. */ if (router->event_handler (router->event_handler_data, &new_event) != FLUID_OK) ret_val = FLUID_FAILED; } fluid_mutex_unlock (router->rules_mutex); /* -- unlock rules */ return ret_val; }
/** * Set a MIDI router to use default "unity" rules. Such a router will pass all * events unmodified. * @param router Router to set to default rules. * @return #FLUID_OK on success, #FLUID_FAILED otherwise * @since 1.1.0 */ int fluid_midi_router_set_default_rules (fluid_midi_router_t *router) { fluid_midi_router_rule_t *new_rules[FLUID_MIDI_ROUTER_RULE_COUNT]; fluid_midi_router_rule_t *del_rules[FLUID_MIDI_ROUTER_RULE_COUNT]; fluid_midi_router_rule_t *rule, *next_rule, *prev_rule; int i, i2; fluid_return_val_if_fail (router != NULL, FLUID_FAILED); /* Allocate new default rules outside of lock */ for (i = 0; i < FLUID_MIDI_ROUTER_RULE_COUNT; i++) { new_rules[i] = new_fluid_midi_router_rule (); if (!new_rules[i]) { /* Free already allocated rules */ for (i2 = 0; i2 < i; i2++) delete_fluid_midi_router_rule (new_rules[i2]); return FLUID_FAILED; } } fluid_mutex_lock (router->rules_mutex); /* ++ lock */ for (i = 0; i < FLUID_MIDI_ROUTER_RULE_COUNT; i++) { del_rules[i] = NULL; prev_rule = NULL; /* Process existing rules */ for (rule = router->rules[i]; rule; rule = next_rule) { next_rule = rule->next; if (rule->pending_events == 0) /* Rule has no pending events? */ { /* Remove rule from rule list */ if (prev_rule) prev_rule->next = next_rule; else if (rule == router->rules[i]) router->rules[i] = next_rule; /* Prepend to delete list */ rule->next = del_rules[i]; del_rules[i] = rule; } else { rule->waiting = TRUE; /* Pending events, mark as waiting */ prev_rule = rule; } } /* Prepend new default rule */ new_rules[i]->next = router->rules[i]; router->rules[i] = new_rules[i]; } fluid_mutex_unlock (router->rules_mutex); /* -- unlock */ /* Free old rules outside of lock */ for (i = 0; i < FLUID_MIDI_ROUTER_RULE_COUNT; i++) { for (rule = del_rules[i]; rule; rule = next_rule) { next_rule = rule->next; FLUID_FREE (rule); } } return FLUID_OK; }