diff --git a/fluidsynth/src/fluid_chan.h b/fluidsynth/src/fluid_chan.h index 84f2493e..b050b3b7 100644 --- a/fluidsynth/src/fluid_chan.h +++ b/fluidsynth/src/fluid_chan.h @@ -55,21 +55,21 @@ struct _fluid_channel_t { fluid_mutex_t mutex; /* Lock for thread sensitive parameters */ - fluid_synth_t* synth; /**> Parent synthesizer instance */ - int channum; /**> MIDI channel number */ + fluid_synth_t* synth; /**< Parent synthesizer instance */ + int channum; /**< MIDI channel number */ - int sfont_bank_prog; /**> SoundFont ID (bit 21-31), bank (bit 7-20), program (bit 0-6) */ - fluid_preset_t* preset; /**> Selected preset */ + int sfont_bank_prog; /**< SoundFont ID (bit 21-31), bank (bit 7-20), program (bit 0-6) */ + fluid_preset_t* preset; /**< Selected preset */ - int key_pressure; /**> MIDI key pressure */ - int channel_pressure; /**> MIDI channel pressure */ - int pitch_bend; /**> Current pitch bend value */ - int pitch_wheel_sensitivity; /**> Current pitch wheel sensitivity */ + int key_pressure; /**< MIDI key pressure */ + int channel_pressure; /**< MIDI channel pressure */ + int pitch_bend; /**< Current pitch bend value */ + int pitch_wheel_sensitivity; /**< Current pitch wheel sensitivity */ - int cc[128]; /**> MIDI controller values */ + int cc[128]; /**< MIDI controller values */ - int interp_method; /**> Interpolation method (enum fluid_interp) */ - fluid_tuning_t* tuning; /**> Micro tuning */ + int interp_method; /**< Interpolation method (enum fluid_interp) */ + fluid_tuning_t* tuning; /**< Micro tuning */ /* NRPN system */ int nrpn_select; /* Generator ID of SoundFont NRPN message */ @@ -87,7 +87,7 @@ struct _fluid_channel_t * combined attack time of the sound font and the modulators. * * However, it is useful to be able to specify the generator value - * absolutely, completely ignoring the generators of the sound font + * absolutely, completely ignoring the generators of the SoundFont * and the values of modulators. The gen_abs field, is a boolean * flag indicating whether the NRPN value is absolute or not. */ diff --git a/fluidsynth/src/fluid_event_queue.h b/fluidsynth/src/fluid_event_queue.h index 464c7cee..a8efa3f3 100644 --- a/fluidsynth/src/fluid_event_queue.h +++ b/fluidsynth/src/fluid_event_queue.h @@ -37,7 +37,10 @@ enum fluid_event_queue_elem FLUID_EVENT_QUEUE_ELEM_STOP_VOICES, /**< Stop voices event. Uses ival field of event value */ FLUID_EVENT_QUEUE_ELEM_REVERB, /**< Reverb set or return event. Uses reverb field of event value */ FLUID_EVENT_QUEUE_ELEM_CHORUS, /**< Chorus set or return event. Uses chorus field of event value */ - FLUID_EVENT_QUEUE_ELEM_FREE_PRESET /**< Free a preset return event. Uses pval field of event value */ + FLUID_EVENT_QUEUE_ELEM_FREE_PRESET, /**< Free preset return event. Uses pval field of event value */ + FLUID_EVENT_QUEUE_ELEM_SET_TUNING, /**< Set tuning event. Uses set_tuning field of event value */ + FLUID_EVENT_QUEUE_ELEM_REPL_TUNING, /**< Replace tuning event. Uses repl_tuning field of event value */ + FLUID_EVENT_QUEUE_ELEM_UNREF_TUNING, /**< Unref tuning return event. Uses unref_tuning field of event value */ }; /** @@ -85,6 +88,35 @@ typedef struct float depth; } fluid_event_chorus_t; +/** + * Tuning assignment event structure. + */ +typedef struct +{ + char apply; /**< TRUE to set tuning in realtime */ + int channel; /**< MIDI channel number */ + fluid_tuning_t *tuning; /**< Tuning to assign */ +} fluid_event_set_tuning_t; + +/** + * Tuning replacement event structure. + */ +typedef struct +{ + char apply; /**< TRUE if tuning change should be applied in realtime */ + fluid_tuning_t *old_tuning; /**< Old tuning pointer to replace */ + fluid_tuning_t *new_tuning; /**< New tuning to assign */ +} fluid_event_repl_tuning_t; + +/** + * Tuning unref event structure. + */ +typedef struct +{ + fluid_tuning_t *tuning; /**< Tuning to unref */ + int count; /**< Number of times to unref */ +} fluid_event_unref_tuning_t; + /** * Event queue element structure. @@ -100,6 +132,9 @@ typedef struct fluid_event_preset_t preset; /**< If type == #FLUID_EVENT_QUEUE_ELEM_PRESET */ fluid_event_reverb_t reverb; /**< If type == #FLUID_EVENT_QUEUE_ELEM_REVERB */ fluid_event_chorus_t chorus; /**< If type == #FLUID_EVENT_QUEUE_ELEM_CHORUS */ + fluid_event_set_tuning_t set_tuning; /**< If type == #FLUID_EVENT_QUEUE_ELEM_SET_TUNING */ + fluid_event_repl_tuning_t repl_tuning; /**< If type == #FLUID_EVENT_QUEUE_ELEM_REPL_TUNING */ + fluid_event_unref_tuning_t unref_tuning; /**< If type == #FLUID_EVENT_QUEUE_ELEM_UNREF_TUNING */ double dval; /**< A floating point payload value */ int ival; /**< An integer payload value */ void *pval; /**< A pointer payload value */ diff --git a/fluidsynth/src/fluid_settings.c b/fluidsynth/src/fluid_settings.c index 709c854a..f2125d3d 100644 --- a/fluidsynth/src/fluid_settings.c +++ b/fluidsynth/src/fluid_settings.c @@ -39,7 +39,7 @@ static int fluid_settings_tokenize(char* s, char *buf, char** ptr); /* Common structure to all settings nodes */ typedef struct { - int type; /**> fluid_types_enum */ + int type; /**< fluid_types_enum */ } fluid_setting_node_t; typedef struct { diff --git a/fluidsynth/src/fluid_synth.c b/fluidsynth/src/fluid_synth.c index 7807ea1b..8d024d81 100644 --- a/fluidsynth/src/fluid_synth.c +++ b/fluidsynth/src/fluid_synth.c @@ -108,8 +108,17 @@ static void fluid_synth_release_voice_on_same_note_LOCAL(fluid_synth_t* synth, int chan, int key); static fluid_tuning_t* fluid_synth_get_tuning(fluid_synth_t* synth, int bank, int prog); -static fluid_tuning_t* fluid_synth_create_tuning(fluid_synth_t* synth, int bank, - int prog, char* name); +static int fluid_synth_replace_tuning_LOCK (fluid_synth_t* synth, + fluid_tuning_t *tuning, + int bank, int prog, int apply); +static void fluid_synth_replace_tuning_LOCAL (fluid_synth_t *synth, + fluid_tuning_t *old_tuning, + fluid_tuning_t *new_tuning, + int apply, int unref_new); +static void fluid_synth_update_voice_tuning_LOCAL (fluid_synth_t *synth, + fluid_channel_t *channel); +static int fluid_synth_set_tuning_LOCAL (fluid_synth_t *synth, int chan, + fluid_tuning_t *tuning, int apply); static void fluid_synth_set_gen_LOCAL (fluid_synth_t* synth, int chan, int param, float value, int absolute); static void fluid_synth_stop_LOCAL (fluid_synth_t *synth, unsigned int id); @@ -595,6 +604,7 @@ new_fluid_synth(fluid_settings_t *settings) synth->noteid = 0; synth->ticks = 0; synth->tuning = NULL; + fluid_private_init(synth->tuning_iter); /* allocate and add the default sfont loader */ loader = new_fluid_defsfloader(); @@ -917,6 +927,8 @@ delete_fluid_synth(fluid_synth_t* synth) FLUID_FREE(synth->tuning); } + fluid_private_free (synth->tuning_iter); + #ifdef LADSPA /* Release the LADSPA Fx unit */ fluid_LADSPA_shutdown(synth->LADSPA_FxUnit); @@ -936,8 +948,8 @@ delete_fluid_synth(fluid_synth_t* synth) for (i = 0; i < FLUID_MAX_EVENT_QUEUES; i++) if (synth->queues[i]) fluid_event_queue_free (synth->queues[i]); - delete_fluid_list (synth->queue_pool); + fluid_private_free (synth->thread_queues); fluid_mutex_destroy(synth->mutex); @@ -1020,6 +1032,29 @@ fluid_synth_get_event_queue (fluid_synth_t* synth) return queue; } +/* Get available event for sending to synthesis thread. Returns NULL on error. + * queue is an output parameter. */ +static fluid_event_queue_elem_t * +fluid_synth_get_event_elem (fluid_synth_t* synth, fluid_event_queue_t **queue) +{ + fluid_event_queue_t *q; + fluid_event_queue_elem_t *event; + + q = fluid_synth_get_event_queue (synth); + if (!q) return NULL; + + event = fluid_event_queue_get_inptr (q); + if (!event) + { + FLUID_LOG (FLUID_ERR, "Synthesis event queue full"); + return NULL; + } + + *queue = q; + + return event; +} + /** * Queues a MIDI event to the FluidSynth synthesis thread. * @param synth FluidSynth instance @@ -1036,15 +1071,8 @@ fluid_synth_queue_midi_event (fluid_synth_t* synth, int type, int chan, fluid_event_queue_t *queue; fluid_event_queue_elem_t *event; - queue = fluid_synth_get_event_queue (synth); - if (!queue) return FLUID_FAILED; - - event = fluid_event_queue_get_inptr (queue); - if (!event) - { - FLUID_LOG (FLUID_ERR, "Synthesis event queue full"); - return FLUID_FAILED; - } + event = fluid_synth_get_event_elem (synth, &queue); + if (!event) return FLUID_FAILED; event->type = FLUID_EVENT_QUEUE_ELEM_MIDI; event->midi.type = type; @@ -1073,15 +1101,8 @@ fluid_synth_queue_gen_event (fluid_synth_t* synth, int chan, fluid_event_queue_t *queue; fluid_event_queue_elem_t *event; - queue = fluid_synth_get_event_queue (synth); - if (!queue) return FLUID_FAILED; - - event = fluid_event_queue_get_inptr (queue); - if (!event) - { - FLUID_LOG (FLUID_ERR, "Synthesis event queue full"); - return FLUID_FAILED; - } + event = fluid_synth_get_event_elem (synth, &queue); + if (!event) return FLUID_FAILED; event->type = FLUID_EVENT_QUEUE_ELEM_GEN; event->gen.channel = chan; @@ -1107,15 +1128,8 @@ fluid_synth_queue_float_event (fluid_synth_t* synth, int type, float val) fluid_event_queue_t *queue; fluid_event_queue_elem_t *event; - queue = fluid_synth_get_event_queue (synth); - if (!queue) return FLUID_FAILED; - - event = fluid_event_queue_get_inptr (queue); - if (!event) - { - FLUID_LOG (FLUID_ERR, "Synthesis event queue full"); - return FLUID_FAILED; - } + event = fluid_synth_get_event_elem (synth, &queue); + if (!event) return FLUID_FAILED; event->type = type; event->dval = val; @@ -1138,15 +1152,8 @@ fluid_synth_queue_int_event (fluid_synth_t* synth, int type, int val) fluid_event_queue_t *queue; fluid_event_queue_elem_t *event; - queue = fluid_synth_get_event_queue (synth); - if (!queue) return FLUID_FAILED; - - event = fluid_event_queue_get_inptr (queue); - if (!event) - { - FLUID_LOG (FLUID_ERR, "Synthesis event queue full"); - return FLUID_FAILED; - } + event = fluid_synth_get_event_elem (synth, &queue); + if (!event) return FLUID_FAILED; event->type = type; event->ival = val; @@ -1749,15 +1756,8 @@ fluid_synth_set_preset (fluid_synth_t *synth, int chan, fluid_preset_t *preset) if (fluid_synth_should_queue (synth)) { - queue = fluid_synth_get_event_queue (synth); - if (!queue) return FLUID_FAILED; - - event = fluid_event_queue_get_inptr (queue); - if (!event) - { - FLUID_LOG (FLUID_ERR, "Synthesis event queue full"); - return FLUID_FAILED; - } + event = fluid_synth_get_event_elem (synth, &queue); + if (!event) return FLUID_FAILED; event->type = FLUID_EVENT_QUEUE_ELEM_PRESET; event->preset.channel = chan; @@ -2299,6 +2299,7 @@ fluid_synth_nwrite_float(fluid_synth_t* synth, int len, fluid_real_t** right_in = synth->right_buf; double time = fluid_utime(); int i, num, available, count, bytes; + float cpu_load; /* make sure we're playing */ if (synth->state != FLUID_SYNTH_PLAYING) @@ -2339,10 +2340,8 @@ fluid_synth_nwrite_float(fluid_synth_t* synth, int len, synth->cur = num; time = fluid_utime() - time; - synth->cpu_load = 0.5 * (synth->cpu_load + - time * synth->sample_rate / len / 10000.0); - -/* printf("CPU: %.2f\n", synth->cpu_load); */ + cpu_load = 0.5 * (synth->cpu_load + time * synth->sample_rate / len / 10000.0); + fluid_atomic_float_set (&synth->cpu_load, cpu_load); return FLUID_OK; } @@ -2414,6 +2413,7 @@ fluid_synth_write_float(fluid_synth_t* synth, int len, fluid_real_t* left_in = synth->left_buf[0]; fluid_real_t* right_in = synth->right_buf[0]; double time = fluid_utime(); + float cpu_load; /* make sure we're playing */ if (synth->state != FLUID_SYNTH_PLAYING) @@ -2435,10 +2435,8 @@ fluid_synth_write_float(fluid_synth_t* synth, int len, synth->cur = l; time = fluid_utime() - time; - synth->cpu_load = 0.5 * (synth->cpu_load + - time * synth->sample_rate / len / 10000.0); - -/* printf("CPU: %.2f\n", synth->cpu_load); */ + cpu_load = 0.5 * (synth->cpu_load + time * synth->sample_rate / len / 10000.0); + fluid_atomic_float_set (&synth->cpu_load, cpu_load); return FLUID_OK; } @@ -2511,6 +2509,7 @@ fluid_synth_write_s16(fluid_synth_t* synth, int len, double time = fluid_utime(); int di = synth->dither_index; double prof_ref_on_block; + float cpu_load; /* make sure we're playing */ if (synth->state != FLUID_SYNTH_PLAYING) { @@ -2552,12 +2551,9 @@ fluid_synth_write_s16(fluid_synth_t* synth, int len, fluid_profile(FLUID_PROF_WRITE_S16, prof_ref); - time = fluid_utime() - time; - synth->cpu_load = 0.5 * (synth->cpu_load + - time * synth->sample_rate / len / 10000.0); - -/* printf("CPU: %.2f\n", synth->cpu_load); */ + cpu_load = 0.5 * (synth->cpu_load + time * synth->sample_rate / len / 10000.0); + fluid_atomic_float_set (&synth->cpu_load, cpu_load); return 0; } @@ -2773,8 +2769,9 @@ fluid_synth_process_event_queue_LOCAL (fluid_synth_t *synth, while ((event = fluid_event_queue_get_outptr (queue))) { - if (event->type == FLUID_EVENT_QUEUE_ELEM_MIDI) + switch (event->type) { + case FLUID_EVENT_QUEUE_ELEM_MIDI: switch (event->midi.type) { case NOTE_ON: @@ -2810,27 +2807,44 @@ fluid_synth_process_event_queue_LOCAL (fluid_synth_t *synth, } break; } - } - else if (event->type == FLUID_EVENT_QUEUE_ELEM_GAIN) + break; + case FLUID_EVENT_QUEUE_ELEM_GAIN: fluid_synth_set_gain_LOCAL (synth, event->dval); - else if (event->type == FLUID_EVENT_QUEUE_ELEM_POLYPHONY) + break; + case FLUID_EVENT_QUEUE_ELEM_POLYPHONY: fluid_synth_set_polyphony_LOCAL (synth, event->ival); - else if (event->type == FLUID_EVENT_QUEUE_ELEM_GEN) + break; + case FLUID_EVENT_QUEUE_ELEM_GEN: fluid_synth_set_gen_LOCAL (synth, event->gen.channel, event->gen.param, event->gen.value, event->gen.absolute); - else if (event->type == FLUID_EVENT_QUEUE_ELEM_PRESET) + break; + case FLUID_EVENT_QUEUE_ELEM_PRESET: fluid_synth_set_preset_LOCAL (synth, event->preset.channel, event->preset.preset); - else if (event->type == FLUID_EVENT_QUEUE_ELEM_STOP_VOICES) + break; + case FLUID_EVENT_QUEUE_ELEM_STOP_VOICES: fluid_synth_stop_LOCAL (synth, event->ival); - else if (event->type == FLUID_EVENT_QUEUE_ELEM_REVERB) + break; + case FLUID_EVENT_QUEUE_ELEM_REVERB: fluid_synth_set_reverb_LOCAL (synth, event->reverb.set, event->reverb.roomsize, event->reverb.damping, event->reverb.width, event->reverb.level); - else if (event->type == FLUID_EVENT_QUEUE_ELEM_CHORUS) + break; + case FLUID_EVENT_QUEUE_ELEM_CHORUS: fluid_synth_set_chorus_LOCAL (synth, event->chorus.set, event->chorus.nr, event->chorus.type, event->chorus.level, event->chorus.speed, event->chorus.depth); + break; + case FLUID_EVENT_QUEUE_ELEM_SET_TUNING: + fluid_synth_set_tuning_LOCAL (synth, event->set_tuning.channel, + event->set_tuning.tuning, event->set_tuning.apply); + break; + case FLUID_EVENT_QUEUE_ELEM_REPL_TUNING: + fluid_synth_replace_tuning_LOCAL (synth, event->repl_tuning.old_tuning, + event->repl_tuning.new_tuning, + event->repl_tuning.apply, TRUE); + break; + } fluid_event_queue_next_outptr (queue); } @@ -3636,15 +3650,8 @@ fluid_synth_set_reverb_full(fluid_synth_t* synth, int set, double roomsize, if (fluid_synth_should_queue (synth)) { - queue = fluid_synth_get_event_queue (synth); - if (!queue) return FLUID_FAILED; - - event = fluid_event_queue_get_inptr (queue); - if (!event) - { - FLUID_LOG (FLUID_ERR, "Synthesis event queue full"); - return FLUID_FAILED; - } + event = fluid_synth_get_event_elem (synth, &queue); + if (!event) return FLUID_FAILED; event->type = FLUID_EVENT_QUEUE_ELEM_REVERB; event->reverb.set = set; @@ -3823,15 +3830,8 @@ fluid_synth_set_chorus_full(fluid_synth_t* synth, int set, int nr, double level, if (fluid_synth_should_queue (synth)) { - queue = fluid_synth_get_event_queue (synth); - if (!queue) return FLUID_FAILED; - - event = fluid_event_queue_get_inptr (queue); - if (!event) - { - FLUID_LOG (FLUID_ERR, "Synthesis event queue full"); - return FLUID_FAILED; - } + event = fluid_synth_get_event_elem (synth, &queue); + if (!event) return FLUID_FAILED; event->type = FLUID_EVENT_QUEUE_ELEM_CHORUS; event->chorus.set = set; @@ -4091,40 +4091,36 @@ fluid_synth_get_cpu_load(fluid_synth_t* synth) { fluid_return_val_if_fail (synth != NULL, 0); - return synth->cpu_load; + return fluid_atomic_float_get (&synth->cpu_load); } /* Get tuning for a given bank:program */ static fluid_tuning_t * fluid_synth_get_tuning(fluid_synth_t* synth, int bank, int prog) { - fluid_return_val_if_fail (synth != NULL, NULL); - fluid_return_val_if_fail (bank >= 0 && bank < 128, NULL); - fluid_return_val_if_fail (prog >= 0 && prog < 128, NULL); - if ((synth->tuning == NULL) || (synth->tuning[bank] == NULL) || - (synth->tuning[bank][prog] == NULL)) { - FLUID_LOG(FLUID_WARN, "No tuning at bank %d, prog %d", bank, prog); + (synth->tuning[bank][prog] == NULL)) return NULL; - } return synth->tuning[bank][prog]; } -/* Create tuning for a given bank:program */ -static fluid_tuning_t* -fluid_synth_create_tuning(fluid_synth_t* synth, int bank, int prog, char* name) +/* Replace tuning on a given bank:program (need not already exist). + * Synth mutex should already be locked by caller. */ +static int +fluid_synth_replace_tuning_LOCK (fluid_synth_t* synth, fluid_tuning_t *tuning, + int bank, int prog, int apply) { - fluid_return_val_if_fail (synth != NULL, NULL); - fluid_return_val_if_fail (bank >= 0 && bank < 128, NULL); - fluid_return_val_if_fail (prog >= 0 && prog < 128, NULL); + fluid_tuning_t *old_tuning; + fluid_event_queue_t *queue; + fluid_event_queue_elem_t *event; if (synth->tuning == NULL) { synth->tuning = FLUID_ARRAY(fluid_tuning_t**, 128); if (synth->tuning == NULL) { FLUID_LOG(FLUID_PANIC, "Out of memory"); - return NULL; + return FLUID_FAILED; } FLUID_MEMSET(synth->tuning, 0, 128 * sizeof(fluid_tuning_t**)); } @@ -4133,31 +4129,126 @@ fluid_synth_create_tuning(fluid_synth_t* synth, int bank, int prog, char* name) synth->tuning[bank] = FLUID_ARRAY(fluid_tuning_t*, 128); if (synth->tuning[bank] == NULL) { FLUID_LOG(FLUID_PANIC, "Out of memory"); - return NULL; + return FLUID_FAILED; } FLUID_MEMSET(synth->tuning[bank], 0, 128 * sizeof(fluid_tuning_t*)); } - if (synth->tuning[bank][prog] == NULL) { - synth->tuning[bank][prog] = new_fluid_tuning(name, bank, prog); - if (synth->tuning[bank][prog] == NULL) { - return NULL; + old_tuning = synth->tuning[bank][prog]; + synth->tuning[bank][prog] = tuning; + + if (old_tuning) { + if (!fluid_tuning_unref (old_tuning, 1)) /* -- unref old tuning */ + { /* Replace old tuning if present */ + if (fluid_synth_should_queue (synth)) + { + event = fluid_synth_get_event_elem (synth, &queue); + + if (event) + { + fluid_tuning_ref (tuning); /* ++ ref new tuning for event */ + + event->type = FLUID_EVENT_QUEUE_ELEM_REPL_TUNING; + event->repl_tuning.apply = apply; + event->repl_tuning.old_tuning = old_tuning; + event->repl_tuning.new_tuning = tuning; + fluid_event_queue_next_inptr (queue); + } + } + else fluid_synth_replace_tuning_LOCAL (synth, old_tuning, tuning, apply, FALSE); } } - if ((fluid_tuning_get_name(synth->tuning[bank][prog]) == NULL) - || (FLUID_STRCMP(fluid_tuning_get_name(synth->tuning[bank][prog]), name) != 0)) { - fluid_tuning_set_name(synth->tuning[bank][prog], name); + return FLUID_OK; +} + +/* Replace a tuning with a new one in all MIDI channels. new_tuning can be + * NULL, in which case channels are reset to default equal tempered scale. */ +static void +fluid_synth_replace_tuning_LOCAL (fluid_synth_t *synth, fluid_tuning_t *old_tuning, + fluid_tuning_t *new_tuning, int apply, int unref_new) +{ + fluid_event_queue_elem_t *event; + fluid_channel_t *channel; + int old_tuning_unref = 0; + int i; + + for (i = 0; i < synth->midi_channels; i++) + { + channel = synth->channel[i]; + + if (fluid_channel_get_tuning (channel) == old_tuning) + { + old_tuning_unref++; + if (new_tuning) fluid_tuning_ref (new_tuning); /* ++ ref new tuning for channel */ + fluid_channel_set_tuning (channel, new_tuning); + + if (apply) fluid_synth_update_voice_tuning_LOCAL (synth, channel); + } } - return synth->tuning[bank][prog]; + /* Send unref old tuning event if any unrefs */ + if (old_tuning_unref > 0) + { + event = fluid_event_queue_get_inptr (synth->return_queue); + + if (event) + { + event->type = FLUID_EVENT_QUEUE_ELEM_UNREF_TUNING; + event->unref_tuning.tuning = old_tuning; + event->unref_tuning.count = old_tuning_unref; + fluid_event_queue_next_inptr (synth->return_queue); + } + else + { /* Just unref it in synthesis thread if queue is full */ + fluid_tuning_unref (old_tuning, old_tuning_unref); + FLUID_LOG (FLUID_ERR, "Synth return event queue full"); + } + } + + if (!unref_new || !new_tuning) return; + + /* Send new tuning unref if requested (for replace queue event for example) */ + event = fluid_event_queue_get_inptr (synth->return_queue); + + if (event) + { + event->type = FLUID_EVENT_QUEUE_ELEM_UNREF_TUNING; + event->unref_tuning.tuning = new_tuning; + event->unref_tuning.count = 1; + fluid_event_queue_next_inptr (synth->return_queue); + } + else + { /* Just unref it in synthesis thread if queue is full */ + fluid_tuning_unref (new_tuning, 1); + FLUID_LOG (FLUID_ERR, "Synth return event queue full"); + } +} + +/* Update voice tunings in realtime */ +static void +fluid_synth_update_voice_tuning_LOCAL (fluid_synth_t *synth, fluid_channel_t *channel) +{ + fluid_voice_t *voice; + int i; + + for (i = 0; i < synth->polyphony; i++) + { + voice = synth->voice[i]; + + if (_ON (voice) && (voice->channel == channel)) + { + fluid_voice_calculate_gen_pitch (voice); + fluid_voice_update_param (voice, GEN_PITCH); + } + } } /** * Set the tuning of the entire MIDI note scale. * @param synth FluidSynth instance - * @param bank MIDI bank number for this tuning (0-16383) - * @param prog MIDI program number for this tuning (0-127) + * @param bank Tuning bank number (0-127), not related to MIDI instrument bank + * @param prog Tuning preset number (0-127), not related to MIDI instrument program * @param name Label name for this tuning * @param pitch Array of pitch values (length of 128, each value is number of * cents, for example normally note 0 is 0.0, 1 is 100.0, 60 is 6000.0, etc). @@ -4168,19 +4259,36 @@ int fluid_synth_create_key_tuning(fluid_synth_t* synth, int bank, int prog, char* name, double* pitch) { - fluid_tuning_t* tuning = fluid_synth_create_tuning(synth, bank, prog, name); + fluid_tuning_t* tuning; + int retval = FLUID_OK; - if (tuning == NULL) return FLUID_FAILED; - if (pitch) fluid_tuning_set_all(tuning, pitch); + fluid_return_val_if_fail (synth != NULL, FLUID_FAILED); + fluid_return_val_if_fail (bank >= 0 && bank < 128, FLUID_FAILED); + fluid_return_val_if_fail (prog >= 0 && prog < 128, FLUID_FAILED); + fluid_return_val_if_fail (name != NULL, FLUID_FAILED); - return FLUID_OK; + fluid_mutex_lock (synth->mutex); /* ++ Lock tunings */ + + tuning = new_fluid_tuning (name, bank, prog); + + if (tuning) + { + if (pitch) fluid_tuning_set_all (tuning, pitch); + retval = fluid_synth_replace_tuning_LOCK (synth, tuning, bank, prog, FALSE); + if (retval == FLUID_FAILED) fluid_tuning_unref (tuning, 1); + } + else retval = FLUID_FAILED; + + fluid_mutex_unlock (synth->mutex); /* -- Unlock tunings */ + + return retval; } /** * Apply an octave tuning to every octave in the MIDI note scale. * @param synth FluidSynth instance - * @param bank MIDI bank number for this tuning (0-16383) - * @param prog MIDI program number for this tuning (0-127) + * @param bank Tuning bank number (0-127), not related to MIDI instrument bank + * @param prog Tuning preset number (0-127), not related to MIDI instrument program * @param name Label name for this tuning * @param pitch Array of pitch values (length of 12 for each note of an octave * starting at note C, values are number of offset cents to add to the normal @@ -4191,19 +4299,37 @@ int fluid_synth_create_octave_tuning(fluid_synth_t* synth, int bank, int prog, char* name, double* pitch) { - fluid_tuning_t* tuning = fluid_synth_create_tuning(synth, bank, prog, name); + fluid_tuning_t* tuning; + int retval = FLUID_OK; - if (tuning == NULL) return FLUID_FAILED; - fluid_tuning_set_octave(tuning, pitch); + fluid_return_val_if_fail (synth != NULL, FLUID_FAILED); + fluid_return_val_if_fail (bank >= 0 && bank < 128, FLUID_FAILED); + fluid_return_val_if_fail (prog >= 0 && prog < 128, FLUID_FAILED); + fluid_return_val_if_fail (name != NULL, FLUID_FAILED); + fluid_return_val_if_fail (pitch != NULL, FLUID_FAILED); - return FLUID_OK; + fluid_mutex_lock (synth->mutex); /* ++ Lock tunings */ + + tuning = new_fluid_tuning (name, bank, prog); + + if (tuning) + { + fluid_tuning_set_octave (tuning, pitch); + retval = fluid_synth_replace_tuning_LOCK (synth, tuning, bank, prog, TRUE); + if (retval == FLUID_FAILED) fluid_tuning_unref (tuning, 1); + } + else retval = FLUID_FAILED; + + fluid_mutex_unlock (synth->mutex); /* -- Unlock tunings */ + + return retval; } /** * Set tuning values for one or more MIDI notes for an existing tuning. * @param synth FluidSynth instance - * @param bank MIDI bank number for this tuning (0-16383) - * @param prog MIDI program number for this tuning (0-127) + * @param bank Tuning bank number (0-127), not related to MIDI instrument bank + * @param prog Tuning preset number (0-127), not related to MIDI instrument program * @param len Number of MIDI notes to assign * @param key Array of MIDI key numbers (length of 'len', values 0-127) * @param pitch Array of pitch values (length of 'len', values are number of @@ -4211,40 +4337,155 @@ fluid_synth_create_octave_tuning(fluid_synth_t* synth, int bank, int prog, * @param apply Not used currently, may be used in the future to apply tuning * change in realtime. * @return FLUID_OK on success, FLUID_FAILED otherwise + * + * NOTE: Prior to version 1.1.0 it was an error to specify a tuning that didn't + * already exist. Starting with 1.1.0, the default equal tempered scale will be + * used as a basis, if no tuning exists for the given bank and prog. */ int fluid_synth_tune_notes(fluid_synth_t* synth, int bank, int prog, int len, int *key, double* pitch, int apply) { - fluid_tuning_t* tuning = fluid_synth_get_tuning(synth, bank, prog); + fluid_tuning_t* old_tuning, *new_tuning; + int retval = FLUID_OK; int i; - if (tuning == NULL) return FLUID_FAILED; + fluid_return_val_if_fail (synth != NULL, FLUID_FAILED); + fluid_return_val_if_fail (bank >= 0 && bank < 128, FLUID_FAILED); + fluid_return_val_if_fail (prog >= 0 && prog < 128, FLUID_FAILED); + fluid_return_val_if_fail (len > 0, FLUID_FAILED); + fluid_return_val_if_fail (key != NULL, FLUID_FAILED); + fluid_return_val_if_fail (pitch != NULL, FLUID_FAILED); - for (i = 0; i < len; i++) - fluid_tuning_set_pitch(tuning, key[i], pitch[i]); + fluid_mutex_lock (synth->mutex); /* ++ Lock tunings */ - return FLUID_OK; + old_tuning = fluid_synth_get_tuning (synth, bank, prog); + + if (old_tuning) + new_tuning = fluid_tuning_duplicate (old_tuning); + else new_tuning = new_fluid_tuning ("Unnamed", bank, prog); + + if (new_tuning) + { + for (i = 0; i < len; i++) + fluid_tuning_set_pitch (new_tuning, key[i], pitch[i]); + + retval = fluid_synth_replace_tuning_LOCK (synth, new_tuning, bank, prog, apply); + if (retval == FLUID_FAILED) fluid_tuning_unref (new_tuning, 1); + } + else retval = FLUID_FAILED; + + fluid_mutex_unlock (synth->mutex); /* -- Unlock tunings */ + + return retval; } /** - * Activate an existing tuning scale on a MIDI channel. + * Activate a tuning scale on a MIDI channel. * @param synth FluidSynth instance * @param chan MIDI channel number (0 to MIDI channel count - 1) - * @param bank MIDI bank number of tuning - * @param prog MIDI program number of tuning + * @param bank Tuning bank number (0-127), not related to MIDI instrument bank + * @param prog Tuning preset number (0-127), not related to MIDI instrument program * @return FLUID_OK on success, FLUID_FAILED otherwise + * + * NOTE: Prior to version 1.1.0 it was an error to select a tuning that didn't + * already exist. Starting with 1.1.0, the default equal tempered scale will be + * used, if no tuning exists for the given bank and prog. */ int fluid_synth_select_tuning(fluid_synth_t* synth, int chan, int bank, int prog) { - fluid_tuning_t* tuning = fluid_synth_get_tuning(synth, bank, prog); - - if (tuning == NULL) return FLUID_FAILED; + fluid_event_queue_elem_t *event; + fluid_event_queue_t *queue; + fluid_tuning_t* tuning; + int retval = FLUID_OK; + fluid_return_val_if_fail (synth != NULL, FLUID_FAILED); fluid_return_val_if_fail (chan >= 0 && chan < synth->midi_channels, FLUID_FAILED); + fluid_return_val_if_fail (bank >= 0 && bank < 128, FLUID_FAILED); + fluid_return_val_if_fail (prog >= 0 && prog < 128, FLUID_FAILED); - fluid_channel_set_tuning(synth->channel[chan], synth->tuning[bank][prog]); + fluid_mutex_lock (synth->mutex); /* ++ Lock tunings */ + + tuning = fluid_synth_get_tuning (synth, bank, prog); + + /* If no tuning exists, create a new default tuning. We do this, so that + * it can be replaced later, if any changes are made. */ + if (!tuning) + { + tuning = new_fluid_tuning ("Unnamed", bank, prog); + if (tuning) fluid_synth_replace_tuning_LOCK (synth, tuning, bank, prog, FALSE); + } + + if (tuning) fluid_tuning_ref (tuning); /* ++ ref for outside of lock */ + + fluid_mutex_unlock (synth->mutex); /* -- Unlock tunings */ + + if (!tuning) return (FLUID_FAILED); + + /* NOTE: tuning can be NULL, to use default equal tempered scale */ + + if (fluid_synth_should_queue (synth)) + { + event = fluid_synth_get_event_elem (synth, &queue); + + if (event) + { + fluid_tuning_ref (tuning); /* ++ ref new tuning for event */ + + event->type = FLUID_EVENT_QUEUE_ELEM_SET_TUNING; + event->set_tuning.apply = TRUE; + event->set_tuning.channel = chan; + event->set_tuning.tuning = tuning; + fluid_event_queue_next_inptr (queue); + } + else retval = FLUID_FAILED; + } + else + { + fluid_tuning_ref (tuning); /* ++ ref new tuning for following function */ + retval = fluid_synth_set_tuning_LOCAL (synth, chan, tuning, TRUE); + } + + fluid_tuning_unref (tuning, 1); /* -- unref for outside of lock */ + + return retval; +} + +/* Local synthesis thread set tuning function (takes over tuning reference) */ +static int +fluid_synth_set_tuning_LOCAL (fluid_synth_t *synth, int chan, + fluid_tuning_t *tuning, int apply) +{ + fluid_event_queue_elem_t *event; + fluid_tuning_t *old_tuning; + fluid_channel_t *channel; + + channel = synth->channel[chan]; + + old_tuning = fluid_channel_get_tuning (channel); + fluid_channel_set_tuning (channel, tuning); /* !! Takes over callers reference */ + + if (apply) fluid_synth_update_voice_tuning_LOCAL (synth, channel); + + /* Send unref old tuning event */ + if (old_tuning) + { + event = fluid_event_queue_get_inptr (synth->return_queue); + + if (event) + { + event->type = FLUID_EVENT_QUEUE_ELEM_UNREF_TUNING; + event->unref_tuning.tuning = old_tuning; + event->unref_tuning.count = 1; + fluid_event_queue_next_inptr (synth->return_queue); + } + else + { /* Just unref it in synthesis thread if queue is full */ + fluid_tuning_unref (old_tuning, 1); + FLUID_LOG (FLUID_ERR, "Synth return event queue full"); + } + } return FLUID_OK; } @@ -4258,12 +4499,30 @@ fluid_synth_select_tuning(fluid_synth_t* synth, int chan, int bank, int prog) int fluid_synth_reset_tuning(fluid_synth_t* synth, int chan) { + fluid_event_queue_elem_t *event; + fluid_event_queue_t *queue; + int retval = FLUID_OK; + fluid_return_val_if_fail (synth != NULL, FLUID_FAILED); fluid_return_val_if_fail (chan >= 0 && chan < synth->midi_channels, FLUID_FAILED); - fluid_channel_set_tuning(synth->channel[chan], NULL); + if (fluid_synth_should_queue (synth)) + { + event = fluid_synth_get_event_elem (synth, &queue); - return FLUID_OK; + if (event) + { + event->type = FLUID_EVENT_QUEUE_ELEM_SET_TUNING; + event->set_tuning.apply = TRUE; + event->set_tuning.channel = chan; + event->set_tuning.tuning = NULL; + fluid_event_queue_next_inptr (queue); + } + else retval = FLUID_FAILED; + } + else retval = fluid_synth_set_tuning_LOCAL (synth, chan, NULL, TRUE); + + return retval; } /** @@ -4275,7 +4534,7 @@ fluid_synth_tuning_iteration_start(fluid_synth_t* synth) { fluid_return_if_fail (synth != NULL); - synth->cur_tuning = NULL; + fluid_private_set (synth->tuning_iter, FLUID_INT_TO_POINTER (0), NULL); } /** @@ -4288,39 +4547,49 @@ fluid_synth_tuning_iteration_start(fluid_synth_t* synth) int fluid_synth_tuning_iteration_next(fluid_synth_t* synth, int* bank, int* prog) { + void *pval; int b = 0, p = 0; fluid_return_val_if_fail (synth != NULL, 0); fluid_return_val_if_fail (bank != NULL, 0); fluid_return_val_if_fail (prog != NULL, 0); - if (synth->tuning == NULL) return 0; + /* Current tuning iteration stored as: bank << 8 | program */ + pval = fluid_private_get (synth->tuning_iter); + p = FLUID_POINTER_TO_INT (pval); + b = (p >> 8) & 0xFF; + p &= 0xFF; - if (synth->cur_tuning != NULL) { - /* get the next program number */ - b = fluid_tuning_get_bank(synth->cur_tuning); - p = 1 + fluid_tuning_get_prog(synth->cur_tuning); - if (p >= 128) { - p = 0; - b++; + fluid_mutex_lock (synth->mutex); /* ++ lock tunings */ + + if (!synth->tuning) + { + fluid_mutex_unlock (synth->mutex); /* -- unlock tunings */ + return 0; + } + + for (; b < 128; b++, p = 0) + { + if (synth->tuning[b] == NULL) continue; + + for (; p < 128; p++) + { + if (synth->tuning[b][p] == NULL) continue; + + *bank = b; + *prog = p; + + if (p < 127) fluid_private_set (synth->tuning_iter, + FLUID_INT_TO_POINTER (b << 8 | (p + 1)), NULL); + else fluid_private_set (synth->tuning_iter, + FLUID_INT_TO_POINTER ((b + 1) << 8), NULL); + + fluid_mutex_unlock (synth->mutex); /* -- unlock tunings */ + return 1; } } - while (b < 128) { - if (synth->tuning[b] != NULL) { - while (p < 128) { - if (synth->tuning[b][p] != NULL) { - synth->cur_tuning = synth->tuning[b][p]; - *bank = b; - *prog = p; - return 1; - } - p++; - } - } - p = 0; - b++; - } + fluid_mutex_unlock (synth->mutex); /* -- unlock tunings */ return 0; } @@ -4333,25 +4602,33 @@ fluid_synth_tuning_iteration_next(fluid_synth_t* synth, int* bank, int* prog) * @param name Location to store tuning name or NULL to ignore * @param len Maximum number of chars to store to 'name' (including NULL byte) * @param pitch Array to store tuning scale to or NULL to ignore (len of 128) - * @return FLUID_OK on success, FLUID_FAILED otherwise + * @return FLUID_OK if matching tuning was found, FLUID_FAILED otherwise */ int fluid_synth_tuning_dump(fluid_synth_t* synth, int bank, int prog, char* name, int len, double* pitch) { - fluid_tuning_t* tuning = fluid_synth_get_tuning(synth, bank, prog); + fluid_tuning_t* tuning; - if (tuning == NULL) return FLUID_FAILED; + fluid_mutex_lock (synth->mutex); /* ++ lock tunings */ - if (name) { - snprintf(name, len - 1, "%s", fluid_tuning_get_name(tuning)); - name[len - 1] = 0; /* make sure the string is null terminated */ + tuning = fluid_synth_get_tuning (synth, bank, prog); + + if (tuning) + { + if (name) + { + snprintf (name, len - 1, "%s", fluid_tuning_get_name (tuning)); + name[len - 1] = 0; /* make sure the string is null terminated */ + } + + if (pitch) + FLUID_MEMCPY (pitch, fluid_tuning_get_all (tuning), 128 * sizeof (double)); } - if (pitch) - FLUID_MEMCPY(pitch, fluid_tuning_get_all(tuning), 128 * sizeof(double)); + fluid_mutex_unlock (synth->mutex); /* unlock tunings */ - return FLUID_OK; + return tuning ? FLUID_OK : FLUID_FAILED; } /** @@ -4486,7 +4763,7 @@ fluid_synth_set_gen(fluid_synth_t* synth, int chan, int param, float value) fluid_return_val_if_fail (param >= 0 && param < GEN_LAST, FLUID_FAILED); if (fluid_synth_should_queue (synth)) - return (fluid_synth_queue_gen_event (synth, chan, param, value, FALSE)); + return fluid_synth_queue_gen_event (synth, chan, param, value, FALSE); else fluid_synth_set_gen_LOCAL (synth, chan, param, value, FALSE); return FLUID_OK; diff --git a/fluidsynth/src/fluid_synth.h b/fluidsynth/src/fluid_synth.h index 48e53e0f..55f9dc47 100644 --- a/fluidsynth/src/fluid_synth.h +++ b/fluidsynth/src/fluid_synth.h @@ -82,10 +82,10 @@ enum fluid_synth_status * SoundFont with the SoundFont instance and additional fields. */ typedef struct _fluid_sfont_info_t { - fluid_sfont_t *sfont; /**> Loaded SoundFont */ - fluid_synth_t *synth; /**> Parent synth */ - int refcount; /**> SoundFont reference count (0 if no presets referencing it) */ - int bankofs; /**> Bank offset */ + fluid_sfont_t *sfont; /**< Loaded SoundFont */ + fluid_synth_t *synth; /**< Parent synth */ + int refcount; /**< SoundFont reference count (0 if no presets referencing it) */ + int bankofs; /**< Bank offset */ } fluid_sfont_info_t; /* @@ -119,18 +119,17 @@ typedef struct _fluid_sfont_info_t { * settings{} (has its own mutex) * sfont_info<> * tuning - * cur_tuning * sfont_id * gain * reverb_roomsize, reverb_damping, reverb_width, reverb_level * chorus_nr, chorus_level, chorus_speed, chorus_depth, chorus_type * - * Atomic int operations: + * Atomic operations: * ---------------------- * with_reverb * with_chorus * state - * + * cpu_load * noteid * storeid * outbuf @@ -143,51 +142,50 @@ typedef struct _fluid_sfont_info_t { * chorus{} * cur * dither_index - * cpu_load * polyphony (atomic int get for non-synth threads) * st_gain */ struct _fluid_synth_t { - fluid_thread_id_t synth_thread_id; /**> ID of the synthesis thread or FLUID_THREAD_ID_NULL if not yet set */ - fluid_private_t thread_queues; /**> Thread private data for event queues for each non-synthesis thread queuing events */ - fluid_event_queue_t *queues[FLUID_MAX_EVENT_QUEUES]; /**> Thread event queues (NULL for unused elements) */ + fluid_thread_id_t synth_thread_id; /**< ID of the synthesis thread or FLUID_THREAD_ID_NULL if not yet set */ + fluid_private_t thread_queues; /**< Thread private data for event queues for each non-synthesis thread queuing events */ + fluid_event_queue_t *queues[FLUID_MAX_EVENT_QUEUES]; /**< Thread event queues (NULL for unused elements) */ - fluid_mutex_t mutex; /**> Lock for multi-thread sensitive variables (not used by synthesis process) */ - fluid_list_t *queue_pool; /**> List of event queues whose threads have been destroyed and which can be re-used */ - fluid_event_queue_t *return_queue; /**> Event queue for events from synthesis thread to non-synthesis threads (memory frees, etc) */ - fluid_timer_t *return_queue_timer; /**> Timer thread to process return event queue */ + fluid_mutex_t mutex; /**< Lock for multi-thread sensitive variables (not used by synthesis process) */ + fluid_list_t *queue_pool; /**< List of event queues whose threads have been destroyed and which can be re-used */ + fluid_event_queue_t *return_queue; /**< Event queue for events from synthesis thread to non-synthesis threads (memory frees, etc) */ + fluid_timer_t *return_queue_timer; /**< Timer thread to process return event queue */ - fluid_settings_t* settings; /**> the synthesizer settings */ - int polyphony; /**> maximum polyphony */ - char with_reverb; /**> Should the synth use the built-in reverb unit? */ - char with_chorus; /**> Should the synth use the built-in chorus unit? */ - char verbose; /**> Turn verbose mode on? */ - char dump; /**> Dump events to stdout to hook up a user interface? */ - double sample_rate; /**> The sample rate */ - int midi_channels; /**> the number of MIDI channels (>= 16) */ - int audio_channels; /**> the number of audio channels (1 channel=left+right) */ - int audio_groups; /**> the number of (stereo) 'sub'groups from the synth. + fluid_settings_t* settings; /**< the synthesizer settings */ + int polyphony; /**< maximum polyphony */ + char with_reverb; /**< Should the synth use the built-in reverb unit? */ + char with_chorus; /**< Should the synth use the built-in chorus unit? */ + char verbose; /**< Turn verbose mode on? */ + char dump; /**< Dump events to stdout to hook up a user interface? */ + double sample_rate; /**< The sample rate */ + int midi_channels; /**< the number of MIDI channels (>= 16) */ + int audio_channels; /**< the number of audio channels (1 channel=left+right) */ + int audio_groups; /**< the number of (stereo) 'sub'groups from the synth. Typically equal to audio_channels. */ - int effects_channels; /**> the number of effects channels (>= 2) */ - int state; /**> the synthesizer state */ - unsigned int ticks; /**> the number of audio samples since the start */ - unsigned int start; /**> the start in msec, as returned by system clock */ + int effects_channels; /**< the number of effects channels (>= 2) */ + int state; /**< the synthesizer state */ + unsigned int ticks; /**< the number of audio samples since the start */ + unsigned int start; /**< the start in msec, as returned by system clock */ - fluid_list_t *loaders; /**> the SoundFont loaders */ - fluid_list_t *sfont_info; /**> List of fluid_sfont_info_t for each loaded SoundFont (remains until SoundFont is unloaded) */ - fluid_hashtable_t *sfont_hash; /**> Hash of fluid_sfont_t->fluid_sfont_info_t (remains until SoundFont is deleted) */ - unsigned int sfont_id; /**> Incrementing ID assigned to each loaded SoundFont */ + fluid_list_t *loaders; /**< the SoundFont loaders */ + fluid_list_t *sfont_info; /**< List of fluid_sfont_info_t for each loaded SoundFont (remains until SoundFont is unloaded) */ + fluid_hashtable_t *sfont_hash; /**< Hash of fluid_sfont_t->fluid_sfont_info_t (remains until SoundFont is deleted) */ + unsigned int sfont_id; /**< Incrementing ID assigned to each loaded SoundFont */ - double gain; /**> master gain */ - double st_gain; /**> Synth thread gain shadow value */ - fluid_channel_t** channel; /**> the channels */ - int nvoice; /**> the length of the synthesis process array (max polyphony allowed) */ - fluid_voice_t** voice; /**> the synthesis voices */ - unsigned int noteid; /**> the id is incremented for every new note. it's used for noteoff's */ + double gain; /**< master gain */ + double st_gain; /**< Synth thread gain shadow value */ + fluid_channel_t** channel; /**< the channels */ + int nvoice; /**< the length of the synthesis process array (max polyphony allowed) */ + fluid_voice_t** voice; /**< the synthesis voices */ + unsigned int noteid; /**< the id is incremented for every new note. it's used for noteoff's */ unsigned int storeid; - int nbuf; /**> How many audio buffers are used? (depends on nr of audio channels / groups)*/ + int nbuf; /**< How many audio buffers are used? (depends on nr of audio channels / groups)*/ fluid_real_t** left_buf; fluid_real_t** right_buf; @@ -197,31 +195,31 @@ struct _fluid_synth_t fluid_revmodel_t* reverb; fluid_chorus_t* chorus; - float reverb_roomsize; /**> Shadow of reverb roomsize */ - float reverb_damping; /**> Shadow of reverb damping */ - float reverb_width; /**> Shadow of reverb width */ - float reverb_level; /**> Shadow of reverb level */ + float reverb_roomsize; /**< Shadow of reverb roomsize */ + float reverb_damping; /**< Shadow of reverb damping */ + float reverb_width; /**< Shadow of reverb width */ + float reverb_level; /**< Shadow of reverb level */ - int chorus_nr; /**> Shadow of chorus number */ - float chorus_level; /**> Shadow of chorus level */ - float chorus_speed; /**> Shadow of chorus speed */ - float chorus_depth; /**> Shadow of chorus depth */ - int chorus_type; /**> Shadow of chorus type */ + int chorus_nr; /**< Shadow of chorus number */ + float chorus_level; /**< Shadow of chorus level */ + float chorus_speed; /**< Shadow of chorus speed */ + float chorus_depth; /**< Shadow of chorus depth */ + int chorus_type; /**< Shadow of chorus type */ - int cur; /**> the current sample in the audio buffers to be output */ - int dither_index; /**> current index in random dither value buffer: fluid_synth_(write_s16|dither_s16) */ + int cur; /**< the current sample in the audio buffers to be output */ + int dither_index; /**< current index in random dither value buffer: fluid_synth_(write_s16|dither_s16) */ - char outbuf[256]; /**> buffer for message output */ - double cpu_load; + char outbuf[256]; /**< buffer for message output */ + float cpu_load; /**< CPU load in percent (CPU time required / audio synthesized time * 100) */ - fluid_tuning_t*** tuning; /**> 128 banks of 128 programs for the tunings */ - fluid_tuning_t* cur_tuning; /**> current tuning in the iteration */ + fluid_tuning_t*** tuning; /**< 128 banks of 128 programs for the tunings */ + fluid_private_t tuning_iter; /**< Tuning iterators per each thread */ - fluid_midi_router_t* midi_router; /**> The midi router. Could be done nicer. */ - fluid_sample_timer_t* sample_timers; /**> List of timers triggered after a block has been processed */ + fluid_midi_router_t* midi_router; /**< The midi router. Could be done nicer. */ + fluid_sample_timer_t* sample_timers; /**< List of timers triggered after a block has been processed */ #ifdef LADSPA - fluid_LADSPA_FxUnit_t* LADSPA_FxUnit; /**> Effects unit for LADSPA support */ + fluid_LADSPA_FxUnit_t* LADSPA_FxUnit; /**< Effects unit for LADSPA support */ #endif }; diff --git a/fluidsynth/src/fluid_sys.h b/fluidsynth/src/fluid_sys.h index 853ef869..77f0eb65 100644 --- a/fluidsynth/src/fluid_sys.h +++ b/fluidsynth/src/fluid_sys.h @@ -60,6 +60,9 @@ void fluid_time_config(void); #define fluid_return_if_fail g_return_if_fail #define FLUID_INLINE inline #define FLUID_POINTER_TO_UINT GPOINTER_TO_UINT +#define FLUID_UINT_TO_POINTER GUINT_TO_POINTER +#define FLUID_POINTER_TO_INT GPOINTER_TO_INT +#define FLUID_INT_TO_POINTER GINT_TO_POINTER /* @@ -149,6 +152,24 @@ typedef GStaticRecMutex fluid_mutex_t; #define fluid_atomic_pointer_compare_and_exchange(_pp, _old, _new) \ g_atomic_pointer_compare_and_exchange(_pp, _old, _new) +static FLUID_INLINE void +fluid_atomic_float_set(volatile float *fptr, float val) +{ + sint32 ival; + memcpy (&ival, &val, 4); + fluid_atomic_int_set ((volatile int *)fptr, ival); +} + +static FLUID_INLINE float +fluid_atomic_float_get(volatile float *fptr) +{ + sint32 ival; + float fval; + ival = fluid_atomic_int_get ((volatile int *)fptr); + memcpy (&fval, &ival, 4); + return fval; +} + /* Thread private data */ diff --git a/fluidsynth/src/fluid_tuning.c b/fluidsynth/src/fluid_tuning.c index 7a12e228..bc9feb02 100644 --- a/fluidsynth/src/fluid_tuning.c +++ b/fluidsynth/src/fluid_tuning.c @@ -21,6 +21,7 @@ #include "fluid_tuning.h" #include "fluidsynth_priv.h" +#include "fluid_sys.h" fluid_tuning_t* new_fluid_tuning(char* name, int bank, int prog) @@ -47,18 +48,83 @@ fluid_tuning_t* new_fluid_tuning(char* name, int bank, int prog) tuning->pitch[i] = i * 100.0; } + tuning->refcount = 1; /* Start with a refcount of 1 */ + return tuning; } -void delete_fluid_tuning(fluid_tuning_t* tuning) +/* Duplicate a tuning */ +fluid_tuning_t * +fluid_tuning_duplicate (fluid_tuning_t *tuning) { - if (tuning == NULL) { - return; + fluid_tuning_t *new_tuning; + int i; + + new_tuning = FLUID_NEW (fluid_tuning_t); + + if (!new_tuning) { + FLUID_LOG (FLUID_PANIC, "Out of memory"); + return NULL; } - if (tuning->name != NULL) { - FLUID_FREE(tuning->name); + + if (tuning->name) + { + new_tuning->name = FLUID_STRDUP (tuning->name); + + if (!new_tuning->name) + { + FLUID_FREE (new_tuning); + FLUID_LOG (FLUID_PANIC, "Out of memory"); + return NULL; + } } - FLUID_FREE(tuning); + else new_tuning->name = NULL; + + new_tuning->bank = tuning->bank; + new_tuning->prog = tuning->prog; + + for (i = 0; i < 128; i++) + new_tuning->pitch[i] = tuning->pitch[i]; + + new_tuning->refcount = 1; /* Start with a refcount of 1 */ + + return new_tuning; +} + +void +delete_fluid_tuning (fluid_tuning_t *tuning) +{ + if (tuning->name) FLUID_FREE (tuning->name); + FLUID_FREE (tuning); +} + +/* Add a reference to a tuning object */ +void +fluid_tuning_ref (fluid_tuning_t *tuning) +{ + fluid_return_if_fail (tuning != NULL); + + fluid_atomic_int_inc (&tuning->refcount); +} + +/* Unref a tuning object, when it reaches 0 it is deleted, returns TRUE if deleted */ +int +fluid_tuning_unref (fluid_tuning_t *tuning, int count) +{ + fluid_return_val_if_fail (tuning != NULL, FALSE); + + /* Add and compare are separate, but that is OK, since refcount will only + * reach 0 when there are no references and therefore no possibility of + * another thread adding a reference in between */ + fluid_atomic_int_add (&tuning->refcount, -count); + + /* Delete when refcount reaches 0 */ + if (!fluid_atomic_int_get (&tuning->refcount)) + { + delete_fluid_tuning (tuning); + return TRUE; + } + else return FALSE; } void fluid_tuning_set_name(fluid_tuning_t* tuning, char* name) diff --git a/fluidsynth/src/fluid_tuning.h b/fluidsynth/src/fluid_tuning.h index 4693f4ec..0356e493 100644 --- a/fluidsynth/src/fluid_tuning.h +++ b/fluidsynth/src/fluid_tuning.h @@ -39,10 +39,14 @@ struct _fluid_tuning_t { int bank; int prog; double pitch[128]; /* the pitch of every key, in cents */ + int refcount; /* Tuning reference count */ }; fluid_tuning_t* new_fluid_tuning(char* name, int bank, int prog); -void delete_fluid_tuning(fluid_tuning_t* tuning); +void delete_fluid_tuning (fluid_tuning_t *tuning); +fluid_tuning_t *fluid_tuning_duplicate (fluid_tuning_t *tuning); +void fluid_tuning_ref (fluid_tuning_t *tuning); +int fluid_tuning_unref (fluid_tuning_t *tuning, int count); void fluid_tuning_set_name(fluid_tuning_t* tuning, char* name); char* fluid_tuning_get_name(fluid_tuning_t* tuning); diff --git a/fluidsynth/src/fluid_voice.c b/fluidsynth/src/fluid_voice.c index f789a816..ff60eaeb 100644 --- a/fluidsynth/src/fluid_voice.c +++ b/fluidsynth/src/fluid_voice.c @@ -813,9 +813,10 @@ void fluid_voice_start(fluid_voice_t* voice) voice->status = FLUID_VOICE_ON; } -static void +void fluid_voice_calculate_gen_pitch(fluid_voice_t* voice) { + fluid_tuning_t* tuning; fluid_real_t x; /* The GEN_PITCH is a hack to fit the pitch bend controller into the @@ -826,17 +827,14 @@ fluid_voice_calculate_gen_pitch(fluid_voice_t* voice) * one key remains fixed. Here C3 (MIDI number 60) is used. */ if (fluid_channel_has_tuning(voice->channel)) { - /* pitch(scalekey) + scale * (pitch(key) - pitch(scalekey)) */ - #define __pitch(_k) fluid_tuning_get_pitch(tuning, _k) - fluid_tuning_t* tuning = fluid_channel_get_tuning(voice->channel); - x = __pitch((int) (voice->root_pitch / 100.0f)); - voice->gen[GEN_PITCH].val = (x + (voice->gen[GEN_SCALETUNE].val / 100.0f * - (__pitch(voice->key) - x))); + tuning = fluid_channel_get_tuning (voice->channel); + x = fluid_tuning_get_pitch (tuning, (int)(voice->root_pitch / 100.0f)); + voice->gen[GEN_PITCH].val = voice->gen[GEN_SCALETUNE].val / 100.0f * + (fluid_tuning_get_pitch (tuning, voice->key) - x) + x; } else { - voice->gen[GEN_PITCH].val = (voice->gen[GEN_SCALETUNE].val * (voice->key - voice->root_pitch / 100.0f) - + voice->root_pitch); + voice->gen[GEN_PITCH].val = voice->gen[GEN_SCALETUNE].val + * (voice->key - voice->root_pitch / 100.0f) + voice->root_pitch; } - } /* diff --git a/fluidsynth/src/fluid_voice.h b/fluidsynth/src/fluid_voice.h index 4de645a9..e838abae 100644 --- a/fluidsynth/src/fluid_voice.h +++ b/fluidsynth/src/fluid_voice.h @@ -217,6 +217,7 @@ fluid_voice_t* new_fluid_voice(fluid_real_t output_rate); int delete_fluid_voice(fluid_voice_t* voice); void fluid_voice_start(fluid_voice_t* voice); +void fluid_voice_calculate_gen_pitch(fluid_voice_t* voice); int fluid_voice_write(fluid_voice_t* voice, fluid_real_t* left, fluid_real_t* right,