summaryrefslogtreecommitdiffstatshomepage
diff options
context:
space:
mode:
author Aaron Giles <aaron@aarongiles.com>2020-09-15 15:14:15 -0700
committer Aaron Giles <aaron@aarongiles.com>2020-09-15 15:17:06 -0700
commitea60f78b23d94cd9c57f3de619f51e57503cddc1 (patch)
tree8d5cc779e0c11add74292ca7573e9dbcde4fabaf
parent87dce66d84f1fb33f8f09b6ef1b5c86dcc5db8af (diff)
cem3394: Major rework
* Updated to new stream callback * All calculations are now doing in floating point * External input is now done via an input stream * First attempt at implementing a proper filter -- needs some major work; checking in current state to get some assistance
-rw-r--r--src/devices/sound/cem3394.cpp444
-rw-r--r--src/devices/sound/cem3394.h52
-rw-r--r--src/mame/audio/sente6vb.cpp138
-rw-r--r--src/mame/audio/sente6vb.h14
4 files changed, 275 insertions, 373 deletions
diff --git a/src/devices/sound/cem3394.cpp b/src/devices/sound/cem3394.cpp
index 778739c7a54..5b288d2d700 100644
--- a/src/devices/sound/cem3394.cpp
+++ b/src/devices/sound/cem3394.cpp
@@ -19,23 +19,36 @@
#include <algorithm>
-/* waveform generation parameters */
+// use 0.25 as the base volume for pulses
+static constexpr double PULSE_VOLUME = 0.25;
+
+// sawtooth is 27% larger than pulses
+static constexpr double SAWTOOTH_VOLUME = PULSE_VOLUME * 1.27f;
+
+// triangle is 27% larger than sawtooth
+static constexpr double TRIANGLE_VOLUME = SAWTOOTH_VOLUME * 1.27f;
+
+// external input is unknown but let's make it the same as the pulse
+static constexpr double EXTERNAL_VOLUME = PULSE_VOLUME;
+
+
+// waveform generation parameters
#define ENABLE_PULSE 1
#define ENABLE_TRIANGLE 1
#define ENABLE_SAWTOOTH 1
#define ENABLE_EXTERNAL 1
-/* pulse shaping parameters */
-/* examples: */
-/* hat trick - skidding ice sounds too loud if minimum width is too big */
-/* snake pit - melody during first level too soft if minimum width is too small */
-/* snake pit - bonus counter at the end of level */
-/* snacks'n jaxson - laugh at end of level is too soft if minimum width is too small */
+// pulse shaping parameters
+// examples:
+// hat trick - skidding ice sounds too loud if minimum width is too big
+// snake pit - melody during first level too soft if minimum width is too small
+// snake pit - bonus counter at the end of level
+// snacks'n jaxson - laugh at end of level is too soft if minimum width is too small
#define LIMIT_WIDTH 1
-#define MINIMUM_WIDTH 0.25
-#define MAXIMUM_WIDTH 0.75
+#define MINIMUM_WIDTH 0.2
+#define MAXIMUM_WIDTH 0.8
/********************************************************************************
@@ -97,13 +110,6 @@
#define WAVE_SAWTOOTH 2
#define WAVE_PULSE 4
-// keep lots of fractional bits
-#define FRACTION_BITS 28
-#define FRACTION_ONE (1 << FRACTION_BITS)
-#define FRACTION_ONE_D ((double)(1 << FRACTION_BITS))
-#define FRACTION_MASK (FRACTION_ONE - 1)
-#define FRACTION_MULT(a,b) (((a) >> (FRACTION_BITS / 2)) * ((b) >> (FRACTION_BITS - FRACTION_BITS / 2)))
-
// device type definition
DEFINE_DEVICE_TYPE(CEM3394, cem3394_device, "cem3394", "CEM3394 Synthesizer Voice")
@@ -116,208 +122,143 @@ DEFINE_DEVICE_TYPE(CEM3394, cem3394_device, "cem3394", "CEM3394 Synthesizer Voic
// cem3394_device - constructor
//-------------------------------------------------
-cem3394_device::cem3394_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
- : device_t(mconfig, CEM3394, tag, owner, clock),
- device_sound_interface(mconfig, *this),
- m_ext_cb(*this),
- m_stream(nullptr),
- m_vco_zero_freq(0.0),
- m_filter_zero_freq(0.0),
- m_wave_select(0),
- m_volume(0),
- m_mixer_internal(0),
- m_mixer_external(0),
- m_position(0),
- m_step(0),
- m_filter_position(0),
- m_filter_step(0),
- m_modulation_depth(0),
- m_last_ext(0),
- m_pulse_width(0),
- m_inv_sample_rate(0.0),
- m_sample_rate(0),
- m_mixer_buffer(nullptr),
- m_external_buffer(nullptr)
+cem3394_device::cem3394_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock) :
+ device_t(mconfig, CEM3394, tag, owner, clock),
+ device_sound_interface(mconfig, *this),
+ m_stream(nullptr),
+ m_vco_zero_freq(500.0),
+ m_filter_zero_freq(1300.0),
+ m_values{0},
+ m_wave_select(0),
+ m_volume(0),
+ m_mixer_internal(0),
+ m_mixer_external(0),
+ m_vco_position(0),
+ m_vco_step(0),
+ m_filter_frequency(1300),
+ m_filter_modulation(0),
+ m_filter_resonance(0),
+ m_filter_in{0},
+ m_filter_out{0},
+ m_pulse_width(0),
+ m_inv_sample_rate(1.0/48000.0)
{
- std::fill(std::begin(m_values), std::end(m_values), 0.0);
}
//-------------------------------------------------
-// sound_stream_update_legacy - generate sound to the mix buffer in mono
+// sound_stream_update - generate sound to the mix
+// buffer in mono
//-------------------------------------------------
-void cem3394_device::sound_stream_update_legacy(sound_stream &stream, stream_sample_t const * const *inputs, stream_sample_t * const *outputs, int samples)
+double cem3394_device::filter(double input, double cutoff)
{
- int int_volume = (m_volume * m_mixer_internal) / 256;
- int ext_volume = (m_volume * m_mixer_external) / 256;
- uint32_t step = m_step, position, end_position = 0;
- stream_sample_t *buffer = outputs[0];
- int16_t *mix, *ext;
- int i;
-
- /* external volume is effectively 0 if no external function */
- if (m_ext_cb.isnull() || !ENABLE_EXTERNAL)
- ext_volume = 0;
-
- /* adjust the volume for the filter */
- if (step > m_filter_step)
- int_volume /= step - m_filter_step;
-
- /* bail if nothing's going on */
- if (int_volume == 0 && ext_volume == 0)
+ // clamp cutoff to useful range, 20Hz-20kHz
+ cutoff = std::max(std::min(cutoff, 20000.0), 20.0);
+
+ // clamp resonance to 0.95 to prevent infinite gain
+ double r = 4.0 * std::min(m_filter_resonance, 0.95);
+
+ double g = 2 * M_PI * cutoff;
+ double zc = g / tan(g/2 * m_inv_sample_rate);
+
+ double gzc = zc / g;
+ double gzc2 = gzc * gzc;
+ double gzc3 = gzc2 * gzc;
+ double gzc4 = gzc3 * gzc;
+ double r1 = 1 + r;
+
+ double a0 = r1;
+ double a1 = 4 * r1;
+ double a2 = 6 * r1;
+ double a3 = 4 * r1;
+ double a4 = r1;
+
+ double b0 = r1 + 4 * gzc + 6 * gzc2 + 4 * gzc3 + gzc4;
+ double b1 = 4 * (r1 + 2 * gzc - 2 * gzc3 - gzc4);
+ double b2 = 6 * (r1 - 2 * gzc2 + gzc4);
+ double b3 = 4 * (r1 - 2 * gzc + 2 * gzc3 - gzc4);
+ double b4 = r1 - 4 * gzc + 6 * gzc2 - 4 * gzc3 + gzc4;
+
+ double output = (input * a0
+ + m_filter_in[0] * a1 + m_filter_in[1] * a2 + m_filter_in[2] * a3 + m_filter_in[3] * a4
+ - m_filter_out[0] * b1 - m_filter_out[1] * b2 - m_filter_out[2] * b3 - m_filter_out[3] * b4) / b0;
+
+// sound_assert(!std::isnan(output));
+// sound_assert(output >= -1.5 && output <= 1.5);
+ if (output < -1.5 || output > 1.5 || std::isnan(output))
{
- memset(buffer, 0, sizeof(*buffer) * samples);
- return;
+ if (m_filter_out[0] > -1.5 && m_filter_out[0] < 1.5)
+ printf("cutoff: %6.0f res: %.5f output: %.5f\n", cutoff, m_filter_resonance, output);
}
+ if (std::isnan(output)) output = 0;
+
+ m_filter_in[3] = m_filter_in[2];
+ m_filter_in[2] = m_filter_in[1];
+ m_filter_in[1] = m_filter_in[0];
+ m_filter_in[0] = input;
+
+ m_filter_out[3] = m_filter_out[2];
+ m_filter_out[2] = m_filter_out[1];
+ m_filter_out[1] = m_filter_out[0];
+ m_filter_out[0] = output;
+
+ if (output < -1.0)
+ output = -1.0;
+ else if (output > 1.0)
+ output = 1.0;
+ return output;
+}
- /* if there's external stuff, fetch and process it now */
- if (ext_volume != 0)
- {
- uint32_t fposition = m_filter_position, fstep = m_filter_step, depth;
- int16_t last_ext = m_last_ext;
-
- /* fetch the external data */
- m_ext_cb(samples, m_external_buffer.get());
-
- /* compute the modulation depth, and adjust fstep to the maximum frequency */
- /* we lop off 13 bits of depth so that we can multiply by stepadjust, below, */
- /* which has 13 bits of precision */
- depth = FRACTION_MULT(fstep, m_modulation_depth);
- fstep += depth;
- depth >>= 13;
-
- /* "apply" the filter: note this is pretty cheesy; it basically just downsamples the
- external sample to filter_freq by allowing only 2 transitions for every cycle */
- for (i = 0, ext = m_external_buffer.get(), position = m_position; i < samples; i++, ext++)
- {
- uint32_t newposition;
- int32_t stepadjust;
-
- /* update the position and compute the adjustment from a triangle wave */
- if (position & (1 << (FRACTION_BITS - 1)))
- stepadjust = 0x2000 - ((position >> (FRACTION_BITS - 14)) & 0x1fff);
- else
- stepadjust = (position >> (FRACTION_BITS - 14)) & 0x1fff;
- position += step;
-
- /* if we cross a half-step boundary, allow the next byte of the external input */
- newposition = fposition + fstep - (stepadjust * depth);
- if ((newposition ^ fposition) & ~(FRACTION_MASK >> 1))
- last_ext = *ext;
- else
- *ext = last_ext;
- fposition = newposition & FRACTION_MASK;
- }
+void cem3394_device::sound_stream_update(sound_stream &stream, std::vector<read_stream_view> const &inputs, std::vector<write_stream_view> &outputs)
+{
+ auto &external = inputs[0];
+ auto &buffer = outputs[0];
- /* update the final filter values */
- m_filter_position = fposition;
- m_last_ext = last_ext;
- }
+ if (m_wave_select == 0 && m_mixer_external == 0)
+ logerror("%f V didn't cut it\n", m_values[WAVE_SELECT]);
- /* if there's internal stuff, generate it */
- if (int_volume != 0)
+ // loop over samples
+ for (int sampindex = 0; sampindex < buffer.samples(); sampindex++)
{
- if (m_wave_select == 0 && !ext_volume)
- logerror("%f V didn't cut it\n", m_values[WAVE_SELECT]);
-
- /* handle the pulse component; it maxes out at 0x1932, which is 27% smaller than */
- /* the sawtooth (since the value is constant, this is the best place to have an */
- /* odd value for volume) */
- if (ENABLE_PULSE && (m_wave_select & WAVE_PULSE))
- {
- uint32_t pulse_width = m_pulse_width;
+ // get the current VCO position and step it forward
+ double vco_position = m_vco_position;
+ m_vco_position += m_vco_step;
- /* if the width is wider than the step, we're guaranteed to hit it once per cycle */
- if (pulse_width >= step)
- {
- for (i = 0, mix = m_mixer_buffer.get(), position = m_position; i < samples; i++, mix++)
- {
- if (position < pulse_width)
- *mix = 0x1932;
- else
- *mix = 0x0000;
- position = (position + step) & FRACTION_MASK;
- }
- }
-
- /* otherwise, we compute a volume and watch for cycle boundary crossings */
- else
- {
- int16_t volume = 0x1932 * pulse_width / step;
- for (i = 0, mix = m_mixer_buffer.get(), position = m_position; i < samples; i++, mix++)
- {
- uint32_t newposition = position + step;
- if ((newposition ^ position) & ~FRACTION_MASK)
- *mix = volume;
- else
- *mix = 0x0000;
- position = newposition & FRACTION_MASK;
- }
- }
- end_position = position;
- }
+ // clamp VCO position to a fraction
+ if (m_vco_position >= 1.0)
+ m_vco_position -= floor(m_vco_position);
- /* otherwise, clear the mixing buffer */
- else
- memset(m_mixer_buffer.get(), 0, sizeof(int16_t) * samples);
+ // handle the pulse component; might need some more thought here
+ double result = 0;
+ if (ENABLE_PULSE && (m_wave_select & WAVE_PULSE))
+ if (vco_position < m_pulse_width)
+ result += PULSE_VOLUME * m_mixer_internal;
- /* handle the sawtooth component; it maxes out at 0x2000, which is 27% larger */
- /* than the pulse */
+ // handle the sawtooth component
if (ENABLE_SAWTOOTH && (m_wave_select & WAVE_SAWTOOTH))
- {
- for (i = 0, mix = m_mixer_buffer.get(), position = m_position; i < samples; i++, mix++)
- {
- *mix += ((position >> (FRACTION_BITS - 14)) & 0x3fff) - 0x2000;
- position += step;
- }
- end_position = position & FRACTION_MASK;
- }
+ result += SAWTOOTH_VOLUME * m_mixer_internal * vco_position;
- /* handle the triangle component; it maxes out at 0x2800, which is 25% larger */
- /* than the sawtooth (should be 27% according to the specs, but 25% saves us */
- /* a multiplication) */
+ // always compute the triangle waveform which is also used for filter modulation
+ double triangle = 2.0 * vco_position;
+ if (triangle > 1.0)
+ triangle = 2.0 - triangle;
+
+ // handle the triangle component
if (ENABLE_TRIANGLE && (m_wave_select & WAVE_TRIANGLE))
- {
- for (i = 0, mix = m_mixer_buffer.get(), position = m_position; i < samples; i++, mix++)
- {
- int16_t value;
- if (position & (1 << (FRACTION_BITS - 1)))
- value = 0x2000 - ((position >> (FRACTION_BITS - 14)) & 0x1fff);
- else
- value = (position >> (FRACTION_BITS - 14)) & 0x1fff;
- *mix += value + (value >> 2);
- position += step;
- }
- end_position = position & FRACTION_MASK;
- }
+ result += TRIANGLE_VOLUME * m_mixer_internal * triangle;
- /* update the final position */
- m_position = end_position;
- }
+ // compute extension input (for Bally/Sente this is the noise)
+ if (ENABLE_EXTERNAL)
+ result += EXTERNAL_VOLUME * m_mixer_external * external.get(sampindex);
- /* mix it down */
- mix = m_mixer_buffer.get();
- ext = m_external_buffer.get();
- {
- /* internal + external */
- if (ext_volume != 0 && int_volume != 0)
- {
- for (i = 0; i < samples; i++, mix++, ext++)
- *buffer++ = (*mix * int_volume + *ext * ext_volume) / 128;
- }
- /* internal only */
- else if (int_volume != 0)
- {
- for (i = 0; i < samples; i++, mix++)
- *buffer++ = *mix * int_volume / 128;
- }
- /* external only */
- else
- {
- for (i = 0; i < samples; i++, ext++)
- *buffer++ = *ext * ext_volume / 128;
- }
+ // compute the modulated filter frequency and apply the filter
+ // modulation tracks the VCO triangle
+ double filter_freq = m_filter_frequency * (1 + m_filter_modulation * (triangle - 0.5));
+ result = filter(result, filter_freq);
+
+ // write the sample
+ buffer.put(sampindex, result * m_volume);
}
}
@@ -328,51 +269,49 @@ void cem3394_device::sound_stream_update_legacy(sound_stream &stream, stream_sam
void cem3394_device::device_start()
{
- /* copy global parameters */
- m_sample_rate = SAMPLE_RATE;
- m_inv_sample_rate = 1.0 / (double)m_sample_rate;
-
- /* allocate stream channels, 1 per chip */
- m_stream = stream_alloc_legacy(0, 1, m_sample_rate);
-
- m_ext_cb.resolve();
+ // compute a sample rate
+ // VCO can range up to pow(2, 4.0/.75) = ~40.3 * zero-voltage-freq (ZVF)
+ int sample_rate = m_vco_zero_freq * pow(2, 4.0 / 0.75) * 5;
+ m_inv_sample_rate = 1.0 / double(sample_rate);
- /* allocate memory for a mixer buffer and external buffer (1 second should do it!) */
- m_mixer_buffer = std::make_unique<int16_t[]>(m_sample_rate);
- m_external_buffer = std::make_unique<int16_t[]>(m_sample_rate);
+ // allocate stream channels, 1 per chip, with one external input
+ m_stream = stream_alloc(1, 1, sample_rate);
save_item(NAME(m_values));
save_item(NAME(m_wave_select));
+
save_item(NAME(m_volume));
save_item(NAME(m_mixer_internal));
save_item(NAME(m_mixer_external));
- save_item(NAME(m_position));
- save_item(NAME(m_step));
- save_item(NAME(m_filter_position));
- save_item(NAME(m_filter_step));
- save_item(NAME(m_modulation_depth));
- save_item(NAME(m_last_ext));
+
+ save_item(NAME(m_vco_position));
+ save_item(NAME(m_vco_step));
+
+ save_item(NAME(m_filter_frequency));
+ save_item(NAME(m_filter_modulation));
+ save_item(NAME(m_filter_resonance));
+
save_item(NAME(m_pulse_width));
}
double cem3394_device::compute_db(double voltage)
{
- /* assumes 0.0 == full off, 4.0 == full on, with linear taper, as described in the datasheet */
+ // assumes 0.0 == full off, 4.0 == full on, with linear taper, as described in the datasheet
- /* above 4.0, maximum volume */
+ // above 4.0, maximum volume
if (voltage >= 4.0)
return 0.0;
- /* below 0.0, minimum volume */
+ // below 0.0, minimum volume
else if (voltage <= 0.0)
return 90.0;
- /* between 2.5 and 4.0, linear from 20dB to 0dB */
+ // between 2.5 and 4.0, linear from 20dB to 0dB
else if (voltage >= 2.5)
return (4.0 - voltage) * (1.0 / 1.5) * 20.0;
- /* between 0.0 and 2.5, exponential to 20dB */
+ // between 0.0 and 2.5, exponential to 20dB
else
{
double temp = 20.0 * pow(2.0, 2.5 - voltage);
@@ -382,33 +321,33 @@ double cem3394_device::compute_db(double voltage)
}
-uint32_t cem3394_device::compute_db_volume(double voltage)
+stream_buffer::sample_t cem3394_device::compute_db_volume(double voltage)
{
double temp;
- /* assumes 0.0 == full off, 4.0 == full on, with linear taper, as described in the datasheet */
+ // assumes 0.0 == full off, 4.0 == full on, with linear taper, as described in the datasheet
- /* above 4.0, maximum volume */
+ // above 4.0, maximum volume
if (voltage >= 4.0)
- return 256;
+ return 1.0;
- /* below 0.0, minimum volume */
+ // below 0.0, minimum volume
else if (voltage <= 0.0)
return 0;
- /* between 2.5 and 4.0, linear from 20dB to 0dB */
+ // between 2.5 and 4.0, linear from 20dB to 0dB
else if (voltage >= 2.5)
temp = (4.0 - voltage) * (1.0 / 1.5) * 20.0;
- /* between 0.0 and 2.5, exponential to 20dB */
+ // between 0.0 and 2.5, exponential to 20dB
else
{
temp = 20.0 * pow(2.0, 2.5 - voltage);
if (temp < 50.0) return 0;
}
- /* convert from dB to volume and return */
- return (uint32_t)(256.0 * pow(0.891251, temp));
+ // convert from dB to volume and return
+ return powf(0.891251f, temp);
}
@@ -416,24 +355,24 @@ void cem3394_device::set_voltage(int input, double voltage)
{
double temp;
- /* don't do anything if no change */
+ // don't do anything if no change
if (voltage == m_values[input])
return;
m_values[input] = voltage;
- /* update the stream first */
+ // update the stream first
m_stream->update();
- /* switch off the input */
+ // switch off the input
switch (input)
{
- /* frequency varies from -4.0 to +4.0, at 0.75V/octave */
+ // frequency varies from -4.0 to +4.0, at 0.75V/octave
case VCO_FREQUENCY:
temp = m_vco_zero_freq * pow(2.0, -voltage * (1.0 / 0.75));
- m_step = (uint32_t)(temp * m_inv_sample_rate * FRACTION_ONE_D);
+ m_vco_step = temp * m_inv_sample_rate;
break;
- /* wave select determines triangle/sawtooth enable */
+ // wave select determines triangle/sawtooth enable
case WAVE_SELECT:
m_wave_select &= ~(WAVE_TRIANGLE | WAVE_SAWTOOTH);
if (voltage >= -0.5 && voltage <= -0.2)
@@ -444,7 +383,7 @@ void cem3394_device::set_voltage(int input, double voltage)
m_wave_select |= WAVE_SAWTOOTH;
break;
- /* pulse width determines duty cycle; 0.0 means 0%, 2.0 means 100% */
+ // pulse width determines duty cycle; 0.0 means 0%, 2.0 means 100%
case PULSE_WIDTH:
if (voltage < 0.0)
{
@@ -453,21 +392,20 @@ void cem3394_device::set_voltage(int input, double voltage)
}
else
{
- temp = voltage * 0.5;
+ m_pulse_width = voltage * 0.5;
if (LIMIT_WIDTH)
- temp = MINIMUM_WIDTH + (MAXIMUM_WIDTH - MINIMUM_WIDTH) * temp;
- m_pulse_width = (uint32_t)(temp * FRACTION_ONE_D);
+ m_pulse_width = MINIMUM_WIDTH + (MAXIMUM_WIDTH - MINIMUM_WIDTH) * m_pulse_width;
m_wave_select |= WAVE_PULSE;
}
break;
- /* final gain is pretty self-explanatory; 0.0 means ~90dB, 4.0 means 0dB */
+ // final gain is pretty self-explanatory; 0.0 means ~90dB, 4.0 means 0dB
case FINAL_GAIN:
m_volume = compute_db_volume(voltage);
break;
- /* mixer balance is a pan between the external input and the internal input */
- /* 0.0 is equal parts of both; positive values favor external, negative favor internal */
+ // mixer balance is a pan between the external input and the internal input
+ // 0.0 is equal parts of both; positive values favor external, negative favor internal
case MIXER_BALANCE:
if (voltage >= 0.0)
{
@@ -481,25 +419,29 @@ void cem3394_device::set_voltage(int input, double voltage)
}
break;
- /* filter frequency varies from -4.0 to +4.0, at 0.375V/octave */
+ // filter frequency varies from -3.0 to +4.0, at 0.375V/octave
case FILTER_FREQENCY:
- temp = m_filter_zero_freq * pow(2.0, -voltage * (1.0 / 0.375));
- m_filter_step = (uint32_t)(temp * m_inv_sample_rate * FRACTION_ONE_D);
+ m_filter_frequency = m_filter_zero_freq * pow(2.0, -voltage * (1.0 / 0.375));
break;
- /* modulation depth is 0.01 at 0V and 2.0 at 3.5V; how it grows from one to the other */
- /* is still unclear at this point */
+ // modulation depth is 0.01*freq at 0V and 2.0*freq at 3.5V
case MODULATION_AMOUNT:
if (voltage < 0.0)
- m_modulation_depth = (uint32_t)(0.01 * FRACTION_ONE_D);
+ m_filter_modulation = 0.01;
else if (voltage > 3.5)
- m_modulation_depth = (uint32_t)(2.00 * FRACTION_ONE_D);
+ m_filter_modulation = 1.99;
else
- m_modulation_depth = (uint32_t)(((voltage * (1.0 / 3.5)) * 1.99 + 0.01) * FRACTION_ONE_D);
+ m_filter_modulation = (voltage * (1.0 / 3.5)) * 1.98 + 0.01;
break;
- /* this is not yet implemented */
+ // this is not yet implemented
case FILTER_RESONANCE:
+ if (voltage < 0.0)
+ m_filter_resonance = 0.0;
+ else if (voltage > 2.5)
+ m_filter_resonance = 1.0;
+ else
+ m_filter_resonance = voltage * (1.0 / 2.5);
break;
}
}
@@ -535,9 +477,9 @@ double cem3394_device::get_parameter(int input)
if (voltage < 0.0)
return 0.01;
else if (voltage > 3.5)
- return 2.0;
+ return 1.99;
else
- return (voltage * (1.0 / 3.5)) * 1.99 + 0.01;
+ return (voltage * (1.0 / 3.5)) * 1.98 + 0.01;
case FILTER_RESONANCE:
if (voltage < 0.0)
diff --git a/src/devices/sound/cem3394.h b/src/devices/sound/cem3394.h
index 9dfaa4d356c..a3e5e3a85ea 100644
--- a/src/devices/sound/cem3394.h
+++ b/src/devices/sound/cem3394.h
@@ -5,14 +5,10 @@
#pragma once
-#define CEM3394_EXT_INPUT(_name) void _name(int count, short *buffer)
-
class cem3394_device : public device_t, public device_sound_interface
{
public:
- static constexpr unsigned SAMPLE_RATE = 44100*4;
-
// inputs
enum
{
@@ -26,11 +22,8 @@ public:
FINAL_GAIN
};
- typedef device_delegate<void (int count, short *buffer)> ext_input_delegate;
+ cem3394_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock);
- cem3394_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock);
-
- template <typename... T> void set_ext_input_callback(T &&... args) { m_ext_cb.set(std::forward<T>(args)...); }
void set_vco_zero_freq(double freq) { m_vco_zero_freq = freq; }
void set_filter_zero_freq(double freq) { m_filter_zero_freq = freq; }
@@ -39,7 +32,7 @@ protected:
virtual void device_start() override;
// sound stream update overrides
- virtual void sound_stream_update_legacy(sound_stream &stream, stream_sample_t const * const *inputs, stream_sample_t * const *outputs, int samples) override;
+ virtual void sound_stream_update(sound_stream &stream, std::vector<read_stream_view> const &inputs, std::vector<write_stream_view> &outputs) override;
public:
// Set the voltage going to a particular parameter
@@ -58,37 +51,34 @@ public:
private:
double compute_db(double voltage);
- uint32_t compute_db_volume(double voltage);
+ stream_buffer::sample_t compute_db_volume(double voltage);
private:
- ext_input_delegate m_ext_cb; /* callback to generate external samples */
-
- sound_stream *m_stream; /* our stream */
- double m_vco_zero_freq; /* frequency of VCO at 0.0V */
- double m_filter_zero_freq; /* frequency of filter at 0.0V */
+ double filter(double input, double cutoff);
- double m_values[8]; /* raw values of registers */
- uint8_t m_wave_select; /* flags which waveforms are enabled */
+ sound_stream *m_stream; // our stream
+ double m_vco_zero_freq; // frequency of VCO at 0.0V
+ double m_filter_zero_freq; // frequency of filter at 0.0V
- uint32_t m_volume; /* linear overall volume (0-256) */
- uint32_t m_mixer_internal; /* linear internal volume (0-256) */
- uint32_t m_mixer_external; /* linear external volume (0-256) */
+ double m_values[8]; // raw values of registers
+ u8 m_wave_select; // flags which waveforms are enabled
- uint32_t m_position; /* current VCO frequency position (0.FRACTION_BITS) */
- uint32_t m_step; /* per-sample VCO step (0.FRACTION_BITS) */
+ double m_volume; // linear overall volume (0-1)
+ double m_mixer_internal; // linear internal volume (0-1)
+ double m_mixer_external; // linear external volume (0-1)
- uint32_t m_filter_position; /* current filter frequency position (0.FRACTION_BITS) */
- uint32_t m_filter_step; /* per-sample filter step (0.FRACTION_BITS) */
- uint32_t m_modulation_depth; /* fraction of total by which we modulate (0.FRACTION_BITS) */
- int16_t m_last_ext; /* last external sample we read */
+ double m_vco_position; // current VCO frequency position (always < 1.0)
+ double m_vco_step; // per-sample VCO step
- uint32_t m_pulse_width; /* fractional pulse width (0.FRACTION_BITS) */
+ double m_filter_frequency; // baseline filter frequency
+ double m_filter_modulation; // depth of modulation (up to 1.0)
+ double m_filter_resonance; // depth of modulation (up to 1.0)
+ double m_filter_in[4]; // filter input history
+ double m_filter_out[4]; // filter output history
- double m_inv_sample_rate;
- int m_sample_rate;
+ double m_pulse_width; // fractional pulse width
- std::unique_ptr<int16_t[]> m_mixer_buffer;
- std::unique_ptr<int16_t[]> m_external_buffer;
+ double m_inv_sample_rate; // 1/current sample rate
};
DECLARE_DEVICE_TYPE(CEM3394, cem3394_device)
diff --git a/src/mame/audio/sente6vb.cpp b/src/mame/audio/sente6vb.cpp
index f4232038c9b..22c9721a18a 100644
--- a/src/mame/audio/sente6vb.cpp
+++ b/src/mame/audio/sente6vb.cpp
@@ -65,6 +65,55 @@
DEFINE_DEVICE_TYPE(SENTE6VB, sente6vb_device, "sente6vb", "Bally Sente 6VB Audio Board")
+
+/*************************************
+*
+* Trivial sound device to spew
+* a MM5837 noise stream to CEM3394
+* inputs
+*
+*************************************/
+
+DECLARE_DEVICE_TYPE(MM5837_NOISE_SOURCE, mm5837_noise_source)
+
+class mm5837_noise_source : public device_t, public device_sound_interface
+{
+public:
+ mm5837_noise_source(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) :
+ device_t(mconfig, MM5837_NOISE_SOURCE, tag, owner, clock),
+ device_sound_interface(mconfig, *this),
+ m_stream(nullptr),
+ m_noise_state(0x1ffff)
+ {
+ }
+
+protected:
+ // device-level overrides
+ virtual void device_start() override
+ {
+ m_stream = stream_alloc(0, 1, clock());
+ save_item(NAME(m_noise_state));
+ }
+
+ // 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
+ {
+ for (int sampindex = 0; sampindex < outputs[0].samples(); sampindex++)
+ {
+ outputs[0].put(sampindex, BIT(m_noise_state, 0) ? 1.0 : 0.0);
+ m_noise_state = (m_noise_state >> 1) | ((BIT(m_noise_state, 0) ^ BIT(m_noise_state, 3)) << 16);
+ }
+ }
+
+private:
+ sound_stream *m_stream; // sound stream
+ u32 m_noise_state; // noise state
+};
+
+DEFINE_DEVICE_TYPE(MM5837_NOISE_SOURCE, mm5837_noise_source, "mm5837noise", "MM5837")
+
+
+
/*************************************
*
* Sound CPU memory handlers
@@ -123,20 +172,16 @@ void sente6vb_device::device_add_mconfig(machine_config &config)
SPEAKER(config, "mono").front_center();
+ mm5837_noise_source &noise(MM5837_NOISE_SOURCE(config, "noise", 100000));
+
for (auto &cem_device : m_cem_device)
{
CEM3394(config, cem_device, 0);
cem_device->set_vco_zero_freq(431.894);
cem_device->set_filter_zero_freq(1300.0);
cem_device->add_route(ALL_OUTPUTS, "mono", 0.90);
+ noise.add_route(0, *cem_device, 1.0);
}
-
- m_cem_device[0]->set_ext_input_callback(FUNC(sente6vb_device::noise_gen_0));
- m_cem_device[1]->set_ext_input_callback(FUNC(sente6vb_device::noise_gen_1));
- m_cem_device[2]->set_ext_input_callback(FUNC(sente6vb_device::noise_gen_2));
- m_cem_device[3]->set_ext_input_callback(FUNC(sente6vb_device::noise_gen_3));
- m_cem_device[4]->set_ext_input_callback(FUNC(sente6vb_device::noise_gen_4));
- m_cem_device[5]->set_ext_input_callback(FUNC(sente6vb_device::noise_gen_5));
}
@@ -164,24 +209,21 @@ const tiny_rom_entry *sente6vb_device::device_rom_region() const
*
*************************************/
-sente6vb_device::sente6vb_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
- : device_t(mconfig, SENTE6VB, tag, owner, clock)
- , m_pit(*this, "pit")
- , m_counter_0_timer(*this, "8253_0_timer")
- , m_cem_device(*this, "cem%u", 1U)
- , m_audiocpu(*this, "audiocpu")
- , m_uart(*this, "uart")
- , m_send_cb(*this)
- , m_clock_out_cb(*this)
+sente6vb_device::sente6vb_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) :
+ device_t(mconfig, SENTE6VB, tag, owner, clock),
+ m_pit(*this, "pit"),
+ m_counter_0_timer(*this, "8253_0_timer"),
+ m_cem_device(*this, "cem%u", 1U),
+ m_audiocpu(*this, "audiocpu"),
+ m_uart(*this, "uart"),
+ m_send_cb(*this),
+ m_clock_out_cb(*this)
{
}
void sente6vb_device::device_start()
{
- // create the polynomial tables
- poly17_init();
-
m_send_cb.resolve_safe();
m_clock_out_cb.resolve_safe();
m_uart->write_cts(0);
@@ -197,8 +239,6 @@ void sente6vb_device::device_start()
save_item(NAME(m_chip_select));
save_item(NAME(m_uint));
-
- save_item(NAME(m_noise_position));
}
@@ -215,64 +255,8 @@ void sente6vb_device::device_reset()
m_dac_value = 0;
m_dac_register = 0;
m_chip_select = 0x3f;
-
- // reset the noise generator
- memset(m_noise_position, 0, sizeof(m_noise_position));
-}
-
-
-
-/*************************************
- *
- * MM5837 noise generator
- *
- * NOTE: this is stolen straight from
- * POKEY.c
- *
- *************************************/
-
-void sente6vb_device::poly17_init()
-{
- uint32_t i, x = 0;
- uint8_t *p;
-
- // allocate memory
- p = m_poly17;
-
- // generate the polynomial
- for (i = 0; i < POLY17_SIZE; i++)
- {
- // store new values
- *p++ = x & 1;
-
- // calculate next bit
- x = ((x << POLY17_SHL) + (x >> POLY17_SHR) + POLY17_ADD) & POLY17_SIZE;
- }
-}
-
-
-inline void sente6vb_device::noise_gen_chip(int chip, int count, short *buffer)
-{
- // noise generator runs at 100kHz
- uint32_t step = (100000 << 14) / cem3394_device::SAMPLE_RATE;
- uint32_t noise_counter = m_noise_position[chip];
-
- while (count--)
- {
- *buffer++ = m_poly17[(noise_counter >> 14) & POLY17_SIZE] << 12;
- noise_counter += step;
- }
-
- // remember the noise position
- m_noise_position[chip] = noise_counter;
}
-CEM3394_EXT_INPUT(sente6vb_device::noise_gen_0) { noise_gen_chip(0, count, buffer); }
-CEM3394_EXT_INPUT(sente6vb_device::noise_gen_1) { noise_gen_chip(1, count, buffer); }
-CEM3394_EXT_INPUT(sente6vb_device::noise_gen_2) { noise_gen_chip(2, count, buffer); }
-CEM3394_EXT_INPUT(sente6vb_device::noise_gen_3) { noise_gen_chip(3, count, buffer); }
-CEM3394_EXT_INPUT(sente6vb_device::noise_gen_4) { noise_gen_chip(4, count, buffer); }
-CEM3394_EXT_INPUT(sente6vb_device::noise_gen_5) { noise_gen_chip(5, count, buffer); }
/*************************************
diff --git a/src/mame/audio/sente6vb.h b/src/mame/audio/sente6vb.h
index 9780703a1fd..ee199817360 100644
--- a/src/mame/audio/sente6vb.h
+++ b/src/mame/audio/sente6vb.h
@@ -50,15 +50,7 @@ private:
void update_counter_0_timer();
TIMER_DEVICE_CALLBACK_MEMBER(clock_counter_0_ff);
- void poly17_init();
DECLARE_WRITE_LINE_MEMBER(set_counter_0_ff);
- inline void noise_gen_chip(int chip, int count, short *buffer);
- CEM3394_EXT_INPUT(noise_gen_0);
- CEM3394_EXT_INPUT(noise_gen_1);
- CEM3394_EXT_INPUT(noise_gen_2);
- CEM3394_EXT_INPUT(noise_gen_3);
- CEM3394_EXT_INPUT(noise_gen_4);
- CEM3394_EXT_INPUT(noise_gen_5);
void mem_map(address_map &map);
void io_map(address_map &map);
@@ -78,9 +70,6 @@ private:
bool m_counter_0_out;
bool m_counter_0_timer_active;
- // random number generator states
- uint8_t m_poly17[POLY17_SIZE + 1];
-
// CEM3394 DAC control states
uint16_t m_dac_value;
uint8_t m_dac_register;
@@ -88,9 +77,6 @@ private:
// sound CPU 6850 states
bool m_uint;
-
- // noise generator states
- uint32_t m_noise_position[6];
};
DECLARE_DEVICE_TYPE(SENTE6VB, sente6vb_device)