// license:BSD-3-Clause // copyright-holders:Olivier Galibert /*************************************************************************** votrax.h Votrax SC01A simulation ***************************************************************************/ #ifndef MAME_SOUND_VOTRAX_H #define MAME_SOUND_VOTRAX_H #pragma once #define MCFG_VOTRAX_SC01_REQUEST_CB(_devcb) \ downcast(device)->set_ar_callback(DEVCB_##_devcb); class votrax_sc01_device : public device_t, public device_sound_interface { public: static constexpr feature_type imperfect_features() { return feature::SOUND; } // construction/destruction votrax_sc01_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock); template devcb_base &set_ar_callback(Object &&cb) { return m_ar_cb.set_callback(std::forward(cb)); } DECLARE_WRITE8_MEMBER(write); DECLARE_WRITE8_MEMBER(inflection_w); DECLARE_READ_LINE_MEMBER(request) { m_stream->update(); return m_ar_state; } protected: // device-level overrides virtual const tiny_rom_entry *device_rom_region() const override; virtual void device_start() override; virtual void device_reset() override; virtual void device_clock_changed() override; virtual void device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr) override; // device_sound_interface overrides virtual void sound_stream_update(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples) override; private: // Possible timer parameters enum { T_COMMIT_PHONE, T_END_OF_PHONE }; static const char *const s_phone_table[64]; static const double s_glottal_wave[9]; sound_stream *m_stream; // Output stream emu_timer *m_timer; // General timer required_memory_region m_rom; // Internal ROM u32 m_mainclock; // Current main clock double m_sclock; // Stream sample clock (40KHz, main/18) double m_cclock; // 20KHz capacitor switching clock (main/36) u32 m_sample_count; // Sample counter, to cadence chip updates // Inputs u8 m_inflection; // 2-bit inflection value u8 m_phone; // 6-bit phone value // Outputs devcb_write_line m_ar_cb; // Callback for ar bool m_ar_state; // Current ar state // "Unpacked" current rom values u8 m_rom_duration; // Duration in 5KHz units (main/144) of one tick, 16 ticks per phone, 7 bits u8 m_rom_vd, m_rom_cld; // Duration in ticks of the "voice" and "closure" delays, 4 bits u8 m_rom_fa, m_rom_fc, m_rom_va; // Analog parameters, noise volume, noise freq cutoff and voice volume, 4 bits each u8 m_rom_f1, m_rom_f2, m_rom_f2q, m_rom_f3; // Analog parameters, formant frequencies and Q, 4 bits each bool m_rom_closure; // Closure bit, true = silence at cld bool m_rom_pause; // Pause bit // Current interpolated values (8 bits each) u8 m_cur_fa, m_cur_fc, m_cur_va; u8 m_cur_f1, m_cur_f2, m_cur_f2q, m_cur_f3; // Current committed values u8 m_filt_fa, m_filt_fc, m_filt_va; // Analog parameters, noise volume, noise freq cutoff and voice volume, 4 bits each u8 m_filt_f1, m_filt_f2, m_filt_f2q, m_filt_f3; // Analog parameters, formant frequencies/Q on 4 bits except f2 on 5 bits // Internal counters u16 m_phonetick; // 9-bits phone tick duration counter u8 m_ticks; // 5-bits tick counter u8 m_pitch; // 7-bits pitch counter u8 m_closure; // 5-bits glottal closure counter u8 m_update_counter; // 6-bits counter for the 625Hz (main/1152) and 208Hz (main/3456) update timing generators // Internal state bool m_cur_closure; // Current internal closure state u16 m_noise; // 15-bit noise shift register bool m_cur_noise; // Current noise output // Filter coefficients and level histories double m_voice_1[4]; double m_voice_2[4]; double m_voice_3[4]; double m_noise_1[3]; double m_noise_2[3]; double m_noise_3[2]; double m_noise_4[2]; double m_vn_1[4]; double m_vn_2[4]; double m_vn_3[4]; double m_vn_4[4]; double m_vn_5[2]; double m_vn_6[2]; double m_f1_a[4], m_f1_b[4]; // F1 filtering double m_f2v_a[4], m_f2v_b[4]; // F2 voice filtering double m_f2n_a[2], m_f2n_b[2]; // F2 noise filtering double m_f3_a[4], m_f3_b[4]; // F3 filtering double m_f4_a[4], m_f4_b[4]; // F4 filtering double m_fx_a[1], m_fx_b[2]; // Final filtering double m_fn_a[3], m_fn_b[3]; // Noise shaping // Compute a total capacitor value based on which bits are currently active static double bits_to_caps(u32 value, std::initializer_list caps_values) { double total = 0; for(double d : caps_values) { if(value & 1) total += d; value >>= 1; } return total; } // Shift a history of values by one and insert the new value at the front template static void shift_hist(double val, double (&hist_array)[N]) { for(u32 i=N-1; i>0; i--) hist_array[i] = hist_array[i-1]; hist_array[0] = val; } // Apply a filter and compute the result. 'a' is applied to x (inputs) and 'b' to y (outputs) template static double apply_filter(const double (&x)[Nx], const double (&y)[Ny], const double (&a)[Na], const double (&b)[Nb]) { double total = 0; for(u32 i=0; i