diff --git a/src/devices/sound/flt_biquad.cpp b/src/devices/sound/flt_biquad.cpp index 9f21f9e5c25aa..88d06bcd9ad63 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 d33c5f1db3997..ec292a462c825 100644 --- a/src/devices/sound/flt_biquad.h +++ b/src/devices/sound/flt_biquad.h @@ -82,6 +82,15 @@ class filter_biquad_device : public device_t, public device_sound_interface 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 4b2ea527bd7dd..e2fdf63b43c9f 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 47caca2dbe921..28dbfa03a1ccb 100644 --- a/src/mame/roland/roland_tr707.cpp +++ b/src/mame/roland/roland_tr707.cpp @@ -262,7 +262,6 @@ class tr707_audio_device : public device_t 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)