1 // license:BSD-3-Clause
2 // copyright-holders:K.Wilkins,Couriersud,Derrick Renaud,Frank Palazzolo,Jonathan Gevaryahu
3 /*
4     This is an implementation of a Direct-form II digital biquad filter,
5     intended for use in audio paths for filtering audio to or from other
6     stream devices.
7 
8     It has a number of constructor-helpers for automatically generating
9     a biquad filter equivalent to the filter response of a few standard
10     analog second order filter topographies.
11 
12     This biquad filter implementation is based on one written by Frank
13     Palazzolo, K. Wilkins, Couriersud, and Derrick Renaud, with some changes:
14     * It uses the Q factor directly in the filter definitions, rather than the damping factor (1/Q)
15     * It implements every common type of digital biquad filter which I could find documentation for.
16     * The filter is Direct-form II instead of Direct-form I, which results in shorter compiled code.
17 
18     Possibly useful features which aren't implemented because nothing uses them yet:
19     * More Sallen-Key filter variations (band-pass, high-pass)
20     * Direct control of the 5 normalized biquad parameters for a custom/raw parameter filter.
21 */
22 #include "emu.h"
23 #include "flt_biquad.h"
24 
25 // we need the M_SQRT2 constant
26 #ifndef M_SQRT2
27 #define M_SQRT2 1.41421356237309504880
28 #endif
29 
30 // define this to display debug info about the filters being set up
31 #undef FLT_BIQUAD_DEBUG_SETUP
32 
33 // device type definition
34 DEFINE_DEVICE_TYPE(FILTER_BIQUAD, filter_biquad_device, "filter_biquad", "Biquad Filter")
35 
36 // allow the enum class for the biquad filter type to be saved by the savestate system
37 ALLOW_SAVE_TYPE(filter_biquad_device::biquad_type);
38 
39 //**************************************************************************
40 //  LIVE DEVICE
41 //**************************************************************************
42 
43 //-------------------------------------------------
44 //  filter_biquad_device - constructor
45 //-------------------------------------------------
46 
47 // initialize with some sane defaults for a highpass filter with a cutoff at 16hz, same as flt_rc's 'ac' mode.
filter_biquad_device(const machine_config & mconfig,const char * tag,device_t * owner,uint32_t clock)48 filter_biquad_device::filter_biquad_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
49 	: device_t(mconfig, FILTER_BIQUAD, tag, owner, clock),
50 		device_sound_interface(mconfig, *this),
51 		m_stream(nullptr),
52 		m_type(biquad_type::HIGHPASS),
53 		m_last_sample_rate(0),
54 		m_fc(16.0),
55 		m_q(M_SQRT2/2.0),
56 		m_gain(1.0),
57 		m_input(0.0),
58 		m_w0(0.0),
59 		m_w1(0.0),
60 		m_w2(0.0),
61 		m_output(0.0),
62 		m_a1(0.0),
63 		m_a2(0.0),
64 		m_b0(1.0),
65 		m_b1(0.0),
66 		m_b2(0.0)
67 {
68 }
69 
70 // set up the filter with the specified parameters and return a pointer to the new device
setup(biquad_type type,double fc,double q,double gain)71 filter_biquad_device& filter_biquad_device::setup(biquad_type type, double fc, double q, double gain)
72 {
73 	m_type = type;
74 	m_fc = fc;
75 	m_q = q;
76 	m_gain = gain;
77 	return *this;
78 }
79 
80 // update an existing instance with new filter parameters
update_params(biquad_type type,double fc,double q,double gain)81 void filter_biquad_device::update_params(biquad_type type, double fc, double q, double gain)
82 {
83 	m_stream->update();
84 	m_type = type;
85 	m_fc = fc;
86 	m_q = q;
87 	m_gain = gain;
88 	recalc();
89 }
90 
91 
92 //-------------------------------------------------
93 // Filter setup helpers for various filter models
94 //-------------------------------------------------
95 
96 // Sallen-Key filters
97 
98 /* Setup a biquad filter structure based on a single op-amp Sallen-Key low-pass filter circuit.
99  *
100  *                   .----------------------------.
101  *                   |                            |
102  *                  ---  c2                       |
103  *                  ---                           |
104  *                   |                            |
105  *            r1     |   r2                |\     |
106  *   In >----ZZZZ----+--ZZZZ---+--------+  | \    |
107  *                             |        '--|+ \   |
108  *                            ---  c1      |   >--+------> out
109  *                            ---       .--|- /   |
110  *                             |        |  | /    |
111  *                            gnd       |  |/     |
112  *                                      |         |
113  *                                      |   r4    |
114  *                                      +--ZZZZ---'
115  *                                      |
116  *                                      Z
117  *                                      Z r3
118  *                                      Z
119  *                                      |
120  *                                     gnd
121  */
opamp_sk_lowpass_setup(double r1,double r2,double r3,double r4,double c1,double c2)122 filter_biquad_device& filter_biquad_device::opamp_sk_lowpass_setup(double r1, double r2, double r3, double r4, double c1, double c2)
123 {
124 	if ((r1 == 0) || (r2 == 0) || (r3 == 0) || (r4 == 0) || (c1 == 0) || (c2 == 0))
125 	{
126 		fatalerror("filter_biquad_device::opamp_sk_lowpass_setup() - no parameters can be 0; parameters were: r1: %f, r2: %f, r3: %f, r4: %f, c1: %f, c2: %f", r1, r2, r3, r4, c1, c2); /* Filter can not be setup.  Undefined results. */
127 	}
128 
129 	// note: if R3 doesn't exist (no link to ground), pass a value of RES_M(999.99) or the like, i.e. an 'infinite resistor'
130 	double const gain = (r3 + r4) / r3;
131 	double const fc = 1.0 / (2 * M_PI * sqrt(r1 * r2 * c1 * c2));
132 	double const q = sqrt(r1 * r2 * c1 * c2) / ((r1 * c1) + (r2 * c1) + ((r2 * c2) * (1.0 - gain)));
133 #ifdef FLT_BIQUAD_DEBUG_SETUP
134 		logerror("filter_biquad_device::opamp_sk_lowpass_setup() yields: fc = %f, Q = %f, gain = %f\n", fc, q, gain);
135 #endif
136 		return setup(biquad_type::LOWPASS, fc, q, gain);
137 }
138 
139 // Multiple-Feedback filters
140 // (This is sometimes called a 'Rauch' filter circuit.)
141 
142 /* Setup a biquad filter structure based on a single op-amp Multiple-Feedback low-pass filter circuit.
143  * NOTE: vRef is not definable when setting up the filter.
144  *  If the analog effects caused by vRef are important to the operation of the specific filter
145  *  in question, a netlist implementation may work better under those circumstances.
146  * NOTE2: a variant of this circuit has the c1 capacitor left off. if so, set c1 to 0
147  *  which will form a controllable gain 1st order version of the circuit.
148  * NOTE3: There is a well known 'proper' 1st order version of this circuit where r2 is
149  *  a dead short, and c1 is also ommitted as in NOTE2. Set both r2 to 0 and c1 to 0 to
150  *  emulate a circuit of this type.
151  *
152  *                             .--------+---------.
153  *                             |        |         |
154  *                             Z       --- c2     |
155  *                             Z r3    ---        |
156  *                             Z        |         |
157  *            r1               |   r2   |  |\     |
158  *   In >----ZZZZ----+---------+--ZZZZ--+  | \    |
159  *                   |                  '--|- \   |
160  *                  ---  c1                |   >--+------> out
161  *                  ---                 .--|+ /
162  *                   |                  |  | /
163  *                  gnd        vRef >---'  |/
164  *
165  */
opamp_mfb_lowpass_setup(double r1,double r2,double r3,double c1,double c2)166 filter_biquad_device& filter_biquad_device::opamp_mfb_lowpass_setup(double r1, double r2, double r3, double c1, double c2)
167 {
168 	if ((r1 == 0) || ((r2 == 0) && (c1 != 0)) || (r3 == 0) || (c2 == 0))
169 	{
170 		fatalerror("filter_biquad_device::opamp_mfb_lowpass_setup() - only c1 can be 0 (and if c1 is 0, r2 can also be 0); parameters were: r1: %f, r2: %f, r3: %f, c1: %f, c2: %f", r1, r2, r3, c1, c2); /* Filter can not be setup.  Undefined results. */
171 	}
172 	double const gain = -r3 / r1;
173 	double fc, q = (M_SQRT2 / 2.0);
174 	if ((r2 != 0) && (c1 == 0)) // set C1 to 0 to run this filter in single pole mode where C1 was left off the filter entirely. Certain Williams boards seem to have omitted C1, presumably by accident.
175 	{
176 		fc = (r1 * r3) / (2 * M_PI * ((r1 * r2) + (r1 * r3) + (r2 * r3)) * r3 * c2);
177 #ifdef FLT_BIQUAD_DEBUG_SETUP
178 		logerror("filter_biquad_device::opamp_mfb_lowpass_setup() in degraded mode yields: fc = %f, Q = %f(ignored), gain = %f\n", fc, q, gain);
179 #endif
180 		return setup(biquad_type::LOWPASS1P, fc, q, gain);
181 	}
182 	else if ((r2 == 0) && (c1 == 0)) // proper first order case, set both C1 to 0 and R2 to 0 for the first order
183 	{
184 		fc = 1.0 / (2 * M_PI * r3 * c2);
185 #ifdef FLT_BIQUAD_DEBUG_SETUP
186 		logerror("filter_biquad_device::opamp_mfb_lowpass_setup() in 1st order mode yields: fc = %f, Q = %f(ignored), gain = %f\n", fc, q, gain);
187 #endif
188 		return setup(biquad_type::LOWPASS1P, fc, q, gain);
189 	}
190 	else // common case, (r2 != 0) && (c1 != 0)
191 	{
192 		fc = 1.0 / (2 * M_PI * sqrt(r2 * r3 * c1 * c2));
193 		q = sqrt(r2 * r3 * c1 * c2) / ((r3 * c2) + (r2 * c2) + ((r2 * c2) * -gain));
194 #ifdef FLT_BIQUAD_DEBUG_SETUP
195 		logerror("filter_biquad_device::opamp_mfb_lowpass_setup() in 2nd order mode yields: fc = %f, Q = %f, gain = %f\n", fc, q, gain);
196 #endif
197 		return setup(biquad_type::LOWPASS, fc, q, gain);
198 	}
199 }
200 
201 /* Setup a biquad filter structure based on a single op-amp Multiple-Feedback band-pass filter circuit.
202  * NOTE: vRef is not definable when setting up the filter.
203  *  If the analog effects caused by vRef are important to the operation of the specific filter
204  *  in question, a netlist implementation may work better under those circumstances.
205  * NOTE2: If r2 is not used, then set it to 0 ohms, the code will ignore it and assume a fixed gain of 1.
206  *
207  *                             .--------+---------.
208  *                             |        |         |
209  *                            --- c1    Z         |
210  *                            ---       Z r3      |
211  *                             |        Z         |
212  *            r1               |  c2    |  |\     |
213  *   In >----ZZZZ----+---------+--||----+  | \    |
214  *                   Z                  '--|- \   |
215  *                   Z r2                  |   >--+------> out
216  *                   Z                  .--|+ /
217  *                   |                  |  | /
218  *                  gnd        vRef >---'  |/
219  *
220  */
opamp_mfb_bandpass_setup(double r1,double r2,double r3,double c1,double c2)221 filter_biquad_device& filter_biquad_device::opamp_mfb_bandpass_setup(double r1, double r2, double r3, double c1, double c2)
222 {
223 	if ((r1 == 0) || (r3 == 0) || (c1 == 0) || (c2 == 0))
224 	{
225 		fatalerror("filter_biquad_device::opamp_mfb_bandpass_setup() - only r2 can be 0; parameters were: r1: %f, r2: %f, r3: %f, c1: %f, c2: %f", r1, r2, r3, c1, c2); /* Filter can not be setup.  Undefined results. */
226 	}
227 
228 	double r_in, gain;
229 
230 	if (r2 == 0)
231 	{
232 		gain = 1;
233 		r_in = r1;
234 	}
235 	else
236 	{
237 		gain = r2 / (r1 + r2);
238 		r_in = 1.0 / (1.0/r1 + 1.0/r2);
239 	}
240 
241 	double const fc = 1.0 / (2 * M_PI * sqrt(r_in * r3 * c1 * c2)); // technically this is the center frequency of the bandpass
242 	double const q = sqrt(r3 / r_in * c1 * c2) / (c1 + c2);
243 	gain *= -r3 / r_in * c2 / (c1 + c2);
244 #ifdef FLT_BIQUAD_DEBUG_SETUP
245 	logerror("filter_biquad_device::opamp_mfb_bandpass_setup() yields: fc = %f, Q = %f, gain = %f\n", fc, q, gain);
246 #endif
247 	return setup(biquad_type::BANDPASS, fc, q, gain);
248 }
249 
250 /* Setup a biquad filter structure based on a single op-amp Multiple-Feedback high-pass filter circuit.
251  * NOTE: vRef is not definable when setting up the filter.
252  *  If the analog effects caused by vRef are important to the operation of the specific filter
253  *  in question, a netlist implementation may work better under those circumstances.
254  *
255  *                             .--------+---------.
256  *                             |        |         |
257  *                            --- c3    Z         |
258  *                            ---       Z r2      |
259  *                             |        Z         |
260  *            c1               |   c2   |  |\     |
261  *   In >-----||-----+---------+---||---+  | \    |
262  *                   Z                  '--|- \   |
263  *                   Z r1                  |   >--+------> out
264  *                   Z                  .--|+ /
265  *                   |                  |  | /
266  *                  gnd        vRef >---'  |/
267  *
268  */
opamp_mfb_highpass_setup(double r1,double r2,double c1,double c2,double c3)269 filter_biquad_device& filter_biquad_device::opamp_mfb_highpass_setup(double r1, double r2, double c1, double c2, double c3)
270 {
271 	if ((r1 == 0) || (r2 == 0) || (c1 == 0) || (c2 == 0) || (c3 == 0))
272 	{
273 		fatalerror("filter_biquad_device::opamp_mfb_highpass_setup() - no parameters can be 0; parameters were: r1: %f, r2: %f, c1: %f, c2: %f, c3: %f", r1, r2, c1, c2, c3); /* Filter can not be setup.  Undefined results. */
274 	}
275 	// TODO: if c1 is 0/shorted, should the circuit should work with a gain of 1 in a first order mode?
276 
277 	double const gain = -c1 / c3;
278 	double const fc = 1.0 / (2 * M_PI * sqrt(c2 * c3 * r1 * r2));
279 	double const q = sqrt(c2 * c3 * r1 * r2) / ((c2 * r1) + (c3 * r1) + ((c3 * r1) * -gain));
280 #ifdef FLT_BIQUAD_DEBUG_SETUP
281 	logerror("filter_biquad_device::opamp_mfb_highpass_setup() yields: fc = %f, Q = %f, gain = %f\n", fc, q, gain);
282 #endif
283 	return setup(biquad_type::HIGHPASS, fc, q, gain);
284 }
285 
286 
287 //-------------------------------------------------
288 //  device_start - device-specific startup
289 //-------------------------------------------------
290 
device_start()291 void filter_biquad_device::device_start()
292 {
293 	m_stream = stream_alloc(1, 1, SAMPLE_RATE_OUTPUT_ADAPTIVE);
294 	m_last_sample_rate = 0;
295 	recalc();
296 
297 	save_item(NAME(m_type));
298 	save_item(NAME(m_last_sample_rate));
299 	save_item(NAME(m_fc));
300 	save_item(NAME(m_q));
301 	save_item(NAME(m_gain));
302 	save_item(NAME(m_input));
303 	save_item(NAME(m_w0));
304 	save_item(NAME(m_w1));
305 	save_item(NAME(m_w2));
306 	save_item(NAME(m_output));
307 	save_item(NAME(m_a1));
308 	save_item(NAME(m_a2));
309 	save_item(NAME(m_b0));
310 	save_item(NAME(m_b1));
311 	save_item(NAME(m_b2));
312 }
313 
314 
315 //-------------------------------------------------
316 //  sound_stream_update - handle a stream update
317 //-------------------------------------------------
318 
sound_stream_update(sound_stream & stream,std::vector<read_stream_view> const & inputs,std::vector<write_stream_view> & outputs)319 void filter_biquad_device::sound_stream_update(sound_stream &stream, std::vector<read_stream_view> const &inputs, std::vector<write_stream_view> &outputs)
320 {
321 	auto &src = inputs[0];
322 	auto &dst = outputs[0];
323 
324 	if (m_last_sample_rate != m_stream->sample_rate())
325 	{
326 		recalc();
327 		m_last_sample_rate = m_stream->sample_rate();
328 	}
329 
330 	for (int sampindex = 0; sampindex < dst.samples(); sampindex++)
331 	{
332 		m_input = src.get(sampindex);
333 		step();
334 		dst.put(sampindex, m_output);
335 	}
336 }
337 
338 
339 /* Calculate the filter context based on the passed filter type info.
340  * m_type - 1 of the 9 defined filter types
341  * m_fc   - center frequency
342  * m_q    - 'Q' (quality) factor of filter (1/damp)
343  * m_gain - overall filter gain. Set to 1.0 if not needed. The exact meaning of gain changes depending on the filter type.
344  */
recalc()345 void filter_biquad_device::recalc()
346 {
347 	double const MGain = fabs(m_gain); // absolute multiplicative gain
348 	double const DBGain = log10(MGain) * 20.0; // gain in dB
349 	double const AMGain = pow(10, fabs(DBGain) / 20.0); // multiplicative gain of absolute DB
350 	double const K = tan(M_PI * m_fc / m_stream->sample_rate());
351 	double const Ksquared = K * K;
352 	double const KoverQ = K / m_q;
353 	double normal = 1.0 / (1.0 + KoverQ + Ksquared);
354 
355 	m_a1 = 2.0 * (Ksquared - 1.0) * normal;
356 	m_a2 = (1.0 - KoverQ + Ksquared) * normal;
357 
358 	switch (m_type)
359 	{
360 		case biquad_type::LOWPASS1P:
361 			m_a1 = exp(-2.0 * M_PI * (m_fc / m_stream->sample_rate()));
362 			m_b0 = 1.0 - m_a1;
363 			m_a1 = -m_a1;
364 			m_b1 = m_b2 = m_a2 = 0.0;
365 			break;
366 		case biquad_type::HIGHPASS1P:
367 			m_a1 = -exp(-2.0 * M_PI * (0.5 - m_fc / m_stream->sample_rate()));
368 			m_b0 = 1.0 + m_a1;
369 			m_a1 = -m_a1;
370 			m_b1 = m_b2 = m_a2 = 0.0;
371 			break;
372 		case biquad_type::LOWPASS:
373 			m_b0 = Ksquared * normal;
374 			m_b1 = 2.0 * m_b0;
375 			m_b2 = 1.0 * m_b0;
376 			m_a1 = 2.0 * (Ksquared - 1.0) * normal;
377 			m_a2 = (1.0 - KoverQ + Ksquared) * normal;
378 			break;
379 		case biquad_type::HIGHPASS:
380 			m_b0 = 1.0 * normal;
381 			m_b1 = -2.0 * m_b0;
382 			m_b2 = 1.0 * m_b0;
383 			m_a1 = 2.0 * (Ksquared - 1.0) * normal;
384 			m_a2 = (1.0 - KoverQ + Ksquared) * normal;
385 			break;
386 		case biquad_type::BANDPASS:
387 			m_b0 = KoverQ * normal;
388 			m_b1 = 0.0;
389 			m_b2 = -1.0 * m_b0;
390 			m_a1 = 2.0 * (Ksquared - 1.0) * normal;
391 			m_a2 = (1.0 - KoverQ + Ksquared) * normal;
392 			break;
393 		case biquad_type::NOTCH:
394 			m_b0 = (1.0 + Ksquared) * normal;
395 			m_b1 = 2.0 * (Ksquared - 1.0) * normal;
396 			m_b2 = 1.0 * m_b0;
397 			m_a1 = 1.0 * m_b1;
398 			m_a2 = (1.0 - KoverQ + Ksquared) * normal;
399 			break;
400 		case biquad_type::PEAK:
401 			if (DBGain >= 0.0)
402 			{
403 				m_b0 = (1.0 + (AMGain * KoverQ) + Ksquared) * normal;
404 				m_b1 = 2.0 * (Ksquared - 1.0) * normal;
405 				m_b2 = (1.0 - (AMGain * KoverQ) + Ksquared) * normal;
406 				m_a1 = 1.0 * m_b1;
407 				m_a2 = (1.0 - KoverQ + Ksquared) * normal;
408 			}
409 			else
410 			{
411 				normal = 1.0 / (1.0 + (AMGain * KoverQ) + Ksquared);
412 				m_b0 = (1.0 + KoverQ + Ksquared) * normal;
413 				m_b1 = 2.0 * (Ksquared - 1.0) * normal;
414 				m_b2 = (1.0 - KoverQ + Ksquared) * normal;
415 				m_a1 = 1.0 * m_b1;
416 				m_a2 = (1.0 - (AMGain * KoverQ) + Ksquared) * normal;
417 			}
418 			break;
419 		case biquad_type::LOWSHELF:
420 			if (DBGain >= 0.0)
421 			{
422 				normal = 1.0 / (1.0 + M_SQRT2 * K + Ksquared);
423 				m_b0 = (1.0 + sqrt(2.0 * AMGain) * K + AMGain * Ksquared) * normal;
424 				m_b1 = 2.0 * (AMGain * Ksquared - 1.0) * normal;
425 				m_b2 = (1.0 - sqrt(2.0 * AMGain) * K + AMGain * Ksquared) * normal;
426 				m_a1 = 2.0 * (Ksquared - 1.0) * normal;
427 				m_a2 = (1.0 - M_SQRT2 * K + Ksquared) * normal;
428 			}
429 			else
430 			{
431 				normal = 1.0 / (1.0 + sqrt(2.0 * AMGain) * K + AMGain * Ksquared);
432 				m_b0 = (1.0 + M_SQRT2 * K + Ksquared) * normal;
433 				m_b1 = 2.0 * (Ksquared - 1.0) * normal;
434 				m_b2 = (1.0 - M_SQRT2 * K + Ksquared) * normal;
435 				m_a1 = 2.0 * (AMGain * Ksquared - 1.0) * normal;
436 				m_a2 = (1.0 - sqrt(2.0 * AMGain) * K + AMGain * Ksquared) * normal;
437 			}
438 			break;
439 		case biquad_type::HIGHSHELF:
440 			if (DBGain >= 0.0)
441 			{
442 				normal = 1.0 / (1.0 + M_SQRT2 * K + Ksquared);
443 				m_b0 = (AMGain + sqrt(2.0 * AMGain) * K + Ksquared) * normal;
444 				m_b1 = 2.0 * (Ksquared - AMGain) * normal;
445 				m_b2 = (AMGain - sqrt(2.0 * AMGain) * K + Ksquared) * normal;
446 				m_a1 = 2.0 * (Ksquared - 1.0) * normal;
447 				m_a2 = (1.0 - M_SQRT2 * K + Ksquared) * normal;
448 			}
449 			else
450 			{
451 				normal = 1.0 / (AMGain + sqrt(2.0 * AMGain) * K + Ksquared);
452 				m_b0 = (1.0 + M_SQRT2 * K + Ksquared) * normal;
453 				m_b1 = 2.0 * (Ksquared - 1.0) * normal;
454 				m_b2 = (1.0 - M_SQRT2 * K + Ksquared) * normal;
455 				m_a1 = 2.0 * (Ksquared - AMGain) * normal;
456 				m_a2 = (AMGain - sqrt(2.0 * AMGain) * K + Ksquared) * normal;
457 			}
458 			break;
459 		default:
460 			fatalerror("filter_biquad_device::recalc() - Invalid filter type!");
461 			break;
462 	}
463 #ifdef FLT_BIQUAD_DEBUG
464 	logerror("Calculated Parameters:\n");
465 	logerror( "Gain (dB): %f, (raw): %f\n", DBGain, MGain);
466 	logerror( "k: %f\n", K);
467 	logerror( "normal: %f\n", normal);
468 	logerror("b0: %f\n", m_b0);
469 	logerror("b1: %f\n", m_b1);
470 	logerror("b2: %f\n", m_b2);
471 	logerror("a1: %f\n", m_a1);
472 	logerror("a2: %f\n", m_a2);
473 #endif
474 	// peak and shelf filters do not use gain for the entire signal, only for the peak/shelf portions
475 	// side note: the first order lowpass and highpass filter analogues technically don't have gain either,
476 	// but this can be 'faked' by adjusting the bx factors, so we support that anyway, even if it isn't realistic.
477 	if ( (m_type != biquad_type::PEAK)
478 		&& (m_type != biquad_type::LOWSHELF)
479 		&& (m_type != biquad_type::HIGHSHELF) )
480 	{
481 		m_b0 *= m_gain;
482 		m_b1 *= m_gain;
483 		m_b2 *= m_gain;
484 #ifdef FLT_BIQUAD_DEBUG
485 		logerror("b0g: %f\n", m_b0);
486 		logerror("b1g: %f\n", m_b1);
487 		logerror("b2g: %f\n", m_b2);
488 #endif
489 	}
490 }
491 
492 /* Step the filter */
step()493 void filter_biquad_device::step()
494 {
495 	m_w2 = m_w1;
496 	m_w1 = m_w0;
497 	m_w0 = (-m_a1 * m_w1) + (-m_a2 * m_w2) + m_input;
498 	m_output = (m_b0 * m_w0) + (m_b1 * m_w1) + (m_b2 * m_w2);
499 }
500