From e76a5e021740e5631e90a3280a1e5e634de7cb6e Mon Sep 17 00:00:00 2001 From: Lord-Nightmare Date: Thu, 19 Nov 2020 07:04:33 -0500 Subject: flt_biquad: Allow recalculating the filter values at runtime if a component value can be altered or changed through some means. [Lord Nightmare] --- src/devices/sound/flt_biquad.cpp | 83 ++++++++++++++++++++++++---------------- src/devices/sound/flt_biquad.h | 16 +++++++- 2 files changed, 64 insertions(+), 35 deletions(-) diff --git a/src/devices/sound/flt_biquad.cpp b/src/devices/sound/flt_biquad.cpp index c625b28c6df..8daa78e07ae 100644 --- a/src/devices/sound/flt_biquad.cpp +++ b/src/devices/sound/flt_biquad.cpp @@ -76,9 +76,17 @@ filter_biquad_device& filter_biquad_device::setup(biquad_type type, double fc, d m_gain = gain; return *this; } +filter_biquad_device& filter_biquad_device::setup(filter_biquad_device::biquad_params p) +{ + m_type = p.type; + m_fc = p.fc; + m_q = p.q; + m_gain = p.gain; + return *this; +} -// update an existing instance with new filter parameters -void filter_biquad_device::update_params(biquad_type type, double fc, double q, double gain) +// modify an existing instance with new filter parameters +void filter_biquad_device::modify(biquad_type type, double fc, double q, double gain) { m_stream->update(); m_type = type; @@ -87,7 +95,15 @@ void filter_biquad_device::update_params(biquad_type type, double fc, double q, m_gain = gain; recalc(); } - +void filter_biquad_device::modify(filter_biquad_device::biquad_params p) +{ + m_stream->update(); + m_type = p.type; + m_fc = p.fc; + m_q = p.q; + m_gain = p.gain; + recalc(); +} //------------------------------------------------- // Filter setup helpers for various filter models @@ -141,13 +157,12 @@ filter_biquad_device& filter_biquad_device::opamp_sk_lowpass_setup(double r1, do /* Setup a biquad filter structure based on a single op-amp Multiple-Feedback low-pass filter circuit. * NOTE: vRef is not definable when setting up the filter. - * If the analog effects caused by vRef are important to the operation of the specific filter - * in question, a netlist implementation may work better under those circumstances. - * NOTE2: a variant of this circuit has the c1 capacitor left off. if so, set c1 to 0 - * which will form a controllable gain 1st order version of the circuit. - * NOTE3: There is a well known 'proper' 1st order version of this circuit where r2 is - * a dead short, and c1 is also ommitted as in NOTE2. Set both r2 to 0 and c1 to 0 to - * emulate a circuit of this type. + * If the analog effects caused by vRef are important to the operation of the specific + * filter in question, a netlist implementation may work better under those circumstances. + * NOTE2: There is a well known 'proper' 1st order version of this circuit where r2 is + * a dead short, and c1 omitted. set both c1 and r2 to 0 in this case. + * NOTE3: a variant of NOTE2 has only the c1 capacitor left off, and r2 present. if so, + * set c1 to 0 and r2 to its expected value. * * .--------+---------. * | | | @@ -165,37 +180,40 @@ filter_biquad_device& filter_biquad_device::opamp_sk_lowpass_setup(double r1, do */ filter_biquad_device& filter_biquad_device::opamp_mfb_lowpass_setup(double r1, double r2, double r3, double c1, double c2) { + filter_biquad_device::biquad_params p = opamp_mfb_lowpass_calc(r1, r2, r3, c1, c2); + return setup(p); +} + +void filter_biquad_device::opamp_mfb_lowpass_modify(double r1, double r2, double r3, double c1, double c2) +{ + filter_biquad_device::biquad_params p = opamp_mfb_lowpass_calc(r1, r2, r3, c1, c2); + modify(p); +} + +filter_biquad_device::biquad_params filter_biquad_device::opamp_mfb_lowpass_calc(double r1, double r2, double r3, double c1, double c2) +{ + filter_biquad_device::biquad_params r; if ((r1 == 0) || ((r2 == 0) && (c1 != 0)) || (r3 == 0) || (c2 == 0)) { - 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. */ - } - double const gain = -r3 / r1; - double fc, q = (M_SQRT2 / 2.0); - 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. - { - fc = (r1 * r3) / (2 * M_PI * ((r1 * r2) + (r1 * r3) + (r2 * r3)) * r3 * c2); -#ifdef FLT_BIQUAD_DEBUG_SETUP - logerror("filter_biquad_device::opamp_mfb_lowpass_setup() in degraded mode yields: fc = %f, Q = %f(ignored), gain = %f\n", fc, q, gain); -#endif - return setup(biquad_type::LOWPASS1P, fc, q, gain); + fatalerror("filter_biquad_device::opamp_mfb_lowpass_calc() - 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. */ } - else if ((r2 == 0) && (c1 == 0)) // proper first order case, set both C1 to 0 and R2 to 0 for the first order + r.gain = -r3 / r1; + r.q = (M_SQRT2 / 2.0); + if (c1 == 0) // if both R2 and C1 are 0, it is the 'proper' first order case. There do exist some unusual filters where R2 is not 0, though. In both cases this yields a single-pole filter with limited configurable gain, and a Q of ~0.707. R2 being zero makes the (r1 * r3) numerator term cancel out to 1.0. { - fc = 1.0 / (2 * M_PI * r3 * c2); -#ifdef FLT_BIQUAD_DEBUG_SETUP - logerror("filter_biquad_device::opamp_mfb_lowpass_setup() in 1st order mode yields: fc = %f, Q = %f(ignored), gain = %f\n", fc, q, gain); -#endif - return setup(biquad_type::LOWPASS1P, fc, q, gain); + r.fc = (r1 * r3) / (2 * M_PI * ((r1 * r2) + (r1 * r3) + (r2 * r3)) * r3 * c2); + r.type = biquad_type::LOWPASS1P; } else // common case, (r2 != 0) && (c1 != 0) { - fc = 1.0 / (2 * M_PI * sqrt(r2 * r3 * c1 * c2)); - q = sqrt(r2 * r3 * c1 * c2) / ((r3 * c2) + (r2 * c2) + ((r2 * c2) * -gain)); + r.fc = 1.0 / (2 * M_PI * sqrt(r2 * r3 * c1 * c2)); + r.q = sqrt(r2 * r3 * c1 * c2) / ((r3 * c2) + (r2 * c2) + ((r2 * c2) * -r.gain)); + r.type = biquad_type::LOWPASS; + } #ifdef FLT_BIQUAD_DEBUG_SETUP - logerror("filter_biquad_device::opamp_mfb_lowpass_setup() in 2nd order mode yields: fc = %f, Q = %f, gain = %f\n", fc, q, gain); + logerror("filter_biquad_device::opamp_mfb_lowpass_setup(%f, %f, %f, %f, %f) yields:\n\ttype = %d, fc = %f, Q = %f, gain = %f\n", r1, r2, r3, c1*1000000, c2*1000000, static_cast(r.type), r.fc, r.q, r.gain); #endif - return setup(biquad_type::LOWPASS, fc, q, gain); - } + return r; } /* Setup a biquad filter structure based on a single op-amp Multiple-Feedback band-pass filter circuit. @@ -272,7 +290,6 @@ filter_biquad_device& filter_biquad_device::opamp_mfb_highpass_setup(double r1, { 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. */ } - // TODO: if c1 is 0/shorted, should the circuit should work with a gain of 1 in a first order mode? double const gain = -c1 / c3; double const fc = 1.0 / (2 * M_PI * sqrt(c2 * c3 * r1 * r2)); diff --git a/src/devices/sound/flt_biquad.h b/src/devices/sound/flt_biquad.h index 17050c75017..97c434d9f79 100644 --- a/src/devices/sound/flt_biquad.h +++ b/src/devices/sound/flt_biquad.h @@ -27,12 +27,22 @@ public: HIGHSHELF }; + struct biquad_params + { + biquad_type type; + double fc; + double q; + double gain; + }; + filter_biquad_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock = 0); // set up the filter with the specified filter parameters and return a pointer to the new device filter_biquad_device& setup(biquad_type type, double fc, double q, double gain); - // update an existing instance with new filter parameters - void update_params(biquad_type type, double fc, double q, double gain); + filter_biquad_device& setup(biquad_params p); + // modify an existing instance with new filter parameters + void modify(biquad_type type, double fc, double q, double gain); + void modify(biquad_params p); // helper setup functions to create common filters representable by biquad filters // Sallen-Key low-pass @@ -42,6 +52,8 @@ public: // Multiple-Feedback low-pass filter_biquad_device& opamp_mfb_lowpass_setup(double r1, double r2, double r3, double c1, double c2); + void opamp_mfb_lowpass_modify(double r1, double r2, double r3, double c1, double c2); + biquad_params opamp_mfb_lowpass_calc(double r1, double r2, double r3, double c1, double c2); // Multiple-Feedback band-pass filter_biquad_device& opamp_mfb_bandpass_setup(double r1, double r2, double r3, double c1, double c2); -- cgit v1.2.3