diff options
author | 2020-09-15 15:14:15 -0700 | |
---|---|---|
committer | 2020-09-15 15:17:06 -0700 | |
commit | ea60f78b23d94cd9c57f3de619f51e57503cddc1 (patch) | |
tree | 8d5cc779e0c11add74292ca7573e9dbcde4fabaf | |
parent | 87dce66d84f1fb33f8f09b6ef1b5c86dcc5db8af (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.cpp | 444 | ||||
-rw-r--r-- | src/devices/sound/cem3394.h | 52 | ||||
-rw-r--r-- | src/mame/audio/sente6vb.cpp | 138 | ||||
-rw-r--r-- | src/mame/audio/sente6vb.h | 14 |
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) |