void fluid_iir_filter_calc(fluid_iir_filter_t* iir_filter, fluid_real_t output_rate, fluid_real_t fres_mod) { fluid_real_t fres; /* calculate the frequency of the resonant filter in Hz */ fres = fluid_ct2hz(iir_filter->fres + fres_mod); /* FIXME - Still potential for a click during turn on, can we interpolate between 20khz cutoff and 0 Q? */ /* I removed the optimization of turning the filter off when the * resonance frequence is above the maximum frequency. Instead, the * filter frequency is set to a maximum of 0.45 times the sampling * rate. For a 44100 kHz sampling rate, this amounts to 19845 * Hz. The reason is that there were problems with anti-aliasing when the * synthesizer was run at lower sampling rates. Thanks to Stephan * Tassart for pointing me to this bug. By turning the filter on and * clipping the maximum filter frequency at 0.45*srate, the filter * is used as an anti-aliasing filter. */ if (fres > 0.45f * output_rate) fres = 0.45f * output_rate; else if (fres < 5) fres = 5; /* if filter enabled and there is a significant frequency change.. */ if ((abs (fres - iir_filter->last_fres) > 0.01)) { /* The filter coefficients have to be recalculated (filter * parameters have changed). Recalculation for various reasons is * forced by setting last_fres to -1. The flag filter_startup * indicates, that the DSP loop runs for the first time, in this * case, the filter is set directly, instead of smoothly fading * between old and new settings. */ iir_filter->last_fres = fres; fluid_iir_filter_calculate_coefficients(iir_filter, FLUID_BUFSIZE, output_rate); } fluid_check_fpe ("voice_write DSP coefficients"); }
void Voice::dsp_float_config() { /* 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 (int i = 0; i < FLUID_INTERP_MAX; i++) { double x = (double) i / (double) FLUID_INTERP_MAX; interp_coeff[i][0] = (float)(x * (-0.5 + x * (1 - 0.5 * x))); interp_coeff[i][1] = (float)(1.0 + x * x * (1.5 * x - 2.5)); interp_coeff[i][2] = (float)(x * (0.5 + x * (2.0 - 1.5 * x))); interp_coeff[i][3] = (float)(0.5 * x * x * (x - 1.0)); interp_coeff_linear[i][0] = (float)(1.0 - x); interp_coeff_linear[i][1] = (float)x; } /* i: Offset in terms of whole samples */ for (int i = 0; i < SINC_INTERP_ORDER; i++) { /* i2: Offset in terms of fractional samples ('subsamples') */ for (int i2 = 0; i2 < FLUID_INTERP_MAX; i2++) { /* center on middle of table */ double 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) */ double v; if (fabs (i_shifted) > 0.000001) { v = (float)sin (i_shifted * M_PI) / (M_PI * i_shifted); /* Hamming window */ v *= (float)0.5 * (1.0 + cos (2.0 * M_PI * i_shifted / (float)SINC_INTERP_ORDER)); } else v = 1.0; sinc_table7[FLUID_INTERP_MAX - i2 - 1][i] = v; } } fluid_check_fpe("interpolation table calculation"); }
/** * @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; }
/** * 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.sample) return 0; 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; }
/* 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"); }
/* * Variable description: * - dsp_a1, dsp_a2, dsp_b0, dsp_b1, dsp_b2: Filter coefficients * * A couple of variables are used internally, their results are discarded: * - dsp_i: Index through the output buffer * - dsp_phase_fractional: The fractional part of dsp_phase * - dsp_coeff: A table of four coefficients, depending on the fractional phase. * Used to interpolate between samples. * - dsp_process_buffer: Holds the processed signal between stages * - dsp_centernode: delay line for the IIR filter * - dsp_hist1: same * - dsp_hist2: same */ void fluid_iir_filter_apply(fluid_iir_filter_t* iir_filter, fluid_real_t *dsp_buf, int count) { /* IIR filter sample history */ fluid_real_t dsp_hist1 = iir_filter->hist1; fluid_real_t dsp_hist2 = iir_filter->hist2; /* IIR filter coefficients */ fluid_real_t dsp_a1 = iir_filter->a1; fluid_real_t dsp_a2 = iir_filter->a2; fluid_real_t dsp_b02 = iir_filter->b02; fluid_real_t dsp_b1 = iir_filter->b1; int dsp_filter_coeff_incr_count = iir_filter->filter_coeff_incr_count; fluid_real_t dsp_centernode; int dsp_i; /* filter (implement the voice filter according to SoundFont standard) */ /* Check for denormal number (too close to zero). */ if (fabs (dsp_hist1) < 1e-20) dsp_hist1 = 0.0f; /* FIXME JMG - Is this even needed? */ /* Two versions of the filter loop. One, while the filter is * changing towards its new setting. The other, if the filter * doesn't change. */ if (dsp_filter_coeff_incr_count > 0) { fluid_real_t dsp_a1_incr = iir_filter->a1_incr; fluid_real_t dsp_a2_incr = iir_filter->a2_incr; fluid_real_t dsp_b02_incr = iir_filter->b02_incr; fluid_real_t dsp_b1_incr = iir_filter->b1_incr; /* Increment is added to each filter coefficient filter_coeff_incr_count times. */ for (dsp_i = 0; dsp_i < count; dsp_i++) { /* The filter is implemented in Direct-II form. */ dsp_centernode = dsp_buf[dsp_i] - dsp_a1 * dsp_hist1 - dsp_a2 * dsp_hist2; dsp_buf[dsp_i] = dsp_b02 * (dsp_centernode + dsp_hist2) + dsp_b1 * dsp_hist1; dsp_hist2 = dsp_hist1; dsp_hist1 = dsp_centernode; if (dsp_filter_coeff_incr_count-- > 0) { fluid_real_t old_b02 = dsp_b02; dsp_a1 += dsp_a1_incr; dsp_a2 += dsp_a2_incr; dsp_b02 += dsp_b02_incr; dsp_b1 += dsp_b1_incr; /* Compensate history to avoid the filter going havoc with large frequency changes */ if (iir_filter->compensate_incr && fabs(dsp_b02) > 0.001) { fluid_real_t compensate = old_b02 / dsp_b02; dsp_centernode *= compensate; dsp_hist1 *= compensate; dsp_hist2 *= compensate; } } } /* for dsp_i */ } else /* The filter parameters are constant. This is duplicated to save time. */ { for (dsp_i = 0; dsp_i < count; dsp_i++) { /* The filter is implemented in Direct-II form. */ dsp_centernode = dsp_buf[dsp_i] - dsp_a1 * dsp_hist1 - dsp_a2 * dsp_hist2; dsp_buf[dsp_i] = dsp_b02 * (dsp_centernode + dsp_hist2) + dsp_b1 * dsp_hist1; dsp_hist2 = dsp_hist1; dsp_hist1 = dsp_centernode; } } iir_filter->hist1 = dsp_hist1; iir_filter->hist2 = dsp_hist2; iir_filter->a1 = dsp_a1; iir_filter->a2 = dsp_a2; iir_filter->b02 = dsp_b02; iir_filter->b1 = dsp_b1; iir_filter->filter_coeff_incr_count = dsp_filter_coeff_incr_count; fluid_check_fpe ("voice_filter"); }
static FLUID_INLINE void fluid_iir_filter_calculate_coefficients(fluid_iir_filter_t* iir_filter, int transition_samples, fluid_real_t output_rate) { /* * Those equations from Robert Bristow-Johnson's `Cookbook * formulae for audio EQ biquad filter coefficients', obtained * from Harmony-central.com / Computer / Programming. They are * the result of the bilinear transform on an analogue filter * prototype. To quote, `BLT frequency warping has been taken * into account for both significant frequency relocation and for * bandwidth readjustment'. */ fluid_real_t omega = (fluid_real_t) (2.0 * M_PI * (iir_filter->last_fres / ((float) output_rate))); fluid_real_t sin_coeff = (fluid_real_t) sin(omega); fluid_real_t cos_coeff = (fluid_real_t) cos(omega); fluid_real_t alpha_coeff = sin_coeff / (2.0f * iir_filter->q_lin); fluid_real_t a0_inv = 1.0f / (1.0f + alpha_coeff); /* Calculate the filter coefficients. All coefficients are * normalized by a0. Think of `a1' as `a1/a0'. * * Here a couple of multiplications are saved by reusing common expressions. * The original equations should be: * iir_filter->b0=(1.-cos_coeff)*a0_inv*0.5*iir_filter->filter_gain; * iir_filter->b1=(1.-cos_coeff)*a0_inv*iir_filter->filter_gain; * iir_filter->b2=(1.-cos_coeff)*a0_inv*0.5*iir_filter->filter_gain; */ fluid_real_t a1_temp = -2.0f * cos_coeff * a0_inv; fluid_real_t a2_temp = (1.0f - alpha_coeff) * a0_inv; fluid_real_t b1_temp = (1.0f - cos_coeff) * a0_inv * iir_filter->filter_gain; /* both b0 -and- b2 */ fluid_real_t b02_temp = b1_temp * 0.5f; iir_filter->compensate_incr = 0; if (iir_filter->filter_startup || (transition_samples == 0)) { /* The filter is calculated, because the voice was started up. * In this case set the filter coefficients without delay. */ iir_filter->a1 = a1_temp; iir_filter->a2 = a2_temp; iir_filter->b02 = b02_temp; iir_filter->b1 = b1_temp; iir_filter->filter_coeff_incr_count = 0; iir_filter->filter_startup = 0; // printf("Setting initial filter coefficients.\n"); } else { /* The filter frequency is changed. Calculate an increment * factor, so that the new setting is reached after one buffer * length. x_incr is added to the current value FLUID_BUFSIZE * times. The length is arbitrarily chosen. Longer than one * buffer will sacrifice some performance, though. Note: If * the filter is still too 'grainy', then increase this number * at will. */ iir_filter->a1_incr = (a1_temp - iir_filter->a1) / transition_samples; iir_filter->a2_incr = (a2_temp - iir_filter->a2) / transition_samples; iir_filter->b02_incr = (b02_temp - iir_filter->b02) / transition_samples; iir_filter->b1_incr = (b1_temp - iir_filter->b1) / transition_samples; if (fabs(iir_filter->b02) > 0.0001) { fluid_real_t quota = b02_temp / iir_filter->b02; iir_filter->compensate_incr = quota < 0.5 || quota > 2; } /* Have to add the increments filter_coeff_incr_count times. */ iir_filter->filter_coeff_incr_count = transition_samples; } fluid_check_fpe ("voice_write filter calculation"); }