1 /* FluidSynth - A Software Synthesizer
3 * Copyright (C) 2003 Peter Hanappe and others.
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Lesser General Public License
7 * as published by the Free Software Foundation; either version 2.1 of
8 * the License, or (at your option) any later version.
10 * This library is distributed in the hope that it will be useful, but
11 * WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Lesser General Public License for more details.
15 * You should have received a copy of the GNU Lesser General Public
16 * License along with this library; if not, write to the Free
17 * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
21 #include "fluid_iir_filter.h"
22 #include "fluid_sys.h"
23 #include "fluid_conv.h"
26 * Applies a low- or high-pass filter with variable cutoff frequency and quality factor
27 * for a given biquad transfer function:
28 * b0 + b1*z^-1 + b2*z^-2
29 * H(z) = ------------------------
30 * a0 + a1*z^-1 + a2*z^-2
32 * Also modifies filter state accordingly.
33 * @param iir_filter Filter parameter
34 * @param dsp_buf Pointer to the synthesized audio data
35 * @param count Count of samples in dsp_buf
38 * Variable description:
39 * - dsp_a1, dsp_a2: Filter coefficients for the the previously filtered output signal
40 * - dsp_b0, dsp_b1, dsp_b2: Filter coefficients for input signal
41 * - coefficients normalized to a0
43 * A couple of variables are used internally, their results are discarded:
44 * - dsp_i: Index through the output buffer
45 * - dsp_centernode: delay line for the IIR filter
50 fluid_iir_filter_apply(fluid_iir_filter_t *iir_filter,
51 fluid_real_t *dsp_buf, int count)
53 if(iir_filter->type == FLUID_IIR_DISABLED || iir_filter->q_lin == 0)
59 /* IIR filter sample history */
60 fluid_real_t dsp_hist1 = iir_filter->hist1;
61 fluid_real_t dsp_hist2 = iir_filter->hist2;
63 /* IIR filter coefficients */
64 fluid_real_t dsp_a1 = iir_filter->a1;
65 fluid_real_t dsp_a2 = iir_filter->a2;
66 fluid_real_t dsp_b02 = iir_filter->b02;
67 fluid_real_t dsp_b1 = iir_filter->b1;
68 int dsp_filter_coeff_incr_count = iir_filter->filter_coeff_incr_count;
70 fluid_real_t dsp_centernode;
73 /* filter (implement the voice filter according to SoundFont standard) */
75 /* Check for denormal number (too close to zero). */
76 if(fabs(dsp_hist1) < 1e-20)
78 dsp_hist1 = 0.0f; /* FIXME JMG - Is this even needed? */
81 /* Two versions of the filter loop. One, while the filter is
82 * changing towards its new setting. The other, if the filter
86 if(dsp_filter_coeff_incr_count > 0)
88 fluid_real_t dsp_a1_incr = iir_filter->a1_incr;
89 fluid_real_t dsp_a2_incr = iir_filter->a2_incr;
90 fluid_real_t dsp_b02_incr = iir_filter->b02_incr;
91 fluid_real_t dsp_b1_incr = iir_filter->b1_incr;
94 /* Increment is added to each filter coefficient filter_coeff_incr_count times. */
95 for(dsp_i = 0; dsp_i < count; dsp_i++)
97 /* The filter is implemented in Direct-II form. */
98 dsp_centernode = dsp_buf[dsp_i] - dsp_a1 * dsp_hist1 - dsp_a2 * dsp_hist2;
99 dsp_buf[dsp_i] = dsp_b02 * (dsp_centernode + dsp_hist2) + dsp_b1 * dsp_hist1;
100 dsp_hist2 = dsp_hist1;
101 dsp_hist1 = dsp_centernode;
103 if(dsp_filter_coeff_incr_count-- > 0)
105 fluid_real_t old_b02 = dsp_b02;
106 dsp_a1 += dsp_a1_incr;
107 dsp_a2 += dsp_a2_incr;
108 dsp_b02 += dsp_b02_incr;
109 dsp_b1 += dsp_b1_incr;
111 /* Compensate history to avoid the filter going havoc with large frequency changes */
112 if(iir_filter->compensate_incr && fabs(dsp_b02) > 0.001)
114 fluid_real_t compensate = old_b02 / dsp_b02;
115 dsp_hist1 *= compensate;
116 dsp_hist2 *= compensate;
121 else /* The filter parameters are constant. This is duplicated to save time. */
123 for(dsp_i = 0; dsp_i < count; dsp_i++)
125 /* The filter is implemented in Direct-II form. */
126 dsp_centernode = dsp_buf[dsp_i] - dsp_a1 * dsp_hist1 - dsp_a2 * dsp_hist2;
127 dsp_buf[dsp_i] = dsp_b02 * (dsp_centernode + dsp_hist2) + dsp_b1 * dsp_hist1;
128 dsp_hist2 = dsp_hist1;
129 dsp_hist1 = dsp_centernode;
133 iir_filter->hist1 = dsp_hist1;
134 iir_filter->hist2 = dsp_hist2;
135 iir_filter->a1 = dsp_a1;
136 iir_filter->a2 = dsp_a2;
137 iir_filter->b02 = dsp_b02;
138 iir_filter->b1 = dsp_b1;
139 iir_filter->filter_coeff_incr_count = dsp_filter_coeff_incr_count;
141 fluid_check_fpe("voice_filter");
146 DECLARE_FLUID_RVOICE_FUNCTION(fluid_iir_filter_init)
148 fluid_iir_filter_t *iir_filter = obj;
149 enum fluid_iir_filter_type type = param[0].i;
150 enum fluid_iir_filter_flags flags = param[1].i;
152 iir_filter->type = type;
153 iir_filter->flags = flags;
155 if(type != FLUID_IIR_DISABLED)
157 fluid_iir_filter_reset(iir_filter);
162 fluid_iir_filter_reset(fluid_iir_filter_t *iir_filter)
164 iir_filter->hist1 = 0;
165 iir_filter->hist2 = 0;
166 iir_filter->last_fres = -1.;
167 iir_filter->q_lin = 0;
168 iir_filter->filter_startup = 1;
171 DECLARE_FLUID_RVOICE_FUNCTION(fluid_iir_filter_set_fres)
173 fluid_iir_filter_t *iir_filter = obj;
174 fluid_real_t fres = param[0].real;
176 iir_filter->fres = fres;
177 iir_filter->last_fres = -1.;
180 static fluid_real_t fluid_iir_filter_q_from_dB(fluid_real_t q_dB)
182 /* The generator contains 'centibels' (1/10 dB) => divide by 10 to
186 /* Range: SF2.01 section 8.1.3 # 8 (convert from cB to dB => /10) */
187 fluid_clip(q_dB, 0.0f, 96.0f);
189 /* Short version: Modify the Q definition in a way, that a Q of 0
190 * dB leads to no resonance hump in the freq. response.
192 * Long version: From SF2.01, page 39, item 9 (initialFilterQ):
193 * "The gain at the cutoff frequency may be less than zero when
194 * zero is specified". Assume q_dB=0 / q_lin=1: If we would leave
195 * q as it is, then this results in a 3 dB hump slightly below
196 * fc. At fc, the gain is exactly the DC gain (0 dB). What is
197 * (probably) meant here is that the filter does not show a
198 * resonance hump for q_dB=0. In this case, the corresponding
199 * q_lin is 1/sqrt(2)=0.707. The filter should have 3 dB of
200 * attenuation at fc now. In this case Q_dB is the height of the
201 * resonance peak not over the DC gain, but over the frequency
202 * response of a non-resonant filter. This idea is implemented as
206 /* The 'sound font' Q is defined in dB. The filter needs a linear
208 return pow(10.0f, q_dB / 20.0f);
211 DECLARE_FLUID_RVOICE_FUNCTION(fluid_iir_filter_set_q)
213 fluid_iir_filter_t *iir_filter = obj;
214 fluid_real_t q = param[0].real;
215 int flags = iir_filter->flags;
217 if(flags & FLUID_IIR_Q_ZERO_OFF && q <= 0.0)
221 else if(flags & FLUID_IIR_Q_LINEAR)
223 /* q is linear (only for user-defined filter)
224 * increase to avoid Q being somewhere between zero and one,
225 * which results in some strange amplified lowpass signal
231 q = fluid_iir_filter_q_from_dB(q);
234 iir_filter->q_lin = q;
235 iir_filter->filter_gain = 1.0;
237 if(!(flags & FLUID_IIR_NO_GAIN_AMP))
241 * The SoundFont specs ask for a gain reduction equal to half the
242 * height of the resonance peak (Q). For example, for a 10 dB
243 * resonance peak, the gain is reduced by 5 dB. This is done by
244 * multiplying the total gain with sqrt(1/Q). `Sqrt' divides dB
245 * by 2 (100 lin = 40 dB, 10 lin = 20 dB, 3.16 lin = 10 dB etc)
246 * The gain is later factored into the 'b' coefficients
247 * (numerator of the filter equation). This gain factor depends
248 * only on Q, so this is the right place to calculate it.
250 iir_filter->filter_gain /= sqrt(q);
253 /* The synthesis loop will have to recalculate the filter coefficients. */
254 iir_filter->last_fres = -1.;
257 static FLUID_INLINE void
258 fluid_iir_filter_calculate_coefficients(fluid_iir_filter_t *iir_filter,
259 int transition_samples,
260 fluid_real_t output_rate)
262 /* FLUID_IIR_Q_LINEAR may switch the filter off by setting Q==0 */
263 if(iir_filter->q_lin == 0)
270 * Those equations from Robert Bristow-Johnson's `Cookbook
271 * formulae for audio EQ biquad filter coefficients', obtained
272 * from Harmony-central.com / Computer / Programming. They are
273 * the result of the bilinear transform on an analogue filter
274 * prototype. To quote, `BLT frequency warping has been taken
275 * into account for both significant frequency relocation and for
276 * bandwidth readjustment'. */
278 fluid_real_t omega = (fluid_real_t)(2.0 * M_PI) *
279 (iir_filter->last_fres / output_rate);
280 fluid_real_t sin_coeff = (fluid_real_t) sin(omega);
281 fluid_real_t cos_coeff = (fluid_real_t) cos(omega);
282 fluid_real_t alpha_coeff = sin_coeff / (2.0f * iir_filter->q_lin);
283 fluid_real_t a0_inv = 1.0f / (1.0f + alpha_coeff);
285 /* Calculate the filter coefficients. All coefficients are
286 * normalized by a0. Think of `a1' as `a1/a0'.
288 * Here a couple of multiplications are saved by reusing common expressions.
289 * The original equations should be:
290 * iir_filter->b0=(1.-cos_coeff)*a0_inv*0.5*iir_filter->filter_gain;
291 * iir_filter->b1=(1.-cos_coeff)*a0_inv*iir_filter->filter_gain;
292 * iir_filter->b2=(1.-cos_coeff)*a0_inv*0.5*iir_filter->filter_gain; */
294 /* "a" coeffs are same for all 3 available filter types */
295 fluid_real_t a1_temp = -2.0f * cos_coeff * a0_inv;
296 fluid_real_t a2_temp = (1.0f - alpha_coeff) * a0_inv;
298 fluid_real_t b02_temp, b1_temp;
300 switch(iir_filter->type)
302 case FLUID_IIR_HIGHPASS:
303 b1_temp = (1.0f + cos_coeff) * a0_inv * iir_filter->filter_gain;
305 /* both b0 -and- b2 */
306 b02_temp = b1_temp * 0.5f;
311 case FLUID_IIR_LOWPASS:
312 b1_temp = (1.0f - cos_coeff) * a0_inv * iir_filter->filter_gain;
314 /* both b0 -and- b2 */
315 b02_temp = b1_temp * 0.5f;
319 /* filter disabled, should never get here */
323 iir_filter->compensate_incr = 0;
325 if(iir_filter->filter_startup || (transition_samples == 0))
327 /* The filter is calculated, because the voice was started up.
328 * In this case set the filter coefficients without delay.
330 iir_filter->a1 = a1_temp;
331 iir_filter->a2 = a2_temp;
332 iir_filter->b02 = b02_temp;
333 iir_filter->b1 = b1_temp;
334 iir_filter->filter_coeff_incr_count = 0;
335 iir_filter->filter_startup = 0;
336 // printf("Setting initial filter coefficients.\n");
341 /* The filter frequency is changed. Calculate an increment
342 * factor, so that the new setting is reached after one buffer
343 * length. x_incr is added to the current value FLUID_BUFSIZE
344 * times. The length is arbitrarily chosen. Longer than one
345 * buffer will sacrifice some performance, though. Note: If
346 * the filter is still too 'grainy', then increase this number
350 iir_filter->a1_incr = (a1_temp - iir_filter->a1) / transition_samples;
351 iir_filter->a2_incr = (a2_temp - iir_filter->a2) / transition_samples;
352 iir_filter->b02_incr = (b02_temp - iir_filter->b02) / transition_samples;
353 iir_filter->b1_incr = (b1_temp - iir_filter->b1) / transition_samples;
355 if(fabs(iir_filter->b02) > 0.0001)
357 fluid_real_t quota = b02_temp / iir_filter->b02;
358 iir_filter->compensate_incr = quota < 0.5 || quota > 2;
361 /* Have to add the increments filter_coeff_incr_count times. */
362 iir_filter->filter_coeff_incr_count = transition_samples;
365 fluid_check_fpe("voice_write filter calculation");
370 void fluid_iir_filter_calc(fluid_iir_filter_t *iir_filter,
371 fluid_real_t output_rate,
372 fluid_real_t fres_mod)
376 /* calculate the frequency of the resonant filter in Hz */
377 fres = fluid_ct2hz(iir_filter->fres + fres_mod);
379 /* FIXME - Still potential for a click during turn on, can we interpolate
380 between 20khz cutoff and 0 Q? */
382 /* I removed the optimization of turning the filter off when the
383 * resonance frequence is above the maximum frequency. Instead, the
384 * filter frequency is set to a maximum of 0.45 times the sampling
385 * rate. For a 44100 kHz sampling rate, this amounts to 19845
386 * Hz. The reason is that there were problems with anti-aliasing when the
387 * synthesizer was run at lower sampling rates. Thanks to Stephan
388 * Tassart for pointing me to this bug. By turning the filter on and
389 * clipping the maximum filter frequency at 0.45*srate, the filter
390 * is used as an anti-aliasing filter. */
392 if(fres > 0.45f * output_rate)
394 fres = 0.45f * output_rate;
401 /* if filter enabled and there is a significant frequency change.. */
402 if(iir_filter->type != FLUID_IIR_DISABLED && fabs(fres - iir_filter->last_fres) > 0.01)
404 /* The filter coefficients have to be recalculated (filter
405 * parameters have changed). Recalculation for various reasons is
406 * forced by setting last_fres to -1. The flag filter_startup
407 * indicates, that the DSP loop runs for the first time, in this
408 * case, the filter is set directly, instead of smoothly fading
409 * between old and new settings. */
410 iir_filter->last_fres = fres;
411 fluid_iir_filter_calculate_coefficients(iir_filter, FLUID_BUFSIZE,
416 fluid_check_fpe("voice_write DSP coefficients");