From e3fd4984dd1fbb8c5284dc1e6604db8bf48af1fc Mon Sep 17 00:00:00 2001 From: David Henningsson Date: Sat, 26 Jun 2010 15:02:58 +0000 Subject: [PATCH] Split voice into soft and hard real-time parts --- fluidsynth/src/CMakeLists.txt | 4 +- fluidsynth/src/fluid_synth.c | 22 +- fluidsynth/src/fluid_synth.h | 4 +- fluidsynth/src/fluid_voice.c | 644 +++++---------------- fluidsynth/src/fluid_voice.h | 105 +--- fluidsynth/src/rvoice/fluid_rvoice.c | 644 +++++++++++++++++++++ fluidsynth/src/rvoice/fluid_rvoice.h | 196 +++++++ fluidsynth/src/rvoice/fluid_rvoice_dsp.c | 675 +++++++++++++++++++++++ 8 files changed, 1695 insertions(+), 599 deletions(-) create mode 100644 fluidsynth/src/rvoice/fluid_rvoice.c create mode 100644 fluidsynth/src/rvoice/fluid_rvoice.h create mode 100644 fluidsynth/src/rvoice/fluid_rvoice_dsp.c diff --git a/fluidsynth/src/CMakeLists.txt b/fluidsynth/src/CMakeLists.txt index 8612f071..4f7a9948 100644 --- a/fluidsynth/src/CMakeLists.txt +++ b/fluidsynth/src/CMakeLists.txt @@ -114,7 +114,6 @@ set ( libfluidsynth_SOURCES fluid_conv.h fluid_defsfont.c fluid_defsfont.h - fluid_dsp_float.c fluid_event.c fluid_event_priv.h fluid_event_queue.c @@ -127,6 +126,9 @@ set ( libfluidsynth_SOURCES rvoice/fluid_iir_filter.h rvoice/fluid_lfo.h rvoice/fluid_adsr_env.h + rvoice/fluid_rvoice.h + rvoice/fluid_rvoice.c + rvoice/fluid_rvoice_dsp.c fluid_list.c fluid_list.h fluid_mdriver.c diff --git a/fluidsynth/src/fluid_synth.c b/fluidsynth/src/fluid_synth.c index 38a859c1..46bdfa43 100644 --- a/fluidsynth/src/fluid_synth.c +++ b/fluidsynth/src/fluid_synth.c @@ -249,7 +249,7 @@ fluid_synth_init(void) fluid_conversion_config(); - fluid_dsp_float_config(); + fluid_rvoice_dsp_config(); fluid_sys_config(); @@ -1324,11 +1324,9 @@ fluid_synth_noteoff_LOCAL(fluid_synth_t* synth, int chan, int key) used_voices++; } } - FLUID_LOG(FLUID_INFO, "noteoff\t%d\t%d\t%d\t%05d\t%.3f\t%.3f\t%.3f\t%d", + FLUID_LOG(FLUID_INFO, "noteoff\t%d\t%d\t%d\t%05d\t%.3f\t%d", voice->chan, voice->key, 0, voice->id, - (float) (voice->start_time + voice->ticks) / 44100.0f, (fluid_curtime() - synth->start) / 1000.0f, - (float) voice->ticks / 44100.0f, used_voices); } /* if verbose */ @@ -2977,7 +2975,8 @@ static int fluid_synth_one_block(fluid_synth_t* synth, int do_not_mix_fx_to_out) { fluid_real_t local_voice_buf[FLUID_BUFSIZE]; - int i, auchan, start_index, voice_index; + int i, auchan; +// int start_index, voice_index; fluid_voice_t* voice; fluid_real_t* left_buf; fluid_real_t* right_buf; @@ -3024,6 +3023,7 @@ fluid_synth_one_block(fluid_synth_t* synth, int do_not_mix_fx_to_out) fluid_profile(FLUID_PROF_ONE_BLOCK_CLEAR, prof_ref); +#if 0 if (synth->cores > 1) { /* Look for first active voice to process */ @@ -3111,6 +3111,7 @@ got_voice: /* We got a voice to process */ } else /* synth->cores < 1 - Not multi-core enabled */ { +#endif /* call all playing synthesis processes */ for (i = 0; i < synth->polyphony; i++) { fluid_profile_ref_var (prof_ref_voice); @@ -3135,12 +3136,16 @@ got_voice: /* We got a voice to process */ left_buf = synth->left_buf[auchan]; right_buf = synth->right_buf[auchan]; - fluid_voice_write (voice, local_voice_buf); - fluid_voice_mix (voice, left_buf, right_buf, reverb_buf, chorus_buf); + count = fluid_voice_write (voice, local_voice_buf); + if (count > 0) + fluid_voice_mix (voice, count, local_voice_buf, left_buf, right_buf, + reverb_buf, chorus_buf); fluid_profile (FLUID_PROF_ONE_BLOCK_VOICE, prof_ref_voice); } +#if 0 } +#endif fluid_check_fpe("Synthesis processes"); @@ -3424,7 +3429,8 @@ fluid_synth_free_voice_by_kill_LOCAL(fluid_synth_t* synth) this_voice_prio -= (synth->noteid - fluid_voice_get_id(voice)); /* take a rough estimate of loudness into account. Louder voices are more important. */ - this_voice_prio += fluid_voice_get_loudness(voice) * 1000.; + // FIXME + // this_voice_prio += fluid_voice_get_loudness(voice) * 1000.; /* check if this voice has less priority than the previous candidate. */ if (this_voice_prio < best_prio) diff --git a/fluidsynth/src/fluid_synth.h b/fluidsynth/src/fluid_synth.h index b7743466..b862ad27 100644 --- a/fluidsynth/src/fluid_synth.h +++ b/fluidsynth/src/fluid_synth.h @@ -61,12 +61,12 @@ * * ENUM */ -enum fluid_loop { +/*enum fluid_loop { FLUID_UNLOOPED = 0, FLUID_LOOP_DURING_RELEASE = 1, FLUID_NOTUSED = 2, FLUID_LOOP_UNTIL_RELEASE = 3 -}; +};*/ enum fluid_synth_status { diff --git a/fluidsynth/src/fluid_voice.c b/fluidsynth/src/fluid_voice.c index 3a2cfce8..96cfde88 100644 --- a/fluidsynth/src/fluid_voice.c +++ b/fluidsynth/src/fluid_voice.c @@ -32,16 +32,6 @@ #define FLUID_MAX_AUDIBLE_FILTER_FC 19000.0f #define FLUID_MIN_AUDIBLE_FILTER_Q 1.2f -/* Smallest amplitude that can be perceived (full scale is +/- 0.5) - * 16 bits => 96+4=100 dB dynamic range => 0.00001 - * 0.00001 * 2 is approximately 0.00003 :) - */ -#define FLUID_NOISE_FLOOR 0.00003 - -/* these should be the absolute minimum that FluidSynth can deal with */ -#define FLUID_MIN_LOOP_SIZE 2 -#define FLUID_MIN_LOOP_PAD 0 - /* min vol envelope release (to stop clicks) in SoundFont timecents */ #define FLUID_MIN_VOLENVRELEASE -7200.0f /* ~16ms */ @@ -50,7 +40,14 @@ static int calculate_hold_decay_buffers(fluid_voice_t* voice, int gen_base, int gen_key2base, int is_decay); static fluid_real_t fluid_voice_get_lower_boundary_for_attenuation(fluid_voice_t* voice); -static void fluid_voice_check_sample_sanity(fluid_voice_t* voice); + +#define UPDATE_RVOICE0(proc) proc(voice->rvoice) +#define UPDATE_RVOICE1(proc, arg1) proc(voice->rvoice, arg1) +#define UPDATE_RVOICE2(proc, arg1, arg2) proc(voice->rvoice, arg1, arg2) +#define UPDATE_RVOICE_ENVLFO1(proc, envp, arg1) proc(&voice->rvoice->envlfo.envp, arg1) +#define UPDATE_RVOICE_ENVDATA(envp, section, arg1, arg2, arg3, arg4, arg5) \ + fluid_adsr_env_set_data(&voice->rvoice->envlfo.envp, section, arg1, arg2, arg3, arg4, arg5) +#define UPDATE_RVOICE_FILTER1(proc, arg1) proc(&voice->rvoice->resonant_filter, arg1) /* * new_fluid_voice @@ -64,6 +61,15 @@ new_fluid_voice(fluid_real_t output_rate) FLUID_LOG(FLUID_ERR, "Out of memory"); return NULL; } + voice->rvoice = FLUID_NEW(fluid_rvoice_t); + if (voice->rvoice == NULL) { + FLUID_LOG(FLUID_ERR, "Out of memory"); + FLUID_FREE(voice); + return NULL; + } + FLUID_MEMSET(voice->rvoice, 0, sizeof(fluid_rvoice_t)); + voice->can_access_rvoice = 1; + voice->status = FLUID_VOICE_CLEAN; voice->chan = NO_CHANNEL; voice->key = 0; @@ -71,19 +77,20 @@ new_fluid_voice(fluid_real_t output_rate) voice->channel = NULL; voice->sample = NULL; voice->output_rate = output_rate; + UPDATE_RVOICE1(fluid_rvoice_set_output_rate, output_rate); /* The 'sustain' and 'finished' segments of the volume / modulation * envelope are constant. They are never affected by any modulator * or generator. Therefore it is enough to initialize them once * during the lifetime of the synth. */ - fluid_adsr_env_set_data(&voice->volenv, FLUID_VOICE_ENVSUSTAIN, + UPDATE_RVOICE_ENVDATA(volenv, FLUID_VOICE_ENVSUSTAIN, 0xffffffff, 1.0f, 0.0f, -1.0f, 2.0f); - fluid_adsr_env_set_data(&voice->volenv, FLUID_VOICE_ENVFINISHED, + UPDATE_RVOICE_ENVDATA(volenv, FLUID_VOICE_ENVFINISHED, 0xffffffff, 0.0f, 0.0f, -1.0f, 1.0f); - fluid_adsr_env_set_data(&voice->modenv, FLUID_VOICE_ENVSUSTAIN, + UPDATE_RVOICE_ENVDATA(modenv, FLUID_VOICE_ENVSUSTAIN, 0xffffffff, 1.0f, 0.0f, -1.0f, 2.0f); - fluid_adsr_env_set_data(&voice->modenv, FLUID_VOICE_ENVFINISHED, + UPDATE_RVOICE_ENVDATA(modenv, FLUID_VOICE_ENVFINISHED, 0xffffffff, 0.0f, 0.0f, -1.0f, 1.0f); return voice; @@ -98,6 +105,11 @@ delete_fluid_voice(fluid_voice_t* voice) if (voice == NULL) { return FLUID_OK; } + if (!voice->can_access_rvoice) { + /* stop rvoice before deleting voice! */ + return FLUID_FAILED; + } + FLUID_FREE(voice->rvoice); FLUID_FREE(voice); return FLUID_OK; } @@ -115,7 +127,9 @@ fluid_voice_init(fluid_voice_t* voice, fluid_sample_t* sample, * generators have been retrieved from the sound font. Here, only * the 'working memory' of the voice (position in envelopes, history * of IIR filters, position in sample etc) is initialized. */ + int i; + fluid_return_val_if_fail(voice->can_access_rvoice, FLUID_FAILED); voice->id = id; voice->chan = fluid_channel_get_num(channel); @@ -125,31 +139,13 @@ fluid_voice_init(fluid_voice_t* voice, fluid_sample_t* sample, voice->mod_count = 0; voice->sample = sample; voice->start_time = start_time; - voice->ticks = 0; - voice->noteoff_ticks = 0; voice->debug = 0; - voice->has_looped = 0; /* Will be set during voice_write when the 2nd loop point is reached */ - voice->interp_method = fluid_channel_get_interp_method(voice->channel); + voice->has_noteoff = 0; + UPDATE_RVOICE0(fluid_rvoice_reset); + UPDATE_RVOICE1(fluid_rvoice_set_sample, sample); - /* vol env initialization */ - fluid_adsr_env_reset(&voice->volenv); - voice->amp = 0.0f; /* The last value of the volume envelope, used to - calculate the volume increment during - processing */ - - /* mod env initialization*/ - fluid_adsr_env_reset(&voice->modenv); - - /* lfo init */ - /* Fixme: Retrieve from any other existing - voice on this channel to keep LFOs in - unison? */ - - fluid_lfo_reset(&voice->modlfo); - fluid_lfo_reset(&voice->viblfo); - - /* Clear sample history in filter */ - fluid_iir_filter_reset(&voice->resonant_filter); + i = fluid_channel_get_interp_method(voice->channel); + UPDATE_RVOICE1(fluid_rvoice_set_interp_method, i); /* Set all the generators to their default value, according to SF * 2.01 section 8.1.3 (page 48). The value of NRPN messages are @@ -158,20 +154,14 @@ fluid_voice_init(fluid_voice_t* voice, fluid_sample_t* sample, * into voice parameters in * fluid_voice_calculate_runtime_synthesis_parameters. */ fluid_gen_init(&voice->gen[0], channel); + UPDATE_RVOICE1(fluid_rvoice_set_samplemode, voice->gen[GEN_SAMPLEMODE].val); voice->synth_gain = gain; /* avoid division by zero later*/ if (voice->synth_gain < 0.0000001){ voice->synth_gain = 0.0000001; } - - /* For a looped sample, this value will be overwritten as soon as the - * loop parameters are initialized (they may depend on modulators). - * This value can be kept, it is a worst-case estimate. - */ - - voice->amplitude_that_reaches_noise_floor_nonloop = FLUID_NOISE_FLOOR / voice->synth_gain; - voice->amplitude_that_reaches_noise_floor_loop = FLUID_NOISE_FLOOR / voice->synth_gain; + UPDATE_RVOICE1(fluid_rvoice_set_synth_gain, voice->synth_gain); /* Increment the reference count of the sample to prevent the unloading of the soundfont while this voice is playing. */ @@ -191,6 +181,8 @@ fluid_voice_gen_set(fluid_voice_t* voice, int i, float val) { voice->gen[i].val = val; voice->gen[i].flags = GEN_SET; + if (i == GEN_SAMPLEMODE) + UPDATE_RVOICE1(fluid_rvoice_set_samplemode, val); } /** @@ -244,265 +236,58 @@ fluid_real_t fluid_voice_gen_value(fluid_voice_t* voice, int num) int fluid_voice_write (fluid_voice_t* voice, fluid_real_t *dsp_buf) { - fluid_real_t target_amp; /* target amplitude */ - int count = 0; + int result; + if (!voice->can_access_rvoice) + return 0; - /* Other routines (such as fluid_voice_effects) use the last dsp_buf assigned */ - voice->dsp_buf = dsp_buf; - voice->dsp_buf_count = 0; + result = fluid_rvoice_write(voice->rvoice, dsp_buf); - /******************* sample **********************/ + if (result == -1) + return 0; - if (voice->sample == NULL) - { + if ((result < FLUID_BUFSIZE) && _PLAYING(voice)) /* Voice finished by itself */ fluid_voice_off(voice); - return 0; - } - if (voice->noteoff_ticks != 0 && voice->ticks >= voice->noteoff_ticks) - { - fluid_voice_noteoff(voice); - } - - fluid_check_fpe ("voice_write startup"); - - /* Range checking for sample- and loop-related parameters - * Initial phase is calculated here*/ - fluid_voice_check_sample_sanity (voice); - - /******************* vol env **********************/ - - fluid_adsr_env_calc(&voice->volenv, 1); - - - if (fluid_adsr_env_get_section(&voice->volenv) == FLUID_VOICE_ENVFINISHED) - { - fluid_profile (FLUID_PROF_VOICE_RELEASE, voice->ref); - fluid_voice_off (voice); - return 0; - } - - fluid_check_fpe ("voice_write vol env"); - - /******************* mod env **********************/ - - fluid_adsr_env_calc(&voice->modenv, 0); - fluid_check_fpe ("voice_write mod env"); - - /******************* lfo **********************/ - - fluid_lfo_calc(&voice->modlfo, voice->ticks); - fluid_check_fpe ("voice_write mod LFO"); - fluid_lfo_calc(&voice->viblfo, voice->ticks); - fluid_check_fpe ("voice_write vib LFO"); - - /******************* amplitude **********************/ - - /* calculate final amplitude - * - initial gain - * - amplitude envelope - */ - - if (fluid_adsr_env_get_section(&voice->volenv) == FLUID_VOICE_ENVDELAY) - goto post_process; /* The volume amplitude is in hold phase. No sound is produced. */ - - if (fluid_adsr_env_get_section(&voice->volenv) == FLUID_VOICE_ENVATTACK) - { - /* the envelope is in the attack section: ramp linearly to max value. - * A positive modlfo_to_vol should increase volume (negative attenuation). - */ - target_amp = fluid_atten2amp (voice->attenuation) - * fluid_cb2amp (fluid_lfo_get_val(&voice->modlfo) * -voice->modlfo_to_vol) - * fluid_adsr_env_get_val(&voice->volenv); - } - else - { - fluid_real_t amplitude_that_reaches_noise_floor; - fluid_real_t amp_max; - - target_amp = fluid_atten2amp (voice->attenuation) - * fluid_cb2amp (960.0f * (1.0f - fluid_adsr_env_get_val(&voice->volenv)) - + fluid_lfo_get_val(&voice->modlfo) * -voice->modlfo_to_vol); - - /* We turn off a voice, if the volume has dropped low enough. */ - - /* A voice can be turned off, when an estimate for the volume - * (upper bound) falls below that volume, that will drop the - * sample below the noise floor. - */ - - /* If the loop amplitude is known, we can use it if the voice loop is within - * the sample loop - */ - - /* Is the playing pointer already in the loop? */ - if (voice->has_looped) - amplitude_that_reaches_noise_floor = voice->amplitude_that_reaches_noise_floor_loop; - else - amplitude_that_reaches_noise_floor = voice->amplitude_that_reaches_noise_floor_nonloop; - - /* voice->attenuation_min is a lower boundary for the attenuation - * now and in the future (possibly 0 in the worst case). Now the - * amplitude of sample and volenv cannot exceed amp_max (since - * volenv_val can only drop): - */ - - amp_max = fluid_atten2amp (voice->min_attenuation_cB) * - fluid_adsr_env_get_val(&voice->volenv); - - /* And if amp_max is already smaller than the known amplitude, - * which will attenuate the sample below the noise floor, then we - * can safely turn off the voice. Duh. */ - if (amp_max < amplitude_that_reaches_noise_floor) - { - fluid_profile (FLUID_PROF_VOICE_RELEASE, voice->ref); - fluid_voice_off (voice); - goto post_process; - } - } - - /* Volume increment to go from voice->amp to target_amp in FLUID_BUFSIZE steps */ - voice->amp_incr = (target_amp - voice->amp) / FLUID_BUFSIZE; - - fluid_check_fpe ("voice_write amplitude calculation"); - - /* no volume and not changing? - No need to process */ - if ((voice->amp == 0.0f) && (voice->amp_incr == 0.0f)) - goto post_process; - - /* Calculate the number of samples, that the DSP loop advances - * through the original waveform with each step in the output - * buffer. It is the ratio between the frequencies of original - * waveform and output waveform.*/ - voice->phase_incr = fluid_ct2hz_real - (voice->pitch + fluid_lfo_get_val(&voice->modlfo) * voice->modlfo_to_pitch - + fluid_lfo_get_val(&voice->viblfo) * voice->viblfo_to_pitch - + fluid_adsr_env_get_val(&voice->modenv) * voice->modenv_to_pitch) / voice->root_pitch_hz; - - fluid_check_fpe ("voice_write phase calculation"); - - /* if phase_incr is not advancing, set it to the minimum fraction value (prevent stuckage) */ - if (voice->phase_incr == 0) voice->phase_incr = 1; - - /*************** resonant filter ******************/ - fluid_iir_filter_calc(&voice->resonant_filter, voice->output_rate, - fluid_lfo_get_val(&voice->modlfo) * voice->modlfo_to_fc + - fluid_adsr_env_get_val(&voice->modenv) * voice->modenv_to_fc); - - /*********************** run the dsp chain ************************ - * The sample is mixed with the output buffer. - * The buffer has to be filled from 0 to FLUID_BUFSIZE-1. - * Depending on the position in the loop and the loop size, this - * may require several runs. */ - - switch (voice->interp_method) - { - case FLUID_INTERP_NONE: - count = fluid_dsp_float_interpolate_none (voice); - break; - case FLUID_INTERP_LINEAR: - count = fluid_dsp_float_interpolate_linear (voice); - break; - case FLUID_INTERP_4THORDER: - default: - count = fluid_dsp_float_interpolate_4th_order (voice); - break; - case FLUID_INTERP_7THORDER: - count = fluid_dsp_float_interpolate_7th_order (voice); - break; - } - - fluid_check_fpe ("voice_write interpolation"); - - voice->dsp_buf_count = count; - - /* Apply filter */ - if (count > 0) - fluid_iir_filter_apply(&voice->resonant_filter, voice->dsp_buf, count); - - /* turn off voice if short count (sample ended and not looping) */ - if (count < FLUID_BUFSIZE) - { - fluid_profile(FLUID_PROF_VOICE_RELEASE, voice->ref); - fluid_voice_off(voice); - } - - post_process: - voice->ticks += FLUID_BUFSIZE; - fluid_check_fpe ("voice_write postprocess"); - return count; + return result; } /** * Mix voice data to left/right (panning), reverb and chorus buffers. + * @param count Number of samples + * @param dsp_buf Source buffer * @param voice Voice to mix * @param left_buf Left audio buffer * @param right_buf Right audio buffer * @param reverb_buf Reverb buffer * @param chorus_buf Chorus buffer * - * NOTE: Uses voice->dsp_buf and voice->dsp_buf_count which were assigned - * by fluid_voice_write(). This is therefore meant to be called only after - * that function. */ void -fluid_voice_mix (fluid_voice_t *voice, +fluid_voice_mix (fluid_voice_t *voice, int count, fluid_real_t* dsp_buf, fluid_real_t* left_buf, fluid_real_t* right_buf, fluid_real_t* reverb_buf, fluid_real_t* chorus_buf) { - fluid_real_t *dsp_buf = voice->dsp_buf; - int count = voice->dsp_buf_count; - int dsp_i; - float v; + fluid_rvoice_buffers_t buffers; - /* pan (Copy the signal to the left and right output buffer) The voice - * panning generator has a range of -500 .. 500. If it is centered, - * it's close to 0. voice->amp_left and voice->amp_right are then the - * same, and we can save one multiplication per voice and sample. - */ - if ((-0.5 < voice->pan) && (voice->pan < 0.5)) - { - /* The voice is centered. Use voice->amp_left twice. */ - for (dsp_i = 0; dsp_i < count; dsp_i++) - { - v = voice->amp_left * dsp_buf[dsp_i]; - left_buf[dsp_i] += v; - right_buf[dsp_i] += v; - } - } - else /* The voice is not centered. Stereo samples have one side zero. */ - { - if (voice->amp_left != 0.0) - { - for (dsp_i = 0; dsp_i < count; dsp_i++) - left_buf[dsp_i] += voice->amp_left * dsp_buf[dsp_i]; - } + buffers.count = 4; + buffers.bufs[0].buf = left_buf; + buffers.bufs[1].buf = right_buf; + buffers.bufs[2].buf = reverb_buf; + buffers.bufs[3].buf = chorus_buf; - if (voice->amp_right != 0.0) - { - for (dsp_i = 0; dsp_i < count; dsp_i++) - right_buf[dsp_i] += voice->amp_right * dsp_buf[dsp_i]; - } - } - - /* reverb send. Buffer may be NULL. */ - if ((reverb_buf != NULL) && (voice->amp_reverb != 0.0)) - { - for (dsp_i = 0; dsp_i < count; dsp_i++) - reverb_buf[dsp_i] += voice->amp_reverb * dsp_buf[dsp_i]; - } - - /* chorus send. Buffer may be NULL. */ - if ((chorus_buf != NULL) && (voice->amp_chorus != 0)) - { - for (dsp_i = 0; dsp_i < count; dsp_i++) - chorus_buf[dsp_i] += voice->amp_chorus * dsp_buf[dsp_i]; - } + buffers.bufs[0].amp = voice->amp_left; + buffers.bufs[1].amp = voice->amp_right; + buffers.bufs[2].amp = voice->amp_reverb; + buffers.bufs[3].amp = voice->amp_chorus; + + fluid_rvoice_buffers_mix(&buffers, dsp_buf, count); fluid_check_fpe ("voice_mix"); } + + /* * fluid_voice_start */ @@ -514,10 +299,6 @@ void fluid_voice_start(fluid_voice_t* voice) fluid_voice_calculate_runtime_synthesis_parameters(voice); - /* Force setting of the phase at the first DSP loop run - * This cannot be done earlier, because it depends on modulators.*/ - voice->check_sample_sanity_flag=FLUID_SAMPLESANITY_STARTUP; - voice->ref = fluid_profile_ref(); voice->status = FLUID_VOICE_ON; @@ -548,6 +329,7 @@ fluid_voice_calculate_gen_pitch(fluid_voice_t* voice) voice->gen[GEN_PITCH].val = voice->gen[GEN_SCALETUNE].val * (voice->key - voice->root_pitch / 100.0f) + voice->root_pitch; } + } /* @@ -665,8 +447,8 @@ fluid_voice_calculate_runtime_synthesis_parameters(fluid_voice_t* voice) } /* Make an estimate on how loud this voice can get at any time (attenuation). */ - voice->min_attenuation_cB = fluid_voice_get_lower_boundary_for_attenuation(voice); - + UPDATE_RVOICE1(fluid_rvoice_set_min_attenuation_cB, + fluid_voice_get_lower_boundary_for_attenuation(voice)); return FLUID_OK; } @@ -762,7 +544,7 @@ fluid_voice_update_param(fluid_voice_t* voice, int gen) double q_dB; fluid_real_t x; fluid_real_t y; - unsigned int count; + unsigned int count, z; // Alternate attenuation scale used by EMU10K1 cards when setting the attenuation at the preset or instrument level within the SoundFont bank. static const float ALT_ATTENUATION_SCALE = 0.4; @@ -773,6 +555,8 @@ fluid_voice_update_param(fluid_voice_t* voice, int gen) voice->pan = _GEN(voice, GEN_PAN); voice->amp_left = fluid_pan(voice->pan, 1) * voice->synth_gain / 32768.0f; voice->amp_right = fluid_pan(voice->pan, 0) * voice->synth_gain / 32768.0f; + UPDATE_RVOICE2(fluid_rvoice_set_buf_amp, 0, voice->amp_left); + UPDATE_RVOICE2(fluid_rvoice_set_buf_amp, 1, voice->amp_right); break; case GEN_ATTENUATION: @@ -783,6 +567,7 @@ fluid_voice_update_param(fluid_voice_t* voice, int gen) * Motivation for range checking: * OHPiano.SF2 sets initial attenuation to a whooping -96 dB */ fluid_clip(voice->attenuation, 0.0, 1440.0); + UPDATE_RVOICE1(fluid_rvoice_set_attenuation, voice->attenuation); break; /* The pitch is calculated from three different generators. @@ -795,6 +580,7 @@ fluid_voice_update_param(fluid_voice_t* voice, int gen) voice->pitch = (_GEN(voice, GEN_PITCH) + 100.0f * _GEN(voice, GEN_COARSETUNE) + _GEN(voice, GEN_FINETUNE)); + UPDATE_RVOICE1(fluid_rvoice_set_pitch, voice->pitch); break; case GEN_REVERBSEND: @@ -802,6 +588,7 @@ fluid_voice_update_param(fluid_voice_t* voice, int gen) voice->reverb_send = _GEN(voice, GEN_REVERBSEND) / 1000.0f; fluid_clip(voice->reverb_send, 0.0, 1.0); voice->amp_reverb = voice->reverb_send * voice->synth_gain / 32768.0f; + UPDATE_RVOICE2(fluid_rvoice_set_buf_amp, 2, voice->amp_reverb); break; case GEN_CHORUSSEND: @@ -809,6 +596,7 @@ fluid_voice_update_param(fluid_voice_t* voice, int gen) voice->chorus_send = _GEN(voice, GEN_CHORUSSEND) / 1000.0f; fluid_clip(voice->chorus_send, 0.0, 1.0); voice->amp_chorus = voice->chorus_send * voice->synth_gain / 32768.0f; + UPDATE_RVOICE2(fluid_rvoice_set_buf_amp, 3, voice->amp_chorus); break; case GEN_OVERRIDEROOTKEY: @@ -824,12 +612,14 @@ fluid_voice_update_param(fluid_voice_t* voice, int gen) } else { voice->root_pitch = voice->sample->origpitch * 100.0f - voice->sample->pitchadj; } - voice->root_pitch_hz = fluid_ct2hz(voice->root_pitch); + x = fluid_ct2hz(voice->root_pitch); if (voice->sample != NULL) { - voice->root_pitch_hz *= (fluid_real_t) voice->output_rate / voice->sample->samplerate; + x *= (fluid_real_t) voice->output_rate / voice->sample->samplerate; } /* voice->pitch depends on voice->root_pitch, so calculate voice->pitch now */ fluid_voice_calculate_gen_pitch(voice); + UPDATE_RVOICE1(fluid_rvoice_set_root_pitch_hz, x); + break; case GEN_FILTERFC: @@ -838,7 +628,8 @@ fluid_voice_update_param(fluid_voice_t* voice, int gen) * modulation. The allowed range is tested in the 'fluid_ct2hz' * function [PH,20021214] */ - fluid_iir_filter_set_fres(&voice->resonant_filter, _GEN(voice, GEN_FILTERFC)); + x = _GEN(voice, GEN_FILTERFC); + UPDATE_RVOICE_FILTER1(fluid_iir_filter_set_fres, x); break; case GEN_FILTERQ: @@ -865,29 +656,33 @@ fluid_voice_update_param(fluid_voice_t* voice, int gen) * response of a non-resonant filter. This idea is implemented as * follows: */ q_dB -= 3.01f; - fluid_iir_filter_set_q_dB(&voice->resonant_filter, q_dB); + UPDATE_RVOICE_FILTER1(fluid_iir_filter_set_q_dB, q_dB); break; case GEN_MODLFOTOPITCH: - voice->modlfo_to_pitch = _GEN(voice, GEN_MODLFOTOPITCH); - fluid_clip(voice->modlfo_to_pitch, -12000.0, 12000.0); + x = _GEN(voice, GEN_MODLFOTOPITCH); + fluid_clip(x, -12000.0, 12000.0); + UPDATE_RVOICE1(fluid_rvoice_set_modlfo_to_pitch, x); break; case GEN_MODLFOTOVOL: - voice->modlfo_to_vol = _GEN(voice, GEN_MODLFOTOVOL); - fluid_clip(voice->modlfo_to_vol, -960.0, 960.0); + x = _GEN(voice, GEN_MODLFOTOVOL); + fluid_clip(x, -960.0, 960.0); + UPDATE_RVOICE1(fluid_rvoice_set_modlfo_to_vol, x); break; case GEN_MODLFOTOFILTERFC: - voice->modlfo_to_fc = _GEN(voice, GEN_MODLFOTOFILTERFC); - fluid_clip(voice->modlfo_to_fc, -12000, 12000); + x = _GEN(voice, GEN_MODLFOTOFILTERFC); + fluid_clip(x, -12000, 12000); + UPDATE_RVOICE1(fluid_rvoice_set_modlfo_to_fc, x); break; case GEN_MODLFODELAY: x = _GEN(voice, GEN_MODLFODELAY); fluid_clip(x, -12000.0f, 5000.0f); - fluid_lfo_set_delay(&voice->modlfo, (unsigned int) (voice->output_rate * fluid_tc2sec_delay(x))); + z = (unsigned int) (voice->output_rate * fluid_tc2sec_delay(x)); + UPDATE_RVOICE_ENVLFO1(fluid_lfo_set_delay, modlfo, z); break; case GEN_MODLFOFREQ: @@ -896,7 +691,8 @@ fluid_voice_update_param(fluid_voice_t* voice, int gen) */ x = _GEN(voice, GEN_MODLFOFREQ); fluid_clip(x, -16000.0f, 4500.0f); - fluid_lfo_set_incr(&voice->modlfo, (4.0f * FLUID_BUFSIZE * fluid_act2hz(x) / voice->output_rate)); + x = (4.0f * FLUID_BUFSIZE * fluid_act2hz(x) / voice->output_rate); + UPDATE_RVOICE_ENVLFO1(fluid_lfo_set_incr, modlfo, x); break; case GEN_VIBLFOFREQ: @@ -907,18 +703,21 @@ fluid_voice_update_param(fluid_voice_t* voice, int gen) */ x = _GEN(voice, GEN_VIBLFOFREQ); fluid_clip(x, -16000.0f, 4500.0f); - fluid_lfo_set_incr(&voice->viblfo, (4.0f * FLUID_BUFSIZE * fluid_act2hz(x) / voice->output_rate)); + x = 4.0f * FLUID_BUFSIZE * fluid_act2hz(x) / voice->output_rate; + UPDATE_RVOICE_ENVLFO1(fluid_lfo_set_incr, viblfo, x); break; case GEN_VIBLFODELAY: x = _GEN(voice,GEN_VIBLFODELAY); fluid_clip(x, -12000.0f, 5000.0f); - fluid_lfo_set_delay(&voice->viblfo, (unsigned int) (voice->output_rate * fluid_tc2sec_delay(x))); + z = (unsigned int) (voice->output_rate * fluid_tc2sec_delay(x)); + UPDATE_RVOICE_ENVLFO1(fluid_lfo_set_delay, viblfo, z); break; case GEN_VIBLFOTOPITCH: - voice->viblfo_to_pitch = _GEN(voice, GEN_VIBLFOTOPITCH); - fluid_clip(voice->viblfo_to_pitch, -12000.0, 12000.0); + x = _GEN(voice, GEN_VIBLFOTOPITCH); + fluid_clip(x, -12000.0, 12000.0); + UPDATE_RVOICE1(fluid_rvoice_set_viblfo_to_pitch, x); break; case GEN_KEYNUM: @@ -951,18 +750,20 @@ fluid_voice_update_param(fluid_voice_t* voice, int gen) break; case GEN_MODENVTOPITCH: - voice->modenv_to_pitch = _GEN(voice, GEN_MODENVTOPITCH); - fluid_clip(voice->modenv_to_pitch, -12000.0, 12000.0); + x = _GEN(voice, GEN_MODENVTOPITCH); + fluid_clip(x, -12000.0, 12000.0); + UPDATE_RVOICE1(fluid_rvoice_set_modenv_to_pitch, x); break; case GEN_MODENVTOFILTERFC: - voice->modenv_to_fc = _GEN(voice,GEN_MODENVTOFILTERFC); + x = _GEN(voice,GEN_MODENVTOFILTERFC); /* Range: SF2.01 section 8.1.3 # 1 * Motivation for range checking: * Filter is reported to make funny noises now and then */ - fluid_clip(voice->modenv_to_fc, -12000.0, 12000.0); + fluid_clip(x, -12000.0, 12000.0); + UPDATE_RVOICE1(fluid_rvoice_set_modenv_to_fc, x); break; @@ -980,38 +781,38 @@ fluid_voice_update_param(fluid_voice_t* voice, int gen) case GEN_STARTADDROFS: /* SF2.01 section 8.1.3 # 0 */ case GEN_STARTADDRCOARSEOFS: /* SF2.01 section 8.1.3 # 4 */ if (voice->sample != NULL) { - voice->start = (voice->sample->start + x = (voice->sample->start + (int) _GEN(voice, GEN_STARTADDROFS) + 32768 * (int) _GEN(voice, GEN_STARTADDRCOARSEOFS)); - voice->check_sample_sanity_flag = FLUID_SAMPLESANITY_CHECK; + UPDATE_RVOICE1(fluid_rvoice_set_start, x); } break; case GEN_ENDADDROFS: /* SF2.01 section 8.1.3 # 1 */ case GEN_ENDADDRCOARSEOFS: /* SF2.01 section 8.1.3 # 12 */ if (voice->sample != NULL) { - voice->end = (voice->sample->end + x = (voice->sample->end + (int) _GEN(voice, GEN_ENDADDROFS) + 32768 * (int) _GEN(voice, GEN_ENDADDRCOARSEOFS)); - voice->check_sample_sanity_flag = FLUID_SAMPLESANITY_CHECK; + UPDATE_RVOICE1(fluid_rvoice_set_end, x); } break; case GEN_STARTLOOPADDROFS: /* SF2.01 section 8.1.3 # 2 */ case GEN_STARTLOOPADDRCOARSEOFS: /* SF2.01 section 8.1.3 # 45 */ if (voice->sample != NULL) { - voice->loopstart = (voice->sample->loopstart + x = (voice->sample->loopstart + (int) _GEN(voice, GEN_STARTLOOPADDROFS) + 32768 * (int) _GEN(voice, GEN_STARTLOOPADDRCOARSEOFS)); - voice->check_sample_sanity_flag = FLUID_SAMPLESANITY_CHECK; + UPDATE_RVOICE1(fluid_rvoice_set_loopstart, x); } break; case GEN_ENDLOOPADDROFS: /* SF2.01 section 8.1.3 # 3 */ case GEN_ENDLOOPADDRCOARSEOFS: /* SF2.01 section 8.1.3 # 50 */ if (voice->sample != NULL) { - voice->loopend = (voice->sample->loopend + x = (voice->sample->loopend + (int) _GEN(voice, GEN_ENDLOOPADDROFS) + 32768 * (int) _GEN(voice, GEN_ENDLOOPADDRCOARSEOFS)); - voice->check_sample_sanity_flag = FLUID_SAMPLESANITY_CHECK; + UPDATE_RVOICE1(fluid_rvoice_set_loopend, x); } break; @@ -1030,7 +831,7 @@ fluid_voice_update_param(fluid_voice_t* voice, int gen) x = _GEN(voice, GEN_VOLENVDELAY); fluid_clip(x, -12000.0f, 5000.0f); count = NUM_BUFFERS_DELAY(x); - fluid_adsr_env_set_data(&voice->volenv, FLUID_VOICE_ENVDELAY, + UPDATE_RVOICE_ENVDATA(volenv, FLUID_VOICE_ENVDELAY, count, 0.0f, 0.0f, -1.0f, 1.0f); break; @@ -1038,14 +839,14 @@ fluid_voice_update_param(fluid_voice_t* voice, int gen) x = _GEN(voice, GEN_VOLENVATTACK); fluid_clip(x, -12000.0f, 8000.0f); count = 1 + NUM_BUFFERS_ATTACK(x); - fluid_adsr_env_set_data(&voice->volenv, FLUID_VOICE_ENVATTACK, + UPDATE_RVOICE_ENVDATA(volenv, FLUID_VOICE_ENVATTACK, count, 1.0f, count ? 1.0f / count : 0.0f, -1.0f, 1.0f); break; case GEN_VOLENVHOLD: /* SF2.01 section 8.1.3 # 35 */ case GEN_KEYTOVOLENVHOLD: /* SF2.01 section 8.1.3 # 39 */ count = calculate_hold_decay_buffers(voice, GEN_VOLENVHOLD, GEN_KEYTOVOLENVHOLD, 0); /* 0 means: hold */ - fluid_adsr_env_set_data(&voice->volenv, FLUID_VOICE_ENVHOLD, + UPDATE_RVOICE_ENVDATA(volenv, FLUID_VOICE_ENVHOLD, count, 1.0f, 0.0f, -1.0f, 2.0f); break; @@ -1055,7 +856,7 @@ fluid_voice_update_param(fluid_voice_t* voice, int gen) y = 1.0f - 0.001f * _GEN(voice, GEN_VOLENVSUSTAIN); fluid_clip(y, 0.0f, 1.0f); count = calculate_hold_decay_buffers(voice, GEN_VOLENVDECAY, GEN_KEYTOVOLENVDECAY, 1); /* 1 for decay */ - fluid_adsr_env_set_data(&voice->volenv, FLUID_VOICE_ENVDECAY, + UPDATE_RVOICE_ENVDATA(volenv, FLUID_VOICE_ENVDECAY, count, 1.0f, count ? -1.0f / count : 0.0f, y, 2.0f); break; @@ -1063,7 +864,7 @@ fluid_voice_update_param(fluid_voice_t* voice, int gen) x = _GEN(voice, GEN_VOLENVRELEASE); fluid_clip(x, FLUID_MIN_VOLENVRELEASE, 8000.0f); count = 1 + NUM_BUFFERS_RELEASE(x); - fluid_adsr_env_set_data(&voice->volenv, FLUID_VOICE_ENVRELEASE, + UPDATE_RVOICE_ENVDATA(volenv, FLUID_VOICE_ENVRELEASE, count, 1.0f, count ? -1.0f / count : 0.0f, 0.0f, 1.0f); break; @@ -1071,7 +872,7 @@ fluid_voice_update_param(fluid_voice_t* voice, int gen) case GEN_MODENVDELAY: /* SF2.01 section 8.1.3 # 25 */ x = _GEN(voice, GEN_MODENVDELAY); fluid_clip(x, -12000.0f, 5000.0f); - fluid_adsr_env_set_data(&voice->modenv, FLUID_VOICE_ENVDELAY, + UPDATE_RVOICE_ENVDATA(modenv, FLUID_VOICE_ENVDELAY, NUM_BUFFERS_DELAY(x), 0.0f, 0.0f, -1.0f, 1.0f); break; @@ -1079,14 +880,14 @@ fluid_voice_update_param(fluid_voice_t* voice, int gen) x = _GEN(voice, GEN_MODENVATTACK); fluid_clip(x, -12000.0f, 8000.0f); count = 1 + NUM_BUFFERS_ATTACK(x); - fluid_adsr_env_set_data(&voice->modenv, FLUID_VOICE_ENVATTACK, + UPDATE_RVOICE_ENVDATA(modenv, FLUID_VOICE_ENVATTACK, count, 1.0f, count ? 1.0f / count : 0.0f, -1.0f, 1.0f); break; case GEN_MODENVHOLD: /* SF2.01 section 8.1.3 # 27 */ case GEN_KEYTOMODENVHOLD: /* SF2.01 section 8.1.3 # 31 */ count = calculate_hold_decay_buffers(voice, GEN_MODENVHOLD, GEN_KEYTOMODENVHOLD, 0); /* 1 means: hold */ - fluid_adsr_env_set_data(&voice->modenv, FLUID_VOICE_ENVHOLD, + UPDATE_RVOICE_ENVDATA(modenv, FLUID_VOICE_ENVHOLD, count, 1.0f, 0.0f, -1.0f, 2.0f); break; @@ -1096,7 +897,7 @@ fluid_voice_update_param(fluid_voice_t* voice, int gen) count = calculate_hold_decay_buffers(voice, GEN_MODENVDECAY, GEN_KEYTOMODENVDECAY, 1); /* 1 for decay */ y = 1.0f - 0.001f * _GEN(voice, GEN_MODENVSUSTAIN); fluid_clip(y, 0.0f, 1.0f); - fluid_adsr_env_set_data(&voice->modenv, FLUID_VOICE_ENVDECAY, + UPDATE_RVOICE_ENVDATA(modenv, FLUID_VOICE_ENVDECAY, count, 1.0f, count ? -1.0f / count : 0.0f, y, 2.0f); break; @@ -1104,7 +905,7 @@ fluid_voice_update_param(fluid_voice_t* voice, int gen) x = _GEN(voice, GEN_MODENVRELEASE); fluid_clip(x, -12000.0f, 8000.0f); count = 1 + NUM_BUFFERS_RELEASE(x); - fluid_adsr_env_set_data(&voice->modenv, FLUID_VOICE_ENVRELEASE, + UPDATE_RVOICE_ENVDATA(modenv, FLUID_VOICE_ENVRELEASE, count, 1.0f, count ? -1.0f / count : 0.0f, 0.0f, 2.0f); break; @@ -1230,35 +1031,13 @@ fluid_voice_noteoff(fluid_voice_t* voice) fluid_profile(FLUID_PROF_VOICE_NOTE, voice->ref); - at_tick = fluid_channel_get_min_note_length_ticks (voice->channel); - - if (at_tick > voice->ticks) { - /* Delay noteoff */ - voice->noteoff_ticks = at_tick; - return FLUID_OK; - } - voice->noteoff_ticks = 0; if (voice->channel && fluid_channel_sustained(voice->channel)) { voice->status = FLUID_VOICE_SUSTAINED; } else { - if (fluid_adsr_env_get_section(&voice->volenv) == FLUID_VOICE_ENVATTACK) { - /* A voice is turned off during the attack section of the volume - * envelope. The attack section ramps up linearly with - * amplitude. The other sections use logarithmic scaling. Calculate new - * volenv_val to achieve equievalent amplitude during the release phase - * for seamless volume transition. - */ - if (fluid_adsr_env_get_val(&voice->volenv) > 0){ - fluid_real_t lfo = fluid_lfo_get_val(&voice->modlfo) * -voice->modlfo_to_vol; - fluid_real_t amp = fluid_adsr_env_get_val(&voice->volenv) * pow (10.0, lfo / -200); - fluid_real_t env_value = - ((-200 * log (amp) / log (10.0) - lfo) / 960.0 - 1); - fluid_clip (env_value, 0.0, 1.0); - fluid_adsr_env_set_val(&voice->volenv, env_value); - } - } - fluid_adsr_env_set_section(&voice->volenv, FLUID_VOICE_ENVRELEASE); - fluid_adsr_env_set_section(&voice->modenv, FLUID_VOICE_ENVRELEASE); + at_tick = fluid_channel_get_min_note_length_ticks (voice->channel); + UPDATE_RVOICE1(fluid_rvoice_noteoff, at_tick); + voice->has_noteoff = 1; } return FLUID_OK; @@ -1275,9 +1054,12 @@ fluid_voice_noteoff(fluid_voice_t* voice) * or channel. fluid_voice_kill_excl gets called, when 'voice' is to * be killed for that reason. */ + int fluid_voice_kill_excl(fluid_voice_t* voice){ + unsigned int at_tick; + if (!_PLAYING(voice)) { return FLUID_OK; } @@ -1287,12 +1069,6 @@ fluid_voice_kill_excl(fluid_voice_t* voice){ */ fluid_voice_gen_set(voice, GEN_EXCLUSIVECLASS, 0); - /* If the voice is not yet in release state, put it into release state */ - if (fluid_adsr_env_get_section(&voice->volenv) != FLUID_VOICE_ENVRELEASE){ - fluid_adsr_env_set_section(&voice->volenv, FLUID_VOICE_ENVRELEASE); - fluid_adsr_env_set_section(&voice->modenv, FLUID_VOICE_ENVRELEASE); - } - /* Speed up the volume envelope */ /* The value was found through listening tests with hi-hat samples. */ fluid_voice_gen_set(voice, GEN_VOLENVRELEASE, -200); @@ -1302,6 +1078,10 @@ fluid_voice_kill_excl(fluid_voice_t* voice){ fluid_voice_gen_set(voice, GEN_MODENVRELEASE, -200); fluid_voice_update_param(voice, GEN_MODENVRELEASE); + at_tick = fluid_channel_get_min_note_length_ticks (voice->channel); + UPDATE_RVOICE1(fluid_rvoice_noteoff, at_tick); + + return FLUID_OK; } @@ -1318,9 +1098,10 @@ fluid_voice_off(fluid_voice_t* voice) fluid_profile(FLUID_PROF_VOICE_RELEASE, voice->ref); voice->chan = NO_CHANNEL; - fluid_adsr_env_set_section(&voice->volenv, FLUID_VOICE_ENVFINISHED); - fluid_adsr_env_set_section(&voice->modenv, FLUID_VOICE_ENVFINISHED); + UPDATE_RVOICE0(fluid_rvoice_voiceoff); + voice->status = FLUID_VOICE_OFF; + voice->has_noteoff = 1; /* Decrement the reference count of the sample. */ if (voice->sample) { @@ -1491,151 +1272,6 @@ fluid_voice_get_lower_boundary_for_attenuation(fluid_voice_t* voice) } -/* Purpose: - * - * Make sure, that sample start / end point and loop points are in - * proper order. When starting up, calculate the initial phase. - */ -static void -fluid_voice_check_sample_sanity(fluid_voice_t* voice) -{ - int min_index_nonloop=(int) voice->sample->start; - int max_index_nonloop=(int) voice->sample->end; - - /* make sure we have enough samples surrounding the loop */ - int min_index_loop=(int) voice->sample->start + FLUID_MIN_LOOP_PAD; - int max_index_loop=(int) voice->sample->end - FLUID_MIN_LOOP_PAD + 1; /* 'end' is last valid sample, loopend can be + 1 */ - fluid_check_fpe("voice_check_sample_sanity start"); - - if (!voice->check_sample_sanity_flag){ - return; - } - -#if 0 - printf("Sample from %i to %i\n",voice->sample->start, voice->sample->end); - printf("Sample loop from %i %i\n",voice->sample->loopstart, voice->sample->loopend); - printf("Playback from %i to %i\n", voice->start, voice->end); - printf("Playback loop from %i to %i\n",voice->loopstart, voice->loopend); -#endif - - /* Keep the start point within the sample data */ - if (voice->start < min_index_nonloop){ - voice->start = min_index_nonloop; - } else if (voice->start > max_index_nonloop){ - voice->start = max_index_nonloop; - } - - /* Keep the end point within the sample data */ - if (voice->end < min_index_nonloop){ - voice->end = min_index_nonloop; - } else if (voice->end > max_index_nonloop){ - voice->end = max_index_nonloop; - } - - /* Keep start and end point in the right order */ - if (voice->start > voice->end){ - int temp = voice->start; - voice->start = voice->end; - voice->end = temp; - /*FLUID_LOG(FLUID_DBG, "Loop / sample sanity check: Changing order of start / end points!"); */ - } - - /* Zero length? */ - if (voice->start == voice->end){ - fluid_voice_off(voice); - return; - } - - if ((_SAMPLEMODE(voice) == FLUID_LOOP_UNTIL_RELEASE) - || (_SAMPLEMODE(voice) == FLUID_LOOP_DURING_RELEASE)) { - /* Keep the loop start point within the sample data */ - if (voice->loopstart < min_index_loop){ - voice->loopstart = min_index_loop; - } else if (voice->loopstart > max_index_loop){ - voice->loopstart = max_index_loop; - } - - /* Keep the loop end point within the sample data */ - if (voice->loopend < min_index_loop){ - voice->loopend = min_index_loop; - } else if (voice->loopend > max_index_loop){ - voice->loopend = max_index_loop; - } - - /* Keep loop start and end point in the right order */ - if (voice->loopstart > voice->loopend){ - int temp = voice->loopstart; - voice->loopstart = voice->loopend; - voice->loopend = temp; - /*FLUID_LOG(FLUID_DBG, "Loop / sample sanity check: Changing order of loop points!"); */ - } - - /* Loop too short? Then don't loop. */ - if (voice->loopend < voice->loopstart + FLUID_MIN_LOOP_SIZE){ - voice->gen[GEN_SAMPLEMODE].val = FLUID_UNLOOPED; - } - - /* The loop points may have changed. Obtain a new estimate for the loop volume. */ - /* Is the voice loop within the sample loop? */ - if ((int)voice->loopstart >= (int)voice->sample->loopstart - && (int)voice->loopend <= (int)voice->sample->loopend){ - /* Is there a valid peak amplitude available for the loop? */ - if (voice->sample->amplitude_that_reaches_noise_floor_is_valid){ - voice->amplitude_that_reaches_noise_floor_loop=voice->sample->amplitude_that_reaches_noise_floor / voice->synth_gain; - } else { - /* Worst case */ - voice->amplitude_that_reaches_noise_floor_loop=voice->amplitude_that_reaches_noise_floor_nonloop; - }; - }; - - } /* if sample mode is looped */ - - /* Run startup specific code (only once, when the voice is started) */ - if (voice->check_sample_sanity_flag & FLUID_SAMPLESANITY_STARTUP){ - if (max_index_loop - min_index_loop < FLUID_MIN_LOOP_SIZE){ - if ((_SAMPLEMODE(voice) == FLUID_LOOP_UNTIL_RELEASE) - || (_SAMPLEMODE(voice) == FLUID_LOOP_DURING_RELEASE)){ - voice->gen[GEN_SAMPLEMODE].val = FLUID_UNLOOPED; - } - } - - /* Set the initial phase of the voice (using the result from the - start offset modulators). */ - fluid_phase_set_int(voice->phase, voice->start); - } /* if startup */ - - /* Is this voice run in loop mode, or does it run straight to the - end of the waveform data? */ - if (((_SAMPLEMODE(voice) == FLUID_LOOP_UNTIL_RELEASE) && - (fluid_adsr_env_get_section(&voice->volenv) < FLUID_VOICE_ENVRELEASE)) - || (_SAMPLEMODE(voice) == FLUID_LOOP_DURING_RELEASE)) { - /* Yes, it will loop as soon as it reaches the loop point. In - * this case we must prevent, that the playback pointer (phase) - * happens to end up beyond the 2nd loop point, because the - * point has moved. The DSP algorithm is unable to cope with - * that situation. So if the phase is beyond the 2nd loop - * point, set it to the start of the loop. No way to avoid some - * noise here. Note: If the sample pointer ends up -before the - * first loop point- instead, then the DSP loop will just play - * the sample, enter the loop and proceed as expected => no - * actions required. - */ - int index_in_sample = fluid_phase_index(voice->phase); - if (index_in_sample >= voice->loopend){ - /* FLUID_LOG(FLUID_DBG, "Loop / sample sanity check: Phase after 2nd loop point!"); */ - fluid_phase_set_int(voice->phase, voice->loopstart); - } - } -/* FLUID_LOG(FLUID_DBG, "Loop / sample sanity check: Sample from %i to %i, loop from %i to %i", voice->start, voice->end, voice->loopstart, voice->loopend); */ - - /* Sample sanity has been assured. Don't check again, until some - sample parameter is changed by modulation. */ - voice->check_sample_sanity_flag=0; -#if 0 - printf("Sane? playback loop from %i to %i\n", voice->loopstart, voice->loopend); -#endif - fluid_check_fpe("voice_check_sample_sanity"); -} int fluid_voice_set_param(fluid_voice_t* voice, int gen, fluid_real_t nrpn_value, int abs) @@ -1659,6 +1295,12 @@ int fluid_voice_set_gain(fluid_voice_t* voice, fluid_real_t gain) voice->amp_reverb = voice->reverb_send * gain / 32768.0f; voice->amp_chorus = voice->chorus_send * gain / 32768.0f; + UPDATE_RVOICE1(fluid_rvoice_set_synth_gain, gain); + UPDATE_RVOICE2(fluid_rvoice_set_buf_amp, 0, voice->amp_left); + UPDATE_RVOICE2(fluid_rvoice_set_buf_amp, 1, voice->amp_right); + UPDATE_RVOICE2(fluid_rvoice_set_buf_amp, 2, voice->amp_reverb); + UPDATE_RVOICE2(fluid_rvoice_set_buf_amp, 3, voice->amp_chorus); + return FLUID_OK; } diff --git a/fluidsynth/src/fluid_voice.h b/fluidsynth/src/fluid_voice.h index ba2757ac..a0ded2ca 100644 --- a/fluidsynth/src/fluid_voice.h +++ b/fluidsynth/src/fluid_voice.h @@ -28,6 +28,7 @@ #include "fluid_iir_filter.h" #include "fluid_adsr_env.h" #include "fluid_lfo.h" +#include "fluid_rvoice.h" #define NO_CHANNEL 0xff @@ -49,97 +50,29 @@ struct _fluid_voice_t it's used for noteoff's */ unsigned char status; unsigned char chan; /* the channel number, quick access for channel messages */ - unsigned char key; /* the key, quick acces for noteoff */ + unsigned char key; /* the key, quick access for noteoff */ unsigned char vel; /* the velocity */ fluid_channel_t* channel; fluid_gen_t gen[GEN_LAST]; fluid_mod_t mod[FLUID_NUM_MOD]; int mod_count; - int has_looped; /* Flag that is set as soon as the first loop is completed. */ - fluid_sample_t* sample; - int check_sample_sanity_flag; /* Flag that initiates, that sample-related parameters - have to be checked. */ -#if 0 - /* Instead of keeping a pointer to a fluid_sample_t structure, - * I think it would be better to copy the sample data in the - * voice structure. SoundFont loader then do not have to - * allocate and maintain the fluid_sample_t structure. [PH] - * - * The notify callback may be used also for streaming samples. - */ - short* sample_data; /* pointer to the sample data */ - int sample_data_offset; /* the offset of data[0] in the whole sample */ - int sample_data_length; /* the length of the data array */ - unsigned int sample_start; - unsigned int sample_end; - unsigned int sample_loopstart; - unsigned int sample_loopend; - unsigned int sample_rate; - int sample_origpitch; - int sample_pitchadj; - int sample_type; - int (*sample_notify)(fluid_voice_t* voice, int reason); - void* sample_userdata; -#endif + fluid_sample_t* sample; /* Pointer to sample (dupe in rvoice) */ + + int has_noteoff; /* Flag set when noteoff has been sent */ /* basic parameters */ - fluid_real_t output_rate; /* the sample rate of the synthesizer */ + fluid_real_t output_rate; /* the sample rate of the synthesizer (dupe in rvoice) */ unsigned int start_time; - unsigned int ticks; - unsigned int noteoff_ticks; /* Delay note-off until this tick */ - - fluid_real_t amp; /* current linear amplitude */ - fluid_phase_t phase; /* the phase of the sample wave */ - - /* Temporary variables used in fluid_voice_write() */ - - fluid_real_t phase_incr; /* the phase increment for the next 64 samples */ - fluid_real_t amp_incr; /* amplitude increment value */ - fluid_real_t *dsp_buf; /* buffer to store interpolated sample data to */ - int dsp_buf_count; /* Number of audio samples in dsp_buf */ - - /* End temporary variables */ /* basic parameters */ - fluid_real_t pitch; /* the pitch in midicents */ - fluid_real_t attenuation; /* the attenuation in centibels */ - fluid_real_t min_attenuation_cB; /* Estimate on the smallest possible attenuation - * during the lifetime of the voice */ - fluid_real_t root_pitch, root_pitch_hz; - + fluid_real_t pitch; /* the pitch in midicents (dupe in rvoice) */ + fluid_real_t attenuation; /* the attenuation in centibels (dupe in rvoice) */ + fluid_real_t root_pitch; - /* sample and loop start and end points (offset in sample memory). */ - int start; - int end; - int loopstart; - int loopend; /* Note: first point following the loop (superimposed on loopstart) */ - - /* master gain */ + /* master gain (dupe in rvoice) */ fluid_real_t synth_gain; - /* vol env */ - fluid_adsr_env_t volenv; - fluid_real_t amplitude_that_reaches_noise_floor_nonloop; - fluid_real_t amplitude_that_reaches_noise_floor_loop; - - /* mod env */ - fluid_adsr_env_t modenv; - fluid_real_t modenv_to_fc; - fluid_real_t modenv_to_pitch; - - /* mod lfo */ - fluid_lfo_t modlfo; - fluid_real_t modlfo_to_fc; - fluid_real_t modlfo_to_pitch; - fluid_real_t modlfo_to_vol; - - /* vib lfo */ - fluid_lfo_t viblfo; - fluid_real_t viblfo_to_pitch; - - fluid_iir_filter_t resonant_filter; /* IIR resonance dsp filter */ - /* pan */ fluid_real_t pan; fluid_real_t amp_left; @@ -153,8 +86,9 @@ struct _fluid_voice_t fluid_real_t chorus_send; fluid_real_t amp_chorus; - /* interpolation method, as in fluid_interp in fluidsynth.h */ - int interp_method; + /* rvoice control */ + fluid_rvoice_t* rvoice; + int can_access_rvoice; /* False if rvoice is being rendered in separate thread */ /* for debugging */ int debug; @@ -193,9 +127,10 @@ void fluid_voice_update_param(fluid_voice_t* voice, int gen); int fluid_voice_noteoff(fluid_voice_t* voice); int fluid_voice_off(fluid_voice_t* voice); -void fluid_voice_mix (fluid_voice_t *voice, - fluid_real_t* left_buf, fluid_real_t* right_buf, - fluid_real_t* reverb_buf, fluid_real_t* chorus_buf); +void fluid_voice_mix (fluid_voice_t *voice, int count, fluid_real_t* dsp_buf, + fluid_real_t* left_buf, fluid_real_t* right_buf, + fluid_real_t* reverb_buf, fluid_real_t* chorus_buf); + int fluid_voice_kill_excl(fluid_voice_t* voice); #define fluid_voice_get_channel(voice) ((voice)->channel) @@ -210,7 +145,7 @@ int fluid_voice_kill_excl(fluid_voice_t* voice); /* A voice is 'ON', if it has not yet received a noteoff * event. Sending a noteoff event will advance the envelopes to * section 5 (release). */ -#define _ON(voice) ((voice)->status == FLUID_VOICE_ON && fluid_adsr_env_get_section(&voice->volenv) < FLUID_VOICE_ENVRELEASE) +#define _ON(voice) ((voice)->status == FLUID_VOICE_ON && !voice->has_noteoff) #define _SUSTAINED(voice) ((voice)->status == FLUID_VOICE_SUSTAINED) #define _AVAILABLE(voice) (((voice)->status == FLUID_VOICE_CLEAN) || ((voice)->status == FLUID_VOICE_OFF)) #define _RELEASED(voice) ((voice)->chan == NO_CHANNEL) @@ -227,10 +162,6 @@ fluid_real_t fluid_voice_gen_value(fluid_voice_t* voice, int num); + (fluid_real_t)(_voice)->gen[_n].mod \ + (fluid_real_t)(_voice)->gen[_n].nrpn) -#define FLUID_SAMPLESANITY_CHECK (1 << 0) -#define FLUID_SAMPLESANITY_STARTUP (1 << 1) - - /* defined in fluid_dsp_float.c */ void fluid_dsp_float_config (void); diff --git a/fluidsynth/src/rvoice/fluid_rvoice.c b/fluidsynth/src/rvoice/fluid_rvoice.c new file mode 100644 index 00000000..9be63cb2 --- /dev/null +++ b/fluidsynth/src/rvoice/fluid_rvoice.c @@ -0,0 +1,644 @@ +/* FluidSynth - A Software Synthesizer + * + * Copyright (C) 2003 Peter Hanappe and others. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public License + * as published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the Free + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + * 02111-1307, USA + */ + +#include "fluid_rvoice.h" +#include "fluid_conv.h" +#include "fluid_sys.h" + +/** + * @return -1 if voice has finished, 0 if it's currently quiet, 1 otherwise + */ +static inline int +fluid_rvoice_calc_amp(fluid_rvoice_t* voice) +{ + fluid_real_t target_amp; /* target amplitude */ + + if (fluid_adsr_env_get_section(&voice->envlfo.volenv) == FLUID_VOICE_ENVDELAY) + return -1; /* The volume amplitude is in hold phase. No sound is produced. */ + + if (fluid_adsr_env_get_section(&voice->envlfo.volenv) == FLUID_VOICE_ENVATTACK) + { + /* the envelope is in the attack section: ramp linearly to max value. + * A positive modlfo_to_vol should increase volume (negative attenuation). + */ + target_amp = fluid_atten2amp (voice->dsp.attenuation) + * fluid_cb2amp (fluid_lfo_get_val(&voice->envlfo.modlfo) * -voice->envlfo.modlfo_to_vol) + * fluid_adsr_env_get_val(&voice->envlfo.volenv); + } + else + { + fluid_real_t amplitude_that_reaches_noise_floor; + fluid_real_t amp_max; + + target_amp = fluid_atten2amp (voice->dsp.attenuation) + * fluid_cb2amp (960.0f * (1.0f - fluid_adsr_env_get_val(&voice->envlfo.volenv)) + + fluid_lfo_get_val(&voice->envlfo.modlfo) * -voice->envlfo.modlfo_to_vol); + + /* We turn off a voice, if the volume has dropped low enough. */ + + /* A voice can be turned off, when an estimate for the volume + * (upper bound) falls below that volume, that will drop the + * sample below the noise floor. + */ + + /* If the loop amplitude is known, we can use it if the voice loop is within + * the sample loop + */ + + /* Is the playing pointer already in the loop? */ + if (voice->dsp.has_looped) + amplitude_that_reaches_noise_floor = voice->dsp.amplitude_that_reaches_noise_floor_loop; + else + amplitude_that_reaches_noise_floor = voice->dsp.amplitude_that_reaches_noise_floor_nonloop; + + /* voice->attenuation_min is a lower boundary for the attenuation + * now and in the future (possibly 0 in the worst case). Now the + * amplitude of sample and volenv cannot exceed amp_max (since + * volenv_val can only drop): + */ + + amp_max = fluid_atten2amp (voice->dsp.min_attenuation_cB) * + fluid_adsr_env_get_val(&voice->envlfo.volenv); + + /* And if amp_max is already smaller than the known amplitude, + * which will attenuate the sample below the noise floor, then we + * can safely turn off the voice. Duh. */ + if (amp_max < amplitude_that_reaches_noise_floor) + { + return 0; + } + } + + /* Volume increment to go from voice->amp to target_amp in FLUID_BUFSIZE steps */ + voice->dsp.amp_incr = (target_amp - voice->dsp.amp) / FLUID_BUFSIZE; + + fluid_check_fpe ("voice_write amplitude calculation"); + + /* no volume and not changing? - No need to process */ + if ((voice->dsp.amp == 0.0f) && (voice->dsp.amp_incr == 0.0f)) + return -1; + + return 1; +} + + +/* these should be the absolute minimum that FluidSynth can deal with */ +#define FLUID_MIN_LOOP_SIZE 2 +#define FLUID_MIN_LOOP_PAD 0 + +#define FLUID_SAMPLESANITY_CHECK (1 << 0) +#define FLUID_SAMPLESANITY_STARTUP (1 << 1) + +/* Purpose: + * + * Make sure, that sample start / end point and loop points are in + * proper order. When starting up, calculate the initial phase. + * TODO: Investigate whether this can be moved from rvoice to voice. + */ +static void +fluid_rvoice_check_sample_sanity(fluid_rvoice_t* voice) +{ + int min_index_nonloop=(int) voice->dsp.sample->start; + int max_index_nonloop=(int) voice->dsp.sample->end; + + /* make sure we have enough samples surrounding the loop */ + int min_index_loop=(int) voice->dsp.sample->start + FLUID_MIN_LOOP_PAD; + int max_index_loop=(int) voice->dsp.sample->end - FLUID_MIN_LOOP_PAD + 1; /* 'end' is last valid sample, loopend can be + 1 */ + fluid_check_fpe("voice_check_sample_sanity start"); + + if (!voice->dsp.check_sample_sanity_flag){ + return; + } + +#if 0 + printf("Sample from %i to %i\n",voice->dsp.sample->start, voice->dsp.sample->end); + printf("Sample loop from %i %i\n",voice->dsp.sample->loopstart, voice->dsp.sample->loopend); + printf("Playback from %i to %i\n", voice->dsp.start, voice->dsp.end); + printf("Playback loop from %i to %i\n",voice->dsp.loopstart, voice->dsp.loopend); +#endif + + /* Keep the start point within the sample data */ + if (voice->dsp.start < min_index_nonloop){ + voice->dsp.start = min_index_nonloop; + } else if (voice->dsp.start > max_index_nonloop){ + voice->dsp.start = max_index_nonloop; + } + + /* Keep the end point within the sample data */ + if (voice->dsp.end < min_index_nonloop){ + voice->dsp.end = min_index_nonloop; + } else if (voice->dsp.end > max_index_nonloop){ + voice->dsp.end = max_index_nonloop; + } + + /* Keep start and end point in the right order */ + if (voice->dsp.start > voice->dsp.end){ + int temp = voice->dsp.start; + voice->dsp.start = voice->dsp.end; + voice->dsp.end = temp; + /*FLUID_LOG(FLUID_DBG, "Loop / sample sanity check: Changing order of start / end points!"); */ + } + + /* Zero length? */ + if (voice->dsp.start == voice->dsp.end){ + fluid_rvoice_voiceoff(voice); + return; + } + + if ((voice->dsp.samplemode == FLUID_LOOP_UNTIL_RELEASE) + || (voice->dsp.samplemode == FLUID_LOOP_DURING_RELEASE)) { + /* Keep the loop start point within the sample data */ + if (voice->dsp.loopstart < min_index_loop){ + voice->dsp.loopstart = min_index_loop; + } else if (voice->dsp.loopstart > max_index_loop){ + voice->dsp.loopstart = max_index_loop; + } + + /* Keep the loop end point within the sample data */ + if (voice->dsp.loopend < min_index_loop){ + voice->dsp.loopend = min_index_loop; + } else if (voice->dsp.loopend > max_index_loop){ + voice->dsp.loopend = max_index_loop; + } + + /* Keep loop start and end point in the right order */ + if (voice->dsp.loopstart > voice->dsp.loopend){ + int temp = voice->dsp.loopstart; + voice->dsp.loopstart = voice->dsp.loopend; + voice->dsp.loopend = temp; + /*FLUID_LOG(FLUID_DBG, "Loop / sample sanity check: Changing order of loop points!"); */ + } + + /* Loop too short? Then don't loop. */ + if (voice->dsp.loopend < voice->dsp.loopstart + FLUID_MIN_LOOP_SIZE){ + voice->dsp.samplemode = FLUID_UNLOOPED; + } + + /* The loop points may have changed. Obtain a new estimate for the loop volume. */ + /* Is the voice loop within the sample loop? */ + if ((int)voice->dsp.loopstart >= (int)voice->dsp.sample->loopstart + && (int)voice->dsp.loopend <= (int)voice->dsp.sample->loopend){ + /* Is there a valid peak amplitude available for the loop? */ + if (voice->dsp.sample->amplitude_that_reaches_noise_floor_is_valid){ + voice->dsp.amplitude_that_reaches_noise_floor_loop=voice->dsp.sample->amplitude_that_reaches_noise_floor / voice->dsp.synth_gain; + } else { + /* Worst case */ + voice->dsp.amplitude_that_reaches_noise_floor_loop=voice->dsp.amplitude_that_reaches_noise_floor_nonloop; + }; + }; + + } /* if sample mode is looped */ + + /* Run startup specific code (only once, when the voice is started) */ + if (voice->dsp.check_sample_sanity_flag & FLUID_SAMPLESANITY_STARTUP){ + if (max_index_loop - min_index_loop < FLUID_MIN_LOOP_SIZE){ + if ((voice->dsp.samplemode == FLUID_LOOP_UNTIL_RELEASE) + || (voice->dsp.samplemode == FLUID_LOOP_DURING_RELEASE)){ + voice->dsp.samplemode = FLUID_UNLOOPED; + } + } + + /* Set the initial phase of the voice (using the result from the + start offset modulators). */ + fluid_phase_set_int(voice->dsp.phase, voice->dsp.start); + } /* if startup */ + + /* Is this voice run in loop mode, or does it run straight to the + end of the waveform data? */ + if (((voice->dsp.samplemode == FLUID_LOOP_UNTIL_RELEASE) && + (fluid_adsr_env_get_section(&voice->envlfo.volenv) < FLUID_VOICE_ENVRELEASE)) + || (voice->dsp.samplemode == FLUID_LOOP_DURING_RELEASE)) { + /* Yes, it will loop as soon as it reaches the loop point. In + * this case we must prevent, that the playback pointer (phase) + * happens to end up beyond the 2nd loop point, because the + * point has moved. The DSP algorithm is unable to cope with + * that situation. So if the phase is beyond the 2nd loop + * point, set it to the start of the loop. No way to avoid some + * noise here. Note: If the sample pointer ends up -before the + * first loop point- instead, then the DSP loop will just play + * the sample, enter the loop and proceed as expected => no + * actions required. + */ + int index_in_sample = fluid_phase_index(voice->dsp.phase); + if (index_in_sample >= voice->dsp.loopend){ + /* FLUID_LOG(FLUID_DBG, "Loop / sample sanity check: Phase after 2nd loop point!"); */ + fluid_phase_set_int(voice->dsp.phase, voice->dsp.loopstart); + } + } +/* FLUID_LOG(FLUID_DBG, "Loop / sample sanity check: Sample from %i to %i, loop from %i to %i", voice->dsp.start, voice->dsp.end, voice->dsp.loopstart, voice->dsp.loopend); */ + + /* Sample sanity has been assured. Don't check again, until some + sample parameter is changed by modulation. */ + voice->dsp.check_sample_sanity_flag=0; +#if 0 + printf("Sane? playback loop from %i to %i\n", voice->dsp.loopstart, voice->dsp.loopend); +#endif + fluid_check_fpe("voice_check_sample_sanity"); +} + + +/** + * Synthesize a voice to a buffer. + * + * @param voice rvoice to synthesize + * @param dsp_buf Audio buffer to synthesize to (#FLUID_BUFSIZE in length) + * @return Count of samples written to dsp_buf. (-1 means voice is currently + * quiet, 0 .. #FLUID_BUFSIZE-1 means voice finished.) + * + * Panning, reverb and chorus are processed separately. The dsp interpolation + * routine is in (fluid_dsp_float.c). + */ +int +fluid_rvoice_write (fluid_rvoice_t* voice, fluid_real_t *dsp_buf) +{ + int ticks = voice->envlfo.ticks; + int count; + + /******************* sample sanity check **********/ + + if (voice->dsp.check_sample_sanity_flag) + fluid_rvoice_check_sample_sanity(voice); + + /******************* noteoff check ****************/ + + if (voice->envlfo.noteoff_ticks != 0 && + voice->envlfo.ticks >= voice->envlfo.noteoff_ticks) { + fluid_rvoice_noteoff(voice, 0); + } + + voice->envlfo.ticks += FLUID_BUFSIZE; + + /******************* vol env **********************/ + + fluid_adsr_env_calc(&voice->envlfo.volenv, 1); + fluid_check_fpe ("voice_write vol env"); + if (fluid_adsr_env_get_section(&voice->envlfo.volenv) == FLUID_VOICE_ENVFINISHED) + return 0; + + /******************* mod env **********************/ + + fluid_adsr_env_calc(&voice->envlfo.modenv, 0); + fluid_check_fpe ("voice_write mod env"); + + /******************* lfo **********************/ + + fluid_lfo_calc(&voice->envlfo.modlfo, ticks); + fluid_check_fpe ("voice_write mod LFO"); + fluid_lfo_calc(&voice->envlfo.viblfo, ticks); + fluid_check_fpe ("voice_write vib LFO"); + + /******************* amplitude **********************/ + + count = fluid_rvoice_calc_amp(voice); + if (count <= 0) + return count; + + /******************* phase **********************/ + + /* Calculate the number of samples, that the DSP loop advances + * through the original waveform with each step in the output + * buffer. It is the ratio between the frequencies of original + * waveform and output waveform.*/ + voice->dsp.phase_incr = fluid_ct2hz_real(voice->dsp.pitch + + fluid_lfo_get_val(&voice->envlfo.modlfo) * voice->envlfo.modlfo_to_pitch + + fluid_lfo_get_val(&voice->envlfo.viblfo) * voice->envlfo.viblfo_to_pitch + + fluid_adsr_env_get_val(&voice->envlfo.modenv) * voice->envlfo.modenv_to_pitch) + / voice->dsp.root_pitch_hz; + + fluid_check_fpe ("voice_write phase calculation"); + + /* if phase_incr is not advancing, set it to the minimum fraction value (prevent stuckage) */ + if (voice->dsp.phase_incr == 0) voice->dsp.phase_incr = 1; + + voice->dsp.is_looping = voice->dsp.samplemode == FLUID_LOOP_DURING_RELEASE + || (voice->dsp.samplemode == FLUID_LOOP_UNTIL_RELEASE + && fluid_adsr_env_get_section(&voice->envlfo.volenv) < FLUID_VOICE_ENVRELEASE); + + /*********************** run the dsp chain ************************ + * The sample is mixed with the output buffer. + * The buffer has to be filled from 0 to FLUID_BUFSIZE-1. + * Depending on the position in the loop and the loop size, this + * may require several runs. */ + voice->dsp.dsp_buf = dsp_buf; + + switch (voice->dsp.interp_method) + { + case FLUID_INTERP_NONE: + count = fluid_rvoice_dsp_interpolate_none (&voice->dsp); + break; + case FLUID_INTERP_LINEAR: + count = fluid_rvoice_dsp_interpolate_linear (&voice->dsp); + break; + case FLUID_INTERP_4THORDER: + default: + count = fluid_rvoice_dsp_interpolate_4th_order (&voice->dsp); + break; + case FLUID_INTERP_7THORDER: + count = fluid_rvoice_dsp_interpolate_7th_order (&voice->dsp); + break; + } + fluid_check_fpe ("voice_write interpolation"); + if (count == 0) + return count; + + /*************** resonant filter ******************/ + fluid_iir_filter_calc(&voice->resonant_filter, voice->dsp.output_rate, + fluid_lfo_get_val(&voice->envlfo.modlfo) * voice->envlfo.modlfo_to_fc + + fluid_adsr_env_get_val(&voice->envlfo.modenv) * voice->envlfo.modenv_to_fc); + + fluid_iir_filter_apply(&voice->resonant_filter, dsp_buf, count); + + return count; +} + + +/** + * Mix data down to buffers + * + * @param buffers Destination buffer(s) + * @param dsp_buf Mono sample source + * @param count Number of samples to process + */ +void +fluid_rvoice_buffers_mix(fluid_rvoice_buffers_t* buffers, + fluid_real_t* dsp_buf, int count) +{ + int bufcount = buffers->count; + int i, dsp_i; + + for (i=0; i < bufcount; i++) { + fluid_real_t amp = buffers->bufs[i].amp; + fluid_real_t* buf = buffers->bufs[i].buf; + if (buf == NULL || amp == 0.0f) + continue; + + /* Optimization for centered stereo samples - we can save one + multiplication per sample */ + if (i < bufcount-1 && buffers->bufs[i+1].buf != NULL && + buffers->bufs[i+1].amp == amp) { + fluid_real_t* next_buf = buffers->bufs[i+1].buf; + for (dsp_i = 0; dsp_i < count; dsp_i++) { + fluid_real_t samp = amp * dsp_buf[dsp_i]; + buf[dsp_i] += samp; + next_buf[dsp_i] += samp; + } + i++; + } + else { + for (dsp_i = 0; dsp_i < count; dsp_i++) + buf[dsp_i] += amp * dsp_buf[dsp_i]; + } + } +} + +/** + * Synthesize a voice to several buffers. + * + * @param voice rvoice to synthesize + * @return Count of samples written to the buffers. (-1 means voice is currently + * quiet, 0 .. #FLUID_BUFSIZE-1 means voice finished.) + */ +int +fluid_rvoice_render(fluid_rvoice_t* voice) +{ + fluid_real_t dsp_buf[FLUID_BUFSIZE]; + int result = fluid_rvoice_write(voice, dsp_buf); + if (result > 0) + fluid_rvoice_buffers_mix(&voice->buffers, dsp_buf, result); + return result; +} + +void +fluid_rvoice_reset(fluid_rvoice_t* voice) +{ + voice->dsp.has_looped = 0; + voice->envlfo.ticks = 0; + voice->envlfo.noteoff_ticks = 0; + voice->dsp.amp = 0.0f; /* The last value of the volume envelope, used to + calculate the volume increment during + processing */ + + /* mod env initialization*/ + fluid_adsr_env_reset(&voice->envlfo.modenv); + + /* vol env initialization */ + fluid_adsr_env_reset(&voice->envlfo.volenv); + + /* Fixme: Retrieve from any other existing + voice on this channel to keep LFOs in + unison? */ + fluid_lfo_reset(&voice->envlfo.viblfo); + fluid_lfo_reset(&voice->envlfo.modlfo); + + /* Clear sample history in filter */ + fluid_iir_filter_reset(&voice->resonant_filter); + + /* Force setting of the phase at the first DSP loop run + * This cannot be done earlier, because it depends on modulators. + [DH] Is that comment really true? */ + voice->dsp.check_sample_sanity_flag |= FLUID_SAMPLESANITY_STARTUP; +} + + +void +fluid_rvoice_noteoff(fluid_rvoice_t* voice, unsigned int min_ticks) +{ + if (min_ticks > voice->envlfo.ticks) { + /* Delay noteoff */ + voice->envlfo.noteoff_ticks = min_ticks; + return; + } + voice->envlfo.noteoff_ticks = 0; + + if (fluid_adsr_env_get_section(&voice->envlfo.volenv) == FLUID_VOICE_ENVATTACK) { + /* A voice is turned off during the attack section of the volume + * envelope. The attack section ramps up linearly with + * amplitude. The other sections use logarithmic scaling. Calculate new + * volenv_val to achieve equievalent amplitude during the release phase + * for seamless volume transition. + */ + if (fluid_adsr_env_get_val(&voice->envlfo.volenv) > 0){ + fluid_real_t lfo = fluid_lfo_get_val(&voice->envlfo.modlfo) * -voice->envlfo.modlfo_to_vol; + fluid_real_t amp = fluid_adsr_env_get_val(&voice->envlfo.volenv) * pow (10.0, lfo / -200); + fluid_real_t env_value = - ((-200 * log (amp) / log (10.0) - lfo) / 960.0 - 1); + fluid_clip (env_value, 0.0, 1.0); + fluid_adsr_env_set_val(&voice->envlfo.volenv, env_value); + } + } + fluid_adsr_env_set_section(&voice->envlfo.volenv, FLUID_VOICE_ENVRELEASE); + fluid_adsr_env_set_section(&voice->envlfo.modenv, FLUID_VOICE_ENVRELEASE); +} + + +void +fluid_rvoice_set_output_rate(fluid_rvoice_t* voice, fluid_real_t value) +{ + voice->dsp.output_rate = value; +} + +void +fluid_rvoice_set_interp_method(fluid_rvoice_t* voice, int value) +{ + voice->dsp.interp_method = value; +} + +void +fluid_rvoice_set_root_pitch_hz(fluid_rvoice_t* voice, fluid_real_t value) +{ + voice->dsp.root_pitch_hz = value; +} + +void +fluid_rvoice_set_pitch(fluid_rvoice_t* voice, fluid_real_t value) +{ + voice->dsp.pitch = value; +} + + +void +fluid_rvoice_set_attenuation(fluid_rvoice_t* voice, fluid_real_t value) +{ + voice->dsp.attenuation = value; +} + +void +fluid_rvoice_set_min_attenuation_cB(fluid_rvoice_t* voice, fluid_real_t value) +{ + voice->dsp.min_attenuation_cB = value; +} + +void +fluid_rvoice_set_buf_amp(fluid_rvoice_t* voice, unsigned int bufnum, fluid_real_t value) +{ + if (bufnum >= FLUID_RVOICE_MAX_BUFS) return; /* Safety net */ + if (voice->buffers.count <= bufnum) + voice->buffers.count = bufnum+1; + voice->buffers.bufs[bufnum].amp = value; +} + +void +fluid_rvoice_set_viblfo_to_pitch(fluid_rvoice_t* voice, fluid_real_t value) +{ + voice->envlfo.viblfo_to_pitch = value; +} + +void fluid_rvoice_set_modlfo_to_pitch(fluid_rvoice_t* voice, fluid_real_t value) +{ + voice->envlfo.modlfo_to_pitch = value; +} + +void +fluid_rvoice_set_modlfo_to_vol(fluid_rvoice_t* voice, fluid_real_t value) +{ + voice->envlfo.modlfo_to_vol = value; +} + +void +fluid_rvoice_set_modlfo_to_fc(fluid_rvoice_t* voice, fluid_real_t value) +{ + voice->envlfo.modlfo_to_fc = value; +} + +void +fluid_rvoice_set_modenv_to_fc(fluid_rvoice_t* voice, fluid_real_t value) +{ + voice->envlfo.modenv_to_fc = value; +} + +void +fluid_rvoice_set_modenv_to_pitch(fluid_rvoice_t* voice, fluid_real_t value) +{ + voice->envlfo.modenv_to_pitch = value; +} + +void +fluid_rvoice_set_synth_gain(fluid_rvoice_t* voice, fluid_real_t value) +{ + voice->dsp.synth_gain = value; + + /* For a looped sample, this value will be overwritten as soon as the + * loop parameters are initialized (they may depend on modulators). + * This value can be kept, it is a worst-case estimate. + */ + voice->dsp.amplitude_that_reaches_noise_floor_nonloop = FLUID_NOISE_FLOOR / value; + voice->dsp.amplitude_that_reaches_noise_floor_loop = FLUID_NOISE_FLOOR / value; + voice->dsp.check_sample_sanity_flag |= FLUID_SAMPLESANITY_CHECK; +} + +void +fluid_rvoice_set_start(fluid_rvoice_t* voice, int value) +{ + voice->dsp.start = value; + voice->dsp.check_sample_sanity_flag |= FLUID_SAMPLESANITY_CHECK; +} + +void +fluid_rvoice_set_end(fluid_rvoice_t* voice, int value) +{ + voice->dsp.end = value; + voice->dsp.check_sample_sanity_flag |= FLUID_SAMPLESANITY_CHECK; +} + +void +fluid_rvoice_set_loopstart(fluid_rvoice_t* voice, int value) +{ + voice->dsp.loopstart = value; + voice->dsp.check_sample_sanity_flag |= FLUID_SAMPLESANITY_CHECK; +} + +void fluid_rvoice_set_loopend(fluid_rvoice_t* voice, int value) +{ + voice->dsp.loopend = value; + voice->dsp.check_sample_sanity_flag |= FLUID_SAMPLESANITY_CHECK; +} + +void fluid_rvoice_set_samplemode(fluid_rvoice_t* voice, enum fluid_loop value) +{ + voice->dsp.samplemode = value; + voice->dsp.check_sample_sanity_flag |= FLUID_SAMPLESANITY_CHECK; +} + + +void +fluid_rvoice_set_sample(fluid_rvoice_t* voice, fluid_sample_t* value) +{ + if (voice->dsp.sample) { + fluid_sample_decr_ref(voice->dsp.sample); + voice->dsp.sample = NULL; + } + if (value) { + voice->dsp.sample = value; + fluid_sample_incr_ref(voice->dsp.sample); + voice->dsp.check_sample_sanity_flag |= FLUID_SAMPLESANITY_STARTUP; + } +} + +void +fluid_rvoice_voiceoff(fluid_rvoice_t* voice) +{ + fluid_adsr_env_set_section(&voice->envlfo.volenv, FLUID_VOICE_ENVFINISHED); + fluid_adsr_env_set_section(&voice->envlfo.modenv, FLUID_VOICE_ENVFINISHED); + if (voice->dsp.sample) { + fluid_sample_decr_ref(voice->dsp.sample); + voice->dsp.sample = NULL; + } +} + + diff --git a/fluidsynth/src/rvoice/fluid_rvoice.h b/fluidsynth/src/rvoice/fluid_rvoice.h new file mode 100644 index 00000000..205ea208 --- /dev/null +++ b/fluidsynth/src/rvoice/fluid_rvoice.h @@ -0,0 +1,196 @@ +/* FluidSynth - A Software Synthesizer + * + * Copyright (C) 2003 Peter Hanappe and others. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public License + * as published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the Free + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + * 02111-1307, USA + */ + + +#ifndef _FLUID_RVOICE_H +#define _FLUID_RVOICE_H + +#include "fluidsynth_priv.h" +#include "fluid_iir_filter.h" +#include "fluid_adsr_env.h" +#include "fluid_lfo.h" +#include "fluid_phase.h" +#include "fluid_sfont.h" + +typedef struct _fluid_rvoice_envlfo_t fluid_rvoice_envlfo_t; +typedef struct _fluid_rvoice_dsp_t fluid_rvoice_dsp_t; +typedef struct _fluid_rvoice_buffers_t fluid_rvoice_buffers_t; +typedef struct _fluid_rvoice_t fluid_rvoice_t; + +/* Smallest amplitude that can be perceived (full scale is +/- 0.5) + * 16 bits => 96+4=100 dB dynamic range => 0.00001 + * 0.00001 * 2 is approximately 0.00003 :) + */ +#define FLUID_NOISE_FLOOR 0.00003 + + +enum fluid_loop { + FLUID_UNLOOPED = 0, + FLUID_LOOP_DURING_RELEASE = 1, + FLUID_NOTUSED = 2, + FLUID_LOOP_UNTIL_RELEASE = 3 +}; + +/** + * rvoice ticks-based parameters + * These parameters must be updated even if the voice is currently quiet. + */ +struct _fluid_rvoice_envlfo_t +{ + /* Note-off minimum length */ + unsigned int ticks; + unsigned int noteoff_ticks; + + /* vol env */ + fluid_adsr_env_t volenv; + + /* mod env */ + fluid_adsr_env_t modenv; + fluid_real_t modenv_to_fc; + fluid_real_t modenv_to_pitch; + + /* mod lfo */ + fluid_lfo_t modlfo; + fluid_real_t modlfo_to_fc; + fluid_real_t modlfo_to_pitch; + fluid_real_t modlfo_to_vol; + + /* vib lfo */ + fluid_lfo_t viblfo; + fluid_real_t viblfo_to_pitch; +}; + +/** + * rvoice parameters needed for dsp interpolation + */ +struct _fluid_rvoice_dsp_t +{ + /* interpolation method, as in fluid_interp in fluidsynth.h */ + int interp_method; + fluid_sample_t* sample; + int check_sample_sanity_flag; /* Flag that initiates, that sample-related parameters + have to be checked. */ + + /* sample and loop start and end points (offset in sample memory). */ + int start; + int end; + int loopstart; + int loopend; /* Note: first point following the loop (superimposed on loopstart) */ + enum fluid_loop samplemode; + + /* Stuff needed for phase calculations */ + + fluid_real_t pitch; /* the pitch in midicents */ + fluid_real_t root_pitch_hz; + fluid_real_t output_rate; + + /* Stuff needed for amplitude calculations */ + + int has_looped; /* Flag that is set as soon as the first loop is completed. */ + fluid_real_t attenuation; /* the attenuation in centibels */ + fluid_real_t min_attenuation_cB; /* Estimate on the smallest possible attenuation + * during the lifetime of the voice */ + fluid_real_t amplitude_that_reaches_noise_floor_nonloop; + fluid_real_t amplitude_that_reaches_noise_floor_loop; + fluid_real_t synth_gain; /* master gain */ + + + /* Dynamic input to the interpolator below */ + + fluid_real_t *dsp_buf; /* buffer to store interpolated sample data to */ + + fluid_real_t amp; /* current linear amplitude */ + fluid_real_t amp_incr; /* amplitude increment value for the next FLUID_BUFSIZE samples */ + + fluid_phase_t phase; /* the phase (current sample offset) of the sample wave */ + fluid_real_t phase_incr; /* the phase increment for the next FLUID_BUFSIZE samples */ + int is_looping; + +}; + +/* Currently left, right, reverb, chorus. To be changed if we + ever add surround positioning, or stereo reverb/chorus */ +#define FLUID_RVOICE_MAX_BUFS (4) + +/** + * rvoice mixer-related parameters + */ +struct _fluid_rvoice_buffers_t +{ + unsigned int count; /* Number of buffers */ + struct { + fluid_real_t amp; + fluid_real_t* buf; + } bufs[FLUID_RVOICE_MAX_BUFS]; +}; + + +/** + * Parameters needed to synthesize a voice + */ +struct _fluid_rvoice_t +{ + fluid_rvoice_envlfo_t envlfo; + fluid_rvoice_dsp_t dsp; + fluid_iir_filter_t resonant_filter; /* IIR resonant dsp filter */ + fluid_rvoice_buffers_t buffers; +}; + + +int fluid_rvoice_write (fluid_rvoice_t* voice, fluid_real_t *dsp_buf); +void fluid_rvoice_buffers_mix(fluid_rvoice_buffers_t* buffers, + fluid_real_t* dsp_buf, int count); +int fluid_rvoice_render(fluid_rvoice_t* voice); + +/* Dynamic update functions */ + +void fluid_rvoice_noteoff(fluid_rvoice_t* voice, unsigned int min_ticks); +void fluid_rvoice_voiceoff(fluid_rvoice_t* voice); +void fluid_rvoice_reset(fluid_rvoice_t* voice); +void fluid_rvoice_set_output_rate(fluid_rvoice_t* voice, fluid_real_t output_rate); +void fluid_rvoice_set_interp_method(fluid_rvoice_t* voice, int interp_method); +void fluid_rvoice_set_root_pitch_hz(fluid_rvoice_t* voice, fluid_real_t root_pitch_hz); +void fluid_rvoice_set_pitch(fluid_rvoice_t* voice, fluid_real_t value); +void fluid_rvoice_set_synth_gain(fluid_rvoice_t* voice, fluid_real_t value); +void fluid_rvoice_set_attenuation(fluid_rvoice_t* voice, fluid_real_t value); +void fluid_rvoice_set_min_attenuation_cB(fluid_rvoice_t* voice, fluid_real_t value); +void fluid_rvoice_set_buf_amp(fluid_rvoice_t* voice, unsigned int bufnum, fluid_real_t value); +void fluid_rvoice_set_viblfo_to_pitch(fluid_rvoice_t* voice, fluid_real_t value); +void fluid_rvoice_set_modlfo_to_pitch(fluid_rvoice_t* voice, fluid_real_t value); +void fluid_rvoice_set_modlfo_to_vol(fluid_rvoice_t* voice, fluid_real_t value); +void fluid_rvoice_set_modlfo_to_fc(fluid_rvoice_t* voice, fluid_real_t value); +void fluid_rvoice_set_modenv_to_fc(fluid_rvoice_t* voice, fluid_real_t value); +void fluid_rvoice_set_modenv_to_pitch(fluid_rvoice_t* voice, fluid_real_t value); +void fluid_rvoice_set_start(fluid_rvoice_t* voice, int value); +void fluid_rvoice_set_end(fluid_rvoice_t* voice, int value); +void fluid_rvoice_set_loopstart(fluid_rvoice_t* voice, int value); +void fluid_rvoice_set_loopend(fluid_rvoice_t* voice, int value); +void fluid_rvoice_set_sample(fluid_rvoice_t* voice, fluid_sample_t* value); +void fluid_rvoice_set_samplemode(fluid_rvoice_t* voice, enum fluid_loop value); + +/* defined in fluid_rvoice_dsp.c */ + +void fluid_rvoice_dsp_config (void); +int fluid_rvoice_dsp_interpolate_none (fluid_rvoice_dsp_t *voice); +int fluid_rvoice_dsp_interpolate_linear (fluid_rvoice_dsp_t *voice); +int fluid_rvoice_dsp_interpolate_4th_order (fluid_rvoice_dsp_t *voice); +int fluid_rvoice_dsp_interpolate_7th_order (fluid_rvoice_dsp_t *voice); + +#endif diff --git a/fluidsynth/src/rvoice/fluid_rvoice_dsp.c b/fluidsynth/src/rvoice/fluid_rvoice_dsp.c new file mode 100644 index 00000000..26f81b24 --- /dev/null +++ b/fluidsynth/src/rvoice/fluid_rvoice_dsp.c @@ -0,0 +1,675 @@ +/* FluidSynth - A Software Synthesizer + * + * Copyright (C) 2003 Peter Hanappe and others. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public License + * as published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the Free + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + * 02111-1307, USA + */ + +#include "fluidsynth_priv.h" +#include "fluid_phase.h" +#include "fluid_rvoice.h" +#include "fluid_sys.h" + +/* Purpose: + * + * Interpolates audio data (obtains values between the samples of the original + * waveform data). + * + * Variables loaded from the voice structure (assigned in fluid_voice_write()): + * - dsp_data: Pointer to the original waveform data + * - dsp_phase: The position in the original waveform data. + * This has an integer and a fractional part (between samples). + * - dsp_phase_incr: For each output sample, the position in the original + * waveform advances by dsp_phase_incr. This also has an integer + * part and a fractional part. + * If a sample is played at root pitch (no pitch change), + * dsp_phase_incr is integer=1 and fractional=0. + * - dsp_amp: The current amplitude envelope value. + * - dsp_amp_incr: The changing rate of the amplitude envelope. + * + * A couple of variables are used internally, their results are discarded: + * - dsp_i: Index through the output buffer + * - dsp_buf: Output buffer of floating point values (FLUID_BUFSIZE in length) + */ + +/* Interpolation (find a value between two samples of the original waveform) */ + +/* Linear interpolation table (2 coefficients centered on 1st) */ +static fluid_real_t interp_coeff_linear[FLUID_INTERP_MAX][2]; + +/* 4th order (cubic) interpolation table (4 coefficients centered on 2nd) */ +static fluid_real_t interp_coeff[FLUID_INTERP_MAX][4]; + +/* 7th order interpolation (7 coefficients centered on 3rd) */ +static fluid_real_t sinc_table7[FLUID_INTERP_MAX][7]; + + +#define SINC_INTERP_ORDER 7 /* 7th order constant */ + + +/* Initializes interpolation tables */ +void fluid_rvoice_dsp_config (void) +{ + int i, i2; + double x, v; + double i_shifted; + + /* Initialize the coefficients for the interpolation. The math comes + * from a mail, posted by Olli Niemitalo to the music-dsp mailing + * list (I found it in the music-dsp archives + * http://www.smartelectronix.com/musicdsp/). */ + + for (i = 0; i < FLUID_INTERP_MAX; i++) + { + x = (double) i / (double) FLUID_INTERP_MAX; + + interp_coeff[i][0] = (fluid_real_t)(x * (-0.5 + x * (1 - 0.5 * x))); + interp_coeff[i][1] = (fluid_real_t)(1.0 + x * x * (1.5 * x - 2.5)); + interp_coeff[i][2] = (fluid_real_t)(x * (0.5 + x * (2.0 - 1.5 * x))); + interp_coeff[i][3] = (fluid_real_t)(0.5 * x * x * (x - 1.0)); + + interp_coeff_linear[i][0] = (fluid_real_t)(1.0 - x); + interp_coeff_linear[i][1] = (fluid_real_t)x; + } + + /* i: Offset in terms of whole samples */ + for (i = 0; i < SINC_INTERP_ORDER; i++) + { /* i2: Offset in terms of fractional samples ('subsamples') */ + for (i2 = 0; i2 < FLUID_INTERP_MAX; i2++) + { + /* center on middle of table */ + i_shifted = (double)i - ((double)SINC_INTERP_ORDER / 2.0) + + (double)i2 / (double)FLUID_INTERP_MAX; + + /* sinc(0) cannot be calculated straightforward (limit needed for 0/0) */ + if (fabs (i_shifted) > 0.000001) + { + v = (fluid_real_t)sin (i_shifted * M_PI) / (M_PI * i_shifted); + /* Hamming window */ + v *= (fluid_real_t)0.5 * (1.0 + cos (2.0 * M_PI * i_shifted / (fluid_real_t)SINC_INTERP_ORDER)); + } + else v = 1.0; + + sinc_table7[FLUID_INTERP_MAX - i2 - 1][i] = v; + } + } + +#if 0 + for (i = 0; i < FLUID_INTERP_MAX; i++) + { + printf ("%d %0.3f %0.3f %0.3f %0.3f %0.3f %0.3f %0.3f\n", + i, sinc_table7[0][i], sinc_table7[1][i], sinc_table7[2][i], + sinc_table7[3][i], sinc_table7[4][i], sinc_table7[5][i], sinc_table7[6][i]); + } +#endif + + fluid_check_fpe("interpolation table calculation"); +} + +/* No interpolation. Just take the sample, which is closest to + * the playback pointer. Questionable quality, but very + * efficient. */ +int +fluid_rvoice_dsp_interpolate_none (fluid_rvoice_dsp_t *voice) +{ + fluid_phase_t dsp_phase = voice->phase; + fluid_phase_t dsp_phase_incr; + short int *dsp_data = voice->sample->data; + fluid_real_t *dsp_buf = voice->dsp_buf; + fluid_real_t dsp_amp = voice->amp; + fluid_real_t dsp_amp_incr = voice->amp_incr; + unsigned int dsp_i = 0; + unsigned int dsp_phase_index; + unsigned int end_index; + int looping; + + /* Convert playback "speed" floating point value to phase index/fract */ + fluid_phase_set_float (dsp_phase_incr, voice->phase_incr); + + /* voice is currently looping? */ + looping = voice->is_looping; + + end_index = looping ? voice->loopend - 1 : voice->end; + + while (1) + { + dsp_phase_index = fluid_phase_index_round (dsp_phase); /* round to nearest point */ + + /* interpolate sequence of sample points */ + for ( ; dsp_i < FLUID_BUFSIZE && dsp_phase_index <= end_index; dsp_i++) + { + dsp_buf[dsp_i] = dsp_amp * dsp_data[dsp_phase_index]; + + /* increment phase and amplitude */ + fluid_phase_incr (dsp_phase, dsp_phase_incr); + dsp_phase_index = fluid_phase_index_round (dsp_phase); /* round to nearest point */ + dsp_amp += dsp_amp_incr; + } + + /* break out if not looping (buffer may not be full) */ + if (!looping) break; + + /* go back to loop start */ + if (dsp_phase_index > end_index) + { + fluid_phase_sub_int (dsp_phase, voice->loopend - voice->loopstart); + voice->has_looped = 1; + } + + /* break out if filled buffer */ + if (dsp_i >= FLUID_BUFSIZE) break; + } + + voice->phase = dsp_phase; + voice->amp = dsp_amp; + + return (dsp_i); +} + +/* Straight line interpolation. + * Returns number of samples processed (usually FLUID_BUFSIZE but could be + * smaller if end of sample occurs). + */ +int +fluid_rvoice_dsp_interpolate_linear (fluid_rvoice_dsp_t *voice) +{ + fluid_phase_t dsp_phase = voice->phase; + fluid_phase_t dsp_phase_incr; + short int *dsp_data = voice->sample->data; + fluid_real_t *dsp_buf = voice->dsp_buf; + fluid_real_t dsp_amp = voice->amp; + fluid_real_t dsp_amp_incr = voice->amp_incr; + unsigned int dsp_i = 0; + unsigned int dsp_phase_index; + unsigned int end_index; + short int point; + fluid_real_t *coeffs; + int looping; + + /* Convert playback "speed" floating point value to phase index/fract */ + fluid_phase_set_float (dsp_phase_incr, voice->phase_incr); + + /* voice is currently looping? */ + looping = voice->is_looping; + + /* last index before 2nd interpolation point must be specially handled */ + end_index = (looping ? voice->loopend - 1 : voice->end) - 1; + + /* 2nd interpolation point to use at end of loop or sample */ + if (looping) point = dsp_data[voice->loopstart]; /* loop start */ + else point = dsp_data[voice->end]; /* duplicate end for samples no longer looping */ + + while (1) + { + dsp_phase_index = fluid_phase_index (dsp_phase); + + /* interpolate the sequence of sample points */ + for ( ; dsp_i < FLUID_BUFSIZE && dsp_phase_index <= end_index; dsp_i++) + { + coeffs = interp_coeff_linear[fluid_phase_fract_to_tablerow (dsp_phase)]; + dsp_buf[dsp_i] = dsp_amp * (coeffs[0] * dsp_data[dsp_phase_index] + + coeffs[1] * dsp_data[dsp_phase_index+1]); + + /* increment phase and amplitude */ + fluid_phase_incr (dsp_phase, dsp_phase_incr); + dsp_phase_index = fluid_phase_index (dsp_phase); + dsp_amp += dsp_amp_incr; + } + + /* break out if buffer filled */ + if (dsp_i >= FLUID_BUFSIZE) break; + + end_index++; /* we're now interpolating the last point */ + + /* interpolate within last point */ + for (; dsp_phase_index <= end_index && dsp_i < FLUID_BUFSIZE; dsp_i++) + { + coeffs = interp_coeff_linear[fluid_phase_fract_to_tablerow (dsp_phase)]; + dsp_buf[dsp_i] = dsp_amp * (coeffs[0] * dsp_data[dsp_phase_index] + + coeffs[1] * point); + + /* increment phase and amplitude */ + fluid_phase_incr (dsp_phase, dsp_phase_incr); + dsp_phase_index = fluid_phase_index (dsp_phase); + dsp_amp += dsp_amp_incr; /* increment amplitude */ + } + + if (!looping) break; /* break out if not looping (end of sample) */ + + /* go back to loop start (if past */ + if (dsp_phase_index > end_index) + { + fluid_phase_sub_int (dsp_phase, voice->loopend - voice->loopstart); + voice->has_looped = 1; + } + + /* break out if filled buffer */ + if (dsp_i >= FLUID_BUFSIZE) break; + + end_index--; /* set end back to second to last sample point */ + } + + voice->phase = dsp_phase; + voice->amp = dsp_amp; + + return (dsp_i); +} + +/* 4th order (cubic) interpolation. + * Returns number of samples processed (usually FLUID_BUFSIZE but could be + * smaller if end of sample occurs). + */ +int +fluid_rvoice_dsp_interpolate_4th_order (fluid_rvoice_dsp_t *voice) +{ + fluid_phase_t dsp_phase = voice->phase; + fluid_phase_t dsp_phase_incr; + short int *dsp_data = voice->sample->data; + fluid_real_t *dsp_buf = voice->dsp_buf; + fluid_real_t dsp_amp = voice->amp; + fluid_real_t dsp_amp_incr = voice->amp_incr; + unsigned int dsp_i = 0; + unsigned int dsp_phase_index; + unsigned int start_index, end_index; + short int start_point, end_point1, end_point2; + fluid_real_t *coeffs; + int looping; + + /* Convert playback "speed" floating point value to phase index/fract */ + fluid_phase_set_float (dsp_phase_incr, voice->phase_incr); + + /* voice is currently looping? */ + looping = voice->is_looping; + + /* last index before 4th interpolation point must be specially handled */ + end_index = (looping ? voice->loopend - 1 : voice->end) - 2; + + if (voice->has_looped) /* set start_index and start point if looped or not */ + { + start_index = voice->loopstart; + start_point = dsp_data[voice->loopend - 1]; /* last point in loop (wrap around) */ + } + else + { + start_index = voice->start; + start_point = dsp_data[voice->start]; /* just duplicate the point */ + } + + /* get points off the end (loop start if looping, duplicate point if end) */ + if (looping) + { + end_point1 = dsp_data[voice->loopstart]; + end_point2 = dsp_data[voice->loopstart + 1]; + } + else + { + end_point1 = dsp_data[voice->end]; + end_point2 = end_point1; + } + + while (1) + { + dsp_phase_index = fluid_phase_index (dsp_phase); + + /* interpolate first sample point (start or loop start) if needed */ + for ( ; dsp_phase_index == start_index && dsp_i < FLUID_BUFSIZE; dsp_i++) + { + coeffs = interp_coeff[fluid_phase_fract_to_tablerow (dsp_phase)]; + dsp_buf[dsp_i] = dsp_amp * (coeffs[0] * start_point + + coeffs[1] * dsp_data[dsp_phase_index] + + coeffs[2] * dsp_data[dsp_phase_index+1] + + coeffs[3] * dsp_data[dsp_phase_index+2]); + + /* increment phase and amplitude */ + fluid_phase_incr (dsp_phase, dsp_phase_incr); + dsp_phase_index = fluid_phase_index (dsp_phase); + dsp_amp += dsp_amp_incr; + } + + /* interpolate the sequence of sample points */ + for ( ; dsp_i < FLUID_BUFSIZE && dsp_phase_index <= end_index; dsp_i++) + { + coeffs = interp_coeff[fluid_phase_fract_to_tablerow (dsp_phase)]; + dsp_buf[dsp_i] = dsp_amp * (coeffs[0] * dsp_data[dsp_phase_index-1] + + coeffs[1] * dsp_data[dsp_phase_index] + + coeffs[2] * dsp_data[dsp_phase_index+1] + + coeffs[3] * dsp_data[dsp_phase_index+2]); + + /* increment phase and amplitude */ + fluid_phase_incr (dsp_phase, dsp_phase_incr); + dsp_phase_index = fluid_phase_index (dsp_phase); + dsp_amp += dsp_amp_incr; + } + + /* break out if buffer filled */ + if (dsp_i >= FLUID_BUFSIZE) break; + + end_index++; /* we're now interpolating the 2nd to last point */ + + /* interpolate within 2nd to last point */ + for (; dsp_phase_index <= end_index && dsp_i < FLUID_BUFSIZE; dsp_i++) + { + coeffs = interp_coeff[fluid_phase_fract_to_tablerow (dsp_phase)]; + dsp_buf[dsp_i] = dsp_amp * (coeffs[0] * dsp_data[dsp_phase_index-1] + + coeffs[1] * dsp_data[dsp_phase_index] + + coeffs[2] * dsp_data[dsp_phase_index+1] + + coeffs[3] * end_point1); + + /* increment phase and amplitude */ + fluid_phase_incr (dsp_phase, dsp_phase_incr); + dsp_phase_index = fluid_phase_index (dsp_phase); + dsp_amp += dsp_amp_incr; + } + + end_index++; /* we're now interpolating the last point */ + + /* interpolate within the last point */ + for (; dsp_phase_index <= end_index && dsp_i < FLUID_BUFSIZE; dsp_i++) + { + coeffs = interp_coeff[fluid_phase_fract_to_tablerow (dsp_phase)]; + dsp_buf[dsp_i] = dsp_amp * (coeffs[0] * dsp_data[dsp_phase_index-1] + + coeffs[1] * dsp_data[dsp_phase_index] + + coeffs[2] * end_point1 + + coeffs[3] * end_point2); + + /* increment phase and amplitude */ + fluid_phase_incr (dsp_phase, dsp_phase_incr); + dsp_phase_index = fluid_phase_index (dsp_phase); + dsp_amp += dsp_amp_incr; + } + + if (!looping) break; /* break out if not looping (end of sample) */ + + /* go back to loop start */ + if (dsp_phase_index > end_index) + { + fluid_phase_sub_int (dsp_phase, voice->loopend - voice->loopstart); + + if (!voice->has_looped) + { + voice->has_looped = 1; + start_index = voice->loopstart; + start_point = dsp_data[voice->loopend - 1]; + } + } + + /* break out if filled buffer */ + if (dsp_i >= FLUID_BUFSIZE) break; + + end_index -= 2; /* set end back to third to last sample point */ + } + + voice->phase = dsp_phase; + voice->amp = dsp_amp; + + return (dsp_i); +} + +/* 7th order interpolation. + * Returns number of samples processed (usually FLUID_BUFSIZE but could be + * smaller if end of sample occurs). + */ +int +fluid_rvoice_dsp_interpolate_7th_order (fluid_rvoice_dsp_t *voice) +{ + fluid_phase_t dsp_phase = voice->phase; + fluid_phase_t dsp_phase_incr; + short int *dsp_data = voice->sample->data; + fluid_real_t *dsp_buf = voice->dsp_buf; + fluid_real_t dsp_amp = voice->amp; + fluid_real_t dsp_amp_incr = voice->amp_incr; + unsigned int dsp_i = 0; + unsigned int dsp_phase_index; + unsigned int start_index, end_index; + short int start_points[3]; + short int end_points[3]; + fluid_real_t *coeffs; + int looping; + + /* Convert playback "speed" floating point value to phase index/fract */ + fluid_phase_set_float (dsp_phase_incr, voice->phase_incr); + + /* add 1/2 sample to dsp_phase since 7th order interpolation is centered on + * the 4th sample point */ + fluid_phase_incr (dsp_phase, (fluid_phase_t)0x80000000); + + /* voice is currently looping? */ + looping = voice->is_looping; + + /* last index before 7th interpolation point must be specially handled */ + end_index = (looping ? voice->loopend - 1 : voice->end) - 3; + + if (voice->has_looped) /* set start_index and start point if looped or not */ + { + start_index = voice->loopstart; + start_points[0] = dsp_data[voice->loopend - 1]; + start_points[1] = dsp_data[voice->loopend - 2]; + start_points[2] = dsp_data[voice->loopend - 3]; + } + else + { + start_index = voice->start; + start_points[0] = dsp_data[voice->start]; /* just duplicate the start point */ + start_points[1] = start_points[0]; + start_points[2] = start_points[0]; + } + + /* get the 3 points off the end (loop start if looping, duplicate point if end) */ + if (looping) + { + end_points[0] = dsp_data[voice->loopstart]; + end_points[1] = dsp_data[voice->loopstart + 1]; + end_points[2] = dsp_data[voice->loopstart + 2]; + } + else + { + end_points[0] = dsp_data[voice->end]; + end_points[1] = end_points[0]; + end_points[2] = end_points[0]; + } + + while (1) + { + dsp_phase_index = fluid_phase_index (dsp_phase); + + /* interpolate first sample point (start or loop start) if needed */ + for ( ; dsp_phase_index == start_index && dsp_i < FLUID_BUFSIZE; dsp_i++) + { + coeffs = sinc_table7[fluid_phase_fract_to_tablerow (dsp_phase)]; + + dsp_buf[dsp_i] = dsp_amp + * (coeffs[0] * (fluid_real_t)start_points[2] + + coeffs[1] * (fluid_real_t)start_points[1] + + coeffs[2] * (fluid_real_t)start_points[0] + + coeffs[3] * (fluid_real_t)dsp_data[dsp_phase_index] + + coeffs[4] * (fluid_real_t)dsp_data[dsp_phase_index+1] + + coeffs[5] * (fluid_real_t)dsp_data[dsp_phase_index+2] + + coeffs[6] * (fluid_real_t)dsp_data[dsp_phase_index+3]); + + /* increment phase and amplitude */ + fluid_phase_incr (dsp_phase, dsp_phase_incr); + dsp_phase_index = fluid_phase_index (dsp_phase); + dsp_amp += dsp_amp_incr; + } + + start_index++; + + /* interpolate 2nd to first sample point (start or loop start) if needed */ + for ( ; dsp_phase_index == start_index && dsp_i < FLUID_BUFSIZE; dsp_i++) + { + coeffs = sinc_table7[fluid_phase_fract_to_tablerow (dsp_phase)]; + + dsp_buf[dsp_i] = dsp_amp + * (coeffs[0] * (fluid_real_t)start_points[1] + + coeffs[1] * (fluid_real_t)start_points[0] + + coeffs[2] * (fluid_real_t)dsp_data[dsp_phase_index-1] + + coeffs[3] * (fluid_real_t)dsp_data[dsp_phase_index] + + coeffs[4] * (fluid_real_t)dsp_data[dsp_phase_index+1] + + coeffs[5] * (fluid_real_t)dsp_data[dsp_phase_index+2] + + coeffs[6] * (fluid_real_t)dsp_data[dsp_phase_index+3]); + + /* increment phase and amplitude */ + fluid_phase_incr (dsp_phase, dsp_phase_incr); + dsp_phase_index = fluid_phase_index (dsp_phase); + dsp_amp += dsp_amp_incr; + } + + start_index++; + + /* interpolate 3rd to first sample point (start or loop start) if needed */ + for ( ; dsp_phase_index == start_index && dsp_i < FLUID_BUFSIZE; dsp_i++) + { + coeffs = sinc_table7[fluid_phase_fract_to_tablerow (dsp_phase)]; + + dsp_buf[dsp_i] = dsp_amp + * (coeffs[0] * (fluid_real_t)start_points[0] + + coeffs[1] * (fluid_real_t)dsp_data[dsp_phase_index-2] + + coeffs[2] * (fluid_real_t)dsp_data[dsp_phase_index-1] + + coeffs[3] * (fluid_real_t)dsp_data[dsp_phase_index] + + coeffs[4] * (fluid_real_t)dsp_data[dsp_phase_index+1] + + coeffs[5] * (fluid_real_t)dsp_data[dsp_phase_index+2] + + coeffs[6] * (fluid_real_t)dsp_data[dsp_phase_index+3]); + + /* increment phase and amplitude */ + fluid_phase_incr (dsp_phase, dsp_phase_incr); + dsp_phase_index = fluid_phase_index (dsp_phase); + dsp_amp += dsp_amp_incr; + } + + start_index -= 2; /* set back to original start index */ + + + /* interpolate the sequence of sample points */ + for ( ; dsp_i < FLUID_BUFSIZE && dsp_phase_index <= end_index; dsp_i++) + { + coeffs = sinc_table7[fluid_phase_fract_to_tablerow (dsp_phase)]; + + dsp_buf[dsp_i] = dsp_amp + * (coeffs[0] * (fluid_real_t)dsp_data[dsp_phase_index-3] + + coeffs[1] * (fluid_real_t)dsp_data[dsp_phase_index-2] + + coeffs[2] * (fluid_real_t)dsp_data[dsp_phase_index-1] + + coeffs[3] * (fluid_real_t)dsp_data[dsp_phase_index] + + coeffs[4] * (fluid_real_t)dsp_data[dsp_phase_index+1] + + coeffs[5] * (fluid_real_t)dsp_data[dsp_phase_index+2] + + coeffs[6] * (fluid_real_t)dsp_data[dsp_phase_index+3]); + + /* increment phase and amplitude */ + fluid_phase_incr (dsp_phase, dsp_phase_incr); + dsp_phase_index = fluid_phase_index (dsp_phase); + dsp_amp += dsp_amp_incr; + } + + /* break out if buffer filled */ + if (dsp_i >= FLUID_BUFSIZE) break; + + end_index++; /* we're now interpolating the 3rd to last point */ + + /* interpolate within 3rd to last point */ + for (; dsp_phase_index <= end_index && dsp_i < FLUID_BUFSIZE; dsp_i++) + { + coeffs = sinc_table7[fluid_phase_fract_to_tablerow (dsp_phase)]; + + dsp_buf[dsp_i] = dsp_amp + * (coeffs[0] * (fluid_real_t)dsp_data[dsp_phase_index-3] + + coeffs[1] * (fluid_real_t)dsp_data[dsp_phase_index-2] + + coeffs[2] * (fluid_real_t)dsp_data[dsp_phase_index-1] + + coeffs[3] * (fluid_real_t)dsp_data[dsp_phase_index] + + coeffs[4] * (fluid_real_t)dsp_data[dsp_phase_index+1] + + coeffs[5] * (fluid_real_t)dsp_data[dsp_phase_index+2] + + coeffs[6] * (fluid_real_t)end_points[0]); + + /* increment phase and amplitude */ + fluid_phase_incr (dsp_phase, dsp_phase_incr); + dsp_phase_index = fluid_phase_index (dsp_phase); + dsp_amp += dsp_amp_incr; + } + + end_index++; /* we're now interpolating the 2nd to last point */ + + /* interpolate within 2nd to last point */ + for (; dsp_phase_index <= end_index && dsp_i < FLUID_BUFSIZE; dsp_i++) + { + coeffs = sinc_table7[fluid_phase_fract_to_tablerow (dsp_phase)]; + + dsp_buf[dsp_i] = dsp_amp + * (coeffs[0] * (fluid_real_t)dsp_data[dsp_phase_index-3] + + coeffs[1] * (fluid_real_t)dsp_data[dsp_phase_index-2] + + coeffs[2] * (fluid_real_t)dsp_data[dsp_phase_index-1] + + coeffs[3] * (fluid_real_t)dsp_data[dsp_phase_index] + + coeffs[4] * (fluid_real_t)dsp_data[dsp_phase_index+1] + + coeffs[5] * (fluid_real_t)end_points[0] + + coeffs[6] * (fluid_real_t)end_points[1]); + + /* increment phase and amplitude */ + fluid_phase_incr (dsp_phase, dsp_phase_incr); + dsp_phase_index = fluid_phase_index (dsp_phase); + dsp_amp += dsp_amp_incr; + } + + end_index++; /* we're now interpolating the last point */ + + /* interpolate within last point */ + for (; dsp_phase_index <= end_index && dsp_i < FLUID_BUFSIZE; dsp_i++) + { + coeffs = sinc_table7[fluid_phase_fract_to_tablerow (dsp_phase)]; + + dsp_buf[dsp_i] = dsp_amp + * (coeffs[0] * (fluid_real_t)dsp_data[dsp_phase_index-3] + + coeffs[1] * (fluid_real_t)dsp_data[dsp_phase_index-2] + + coeffs[2] * (fluid_real_t)dsp_data[dsp_phase_index-1] + + coeffs[3] * (fluid_real_t)dsp_data[dsp_phase_index] + + coeffs[4] * (fluid_real_t)end_points[0] + + coeffs[5] * (fluid_real_t)end_points[1] + + coeffs[6] * (fluid_real_t)end_points[2]); + + /* increment phase and amplitude */ + fluid_phase_incr (dsp_phase, dsp_phase_incr); + dsp_phase_index = fluid_phase_index (dsp_phase); + dsp_amp += dsp_amp_incr; + } + + if (!looping) break; /* break out if not looping (end of sample) */ + + /* go back to loop start */ + if (dsp_phase_index > end_index) + { + fluid_phase_sub_int (dsp_phase, voice->loopend - voice->loopstart); + + if (!voice->has_looped) + { + voice->has_looped = 1; + start_index = voice->loopstart; + start_points[0] = dsp_data[voice->loopend - 1]; + start_points[1] = dsp_data[voice->loopend - 2]; + start_points[2] = dsp_data[voice->loopend - 3]; + } + } + + /* break out if filled buffer */ + if (dsp_i >= FLUID_BUFSIZE) break; + + end_index -= 3; /* set end back to 4th to last sample point */ + } + + /* sub 1/2 sample from dsp_phase since 7th order interpolation is centered on + * the 4th sample point (correct back to real value) */ + fluid_phase_decr (dsp_phase, (fluid_phase_t)0x80000000); + + voice->phase = dsp_phase; + voice->amp = dsp_amp; + + return (dsp_i); +}