From 682eb67235045cbe204dbc99a0c057631401eecd Mon Sep 17 00:00:00 2001 From: Olivier Galibert Date: Mon, 6 Nov 2023 22:43:27 +0100 Subject: swp30: Add lfo, correct issue in state readback making the program lose track of some samples --- src/devices/sound/swp30.cpp | 80 +++++++++++++++++++++++++++++++++------------ src/devices/sound/swp30.h | 13 ++++++++ 2 files changed, 73 insertions(+), 20 deletions(-) diff --git a/src/devices/sound/swp30.cpp b/src/devices/sound/swp30.cpp index ac1a8b09083..56076ece24b 100644 --- a/src/devices/sound/swp30.cpp +++ b/src/devices/sound/swp30.cpp @@ -302,6 +302,14 @@ const std::array swp30_device::decay_linear_step = { 0x8d3dd, 0x939a8, 0x991f2, 0x9d89e, 0xa0a0a, 0xa57eb, 0xa72f0, 0xac769, }; +// Actual shape of the lfos unknown, since the hardware accepts 4 and +// 3 are in use (0, 1 and 3) and no recording are currently available + +const std::array swp30_device::lfo_shape_centered_saw = { 0x00000000, 0x00000000, 0xfff00000, 0xfff00000 }; // --////-- +const std::array swp30_device::lfo_shape_centered_tri = { 0x00000000, 0x0007ffff, 0xfff7ffff, 0xfff00000 }; // --/\/\-- +const std::array swp30_device::lfo_shape_offset_saw = { 0x00000000, 0x00000000, 0x00000000, 0x00000000 }; // __////__ +const std::array swp30_device::lfo_shape_offset_tri = { 0x00000000, 0x00000000, 0x000fffff, 0x000fffff }; // __/\/\__ + swp30_device::swp30_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) : cpu_device(mconfig, SWP30, tag, owner, clock), device_sound_interface(mconfig, *this), @@ -357,10 +365,14 @@ void swp30_device::device_start() save_item(NAME(m_dry_rev)); save_item(NAME(m_cho_var)); + save_item(NAME(m_lfo_step_pmod)); + save_item(NAME(m_lfo_amod)); + save_item(NAME(m_attack)); save_item(NAME(m_decay1)); save_item(NAME(m_decay2)); + save_item(NAME(m_lfo_phase)); save_item(NAME(m_sample_pos)); save_item(NAME(m_envelope_level)); save_item(NAME(m_envelope_on_timer)); @@ -413,10 +425,14 @@ void swp30_device::device_reset() std::fill(m_dry_rev.begin(), m_dry_rev.end(), 0); std::fill(m_cho_var.begin(), m_cho_var.end(), 0); + std::fill(m_lfo_step_pmod.begin(), m_lfo_step_pmod.end(), 0); + std::fill(m_lfo_amod.begin(), m_lfo_amod.end(), 0); + std::fill(m_attack.begin(), m_attack.end(), 0); std::fill(m_decay1.begin(), m_decay1.end(), 0); std::fill(m_decay2.begin(), m_decay2.end(), 0); + std::fill(m_lfo_phase.begin(), m_lfo_phase.end(), 0); std::fill(m_sample_pos.begin(), m_sample_pos.end(), 0); std::fill(m_envelope_level.begin(), m_envelope_level.end(), 0); std::fill(m_envelope_timer.begin(), m_envelope_timer.end(), 0); @@ -462,12 +478,13 @@ void swp30_device::map(address_map &map) rchan(map, 0x02).rw(FUNC(swp30_device::hpf_cutoff_r), FUNC(swp30_device::hpf_cutoff_w)); // 03 seems to always get 5010 except at startup where it's 40ff rchan(map, 0x04).rw(FUNC(swp30_device::lpf_reso_r), FUNC(swp30_device::lpf_reso_w)); - // 05 missing + rchan(map, 0x05).rw(FUNC(swp30_device::lfo_amod_r), FUNC(swp30_device::lfo_amod_w)); rchan(map, 0x06).rw(FUNC(swp30_device::attack_r), FUNC(swp30_device::attack_w)); rchan(map, 0x07).rw(FUNC(swp30_device::decay1_r), FUNC(swp30_device::decay1_w)); rchan(map, 0x08).rw(FUNC(swp30_device::decay2_r), FUNC(swp30_device::decay2_w)); rchan(map, 0x09).rw(FUNC(swp30_device::release_glo_r), FUNC(swp30_device::release_glo_w)); - // 0a-0d missing + rchan(map, 0x0a).rw(FUNC(swp30_device::lfo_step_pmod_r), FUNC(swp30_device::lfo_step_pmod_w)); + // 0b-0d missing // 10 missing rchan(map, 0x11).rw(FUNC(swp30_device::pitch_r), FUNC(swp30_device::pitch_w)); rchan(map, 0x12).rw(FUNC(swp30_device::sample_start_h_r), FUNC(swp30_device::sample_start_h_w)); @@ -587,6 +604,8 @@ void swp30_device::keyon_w(u16) dt = -dt; m_dpcm_address[chan] = ((m_sample_address[chan] & 0xffffff) << 2) - dt; + m_lfo_phase[chan] = 0; + if(1) logerror("[%08d] keyon %02x %08x %08x %08x vol %04x env %04x %04x %04x pan %04x disp %04x %04x\n", scount, chan, m_sample_start[chan], m_sample_end[chan], m_sample_address[chan], m_release_glo[chan], m_attack[chan], m_decay1[chan], m_decay2[chan], m_pan[chan], m_dry_rev[chan], m_cho_var[chan]); } @@ -776,8 +795,6 @@ void swp30_device::release_glo_w(offs_t offset, u16 data) if(1 && m_release_glo[chan] != data) logerror("snd chan %02x rel/glo %02x %02x\n", chan, data >> 8, data & 0xff); m_release_glo[chan] = data; - if(data == 0xea13) - machine().debug_break(); if((data & 0x8000) && m_envelope_mode[chan] != IDLE && m_envelope_mode[chan] != RELEASE) m_envelope_mode[chan] = RELEASE; } @@ -871,6 +888,28 @@ void swp30_device::decay2_w(offs_t offset, u16 data) m_decay2[offset >> 6] = data; } +u16 swp30_device::lfo_step_pmod_r(offs_t offset) +{ + return m_lfo_step_pmod[offset >> 6]; +} + +void swp30_device::lfo_step_pmod_w(offs_t offset, u16 data) +{ + // logerror("lfo_step_pmod[%02x] = %04x\n", offset >> 6, data); + m_lfo_step_pmod[offset >> 6] = data; +} + +u16 swp30_device::lfo_amod_r(offs_t offset) +{ + return m_lfo_amod[offset >> 6]; +} + +void swp30_device::lfo_amod_w(offs_t offset, u16 data) +{ + // logerror("lfo_amod[%02x] = %04x\n", offset >> 6, data); + m_lfo_amod[offset >> 6] = data; +} + u16 swp30_device::sample_start_h_r(offs_t offset) { return m_sample_start[offset >> 6] >> 16; @@ -958,7 +997,7 @@ u16 swp30_device::internal_r() switch(m_internal_adr >> 8) { case 0: // Not certain about the two top bits though, the code seems to only care about 0/non-0 - return m_envelope_mode[chan] == IDLE ? 0xffff : ((m_envelope_mode[chan] - 1) << 14) | (m_envelope_level[chan] >> (26-14)); + return m_envelope_mode[chan] == IDLE ? 0xffff : ((m_envelope_mode[chan] - 1) << 14) | (m_envelope_level[chan] >> (28-14)); case 4: // used at 44c4 @@ -1039,8 +1078,6 @@ u16 swp30_device::snd_r(offs_t offset) preg = util::string_format("%02x.%02x", chan, slot); logerror("snd_r [%04x %04x] %-5s, %04x\n", offset, offset*2, preg, rr[offset]); } - if(0 && offset == 0x080f) - machine().debug_break(); if(offset == 0x080f) return 0; return rr[offset]; @@ -1059,9 +1096,6 @@ void swp30_device::snd_w(offs_t offset, u16 data) if(offset == 0x04e) return; - if(0 && slot == 0x03) - machine().debug_break(); - std::string preg = "-"; if(slot >= 0x21 && slot <= 0x2b && (slot & 1)) preg = util::string_format("fp%03x", (slot-0x21)/2 + 6*chan); @@ -1077,8 +1111,6 @@ void swp30_device::snd_w(offs_t offset, u16 data) preg = util::string_format("lfo[%02x]", (slot-0x3e) + 2*chan); else preg = util::string_format("%02x.%02x", chan, slot); - // if((slot >= 0xa && slot <= 0xd) || (slot >= 0x2c && slot <= 0x2f)) - // machine().debug_break(); logerror("snd_w [%04x %04x] %-5s, %04x\n", offset, offset*2, preg, data); } @@ -1100,6 +1132,14 @@ void swp30_device::sound_stream_update(sound_stream &stream, std::vector> 7; + s32 lfo_p_phase = lfo_phase ^ (m_lfo_step_pmod[chan] & 0xc000 ? lfo_shape_centered_tri : lfo_shape_centered_saw)[lfo_phase >> 18]; + s32 lfo_a_phase = lfo_phase ^ (m_lfo_step_pmod[chan] & 0xc000 ? lfo_shape_offset_tri : lfo_shape_offset_saw )[lfo_phase >> 18]; + // First, read the sample // - Find the base sample index and base address @@ -1278,22 +1318,19 @@ void swp30_device::sound_stream_update(sound_stream &stream, std::vector> 4, 1); istep(m_pan_r[chan], (m_pan[chan] & 0x00ff) << 4, 1); + m_lfo_phase[chan] = (m_lfo_phase[chan] + m_global_step[0x20 + ((m_lfo_step_pmod[chan] >> 8) & 0x3f)]) & 0x7ffffff; + u32 sample_increment = (((m_pitch[chan] & 0x3ff) | 0x400) << (8 + (s16(m_pitch[chan] << 2) >> 12))) >> 10; - m_sample_pos[chan] += sample_increment; //(sample_increment * (0x800 + ((lfo_p_phase * m_lfo_pmod_depth[chan]) >> (m_lfo_step[chan] & 0x40 ? 18 : 19)))) >> 11; + m_sample_pos[chan] += (sample_increment * (0x800 + ((lfo_p_phase * (m_lfo_step_pmod[chan] & 0xff)) >> (m_lfo_step_pmod[chan] & 0xc000 ? 18 : 19)))) >> 11; if((m_sample_pos[chan] >> 8) >= (m_sample_end[chan] & 0xffffff)) { if(!(m_sample_end[chan] & 0xffffff)) m_envelope_mode[chan] = IDLE; @@ -1309,6 +1346,9 @@ void swp30_device::sound_stream_update(sound_stream &stream, std::vector m_global_step; std::array m_dpcm; + static const std::array lfo_shape_centered_saw; + static const std::array lfo_shape_centered_tri; + static const std::array lfo_shape_offset_saw; + static const std::array lfo_shape_offset_tri; + std::array m_sample_start; std::array m_sample_end; std::array m_sample_address; @@ -64,7 +69,10 @@ private: std::array m_pan; std::array m_dry_rev; std::array m_cho_var; + std::array m_lfo_step_pmod; + std::array m_lfo_amod; + std::array m_lfo_phase; std::array m_sample_pos; std::array m_envelope_level; std::array m_envelope_timer; @@ -142,6 +150,11 @@ private: u16 cho_var_r(offs_t offset); void cho_var_w(offs_t offset, u16 data); + void lfo_step_pmod_w(offs_t offset, u16 data); + u16 lfo_step_pmod_r(offs_t offset); + void lfo_amod_w(offs_t offset, u16 data); + u16 lfo_amod_r(offs_t offset); + u16 internal_adr_r(); void internal_adr_w(u16 data); u16 internal_r(); -- cgit v1.2.3