summaryrefslogtreecommitdiffstatshomepage
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/devices/sound/flt_biquad.cpp487
-rw-r--r--src/devices/sound/flt_biquad.h80
-rw-r--r--src/devices/sound/hc55516.cpp572
-rw-r--r--src/devices/sound/hc55516.h155
-rw-r--r--src/mame/audio/exidy.cpp46
-rw-r--r--src/mame/audio/exidy.h10
-rw-r--r--src/mame/audio/s11c_bg.cpp67
-rw-r--r--src/mame/audio/s11c_bg.h4
-rw-r--r--src/mame/audio/williams.cpp8
-rw-r--r--src/mame/audio/williams.h1
-rw-r--r--src/mame/drivers/s11.cpp47
-rw-r--r--src/mame/drivers/s11a.cpp31
-rw-r--r--src/mame/drivers/s11b.cpp33
-rw-r--r--src/mame/drivers/s11c.cpp2
-rw-r--r--src/mame/drivers/williams.cpp43
-rw-r--r--src/mame/includes/s11.h9
-rw-r--r--src/mame/includes/williams.h7
-rw-r--r--src/mame/machine/williams.cpp6
18 files changed, 1328 insertions, 280 deletions
diff --git a/src/devices/sound/flt_biquad.cpp b/src/devices/sound/flt_biquad.cpp
new file mode 100644
index 00000000000..a0416bfafb1
--- /dev/null
+++ b/src/devices/sound/flt_biquad.cpp
@@ -0,0 +1,487 @@
+// license:BSD-3-Clause
+// copyright-holders:K.Wilkins,Couriersud,Derrick Renaud,Frank Palazzolo,Jonathan Gevaryahu
+/*
+ This is an implementation of a Direct-form II digital biquad filter,
+ intended for use in audio paths for filtering audio to or from other
+ stream devices.
+
+ It has a number of constructor-helpers for automatically generating
+ a biquad filter equivalent to the filter response of a few standard
+ analog second order filter topographies.
+
+ This biquad filter implementation is based on one written by Frank
+ Palazzolo, K. Wilkins, Couriersud, and Derrick Renaud, with some changes:
+ * It uses the Q factor directly in the filter definitions, rather than the damping factor (1/Q)
+ * It implements every common type of digital biquad filter which I could find documentation for.
+ * The filter is Direct-form II instead of Direct-form I, which results in shorter compiled code.
+
+ Possibly useful features which aren't implemented because nothing uses them yet:
+ * More Sallen-Key filter variations (band-pass, high-pass)
+ * Direct control of the 5 normalized biquad parameters for a custom/raw parameter filter.
+*/
+#include "emu.h"
+#include "flt_biquad.h"
+
+// we need the M_SQRT2 constant
+#ifndef M_SQRT2
+#define M_SQRT2 1.41421356237309504880
+#endif
+
+// define this to display debug info about the filters being set up
+#undef FLT_BIQUAD_DEBUG_SETUP
+
+// device type definition
+DEFINE_DEVICE_TYPE(FILTER_BIQUAD, filter_biquad_device, "filter_biquad", "Biquad Filter")
+
+// allow the enum class for the biquad filter type to be saved by the savestate system
+ALLOW_SAVE_TYPE(filter_biquad_device::biquad_type);
+
+//**************************************************************************
+// LIVE DEVICE
+//**************************************************************************
+
+//-------------------------------------------------
+// filter_biquad_device - constructor
+//-------------------------------------------------
+
+// initialize with some sane defaults for a highpass filter with a cutoff at 16hz, same as flt_rc's 'ac' mode.
+filter_biquad_device::filter_biquad_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
+ : device_t(mconfig, FILTER_BIQUAD, tag, owner, clock),
+ device_sound_interface(mconfig, *this),
+ m_stream(nullptr),
+ m_type(biquad_type::HIGHPASS),
+ m_last_sample_rate(0),
+ m_fc(16.0),
+ m_q(M_SQRT2/2.0),
+ m_gain(1.0),
+ m_input(0.0),
+ m_w0(0.0),
+ m_w1(0.0),
+ m_w2(0.0),
+ m_output(0.0),
+ m_a1(0.0),
+ m_a2(0.0),
+ m_b0(1.0),
+ m_b1(0.0),
+ m_b2(0.0)
+{
+}
+
+// set up the filter with the specified parameters and return a pointer to the new device
+filter_biquad_device& filter_biquad_device::setup(biquad_type type, double fc, double q, double gain)
+{
+ m_type = type;
+ m_fc = fc;
+ m_q = q;
+ m_gain = 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)
+{
+ m_stream->update();
+ m_type = type;
+ m_fc = fc;
+ m_q = q;
+ m_gain = gain;
+ recalc();
+}
+
+
+//-------------------------------------------------
+// Filter setup helpers for various filter models
+//-------------------------------------------------
+
+// Sallen-Key filters
+
+/* Setup a biquad filter structure based on a single op-amp Sallen-Key low-pass filter circuit.
+ *
+ * .----------------------------.
+ * | |
+ * --- c2 |
+ * --- |
+ * | |
+ * r1 | r2 |\ |
+ * In >----ZZZZ----+--ZZZZ---+--------+ | \ |
+ * | '--|+ \ |
+ * --- c1 | >--+------> out
+ * --- .--|- / |
+ * | | | / |
+ * gnd | |/ |
+ * | |
+ * | r4 |
+ * +--ZZZZ---'
+ * |
+ * Z
+ * Z r3
+ * Z
+ * |
+ * gnd
+ */
+filter_biquad_device& filter_biquad_device::opamp_sk_lowpass_setup(double r1, double r2, double r3, double r4, double c1, double c2)
+{
+ if ((r1 == 0) || (r2 == 0) || (r3 == 0) || (r4 == 0) || (c1 == 0) || (c2 == 0))
+ {
+ 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. */
+ }
+
+ // 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'
+ double const gain = (r3 + r4) / r3;
+ double const fc = 1.0 / (2 * M_PI * sqrt(r1 * r2 * c1 * c2));
+ double const q = sqrt(r1 * r2 * c1 * c2) / ((r1 * c1) + (r2 * c1) + ((r2 * c2) * (1.0 - gain)));
+#ifdef FLT_BIQUAD_DEBUG_SETUP
+ logerror("filter_biquad_device::opamp_sk_lowpass_setup() yields: fc = %f, Q = %f, gain = %f\n", fc, q, gain);
+#endif
+ return setup(biquad_type::LOWPASS, fc, q, gain);
+}
+
+// Multiple-Feedback filters
+// (This is sometimes called a 'Rauch' filter circuit.)
+
+/* 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.
+ *
+ * .--------+---------.
+ * | | |
+ * Z --- c2 |
+ * Z r3 --- |
+ * Z | |
+ * r1 | r2 | |\ |
+ * In >----ZZZZ----+---------+--ZZZZ--+ | \ |
+ * | '--|- \ |
+ * --- c1 | >--+------> out
+ * --- .--|+ /
+ * | | | /
+ * gnd vRef >---' |/
+ *
+ */
+filter_biquad_device& filter_biquad_device::opamp_mfb_lowpass_setup(double r1, double r2, double r3, double c1, double c2)
+{
+ if ((r1 == 0) || (r2 == 0) || (r3 == 0) || (c2 == 0))
+ {
+ fatalerror("filter_biquad_device::opamp_mfb_lowpass_setup() - only c1 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. */
+ }
+
+ double const gain = -r3 / r1;
+ double fc, q = (M_SQRT2 / 2.0);
+ if (c1 == 0) // set C1 to 0 to run this filter in a degraded 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);
+ }
+ else
+ {
+ fc = 1.0 / (2 * M_PI * sqrt(r2 * r3 * c1 * c2));
+ q = sqrt(r2 * r3 * c1 * c2) / ((r3 * c2) + (r2 * c2) + ((r2 * c2) * -gain));
+#ifdef FLT_BIQUAD_DEBUG_SETUP
+ logerror("filter_biquad_device::opamp_mfb_lowpass_setup() yields: fc = %f, Q = %f, gain = %f\n", fc, q, gain);
+#endif
+ return setup(biquad_type::LOWPASS, fc, q, gain);
+ }
+}
+
+/* Setup a biquad filter structure based on a single op-amp Multiple-Feedback band-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: If r2 is not used, then set it to 0 ohms, the code will ignore it and assume a fixed gain of 1.
+ *
+ * .--------+---------.
+ * | | |
+ * --- c1 Z |
+ * --- Z r3 |
+ * | Z |
+ * r1 | c2 | |\ |
+ * In >----ZZZZ----+---------+--||----+ | \ |
+ * Z '--|- \ |
+ * Z r2 | >--+------> out
+ * Z .--|+ /
+ * | | | /
+ * gnd vRef >---' |/
+ *
+ */
+filter_biquad_device& filter_biquad_device::opamp_mfb_bandpass_setup(double r1, double r2, double r3, double c1, double c2)
+{
+ if ((r1 == 0) || (r3 == 0) || (c1 == 0) || (c2 == 0))
+ {
+ 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. */
+ }
+
+ double r_in, gain;
+
+ if (r2 == 0)
+ {
+ gain = 1;
+ r_in = r1;
+ }
+ else
+ {
+ gain = r2 / (r1 + r2);
+ r_in = 1.0 / (1.0/r1 + 1.0/r2);
+ }
+
+ double const fc = 1.0 / (2 * M_PI * sqrt(r_in * r3 * c1 * c2));
+ double const q = sqrt(r3 / r_in * c1 * c2) / (c1 + c2);
+ gain *= -r3 / r_in * c2 / (c1 + c2);
+#ifdef FLT_BIQUAD_DEBUG_SETUP
+ logerror("filter_biquad_device::opamp_mfb_bandpass_setup() yields: fc = %f, Q = %f, gain = %f\n", fc, q, gain);
+#endif
+ return setup(biquad_type::BANDPASS, fc, q, gain);
+}
+
+/* Setup a biquad filter structure based on a single op-amp Multiple-Feedback high-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.
+ *
+ * .--------+---------.
+ * | | |
+ * --- c3 Z |
+ * --- Z r2 |
+ * | Z |
+ * c1 | c2 | |\ |
+ * In >-----||-----+---------+---||---+ | \ |
+ * Z '--|- \ |
+ * Z r1 | >--+------> out
+ * Z .--|+ /
+ * | | | /
+ * gnd vRef >---' |/
+ *
+ */
+filter_biquad_device& filter_biquad_device::opamp_mfb_highpass_setup(double r1, double r2, double c1, double c2, double c3)
+{
+ if ((r1 == 0) || (r2 == 0) || (c1 == 0) || (c2 == 0) || (c3 == 0))
+ {
+ 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));
+ double const q = sqrt(c2 * c3 * r1 * r2) / ((c2 * r1) + (c3 * r1) + ((c3 * r1) * -gain));
+#ifdef FLT_BIQUAD_DEBUG_SETUP
+ logerror("filter_biquad_device::opamp_mfb_highpass_setup() yields: fc = %f, Q = %f, gain = %f\n", fc, q, gain);
+#endif
+ return setup(biquad_type::HIGHPASS, fc, q, gain);
+}
+
+
+//-------------------------------------------------
+// device_start - device-specific startup
+//-------------------------------------------------
+
+void filter_biquad_device::device_start()
+{
+ m_stream = stream_alloc(1, 1, SAMPLE_RATE_OUTPUT_ADAPTIVE);
+ m_last_sample_rate = 0;
+ recalc();
+
+ save_item(NAME(m_type));
+ save_item(NAME(m_last_sample_rate));
+ save_item(NAME(m_fc));
+ save_item(NAME(m_q));
+ save_item(NAME(m_gain));
+ save_item(NAME(m_input));
+ save_item(NAME(m_w0));
+ save_item(NAME(m_w1));
+ save_item(NAME(m_w2));
+ save_item(NAME(m_output));
+ save_item(NAME(m_a1));
+ save_item(NAME(m_a2));
+ save_item(NAME(m_b0));
+ save_item(NAME(m_b1));
+ save_item(NAME(m_b2));
+}
+
+
+//-------------------------------------------------
+// sound_stream_update - handle a stream update
+//-------------------------------------------------
+
+void filter_biquad_device::sound_stream_update(sound_stream &stream, std::vector<read_stream_view> const &inputs, std::vector<write_stream_view> &outputs)
+{
+ auto &src = inputs[0];
+ auto &dst = outputs[0];
+
+ if (m_last_sample_rate != m_stream->sample_rate())
+ {
+ recalc();
+ m_last_sample_rate = m_stream->sample_rate();
+ }
+
+ for (int sampindex = 0; sampindex < dst.samples(); sampindex++)
+ {
+ m_input = src.get(sampindex);
+ step();
+ dst.put(sampindex, m_output);
+ }
+}
+
+
+/* Calculate the filter context based on the passed filter type info.
+ * m_type - 1 of the 9 defined filter types
+ * m_fc - center frequency
+ * m_q - 'Q' (quality) factor of filter (1/damp)
+ * m_gain - overall filter gain. Set to 1.0 if not needed. The exact meaning of gain changes depending on the filter type.
+ */
+void filter_biquad_device::recalc()
+{
+ double const MGain = fabs(m_gain); // absolute multiplicative gain
+ double const DBGain = log10(MGain) * 20.0; // gain in dB
+ double const AMGain = pow(10, fabs(DBGain) / 20.0); // multiplicative gain of absolute DB
+ double const K = tan(M_PI * m_fc / m_stream->sample_rate());
+ double const Ksquared = K * K;
+ double const KoverQ = K / m_q;
+ double normal = 1.0 / (1.0 + KoverQ + Ksquared);
+
+ m_a1 = 2.0 * (Ksquared - 1.0) * normal;
+ m_a2 = (1.0 - KoverQ + Ksquared) * normal;
+
+ switch (m_type)
+ {
+ case biquad_type::LOWPASS1P:
+ m_a1 = exp(-2.0 * M_PI * (m_fc / m_stream->sample_rate()));
+ m_b0 = 1.0 - m_a1;
+ m_a1 = -m_a1;
+ m_b1 = m_b2 = m_a2 = 0.0;
+ break;
+ case biquad_type::HIGHPASS1P:
+ m_a1 = -exp(-2.0 * M_PI * (0.5 - m_fc / m_stream->sample_rate()));
+ m_b0 = 1.0 + m_a1;
+ m_a1 = -m_a1;
+ m_b1 = m_b2 = m_a2 = 0.0;
+ break;
+ case biquad_type::LOWPASS:
+ m_b0 = Ksquared * normal;
+ m_b1 = 2.0 * m_b0;
+ m_b2 = 1.0 * m_b0;
+ m_a1 = 2.0 * (Ksquared - 1.0) * normal;
+ m_a2 = (1.0 - KoverQ + Ksquared) * normal;
+ break;
+ case biquad_type::HIGHPASS:
+ m_b0 = 1.0 * normal;
+ m_b1 = -2.0 * m_b0;
+ m_b2 = 1.0 * m_b0;
+ m_a1 = 2.0 * (Ksquared - 1.0) * normal;
+ m_a2 = (1.0 - KoverQ + Ksquared) * normal;
+ break;
+ case biquad_type::BANDPASS:
+ m_b0 = KoverQ * normal;
+ m_b1 = 0.0;
+ m_b2 = -1.0 * m_b0;
+ m_a1 = 2.0 * (Ksquared - 1.0) * normal;
+ m_a2 = (1.0 - KoverQ + Ksquared) * normal;
+ break;
+ case biquad_type::NOTCH:
+ m_b0 = (1.0 + Ksquared) * normal;
+ m_b1 = 2.0 * (Ksquared - 1.0) * normal;
+ m_b2 = 1.0 * m_b0;
+ m_a1 = 1.0 * m_b1;
+ m_a2 = (1.0 - KoverQ + Ksquared) * normal;
+ break;
+ case biquad_type::PEAK:
+ if (DBGain >= 0.0)
+ {
+ m_b0 = (1.0 + (AMGain * KoverQ) + Ksquared) * normal;
+ m_b1 = 2.0 * (Ksquared - 1.0) * normal;
+ m_b2 = (1.0 - (AMGain * KoverQ) + Ksquared) * normal;
+ m_a1 = 1.0 * m_b1;
+ m_a2 = (1.0 - KoverQ + Ksquared) * normal;
+ }
+ else
+ {
+ normal = 1.0 / (1.0 + (AMGain * KoverQ) + Ksquared);
+ m_b0 = (1.0 + KoverQ + Ksquared) * normal;
+ m_b1 = 2.0 * (Ksquared - 1.0) * normal;
+ m_b2 = (1.0 - KoverQ + Ksquared) * normal;
+ m_a1 = 1.0 * m_b1;
+ m_a2 = (1.0 - (AMGain * KoverQ) + Ksquared) * normal;
+ }
+ break;
+ case biquad_type::LOWSHELF:
+ if (DBGain >= 0.0)
+ {
+ normal = 1.0 / (1.0 + M_SQRT2 * K + Ksquared);
+ m_b0 = (1.0 + sqrt(2.0 * AMGain) * K + AMGain * Ksquared) * normal;
+ m_b1 = 2.0 * (AMGain * Ksquared - 1.0) * normal;
+ m_b2 = (1.0 - sqrt(2.0 * AMGain) * K + AMGain * Ksquared) * normal;
+ m_a1 = 2.0 * (Ksquared - 1.0) * normal;
+ m_a2 = (1.0 - M_SQRT2 * K + Ksquared) * normal;
+ }
+ else
+ {
+ normal = 1.0 / (1.0 + sqrt(2.0 * AMGain) * K + AMGain * Ksquared);
+ m_b0 = (1.0 + M_SQRT2 * K + Ksquared) * normal;
+ m_b1 = 2.0 * (Ksquared - 1.0) * normal;
+ m_b2 = (1.0 - M_SQRT2 * K + Ksquared) * normal;
+ m_a1 = 2.0 * (AMGain * Ksquared - 1.0) * normal;
+ m_a2 = (1.0 - sqrt(2.0 * AMGain) * K + AMGain * Ksquared) * normal;
+ }
+ break;
+ case biquad_type::HIGHSHELF:
+ if (DBGain >= 0.0)
+ {
+ normal = 1.0 / (1.0 + M_SQRT2 * K + Ksquared);
+ m_b0 = (AMGain + sqrt(2.0 * AMGain) * K + Ksquared) * normal;
+ m_b1 = 2.0 * (Ksquared - AMGain) * normal;
+ m_b2 = (AMGain - sqrt(2.0 * AMGain) * K + Ksquared) * normal;
+ m_a1 = 2.0 * (Ksquared - 1.0) * normal;
+ m_a2 = (1.0 - M_SQRT2 * K + Ksquared) * normal;
+ }
+ else
+ {
+ normal = 1.0 / (AMGain + sqrt(2.0 * AMGain) * K + Ksquared);
+ m_b0 = (1.0 + M_SQRT2 * K + Ksquared) * normal;
+ m_b1 = 2.0 * (Ksquared - 1.0) * normal;
+ m_b2 = (1.0 - M_SQRT2 * K + Ksquared) * normal;
+ m_a1 = 2.0 * (Ksquared - AMGain) * normal;
+ m_a2 = (AMGain - sqrt(2.0 * AMGain) * K + Ksquared) * normal;
+ }
+ break;
+ default:
+ fatalerror("filter_biquad_device::recalc() - Invalid filter type!");
+ break;
+ }
+#ifdef FLT_BIQUAD_DEBUG
+ logerror("Calculated Parameters:\n");
+ logerror( "Gain (dB): %f, (raw): %f\n", DBGain, MGain);
+ logerror( "k: %f\n", K);
+ logerror( "normal: %f\n", normal);
+ logerror("b0: %f\n", m_b0);
+ logerror("b1: %f\n", m_b1);
+ logerror("b2: %f\n", m_b2);
+ logerror("a1: %f\n", m_a1);
+ logerror("a2: %f\n", m_a2);
+#endif
+ // peak and shelf filters do not use gain for the entire signal, only for the peak/shelf portions
+ // side note: the first order lowpass and highpass filter analogues technically don't have gain either,
+ // but this can be 'faked' by adjusting the bx factors, so we support that anyway, even if it isn't realistic.
+ if ( (m_type != biquad_type::PEAK)
+ && (m_type != biquad_type::LOWSHELF)
+ && (m_type != biquad_type::HIGHSHELF) )
+ {
+ m_b0 *= m_gain;
+ m_b1 *= m_gain;
+ m_b2 *= m_gain;
+#ifdef FLT_BIQUAD_DEBUG
+ logerror("b0g: %f\n", m_b0);
+ logerror("b1g: %f\n", m_b1);
+ logerror("b2g: %f\n", m_b2);
+#endif
+ }
+}
+
+/* Step the filter */
+void filter_biquad_device::step()
+{
+ m_w2 = m_w1;
+ m_w1 = m_w0;
+ m_w0 = (-m_a1 * m_w1) + (-m_a2 * m_w2) + m_input;
+ m_output = (m_b0 * m_w0) + (m_b1 * m_w1) + (m_b2 * m_w2);
+}
diff --git a/src/devices/sound/flt_biquad.h b/src/devices/sound/flt_biquad.h
new file mode 100644
index 00000000000..17050c75017
--- /dev/null
+++ b/src/devices/sound/flt_biquad.h
@@ -0,0 +1,80 @@
+// license:BSD-3-Clause
+// copyright-holders:K.Wilkins,Couriersud,Derrick Renaud,Frank Palazzolo,Jonathan Gevaryahu
+#ifndef MAME_SOUND_FLT_BIQUAD_H
+#define MAME_SOUND_FLT_BIQUAD_H
+
+#pragma once
+
+//**************************************************************************
+// TYPE DEFINITIONS
+//**************************************************************************
+
+// ======================> filter_biquad_device
+
+class filter_biquad_device : public device_t, public device_sound_interface
+{
+public:
+ enum class biquad_type : int
+ {
+ LOWPASS1P = 0,
+ HIGHPASS1P,
+ LOWPASS,
+ HIGHPASS,
+ BANDPASS,
+ NOTCH,
+ PEAK,
+ LOWSHELF,
+ HIGHSHELF
+ };
+
+ 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);
+
+ // helper setup functions to create common filters representable by biquad filters
+ // Sallen-Key low-pass
+ filter_biquad_device& opamp_sk_lowpass_setup(double r1, double r2, double r3, double r4, double c1, double c2);
+ // TODO when needed: Sallen-Key band-pass
+ // TODO when needed: Sallen-Key high-pass
+
+ // Multiple-Feedback low-pass
+ filter_biquad_device& opamp_mfb_lowpass_setup(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);
+
+ // Multiple-Feedback high-pass
+ filter_biquad_device& opamp_mfb_highpass_setup(double r1, double r2, double c1, double c2, double c3);
+
+
+protected:
+ // device-level overrides
+ virtual void device_start() override;
+
+ // sound stream update overrides
+ virtual void sound_stream_update(sound_stream &stream, std::vector<read_stream_view> const &inputs, std::vector<write_stream_view> &outputs) override;
+
+private:
+ void recalc();
+ void step();
+
+ sound_stream* m_stream;
+ biquad_type m_type;
+ int m_last_sample_rate;
+ double m_fc;
+ double m_q;
+ double m_gain;
+
+ stream_buffer::sample_t m_input;
+ double m_w0, m_w1, m_w2; /* w[k], w[k-1], w[k-2], current and previous intermediate values */
+ stream_buffer::sample_t m_output;
+ double m_a1, m_a2; /* digital filter coefficients, denominator */
+ double m_b0, m_b1, m_b2; /* digital filter coefficients, numerator */
+};
+
+DECLARE_DEVICE_TYPE(FILTER_BIQUAD, filter_biquad_device)
+
+#endif // MAME_SOUND_FLT_BIQUAD_H
diff --git a/src/devices/sound/hc55516.cpp b/src/devices/sound/hc55516.cpp
index 5414f378802..2d96ad2cb3b 100644
--- a/src/devices/sound/hc55516.cpp
+++ b/src/devices/sound/hc55516.cpp
@@ -1,8 +1,18 @@
// license:BSD-3-Clause
-// copyright-holders:Aaron Giles
+// copyright-holders:Aaron Giles, Jonathan Gevaryahu
+// thanks-to:Zonn Moore
/*****************************************************************************
- Harris HC-55516 (and related) emulator
+ Continuously Variable Slope Demodulator standalone chip emulator:
+ Harris HC-55516 (sometimes labeled HCI-55516 or HC1-55516)
+ Harris HC-55532 (sometimes labeled HCI-55532 or HC1-55532) [preliminary]
+ Motorola MC-3417/MC-34115
+ Motorola MC-3418
+ TODO: research HC-55536 and HC-55564 differences vs HC-55516 (better auto-zeroing, and removal of the encoder offset compensation DAC?)
+
+ Driver TODOs:
+ /src/mame/audio/exidy440.cpp has its own internal implementation of the MC3417 and MC3418, it should be using this file instead
+
*****************************************************************************/
@@ -10,7 +20,7 @@
#include "hc55516.h"
-/* 4x oversampling */
+/* fixed samplerate of 192khz */
#define SAMPLE_RATE (48000 * 4)
#define INTEGRATOR_LEAK_TC 0.001
@@ -21,33 +31,23 @@
#define SAMPLE_GAIN (10000.0 / 32768.0)
-
-
-DEFINE_DEVICE_TYPE(HC55516, hc55516_device, "hc55516", "HC-55516")
-
-hc55516_device::hc55516_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
- : hc55516_device(mconfig, HC55516, tag, owner, clock)
-{
-}
-
-hc55516_device::hc55516_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, uint32_t clock)
- : device_t(mconfig, type, tag, owner, clock),
- device_sound_interface(mconfig, *this),
- m_channel(nullptr),
- m_active_clock_hi(0),
- m_shiftreg_mask(0),
- m_last_clock_state(0),
- m_digit(0),
- m_new_digit(0),
- m_shiftreg(0),
- m_curr_sample(0),
- m_next_sample(0),
- m_update_count(0),
- m_filter(0),
- m_integrator(0),
- m_charge(0),
- m_decay(0),
- m_leak(0)
+//#####################################
+// COMMON
+//#####################################
+cvsd_device_base::cvsd_device_base(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, uint32_t clock, bool active_clock_edge, uint8_t shiftreg_mask)
+ : device_t(mconfig, type, tag, owner, clock)
+ , device_sound_interface(mconfig, *this)
+ , m_clock_state_push_cb(*this)
+ , m_digin_pull_cb(*this)
+ , m_digout_push_cb(*this)
+ , m_active_clock_edge(active_clock_edge)
+ , m_shiftreg_mask(shiftreg_mask)
+ , m_last_clock_state(false)
+ , m_buffered_bit(false)
+ , m_shiftreg(0)
+ , m_curr_sample(0)
+ , m_next_sample(0)
+ , m_samples_generated(0)
{
}
@@ -55,208 +55,466 @@ hc55516_device::hc55516_device(const machine_config &mconfig, device_type type,
// device_start - device-specific startup
//-------------------------------------------------
-void hc55516_device::device_start()
+void cvsd_device_base::device_start()
{
- start_common(0x07, true);
+ /* create the stream */
+ m_stream = stream_alloc(0, 1, SAMPLE_RATE);
+
+ save_item(NAME(m_last_clock_state));
+ save_item(NAME(m_buffered_bit));
+ save_item(NAME(m_shiftreg));
+ save_item(NAME(m_curr_sample));
+ save_item(NAME(m_next_sample));
+ save_item(NAME(m_samples_generated));
}
//-------------------------------------------------
// device_reset - device-specific reset
//-------------------------------------------------
-void hc55516_device::device_reset()
+void cvsd_device_base::device_reset()
{
m_last_clock_state = 0;
}
-DEFINE_DEVICE_TYPE(MC3417, mc3417_device, "mc3417", "MC3417")
+//-------------------------------------------------
+// device_clock_changed - device-specific samplerate change
+//-------------------------------------------------
+/*void cvsd_device_base::device_clock_changed()
+{
+ // do nothing.
+ //m_stream->set_sample_rate(clock());
+}*/
-mc3417_device::mc3417_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
- : hc55516_device(mconfig, MC3417, tag, owner, clock)
+READ_LINE_MEMBER( cvsd_device_base::clock_r )
{
+ // prevent debugger from changing the internal state
+ if (!machine().side_effects_disabled())
+ m_stream->update(); /* bring up to date first */
+ return clock_state_r();
}
-//-------------------------------------------------
-// device_start - device-specific startup
-//-------------------------------------------------
+WRITE_LINE_MEMBER( cvsd_device_base::mclock_w )
+{
+ clock_w(state);
+}
-void mc3417_device::device_start()
+WRITE_LINE_MEMBER( cvsd_device_base::digin_w )
{
- start_common(0x07, false);
+ digit_w(state);
}
+// the following encode related functions don't do anything yet, don't call them.
+/*void cvsd_device_base::audio_in_w(int16_t data)
+{
+ assert(0);
+}*/
-DEFINE_DEVICE_TYPE(MC3418, mc3418_device, "mc3418", "MC3418")
+WRITE_LINE_MEMBER( cvsd_device_base::dec_encq_w )
+{
+ assert(0);
+}
-mc3418_device::mc3418_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
- : hc55516_device(mconfig, MC3418, tag, owner, clock)
+READ_LINE_MEMBER( cvsd_device_base::digout_r )
{
+ return 0;
}
-//-------------------------------------------------
-// device_start - device-specific startup
-//-------------------------------------------------
+// default and stub implementations
-void mc3418_device::device_start()
+inline bool cvsd_device_base::is_external_oscillator()
{
- start_common(0x0f, false);
+ return clock() != 0;
}
+inline bool cvsd_device_base::is_clock_changed(bool clock_state)
+{
+ return ((!m_last_clock_state && clock_state) ||
+ (m_last_clock_state && !clock_state));
+}
-void hc55516_device::start_common(uint8_t _shiftreg_mask, int _active_clock_hi)
+inline bool cvsd_device_base::is_active_clock_transition(bool clock_state)
{
- /* compute the fixed charge, decay, and leak time constants */
- m_charge = pow(exp(-1.0), 1.0 / (FILTER_CHARGE_TC * 16000.0));
- m_decay = pow(exp(-1.0), 1.0 / (FILTER_DECAY_TC * 16000.0));
- m_leak = pow(exp(-1.0), 1.0 / (INTEGRATOR_LEAK_TC * 16000.0));
+ return ((clock_state != m_last_clock_state) &&
+ (clock_state == m_active_clock_edge));
+}
- m_shiftreg_mask = _shiftreg_mask;
- m_active_clock_hi = _active_clock_hi;
- m_last_clock_state = 0;
+inline bool cvsd_device_base::current_clock_state()
+{
+ // keep track of the clock state given its previous state and the number of samples produced
+ // i.e. if we generated m_samples_generated samples, at a sample rate of SAMPLE_RATE, then are we on a positive or negative level of a squarewave at clock() hz? SAMPLE_RATE may not be an integer multiple of clock()
+ //uint64_t fractions_of_second = (((uint64_t)m_samples_generated)<<32) / SAMPLE_RATE; // 32.32 bits of seconds passed so far
+ //uint32_t clock_edges_passed = (fractions_of_second * clock() * 2)>>32
+ //return (((((uint64_t)m_samples_generated<<32) * clock() * 2 / SAMPLE_RATE)>>32) & 0x1)?true:false;
+ return (((uint64_t)m_samples_generated * clock() * 2 / SAMPLE_RATE) & 0x01)?true:false;
+}
- /* create the stream */
- m_channel = stream_alloc(0, 1, SAMPLE_RATE);
+void cvsd_device_base::digit_w(int digit)
+{
+ m_stream->update();
+ m_buffered_bit = digit ? true : false;
+}
- save_item(NAME(m_last_clock_state));
- save_item(NAME(m_digit));
- save_item(NAME(m_new_digit));
- save_item(NAME(m_shiftreg));
- save_item(NAME(m_curr_sample));
- save_item(NAME(m_next_sample));
- save_item(NAME(m_update_count));
- save_item(NAME(m_filter));
- save_item(NAME(m_integrator));
+void cvsd_device_base::clock_w(int state)
+{
+ /* update the output buffer first */
+ m_stream->update();
+ bool clock_state = state ? true : false;
+
+ /* only makes sense for setups with a software driven clock */
+ assert(!is_external_oscillator());
+
+ /* speech clock changing? */
+ if (is_clock_changed(clock_state))
+ {
+ /* clear the update count */
+ m_samples_generated = 0;
+ process_bit(m_buffered_bit, clock_state);
+ }
+
+ /* update the clock */
+ m_last_clock_state = clock_state;
}
-inline int hc55516_device::is_external_oscillator()
+int cvsd_device_base::clock_state_r()
{
- return clock() != 0;
+ /* only makes sense for setups with an external oscillator */
+ assert(is_external_oscillator());
+
+ m_stream->update();
+
+ return current_clock_state();
+}
+
+void cvsd_device_base::process_bit(bool bit, bool clock_state)
+{
+ // stub
+}
+
+//-------------------------------------------------
+// sound_stream_update - handle a stream update
+//-------------------------------------------------
+
+void cvsd_device_base::sound_stream_update(sound_stream &stream, std::vector<read_stream_view> const &inputs, std::vector<write_stream_view> &outputs)
+{
+ // Stub, just return silence
+ auto &buffer = outputs[0];
+
+ m_samples_generated += buffer.samples();
+ if (m_samples_generated >= SAMPLE_RATE)
+ m_samples_generated -= SAMPLE_RATE;
+ buffer.fill(0);
}
-inline int hc55516_device::is_active_clock_transition(int clock_state)
+//#########################################
+// HC55516
+//#########################################
+DEFINE_DEVICE_TYPE(HC55516, hc55516_device, "hc55516", "HC-55516")
+
+hc55516_device::hc55516_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
+ : hc55516_device(mconfig, HC55516, tag, owner, clock, 0xfc0, 6, 0xfc1, 4)
+{
+}
+
+// overridable type for hc55532 etc
+hc55516_device::hc55516_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, uint32_t clock, uint32_t sylmask, int32_t sylshift, int32_t syladd, int32_t intshift)
+ : cvsd_device_base(mconfig, type, tag, owner, clock, RISING, 0x7)
+ , m_agc_push_cb(*this)
+ , m_fzq_pull_cb(*this)
+ , m_sylmask(sylmask)
+ , m_sylshift(sylshift)
+ , m_syladd(syladd)
+ , m_intshift(intshift)
+ , m_sylfilter(0)
+ , m_intfilter(0)
+ , m_agc(true)
+ , m_buffered_fzq(true)
{
- return (( m_active_clock_hi && !m_last_clock_state && clock_state) ||
- (!m_active_clock_hi && m_last_clock_state && !clock_state));
}
+//-------------------------------------------------
+// device_start - device-specific start
+//-------------------------------------------------
-inline int hc55516_device::current_clock_state()
+void hc55516_device::device_start()
{
- return ((uint64_t)m_update_count * clock() * 2 / SAMPLE_RATE) & 0x01;
+ cvsd_device_base::device_start();
+ save_item(NAME(m_sylfilter));
+ save_item(NAME(m_intfilter));
+ save_item(NAME(m_agc));
+ save_item(NAME(m_buffered_fzq));
+
+ /* resolve lines */
+ m_agc_push_cb.resolve();
+ m_fzq_pull_cb.resolve();
}
+//-------------------------------------------------
+// device_reset - device-specific reset
+//-------------------------------------------------
-void hc55516_device::process_digit()
+void hc55516_device::device_reset()
{
- double integrator = m_integrator, temp;
+ cvsd_device_base::device_reset();
+ // simulate /FZ having been held for a while
+ m_sylfilter = 0x3f;
+ m_intfilter = 0;
+ m_agc = true;
+ m_buffered_fzq = true; // assuming /FZ was just released and is now high/inactive
+}
- /* shift the bit into the shift register */
- m_shiftreg = (m_shiftreg << 1) | m_digit;
+// device specific functions
- /* move the estimator up or down a step based on the bit */
- if (m_digit)
- integrator += m_filter;
- else
- integrator -= m_filter;
+WRITE_LINE_MEMBER( hc55516_device::fzq_w )
+{
+ m_buffered_fzq = state;
+}
+
+READ_LINE_MEMBER( hc55516_device::agc_r )
+{
+ // prevent debugger from changing the internal state
+ if (!machine().side_effects_disabled())
+ m_stream->update(); /* bring up to date first */
+ return m_agc;
+}
- /* simulate leakage */
- integrator *= m_leak;
+void hc55516_device::process_bit(bool bit, bool clock_state)
+{
+ bool frozen = ( ( (m_intfilter >= 0x180) && (!bit) ) ||
+ ( (m_intfilter <= -0x180) && (bit) ) );
- /* if we got all 0's or all 1's in the last n bits, bump the step up */
- if (((m_shiftreg & m_shiftreg_mask) == 0) ||
- ((m_shiftreg & m_shiftreg_mask) == m_shiftreg_mask))
+ int32_t sum;
+ if (is_active_clock_transition(clock_state))
{
- m_filter = FILTER_MAX - ((FILTER_MAX - m_filter) * m_charge);
+ // grab the /FZ state; if the callback is present, use that, otherwise use the buffered state
+ bool fzq_state = false;
+ if (!m_fzq_pull_cb.isnull())
+ fzq_state = m_fzq_pull_cb();
+ else
+ fzq_state = m_buffered_fzq;
+
+ if (!fzq_state) // /FZ is active low, if it is active, the input bit is ignored and the inverse of the previous bit in the shifter is used instead
+ bit = !(m_shiftreg&1);
+
+ /* shift the new bit into the shift register */
+ m_shiftreg = (m_shiftreg << 1) | (bit?1:0);
+
+ /* if we got all 0's or all 1's in the last n bits... */
+ if (((m_shiftreg & m_shiftreg_mask) == 0) ||
+ ((m_shiftreg & m_shiftreg_mask) == m_shiftreg_mask))
+ {
+ // coincidence is true
+ if (!frozen) m_sylfilter += (((~m_sylfilter) & m_sylmask) >> m_sylshift);
+ }
+ else
+ {
+ // coincidence is false
+ if (!frozen) m_sylfilter += (((~m_sylfilter) & m_sylmask) >> m_sylshift) + m_syladd;
+ }
+ m_sylfilter &= 0xfff;
- if (m_filter > FILTER_MAX)
- m_filter = FILTER_MAX;
+ sum = ( ((~m_intfilter) >> m_intshift) + 1 ) & 0x3ff;
}
-
- /* simulate decay */
- else
+ else // inactive clock transition
{
- m_filter *= m_decay;
+ if (m_shiftreg&1)
+ {
+ sum = ( ( ~std::max(2, m_sylfilter >> 6) ) + 1 ) & 0x3ff;
+ }
+ else
+ {
+ sum = std::max(2, m_sylfilter >> 6) & 0x3ff;
+ }
+ }
- if (m_filter < FILTER_MIN)
- m_filter = FILTER_MIN;
+ if (sum & 0x200)
+ sum |= ~0x3ff; // sign extend
+
+ if (!frozen)
+ {
+ m_intfilter += sum;
+ m_intfilter &= 0x3ff;
+ if (m_intfilter & 0x200) m_intfilter |= ~0x3ff; // sign extend
}
- /* compute the sample as a 32-bit word */
- temp = integrator * SAMPLE_GAIN;
- m_integrator = integrator;
+ /* scale the result (-512 to 511) to -32768 thru 32767 */
+ /*
+ F E D C B A 9 8 7 6 5 4 3 2 1 0
+ 9 8 7 6 5 4 3 2 1 0/9 8 7 6 5 4
+ */
+ m_next_sample = ( (m_intfilter << 6) | ( ((m_intfilter & 0x3ff) ^ 0x200 ) >> 4 ) );
+
+ // update agc state
+ if ( (m_intfilter >= 0x100) || (m_intfilter <= -0x100) )
+ m_agc = false;
+ else
+ m_agc = true;
- m_next_sample = temp;
- /* compress the sample range to fit better in a 16-bit word */
-/* if (temp < 0)
- m_next_sample = (int)(temp / (-temp * (1.0 / 32768.0) + 1.0));
- else
- m_next_sample = (int)(temp / (temp * (1.0 / 32768.0) + 1.0));*/
+ // push agc state if a callback is present
+ if (!m_agc_push_cb.isnull())
+ m_agc_push_cb(m_agc);
}
-void hc55516_device::clock_w(int state)
+//-------------------------------------------------
+// sound_stream_update_legacy - handle a stream update
+//-------------------------------------------------
+
+void hc55516_device::sound_stream_update(sound_stream &stream, std::vector<read_stream_view> const &inputs, std::vector<write_stream_view> &outputs)
{
- uint8_t clock_state = state ? true : false;
+ auto &buffer = outputs[0];
- /* only makes sense for setups with a software driven clock */
- assert(!is_external_oscillator());
+/*
+ if (!is_external_oscillator())
+ {
+ // track how many samples we've updated without a clock; if it's been more than 1/32 of a second, output silence
+ m_samples_generated += buffer.samples();
+ if (m_samples_generated > SAMPLE_RATE / 32)
+ {
+ m_samples_generated = SAMPLE_RATE;
+ m_next_sample = 0;
+ }
+ }
+*/
- /* speech clock changing? */
- if (is_active_clock_transition(clock_state))
+ if (is_external_oscillator())
{
- /* update the output buffer before changing the registers */
- m_channel->update();
+ /* external oscillator */
+ for (int i = 0; i < buffer.samples(); i++)
+ {
+ buffer.put_int(i, m_next_sample, 32768);
- /* clear the update count */
- m_update_count = 0;
+ m_samples_generated++;
- process_digit();
+ uint8_t clock_state = current_clock_state();
+
+ /* pull in next digit on the appropriate edge of the clock */
+ if (is_clock_changed(clock_state))
+ {
+ process_bit(m_buffered_bit, clock_state);
+ }
+
+ m_last_clock_state = clock_state;
+ }
}
- /* update the clock */
- m_last_clock_state = clock_state;
+ /* software driven clock */
+ else
+ for (int i = 0; i < buffer.samples(); i++)
+ buffer.put_int(i, m_next_sample, 32768);
}
-void hc55516_device::digit_w(int digit)
+
+//#########################################
+// HC55532
+//#########################################
+DEFINE_DEVICE_TYPE(HC55532, hc55532_device, "hc55532", "HC-55532")
+
+hc55532_device::hc55532_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
+ : hc55516_device(mconfig, HC55532, tag, owner, clock, 0xf80, 7, 0xfe1, 5)
{
- if (is_external_oscillator())
- {
- m_channel->update();
- m_new_digit = digit & 1;
- }
- else
- m_digit = digit & 1;
}
+//-------------------------------------------------
+// device_reset - device-specific reset
+//-------------------------------------------------
-int hc55516_device::clock_state_r()
+void hc55532_device::device_reset()
{
- /* only makes sense for setups with an external oscillator */
- assert(is_external_oscillator());
+ cvsd_device_base::device_reset();
+ // simulate /FZ having been held for a while
+ m_sylfilter = 0x7f;
+ m_intfilter = 0;
+ m_agc = true;
+ m_buffered_fzq = true; // assuming /FZ was just released and is now high/inactive
+}
- m_channel->update();
- return current_clock_state();
+
+//##########################################
+// MC3417
+//##########################################
+DEFINE_DEVICE_TYPE(MC3417, mc3417_device, "mc3417", "MC3417")
+
+mc3417_device::mc3417_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
+ : mc3417_device(mconfig, MC3417, tag, owner, clock, 0x7)
+{
}
+// overridable type for mc3418 etc
+mc3417_device::mc3417_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, uint32_t clock, uint8_t shiftreg_mask)
+ : cvsd_device_base(mconfig, type, tag, owner, clock, FALLING, shiftreg_mask)
+ , m_charge(pow(exp(-1.0), 1.0 / (FILTER_CHARGE_TC * 16000.0)))
+ , m_decay(pow(exp(-1.0), 1.0 / (FILTER_DECAY_TC * 16000.0)))
+ , m_leak(pow(exp(-1.0), 1.0 / (INTEGRATOR_LEAK_TC * 16000.0)))
+ , m_sylfilter_d(0.0)
+ , m_intfilter_d(0.0)
+{
+}
//-------------------------------------------------
-// sound_stream_update - handle a stream update
+// device_start - device-specific startup
//-------------------------------------------------
-void hc55516_device::sound_stream_update(sound_stream &stream, std::vector<read_stream_view> const &inputs, std::vector<write_stream_view> &outputs)
+void mc3417_device::device_start()
+{
+ cvsd_device_base::device_start();
+ save_item(NAME(m_sylfilter_d));
+ save_item(NAME(m_intfilter_d));
+}
+
+void mc3417_device::process_bit(bool bit, bool clock_state)
+{
+ if (is_active_clock_transition(clock_state))
+ {
+
+ /* shift the new bit into the shift register */
+ m_shiftreg = (m_shiftreg << 1) | (bit?1:0);
+
+ /* move the estimator up or down a step based on the bit */
+ if (!bit)
+ m_intfilter_d += m_sylfilter_d;
+ else
+ m_intfilter_d -= m_sylfilter_d;
+
+ /* simulate leakage */
+ m_intfilter_d *= m_leak;
+
+ /* if we got all 0's or all 1's in the last n bits, bump the step up */
+ if (((m_shiftreg & m_shiftreg_mask) == 0) ||
+ ((m_shiftreg & m_shiftreg_mask) == m_shiftreg_mask))
+ {
+ // coincidence is true
+ m_sylfilter_d = FILTER_MAX - ((FILTER_MAX - m_sylfilter_d) * m_charge);
+
+ if (m_sylfilter_d > FILTER_MAX)
+ m_sylfilter_d = FILTER_MAX;
+ }
+ else
+ {
+ m_sylfilter_d *= m_decay;
+
+ if (m_sylfilter_d < FILTER_MIN)
+ m_sylfilter_d = FILTER_MIN;
+ }
+
+ /* compute the sample as a 32-bit word */
+ m_next_sample = m_intfilter_d * SAMPLE_GAIN;
+ }
+}
+
+void mc3417_device::sound_stream_update(sound_stream &stream, std::vector<read_stream_view> const &inputs, std::vector<write_stream_view> &outputs)
{
auto &buffer = outputs[0];
- int i;
if (!is_external_oscillator())
{
- /* track how many samples we've updated without a clock */
- m_update_count += buffer.samples();
- if (m_update_count > SAMPLE_RATE / 32)
+ /* track how many samples we've updated without a clock; if it's been more than 1/32 of a second, output silence */
+ m_samples_generated += buffer.samples();
+ if (m_samples_generated > SAMPLE_RATE / 32)
{
- m_update_count = SAMPLE_RATE;
+ m_samples_generated = SAMPLE_RATE;
m_next_sample = 0;
}
}
@@ -269,22 +527,18 @@ void hc55516_device::sound_stream_update(sound_stream &stream, std::vector<read_
if (is_external_oscillator())
{
/* external oscillator */
- for (i = 0; i < buffer.samples(); i++, sample += slope)
+ for (int i = 0; i < buffer.samples(); i++, sample += slope)
{
- uint8_t clock_state;
+ buffer.put(i, sample);
- buffer.put(i, stream_buffer::sample_t(sample));
+ m_samples_generated++;
- m_update_count++;
-
- clock_state = current_clock_state();
+ uint8_t clock_state = current_clock_state();
/* pull in next digit on the appropriate edge of the clock */
- if (is_active_clock_transition(clock_state))
+ if (is_clock_changed(clock_state))
{
- m_digit = m_new_digit;
-
- process_digit();
+ process_bit(m_buffered_bit, clock_state);
}
m_last_clock_state = clock_state;
@@ -293,16 +547,18 @@ void hc55516_device::sound_stream_update(sound_stream &stream, std::vector<read_
/* software driven clock */
else
- for (i = 0; i < buffer.samples(); i++, sample += slope)
- buffer.put(i, stream_buffer::sample_t(sample));
+ for (int i = 0; i < buffer.samples(); i++, sample += slope)
+ buffer.put(i, sample);
}
-void mc3417_device::sound_stream_update(sound_stream &stream, std::vector<read_stream_view> const &inputs, std::vector<write_stream_view> &outputs)
-{
- hc55516_device::sound_stream_update(stream, inputs, outputs);
-}
-void mc3418_device::sound_stream_update(sound_stream &stream, std::vector<read_stream_view> const &inputs, std::vector<write_stream_view> &outputs)
+
+//##########################################
+// MC3418
+//##########################################
+DEFINE_DEVICE_TYPE(MC3418, mc3418_device, "mc3418", "MC3418")
+
+mc3418_device::mc3418_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
+ : mc3417_device(mconfig, MC3418, tag, owner, clock, 0xf)
{
- hc55516_device::sound_stream_update(stream, inputs, outputs);
}
diff --git a/src/devices/sound/hc55516.h b/src/devices/sound/hc55516.h
index 0948ce025a4..48c1b768a4f 100644
--- a/src/devices/sound/hc55516.h
+++ b/src/devices/sound/hc55516.h
@@ -1,95 +1,176 @@
// license:BSD-3-Clause
-// copyright-holders:Aaron Giles
+// copyright-holders:Aaron Giles, Jonathan Gevaryahu
+// thanks-to:Zonn Moore
#ifndef MAME_SOUND_HC55516_H
#define MAME_SOUND_HC55516_H
#pragma once
-class hc55516_device : public device_t, public device_sound_interface
+class cvsd_device_base : public device_t, public device_sound_interface
{
public:
- hc55516_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock);
-
- /* sets the digit (0 or 1) */
- void digit_w(int digit);
-
- /* sets the clock state (0 or 1, clocked on the rising edge) */
- void clock_w(int state);
-
- /* returns whether the clock is currently LO or HI */
- int clock_state_r();
+ enum
+ {
+ RISING=true,
+ FALLING=false
+ };
+
+ auto clock_state_cb() { return m_clock_state_push_cb.bind(); } // A clock state change callback. Using this is a bad idea due to lack of synchronization to other devices. TODO: remove this.
+ auto digin_cb() { return m_digin_pull_cb.bind(); } // Digital in pull callback function, for use if a clock is specified and we need to pull in the digital in pin state, otherwise unused. TODO: this is not hooked up yet, and should be.
+ auto digout_cb() { return m_digout_push_cb.bind(); } // Digital out push callback function. TODO: this is not hooked up or implemented yet, although it is only really relevant for devices which use the CVSD chips in encode mode.
+
+ READ_LINE_MEMBER( clock_r ); // Clock pull, really only relevant of something manually polls the clock (and clock is specified), which is a very bad design pattern and will cause synchronization/missed clock transition issues. This function WILL ASSERT if it is called and the clock hz is NOT specified! TODO: remove all use of this, and remove it.
+ WRITE_LINE_MEMBER( mclock_w ); // Clock push; this function WILL ASSERT if it is called and the clock hz IS specified!
+ WRITE_LINE_MEMBER( digin_w ); // Digital in push to the pin, as a pseudo 'buffer' implemented within the cvsd device itself. This is not technically accurate to hardware, and in the future should be deprecated in favor of digin_cb once the latter is implemented.
+ WRITE_LINE_MEMBER( dec_encq_w ); //DEC/ENC decode/encode select push. This is not implemented yet, and relies on an input audio stream. TODO: implement this beyond a do-nothing stub
+ READ_LINE_MEMBER( digout_r ); // Digital out pull. TODO: this is not hooked up or implemented yet, although it is only really relevant for devices which use the CVSD chips in encode mode.
+ //void audio_in_w(stream_buffer::sample_t data); // Audio In pin, an analog value of the audio waveform being pushed to the chip. TODO: this is not hooked up or implemented yet, and this should really be handled as an input stream from a separate DAC device, not a value push function at all.
+ void digit_w(int digit); /* sets the buffered digit (0 or 1), common to all chips. TODO: replace all use of this with digin_cb once implemented */
+ void clock_w(int state); /* sets the clock state (0 or 1, clocked on the rising edge), common to all chips */
+ virtual int clock_state_r(); /* returns whether the clock is currently LO or HI, common to all chips. TODO: get rid of all use of this, then get rid of it. */
protected:
- hc55516_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, uint32_t clock);
+ cvsd_device_base(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, uint32_t clock, bool active_clock_edge, uint8_t shiftreg_mask);
// device-level overrides
virtual void device_start() override;
virtual void device_reset() override;
+ //virtual void device_clock_changed() override;
// sound stream update overrides
virtual void sound_stream_update(sound_stream &stream, std::vector<read_stream_view> const &inputs, std::vector<write_stream_view> &outputs) override;
- void start_common(uint8_t _shiftreg_mask, int _active_clock_hi);
+ // callbacks
+ devcb_write_line m_clock_state_push_cb; ///TODO: get rid of this, if you use it you should feel bad
+ devcb_read_line m_digin_pull_cb;
+ devcb_write_line m_digout_push_cb;
- // internal state
- sound_stream *m_channel;
- int m_active_clock_hi;
- uint8_t m_shiftreg_mask;
+ // const state defined by constructor
+ const bool m_active_clock_edge;
+ const uint8_t m_shiftreg_mask; // it may be desirable to allow this to be changed by the user under some circumstances
- uint8_t m_last_clock_state;
- uint8_t m_digit;
- uint8_t m_new_digit;
+ // internal state
+ sound_stream *m_stream;
+ bool m_last_clock_state;
+ bool m_buffered_bit;
uint8_t m_shiftreg;
-
stream_buffer::sample_t m_curr_sample;
stream_buffer::sample_t m_next_sample;
+ uint32_t m_samples_generated;
- uint32_t m_update_count;
-
- double m_filter;
- double m_integrator;
+ // specific internal handler overrides, overridden by each chip
+ virtual void process_bit(bool bit, bool clock_state);
- double m_charge;
- double m_decay;
- double m_leak;
+ ///TODO: get rid of these
+ inline bool is_external_oscillator();
+ inline bool is_clock_changed(bool clock_state);
+ inline bool is_active_clock_transition(bool clock_state);
+ inline bool current_clock_state();
- inline int is_external_oscillator();
- inline int is_active_clock_transition(int clock_state);
- inline int current_clock_state();
- void process_digit();
};
-class mc3417_device : public hc55516_device
+class hc55516_device : public cvsd_device_base
{
public:
- mc3417_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock);
+ hc55516_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock);
+
+ auto fzq_cb() { return m_fzq_pull_cb.bind(); } // /FZ (partial reset) pull callback, ok to leave unconnected (we assume it is pulled high)
+ auto agc_cb() { return m_agc_push_cb.bind(); } // AGC callback function, called to push the state if the AGC pin changes, ok to leave unconnected
+
+ WRITE_LINE_MEMBER( fzq_w ); // /FZ (partial reset) push
+ READ_LINE_MEMBER( agc_r ); // AGC pull
+ /* TODO: These are only relevant for encode mode, which isn't done yet! */
+ //WRITE_LINE_MEMBER( aptq_w ); // /APT (silence encoder output) push
+ //WRITE_LINE_MEMBER( dec_encq_w ); // DEC/ENC decode/encode select push
protected:
+ // overridable type for subclass
+ hc55516_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, uint32_t clock, uint32_t sylmask, int32_t sylshift, int32_t syladd, int32_t intshift);
+
// device-level overrides
virtual void device_start() override;
+ virtual void device_reset() override;
// sound stream update overrides
virtual void sound_stream_update(sound_stream &stream, std::vector<read_stream_view> const &inputs, std::vector<write_stream_view> &outputs) override;
+
+ // callbacks
+ devcb_write_line m_agc_push_cb;
+ devcb_read_line m_fzq_pull_cb;
+
+ // const coefficients defined by constructor
+ const uint32_t m_sylmask;
+ const int32_t m_sylshift;
+ const int32_t m_syladd;
+ const int32_t m_intshift;
+
+ // internal state
+ int32_t m_sylfilter;
+ int32_t m_intfilter;
+ bool m_agc;
+ bool m_buffered_fzq;
+
+ // internal handlers
+ virtual void process_bit(bool bit, bool clock_state) override;
};
-class mc3418_device : public hc55516_device
+class hc55532_device : public hc55516_device
{
public:
- mc3418_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock);
+ hc55532_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock);
+protected:
+ // device-level overrides
+ virtual void device_reset() override;
+};
+
+
+class mc3417_device : public cvsd_device_base
+{
+public:
+ mc3417_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock);
+
+ // override for clock_w
+ //virtual void clock_w(int state) override;
protected:
+ // overridable type for subclass
+ mc3417_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, uint32_t clock, uint8_t shiftreg_mask);
+
// device-level overrides
virtual void device_start() override;
// sound stream update overrides
virtual void sound_stream_update(sound_stream &stream, std::vector<read_stream_view> const &inputs, std::vector<write_stream_view> &outputs) override;
+
+ // const coefficients defined by constructor; should these be adjustable by the user or externally defined, as they are implemented using a set of two small lowpass filters outside the chip?
+ const double m_charge;
+ const double m_decay;
+ const double m_leak;
+
+ // internal state
+ double m_sylfilter_d;
+ double m_intfilter_d;
+
+ // internal handlers
+ virtual void process_bit(bool bit, bool clock_state) override;
+};
+
+
+class mc3418_device : public mc3417_device
+{
+public:
+ mc3418_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock);
};
DECLARE_DEVICE_TYPE(HC55516, hc55516_device)
+DECLARE_DEVICE_TYPE(HC55532, hc55532_device)
+//DECLARE_DEVICE_TYPE(HC55536, hc55536_device)
+//DECLARE_DEVICE_TYPE(HC55564, hc55564_device)
DECLARE_DEVICE_TYPE(MC3417, mc3417_device)
+//DECLARE_DEVICE_TYPE(MC34115, mc34115_device)
DECLARE_DEVICE_TYPE(MC3418, mc3418_device)
#endif // MAME_SOUND_HC55516_H
diff --git a/src/mame/audio/exidy.cpp b/src/mame/audio/exidy.cpp
index 3f5b6559f2e..6c107bf15fe 100644
--- a/src/mame/audio/exidy.cpp
+++ b/src/mame/audio/exidy.cpp
@@ -196,6 +196,8 @@ exidy_sh8253_sound_device::exidy_sh8253_sound_device(const machine_config &mconf
: exidy_sound_device(mconfig, type, tag, owner, clock),
m_riot(*this, "riot"),
m_cvsd(*this, "cvsd"),
+ m_cvsd_filter(*this, "cvsd_filter"),
+ m_cvsd_filter2(*this, "cvsd_filter2"),
m_cvsdcpu(*this, "cvsdcpu"),
m_tms(*this, "tms"),
m_pia(*this, "pia")
@@ -788,9 +790,33 @@ DEFINE_DEVICE_TYPE(EXIDY_MTRAP, mtrap_sound_device, "mtrap_sound", "Exidy SFX+PS
mtrap_sound_device::mtrap_sound_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
: venture_sound_device(mconfig, EXIDY_MTRAP, tag, owner, clock)
+ , m_cvsd_timer(*this,"cvsd_timer")
+ , m_cvsd_clk(false)
{
}
+//-------------------------------------------------
+// device_start - device-specific startup
+//-------------------------------------------------
+
+void mtrap_sound_device::device_start()
+{
+ common_sh_start();
+
+ /* 8253 */
+ m_freq_to_step = (1 << 24) / SH8253_CLOCK;
+
+ sh8253_register_state_globals();
+
+ save_item(NAME(m_cvsd_clk));
+}
+
+TIMER_DEVICE_CALLBACK_MEMBER(mtrap_sound_device::cvsd_timer)
+{
+ m_cvsd_clk = !m_cvsd_clk;
+ m_cvsd->clock_w(m_cvsd_clk);
+}
+
void mtrap_sound_device::voiceio_w(offs_t offset, uint8_t data)
{
if (!(offset & 0x10))
@@ -803,19 +829,24 @@ void mtrap_sound_device::voiceio_w(offs_t offset, uint8_t data)
uint8_t mtrap_sound_device::voiceio_r(offs_t offset)
{
+ uint8_t retval = 0xff; // this should probably be open bus
if (!(offset & 0x80))
{
+ retval &= 0xf0;
uint8_t porta = m_riot->porta_out_get();
uint8_t data = (porta & 0x06) >> 1;
data |= (porta & 0x01) << 2;
data |= (porta & 0x08);
- return data;
+ retval |= data;
}
if (!(offset & 0x40))
- return m_cvsd->clock_state_r() << 7;
+ {
+ retval &= 0x7f;
+ retval |= (m_cvsd_clk << 7);
+ }
- return 0;
+ return retval;
}
@@ -841,8 +872,15 @@ void mtrap_sound_device::device_add_mconfig(machine_config &config)
m_cvsdcpu->set_addrmap(AS_PROGRAM, &mtrap_sound_device::cvsd_map);
m_cvsdcpu->set_addrmap(AS_IO, &mtrap_sound_device::cvsd_iomap);
+ TIMER(config, m_cvsd_timer).configure_periodic(FUNC(mtrap_sound_device::cvsd_timer), attotime::from_hz(CVSD_CLOCK*2.0)); // this is a 555 timer with 53% duty cycle, within margin of error of 50% duty cycle; the handler clocks on both clock edges, hence * 2.0
+
/* audio hardware */
- MC3417(config, m_cvsd, CVSD_CLOCK).add_route(ALL_OUTPUTS, "mono", 0.80);
+ FILTER_BIQUAD(config, m_cvsd_filter2).opamp_mfb_lowpass_setup(RES_K(10), RES_K(3.9), RES_K(18), CAP_N(20), CAP_N(2.2));
+ m_cvsd_filter2->add_route(ALL_OUTPUTS, "mono", 1.0);
+ FILTER_BIQUAD(config, m_cvsd_filter).opamp_mfb_lowpass_setup(RES_K(10), RES_K(3.9), RES_K(18), CAP_N(20), CAP_N(2.2));
+ m_cvsd_filter->add_route(ALL_OUTPUTS, m_cvsd_filter2, 1.0);
+ MC3417(config, m_cvsd, 0).add_route(ALL_OUTPUTS, m_cvsd_filter, 0.3086); // each filter has gain of 1.8 for total gain of 3.24, 0.3086 cancels this out. was 0.8
+
}
diff --git a/src/mame/audio/exidy.h b/src/mame/audio/exidy.h
index c619cd1975d..8e3ee42916a 100644
--- a/src/mame/audio/exidy.h
+++ b/src/mame/audio/exidy.h
@@ -7,6 +7,8 @@
#include "machine/6532riot.h"
#include "machine/6821pia.h"
+#include "machine/timer.h"
+#include "sound/flt_biquad.h"
#include "sound/hc55516.h"
#include "sound/tms5220.h"
@@ -120,7 +122,9 @@ protected:
required_device<riot6532_device> m_riot;
/* 5220/CVSD variables */
- optional_device<hc55516_device> m_cvsd;
+ optional_device<mc3417_device> m_cvsd;
+ optional_device<filter_biquad_device> m_cvsd_filter;
+ optional_device<filter_biquad_device> m_cvsd_filter2;
optional_device<cpu_device> m_cvsdcpu;
optional_device<tms5220_device> m_tms;
required_device<pia6821_device> m_pia;
@@ -177,11 +181,15 @@ public:
protected:
// device-level overrides
+ virtual void device_start() override;
virtual void device_add_mconfig(machine_config &config) override;
private:
+ required_device<timer_device> m_cvsd_timer;
+ TIMER_DEVICE_CALLBACK_MEMBER(cvsd_timer);
void voiceio_w(offs_t offset, uint8_t data);
uint8_t voiceio_r(offs_t offset);
+ bool m_cvsd_clk;
void cvsd_map(address_map &map);
void cvsd_iomap(address_map &map);
diff --git a/src/mame/audio/s11c_bg.cpp b/src/mame/audio/s11c_bg.cpp
index fef33ee26b9..33fd826e1b8 100644
--- a/src/mame/audio/s11c_bg.cpp
+++ b/src/mame/audio/s11c_bg.cpp
@@ -232,6 +232,8 @@ s11c_bg_device::s11c_bg_device(const machine_config &mconfig, const char *tag, d
, m_dac(*this, "dac")
, m_ym2151(*this, "ym2151")
, m_cvsd(*this, "hc55516")
+ , m_cvsd_filter(*this, "cvsd_filter")
+ , m_cvsd_filter2(*this, "cvsd_filter2")
, m_pia40(*this, "pia40")
, m_cpubank(*this, "bgbank")
, m_cb2_cb(*this)
@@ -248,6 +250,8 @@ s11c_bg_device::s11c_bg_device(const machine_config &mconfig, device_type type,
, m_dac(*this, "dac")
, m_ym2151(*this, "ym2151")
, m_cvsd(*this, "hc55516")
+ , m_cvsd_filter(*this, "cvsd_filter")
+ , m_cvsd_filter2(*this, "cvsd_filter2")
, m_pia40(*this, "pia40")
, m_cpubank(*this, "bgbank")
, m_cb2_cb(*this)
@@ -390,7 +394,18 @@ void s11c_bg_device::s11_bg_ym(machine_config &config)
// add a CVSD chip for boards which have it
void s11c_bg_device::s11_bg_cvsd(machine_config &config)
{
- HC55516(config, m_cvsd, 0);
+ // m_cvsd_filter is the first 'half' of U18(MC1458), with R32, R30, R29, no capacitor to ground(!?!) and C9, output feeding the second half
+ // m_cvsd_filter2 is the second 'half' of U18(MC1458), with R20, R15, R19, C10 and C7, output feeding the final mixer
+ // Note that the (intended 1800uf according to Sinistar/System 6) capacitor
+ // to ground for m_cvsd_filter is COMPLETELY MISSING, and hence this
+ // filter section behaves very oddly under simulation, retaining a
+ // gain but having a first-order falloff response!
+ // This was presumably a design error, and not intended, given the
+ // strange filter response. We emulate this weird circuit as it existed.
+ FILTER_BIQUAD(config, m_cvsd_filter2).opamp_mfb_lowpass_setup(RES_K(27), RES_K(15), RES_K(27), CAP_P(4700), CAP_P(1200));
+ FILTER_BIQUAD(config, m_cvsd_filter).opamp_mfb_lowpass_setup(RES_K(43), RES_K(36), RES_K(180), CAP_P(0), CAP_P(180)); // note the first capacitor is 0pf meaning it doesn't exist
+ m_cvsd_filter->add_route(ALL_OUTPUTS, m_cvsd_filter2, 1.0);
+ HC55516(config, m_cvsd, 0).add_route(ALL_OUTPUTS, m_cvsd_filter, 1.0/4.0); // to prevent massive clipping issues, we divide the signal by 4 here before going into the filters, then multiply it by 4 after it comes out the other end
}
@@ -405,11 +420,13 @@ void s11c_bg_device::device_add_mconfig(machine_config &config)
// 1/resistance * 57990 is 4.460769, 2.8895, 2.8895, 11.62124
// the sum of the previous 4 values is 21.88101; 100/21.88101 = 4.570173
// the 4 (1/r)*rtotal numbers * 4.570173 are 20.38649, 13.25122, 13.25122 and 53.11108 respectively
- // NOTE: audio passthrough from the mainboard is 4.7kohm
- m_dac->add_route(ALL_OUTPUTS, *this, 0.2038); // 13Kohm
- m_ym2151->add_route(1, *this, 0.1325); // 20kohm
- m_ym2151->add_route(0, *this, 0.1325); // 20kohm
- m_cvsd->add_route(ALL_OUTPUTS, *this, 0.5311); // 4.99kohm
+ // NOTE: Multiply all numbers here or the final output by 1/0.6395 = 1.5638 to get the relative
+ // volume values if there is no audio input used at all
+ // audio passthrough resistor from the mainboard input is 4.7kohm, 0.3605 in files sending audio into this device
+ m_dac->add_route(ALL_OUTPUTS, *this, 0.1304); // 13Kohm
+ m_ym2151->add_route(1, *this, 0.08473); // 20kohm
+ m_ym2151->add_route(0, *this, 0.08473); // 20kohm
+ m_cvsd_filter2->add_route(ALL_OUTPUTS, *this, 0.3396*4.0); // 4.99kohm
}
// D-11581 (without the W10/W11 jumpers)
@@ -423,11 +440,13 @@ void s11_bg_device::device_add_mconfig(machine_config &config)
// 1/resistance * 57990 is 4.9666, 3.129, 3.129, 6.2705
// the sum of the previous 4 values is 17.49521; 100/17.49521 = 5.715851
// the 4 (1/r)*rtotal numbers * 5.715851 are 28.38873, 17.8849, 17.8849, and 35.84148 respectively
- // NOTE: audio passthrough from the mainboard is 2.2kohm
- m_dac->add_route(ALL_OUTPUTS, *this, 0.2839); // 6.3Kohm
- m_ym2151->add_route(1, *this, 0.1788); // 10kohm
- m_ym2151->add_route(0, *this, 0.1788); // 10kohm
- m_cvsd->add_route(ALL_OUTPUTS, *this, 0.3584); // 4.99kohm
+ // NOTE: Multiply all numbers here or the final output by 1/0.5516 = 1.8129 to get the relative
+ // volume values correct if there is no audio input used at all
+ // NOTE: audio passthrough from the mainboard is 2.2kohm, 0.4484 in files sending audio into this device
+ m_dac->add_route(ALL_OUTPUTS, *this, 0.1566); // 6.3Kohm
+ m_ym2151->add_route(1, *this, 0.0987); // 10kohm
+ m_ym2151->add_route(0, *this, 0.0987); // 10kohm
+ m_cvsd_filter2->add_route(ALL_OUTPUTS, *this, 0.1977*4.0); // 4.99kohm
}
// D-11297 or D-11298
@@ -441,11 +460,13 @@ void s11_obg_device::device_add_mconfig(machine_config &config)
// 1/resistance * 40000 is 4.0, 4.0, 4.0, 4.0
// the sum of the previous 4 values is 16.0; 100/16 = 6.25
// the 4 (1/r)*rtotal numbers * 6.25 are 25.0, 25.0, 25.0, 25.0 respectively
- // NOTE: audio passthrough from the mainboard is 2.2kohm
- m_dac->add_route(ALL_OUTPUTS, *this, 0.25); // 10Kohm
- m_ym2151->add_route(1, *this, 0.25); // 10kohm
- m_ym2151->add_route(0, *this, 0.25); // 10kohm
- m_cvsd->add_route(ALL_OUTPUTS, *this, 0.25); // 10kohm
+ // NOTE: Multiply all numbers here or the final output by 1/0.468 = 2.1368 to get the relative
+ // volume values correct if there is no audio input used at all
+ // NOTE: audio passthrough from the mainboard is 2.2kohm, 0.5319 in files sending audio into this device
+ m_dac->add_route(ALL_OUTPUTS, *this, 0.1170); // 10Kohm
+ m_ym2151->add_route(1, *this, 0.1170); // 10kohm
+ m_ym2151->add_route(0, *this, 0.1170); // 10kohm
+ m_cvsd_filter2->add_route(ALL_OUTPUTS, *this, 0.1170*4.0); // 10kohm
}
// D-11197
@@ -459,10 +480,12 @@ void s11_bgm_device::device_add_mconfig(machine_config &config)
// 1/resistance * 40000 is 4.0, 4.0, 4.0, 4.0
// the sum of the previous 4 values is 16.0; 100/16 = 6.25
// the 4 (1/r)*rtotal numbers * 6.25 are 25.0, 25.0, 25.0, 25.0 respectively
- // NOTE: audio passthrough from the mainboard is 2.2kohm
- m_dac->add_route(ALL_OUTPUTS, *this, 0.25); // 10Kohm
- m_ym2151->add_route(1, *this, 0.25); // 10kohm
- m_ym2151->add_route(0, *this, 0.25); // 10kohm
+ // NOTE: Multiply all numbers here or the final output by 1/0.468 = 2.1368 to get the relative
+ // volume values correct if there is no audio input used at all
+ // NOTE: audio passthrough from the mainboard is 2.2kohm, 0.5319 in files sending audio into this device
+ m_dac->add_route(ALL_OUTPUTS, *this, 0.1170); // 10Kohm
+ m_ym2151->add_route(1, *this, 0.1170); // 10kohm
+ m_ym2151->add_route(0, *this, 0.1170); // 10kohm
// interestingly, there is no cvsd, but a fourth 10k resistor here, but it is tied to ground. this makes the board quieter than it would otherwise be, presumably.
}
@@ -473,8 +496,8 @@ void s11_bgs_device::device_add_mconfig(machine_config &config)
m_cpu->set_addrmap(AS_PROGRAM, &s11c_bg_device::s11c_bgs_map);
// volume mixer stuff
// the sum of all resistances is 10k + 10k = 20k
- // NOTE: audio passthrough from the mainboard is 10k
- m_dac->add_route(ALL_OUTPUTS, *this, 1.00); // 10Kohm
+ // NOTE: audio passthrough from the mainboard is 10k, 0.50 in files sending audio to this device
+ m_dac->add_route(ALL_OUTPUTS, *this, 0.50); // 10Kohm
}
void s11c_bg_device::device_start()
diff --git a/src/mame/audio/s11c_bg.h b/src/mame/audio/s11c_bg.h
index cab8d03defd..756b71c89f7 100644
--- a/src/mame/audio/s11c_bg.h
+++ b/src/mame/audio/s11c_bg.h
@@ -13,7 +13,9 @@
#include "cpu/m6809/m6809.h"
#include "machine/6821pia.h"
+#include "machine/rescap.h"
#include "sound/dac.h"
+#include "sound/flt_biquad.h"
#include "sound/hc55516.h"
#include "sound/ym2151.h"
@@ -63,6 +65,8 @@ protected:
required_device<mc1408_device> m_dac;
optional_device<ym2151_device> m_ym2151;
optional_device<hc55516_device> m_cvsd;
+ optional_device<filter_biquad_device> m_cvsd_filter;
+ optional_device<filter_biquad_device> m_cvsd_filter2;
required_device<pia6821_device> m_pia40;
required_memory_bank m_cpubank;
diff --git a/src/mame/audio/williams.cpp b/src/mame/audio/williams.cpp
index b685d102652..e20ef10f8ed 100644
--- a/src/mame/audio/williams.cpp
+++ b/src/mame/audio/williams.cpp
@@ -76,6 +76,7 @@ williams_cvsd_sound_device::williams_cvsd_sound_device(const machine_config &mco
device_mixer_interface(mconfig, *this),
m_cpu(*this, "cpu"),
m_pia(*this, "pia"),
+ m_ym2151(*this, "ym2151"),
m_hc55516(*this, "cvsd"),
m_rombank(*this, "rombank"),
m_talkback(0)
@@ -185,12 +186,13 @@ void williams_cvsd_sound_device::device_add_mconfig(machine_config &config)
PIA6821(config, m_pia, 0);
m_pia->writepa_handler().set("dac", FUNC(dac_byte_interface::data_w));
m_pia->writepb_handler().set(FUNC(williams_cvsd_sound_device::talkback_w));
+ m_pia->ca2_handler().set(m_ym2151, FUNC(ym2151_device::reset_w));
m_pia->irqa_handler().set_inputline(m_cpu, M6809_FIRQ_LINE);
m_pia->irqb_handler().set_inputline(m_cpu, INPUT_LINE_NMI);
- ym2151_device &ym(YM2151(config, "ym2151", CVSD_FM_CLOCK));
- ym.irq_handler().set(m_pia, FUNC(pia6821_device::ca1_w)).invert(); // IRQ is not true state
- ym.add_route(ALL_OUTPUTS, *this, 0.10);
+ YM2151(config, m_ym2151, CVSD_FM_CLOCK);
+ m_ym2151->irq_handler().set(m_pia, FUNC(pia6821_device::ca1_w)).invert(); // IRQ is not true state
+ m_ym2151->add_route(ALL_OUTPUTS, *this, 0.10);
MC1408(config, "dac", 0).add_route(ALL_OUTPUTS, *this, 0.25);
voltage_regulator_device &vref(VOLTAGE_REGULATOR(config, "vref"));
diff --git a/src/mame/audio/williams.h b/src/mame/audio/williams.h
index a14e1d57d06..78006b1800d 100644
--- a/src/mame/audio/williams.h
+++ b/src/mame/audio/williams.h
@@ -63,6 +63,7 @@ private:
// devices
required_device<mc6809e_device> m_cpu;
required_device<pia6821_device> m_pia;
+ required_device<ym2151_device> m_ym2151;
required_device<hc55516_device> m_hc55516;
required_memory_bank m_rombank;
diff --git a/src/mame/drivers/s11.cpp b/src/mame/drivers/s11.cpp
index d1c655aa888..e6219180c92 100644
--- a/src/mame/drivers/s11.cpp
+++ b/src/mame/drivers/s11.cpp
@@ -466,13 +466,23 @@ void s11_state::s11(machine_config &config)
m_audiocpu->set_addrmap(AS_PROGRAM, &s11_state::s11_audio_map);
INPUT_MERGER_ANY_HIGH(config, m_audioirq).output_handler().set_inputline(m_audiocpu, M6808_IRQ_LINE);
- SPEAKER(config, "speaker").front_center();
- MC1408(config, m_dac, 0).add_route(ALL_OUTPUTS, "speaker", 0.25);
+ MC1408(config, m_dac, 0);
voltage_regulator_device &vref(VOLTAGE_REGULATOR(config, "vref"));
vref.add_route(0, m_dac, 1.0, DAC_VREF_POS_INPUT); vref.add_route(0, m_dac, -1.0, DAC_VREF_NEG_INPUT);
- SPEAKER(config, "speech").front_center();
- HC55516(config, m_hc55516, 0).add_route(ALL_OUTPUTS, "speech", 0.5);
+ // common CVSD filter for system 11 and 11a, this is also the same filter circuit as Sinistar/System 6 uses, and is ALMOST the same filter from the s11 bg sound boards, see /mame/audio/s11c_bg.cpp
+ // The CVSD filter has a large gain, about 4.6x
+ // The filter is boosting the ~5vpp audio signal from the CVSD chip to a ~23vpp (really ~17vpp) theoretical audio signal that the s11
+ // mainboard outputs on its volume control-repurposed-as-audio-out connector.
+ // In reality, the S11 mainboard outputs audio at a virtual ground level between +5v and -12v (so, 17VPP balanced around -7VDC), but since
+ // the CVSD chip's internal DAC can only output between a bit over +0x180/-0x180 out of 0x200, the most voltage it can ever output is
+ // between (assuming 0x1ff is 5VDC and 0x300 is 0VDC) a max of 4.375VDC and a min of 0.625VDC, i.e. 3.75VPP centered on 2.5VDC.
+ // In reality, the range is likely less than that.
+ // This means multiplying a 3.75VPP signal by 4.6 is 17.25VPP, which is almost exactly the expected 17V (12v+5v) VPP the output should have.
+ FILTER_BIQUAD(config, m_cvsd_filter2).opamp_mfb_lowpass_setup(RES_K(27), RES_K(15), RES_K(27), CAP_P(4700), CAP_P(1200));
+ FILTER_BIQUAD(config, m_cvsd_filter).opamp_mfb_lowpass_setup(RES_K(43), RES_K(36), RES_K(180), CAP_P(1800), CAP_P(180));
+ m_cvsd_filter->add_route(ALL_OUTPUTS, m_cvsd_filter2, 1.0);
+ HC55516(config, m_hc55516, 0).add_route(ALL_OUTPUTS, m_cvsd_filter, 1.0);
PIA6821(config, m_pias, 0);
m_pias->readpa_handler().set(FUNC(s11_state::sound_r));
@@ -489,26 +499,37 @@ void s11_state::s11_bgs(machine_config &config)
{
s11(config);
/* Add the background sound card */
- SPEAKER(config, "bgspk").front_center();
S11_BGS(config, m_bg);
+ m_dac->add_route(ALL_OUTPUTS, m_bg, 0.5/2.0);
+ m_cvsd_filter2->add_route(ALL_OUTPUTS, m_bg, 0.5/2.0);
m_pia34->ca2_handler().set(m_bg, FUNC(s11_bgs_device::resetq_w));
m_bg->pb_cb().set(m_pia34, FUNC(pia6821_device::portb_w));
m_bg->cb2_cb().set(m_pia34, FUNC(pia6821_device::cb1_w));
- m_bg->add_route(ALL_OUTPUTS, "bgspk", 0.5);
+ SPEAKER(config, "speaker").front_center();
+ m_bg->add_route(ALL_OUTPUTS, "speaker", 1.0);
}
void s11_state::s11_bgm(machine_config &config)
{
s11(config);
/* Add the background music card */
- SPEAKER(config, "bgspk").front_center();
S11_BGM(config, m_bg);
+ m_dac->add_route(ALL_OUTPUTS, m_bg, 0.5319/2.0);
+ m_cvsd_filter2->add_route(ALL_OUTPUTS, m_bg, 0.5319/2.0);
m_pia34->ca2_handler().set(m_bg, FUNC(s11_bgm_device::resetq_w));
m_bg->pb_cb().set(m_pia34, FUNC(pia6821_device::portb_w));
m_bg->cb2_cb().set(m_pia34, FUNC(pia6821_device::cb1_w));
- m_bg->add_route(ALL_OUTPUTS, "bgspk", 1.0);
+ SPEAKER(config, "speaker").front_center();
+ m_bg->add_route(ALL_OUTPUTS, "speaker", 1.0);
}
+void s11_state::s11_only(machine_config &config)
+{
+ s11(config);
+ SPEAKER(config, "speaker").front_center();
+ m_dac->add_route(ALL_OUTPUTS, "speaker", 0.25);
+ m_cvsd_filter2->add_route(ALL_OUTPUTS, "speaker", 0.25);
+}
/*----------------------------
/ Grand Lizard 04/86 (#523)
@@ -709,8 +730,8 @@ GAME( 1986, rdkng_l1, rdkng_l4, s11_bgm, s11, s11_state, init_s11, ROT0, "Willia
GAME( 1986, rdkng_l2, rdkng_l4, s11_bgm, s11, s11_state, init_s11, ROT0, "Williams", "Road Kings (L-2)", MACHINE_MECHANICAL | MACHINE_NOT_WORKING)
GAME( 1986, rdkng_l3, rdkng_l4, s11_bgm, s11, s11_state, init_s11, ROT0, "Williams", "Road Kings (L-3)", MACHINE_MECHANICAL | MACHINE_NOT_WORKING)
-GAME( 1986, tts_l2, 0, s11, s11, s11_state, init_s11, ROT0, "Williams", "Tic-Tac-Strike (Shuffle) (L-2)", MACHINE_MECHANICAL | MACHINE_NOT_WORKING | MACHINE_NO_SOUND)
-GAME( 1986, tts_l1, tts_l2, s11, s11, s11_state, init_s11, ROT0, "Williams", "Tic-Tac-Strike (Shuffle) (L-1)", MACHINE_MECHANICAL | MACHINE_NOT_WORKING | MACHINE_NO_SOUND)
-GAME( 1987, gmine_l2, 0, s11, s11, s11_state, init_s11, ROT0, "Williams", "Gold Mine (Shuffle) (L-2)", MACHINE_MECHANICAL | MACHINE_NOT_WORKING)
-GAME( 1987, tdawg_l1, 0, s11, s11, s11_state, init_s11, ROT0, "Williams", "Top Dawg (Shuffle) (L-1)", MACHINE_MECHANICAL | MACHINE_NOT_WORKING)
-GAME( 1987, shfin_l1, 0, s11, s11, s11_state, init_s11, ROT0, "Williams", "Shuffle Inn (Shuffle) (L-1)", MACHINE_MECHANICAL | MACHINE_NOT_WORKING)
+GAME( 1986, tts_l2, 0, s11_only, s11, s11_state, init_s11, ROT0, "Williams", "Tic-Tac-Strike (Shuffle) (L-2)", MACHINE_MECHANICAL | MACHINE_NOT_WORKING | MACHINE_NO_SOUND)
+GAME( 1986, tts_l1, tts_l2, s11_only, s11, s11_state, init_s11, ROT0, "Williams", "Tic-Tac-Strike (Shuffle) (L-1)", MACHINE_MECHANICAL | MACHINE_NOT_WORKING | MACHINE_NO_SOUND)
+GAME( 1987, gmine_l2, 0, s11_only, s11, s11_state, init_s11, ROT0, "Williams", "Gold Mine (Shuffle) (L-2)", MACHINE_MECHANICAL | MACHINE_NOT_WORKING)
+GAME( 1987, tdawg_l1, 0, s11_only, s11, s11_state, init_s11, ROT0, "Williams", "Top Dawg (Shuffle) (L-1)", MACHINE_MECHANICAL | MACHINE_NOT_WORKING)
+GAME( 1987, shfin_l1, 0, s11_only, s11, s11_state, init_s11, ROT0, "Williams", "Shuffle Inn (Shuffle) (L-1)", MACHINE_MECHANICAL | MACHINE_NOT_WORKING)
diff --git a/src/mame/drivers/s11a.cpp b/src/mame/drivers/s11a.cpp
index 8b2aea080b8..9a8b8f6a001 100644
--- a/src/mame/drivers/s11a.cpp
+++ b/src/mame/drivers/s11a.cpp
@@ -218,13 +218,24 @@ void s11a_state::s11a_base(machine_config &config)
m_audiocpu->set_addrmap(AS_PROGRAM, &s11_state::s11_audio_map);
INPUT_MERGER_ANY_HIGH(config, m_audioirq).output_handler().set_inputline(m_audiocpu, M6802_IRQ_LINE);
- SPEAKER(config, "speaker").front_center();
- MC1408(config, m_dac, 0).add_route(ALL_OUTPUTS, "speaker", 0.25);
+ MC1408(config, m_dac, 0);
voltage_regulator_device &vref(VOLTAGE_REGULATOR(config, "vref"));
vref.add_route(0, m_dac, 1.0, DAC_VREF_POS_INPUT); vref.add_route(0, m_dac, -1.0, DAC_VREF_NEG_INPUT);
- SPEAKER(config, "speech").front_center();
- HC55516(config, m_hc55516, 0).add_route(ALL_OUTPUTS, "speech", 0.50);
+ // common CVSD filter for system 11 and 11a, this is also the same filter circuit as Sinistar/System 6 uses,
+ // and is ALMOST the same filter from the s11 bg sound boards, see /mame/audio/s11c_bg.cpp
+ // The CVSD filter has a large gain, about 4.6x
+ // The filter is boosting the ~5vpp audio signal from the CVSD chip to a ~23vpp (really ~17vpp) theoretical audio signal that the s11
+ // mainboard outputs on its volume control-repurposed-as-audio-out connector.
+ // In reality, the S11 mainboard outputs audio at a virtual ground level between +5v and -12v (so, 17VPP balanced around -7VDC), but since
+ // the CVSD chip's internal DAC can only output between a bit over +0x180/-0x180 out of 0x200, the most voltage it can ever output is
+ // between (assuming 0x1ff is 5VDC and 0x300 is 0VDC) a max of 4.375VDC and a min of 0.625VDC, i.e. 3.75VPP centered on 2.5VDC.
+ // In reality, the range is likely less than that.
+ // This means multiplying a 3.75VPP signal by 4.6 is 17.25VPP, which is almost exactly the expected 17V (12v+5v) VPP the output should have.
+ FILTER_BIQUAD(config, m_cvsd_filter2).opamp_mfb_lowpass_setup(RES_K(27), RES_K(15), RES_K(27), CAP_P(4700), CAP_P(1200));
+ FILTER_BIQUAD(config, m_cvsd_filter).opamp_mfb_lowpass_setup(RES_K(43), RES_K(36), RES_K(180), CAP_P(1800), CAP_P(180));
+ m_cvsd_filter->add_route(ALL_OUTPUTS, m_cvsd_filter2, 1.0);
+ HC55516(config, m_hc55516, 0).add_route(ALL_OUTPUTS, m_cvsd_filter, 1.0);
PIA6821(config, m_pias, 0);
m_pias->readpa_handler().set(FUNC(s11_state::sound_r));
@@ -241,24 +252,28 @@ void s11a_state::s11a(machine_config &config)
{
s11a_base(config);
/* Add the background music card */
- SPEAKER(config, "bgspk").front_center();
S11_BG(config, m_bg);
+ m_dac->add_route(ALL_OUTPUTS, m_bg, 0.4484/2.0);
+ m_cvsd_filter2->add_route(ALL_OUTPUTS, m_bg, 0.4484/2.0);
m_pia34->ca2_handler().set(m_bg, FUNC(s11_bg_device::resetq_w));
m_bg->pb_cb().set(m_pia34, FUNC(pia6821_device::portb_w));
m_bg->cb2_cb().set(m_pia34, FUNC(pia6821_device::cb1_w));
- m_bg->add_route(ALL_OUTPUTS, "bgspk", 1.0);
+ SPEAKER(config, "speaker").front_center();
+ m_bg->add_route(ALL_OUTPUTS, "speaker", 1.0);
}
void s11a_state::s11a_obg(machine_config &config)
{
s11a_base(config);
/* Add the older-style background music card */
- SPEAKER(config, "bgspk").front_center();
S11_OBG(config, m_bg);
+ m_dac->add_route(ALL_OUTPUTS, m_bg, 0.5319/2.0);
+ m_cvsd_filter2->add_route(ALL_OUTPUTS, m_bg, 0.5319/2.0);
m_pia34->ca2_handler().set(m_bg, FUNC(s11_obg_device::resetq_w));
m_bg->pb_cb().set(m_pia34, FUNC(pia6821_device::portb_w));
m_bg->cb2_cb().set(m_pia34, FUNC(pia6821_device::cb1_w));
- m_bg->add_route(ALL_OUTPUTS, "bgspk", 1.0);
+ SPEAKER(config, "speaker").front_center();
+ m_bg->add_route(ALL_OUTPUTS, "speaker", 1.0);
}
/*------------------------
diff --git a/src/mame/drivers/s11b.cpp b/src/mame/drivers/s11b.cpp
index bcc1965354d..a91622b7e22 100644
--- a/src/mame/drivers/s11b.cpp
+++ b/src/mame/drivers/s11b.cpp
@@ -27,7 +27,7 @@
- Bad Cats: "H" "Enter"
- Banzai Run: "S" "D" "F" (won't start due to calibration? needs more investigation, try hitting E and / and lots of keys until calibration finishes); - starts music
- Big Guns: "D" "F" "U"
- - Black Knight 2000: "D" "F" "Y"; 'x' starts music
+ - Black Knight 2000: "D" "F" "Y"; 'x' starts music; 'enter' 'left' and 'right' lock the 3 balls in the upper playfield to start the multiball. hold keypad '.' to activate the lightning wheel. '=' is the after-drawbridge target to score jackpots etc
- Cyclone: Nothing, game does not have switches to check for balls in the trough.
- Earthshaker: "D" "F" "W"
- Elvira and the Party Monsters: "D" "F" "U"
@@ -300,13 +300,24 @@ void s11b_state::s11b_base(machine_config &config)
m_audiocpu->set_addrmap(AS_PROGRAM, &s11_state::s11_audio_map);
INPUT_MERGER_ANY_HIGH(config, m_audioirq).output_handler().set_inputline(m_audiocpu, M6802_IRQ_LINE);
- SPEAKER(config, "speaker").front_center();
- MC1408(config, m_dac, 0).add_route(ALL_OUTPUTS, "speaker", 0.25);
+ MC1408(config, m_dac, 0);
voltage_regulator_device &vref(VOLTAGE_REGULATOR(config, "vref"));
vref.add_route(0, m_dac, 1.0, DAC_VREF_POS_INPUT); vref.add_route(0, m_dac, -1.0, DAC_VREF_NEG_INPUT);
- SPEAKER(config, "speech").front_center();
- HC55516(config, m_hc55516, 0).add_route(ALL_OUTPUTS, "speech", 0.50);
+ // this CVSD filter differs from the one on system 11 and 11a, possibly simplified so it uses more of the same components, or so it has a different
+ // shape/cutoff than the filter on the bg music/speech board, on purpose.
+ // The CVSD filter has a large gain, about 4.6x
+ // The filter is boosting the ~5vpp audio signal from the CVSD chip to a ~23vpp (really ~17vpp) theoretical audio signal that the s11
+ // mainboard outputs on its volume control-repurposed-as-audio-out connector.
+ // In reality, the S11 mainboard outputs audio at a virtual ground level between +5v and -12v (so, 17VPP balanced around -7VDC), but since
+ // the CVSD chip's internal DAC can only output between a bit over +0x180/-0x180 out of 0x200, the most voltage it can ever output is
+ // between (assuming 0x1ff is 5VDC and 0x300 is 0VDC) a max of 4.375VDC and a min of 0.625VDC, i.e. 3.75VPP centered on 2.5VDC.
+ // In reality, the range is likely less than that.
+ // This means multiplying a 3.75VPP signal by 4.6 is 17.25VPP, which is almost exactly the expected 17V (12v+5v) VPP the output should have.
+ FILTER_BIQUAD(config, m_cvsd_filter2).opamp_mfb_lowpass_setup(RES_K(12), RES_K(12), RES_K(56), CAP_P(4700), CAP_P(470));
+ FILTER_BIQUAD(config, m_cvsd_filter).opamp_mfb_lowpass_setup(RES_K(180), RES_K(180), RES_K(180), CAP_P(470), CAP_P(100));
+ m_cvsd_filter->add_route(ALL_OUTPUTS, m_cvsd_filter2, 1.0);
+ HC55516(config, m_hc55516, 0).add_route(ALL_OUTPUTS, m_cvsd_filter, 1.0/4.0); // to prevent massive clipping issues, we divide the signal by 4 here before going into the filters, then multiply it by 4 after it comes out the other end
PIA6821(config, m_pias, 0);
m_pias->readpa_handler().set(FUNC(s11_state::sound_r));
@@ -324,11 +335,13 @@ void s11b_state::s11b(machine_config &config)
s11b_base(config);
/* Add the background music card */
S11_BG(config, m_bg);
+ m_dac->add_route(ALL_OUTPUTS, m_bg, 0.4484/2.0);
+ m_cvsd_filter2->add_route(ALL_OUTPUTS, m_bg, (0.4484*4.0)/2.0);
m_pia34->ca2_handler().set(m_bg, FUNC(s11_bg_device::resetq_w));
m_bg->pb_cb().set(m_pia34, FUNC(pia6821_device::portb_w));
m_bg->cb2_cb().set(m_pia34, FUNC(pia6821_device::cb1_w));
- SPEAKER(config, "bgspk").front_center();
- m_bg->add_route(ALL_OUTPUTS, "bgspk", 1.0);
+ SPEAKER(config, "speaker").front_center();
+ m_bg->add_route(ALL_OUTPUTS, "speaker", 1.0);
}
void s11b_state::s11b_jokerz(machine_config &config)
@@ -336,6 +349,12 @@ void s11b_state::s11b_jokerz(machine_config &config)
s11b_base(config);
/* Add the pin sound 88 music card */
PINSND88(config, m_ps88);
+ // the dac and cvsd volumes should be equally mixed on the s11 board send to the audio board, whatever type it is
+ // the 4 gain values in the add_route statements are actually irrelevant, the ps88 device will override them
+ m_dac->add_route(ALL_OUTPUTS, m_ps88, 0.29, AUTO_ALLOC_INPUT, 0);
+ m_dac->add_route(ALL_OUTPUTS, m_ps88, 0.25, AUTO_ALLOC_INPUT, 1);
+ m_cvsd_filter2->add_route(ALL_OUTPUTS, m_ps88, (0.29*4.0), AUTO_ALLOC_INPUT, 0);
+ m_cvsd_filter2->add_route(ALL_OUTPUTS, m_ps88, (0.25*4.0), AUTO_ALLOC_INPUT, 1);
m_pia34->ca2_handler().set(m_ps88, FUNC(pinsnd88_device::resetq_w));
m_ps88->syncq_cb().set(m_pia34, FUNC(pia6821_device::ca1_w)); // the sync connection comes from sound connector pin 16 to MCA1, not the usual pin 12 to MCB1
SPEAKER(config, "cabinet").front_floor(); // the cabinet speaker is aimed down underneath the pinball table itself
diff --git a/src/mame/drivers/s11c.cpp b/src/mame/drivers/s11c.cpp
index 860814017c7..bbe95db10c6 100644
--- a/src/mame/drivers/s11c.cpp
+++ b/src/mame/drivers/s11c.cpp
@@ -190,7 +190,7 @@ void s11c_state::s11c(machine_config &config)
m_pia34->ca2_handler().set(m_bg, FUNC(s11c_bg_device::resetq_w));
m_bg->pb_cb().set(m_pia34, FUNC(pia6821_device::portb_w));
m_bg->cb2_cb().set(m_pia34, FUNC(pia6821_device::cb1_w));
- m_bg->add_route(ALL_OUTPUTS, "speaker", 1.0);
+ m_bg->add_route(ALL_OUTPUTS, "speaker", 1.5638);
}
// Unless otherwise noted, assume S11 Background Sound Board jumpers W2/W3 are
diff --git a/src/mame/drivers/williams.cpp b/src/mame/drivers/williams.cpp
index 0362a9880f2..a47f2195678 100644
--- a/src/mame/drivers/williams.cpp
+++ b/src/mame/drivers/williams.cpp
@@ -1904,7 +1904,8 @@ void joust2_state::joust2(machine_config &config)
// basic machine hardware
m_maincpu->set_addrmap(AS_PROGRAM, &joust2_state::d000_map);
- WILLIAMS_CVSD_SOUND(config, m_cvsd_sound).add_route(ALL_OUTPUTS, "speaker", 1.0);
+ S11_OBG(config, m_bg).add_route(ALL_OUTPUTS, "speaker", 2.0); // D-11298-3035 'pinbot style' older BG sound board
+ // Jumpers for the board: W1=? W2=open W3=present W4=open W5=open W6=open W7=present
// pia
m_pia[0]->readpa_handler().set_ioport("IN0").mask(0xf0);
@@ -1912,12 +1913,16 @@ void joust2_state::joust2(machine_config &config)
m_pia[0]->ca2_handler().set("mux", FUNC(ls157_device::select_w));
m_pia[1]->readpa_handler().set_ioport("IN2");
- m_pia[1]->writepb_handler().set(FUNC(joust2_state::snd_cmd_w));
- m_pia[1]->ca2_handler().set(FUNC(joust2_state::pia_3_cb1_w));
+ m_pia[1]->writepb_handler().set(FUNC(joust2_state::snd_cmd_w)); // this goes both to the sound cpu AND to the s11 bg cpu
+ m_pia[1]->ca2_handler().set(FUNC(joust2_state::pia_s11_bg_strobe_w));
m_pia[1]->cb2_handler().set(m_pia[2], FUNC(pia6821_device::ca1_w));
m_pia[1]->irqa_handler().set("mainirq", FUNC(input_merger_any_high_device::in_w<0>));
m_pia[1]->irqb_handler().set("mainirq", FUNC(input_merger_any_high_device::in_w<1>));
+ // these (and ca2 above) are educated guesses, as we have no schematics for joust 2's pcb which has the 20 pin system 11 bg sound connector on it; inferno, which we have schematics to, lacks this connector. All of pia[1] ca2, pia[2] cb1, and pia[2] cb2 are unconnected/grounded on inferno.
+ m_bg->cb2_cb().set(m_pia[2], FUNC(pia6821_device::cb1_w));
+ m_pia[2]->cb2_handler().set(m_bg, FUNC(s11_obg_device::resetq_w)); // inverted?
+
LS157(config, m_mux, 0);
m_mux->a_in_callback().set_ioport("INP1");
m_mux->b_in_callback().set_ioport("INP2");
@@ -3518,19 +3523,19 @@ ROM_START( joust2 )
ROM_LOAD( "cpu_2764_ic8_rom1_rev1.0f", 0x0E000, 0x2000, CRC(84517c3c) SHA1(de0b6473953783c091ddcc7aaa89fc1ec3b9d378) )
// sound board
- ROM_REGION( 0x90000, "cvsd:cpu", 0 )
- ROM_LOAD( "snd_27256_rom23_rev1.u4", 0x10000, 0x8000, CRC(3af6b47d) SHA1(aff19d65a4d9c249dec6a9e04a4066fada0f8fa1) )
+ ROM_REGION( 0x80000, "bg:cpu", 0 )
+ ROM_LOAD( "snd_27256_rom23_rev1.u4", 0x00000, 0x8000, CRC(3af6b47d) SHA1(aff19d65a4d9c249dec6a9e04a4066fada0f8fa1) )
+ ROM_RELOAD( 0x08000, 0x8000 )
+ ROM_RELOAD( 0x10000, 0x8000 )
ROM_RELOAD( 0x18000, 0x8000 )
- ROM_RELOAD( 0x20000, 0x8000 )
+ ROM_LOAD( "snd_27256_rom24_rev1.u19", 0x20000, 0x8000, CRC(e7f9ed2e) SHA1(6b9ef5189650f0b6b2866da7f532cdf851f02ead) )
ROM_RELOAD( 0x28000, 0x8000 )
- ROM_LOAD( "snd_27256_rom24_rev1.u19", 0x30000, 0x8000, CRC(e7f9ed2e) SHA1(6b9ef5189650f0b6b2866da7f532cdf851f02ead) )
+ ROM_RELOAD( 0x30000, 0x8000 )
ROM_RELOAD( 0x38000, 0x8000 )
- ROM_RELOAD( 0x40000, 0x8000 )
+ ROM_LOAD( "snd_27256_rom25_rev1.u20", 0x40000, 0x8000, CRC(c85b29f7) SHA1(b37e1890bd0dfa0c7db19fc878450718b60c1ca0) )
ROM_RELOAD( 0x48000, 0x8000 )
- ROM_LOAD( "snd_27256_rom25_rev1.u20", 0x50000, 0x8000, CRC(c85b29f7) SHA1(b37e1890bd0dfa0c7db19fc878450718b60c1ca0) )
+ ROM_RELOAD( 0x50000, 0x8000 )
ROM_RELOAD( 0x58000, 0x8000 )
- ROM_RELOAD( 0x60000, 0x8000 )
- ROM_RELOAD( 0x68000, 0x8000 )
ROM_REGION( 0xc000, "gfx1", 0 )
ROM_LOAD( "vid_27128_ic57_rom20_rev1.8f", 0x00000, 0x4000, CRC(572c6b01) SHA1(651df3223c1dc42543f57a7204ae492eb15a4999) )
@@ -3574,19 +3579,19 @@ ROM_START( joust2r1 )
ROM_LOAD( "cpu_2764_ic8_rom1_rev1.0f", 0x0E000, 0x2000, CRC(84517c3c) SHA1(de0b6473953783c091ddcc7aaa89fc1ec3b9d378) )
// sound board
- ROM_REGION( 0x90000, "cvsd:cpu", 0 )
- ROM_LOAD( "snd_27256_rom23_rev1.u4", 0x10000, 0x8000, CRC(3af6b47d) SHA1(aff19d65a4d9c249dec6a9e04a4066fada0f8fa1) )
+ ROM_REGION( 0x80000, "bg:cpu", 0 )
+ ROM_LOAD( "snd_27256_rom23_rev1.u4", 0x00000, 0x8000, CRC(3af6b47d) SHA1(aff19d65a4d9c249dec6a9e04a4066fada0f8fa1) )
+ ROM_RELOAD( 0x08000, 0x8000 )
+ ROM_RELOAD( 0x10000, 0x8000 )
ROM_RELOAD( 0x18000, 0x8000 )
- ROM_RELOAD( 0x20000, 0x8000 )
+ ROM_LOAD( "snd_27256_rom24_rev1.u19", 0x20000, 0x8000, CRC(e7f9ed2e) SHA1(6b9ef5189650f0b6b2866da7f532cdf851f02ead) )
ROM_RELOAD( 0x28000, 0x8000 )
- ROM_LOAD( "snd_27256_rom24_rev1.u19", 0x30000, 0x8000, CRC(e7f9ed2e) SHA1(6b9ef5189650f0b6b2866da7f532cdf851f02ead) )
+ ROM_RELOAD( 0x30000, 0x8000 )
ROM_RELOAD( 0x38000, 0x8000 )
- ROM_RELOAD( 0x40000, 0x8000 )
+ ROM_LOAD( "snd_27256_rom25_rev1.u20", 0x40000, 0x8000, CRC(c85b29f7) SHA1(b37e1890bd0dfa0c7db19fc878450718b60c1ca0) )
ROM_RELOAD( 0x48000, 0x8000 )
- ROM_LOAD( "snd_27256_rom25_rev1.u20", 0x50000, 0x8000, CRC(c85b29f7) SHA1(b37e1890bd0dfa0c7db19fc878450718b60c1ca0) )
+ ROM_RELOAD( 0x50000, 0x8000 )
ROM_RELOAD( 0x58000, 0x8000 )
- ROM_RELOAD( 0x60000, 0x8000 )
- ROM_RELOAD( 0x68000, 0x8000 )
ROM_REGION( 0xc000, "gfx1", 0 )
ROM_LOAD( "vid_27128_ic57_rom20_rev1.8f", 0x00000, 0x4000, CRC(572c6b01) SHA1(651df3223c1dc42543f57a7204ae492eb15a4999) )
diff --git a/src/mame/includes/s11.h b/src/mame/includes/s11.h
index 4c63ff9df64..af42237c704 100644
--- a/src/mame/includes/s11.h
+++ b/src/mame/includes/s11.h
@@ -10,12 +10,14 @@
#define MAME_INCLUDES_S11_H
#include "cpu/m6800/m6800.h"
-#include "audio/s11c_bg.h"
#include "audio/pinsnd88.h"
+#include "audio/s11c_bg.h"
#include "machine/6821pia.h"
#include "machine/genpin.h"
#include "machine/input_merger.h"
+#include "machine/rescap.h"
#include "sound/dac.h"
+#include "sound/flt_biquad.h"
#include "sound/hc55516.h"
#include "sound/ym2151.h"
@@ -45,6 +47,8 @@ public:
, m_audiocpu(*this, "audiocpu")
, m_audioirq(*this, "audioirq")
, m_hc55516(*this, "hc55516")
+ , m_cvsd_filter(*this, "cvsd_filter")
+ , m_cvsd_filter2(*this, "cvsd_filter2")
, m_dac(*this, "dac")
, m_pias(*this, "pias")
, m_pia21(*this, "pia21")
@@ -60,6 +64,7 @@ public:
{ }
void s11(machine_config &config);
+ void s11_only(machine_config &config);
void s11_bgs(machine_config &config);
void s11_bgm(machine_config &config);
@@ -113,6 +118,8 @@ protected:
optional_device<m6802_cpu_device> m_audiocpu;
optional_device<input_merger_device> m_audioirq;
optional_device<hc55516_device> m_hc55516;
+ optional_device<filter_biquad_device> m_cvsd_filter;
+ optional_device<filter_biquad_device> m_cvsd_filter2;
optional_device<mc1408_device> m_dac;
optional_device<pia6821_device> m_pias;
required_device<pia6821_device> m_pia21;
diff --git a/src/mame/includes/williams.h b/src/mame/includes/williams.h
index bc910e8be0e..2054be06828 100644
--- a/src/mame/includes/williams.h
+++ b/src/mame/includes/williams.h
@@ -11,6 +11,7 @@
#pragma once
#include "audio/williams.h"
+#include "audio/s11c_bg.h"
#include "cpu/m6800/m6800.h"
#include "cpu/m6809/m6809.h"
#include "machine/6821pia.h"
@@ -479,7 +480,7 @@ public:
joust2_state(const machine_config &mconfig, device_type type, const char *tag) :
williams_d000_rom_state(mconfig, type, tag),
m_mux(*this, "mux"),
- m_cvsd_sound(*this, "cvsd")
+ m_bg(*this, "bg")
{ }
void joust2(machine_config &config);
@@ -489,7 +490,7 @@ private:
virtual void driver_init() override;
required_device<ls157_device> m_mux;
- required_device<williams_cvsd_sound_device> m_cvsd_sound;
+ required_device<s11_obg_device> m_bg;
uint16_t m_current_sound_data;
virtual TILE_GET_INFO_MEMBER(get_tile_info) override;
@@ -497,7 +498,7 @@ private:
TIMER_CALLBACK_MEMBER(deferred_snd_cmd_w);
void snd_cmd_w(u8 data);
- DECLARE_WRITE_LINE_MEMBER(pia_3_cb1_w);
+ DECLARE_WRITE_LINE_MEMBER(pia_s11_bg_strobe_w);
};
/*----------- defined in video/williams.cpp -----------*/
diff --git a/src/mame/machine/williams.cpp b/src/mame/machine/williams.cpp
index feff3b4b2bc..bd2815f4faa 100644
--- a/src/mame/machine/williams.cpp
+++ b/src/mame/machine/williams.cpp
@@ -508,16 +508,16 @@ TIMER_CALLBACK_MEMBER(joust2_state::deferred_snd_cmd_w)
}
-WRITE_LINE_MEMBER(joust2_state::pia_3_cb1_w)
+WRITE_LINE_MEMBER(joust2_state::pia_s11_bg_strobe_w)
{
m_current_sound_data = (m_current_sound_data & ~0x100) | ((state << 8) & 0x100);
- m_cvsd_sound->write(m_current_sound_data);
+ m_bg->ctrl_w(state);
}
void joust2_state::snd_cmd_w(u8 data)
{
m_current_sound_data = (m_current_sound_data & ~0xff) | (data & 0xff);
- m_cvsd_sound->write(m_current_sound_data);
+ m_bg->data_w(data);
machine().scheduler().synchronize(timer_expired_delegate(FUNC(joust2_state::deferred_snd_cmd_w),this), m_current_sound_data);
}