diff options
Diffstat (limited to 'libs/fluidsynth/src/fluid_synth.c')
-rw-r--r-- | libs/fluidsynth/src/fluid_synth.c | 208 |
1 files changed, 147 insertions, 61 deletions
diff --git a/libs/fluidsynth/src/fluid_synth.c b/libs/fluidsynth/src/fluid_synth.c index 570a2535cd..382979f7f5 100644 --- a/libs/fluidsynth/src/fluid_synth.c +++ b/libs/fluidsynth/src/fluid_synth.c @@ -117,7 +117,6 @@ static int fluid_synth_set_important_channels(fluid_synth_t *synth, const char * /* Callback handlers for real-time settings */ -static void fluid_synth_handle_sample_rate(void *data, const char *name, double value); static void fluid_synth_handle_gain(void *data, const char *name, double value); static void fluid_synth_handle_polyphony(void *data, const char *name, int value); static void fluid_synth_handle_device_id(void *data, const char *name, int value); @@ -197,7 +196,7 @@ void fluid_synth_settings(fluid_settings_t *settings) fluid_settings_register_int(settings, "synth.chorus.active", 1, 0, 1, FLUID_HINT_TOGGLED); fluid_settings_register_int(settings, "synth.chorus.nr", FLUID_CHORUS_DEFAULT_N, 0, 99, 0); fluid_settings_register_num(settings, "synth.chorus.level", FLUID_CHORUS_DEFAULT_LEVEL, 0.0f, 10.0f, 0); - fluid_settings_register_num(settings, "synth.chorus.speed", FLUID_CHORUS_DEFAULT_SPEED, 0.29f, 5.0f, 0); + fluid_settings_register_num(settings, "synth.chorus.speed", FLUID_CHORUS_DEFAULT_SPEED, 0.1f, 5.0f, 0); fluid_settings_register_num(settings, "synth.chorus.depth", FLUID_CHORUS_DEFAULT_DEPTH, 0.0f, 256.0f, 0); fluid_settings_register_int(settings, "synth.ladspa.active", 0, 0, 1, FLUID_HINT_TOGGLED); @@ -431,6 +430,10 @@ fluid_synth_init(void) /* SF2.01 page 57 section 8.4.10 MIDI Pitch Wheel to Initial Pitch ... */ + /* Initial Pitch is not a "standard" generator, because it isn't mentioned in the + list of generators in the SF2 specifications. That's why destination Initial Pitch + is replaced here by fine tune generator. + */ fluid_mod_set_source1(&default_pitch_bend_mod, FLUID_MOD_PITCHWHEEL, /* Index=14 */ FLUID_MOD_GC /* CC =0 */ | FLUID_MOD_LINEAR /* type=0 */ @@ -443,7 +446,8 @@ fluid_synth_init(void) | FLUID_MOD_UNIPOLAR /* P=0 */ | FLUID_MOD_POSITIVE /* D=0 */ ); - fluid_mod_set_dest(&default_pitch_bend_mod, GEN_PITCH); /* Destination: Initial pitch */ + /* Also see the comment in gen.h about GEN_PITCH */ + fluid_mod_set_dest(&default_pitch_bend_mod, GEN_FINETUNE); /* Destination: Fine Tune */ fluid_mod_set_amount(&default_pitch_bend_mod, 12700.0); /* Amount: 12700 cents */ @@ -589,8 +593,10 @@ static FLUID_INLINE unsigned int fluid_synth_get_min_note_length_LOCAL(fluid_syn * @param settings Configuration parameters to use (used directly). * @return New FluidSynth instance or NULL on error * - * @note The settings parameter is used directly and should not be modified - * or freed independently. + * @note The @p settings parameter is used directly and should freed after + * the synth has been deleted. Further note that you may modify FluidSettings of the + * @p settings instance. However, only those FluidSettings marked as 'realtime' will + * affect the synth immediately. */ fluid_synth_t * new_fluid_synth(fluid_settings_t *settings) @@ -647,8 +653,6 @@ new_fluid_synth(fluid_settings_t *settings) fluid_settings_getnum_float(settings, "synth.overflow.important", &synth->overflow.important); /* register the callbacks */ - fluid_settings_callback_num(settings, "synth.sample-rate", - fluid_synth_handle_sample_rate, synth); fluid_settings_callback_num(settings, "synth.gain", fluid_synth_handle_gain, synth); fluid_settings_callback_int(settings, "synth.polyphony", @@ -1126,6 +1130,10 @@ fluid_synth_error(fluid_synth_t *synth) /** * Send a note-on event to a FluidSynth object. + * + * This function will take care of proper legato playing. If a note on channel @p chan is + * already playing at the given key @p key, it will be released (even if it is sustained). + * In other words, overlapping notes are not allowed. * @param synth FluidSynth instance * @param chan MIDI channel number (0 to MIDI channel count - 1) * @param key MIDI note number (0-127) @@ -3046,20 +3054,31 @@ fluid_synth_update_presets(fluid_synth_t *synth) } } -/* Handler for synth.sample-rate setting. */ -static void -fluid_synth_handle_sample_rate(void *data, const char *name, double value) -{ - fluid_synth_t *synth = (fluid_synth_t *)data; - fluid_synth_set_sample_rate(synth, (float) value); -} - - /** - * Set sample rate of the synth. - * @note This function should only be used when no voices or notes are active. + * Set up an event to change the sample-rate of the synth during the next rendering call. + * @warning This function is broken-by-design! Don't use it! Instead, specify the sample-rate when creating the synth. + * @deprecated As of fluidsynth 2.1.0 this function has been deprecated. + * Changing the sample-rate is generally not considered to be a real-time use-case, as it always produces some audible artifact ("click", "pop") on the dry sound and effects (because LFOs for chorus and reverb need to be reinitialized). + * The sample-rate change may also require memory allocation deep down in the effect units. + * However, this memory allocation may fail and there is no way for the caller to know that, because the actual change of the sample-rate is executed during rendering. + * This function cannot (must not) do the sample-rate change itself, otherwise the synth needs to be locked down, causing rendering to block. + * Esp. do not use this function if this @p synth instance is used by an audio driver, because the audio driver cannot be notified by this sample-rate change. + * Long story short: don't use it. + * @code{.cpp} + fluid_synth_t* synth; // assume initialized + // [...] + // sample-rate change needed? Delete the audio driver, if any. + delete_fluid_audio_driver(adriver); + // then delete the synth + delete_fluid_synth(synth); + // update the sample-rate + fluid_settings_setnum(settings, "synth.sample-rate", 22050.0); + // and re-create objects + synth = new_fluid_synth(settings); + adriver = new_fluid_audio_driver(settings, synth); + * @endcode * @param synth FluidSynth instance - * @param sample_rate New sample rate (Hz) + * @param sample_rate New sample-rate (Hz) * @since 1.1.2 */ void @@ -3382,6 +3401,8 @@ fluid_synth_nwrite_float(fluid_synth_t *synth, int len, fluid_return_val_if_fail(synth != NULL, FLUID_FAILED); fluid_return_val_if_fail(left != NULL, FLUID_FAILED); fluid_return_val_if_fail(right != NULL, FLUID_FAILED); + fluid_return_val_if_fail(len >= 0, FLUID_FAILED); + fluid_return_val_if_fail(len != 0, FLUID_OK); // to avoid raising FE_DIVBYZERO below /* First, take what's still available in the buffer */ count = 0; @@ -3666,6 +3687,8 @@ fluid_synth_process_LOCAL(fluid_synth_t *synth, int len, int nfx, float *fx[], fluid_return_val_if_fail(synth != NULL, FLUID_FAILED); fluid_return_val_if_fail(nfx % 2 == 0, FLUID_FAILED); fluid_return_val_if_fail(nout % 2 == 0, FLUID_FAILED); + fluid_return_val_if_fail(len >= 0, FLUID_FAILED); + fluid_return_val_if_fail(len != 0, FLUID_OK); // to avoid raising FE_DIVBYZERO below nfxchan = synth->effects_channels; nfxunits = synth->effects_groups; @@ -3798,9 +3821,19 @@ fluid_synth_write_float(fluid_synth_t *synth, int len, void *lout, int loff, int lincr, void *rout, int roff, int rincr) { - int i, j, k, l; - float *left_out = (float *) lout; - float *right_out = (float *) rout; + return fluid_synth_write_float_LOCAL(synth, len, lout, loff, lincr, rout, roff, rincr, fluid_synth_render_blocks); +} + +int +fluid_synth_write_float_LOCAL(fluid_synth_t *synth, int len, + void *lout, int loff, int lincr, + void *rout, int roff, int rincr, + int (*block_render_func)(fluid_synth_t *, int) + ) +{ + int n, cur, size; + float *left_out = (float *) lout + loff; + float *right_out = (float *) rout + roff; fluid_real_t *left_in; fluid_real_t *right_in; double time = fluid_utime(); @@ -3811,28 +3844,60 @@ fluid_synth_write_float(fluid_synth_t *synth, int len, fluid_return_val_if_fail(synth != NULL, FLUID_FAILED); fluid_return_val_if_fail(lout != NULL, FLUID_FAILED); fluid_return_val_if_fail(rout != NULL, FLUID_FAILED); + fluid_return_val_if_fail(len >= 0, FLUID_FAILED); + fluid_return_val_if_fail(len != 0, FLUID_OK); // to avoid raising FE_DIVBYZERO below fluid_rvoice_mixer_set_mix_fx(synth->eventhandler->mixer, 1); - l = synth->cur; fluid_rvoice_mixer_get_bufs(synth->eventhandler->mixer, &left_in, &right_in); - for(i = 0, j = loff, k = roff; i < len; i++, l++, j += lincr, k += rincr) + size = len; + cur = synth->cur; + + do { /* fill up the buffers as needed */ - if(l >= synth->curmax) + if(cur >= synth->curmax) { - int blocksleft = (len - i + FLUID_BUFSIZE - 1) / FLUID_BUFSIZE; - synth->curmax = FLUID_BUFSIZE * fluid_synth_render_blocks(synth, blocksleft); + int blocksleft = (size + FLUID_BUFSIZE - 1) / FLUID_BUFSIZE; + synth->curmax = FLUID_BUFSIZE * block_render_func(synth, blocksleft); fluid_rvoice_mixer_get_bufs(synth->eventhandler->mixer, &left_in, &right_in); + cur = 0; + } + + /* calculate amount of available samples */ + n = synth->curmax - cur; - l = 0; + /* keep track of emitted samples */ + if(n > size) + { + n = size; } - left_out[j] = (float) left_in[0 * FLUID_BUFSIZE * FLUID_MIXER_MAX_BUFFERS_DEFAULT + l]; - right_out[k] = (float) right_in[0 * FLUID_BUFSIZE * FLUID_MIXER_MAX_BUFFERS_DEFAULT + l]; + size -= n; + + /* update pointers to current position */ + left_in += cur + n; + right_in += cur + n; + + /* set final cursor position */ + cur += n; + + /* reverse index */ + n = 0 - n; + + do + { + *left_out = (float) left_in[n]; + *right_out = (float) right_in[n]; + + left_out += lincr; + right_out += rincr; + } + while(++n < 0); } + while(size); - synth->cur = l; + synth->cur = cur; time = fluid_utime() - time; cpu_load = 0.5 * (fluid_atomic_float_get(&synth->cpu_load) + time * synth->sample_rate / len / 10000.0); @@ -3924,43 +3989,77 @@ fluid_synth_write_s16(fluid_synth_t *synth, int len, void *lout, int loff, int lincr, void *rout, int roff, int rincr) { - int i, j, k, cur; - int16_t *left_out = lout; - int16_t *right_out = rout; + int di, n, cur, size; + int16_t *left_out = (int16_t *)lout + loff; + int16_t *right_out = (int16_t *)rout + roff; fluid_real_t *left_in; fluid_real_t *right_in; double time = fluid_utime(); - int di; float cpu_load; fluid_profile_ref_var(prof_ref); + fluid_return_val_if_fail(synth != NULL, FLUID_FAILED); + fluid_return_val_if_fail(lout != NULL, FLUID_FAILED); + fluid_return_val_if_fail(rout != NULL, FLUID_FAILED); + fluid_return_val_if_fail(len >= 0, FLUID_FAILED); + fluid_return_val_if_fail(len != 0, FLUID_OK); // to avoid raising FE_DIVBYZERO below + fluid_rvoice_mixer_set_mix_fx(synth->eventhandler->mixer, 1); fluid_rvoice_mixer_get_bufs(synth->eventhandler->mixer, &left_in, &right_in); + size = len; cur = synth->cur; di = synth->dither_index; - for(i = 0, j = loff, k = roff; i < len; i++, cur++, j += lincr, k += rincr) + do { - /* fill up the buffers as needed */ if(cur >= synth->curmax) { - int blocksleft = (len - i + FLUID_BUFSIZE - 1) / FLUID_BUFSIZE; + int blocksleft = (size + FLUID_BUFSIZE - 1) / FLUID_BUFSIZE; synth->curmax = FLUID_BUFSIZE * fluid_synth_render_blocks(synth, blocksleft); fluid_rvoice_mixer_get_bufs(synth->eventhandler->mixer, &left_in, &right_in); cur = 0; } - left_out[j] = round_clip_to_i16(left_in[0 * FLUID_BUFSIZE * FLUID_MIXER_MAX_BUFFERS_DEFAULT + cur] * 32766.0f + rand_table[0][di]); - right_out[k] = round_clip_to_i16(right_in[0 * FLUID_BUFSIZE * FLUID_MIXER_MAX_BUFFERS_DEFAULT + cur] * 32766.0f + rand_table[1][di]); + /* calculate amount of available samples */ + n = synth->curmax - cur; - if(++di >= DITHER_SIZE) + /* keep track of emitted samples */ + if(n > size) { - di = 0; + n = size; } + + size -= n; + + /* update pointers to current position */ + left_in += cur + n; + right_in += cur + n; + + /* set final cursor position */ + cur += n; + + /* reverse index */ + n = 0 - n; + + do + { + *left_out = round_clip_to_i16(left_in[n] * 32766.0f + rand_table[0][di]); + *right_out = round_clip_to_i16(right_in[n] * 32766.0f + rand_table[1][di]); + + left_out += lincr; + right_out += rincr; + + if(++di >= DITHER_SIZE) + { + di = 0; + } + } + while(++n < 0); } + while(size); synth->cur = cur; synth->dither_index = di; /* keep dither buffer continous */ @@ -5185,9 +5284,9 @@ fluid_synth_set_chorus_on(fluid_synth_t *synth, int on) * @param nr Chorus voice count (0-99, CPU time consumption proportional to * this value) * @param level Chorus level (0.0-10.0) - * @param speed Chorus speed in Hz (0.29-5.0) - * @param depth_ms Chorus depth (max value depends on synth sample rate, - * 0.0-21.0 is safe for sample rate values up to 96KHz) + * @param speed Chorus speed in Hz (0.1-5.0) + * @param depth_ms Chorus depth (max value depends on synth sample-rate, + * 0.0-21.0 is safe for sample-rate values up to 96KHz) * @param type Chorus waveform type (#fluid_chorus_mod) * @return #FLUID_OK on success, #FLUID_FAILED otherwise */ @@ -5243,19 +5342,6 @@ int fluid_synth_set_chorus_type(fluid_synth_t *synth, int type) return fluid_synth_set_chorus_full(synth, FLUID_CHORUS_SET_TYPE, 0, 0, 0, 0, type); } -/** - * Set one or more chorus parameters. - * @param synth FluidSynth instance - * @param set Flags indicating which chorus parameters to set (#fluid_chorus_set_t) - * @param nr Chorus voice count (0-99, CPU time consumption proportional to - * this value) - * @param level Chorus level (0.0-10.0) - * @param speed Chorus speed in Hz (0.29-5.0) - * @param depth_ms Chorus depth (max value depends on synth sample rate, - * 0.0-21.0 is safe for sample rate values up to 96KHz) - * @param type Chorus waveform type (#fluid_chorus_mod) - * @return #FLUID_OK on success, #FLUID_FAILED otherwise - */ int fluid_synth_set_chorus_full(fluid_synth_t *synth, int set, int nr, double level, double speed, double depth_ms, int type) @@ -5312,7 +5398,7 @@ fluid_synth_set_chorus_full(fluid_synth_t *synth, int set, int nr, double level, /** * Get chorus voice number (delay line count) value. * @param synth FluidSynth instance - * @return Chorus voice count (0-99) + * @return Chorus voice count */ int fluid_synth_get_chorus_nr(fluid_synth_t *synth) @@ -5328,7 +5414,7 @@ fluid_synth_get_chorus_nr(fluid_synth_t *synth) /** * Get chorus level. * @param synth FluidSynth instance - * @return Chorus level value (0.0-10.0) + * @return Chorus level value */ double fluid_synth_get_chorus_level(fluid_synth_t *synth) @@ -5344,7 +5430,7 @@ fluid_synth_get_chorus_level(fluid_synth_t *synth) /** * Get chorus speed in Hz. * @param synth FluidSynth instance - * @return Chorus speed in Hz (0.29-5.0) + * @return Chorus speed in Hz */ double fluid_synth_get_chorus_speed(fluid_synth_t *synth) |