From f96914785fef7181ec967be7e9a4b50c604e0469 Mon Sep 17 00:00:00 2001 From: Olivier Galibert Date: Sun, 6 Oct 2024 19:43:53 +0200 Subject: cmi01a: Add filters --- src/mame/fairlight/cmi01a.cpp | 99 ++++++++++++++++++++++++++++++++++++++++--- src/mame/fairlight/cmi01a.h | 15 +++++++ 2 files changed, 109 insertions(+), 5 deletions(-) diff --git a/src/mame/fairlight/cmi01a.cpp b/src/mame/fairlight/cmi01a.cpp index aaaa7bca016..d03ab9a23cd 100644 --- a/src/mame/fairlight/cmi01a.cpp +++ b/src/mame/fairlight/cmi01a.cpp @@ -135,6 +135,20 @@ void cmi01a_device::device_start() save_item(NAME(m_rp)); save_item(NAME(m_ws)); save_item(NAME(m_dir)); + + save_item(NAME(m_ha0)); + save_item(NAME(m_ha1)); + save_item(NAME(m_hb0)); + save_item(NAME(m_hb1)); + save_item(NAME(m_hc0)); + save_item(NAME(m_hc1)); + + save_item(NAME(m_ka0)); + save_item(NAME(m_ka1)); + save_item(NAME(m_ka2)); + save_item(NAME(m_kb0)); + save_item(NAME(m_kb1)); + save_item(NAME(m_kb2)); } void cmi01a_device::device_reset() @@ -175,10 +189,26 @@ void cmi01a_device::device_reset() m_pitch = 0; m_octave = 0; + m_ha0 = 0; + m_ha1 = 0; + m_hb0 = 0; + m_hb1 = 0; + m_hc0 = 0; + m_hc1 = 0; + + m_ka0 = 1; + m_ka1 = 0; + m_ka2 = 0; + m_kb0 = 1; + m_kb1 = 0; + m_kb2 = 0; + m_bcas_q1_timer->adjust(attotime::from_hz(clock() / 2), 0, attotime::from_hz(clock() / 2)); m_zcint_pulse_timer->adjust(attotime::never); m_rstb_pulse_timer->adjust(attotime::never); m_sample_timer->adjust(attotime::never); + + update_filters(); } void cmi01a_device::sound_stream_update(sound_stream &stream, std::vector const &inputs, std::vector &outputs) @@ -189,15 +219,26 @@ void cmi01a_device::sound_stream_update(sound_stream &stream, std::vector> 8); - buf.put_int(sampindex, sample16, 32768); + double sample = s8(m_current_sample ^ 0x80); // -128..127 + double hbn = (sample + 2*m_ha0 + m_ha1 - m_ka1 * m_hb0 - m_ka2 * m_hb1) / m_ka0; + double hcn = (hbn + 2*m_hb0 + m_hb1 - m_kb1 * m_hc0 - m_kb2 * m_hc1) / m_kb0; // -128..127 + m_ha1 = m_ha0; + m_ha0 = sample; + m_hb1 = m_hb0; + m_hb0 = hbn; + m_hc1 = m_hc0; + m_hc0 = hcn; + + double env = hbn * m_env; // -32768..32767 + double vol = env * m_vol_latch; // -8388608..8388607 + buf.put(sampindex, vol / 8388608); } } else { + m_ha0 = m_ha1 = 0; + m_hb0 = m_hb1 = 0; + m_hc0 = m_hc1 = 0; outputs[0].fill(0); } } @@ -224,6 +265,7 @@ void cmi01a_device::pitch_octave_w(u8 data) m_pitch &= 0x0ff; m_pitch |= (data & 3) << 8; m_octave = (data >> 2) & 0x0f; + update_filters(); } u8 cmi01a_device::pitch_octave_r() @@ -600,6 +642,52 @@ void cmi01a_device::not_wpe_w(int state) update_upper_wave_addr_load(); } +void cmi01a_device::update_filters() +{ + // Filter ADC input level + int fval = (m_octave << 5) + m_flt_latch; + + // Calibrated using the graph page 133 of the CMI Mainframe Service Manual + double fc = 6410 * pow(1.02162, fval - 256); + // -6dB cutoff frequency + double f0 = fc * 0.916; + + // Secondary filter around there for when the first filter is high + if(fc > 14000) + fc = 14000; + + logerror("Filter latch = %02x, octave=%x, fval=%03x, f0 = %g\n", m_flt_latch, m_octave, fval, f0); + + double w1 = 2*M_PI*fc; + double w2 = 2*M_PI*fc*1.22474487139159; // sqrt(c1*c2 / (c3*c4)), the ratio between the two cutoff frequencies in the cmi01 configuration of the SSM2045 + double a1 = 1.81659021245849; // sqrt(c1*10*c2)/c1 + double a2 = 1.48323969741913; // sqrt(c3*10*c4)/c3 + + // Two stages of order-2 lowpass filters with fixed Q + + // H(s) = 1/(m0 * s**2 + m1 * s + 1) + + double ma0 = a1/w1; + double ma1 = 1/(w1*w1); + double mb0 = a2/w2; + double mb1 = 1/(w2*w2); + + // Convert to z, wrap around f0 + double zc = 2*M_PI*f0/tan(M_PI*f0/48000); + double za0 = ma1 * zc*zc; + double za1 = ma0 * zc; + double zb0 = mb1 * zc*zc; + double zb1 = mb0 * zc; + + // H(z) = (1 + 2 * z-1 + z-2) / (k0 + k1 * z-1 + k2 * z-2) + m_ka0 = za0 + za1 + 1; + m_ka1 = -2*za0 + 2; + m_ka2 = za0 - za1 + 1; + m_kb0 = zb0 + zb1 + 1; + m_kb1 = -2*zb0 + 2; + m_kb2 = zb0 - zb1 + 1; +} + inline void cmi01a_device::update_upper_wave_addr_load() { const bool c10_and_out = (!m_not_wpe && m_gzx); @@ -725,6 +813,7 @@ void cmi01a_device::write(offs_t offset, u8 data) case 0x6: m_flt_latch = data; + update_filters(); break; case 0x8: case 0x9: case 0xa: case 0xb: diff --git a/src/mame/fairlight/cmi01a.h b/src/mame/fairlight/cmi01a.h index b7305b574f8..0531b3106d8 100644 --- a/src/mame/fairlight/cmi01a.h +++ b/src/mame/fairlight/cmi01a.h @@ -117,6 +117,7 @@ private: void set_wave_addr_lsb(const u8 wave_addr_lsb); void set_wave_addr_msb(const u8 wave_addr_msb); void set_wave_addr_msb_clock(const bool wave_addr_msb_clock); + void update_filters(); void set_zx(const bool zx); void update_ptm_c1(); @@ -182,6 +183,20 @@ private: u8 m_rp = 0; u8 m_ws = 0; int m_dir = 0; + + double m_ha0 = 0; + double m_ha1 = 0; + double m_hb0 = 0; + double m_hb1 = 0; + double m_hc0 = 0; + double m_hc1 = 0; + + double m_ka0 = 0; + double m_ka1 = 0; + double m_ka2 = 0; + double m_kb0 = 0; + double m_kb1 = 0; + double m_kb2 = 0; }; // device type definition -- cgit v1.2.3