diff options
author | cam900 <dbtlrchl@naver.com> | 2021-02-23 21:12:22 +0900 |
---|---|---|
committer | GitHub <noreply@github.com> | 2021-02-23 23:12:22 +1100 |
commit | 0a2caa105dafac435294f9f33fa56c35c299d004 (patch) | |
tree | efae02f1dfabf0dc03183443b934387bc35dd917 /src/devices | |
parent | e6caf6fb9c697bc2b4c95d67111de54e7029eb8d (diff) |
Refactored Seta sound, adding preliminary ST0032 sound support. (#7800)
* Renamed sound/nile.cpp to sound/setapcm.cpp.
* Added preliminary support for 16-voice ST0032 variant.
* jclub2.cpp: Hooked up ST0032 sound.
* jclub2.cpp, srmp6.cpp: Derive sound clocks from crystal frequencies.
Diffstat (limited to 'src/devices')
-rw-r--r-- | src/devices/sound/nile.cpp | 233 | ||||
-rw-r--r-- | src/devices/sound/nile.h | 48 | ||||
-rw-r--r-- | src/devices/sound/setapcm.cpp | 429 | ||||
-rw-r--r-- | src/devices/sound/setapcm.h | 103 |
4 files changed, 532 insertions, 281 deletions
diff --git a/src/devices/sound/nile.cpp b/src/devices/sound/nile.cpp deleted file mode 100644 index ef05b670e57..00000000000 --- a/src/devices/sound/nile.cpp +++ /dev/null @@ -1,233 +0,0 @@ -// license:BSD-3-Clause -// copyright-holders:Tomasz Slanina -/************************************ - Seta custom Nile ST-0026 chip - sound emulation by Tomasz Slanina - based on ST-0016 emulation - -8 voices, 16 words of config data for each: - - 00 - 01 - sptr ?? (always 0) - 02 - sptr LO - 03 - sptr HI - 04 - 05 - flags? 00000000 0000?L0? - bit 0 loops, other bits appear to be not used by the chip - 06 - freq - 07 - lsptr LO - 08 - 09 - lsptr HI - 0a - leptr LO - 0b - leptr HI - 0c - eptr LO - 0d - eptr HI - 0e - vol R - 0f - vol L - -************************************/ - -#include "emu.h" -#include "nile.h" - -enum -{ - NILE_REG_UNK0=0, - NILE_REG_SPTR_TOP, - NILE_REG_SPTR_LO, - NILE_REG_SPTR_HI, - NILE_REG_UNK_4, - NILE_REG_FLAGS, - NILE_REG_FREQ, - NILE_REG_LSPTR_LO, - MILE_REG_UNK_8, - NILE_REG_LSPTR_HI, - NILE_REG_LEPTR_LO, - NILE_REG_LEPTR_HI, - NILE_REG_EPTR_LO, - NILE_REG_EPTR_HI, - NILE_REG_VOL_R, - NILE_REG_VOL_L -}; - - -DEFINE_DEVICE_TYPE(NILE, nile_device, "nile", "Seta ST-0026 NiLe") - -nile_device::nile_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) - : device_t(mconfig, NILE, tag, owner, clock), - device_sound_interface(mconfig, *this), - m_stream(nullptr), - m_sound_ram(*this, DEVICE_SELF), - m_ctrl(0) -{ -} - - -//------------------------------------------------- -// device_start - device-specific startup -//------------------------------------------------- - -void nile_device::device_start() -{ - m_stream = stream_alloc(0, 2, 44100); - save_item(NAME(m_sound_regs)); - save_item(NAME(m_vpos)); - save_item(NAME(m_frac)); - save_item(NAME(m_lponce)); - save_item(NAME(m_ctrl)); -} - - -//------------------------------------------------- -// sound_stream_update - handle update requests -// for our sound stream -//------------------------------------------------- - -void nile_device::sound_stream_update(sound_stream &stream, std::vector<read_stream_view> const &inputs, std::vector<write_stream_view> &outputs) -{ - uint8_t *sound_ram = &m_sound_ram[0]; - int v, i, snum; - uint16_t *slot; - int32_t mix[4800*2]; - int32_t *mixp; - int16_t sample; - int sptr, eptr, freq, lsptr, leptr; - - lsptr=leptr=0; - - sound_assert(outputs[0].samples() * 2 < std::size(mix)); - std::fill_n(&mix[0], outputs[0].samples()*2, 0); - - for (v = 0; v < NILE_VOICES; v++) - { - slot = &m_sound_regs[v * 16]; - - if (m_ctrl&(1<<v)) - { - mixp = &mix[0]; - - sptr = slot[NILE_REG_SPTR_HI]<<16 | slot[NILE_REG_SPTR_LO]; - eptr = slot[NILE_REG_EPTR_HI]<<16 | slot[NILE_REG_EPTR_LO]; - - freq=slot[NILE_REG_FREQ]*14; - lsptr = slot[NILE_REG_LSPTR_HI]<<16 | slot[NILE_REG_LSPTR_LO]; - leptr = slot[NILE_REG_LEPTR_HI]<<16 | slot[NILE_REG_LEPTR_LO]; - - for (snum = 0; snum < outputs[0].samples(); snum++) - { - sample = sound_ram[sptr + m_vpos[v]]<<8; - - *mixp++ += (sample * (int32_t)slot[NILE_REG_VOL_R]) >> 16; - *mixp++ += (sample * (int32_t)slot[NILE_REG_VOL_L]) >> 16; - - m_frac[v] += freq; - m_vpos[v] += m_frac[v]>>16; - m_frac[v] &= 0xffff; - - // stop if we're at the end - if (m_lponce[v]) - { - // we've looped once, check loop end rather than sample end - if ((m_vpos[v] + sptr) >= leptr) - { - m_vpos[v] = (lsptr - sptr); - } - } - else - { - // not looped yet, check sample end - if ((m_vpos[v] + sptr) >= eptr) - { - // code at 11d8c: - // if bit 2 (0x4) is set, check if loop start = loop end. - // if they are equal, clear bit 0 and don't set the loop start/end - // registers in the NiLe. if they aren't, set bit 0 and set - // the loop start/end registers in the NiLe. - if ((slot[NILE_REG_FLAGS] & 0x5) == 0x5) - { - m_vpos[v] = (lsptr - sptr); - m_lponce[v] = 1; - } - else - { - m_ctrl &= ~(1<<v); - m_vpos[v] = (eptr - sptr); - m_frac[v] = 0; - } - - } - } - } - } - } - mixp = &mix[0]; - for (i = 0; i < outputs[0].samples(); i++) - { - outputs[0].put_int(i, *mixp++, 32768 * 16); - outputs[1].put_int(i, *mixp++, 32768 * 16); - } -} - - -void nile_device::nile_sndctrl_w(offs_t offset, uint16_t data, uint16_t mem_mask) -{ - uint16_t ctrl=m_ctrl; - - m_stream->update(); - - COMBINE_DATA(&m_ctrl); - -// logerror("CTRL: %04x -> %04x %s\n", ctrl, m_ctrl, machine().describe_context()); - - ctrl^=m_ctrl; -} - - -uint16_t nile_device::nile_sndctrl_r() -{ - m_stream->update(); - return m_ctrl; -} - - -uint16_t nile_device::nile_snd_r(offs_t offset) -{ - int reg=offset&0xf; - - m_stream->update(); - - if(reg==2 || reg==3) - { - int slot=offset/16; - int sptr = ((m_sound_regs[slot*16+3]<<16)|m_sound_regs[slot*16+2])+m_vpos[slot]; - - if(reg==2) - { - return sptr&0xffff; - } - else - { - return sptr>>16; - } - } - return m_sound_regs[offset]; -} - - -void nile_device::nile_snd_w(offs_t offset, uint16_t data, uint16_t mem_mask) -{ - int v, r; - - m_stream->update(); - - COMBINE_DATA(&m_sound_regs[offset]); - - v = offset / 16; - r = offset % 16; - - if ((r == 2) || (r == 3)) - { - m_vpos[v] = m_frac[v] = m_lponce[v] = 0; - } - - //logerror("v%02d: %04x to reg %02d (PC=%x)\n", v, m_sound_regs[offset], r, machine().describe_context()); -} diff --git a/src/devices/sound/nile.h b/src/devices/sound/nile.h deleted file mode 100644 index 61add38d44e..00000000000 --- a/src/devices/sound/nile.h +++ /dev/null @@ -1,48 +0,0 @@ -// license:BSD-3-Clause -// copyright-holders:Tomasz Slanina -#ifndef MAME_SOUND_NILE_H -#define MAME_SOUND_NILE_H - -#pragma once - - -//************************************************************************** -// TYPE DEFINITIONS -//************************************************************************** - -// ======================> nile_device - -class nile_device : public device_t, - public device_sound_interface -{ -public: - nile_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock); - -protected: - // device-level overrides - virtual void device_start() override; - - // 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; - -public: - void nile_snd_w(offs_t offset, uint16_t data, uint16_t mem_mask = ~0); - uint16_t nile_snd_r(offs_t offset); - void nile_sndctrl_w(offs_t offset, uint16_t data, uint16_t mem_mask = ~0); - uint16_t nile_sndctrl_r(); - -private: - static constexpr unsigned NILE_VOICES = 8; - - sound_stream *m_stream; - required_region_ptr<uint8_t> m_sound_ram; - uint16_t m_sound_regs[0x80]; - int m_vpos[NILE_VOICES]; - int m_frac[NILE_VOICES]; - int m_lponce[NILE_VOICES]; - uint16_t m_ctrl; -}; - -DECLARE_DEVICE_TYPE(NILE, nile_device) - -#endif // MAME_SOUND_NILE_H diff --git a/src/devices/sound/setapcm.cpp b/src/devices/sound/setapcm.cpp new file mode 100644 index 00000000000..a97e0ded106 --- /dev/null +++ b/src/devices/sound/setapcm.cpp @@ -0,0 +1,429 @@ +// license:BSD-3-Clause +// copyright-holders:Tomasz Slanina,cam900 +/************************************ + Seta PCM emulation + sound emulation by Tomasz Slanina + based on ST-0016 emulation + + used by + - ST-0026 NiLe (srmp6, 8 voices) + - ST-0032 (jclub2, 16 voices) + - Capcom CPS3 sound hardware has similarity? + + Register Format (32 byte per voices) + + 00-1f: Voice 0 + Offset Bit Description + fedcba98 76543210 + 04 xxxxxxxx xxxxxxxx Start position LSB + 06 xxxxxxxx xxxxxxxx Start position MSB + 0a -------- ----xxx- Used but unknown + -------- -----x-- See below for NiLe specific? notes + -------- -------x Loop enable + 0c xxxxxxxx xxxxxxxx Frequency + 0e xxxxxxxx xxxxxxxx Loop Start position LSB + 12 xxxxxxxx xxxxxxxx Loop Start position MSB + 14 xxxxxxxx xxxxxxxx Loop End position LSB + 16 xxxxxxxx xxxxxxxx Loop End position MSB + 18 xxxxxxxx xxxxxxxx End position LSB + 1a xxxxxxxx xxxxxxxx End position MSB + 1c xxxxxxxx xxxxxxxx Right Volume + 1e xxxxxxxx xxxxxxxx Left Volume + + 20-3f: Voice 1 + ... + e0-ff: Voice 7 + + 100: Keyon/off, Bit 0-7 means Voice 0-7 + 110: Used but unknown + + below for 16 voice configurations: + 100-11f: Voice 8 + 120-13f: Voice 9 + ... + 1e0-1ff: Voice 15 + + 200: Keyon/off, Bit 0-15 means Voice 0-15 + 210: Used but unknown + + Other registers are unknown/unused + + TODO: + - Verify loop and flag bit behavior from real hardware + +************************************/ + +#include "emu.h" +#include "setapcm.h" + +// constants +template<unsigned MaxVoices, unsigned Divider> +constexpr unsigned setapcm_device<MaxVoices, Divider>::MAX_VOICES; +template<unsigned MaxVoices, unsigned Divider> +constexpr unsigned setapcm_device<MaxVoices, Divider>::CLOCK_DIVIDER; + +// device type definition +DEFINE_DEVICE_TYPE(NILE_SOUND, nile_sound_device, "nile_sound", "Seta ST-0026 NiLe (Sound)") +DEFINE_DEVICE_TYPE(ST0032_SOUND, st0032_sound_device, "st0032_sound", "Seta ST-0032 (Sound)") + +//************************************************************************** +// LIVE DEVICE +//************************************************************************** + +//------------------------------------------------- +// setapcm_device - constructor +//------------------------------------------------- + +template<unsigned MaxVoices, unsigned Divider> +setapcm_device<MaxVoices, Divider>::setapcm_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, u32 clock) + : device_t(mconfig, type, tag, owner, clock) + , device_sound_interface(mconfig, *this) + , device_rom_interface(mconfig, *this) + , m_stream(nullptr) + , m_keyctrl(0) +{ +} + +nile_sound_device::nile_sound_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock) + : setapcm_device<8, 160>(mconfig, NILE_SOUND, tag, owner, clock) +{ +} + +st0032_sound_device::st0032_sound_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock) + : setapcm_device<16, 384>(mconfig, ST0032_SOUND, tag, owner, clock) +{ +} + +//------------------------------------------------- +// device_start - device-specific startup +//------------------------------------------------- + +template<unsigned MaxVoices, unsigned Divider> +void setapcm_device<MaxVoices, Divider>::device_start() +{ + // allocate stream + m_stream = stream_alloc(0, 2, clock() / CLOCK_DIVIDER); + + // set host device to each voices + for (auto & elem : m_voice) + elem.m_host = this; + + save_item(STRUCT_MEMBER(m_voice, m_start)); + save_item(STRUCT_MEMBER(m_voice, m_flags)); + save_item(STRUCT_MEMBER(m_voice, m_freq)); + save_item(STRUCT_MEMBER(m_voice, m_lpstart)); + save_item(STRUCT_MEMBER(m_voice, m_lpend)); + save_item(STRUCT_MEMBER(m_voice, m_end)); + save_item(STRUCT_MEMBER(m_voice, m_vol_r)); + save_item(STRUCT_MEMBER(m_voice, m_vol_l)); + save_item(STRUCT_MEMBER(m_voice, m_pos)); + save_item(STRUCT_MEMBER(m_voice, m_frac)); + save_item(STRUCT_MEMBER(m_voice, m_lponce)); + save_item(STRUCT_MEMBER(m_voice, m_keyon)); + save_item(STRUCT_MEMBER(m_voice, m_out)); + + save_item(NAME(m_keyctrl)); +} + +//------------------------------------------------- +// device_clock_changed - called if the clock +// changes +//------------------------------------------------- + +template<unsigned MaxVoices, unsigned Divider> +void setapcm_device<MaxVoices, Divider>::device_clock_changed() +{ + m_stream->set_sample_rate(clock() / CLOCK_DIVIDER); +} + +//------------------------------------------------- +// sound_stream_update - handle update requests +// for our sound stream +//------------------------------------------------- + +template<unsigned MaxVoices, unsigned Divider> +void setapcm_device<MaxVoices, Divider>::sound_stream_update(sound_stream &stream, std::vector<read_stream_view> const &inputs, std::vector<write_stream_view> &outputs) +{ + outputs[0].fill(0); + outputs[1].fill(0); + + for (int sampleind = 0; sampleind < outputs[0].samples(); sampleind++) + { + for (int v = 0; v < MAX_VOICES; v++) + { + // check if voice is activated + if (m_voice[v].update()) + { + outputs[0].add_int(sampleind, (m_voice[v].m_out * m_voice[v].m_vol_l) >> 16, 32768 * MAX_VOICES); + outputs[1].add_int(sampleind, (m_voice[v].m_out * m_voice[v].m_vol_r) >> 16, 32768 * MAX_VOICES); + } + } + } +} + +//------------------------------------------------- +// rom_bank_updated - the rom bank has changed +//------------------------------------------------- + +template<unsigned MaxVoices, unsigned Divider> +void setapcm_device<MaxVoices, Divider>::rom_bank_updated() +{ + m_stream->update(); +} + +//------------------------------------------------- +// update - update single voice +//------------------------------------------------- + +template<unsigned MaxVoices, unsigned Divider> +bool setapcm_device<MaxVoices, Divider>::voice_t::update() +{ + if (m_keyon) + { + // fetch sample + m_out = s16(s8(m_host->read_byte(m_pos))) << 8; + + // advance + m_frac += m_freq; + m_pos += m_frac >> 12; + m_frac &= 0xfff; + + // stop if we're at the end + if (m_lponce) + { + // we've looped once, check loop end rather than sample end + if (m_pos >= m_lpend) + { + m_pos = m_lpstart; + } + } + else + { + // not looped yet, check sample end + if (m_pos >= m_end) + { + // code at 11d8c: + // if bit 2 (0x4) is set, check if loop start = loop end. + // if they are equal, clear bit 0 and don't set the loop start/end + // registers in the NiLe. if they aren't, set bit 0 and set + // the loop start/end registers in the NiLe. + // TODO: ST-0032 has same behavior? + if (BIT(m_flags, 0)) + { + m_pos = m_lpstart; + m_lponce = true; + } + else + { + m_keyon = false; + } + } + } + return true; + } + // clear output + m_out = 0; + return false; +} + +//------------------------------------------------- +// keyon - set keyon flag for single voice +//------------------------------------------------- + +template<unsigned MaxVoices, unsigned Divider> +void setapcm_device<MaxVoices, Divider>::voice_t::keyon() +{ + m_keyon = true; + m_pos = m_start; + m_frac = 0; + m_lponce = false; +} + +//------------------------------------------------- +// keyoff - set keyoff flag for single voice +//------------------------------------------------- + +template<unsigned MaxVoices, unsigned Divider> +void setapcm_device<MaxVoices, Divider>::voice_t::keyoff() +{ + m_keyon = false; +} + +//------------------------------------------------- +// reg_r - read single voice registers +//------------------------------------------------- + +template<unsigned MaxVoices, unsigned Divider> +u16 setapcm_device<MaxVoices, Divider>::voice_t::reg_r(offs_t reg) +{ + u16 ret = 0; + switch (reg & 0xf) + { + case 0x02: // Current position LSB + ret = m_pos & 0xffff; + break; + case 0x03: // Current position MSB + ret = m_pos >> 16; + break; + case 0x05: // Flags + ret = m_flags; + break; + case 0x06: // Frequency + ret = m_freq; + break; + case 0x07: // Loop start position LSB + ret = m_lpstart & 0xffff; + break; + case 0x09: // Loop start position MSB + ret = m_lpstart >> 16; + break; + case 0x0a: // Loop end position LSB + ret = m_lpend & 0xffff; + break; + case 0x0b: // Loop end position MSB + ret = m_lpend >> 16; + break; + case 0x0c: // End position LSB + ret = m_end & 0xffff; + break; + case 0x0d: // End position MSB + ret = m_end >> 16; + break; + case 0x0e: // Right volume + ret = m_vol_r; + break; + case 0x0f: // Left volume + ret = m_vol_l; + break; + } + return ret; +} + +//------------------------------------------------- +// reg_w - write single voice registers +//------------------------------------------------- + +template<unsigned MaxVoices, unsigned Divider> +void setapcm_device<MaxVoices, Divider>::voice_t::reg_w(offs_t reg, u16 data, u16 mem_mask) +{ + switch (reg & 0xf) + { + case 0x02: // Start position LSB, also affected current position? + m_start = m_pos = (m_start & ~mem_mask) | (data & mem_mask); + break; + case 0x03: // Start position MSB + m_start = m_pos = (m_start & ~(u32(mem_mask) << 16)) | (u32(data & mem_mask) << 16); + break; + case 0x05: // Flags + COMBINE_DATA(&m_flags); + break; + case 0x06: // Frequency + COMBINE_DATA(&m_freq); + break; + case 0x07: // Loop start position LSB + m_lpstart = (m_lpstart & ~mem_mask) | (data & mem_mask); + break; + case 0x09: // Loop start position MSB + m_lpstart = (m_lpstart & ~(u32(mem_mask) << 16)) | (u32(data & mem_mask) << 16); + break; + case 0x0a: // Loop end position LSB + m_lpend = (m_lpend & ~mem_mask) | (data & mem_mask); + break; + case 0x0b: // Loop end position MSB + m_lpend = (m_lpend & ~(u32(mem_mask) << 16)) | (u32(data & mem_mask) << 16); + break; + case 0x0c: // End position LSB + m_end = (m_end & ~mem_mask) | (data & mem_mask); + break; + case 0x0d: // End position MSB + m_end = (m_end & ~(u32(mem_mask) << 16)) | (u32(data & mem_mask) << 16); + break; + case 0x0e: // Right volume + COMBINE_DATA(&m_vol_r); + break; + case 0x0f: // Left volume + COMBINE_DATA(&m_vol_l); + break; + } +} + +//------------------------------------------------- +// snd_w - write each voice registers +//------------------------------------------------- + +template<unsigned MaxVoices, unsigned Divider> +void setapcm_device<MaxVoices, Divider>::snd_w(offs_t offset, u16 data, u16 mem_mask) +{ + m_stream->update(); + + const int v = (offset >> 4); + + if (v >= MAX_VOICES) + return; + + m_voice[v].reg_w(offset & 0xf, data, mem_mask); + //logerror("v%02d: %04x & %04x to reg %02d (PC=%x)\n", v, data, mem_mask, r, machine().describe_context()); +} + + +//------------------------------------------------- +// snd_r - read each voice registers +//------------------------------------------------- + +template<unsigned MaxVoices, unsigned Divider> +u16 setapcm_device<MaxVoices, Divider>::snd_r(offs_t offset) +{ + const int v = (offset >> 4); + + if (v >= MAX_VOICES) + return 0; + + m_stream->update(); + + return m_voice[v].reg_r(offset & 0xf); +} + + +//------------------------------------------------- +// key_w - set keyon/off flags for each voices +//------------------------------------------------- + +template<unsigned MaxVoices, unsigned Divider> +void setapcm_device<MaxVoices, Divider>::key_w(offs_t offset, u16 data, u16 mem_mask) +{ + const u16 prev = m_keyctrl; + + m_stream->update(); + + COMBINE_DATA(&m_keyctrl); + + //logerror("KEYCTRL: %04x -> %04x %s\n", prev, m_keyctrl, machine().describe_context()); + + for (int v = 0; v < MAX_VOICES; v++) + { + if (BIT(m_keyctrl, v) && (!(BIT(prev, v)))) // keyon + { + m_voice[v].keyon(); + } + else if ((!(BIT(m_keyctrl, v))) && BIT(prev, v)) // keyoff + { + m_voice[v].keyoff(); + } + } +} + + +//------------------------------------------------- +// key_r - get keyon/off status from each voices +//------------------------------------------------- + +template<unsigned MaxVoices, unsigned Divider> +u16 setapcm_device<MaxVoices, Divider>::key_r() +{ + m_stream->update(); + return m_keyctrl; +} + +// template class definition +template class setapcm_device<8, 160>; +template class setapcm_device<16, 384>; diff --git a/src/devices/sound/setapcm.h b/src/devices/sound/setapcm.h new file mode 100644 index 00000000000..8c406c7076f --- /dev/null +++ b/src/devices/sound/setapcm.h @@ -0,0 +1,103 @@ +// license:BSD-3-Clause +// copyright-holders:Tomasz Slanina,cam900 +#ifndef MAME_SOUND_SETAPCM_H +#define MAME_SOUND_SETAPCM_H + +#pragma once + +#include "dirom.h" + +//************************************************************************** +// TYPE DEFINITIONS +//************************************************************************** + +// device type definition +DECLARE_DEVICE_TYPE(NILE_SOUND, nile_sound_device) +DECLARE_DEVICE_TYPE(ST0032_SOUND, st0032_sound_device) + +// ======================> setapcm_device + +// TODO: unknown address bus width +template<unsigned MaxVoices, unsigned Divider> +class setapcm_device : public device_t, + public device_sound_interface, + public device_rom_interface<32> +{ +public: + void snd_w(offs_t offset, u16 data, u16 mem_mask = ~0); + u16 snd_r(offs_t offset); + + void key_w(offs_t offset, u16 data, u16 mem_mask = ~0); + u16 key_r(); + +protected: + setapcm_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, u32 clock); + + // device-level overrides + virtual void device_start() override; + virtual void device_clock_changed() override; + + // 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; + + // device_rom_interface implementation + virtual void rom_bank_updated() override; + + static constexpr unsigned MAX_VOICES = MaxVoices; // max voices + static constexpr unsigned CLOCK_DIVIDER = Divider; // clock divider for generate output rate +private: + struct voice_t + { + bool update(); + void keyon(); + void keyoff(); + + u16 reg_r(offs_t reg); + void reg_w(offs_t reg, u16 data, u16 mem_mask = ~0); + + device_rom_interface<32> *m_host; // host device + u32 m_start = 0; // Start position + u16 m_flags = 0; // Flags (Bit 0 = loop) + u16 m_freq = 0; // Frequency (4.12 fixed point) + u32 m_lpstart = 0; // Loop start position + u32 m_lpend = 0; // Loop end position + u32 m_end = 0; // End position + s32 m_vol_r = 0; // Right volume + s32 m_vol_l = 0; // Left volume + u32 m_pos = 0; // Current position + u32 m_frac = 0; // Position fraction + bool m_lponce = false; // Is looped once? + bool m_keyon = false; // Keyon status + s32 m_out = 0; // output value + }; + + sound_stream *m_stream; + + voice_t m_voice[MaxVoices]; // 8 or 16 Voice engines + u16 m_keyctrl; // Key on/off control bit +}; + +// ======================> nile_sound_device + +class nile_sound_device : public setapcm_device<8, 160> +{ +public: + nile_sound_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock); +}; + +// ======================> st0032_sound_device + +class st0032_sound_device : public setapcm_device<16, 384> +{ +public: + st0032_sound_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock); +}; + +//************************************************************************** +// EXTERNAL TEMPLATE INSTANTIATIONS +//************************************************************************** + +extern template class setapcm_device<8, 160>; +extern template class setapcm_device<16, 384>; + +#endif // MAME_SOUND_SETAPCM_H |