summaryrefslogtreecommitdiffstatshomepage
diff options
context:
space:
mode:
author m1macrophage <168948267+m1macrophage@users.noreply.github.com>2025-07-26 09:28:57 -0700
committer GitHub <noreply@github.com>2025-07-26 12:28:57 -0400
commit4162b94c261bb47a1ffd91ca4db710dc04d77b64 (patch)
tree3acd7d363dee18365a3e097851ac91fa373ad847
parente1149014e4523f6c1bb37df7dda7c38bb6175297 (diff)
sound/flt_biquad: Added calculator functions for RC-based band-pass filters. (#13888)
* sound/flt_biquad: Added calculator functions for RC-based band-pass filters. linndrum.cpp, roland_tr707.cpp: Got rid of custom implementations and used the above.
-rw-r--r--src/devices/sound/flt_biquad.cpp95
-rw-r--r--src/devices/sound/flt_biquad.h9
-rw-r--r--src/mame/linn/linndrum.cpp47
-rw-r--r--src/mame/roland/roland_tr707.cpp50
4 files changed, 120 insertions, 81 deletions
diff --git a/src/devices/sound/flt_biquad.cpp b/src/devices/sound/flt_biquad.cpp
index 9f21f9e5c25..88d06bcd9ad 100644
--- a/src/devices/sound/flt_biquad.cpp
+++ b/src/devices/sound/flt_biquad.cpp
@@ -442,6 +442,101 @@ filter_biquad_device::biquad_params filter_biquad_device::opamp_diff_bandpass_ca
}
+/* RC-based band-pass filters:
+ *
+ * RR variation: the two resistors are connected to each other:
+ *
+ * Vin -- C1 -- R1 -+-----+- Vout
+ * | |
+ * R2 C2
+ * | |
+ * GND (V)GND
+ *
+ *
+ * CC variation: The two capacitors are connected to each other:
+ *
+ * Vin -- R1 -+- C2 -+- Vout
+ * | |
+ * C1 R2
+ * | |
+ * GND (V)GND
+ *
+ * (V)GND could be a virtual ground.
+ *
+ * BPF transfer function: H(s) = (A * s) / (s ^ 2 + B * s + C)
+ * In the RR configuration, we have:
+ * A = 1 / (R1 * C2)
+ * B = (R1 * C1 + R2 * C2 + R2 * C1) / (R1 * R2 * C1 * C2)
+ * C = 1 / (R1 * R2 * C1 * C2)
+ * In the CC configuration, we have:
+ * A = 1 / (R1 * C1)
+ * B = (R1 * C1 + R2 * C2 + R1 * C2) / (R1 * R2 * C1 * C2)
+ * C = 1 / (R1 * R2 * C1 * C2)
+ * From the standard transfer function for BPFs, we have:
+ * A = gain * (w / Q)
+ * B = w / Q
+ * C = w ^ 2
+ * The calculations of Fc, Q and gain in the *_calc functions below are derived
+ * from the equations above, with some algebra.
+ */
+
+filter_biquad_device& filter_biquad_device::rc_rr_bandpass_setup(double r1, double r2, double c1, double c2)
+{
+ return setup(rc_rr_bandpass_calc(r1, r2, c1, c2));
+}
+
+void filter_biquad_device::rc_rr_bandpass_modify(double r1, double r2, double c1, double c2)
+{
+ modify(rc_rr_bandpass_calc(r1, r2, c1, c2));
+}
+
+filter_biquad_device::biquad_params filter_biquad_device::rc_rr_bandpass_calc(double r1, double r2, double c1, double c2)
+{
+ if ((r1 == 0) || (r2 == 0) || (c1 == 0) || (c2 == 0))
+ {
+ fatalerror("filter_biquad_device::rc_rr_bandpass_calc() - no parameters can be 0; parameters were: r1: %f, r2: %f, c1: %f, c2: %f", r1, r2, c1, c2);
+ }
+ const double x = sqrt(r1 * r2 * c1 * c2);
+ const double y = r1 * c1 + r2 * c2 + r2 * c1;
+ const double z = r2 * c1;
+ filter_biquad_device::biquad_params p;
+ p.type = filter_biquad_device::biquad_type::BANDPASS;
+ p.fc = 1.0 / (2.0 * M_PI * x);
+ p.q = x / y;
+ p.gain = z / y;
+ LOGMASKED(LOG_SETUP, "filter_biquad_device::rc_rr_bandpass_calc(%f %f %f %f) yields: fc = %f, Q = %f, gain = %f\n", r1, r2, c1, c2, p.fc, p.q, p.gain);
+ return p;
+}
+
+filter_biquad_device& filter_biquad_device::rc_cc_bandpass_setup(double r1, double r2, double c1, double c2)
+{
+ return setup(rc_cc_bandpass_calc(r1, r2, c1, c2));
+}
+
+void filter_biquad_device::rc_cc_bandpass_modify(double r1, double r2, double c1, double c2)
+{
+ modify(rc_cc_bandpass_calc(r1, r2, c1, c2));
+}
+
+filter_biquad_device::biquad_params filter_biquad_device::rc_cc_bandpass_calc(double r1, double r2, double c1, double c2)
+{
+ if ((r1 == 0) || (r2 == 0) || (c1 == 0) || (c2 == 0))
+ {
+ fatalerror("filter_biquad_device::rc_cc_bandpass_calc() - no parameters can be 0; parameters were: r1: %f, r2: %f, c1: %f, c2: %f", r1, r2, c1, c2);
+ }
+ const double x = sqrt(r1 * r2 * c1 * c2);
+ const double y = r1 * c1 + r2 * c2 + r1 * c2;
+ const double z = r2 * c2;
+ filter_biquad_device::biquad_params p;
+ p.type = filter_biquad_device::biquad_type::BANDPASS;
+ p.fc = 1.0 / (2.0 * M_PI * x);
+ p.q = x / y;
+ p.gain = z / y;
+ LOGMASKED(LOG_SETUP, "filter_biquad_device::rc_cc_bandpass_calc(%f %f %f %f) yields: fc = %f, Q = %f, gain = %f\n", r1, r2, c1, c2, p.fc, p.q, p.gain);
+ return p;
+}
+
+
//-------------------------------------------------
// device_start - device-specific startup
//-------------------------------------------------
diff --git a/src/devices/sound/flt_biquad.h b/src/devices/sound/flt_biquad.h
index d33c5f1db39..ec292a462c8 100644
--- a/src/devices/sound/flt_biquad.h
+++ b/src/devices/sound/flt_biquad.h
@@ -82,6 +82,15 @@ public:
void opamp_diff_bandpass_modify(double r1, double r2, double c1, double c2);
biquad_params opamp_diff_bandpass_calc(double r1, double r2, double c1, double c2);
+ // RC-based band-pass, resistors connected to each other.
+ filter_biquad_device& rc_rr_bandpass_setup(double r1, double r2, double c1, double c2);
+ void rc_rr_bandpass_modify(double r1, double r2, double c1, double c2);
+ biquad_params rc_rr_bandpass_calc(double r1, double r2, double c1, double c2);
+
+ // RC-based band-pass, capacitors connected to each other.
+ filter_biquad_device& rc_cc_bandpass_setup(double r1, double r2, double c1, double c2);
+ void rc_cc_bandpass_modify(double r1, double r2, double c1, double c2);
+ biquad_params rc_cc_bandpass_calc(double r1, double r2, double c1, double c2);
protected:
// device-level overrides
diff --git a/src/mame/linn/linndrum.cpp b/src/mame/linn/linndrum.cpp
index 4b2ea527bd7..e2fdf63b43c 100644
--- a/src/mame/linn/linndrum.cpp
+++ b/src/mame/linn/linndrum.cpp
@@ -908,6 +908,8 @@ void linndrum_audio_device::update_volume_and_pan(int channel)
// Using -gain_*, because the summing op-amps are inverting.
mixer_input->set_route_gain(0, m_left_mixer, 0, -gain_left);
mixer_input->set_route_gain(0, m_right_mixer, 0, -gain_right);
+ LOGMASKED(LOG_MIX, "Gain update for %s - left: %f, right: %f\n",
+ MIXER_CHANNEL_NAMES[channel], gain_left, gain_right);
if (channel == MIX_CLICK)
{
@@ -931,59 +933,36 @@ void linndrum_audio_device::update_volume_and_pan(int channel)
// For now, scale by some number to match the volume of other voices.
static constexpr const float CLICK_GAIN_CORRECTION = 5;
- float fc = 0;
- float q = 1;
- float gain = 1;
if (volume >= 100)
{
// HPF transfer function: H(s) = (g * s) / (s + w) where
// g = C1 / (C1 + C2)
// w = 1 / (R * (C1 + C2))
- fc = 1.0F / (2 * float(M_PI) * r_voice_gnd * (C_CLICK_DCBLOCK + C_CLICK_WIPER));
- gain = C_CLICK_DCBLOCK / (C_CLICK_DCBLOCK + C_CLICK_WIPER);
+ const float fc = 1.0F / (2 * float(M_PI) * r_voice_gnd * (C_CLICK_DCBLOCK + C_CLICK_WIPER));
+ const float gain = C_CLICK_DCBLOCK / (C_CLICK_DCBLOCK + C_CLICK_WIPER);
m_click_bpf->modify(filter_biquad_device::biquad_type::HIGHPASS1P1Z, fc, 1, gain * CLICK_GAIN_CORRECTION);
+ LOGMASKED(LOG_MIX, "- HPF cutoff: %.2f Hz, Gain: %.3f\n", fc, gain);
}
else if (volume > 0)
{
- // BPF transfer function: H(s) = (A * s) / (s ^ 2 + B * s + C)
- // Where:
- // A = 1 / (R1 * C2)
- // B = (C1 * R1 + C1 * R2 + C2 * R2) / (R1 * R2 * C1 * C2)
- // C = 1 / (R1 * R2 * C1 * C2).
- // From the standard transfer function for BPFs, we have:
- // A = gain * (w / Q)
- // B = w / Q
- // C = w ^ 2
- // The calculations of Fc, Q and gain below are derived from the
- // equations above, with some algebra.
-
- const float x = sqrtf(r0 * r_wiper_gnd * C_CLICK_DCBLOCK * C_CLICK_WIPER);
- const float y = C_CLICK_DCBLOCK * r0 + C_CLICK_DCBLOCK * r_wiper_gnd + C_CLICK_WIPER * r_wiper_gnd;
- fc = 1.0F / (2 * float(M_PI) * x);
- q = x / y;
- gain = r_wiper_gnd * C_CLICK_DCBLOCK / y;
-
- // The transfer function above includes the effect of the volume
+ filter_biquad_device::biquad_params p = m_click_bpf->rc_rr_bandpass_calc(r0, r_wiper_gnd, C_CLICK_DCBLOCK, C_CLICK_WIPER);
+ // The filter params above include the effect of the volume
// potentiometer. But `gain_left` and `gain_right` already incorporate
// that effect. So it needs to be undone from the filter's gain.
- gain *= 1.0F / RES_VOLTAGE_DIVIDER(r0, r_wiper_gnd);
-
- m_click_bpf->modify(filter_biquad_device::biquad_type::BANDPASS, fc, q, gain * CLICK_GAIN_CORRECTION);
+ p.gain /= RES_VOLTAGE_DIVIDER(r0, r_wiper_gnd);
+ // See comments for CLICK_GAIN_CORRECTION.
+ p.gain *= CLICK_GAIN_CORRECTION;
+ m_click_bpf->modify(p);
+ LOGMASKED(LOG_MIX, "- BPF cutoff: %.2f Hz, Q: %.3f, Gain: %.3f\n", p.fc, p.q, p.gain);
}
// Else, if the volume is 0, don't change the BPF's configuration to avoid divisions by 0.
-
- LOGMASKED(LOG_MIX, "Gain update for %s - left: %f, right: %f, %s cutoff: %.2f Hz, Q: %.3f, Gain: %.3f\n",
- MIXER_CHANNEL_NAMES[MIX_CLICK], gain_left, gain_right,
- (volume >= 100) ? "HPF" : "BPF", fc, q, gain);
}
else
{
// The rest of the voices just have a DC-blocking filter. Its exact cutoff
// will depend on the volume and pan settings, but it won't be audible.
m_voice_hpf[channel]->filter_rc_set_RC(filter_rc_device::HIGHPASS, r_voice_gnd, 0, 0, C_VOICE);
- LOGMASKED(LOG_MIX, "Gain update for %s - left: %f, right: %f, HPF cutoff: %.2f Hz\n",
- MIXER_CHANNEL_NAMES[channel], gain_left, gain_right,
- 1.0F / (2 * float(M_PI) * r_voice_gnd * C_VOICE));
+ LOGMASKED(LOG_MIX, "- HPF cutoff: %.2f Hz\n", 1.0F / (2 * float(M_PI) * r_voice_gnd * C_VOICE));
}
}
diff --git a/src/mame/roland/roland_tr707.cpp b/src/mame/roland/roland_tr707.cpp
index 47caca2dbe9..28dbfa03a1c 100644
--- a/src/mame/roland/roland_tr707.cpp
+++ b/src/mame/roland/roland_tr707.cpp
@@ -262,7 +262,6 @@ private:
static inline constexpr double VBE = 0.6; // BJT base-emitter voltage drop.
static inline constexpr u16 MAX_CYMBAL_COUNTER = 0x8000;
- static filter_biquad_device::biquad_params rc_bpf(double r1, double r2, double c1, double c2, bool crrc);
static double mux_dac_v(double v_eg, u8 data);
void advance_sample_w(offs_t offset, u16 data);
@@ -602,7 +601,7 @@ void tr707_audio_device::device_add_mconfig(machine_config &config)
for (int i = 0; i < MC_COUNT; ++i)
{
FILTER_BIQUAD(config, m_voice_bpf[i]);
- m_voice_bpf[i]->setup(rc_bpf(bpf_comps[i][0], bpf_comps[i][1], bpf_comps[i][2], bpf_comps[i][3], false));
+ m_voice_bpf[i]->rc_cc_bandpass_setup(bpf_comps[i][0], bpf_comps[i][1], bpf_comps[i][2], bpf_comps[i][3]);
voices[i]->add_route(0, m_voice_bpf[i], 1.0);
FILTER_VOLUME(config, m_level[i]);
@@ -622,8 +621,8 @@ void tr707_audio_device::device_add_mconfig(machine_config &config)
// making it to the left and right output sockets.
auto &left_bpf = FILTER_BIQUAD(config, "left_out_bpf");
auto &right_bpf = FILTER_BIQUAD(config, "right_out_bpf");
- left_bpf.setup(rc_bpf(RES_K(1), RES_K(47), CAP_U(10), CAP_U(0.01), true)); // R114, R112, C79, C76
- right_bpf.setup(rc_bpf(RES_K(1), RES_K(47), CAP_U(10), CAP_U(0.01), true)); // R113, R111, C80, C77
+ left_bpf.rc_rr_bandpass_setup(RES_K(1), RES_K(47), CAP_U(10), CAP_U(0.01)); // R114, R112, C79, C76
+ right_bpf.rc_rr_bandpass_setup(RES_K(1), RES_K(47), CAP_U(10), CAP_U(0.01)); // R113, R111, C80, C77
m_left_mixer->add_route(0, left_bpf, 1.0);
m_right_mixer->add_route(0, right_bpf, 1.0);
@@ -655,49 +654,6 @@ void tr707_audio_device::device_reset()
update_mix(channel);
}
-// Biquad parameters for an RC-based bandpass filter.
-// crrc == true:
-// Vin -- C1 -- R1 -+---+- Vout
-// | |
-// R2 C2
-// | |
-// GND GND
-// crrc == false:
-// Vin -- R1 -+- C2 -+- Vout
-// | |
-// C1 R2
-// | |
-// GND GND
-// TODO: move to sound/flt_biquad.
-filter_biquad_device::biquad_params tr707_audio_device::rc_bpf(double r1, double r2, double c1, double c2, bool crrc)
-{
- // BPF transfer function: H(s) = (A * s) / (s ^ 2 + B * s + C)
- // In the C-R-R-C configuration, we have:
- // A = 1 / (R1 * C2)
- // B = (R1 * C1 + R2 * C2 + R2 * C1) / (R1 * R2 * C1 * C2)
- // C = 1 / (R1 * R2 * C1 * C2)
- // In the R-C-C-R configuration, we have:
- // A = 1 / (R1 * C1)
- // B = (R1 * C1 + R2 * C2 + R1 * C2) / (R1 * R2 * C1 * C2)
- // C = 1 / (R1 * R2 * C1 * C2)
- // From the standard transfer function for BPFs, we have:
- // A = gain * (w / Q)
- // B = w / Q
- // C = w ^ 2
- // The calculations of Fc, Q and gain below are derived from the
- // equations above, with some algebra.
-
- const double x = sqrt(r1 * r2 * c1 * c2);
- const double y = crrc ? (r1 * c1 + r2 * c2 + r2 * c1) : (r1 * c1 + r2 * c2 + r1 * c2);
- const double z = crrc ? (r2 * c1) : (r2 * c2);
- filter_biquad_device::biquad_params params;
- params.type = filter_biquad_device::biquad_type::BANDPASS;
- params.fc = 1.0 / (2.0 * M_PI * x);
- params.q = x / y;
- params.gain = z / y;
- return params;
-}
-
// Computes the output voltage of the mux DAC circuit, given the voltage at the
// output of the envelope generator and the DAC data value.
double tr707_audio_device::mux_dac_v(double v_eg, u8 data)