summaryrefslogtreecommitdiffstatshomepage
path: root/src/devices/sound/hc55516.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/devices/sound/hc55516.cpp')
-rw-r--r--src/devices/sound/hc55516.cpp578
1 files changed, 407 insertions, 171 deletions
diff --git a/src/devices/sound/hc55516.cpp b/src/devices/sound/hc55516.cpp
index 99cc782dc20..0bd36ae381f 100644
--- a/src/devices/sound/hc55516.cpp
+++ b/src/devices/sound/hc55516.cpp
@@ -1,16 +1,33 @@
// 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:
+ - see .h file
+ - research HC-55536 and HC-55564 differences vs HC-55516 (better auto-zeroing,
+ and removal of the encoder offset compensation DAC?)
+ - /src/mame/exidy/exidy440_a.cpp has its own internal implementation of the
+ MC3417 and MC3418, it should be using this file instead
+ - MC3417 interpolation slope being determined by the number of samples in
+ the current stream slice doesn't make sense
+
+ BTANB:
+ - outputs a low-volume but very high-pitched background tone when /FZ is
+ active and the hardware doesn't have a low-pass filter
*****************************************************************************/
#include "emu.h"
#include "hc55516.h"
-
-/* 4x oversampling */
+// fixed samplerate of 192khz
#define SAMPLE_RATE (48000 * 4)
#define INTEGRATOR_LEAK_TC 0.001
@@ -18,36 +35,24 @@
#define FILTER_CHARGE_TC 0.004
#define FILTER_MIN 0.0416
#define FILTER_MAX 1.0954
-#define SAMPLE_GAIN 10000.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)
+#define SAMPLE_GAIN (10000.0 / 32768.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, 1)
+ , 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_samples_generated(0)
{
}
@@ -55,258 +60,489 @@ 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_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)
+int 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
-//-------------------------------------------------
+void cvsd_device_base::mclock_w(int state)
+{
+ clock_w(state);
+}
-void mc3417_device::device_start()
+void cvsd_device_base::digin_w(int state)
{
- 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")
+void cvsd_device_base::dec_encq_w(int state)
+{
+ 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)
+int 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;
}
-
-void hc55516_device::start_common(uint8_t _shiftreg_mask, int _active_clock_hi)
+inline bool cvsd_device_base::is_clock_changed(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 ((!m_last_clock_state && clock_state) || (m_last_clock_state && !clock_state));
+}
- m_shiftreg_mask = _shiftreg_mask;
- m_active_clock_hi = _active_clock_hi;
- m_last_clock_state = 0;
+inline bool cvsd_device_base::is_active_clock_transition(bool clock_state)
+{
+ return ((clock_state != m_last_clock_state) && (clock_state == m_active_clock_edge));
+}
- /* create the stream */
- m_channel = machine().sound().stream_alloc(*this, 0, 1, SAMPLE_RATE);
+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 bool(((((uint64_t)m_samples_generated << 32) * clock() * 2 / SAMPLE_RATE) >> 32) & 0x1);
+ return bool(((uint64_t)m_samples_generated * clock() * 2 / SAMPLE_RATE) & 0x01);
+}
- 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::digit_w(int digit)
+{
+ m_stream->update();
+ m_buffered_bit = bool(digit);
}
-inline int hc55516_device::is_external_oscillator()
+void cvsd_device_base::clock_w(int state)
{
- return clock() != 0;
+ // update the output buffer first
+ m_stream->update();
+ bool clock_state = bool(state);
+
+ // 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;
}
+int cvsd_device_base::clock_state_r()
+{
+ // only makes sense for setups with an external oscillator
+ assert(is_external_oscillator());
+
+ m_stream->update();
+
+ return current_clock_state();
+}
-inline int hc55516_device::is_active_clock_transition(int clock_state)
+void cvsd_device_base::process_bit(bool bit, bool clock_state)
{
- return (( m_active_clock_hi && !m_last_clock_state && clock_state) ||
- (!m_active_clock_hi && m_last_clock_state && !clock_state));
+ // stub
}
+//-------------------------------------------------
+// sound_stream_update - handle a stream update
+//-------------------------------------------------
-inline int hc55516_device::current_clock_state()
+void cvsd_device_base::sound_stream_update(sound_stream &stream)
{
- return ((uint64_t)m_update_count * clock() * 2 / SAMPLE_RATE) & 0x01;
+ // Stub, just return silence
+ m_samples_generated += stream.samples();
+ if (m_samples_generated >= SAMPLE_RATE)
+ m_samples_generated -= SAMPLE_RATE;
}
-void hc55516_device::process_digit()
+//#########################################
+// 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)
{
- double integrator = m_integrator, temp;
+}
- /* shift the bit into the shift register */
- m_shiftreg = (m_shiftreg << 1) | m_digit;
+// 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, 1)
+ , m_sylmask(sylmask)
+ , m_sylshift(sylshift)
+ , m_syladd(syladd)
+ , m_intshift(intshift)
+ , m_sylfilter(0)
+ , m_intfilter(0)
+ , m_next_sample(0)
+ , m_agc(true)
+ , m_buffered_fzq(true)
+{
+}
- /* move the estimator up or down a step based on the bit */
- if (m_digit)
- integrator += m_filter;
- else
- integrator -= m_filter;
+//-------------------------------------------------
+// device_start - device-specific start
+//-------------------------------------------------
- /* simulate leakage */
- integrator *= m_leak;
+void hc55516_device::device_start()
+{
+ cvsd_device_base::device_start();
- /* 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))
- {
- m_filter = FILTER_MAX - ((FILTER_MAX - m_filter) * m_charge);
+ save_item(NAME(m_sylfilter));
+ save_item(NAME(m_intfilter));
+ save_item(NAME(m_next_sample));
+ save_item(NAME(m_agc));
+ save_item(NAME(m_buffered_fzq));
+}
- if (m_filter > FILTER_MAX)
- m_filter = FILTER_MAX;
- }
+//-------------------------------------------------
+// device_reset - device-specific reset
+//-------------------------------------------------
- /* simulate decay */
- else
- {
- m_filter *= m_decay;
+void hc55516_device::device_reset()
+{
+ cvsd_device_base::device_reset();
- if (m_filter < FILTER_MIN)
- m_filter = FILTER_MIN;
- }
+ // 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
+}
- /* compute the sample as a 32-bit word */
- temp = integrator * SAMPLE_GAIN;
- m_integrator = integrator;
+// device specific functions
- /* 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));
+void hc55516_device::fzq_w(int state)
+{
+ m_buffered_fzq = state;
}
-void hc55516_device::clock_w(int state)
+int hc55516_device::agc_r()
{
- uint8_t clock_state = state ? true : false;
+ // prevent debugger from changing the internal state
+ if (!machine().side_effects_disabled())
+ m_stream->update(); // bring up to date first
+ return m_agc;
+}
- /* only makes sense for setups with a software driven clock */
- assert(!is_external_oscillator());
+void hc55516_device::process_bit(bool bit, bool clock_state)
+{
+ const bool frozen = (((m_intfilter >= 0x180) && !bit) || ((m_intfilter <= -0x180) && bit));
+ int32_t sum;
- /* speech clock changing? */
if (is_active_clock_transition(clock_state))
{
- /* update the output buffer before changing the registers */
- m_channel->update();
+ // 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.isunset())
+ fzq_state = m_fzq_pull_cb();
+ else
+ fzq_state = m_buffered_fzq;
+
+ // if /FZ is active, the input bit is ignored and the inverse of the previous bit in the shifter is used instead
+ if (!fzq_state)
+ 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;
- /* clear the update count */
- m_update_count = 0;
+ sum = util::sext(((~m_intfilter) >> m_intshift) + 1, 10);
+ }
+ else
+ {
+ // inactive clock transition
+ if (m_shiftreg & 1)
+ {
+ sum = util::sext((~std::max(2, m_sylfilter >> 6)) + 1, 10);
+ }
+ else
+ {
+ sum = util::sext(std::max(2, m_sylfilter >> 6), 10);
+ }
+ }
- process_digit();
+ if (!frozen)
+ {
+ m_intfilter = util::sext(m_intfilter + sum, 10);
}
- /* update the clock */
- m_last_clock_state = clock_state;
+ /* 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;
+
+ // push agc state if a callback is present
+ m_agc_push_cb(m_agc);
}
+//-------------------------------------------------
+// sound_stream_update_legacy - handle a stream update
+//-------------------------------------------------
-void hc55516_device::digit_w(int digit)
+void hc55516_device::sound_stream_update(sound_stream &stream)
{
if (is_external_oscillator())
{
- m_channel->update();
- m_new_digit = digit & 1;
+ // external oscillator
+ for (int i = 0; i < stream.samples(); i++)
+ {
+ stream.put_int(0, i, m_next_sample, 32768);
+
+ m_samples_generated++;
+
+ 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;
+ }
}
+
+ // software driven clock
else
- m_digit = digit & 1;
+ {
+ for (int i = 0; i < stream.samples(); i++)
+ stream.put_int(0, i, m_next_sample, 32768);
+ }
}
-int hc55516_device::clock_state_r()
+
+//#########################################
+// 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)
{
- /* only makes sense for setups with an external oscillator */
- assert(is_external_oscillator());
+}
- m_channel->update();
+//-------------------------------------------------
+// device_reset - device-specific reset
+//-------------------------------------------------
- return current_clock_state();
+void hc55532_device::device_reset()
+{
+ 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
}
+
+//##########################################
+// 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(0.0)
+ , m_intfilter(0.0)
+ , m_curr_sample(0.0)
+ , m_next_sample(0.0)
+{
+}
+
//-------------------------------------------------
-// sound_stream_update - handle a stream update
+// device_start - device-specific startup
//-------------------------------------------------
-void hc55516_device::sound_stream_update(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples)
+void mc3417_device::device_start()
{
- stream_sample_t *buffer = outputs[0];
- int i;
- int32_t sample, slope;
+ cvsd_device_base::device_start();
+
+ save_item(NAME(m_sylfilter));
+ save_item(NAME(m_intfilter));
+ save_item(NAME(m_curr_sample));
+ save_item(NAME(m_next_sample));
+}
+
+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);
- /* zero-length? bail */
- if (samples == 0)
- return;
+ // move the estimator up or down a step based on the bit
+ if (!bit)
+ m_intfilter += m_sylfilter;
+ else
+ m_intfilter -= m_sylfilter;
+ // simulate leakage
+ m_intfilter *= 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 = FILTER_MAX - ((FILTER_MAX - m_sylfilter) * m_charge);
+
+ if (m_sylfilter > FILTER_MAX)
+ m_sylfilter = FILTER_MAX;
+ }
+ else
+ {
+ m_sylfilter *= m_decay;
+
+ if (m_sylfilter < FILTER_MIN)
+ m_sylfilter = FILTER_MIN;
+ }
+
+ // compute the sample as a 32-bit word
+ m_next_sample = m_intfilter * SAMPLE_GAIN;
+ }
+}
+
+void mc3417_device::sound_stream_update(sound_stream &stream)
+{
if (!is_external_oscillator())
{
- /* track how many samples we've updated without a clock */
- m_update_count += 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 += stream.samples();
+ if (m_samples_generated > SAMPLE_RATE / 32)
{
- m_update_count = SAMPLE_RATE;
+ m_samples_generated = SAMPLE_RATE;
m_next_sample = 0;
}
}
- /* compute the interpolation slope */
- sample = m_curr_sample;
- slope = ((int32_t)m_next_sample - sample) / samples;
+ // compute the interpolation slope
+ sound_stream::sample_t sample = m_curr_sample;
+ sound_stream::sample_t slope = (m_next_sample - sample) / stream.samples();
m_curr_sample = m_next_sample;
if (is_external_oscillator())
{
- /* external oscillator */
- for (i = 0; i < samples; i++, sample += slope)
+ // external oscillator
+ for (int i = 0; i < stream.samples(); i++, sample += slope)
{
- uint8_t clock_state;
-
- *buffer++ = sample;
+ stream.put(0, i, sample);
- m_update_count++;
+ m_samples_generated++;
- 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))
+ // pull in next digit on the appropriate edge of the clock
+ 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;
}
}
- /* software driven clock */
+ // software driven clock
else
- for (i = 0; i < samples; i++, sample += slope)
- *buffer++ = sample;
+ {
+ for (int i = 0; i < stream.samples(); i++, sample += slope)
+ stream.put(0, i, sample);
+ }
}
-void mc3417_device::sound_stream_update(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples)
-{
- hc55516_device::sound_stream_update(stream, inputs, outputs, samples);
-}
-void mc3418_device::sound_stream_update(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples)
+
+//##########################################
+// 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, samples);
}