summaryrefslogtreecommitdiffstatshomepage
diff options
context:
space:
mode:
author Olivier Galibert <galibert@pobox.com>2025-11-14 14:29:54 +0100
committer Olivier Galibert <galibert@pobox.com>2025-11-14 16:50:27 +0100
commitb75872cb98a17df42cdb36a19e093ebeabf49f49 (patch)
tree32bdb5cf7724efcc1dfa113a0b3a8104cf45e86a
parent66ca47301c3c0eaf126f3b836c572d41ed69c6cf (diff)
swp00: Overhaul of the synthesis part, effects to follow
-rw-r--r--src/devices/sound/adc.cpp42
-rw-r--r--src/devices/sound/adc.h17
-rw-r--r--src/devices/sound/swp00.cpp1757
-rw-r--r--src/devices/sound/swp00.h271
-rw-r--r--src/mame/yamaha/ymmu10.cpp46
-rw-r--r--src/osd/modules/sound/pipewire_sound.cpp3
6 files changed, 1441 insertions, 695 deletions
diff --git a/src/devices/sound/adc.cpp b/src/devices/sound/adc.cpp
index 3f2ee0b3175..510cdf5a0a4 100644
--- a/src/devices/sound/adc.cpp
+++ b/src/devices/sound/adc.cpp
@@ -54,7 +54,7 @@ u16 adc10_device::read()
void adc10_device::device_start()
{
save_item(NAME(m_current_value));
- m_stream = stream_alloc(1, 0, SAMPLE_RATE_INPUT_ADAPTIVE);
+ m_stream = stream_alloc(1, 0, clock() ? clock() : SAMPLE_RATE_INPUT_ADAPTIVE);
}
void adc10_device::sound_stream_update(sound_stream &stream)
@@ -64,3 +64,43 @@ void adc10_device::sound_stream_update(sound_stream &stream)
}
DEFINE_DEVICE_TYPE(ADC10, adc10_device, "adc10", "10-bits signed ADC")
+
+adc16_device::adc16_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
+ : device_t(mconfig, ADC16, tag, owner, clock),
+ device_sound_interface(mconfig, *this),
+ m_stream(nullptr),
+ m_current_value(0)
+{
+}
+
+u16 adc16_device::read()
+{
+ m_stream->update();
+ return m_current_value;
+}
+
+u8 adc16_device::read8h()
+{
+ m_stream->update();
+ return m_current_value >> 8;
+}
+
+u8 adc16_device::read8l()
+{
+ m_stream->update();
+ return m_current_value;
+}
+
+void adc16_device::device_start()
+{
+ save_item(NAME(m_current_value));
+ m_stream = stream_alloc(1, 0, clock() ? clock() : SAMPLE_RATE_INPUT_ADAPTIVE);
+}
+
+void adc16_device::sound_stream_update(sound_stream &stream)
+{
+ sound_stream::sample_t last_sample = stream.get(0, stream.samples()-1);
+ m_current_value = std::clamp(int(32768 * last_sample), -32768, 32767);
+}
+
+DEFINE_DEVICE_TYPE(ADC16, adc16_device, "adc16", "16-bits signed ADC")
diff --git a/src/devices/sound/adc.h b/src/devices/sound/adc.h
index 43903a930de..2a88da00bb1 100644
--- a/src/devices/sound/adc.h
+++ b/src/devices/sound/adc.h
@@ -36,7 +36,24 @@ protected:
virtual void sound_stream_update(sound_stream &stream) override;
};
+class adc16_device : public device_t, public device_sound_interface
+{
+public:
+ adc16_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock = 0);
+ u8 read8h();
+ u8 read8l();
+ u16 read();
+
+protected:
+ sound_stream *m_stream;
+ u16 m_current_value;
+
+ virtual void device_start() override ATTR_COLD;
+ virtual void sound_stream_update(sound_stream &stream) override;
+};
+
DECLARE_DEVICE_TYPE(ZN449, zn449_device);
DECLARE_DEVICE_TYPE(ADC10, adc10_device);
+DECLARE_DEVICE_TYPE(ADC16, adc16_device);
#endif // MAME_SOUND_ADC_H
diff --git a/src/devices/sound/swp00.cpp b/src/devices/sound/swp00.cpp
index d6ec9987e01..d32a89ab124 100644
--- a/src/devices/sound/swp00.cpp
+++ b/src/devices/sound/swp00.cpp
@@ -66,36 +66,974 @@
sss sscc cccs AWM2, channel/slot combination (slot = 8-b and 20-37)
*/
+// Interpolation speed subblock
+
+// A generic interpolaton subblock is used by envelopes, filter and
+// lfo. Its function is to provide a step to add or substract to a
+// counter. This step depends on a 00-7f speed value and the current
+// sample number. It goes from adding 1 every 2048 samples to adding
+// 31 every sample.
+
+// Specifically, for speeds between 00 and 57 seen as level*8 +
+// duty_cycle (level=0..a, duty_cycle=0..7), 1 is potentially added
+// every 2**(a-level) (0..1024) samples following a 16-steps pattern
+// with a duty cycle of 8/16th (duty_cycle=0) to 15/16th
+// (duty_cycle=7). For speeds between 58 and 77 (level=b..e) on every
+// sample either 2**(level-a)-1 or 2**(level-9)-1 is added following a
+// 8-step pattern with a duty cycle going from 0/8th to 7/8th. For
+// 78+, 1f is added every sample.
+
+u16 swp00_device::interpolation_step(u32 speed, u32 sample_counter)
+{
+ // Phase is incorrect, and very weird
+
+ if(speed >= 0x78)
+ return 0x7f;
+
+ u32 k0 = speed >> 3;
+ u32 k1 = speed & 7;
+
+ if(speed >= 0x58) {
+ k0 -= 10;
+ u32 a = (4 << k0) - 1;
+ u32 b = (2 << k0) - 1;
+ static const u8 mx[8] = { 0x00, 0x80, 0x88, 0xa8, 0xaa, 0xea, 0xee, 0xfe };
+ return ((mx[k1] << (sample_counter & 7)) & 0x80) ? a : b;
+ }
+
+ k0 = 10 - k0;
+
+ if(sample_counter & util::make_bitmask<u32>(k0))
+ return 0;
+
+ static const u16 mx[8] = { 0xaaaa, 0xeaaa, 0xeaea, 0xeeea, 0xeeee, 0xfeee, 0xfefe, 0xfffe };
+ return (mx[k1] << ((sample_counter >> k0) & 0xf)) & 0x8000 ? 1 : 0;
+}
+
+
+
+// Streaming block
+//
+//
+// 00100 ccccc * MMpp pppp pppp pppp Memory access, initial phase
+// 00101 ccccc * ssss ssss ssss ssss Number of samples before the loop point
+// 11000 ccccc 0 ff0S SSmm/ffll llll Format (3), Scaling, Compressor mode / Format (0-2). Loop size adjust.
+// 1100* ccccc * aaaa aaaa aaaa aaaa aaaa aaaa Sample address (starts after format)
+// 11010 ccccc * eeee mmmm mmmm mmmm Pitch
+// 11011 ccccc * ssss ssss ssss ssss Number of samples in the loop
+
+// Simplified version of the swp30 streaming block.
+//
+// Uses 2-point linear interpolation (9-bits multiplier, 15-bits
+// counter) rather than 4-point cubic.
+//
+// Pitch is linear rather than exponential.
+//
+// Address resolution is byte.
+//
+// The same register is shared for per-loop size adjustment and
+// parameters of the compressed format. As a consequence the
+// compressed format does not have the loop size adjustment active.
+//
+// There is an "initial phase" register available, which sets 14 of
+// the 15-bits sub-sample position at start. There are two bits in
+// that register that seem to set some kind of memory access mode,
+// with the result that putting anything else than 10 gives somewhat
+// unpredictable results (00 weirdly seems to be "add 0x48 to the
+// address", the other values return random values as raw sample
+// data).
+
+
+// Dpcm delta expansion table
+const std::array<s16, 256> swp00_device::streaming_block::dpcm_expand = []() {
+ std::array<s16, 256> deltas;
+ static const s16 offset[4] = { 0, 0x20, 0x60, 0xe0 };
+ for(u32 i=0; i != 128; i++) {
+ u32 e = i >> 5;
+ s16 base = ((i & 0x1f) << e) + offset[e];
+ deltas[i] = base;
+ deltas[i+128] = -base;
+ }
+ deltas[0x80] = 0x88; // Not actually used by samples, but tested on swp30 hardware
+ return deltas;
+}();
+
+const std::array<s32, 8> swp00_device::streaming_block::max_value = {
+ 0x7fff, 0x7ffe, 0x7ffc, 0x7ff8, 0x7ff0, 0x7fe0, 0x7fc0, 0x7f80
+};
+
+void swp00_device::streaming_block::clear()
+{
+ m_phase = 0x8000;
+ m_start = 0;
+ m_loop = 0;
+ m_address = 0;
+ m_pitch = 0;
+ m_format = 0;
+ m_pos = 0;
+ m_pos_dec = 0;
+ m_dpcm_s0 = m_dpcm_s1 = 0;
+ m_dpcm_pos = 0;
+ m_dpcm_delta = 0;
+ m_first = false;
+ m_done = false;
+ m_last = 0;
+}
+
+void swp00_device::streaming_block::keyon()
+{
+ m_pos = -m_start;
+ m_pos_dec = (m_phase << 1) & 0x7ffe;
+ m_dpcm_s0 = m_dpcm_s1 = 0;
+ m_dpcm_pos = m_pos+1;
+ m_dpcm_delta = 0;
+ m_first = true;
+ m_done = false;
+}
+
+void swp00_device::streaming_block::read_16(memory_access<24, 0, 0, ENDIANNESS_LITTLE>::cache &wave, s16 &val0, s16 &val1)
+{
+ offs_t adr = m_address + (m_pos << 1);
+ val0 = wave.read_word(adr);
+ val1 = wave.read_word(adr+2);
+}
+
+void swp00_device::streaming_block::read_12(memory_access<24, 0, 0, ENDIANNESS_LITTLE>::cache &wave, s16 &val0, s16 &val1)
+{
+ offs_t adr = m_address + (m_pos >> 2)*6;
+
+ switch(m_pos & 3) {
+ case 0: { // bcCa AB.. -> Cabc ..AB
+ u16 w0 = wave.read_word(adr);
+ u16 w1 = wave.read_word(adr+2);
+ val0 = w0 << 4;
+ val1 = ((w0 >> 8) | (w1 << 8)) & 0xfff0;
+ break;
+ }
+ case 1: { // ..c. abBC .A.. -> c... BCab ...A
+ u16 w0 = wave.read_word(adr);
+ u16 w1 = wave.read_word(adr+2);
+ u16 w2 = wave.read_word(adr+4);
+ val0 = ((w0 >> 8) | (w1 << 8)) & 0xfff0;
+ val1 = ((w1 >> 4) & 0x0ff0) | (w2 << 12);
+ break;
+ }
+ case 2: { // ..bc CaAB -> bc.. ABCa
+ u16 w1 = wave.read_word(adr+2);
+ u16 w2 = wave.read_word(adr+4);
+ val0 = ((w1 >> 4) & 0x0ff0) | (w2 << 12);
+ val1 = w2 & 0xfff0;
+ break;
+ }
+ case 3: { // c.ab BC.A -> abc. .ABC
+ u16 w2 = wave.read_word(adr+4);
+ u16 w3 = wave.read_word(adr+6);
+ val0 = w2 & 0xfff0;
+ val1 = w3 << 4;
+ break;
+ }
+ }
+}
+
+void swp00_device::streaming_block::read_8(memory_access<24, 0, 0, ENDIANNESS_LITTLE>::cache &wave, s16 &val0, s16 &val1)
+{
+ offs_t adr = m_address + m_pos;
+ val0 = wave.read_byte(adr ) << 8;
+ val1 = wave.read_byte(adr+1) << 8;
+}
+
+void swp00_device::streaming_block::dpcm_step(u8 input)
+{
+ u32 mode = m_format & 3;
+ u32 scale = (m_format >> 2) & 7;
+ s32 limit = max_value[scale];
+
+ m_dpcm_s0 = m_dpcm_s1;
+
+ s32 delta = m_dpcm_delta + dpcm_expand[input];
+ s32 sample = m_dpcm_s1 + (delta << scale);
+
+ if(sample < -0x8000) {
+ sample = -0x8000;
+ delta = 0;
+ } else if(sample > limit) {
+ sample = limit;
+ delta = 0;
+ }
+ m_dpcm_s1 = sample;
+
+ switch(mode) {
+ case 0: delta = delta * 7 / 8; break;
+ case 1: delta = delta * 3 / 4; break;
+ case 2: delta = delta / 2; break;
+ case 3: delta = 0; break;
+ }
+ m_dpcm_delta = delta;
+}
+
+void swp00_device::streaming_block::read_8c(memory_access<24, 0, 0, ENDIANNESS_LITTLE>::cache &wave, s16 &val0, s16 &val1)
+{
+ while(m_dpcm_pos != m_pos + 2) {
+ dpcm_step(wave.read_byte(m_address + m_dpcm_pos));
+ m_dpcm_pos++;
+ }
+
+ val0 = m_dpcm_s0;
+ val1 = m_dpcm_s1;
+}
+
+std::pair<s16, bool> swp00_device::streaming_block::step(memory_access<24, 0, 0, ENDIANNESS_LITTLE>::cache &wave, s32 fmod)
+{
+ if(m_done)
+ return std::make_pair(m_last, false);
+
+ s16 val0, val1;
+
+ switch(m_format >> 6) {
+ case 0: read_16(wave, val0, val1); break;
+ case 1: read_12(wave, val0, val1); break;
+ case 2: read_8 (wave, val0, val1); break;
+ case 3: read_8c(wave, val0, val1); break;
+ }
+
+ u32 step = ((m_pitch & 0xfff) << (8 + (s16(m_pitch) >> 12))) >> 4;
+ s32 interp = (m_pos_dec >> 6) & 0x1ff;
+ // The multiplier has a precision limited to 15 bits
+ s32 result = val0 + ((((val1-val0) * interp) >> 10) << 1);
+
+ m_pos_dec += step + (fmod << 4);
+ if(m_pos_dec >= 0x8000) {
+ m_first = false;
+ m_pos += m_pos_dec >> 15;
+ m_pos_dec &= 0x7fff;
+ if(m_pos >= m_loop) {
+ if(m_loop) {
+ m_pos -= m_loop;
+ if((m_format & 0xc0) != 0xc0) {
+ m_pos_dec += (m_format << 9) & 0x7e00;
+ if(m_pos_dec >= 0x8000)
+ m_pos ++;
+ m_pos_dec &= 0x7fff;
+ }
+ m_dpcm_pos = 1;
+ } else {
+ m_done = true;
+ m_last = result;
+ return std::make_pair(m_last, true);
+ }
+ }
+ }
+ return std::make_pair(result, false);
+}
+
+template<int sel> void swp00_device::streaming_block::phase_w(u8 data)
+{
+ constexpr int shift = 8*sel;
+ m_phase = (m_phase & ~(0xff << shift)) | (data << shift);
+}
+
+template<int sel> void swp00_device::streaming_block::start_w(u8 data)
+{
+ constexpr int shift = 8*sel;
+ m_start = (m_start & ~(0xff << shift)) | (data << shift);
+}
+
+template<int sel> void swp00_device::streaming_block::loop_w(u8 data)
+{
+ constexpr int shift = 8*sel;
+ m_loop = (m_loop & ~(0xff << shift)) | (data << shift);
+}
+
+template<int sel> void swp00_device::streaming_block::address_w(u8 data)
+{
+ constexpr int shift = 8*sel;
+ m_address = (m_address & ~(0xff << shift)) | (data << shift);
+}
+
+template<int sel> void swp00_device::streaming_block::pitch_w(u8 data)
+{
+ constexpr int shift = 8*sel;
+ m_pitch = (m_pitch & ~(0xff << shift)) | (data << shift);
+}
+
+void swp00_device::streaming_block::format_w(u8 data)
+{
+ m_format = data;
+}
+
+template<int sel> u8 swp00_device::streaming_block::phase_r() const
+{
+ return m_phase >> (8*sel);
+}
+
+template<int sel> u8 swp00_device::streaming_block::start_r() const
+{
+ return m_start >> (8*sel);
+}
+
+template<int sel> u8 swp00_device::streaming_block::loop_r() const
+{
+ return m_loop >> (8*sel);
+}
+
+template<int sel> u8 swp00_device::streaming_block::address_r() const
+{
+ return m_address >> (8*sel);
+}
+
+template<int sel> u8 swp00_device::streaming_block::pitch_r() const
+{
+ return m_pitch >> (8*sel);
+}
+
+u8 swp00_device::streaming_block::format_r() const
+{
+ return m_format;
+}
+
+u32 swp00_device::streaming_block::sample_address_get()
+{
+ u32 result = m_address;
+ m_address = (m_address + 1) & 0xffffff;
+ return result;
+}
+
+std::string swp00_device::streaming_block::describe() const
+{
+ std::string desc;
+ desc = util::string_format("[%04x %08x %08x %08x] ", m_pitch, m_start, m_loop, m_address) + util::string_format("sample %04x-%04x @ %06x ", m_start, m_loop, m_address);
+ switch(m_format >> 6) {
+ case 0: desc += "16"; break;
+ case 1: desc += "12"; break;
+ case 2: desc += "8 "; break;
+ case 3: desc += util::string_format("c%x", m_format & 3); break;
+ }
+ if((m_format & 0xc0) == 0xc0)
+ desc += util::string_format(" scale %x", (m_format >> 2) & 7);
+ if(!m_loop)
+ desc += " fwd ";
+ else
+ desc += " loop";
+ if((m_format & 0xc0) != 0xc0 && (m_format & 0x3f))
+ desc += util::string_format(" loop-adjust %02x", m_format & 0x3f);
+ if(m_pitch & 0x8000) {
+ u32 p = 0x10000 - m_pitch;
+ desc += util::string_format(" pitch -%x.%03x", p >> 12, p & 0xfff);
+ } else if(m_pitch)
+ desc += util::string_format(" pitch +%x.%03x", (m_pitch >> 12) & 7, m_pitch & 0xfff);
+
+ return desc;
+}
+
+
+//--------------------------------------------------------------------------------
+
+// Envelope block
+//
+//
+// 10011 ccccc 0 msss ssss Attack speed
+// 10011 ccccc 1 tttt tttt Attack starting point/target
+// 10100 ccccc 0 isss ssss Update decay, Decay speed
+// 10100 ccccc 1 tttt tttt Decay target
+
+// Simplified version of the swp30 envelope block.
+
+// There is no decay2 or release. Release is done by writing a new
+// values to decay speed with bit 7 set and setting the target to ff.
+// Writing a value to decay speed after keyon is ignored when bit 7 is
+// not set. Decay target otoh is always taken into account live.
+
+// The attenuation is on 12 bits (4 exponent, 8 mantissa) and targets
+// are left-shifted and zero-padded by 4 bits.
+
+// Attack can happen in three modes:
+
+// - normal attack when m=1, where attenuation goes from starting point
+// to 0 at variable speed so that it's linear in amplitude space
+
+// - timed attack when m=0 and starting point = 0, where attenuation
+// stays at 0 for a time depending on the speed
+
+// - alternative decay when m=0 and target != 0, where
+// attenuation goes from 0 to target at constant speed, like
+// the decay
+
+// Decay can happen in two modes:
+
+// - normal decay where the attenuation goes from current to target
+// at constant speed
+
+// - alternative decay, when bit 7 is set and speed >= 0x60, where
+// the speed is variable to be linear in amplitude space
+
+// The value to add (or substract) on every cycle is given by the
+// common interpolation subblock. When in constant speed mode (attack
+// mode 3, decay mode 1), the speed value is directly used. When in
+// variable speed mode, the actual speed is the speed (for attack) or
+// speed - 0x10 (for decay) with 4*(attenuation >> 7) added.
+
+// Timed attack mode counts at the given speed until 0x800 is reached.
+
+void swp00_device::envelope_block::clear()
+{
+ m_attack_speed = 0;
+ m_attack_level = 0;
+ m_decay_speed = 0;
+ m_decay_level = 0;
+ m_envelope_level = 0xfff;
+ m_envelope_mode = DECAY_DONE;
+}
+
+void swp00_device::envelope_block::keyon()
+{
+ if(m_decay_speed & 0x80) {
+ // Immediate release
+ m_envelope_level = 0;
+ m_envelope_mode = DECAY;
+
+ } else {
+ if(m_attack_speed & 0x80)
+ // normal mode
+ m_envelope_level = m_attack_level << 4;
+ else
+ // decay-like or timed mode
+ m_envelope_level = 0;
+ m_envelope_mode = ATTACK;
+ }
+}
+
+u8 swp00_device::envelope_block::status() const
+{
+ return m_envelope_mode == DECAY_DONE ? 0xff : (m_envelope_mode << 6) | (m_envelope_level >> 6);
+}
+
+bool swp00_device::envelope_block::active() const
+{
+ return m_envelope_mode != DECAY_DONE;
+}
+
+u16 swp00_device::envelope_block::step(u32 sample_counter)
+{
+ u16 result = m_envelope_level;
+ switch(m_envelope_mode) {
+ case ATTACK: {
+ if(m_attack_speed & 0x80) {
+ // normal mode
+ s32 level = m_envelope_level - swp00_device::interpolation_step((m_attack_speed & 0x7f) + ((m_envelope_level >> 7) << 2), sample_counter);
+ if(level <= 0) {
+ level = 0;
+ m_envelope_mode = DECAY;
+ }
+ m_envelope_level = level;
+ } else {
+ s32 level = m_envelope_level + swp00_device::interpolation_step(m_attack_speed, sample_counter);
+ if(m_attack_level) {
+ // decay-like mode
+ s32 limit = m_attack_level << 4;
+ if(level >= limit) {
+ m_envelope_mode = DECAY;
+ if(level >= 0xfff)
+ level = 0xfff;
+ }
+ } else {
+ // timed mode
+ if(level >= 0x800) {
+ level = 0;
+ m_envelope_mode = DECAY;
+ }
+ result = 0;
+ }
+ }
+ break;
+ }
+
+ case DECAY: {
+ u32 key = m_decay_speed >= 0xe0 ? m_decay_speed - 0x90 + ((m_envelope_level >> 7) << 2) : m_decay_speed & 0x7f;
+ s32 limit = m_decay_level << 4;
+ s32 level = m_envelope_level;
+ if(level < limit) {
+ level += swp00_device::interpolation_step(key, sample_counter);
+ if(level > limit) {
+ m_envelope_mode = DECAY_DONE;
+ if(level > 0xfff)
+ level = 0xfff;
+ }
+ } else if(level > limit) {
+ level -= swp00_device::interpolation_step(key, sample_counter);
+ if(level < limit) {
+ m_envelope_mode = DECAY_DONE;
+ if(level < 0)
+ level = 0;
+ }
+ } else
+ m_envelope_mode = DECAY_DONE;
+ m_envelope_level = level;
+ break;
+ }
+
+ case DECAY_DONE:
+ break;
+ }
+ return result;
+}
+
+void swp00_device::envelope_block::trigger_release()
+{
+ m_envelope_mode = DECAY;
+}
+
+u8 swp00_device::envelope_block::attack_speed_r() const
+{
+ return m_attack_speed;
+}
+
+void swp00_device::envelope_block::attack_speed_w(u8 data)
+{
+ if(m_envelope_mode == DECAY_DONE)
+ m_attack_speed = data;
+}
+
+u8 swp00_device::envelope_block::attack_level_r() const
+{
+ return m_attack_level;
+}
+
+void swp00_device::envelope_block::attack_level_w(u8 data)
+{
+ m_attack_level = data;
+}
+
+u8 swp00_device::envelope_block::decay_speed_r() const
+{
+ return m_decay_speed;
+}
+
+void swp00_device::envelope_block::decay_speed_w(u8 data)
+{
+ if(data & 0x80) {
+ m_decay_speed = data;
+ m_envelope_mode = DECAY;
+
+ } else if(m_envelope_mode == DECAY_DONE)
+ m_decay_speed = data;
+}
+
+u8 swp00_device::envelope_block::decay_level_r() const
+{
+ return m_decay_level;
+}
+
+void swp00_device::envelope_block::decay_level_w(u8 data)
+{
+ m_decay_level = data;
+}
+
+//--------------------------------------------------------------------------------
+
+// Filter block
+
+// 10000 ccccc * qqqq qkkk kkkk kkkk LPF Q and K coefficients
+// 10001 ccccc 0 wsss ssss Sweep, speed
+
+// This block takes samples from the streaming block and applies a
+// Chamberlin configuration low pass filter to them.
+
+// encoded q is fp 2.3 (with a 4 offset), k is fp 4.8 but the top
+// bit is an added 1 except when it's all zero.
+//
+// k = ((0x101 + k.m) << k.e) / (2**24)
+// q = ((0x10 - (q+4).m) << (4 - (q+4).e)) / (2**7)
+//
+// B(0) = L(0) = 0
+// H' = x0 - L - q*B
+// B' = B + k * H'
+// L' = L + k * B'
+// y0 = L'
+
+// The chip interpolates between consecutive values of k. When
+// updating the coefficients registers Q is updated immediatly and K
+// uses the interpolation block with the configured speed to reach the
+// new value.
+
+// When w=1 at keyon, the behaviour is different. The filter sweeps
+// from 800 to fff at the configured speed then jumps to the
+// programmed level. All subsequent changes are ignored until the
+// next keyon. The value of w is only checked on keyon.
+
+void swp00_device::filter_block::clear()
+{
+ m_info = 0x27ff; // Actual reset value unknown
+ m_speed = 0x00;
+ m_sweep = SWEEP_NONE;
+
+ m_k_target = 0x7ff;
+ m_k = 0xfff;
+ m_q = 0x80;
+
+ m_b = 0;
+ m_l = 0;
+}
+
+void swp00_device::filter_block::keyon()
+{
+ m_b = 0;
+ m_l = 0;
+
+ if(m_speed & 0x80) {
+ m_sweep = SWEEP_UP;
+ m_k = 0x800;
+ } else
+ m_k = m_k_target;
+}
+
+s32 swp00_device::filter_block::step(s16 input, s32 lmod, u32 sample_counter)
+{
+ s32 km = std::max(m_k - lmod, 0);
+ s64 k = (0x101 + (km & 0xff)) << (km >> 8);
+ s32 h = (input << 6) - m_l - ((s64(m_q) * m_b) >> 7);
+ m_b = m_b + ((k * h) >> 24);
+ m_l = m_l + ((k * m_b) >> 24);
+
+ switch(m_sweep) {
+ case SWEEP_NONE:
+ if(m_k < m_k_target) {
+ m_k += swp00_device::interpolation_step(m_speed & 0x7f, sample_counter);
+ if(m_k > m_k_target)
+ m_k = m_k_target;
+
+ } else if(m_k > m_k_target) {
+ m_k -= swp00_device::interpolation_step(m_speed & 0x7f, sample_counter);
+ if(m_k < m_k_target)
+ m_k = m_k_target;
+ }
+ break;
+ case SWEEP_UP:
+ m_k += swp00_device::interpolation_step(m_speed & 0x7f, sample_counter);
+ if(m_k >= 0xfff) {
+ m_k = m_k_target;
+ m_sweep = SWEEP_DONE;
+ }
+ break;
+ }
+
+ return m_l;
+}
+
+u8 swp00_device::filter_block::status() const
+{
+ return (m_sweep == SWEEP_DONE || (m_sweep == SWEEP_NONE && m_k == m_k_target) ? 0xc0 : 0x00) | ((m_k >> 6) ^ 0x3f);
+}
+
+template<int sel> void swp00_device::filter_block::info_w(u8 data)
+{
+ if(sel) {
+ m_info = (m_info & 0xff) | (data << 8);
+ s32 q = (m_info >> 11) + 4;
+ m_q = (0x10 - (q & 7)) << (4 - (q >> 3));
+ } else
+ m_info = (m_info & 0xff00) | data;
+
+ m_k_target = m_info & 0x7ff;
+ if(m_k_target)
+ m_k_target |= 0x800;
+}
+
+template<int sel> u8 swp00_device::filter_block::info_r()
+{
+ return m_info >> (8*sel);
+}
+
+void swp00_device::filter_block::speed_w(u8 data)
+{
+ m_speed = data;
+}
+
+u8 swp00_device::filter_block::speed_r()
+{
+ return m_speed;
+}
+
+//--------------------------------------------------------------------------------
+
+// LFO block
+
+// 10001 ccccc 1 llla aaaa LPF level, amplitude level
+// 10010 ccccc 0 dhss ssss Disable, shape, speed
+// 10010 ccccc 1 pppp pppp Pitch level
+
+// The LFO block is based on a 22-bits counter which is incremented on
+// each sample. It is then optionally shaped as a triangle, then
+// multiplied by the different levels to compute the impact. The LFO
+// output can be disabled, zeroing the output.
+
+// The increment is encoded in a variant of fp 3.3, when for e<7 the
+// value is (8+m) << e, and for e=7 (8+2*m) << e, giving a cycle time
+// of 1489 to 523288 cycles, or 0.08 to 30Hz.
+
+void swp00_device::lfo_block::clear()
+{
+ m_counter = 0;
+ m_lamod = 0;
+ m_fmod = 0;
+ m_speed = 0x80;
+}
+
+void swp00_device::lfo_block::keyon()
+{
+}
+
+std::tuple<u32, s32, s32> swp00_device::lfo_block::step()
+{
+ u32 e = (m_speed >> 3) & 7;
+ u32 step;
+ if(e < 7)
+ step = (8 | (m_speed & 7)) << e;
+ else
+ step = (8 + 2*(m_speed & 7)) << 7;
+
+ m_counter = (m_counter + step) & 0x3fffff;
+
+ u32 amod = 0;
+ s32 fmod = 0;
+ s32 lmod = 0;
+
+ if(!(m_speed & 0x80)) {
+ s32 shaped;
+ if(m_speed & 0x40)
+ shaped = ((m_counter << 1) & 0x3fffff) ^ (m_counter & 0x200000 ? 0x3fffff : 0);
+ else
+ shaped = m_counter;
+
+ amod = (shaped * (m_lamod & 0x1f)) >> 16;
+ fmod = ((shaped - 0x200000) * m_fmod) >> 21;
+ lmod = (shaped * (m_lamod & 0xe0)) >> 20;
+ }
+
+ return std::tie(amod, fmod, lmod);
+}
+
+void swp00_device::lfo_block::lamod_w(u8 data)
+{
+ m_lamod = data;
+}
+
+u8 swp00_device::lfo_block::lamod_r()
+{
+ return m_lamod;
+}
+
+void swp00_device::lfo_block::speed_w(u8 data)
+{
+ m_speed = data;
+}
+
+u8 swp00_device::lfo_block::speed_r()
+{
+ return m_speed;
+}
+
+void swp00_device::lfo_block::fmod_w(u8 data)
+{
+ m_fmod = data;
+}
+
+u8 swp00_device::lfo_block::fmod_r()
+{
+ return m_fmod;
+}
+
+
+//--------------------------------------------------------------------------------
+
+// Mixer block
+
+// 10101 ccccc 0 RRRR RRRR Reverb level
+// 10101 ccccc 1 DDDD DDDD Dry level
+// 10110 ccccc 0 CCCC CCCC Chorus level
+// 10110 ccccc 1 VVVV VVVV Variation level
+// 10111 ccccc 0 gggg gggg Global level
+// 10111 ccccc 1 llll rrrr Pan levels
+
+// The mixer block function is to to get the samples from the filter
+// and attenuate them as wanted given the envelope, tremolo, levels
+// and pans to create the inputs to the effects block. Those inputs
+// are three stereo streams (dry, chorus and variation) and one mono
+// stream (reverb).
+
+// An attenuation is a 12-bits floating-point 4.8 value which combines
+// additively. The 8-bits levels are left-shifted by 4, the 4-bits
+// (pan) by 6. A pan of 0xf has a special mapped value of 0xfff
+// (e.g. off).
+
+// Envelope and tremolo are given externally, this block manages all
+// the other volumes. The four levels dry, reverb, chorus and
+// variation are applied immediatly when changed. The global and pan
+// left/right levels are interpolated through time. When a new level
+// is written it becomes a new 12-bits target value through shifting,
+// and that value is reached by changing the current value by 1 on
+// each sample.
+
+// The current value of the interpolators and whether they reached
+// their target can be read out.
+
+void swp00_device::mixer_block::clear()
+{
+ m_cglo = m_cpanl = m_cpanr = 0xfff;
+ m_tglo = m_tpanl = m_tpanr = 0xfff;
+ m_glo = m_pan = m_dry = m_rev = m_cho = m_var = 0xff;
+}
+
+void swp00_device::mixer_block::keyon()
+{
+ m_cglo = m_tglo;
+ m_cpanl = m_tpanl;
+ m_cpanr = m_tpanr;
+}
+
+u8 swp00_device::mixer_block::status_glo() const
+{
+ return (m_cglo == m_tglo ? 0xc0 : 0x00) | (m_cglo >> 6);
+}
+
+u8 swp00_device::mixer_block::status_panl() const
+{
+ return (m_cpanl == m_tpanl ? 0xc0 : 0x00) | (m_cpanl >> 6);
+}
+
+u8 swp00_device::mixer_block::status_panr() const
+{
+ return (m_cpanr == m_tpanr ? 0xc0 : 0x00) | (m_cpanr >> 6);
+}
+
+void swp00_device::mixer_block::step(s32 sample, u16 envelope, u16 tremolo,
+ s32 &dry_l, s32 &dry_r, s32 &rev, s32 &cho_l, s32 &cho_r, s32 &var_l, s32 &var_r)
+{
+ u16 base = envelope + tremolo + m_cglo;
+ dry_l += volume_apply(base + (m_dry << 4) + m_cpanl, sample);
+ dry_r += volume_apply(base + (m_dry << 4) + m_cpanr, sample);
+ rev += volume_apply(base + (m_rev << 4), sample);
+ cho_l += volume_apply(base + (m_cho << 4) + m_cpanl, sample);
+ cho_r += volume_apply(base + (m_cho << 4) + m_cpanr, sample);
+ var_l += volume_apply(base + (m_var << 4) + m_cpanl, sample);
+ var_r += volume_apply(base + (m_var << 4) + m_cpanr, sample);
+
+ if(m_cglo < m_tglo)
+ m_cglo++;
+ else if(m_cglo > m_tglo)
+ m_cglo--;
+ if(m_cpanl < m_tpanl)
+ m_cpanl++;
+ else if(m_cpanl > m_tpanl)
+ m_cpanl--;
+ if(m_cpanr < m_tpanr)
+ m_cpanr++;
+ else if(m_cpanr > m_tpanr)
+ m_cpanr--;
+}
+
+s32 swp00_device::mixer_block::volume_apply(s32 level, s32 sample)
+{
+ // Level is 4.8 floating point positive, and represents an attenuation
+ // Sample is 16.6 signed and the result is in the same format
+
+ // Passed-in value may have overflowed
+ if(level >= 0xfff)
+ return 0;
+
+ s32 e = level >> 8;
+ s32 m = level & 0xff;
+ s64 mul = (0x1000000 - (m << 15)) >> e;
+ return (sample * mul) >> 24;
+}
+
+
+void swp00_device::mixer_block::glo_w(u8 data)
+{
+ m_glo = data;
+ m_tglo = data << 4;
+}
+
+void swp00_device::mixer_block::pan_w(u8 data)
+{
+ m_pan = data;
+ m_tpanl = (data << 2) & 0x3c0;
+ m_tpanr = (data << 6) & 0x3c0;
+ if(m_tpanl == 0x3c0)
+ m_tpanl = 0xfff;
+ if(m_tpanr == 0x3c0)
+ m_tpanr = 0xfff;
+}
+
+void swp00_device::mixer_block::dry_w(u8 data)
+{
+ m_dry = data;
+}
+
+void swp00_device::mixer_block::rev_w(u8 data)
+{
+ m_rev = data;
+}
+
+void swp00_device::mixer_block::cho_w(u8 data)
+{
+ m_cho = data;
+}
+
+void swp00_device::mixer_block::var_w(u8 data)
+{
+ m_var = data;
+}
+
+u8 swp00_device::mixer_block::glo_r()
+{
+ return m_glo;
+}
+
+u8 swp00_device::mixer_block::pan_r()
+{
+ return m_pan;
+}
+
+u8 swp00_device::mixer_block::dry_r()
+{
+ return m_dry;
+}
+
+u8 swp00_device::mixer_block::rev_r()
+{
+ return m_rev;
+}
+
+u8 swp00_device::mixer_block::cho_r()
+{
+ return m_cho;
+}
+
+u8 swp00_device::mixer_block::var_r()
+{
+ return m_var;
+}
+
+
+//--------------------------------------------------------------------------------
+
DEFINE_DEVICE_TYPE(SWP00, swp00_device, "swp00", "Yamaha SWP00 (TC170C120SF / XQ036A00) sound chip")
swp00_device::swp00_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
: device_t(mconfig, SWP00, tag, owner, clock),
device_sound_interface(mconfig, *this),
- device_rom_interface(mconfig, *this)
+ device_rom_interface(mconfig, *this),
+ m_require_sync(false)
{
}
-void swp00_device::device_add_mconfig(machine_config &config)
+void swp00_device::require_sync()
{
+ m_require_sync = true;
}
-const std::array<u32, 4> swp00_device::lfo_shape_centered_saw = { 0x00000000, 0x00000000, 0xfff00000, 0xfff00000 }; // --////--
-const std::array<u32, 4> swp00_device::lfo_shape_centered_tri = { 0x00000000, 0x0007ffff, 0xfff7ffff, 0xfff00000 }; // --/\/\--
-const std::array<u32, 4> swp00_device::lfo_shape_offset_saw = { 0x00000000, 0x00000000, 0x00000000, 0x00000000 }; // __////__
-const std::array<u32, 4> swp00_device::lfo_shape_offset_tri = { 0x00000000, 0x00000000, 0x000fffff, 0x000fffff }; // __/\/\__
-
-const std::array<s32, 16> swp00_device::panmap = {
- 0x000, 0x040, 0x080, 0x0c0,
- 0x100, 0x140, 0x180, 0x1c0,
- 0x200, 0x240, 0x280, 0x2c0,
- 0x300, 0x340, 0x380, 0xfff
-};
-
-const std::array<u8, 4> swp00_device::dpcm_offset = { 7, 6, 4, 0 };
-
bool swp00_device::istep(s32 &value, s32 limit, s32 step)
{
- // fprintf(stderr, "istep(%x, %x, %x)\n", value, limit, step);
if(value < limit) {
value += step;
if(value >= limit) {
@@ -196,40 +1134,55 @@ s32 swp00_device::lpffpapply(s32 value, s32 sample)
return ((((value >> 7) & 0x7fff) | 0x8000) * s64(sample)) >> (31 - (value >> 22));
}
-// Some tables we need. Maybe they're in roms inside the chip,
-// maybe they're logic. Probably slightly inexact too, would need
-// a complicated hardware setup to really test them.
-
-const std::array<s32, 0x80> swp00_device::attack_linear_step = {
- 0x00027, 0x0002b, 0x0002f, 0x00033, 0x00037, 0x0003d, 0x00042, 0x00048,
- 0x0004d, 0x00056, 0x0005e, 0x00066, 0x0006f, 0x0007a, 0x00085, 0x00090,
- 0x0009b, 0x000ac, 0x000bd, 0x000cc, 0x000de, 0x000f4, 0x00109, 0x00120,
- 0x00135, 0x00158, 0x00179, 0x00199, 0x001bc, 0x001e7, 0x00214, 0x00240,
- 0x0026b, 0x002af, 0x002f2, 0x00332, 0x00377, 0x003d0, 0x0042c, 0x00480,
- 0x004dc, 0x0055e, 0x005e9, 0x0066e, 0x006f4, 0x007a4, 0x00857, 0x0090b,
- 0x009c3, 0x00acb, 0x00bd6, 0x00ce6, 0x00e00, 0x00f5e, 0x010d2, 0x01234,
- 0x0139e, 0x015d0, 0x017f3, 0x01a20, 0x01c4a, 0x01f52, 0x02232, 0x0250f,
- 0x027ff, 0x02c72, 0x03109, 0x0338b, 0x039c4, 0x04038, 0x04648, 0x04c84,
- 0x05262, 0x05c1c, 0x065af, 0x06f5c, 0x07895, 0x0866f, 0x09470, 0x0a19e,
- 0x0ae4c, 0x0c566, 0x0db8d, 0x0f00f, 0x10625, 0x12937, 0x14954, 0x16c17,
- 0x1886e, 0x1c71c, 0x20000, 0x239e1, 0x2647c, 0x2aaab, 0x2ecfc, 0x3241f,
- 0x35e51, 0x3a83b, 0x40000, 0x4325c, 0x47dc1, 0x4c8f9, 0x50505, 0x55555,
- 0x58160, 0x5d174, 0x60606, 0x62b2e, 0x67b24, 0x6a63c, 0x6d3a0, 0x6eb3e,
- 0x71c72, 0x73616, 0x75075, 0x76b98, 0x78788, 0x78788, 0x7a44c, 0x7a44c,
- 0x7a44c, 0x7a44c, 0x7a44c, 0x7a44c, 0x7a44c, 0x7a44c, 0x7a44c, 0x7a44c,
-};
-
-const std::array<s32, 0x20> swp00_device::decay_linear_step = {
- 0x15083, 0x17ad2, 0x1a41a, 0x1cbe7, 0x1f16d, 0x22ef1, 0x26a44, 0x2a1e4,
- 0x2da35, 0x34034, 0x3a197, 0x40000, 0x45b82, 0x4b809, 0x51833, 0x57262,
- 0x5d9f7, 0x6483f, 0x6b15c, 0x71c72, 0x77976, 0x7d119, 0x83127, 0x88889,
- 0x8d3dd, 0x939a8, 0x991f2, 0x9d89e, 0xa0a0a, 0xa57eb, 0xa72f0, 0xac769,
-};
-
void swp00_device::device_start()
{
- m_stream = stream_alloc(0, 2, 44100);
-
+ m_stream = stream_alloc(0, 2, 44100, m_require_sync ? STREAM_SYNCHRONOUS : STREAM_DEFAULT_FLAGS);
+
+ save_item(STRUCT_MEMBER(m_streaming, m_phase));
+ save_item(STRUCT_MEMBER(m_streaming, m_start));
+ save_item(STRUCT_MEMBER(m_streaming, m_loop));
+ save_item(STRUCT_MEMBER(m_streaming, m_address));
+ save_item(STRUCT_MEMBER(m_streaming, m_pitch));
+ save_item(STRUCT_MEMBER(m_streaming, m_format));
+ save_item(STRUCT_MEMBER(m_streaming, m_pos));
+ save_item(STRUCT_MEMBER(m_streaming, m_pos_dec));
+ save_item(STRUCT_MEMBER(m_streaming, m_dpcm_s0));
+ save_item(STRUCT_MEMBER(m_streaming, m_dpcm_s1));
+ save_item(STRUCT_MEMBER(m_streaming, m_dpcm_pos));
+ save_item(STRUCT_MEMBER(m_streaming, m_dpcm_delta));
+ save_item(STRUCT_MEMBER(m_streaming, m_first));
+ save_item(STRUCT_MEMBER(m_streaming, m_done));
+ save_item(STRUCT_MEMBER(m_streaming, m_last));
+
+ save_item(STRUCT_MEMBER(m_envelope, m_attack_speed));
+ save_item(STRUCT_MEMBER(m_envelope, m_attack_level));
+ save_item(STRUCT_MEMBER(m_envelope, m_decay_speed));
+ save_item(STRUCT_MEMBER(m_envelope, m_decay_level));
+ save_item(STRUCT_MEMBER(m_envelope, m_envelope_level));
+ save_item(STRUCT_MEMBER(m_envelope, m_envelope_mode));
+
+ save_item(STRUCT_MEMBER(m_filter, m_q));
+ save_item(STRUCT_MEMBER(m_filter, m_b));
+ save_item(STRUCT_MEMBER(m_filter, m_l));
+ save_item(STRUCT_MEMBER(m_filter, m_k));
+ save_item(STRUCT_MEMBER(m_filter, m_k_target));
+ save_item(STRUCT_MEMBER(m_filter, m_info));
+ save_item(STRUCT_MEMBER(m_filter, m_speed));
+
+ save_item(STRUCT_MEMBER(m_mixer, m_cglo));
+ save_item(STRUCT_MEMBER(m_mixer, m_cpanl));
+ save_item(STRUCT_MEMBER(m_mixer, m_cpanr));
+ save_item(STRUCT_MEMBER(m_mixer, m_tglo));
+ save_item(STRUCT_MEMBER(m_mixer, m_tpanl));
+ save_item(STRUCT_MEMBER(m_mixer, m_tpanr));
+ save_item(STRUCT_MEMBER(m_mixer, m_glo));
+ save_item(STRUCT_MEMBER(m_mixer, m_pan));
+ save_item(STRUCT_MEMBER(m_mixer, m_dry));
+ save_item(STRUCT_MEMBER(m_mixer, m_rev));
+ save_item(STRUCT_MEMBER(m_mixer, m_cho));
+ save_item(STRUCT_MEMBER(m_mixer, m_var));
+
+ save_item(NAME(m_sample_counter));
save_item(NAME(m_waverom_access));
save_item(NAME(m_waverom_val));
save_item(NAME(m_meg_control));
@@ -300,76 +1253,22 @@ void swp00_device::device_start()
save_item(NAME(m_var_buffer));
save_item(NAME(m_offset));
save_item(NAME(m_const));
- save_item(NAME(m_lpf_info));
- save_item(NAME(m_lpf_speed));
- save_item(NAME(m_lfo_famod_depth));
- save_item(NAME(m_rev_level));
- save_item(NAME(m_dry_level));
- save_item(NAME(m_cho_level));
- save_item(NAME(m_var_level));
- save_item(NAME(m_glo_level));
- save_item(NAME(m_panning));
- save_item(NAME(m_attack_speed));
- save_item(NAME(m_attack_level));
- save_item(NAME(m_decay_speed));
- save_item(NAME(m_decay_level));
- save_item(NAME(m_pitch));
- save_item(NAME(m_sample_start));
- save_item(NAME(m_sample_end));
- save_item(NAME(m_sample_dpcm_and_format));
- save_item(NAME(m_sample_address));
- save_item(NAME(m_lfo_step));
- save_item(NAME(m_lfo_pmod_depth));
-
- save_item(NAME(m_lfo_phase));
- save_item(NAME(m_sample_pos));
- save_item(NAME(m_envelope_level));
-
- save_item(NAME(m_glo_level_cur));
- save_item(NAME(m_pan_l));
- save_item(NAME(m_pan_r));
-
- save_item(NAME(m_lpf_feedback));
- save_item(NAME(m_lpf_target_value));
- save_item(NAME(m_lpf_value));
- save_item(NAME(m_lpf_timer));
- save_item(NAME(m_lpf_ha));
- save_item(NAME(m_lpf_hb));
-
- save_item(NAME(m_active));
- save_item(NAME(m_decay));
- save_item(NAME(m_decay_done));
- save_item(NAME(m_lpf_done));
-
- save_item(NAME(m_dpcm_current));
- save_item(NAME(m_dpcm_next));
- save_item(NAME(m_dpcm_address));
- save_item(NAME(m_dpcm_sum));
-
- for(int i=0; i != 128; i++) {
- u32 v = 0;
- switch(i >> 3) {
- default: v = ((i & 7) + 8) << (1 + (i >> 3)); break;
- case 0xb: v = ((i & 7) + 4) << 13; break;
- case 0xc: v = ((i & 6) + 6) << 14; break;
- case 0xd: v = ((i & 4) + 7) << 15; break;
- case 0xe: v = 15 << 15; break;
- case 0xf: v = 31 << 15; break;
- }
- m_global_step[i] = v;
- }
-
- // Delta-packed samples decompression.
-
- for(int i=0; i<128; i++) {
- s16 base = ((i & 0x1f) << (3+(i >> 5))) + (((1 << (i >> 5))-1) << 8);
- m_dpcm[i | 0x80] = - base;
- m_dpcm[i] = + base;
- }
}
void swp00_device::device_reset()
{
+ for(auto &s : m_streaming)
+ s.clear();
+ for(auto &e : m_envelope)
+ e.clear();
+ for(auto &f : m_filter)
+ f.clear();
+ for(auto &l : m_lfo)
+ l.clear();
+ for(auto &m : m_mixer)
+ m.clear();
+
+ m_sample_counter = 0;
m_waverom_access = 0;
m_waverom_val = 0;
m_meg_control = 0;
@@ -436,51 +1335,6 @@ void swp00_device::device_reset()
std::fill(m_var_buffer.begin(), m_var_buffer.end(), 0);
std::fill(m_offset.begin(), m_offset.end(), 0);
std::fill(m_const.begin(), m_const.end(), 0);
- std::fill(m_lpf_info.begin(), m_lpf_info.end(), 0);
- std::fill(m_lpf_speed.begin(), m_lpf_speed.end(), 0);
- std::fill(m_lfo_famod_depth.begin(), m_lfo_famod_depth.end(), 0);
- std::fill(m_rev_level.begin(), m_rev_level.end(), 0);
- std::fill(m_dry_level.begin(), m_dry_level.end(), 0);
- std::fill(m_cho_level.begin(), m_cho_level.end(), 0);
- std::fill(m_var_level.begin(), m_var_level.end(), 0);
- std::fill(m_glo_level.begin(), m_glo_level.end(), 0);
- std::fill(m_panning.begin(), m_panning.end(), 0);
- std::fill(m_attack_speed.begin(), m_attack_speed.end(), 0);
- std::fill(m_attack_level.begin(), m_attack_level.end(), 0);
- std::fill(m_decay_speed.begin(), m_decay_speed.end(), 0);
- std::fill(m_decay_level.begin(), m_decay_level.end(), 0);
- std::fill(m_pitch.begin(), m_pitch.end(), 0);
- std::fill(m_sample_start.begin(), m_sample_start.end(), 0);
- std::fill(m_sample_end.begin(), m_sample_end.end(), 0);
- std::fill(m_sample_dpcm_and_format.begin(), m_sample_dpcm_and_format.end(), 0);
- std::fill(m_sample_address.begin(), m_sample_address.end(), 0);
- std::fill(m_lfo_step.begin(), m_lfo_step.end(), 0);
- std::fill(m_lfo_pmod_depth.begin(), m_lfo_pmod_depth.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_glo_level_cur.begin(), m_glo_level_cur.end(), 0);
- std::fill(m_pan_l.begin(), m_pan_l.end(), 0);
- std::fill(m_pan_r.begin(), m_pan_r.end(), 0);
-
- std::fill(m_lpf_feedback.begin(), m_lpf_feedback.end(), 0);
- std::fill(m_lpf_target_value.begin(), m_lpf_target_value.end(), 0);
- std::fill(m_lpf_value.begin(), m_lpf_value.end(), 0);
- std::fill(m_lpf_timer.begin(), m_lpf_timer.end(), 0);
- std::fill(m_lpf_ha.begin(), m_lpf_ha.end(), 0);
- std::fill(m_lpf_hb.begin(), m_lpf_hb.end(), 0);
-
- std::fill(m_active.begin(), m_active.end(), false);
- std::fill(m_decay.begin(), m_decay.end(), false);
- std::fill(m_decay_done.begin(), m_decay_done.end(), false);
- std::fill(m_lpf_done.begin(), m_lpf_done.end(), false);
-
- std::fill(m_dpcm_current.begin(), m_dpcm_current.end(), false);
- std::fill(m_dpcm_next.begin(), m_dpcm_next.end(), false);
- std::fill(m_dpcm_address.begin(), m_dpcm_address.end(), false);
- std::fill(m_dpcm_sum.begin(), m_dpcm_sum.end(), 0);
}
void swp00_device::rom_bank_pre_change()
@@ -494,10 +1348,10 @@ void swp00_device::map(address_map &map)
// 00-01: control
- rchan(map, 0x08).w(FUNC(swp00_device::slot8_w)); // always 80
- rchan(map, 0x09).w(FUNC(swp00_device::slot9_w)); // always 00
- rchan(map, 0x0a).rw(FUNC(swp00_device::sample_start_r<1>), FUNC(swp00_device::sample_start_w<1>));
- rchan(map, 0x0b).rw(FUNC(swp00_device::sample_start_r<0>), FUNC(swp00_device::sample_start_w<0>));
+ rchan(map, 0x08).rw(FUNC(swp00_device::phase_r<1>), FUNC(swp00_device::phase_w<1>));
+ rchan(map, 0x09).rw(FUNC(swp00_device::phase_r<0>), FUNC(swp00_device::phase_w<0>));
+ rchan(map, 0x0a).rw(FUNC(swp00_device::start_r<1>), FUNC(swp00_device::start_w<1>));
+ rchan(map, 0x0b).rw(FUNC(swp00_device::start_r<0>), FUNC(swp00_device::start_w<0>));
// 0c-0f: meg offsets
// 10-1b: meg values
@@ -505,27 +1359,27 @@ void swp00_device::map(address_map &map)
rchan(map, 0x20).rw(FUNC(swp00_device::lpf_info_r<1>), FUNC(swp00_device::lpf_info_w<1>));
rchan(map, 0x21).rw(FUNC(swp00_device::lpf_info_r<0>), FUNC(swp00_device::lpf_info_w<0>));
rchan(map, 0x22).rw(FUNC(swp00_device::lpf_speed_r), FUNC(swp00_device::lpf_speed_w));
- rchan(map, 0x23).rw(FUNC(swp00_device::lfo_famod_depth_r), FUNC(swp00_device::lfo_famod_depth_w));
- rchan(map, 0x24).rw(FUNC(swp00_device::lfo_step_r), FUNC(swp00_device::lfo_step_w));
- rchan(map, 0x25).rw(FUNC(swp00_device::lfo_pmod_depth_r), FUNC(swp00_device::lfo_pmod_depth_w));
+ rchan(map, 0x23).rw(FUNC(swp00_device::lfo_lamod_r), FUNC(swp00_device::lfo_lamod_w));
+ rchan(map, 0x24).rw(FUNC(swp00_device::lfo_speed_r), FUNC(swp00_device::lfo_speed_w));
+ rchan(map, 0x25).rw(FUNC(swp00_device::lfo_fmod_r), FUNC(swp00_device::lfo_fmod_w));
rchan(map, 0x26).rw(FUNC(swp00_device::attack_speed_r), FUNC(swp00_device::attack_speed_w));
rchan(map, 0x27).rw(FUNC(swp00_device::attack_level_r), FUNC(swp00_device::attack_level_w));
rchan(map, 0x28).rw(FUNC(swp00_device::decay_speed_r), FUNC(swp00_device::decay_speed_w));
rchan(map, 0x29).rw(FUNC(swp00_device::decay_level_r), FUNC(swp00_device::decay_level_w));
- rchan(map, 0x2a).rw(FUNC(swp00_device::rev_level_r), FUNC(swp00_device::rev_level_w));
- rchan(map, 0x2b).rw(FUNC(swp00_device::dry_level_r), FUNC(swp00_device::dry_level_w));
- rchan(map, 0x2c).rw(FUNC(swp00_device::cho_level_r), FUNC(swp00_device::cho_level_w));
- rchan(map, 0x2d).rw(FUNC(swp00_device::var_level_r), FUNC(swp00_device::var_level_w));
- rchan(map, 0x2e).rw(FUNC(swp00_device::glo_level_r), FUNC(swp00_device::glo_level_w));
- rchan(map, 0x2f).rw(FUNC(swp00_device::panning_r), FUNC(swp00_device::panning_w));
- rchan(map, 0x30).rw(FUNC(swp00_device::sample_dpcm_and_format_r), FUNC(swp00_device::sample_dpcm_and_format_w));
- rchan(map, 0x31).rw(FUNC(swp00_device::sample_address_r<2>), FUNC(swp00_device::sample_address_w<2>));
- rchan(map, 0x32).rw(FUNC(swp00_device::sample_address_r<1>), FUNC(swp00_device::sample_address_w<1>));
- rchan(map, 0x33).rw(FUNC(swp00_device::sample_address_r<0>), FUNC(swp00_device::sample_address_w<0>));
+ rchan(map, 0x2a).rw(FUNC(swp00_device::rev_r), FUNC(swp00_device::rev_w));
+ rchan(map, 0x2b).rw(FUNC(swp00_device::dry_r), FUNC(swp00_device::dry_w));
+ rchan(map, 0x2c).rw(FUNC(swp00_device::cho_r), FUNC(swp00_device::cho_w));
+ rchan(map, 0x2d).rw(FUNC(swp00_device::var_r), FUNC(swp00_device::var_w));
+ rchan(map, 0x2e).rw(FUNC(swp00_device::glo_r), FUNC(swp00_device::glo_w));
+ rchan(map, 0x2f).rw(FUNC(swp00_device::pan_r), FUNC(swp00_device::pan_w));
+ rchan(map, 0x30).rw(FUNC(swp00_device::format_r), FUNC(swp00_device::format_w));
+ rchan(map, 0x31).rw(FUNC(swp00_device::address_r<2>), FUNC(swp00_device::address_w<2>));
+ rchan(map, 0x32).rw(FUNC(swp00_device::address_r<1>), FUNC(swp00_device::address_w<1>));
+ rchan(map, 0x33).rw(FUNC(swp00_device::address_r<0>), FUNC(swp00_device::address_w<0>));
rchan(map, 0x34).rw(FUNC(swp00_device::pitch_r<1>), FUNC(swp00_device::pitch_w<1>));
rchan(map, 0x35).rw(FUNC(swp00_device::pitch_r<0>), FUNC(swp00_device::pitch_w<0>));
- rchan(map, 0x36).rw(FUNC(swp00_device::sample_end_r<1>), FUNC(swp00_device::sample_end_w<1>));
- rchan(map, 0x37).rw(FUNC(swp00_device::sample_end_r<0>), FUNC(swp00_device::sample_end_w<0>));
+ rchan(map, 0x36).rw(FUNC(swp00_device::loop_r<1>), FUNC(swp00_device::loop_w<1>));
+ rchan(map, 0x37).rw(FUNC(swp00_device::loop_r<0>), FUNC(swp00_device::loop_w<0>));
rctrl(map, 0x00); // 01 at startup
rctrl(map, 0x01).rw(FUNC(swp00_device::state_r), FUNC(swp00_device::state_adr_w));
@@ -546,393 +1400,246 @@ void swp00_device::map(address_map &map)
// Voice control
-
-void swp00_device::slot8_w(offs_t offset, u8 data)
+template<int sel> void swp00_device::lpf_info_w(offs_t offset, u8 data)
{
- if(data == 0x80)
- return;
- logerror("slot8[%02x] = %02x\n", offset >> 1, data);
+ m_stream->update();
+ m_filter[offset >> 1].info_w<sel>(data);
}
-void swp00_device::slot9_w(offs_t offset, u8 data)
+template<int sel> u8 swp00_device::lpf_info_r(offs_t offset)
{
- if(data == 0x00)
- return;
- logerror("slot9[%02x] = %02x\n", offset >> 1, data);
+ return m_filter[offset >> 1].info_r<sel>();
}
-template<int sel> void swp00_device::lpf_info_w(offs_t offset, u8 data)
+void swp00_device::lpf_speed_w(offs_t offset, u8 data)
{
- int chan = offset >> 1;
- u16 old = m_lpf_info[chan];
m_stream->update();
+ m_filter[offset >> 1].speed_w(data);
+}
- m_lpf_info[chan] = (m_lpf_info[chan] & ~(0xff << (8*sel))) | (data << (8*sel));
- if(m_lpf_info[chan] == old)
- return;
-
- // if(!sel)
- // logerror("lpf_info[%02x] = %04x\n", chan, m_lpf_info[chan]);
+u8 swp00_device::lpf_speed_r(offs_t offset)
+{
+ return m_filter[offset >> 1].speed_r();
+}
- u32 fb = m_lpf_info[chan] >> 11;
- u32 level = m_lpf_info[chan] & 0x7ff;
- if(fb < 4 && level > 0x7c0)
- level = 0x7c0;
- if(level)
- level |= 0x800;
- m_lpf_feedback[chan] = (fb + 4) << 21;
- m_lpf_target_value[chan] = level << 14;
+void swp00_device::lfo_lamod_w(offs_t offset, u8 data)
+{
+ m_stream->update();
+ m_lfo[offset >> 1].lamod_w(data);
}
-template<int sel> u8 swp00_device::lpf_info_r(offs_t offset)
+u8 swp00_device::lfo_lamod_r(offs_t offset)
{
- int chan = offset >> 1;
- return m_lpf_info[chan] >> (8*sel);
+ return m_lfo[offset >> 1].lamod_r();
}
-void swp00_device::lpf_speed_w(offs_t offset, u8 data)
+void swp00_device::lfo_speed_w(offs_t offset, u8 data)
{
- int chan = offset >> 1;
- if(m_lpf_speed[chan] == data)
- return;
m_stream->update();
- m_lpf_speed[chan] = data;
- // logerror("lpf_speed[%02x] = %02x\n", chan, m_lpf_speed[chan]);
+ m_lfo[offset >> 1].speed_w(data);
}
-u8 swp00_device::lpf_speed_r(offs_t offset)
+u8 swp00_device::lfo_speed_r(offs_t offset)
{
- int chan = offset >> 1;
- return m_lpf_speed[chan];
+ return m_lfo[offset >> 1].speed_r();
}
-void swp00_device::lfo_famod_depth_w(offs_t offset, u8 data)
+void swp00_device::lfo_fmod_w(offs_t offset, u8 data)
{
- int chan = offset >> 1;
- if(m_lfo_famod_depth[chan] == data)
- return;
m_stream->update();
- m_lfo_famod_depth[chan] = data;
- // logerror("lfo_famod_depth[%02x] = %02x\n", chan, m_lfo_famod_depth[chan]);
+ m_lfo[offset >> 1].fmod_w(data);
}
-u8 swp00_device::lfo_famod_depth_r(offs_t offset)
+u8 swp00_device::lfo_fmod_r(offs_t offset)
{
- int chan = offset >> 1;
- return m_lfo_famod_depth[chan];
+ return m_lfo[offset >> 1].fmod_r();
}
-void swp00_device::rev_level_w(offs_t offset, u8 data)
+void swp00_device::glo_w(offs_t offset, u8 data)
{
- int chan = offset >> 1;
- if(m_rev_level[chan] == data)
- return;
m_stream->update();
- m_rev_level[chan] = data;
- // logerror("rev_level[%02x] = %02x\n", chan, m_rev_level[chan]);
+ m_mixer[offset >> 1].glo_w(data);
}
-u8 swp00_device::rev_level_r(offs_t offset)
+u8 swp00_device::glo_r(offs_t offset)
{
- int chan = offset >> 1;
- return m_rev_level[chan];
+ return m_mixer[offset >> 1].glo_r();
}
-void swp00_device::dry_level_w(offs_t offset, u8 data)
+void swp00_device::pan_w(offs_t offset, u8 data)
{
- int chan = offset >> 1;
- if(m_dry_level[chan] == data)
- return;
m_stream->update();
- m_dry_level[chan] = data;
- // logerror("dry_level[%02x] = %02x\n", chan, m_dry_level[chan]);
+ m_mixer[offset >> 1].pan_w(data);
}
-u8 swp00_device::dry_level_r(offs_t offset)
+u8 swp00_device::pan_r(offs_t offset)
{
- int chan = offset >> 1;
- return m_dry_level[chan];
+ return m_mixer[offset >> 1].pan_r();
}
-void swp00_device::cho_level_w(offs_t offset, u8 data)
+void swp00_device::dry_w(offs_t offset, u8 data)
{
- int chan = offset >> 1;
- if(m_cho_level[chan] == data)
- return;
m_stream->update();
- m_cho_level[chan] = data;
- // logerror("cho_level[%02x] = %02x\n", chan, m_cho_level[chan]);
+ m_mixer[offset >> 1].dry_w(data);
}
-u8 swp00_device::cho_level_r(offs_t offset)
+u8 swp00_device::dry_r(offs_t offset)
{
- int chan = offset >> 1;
- return m_cho_level[chan];
+ return m_mixer[offset >> 1].dry_r();
}
-void swp00_device::var_level_w(offs_t offset, u8 data)
+void swp00_device::rev_w(offs_t offset, u8 data)
{
- int chan = offset >> 1;
- if(m_var_level[chan] == data)
- return;
m_stream->update();
- m_var_level[chan] = data;
- // logerror("var_level[%02x] = %02x\n", chan, m_var_level[chan]);
+ m_mixer[offset >> 1].rev_w(data);
}
-u8 swp00_device::var_level_r(offs_t offset)
+u8 swp00_device::rev_r(offs_t offset)
{
- int chan = offset >> 1;
- return m_var_level[chan];
+ return m_mixer[offset >> 1].rev_r();
}
-void swp00_device::glo_level_w(offs_t offset, u8 data)
+void swp00_device::cho_w(offs_t offset, u8 data)
{
- int chan = offset >> 1;
- if(m_glo_level[chan] == data)
- return;
- m_glo_level[chan] = data;
- // logerror("glo_level[%02x] = %02x\n", chan, m_glo_level[chan]);
+ m_stream->update();
+ m_mixer[offset >> 1].cho_w(data);
}
-u8 swp00_device::glo_level_r(offs_t offset)
+u8 swp00_device::cho_r(offs_t offset)
{
- int chan = offset >> 1;
- return m_glo_level[chan];
+ return m_mixer[offset >> 1].cho_r();
}
-
-void swp00_device::panning_w(offs_t offset, u8 data)
+void swp00_device::var_w(offs_t offset, u8 data)
{
- int chan = offset >> 1;
- if(m_panning[chan] == data)
- return;
m_stream->update();
- m_panning[chan] = data;
- // logerror("panning[%02x] = %02x\n", chan, m_panning[chan]);
+ m_mixer[offset >> 1].var_w(data);
}
-u8 swp00_device::panning_r(offs_t offset)
+u8 swp00_device::var_r(offs_t offset)
{
- int chan = offset >> 1;
- return m_panning[chan];
+ return m_mixer[offset >> 1].var_r();
}
void swp00_device::attack_speed_w(offs_t offset, u8 data)
{
- int chan = offset >> 1;
- if(m_attack_speed[chan] == data)
- return;
m_stream->update();
- m_attack_speed[chan] = data;
- logerror("attack_speed[%02x] = %02x\n", chan, m_attack_speed[chan]);
+ m_envelope[offset >> 1].attack_speed_w(data);
}
u8 swp00_device::attack_speed_r(offs_t offset)
{
- int chan = offset >> 1;
- return m_attack_speed[chan];
+ return m_envelope[offset >> 1].attack_speed_r();
}
void swp00_device::attack_level_w(offs_t offset, u8 data)
{
- int chan = offset >> 1;
- if(m_attack_level[chan] == data)
- return;
m_stream->update();
- m_attack_level[chan] = data;
- logerror("attack_level[%02x] = %02x\n", chan, m_attack_level[chan]);
+ m_envelope[offset >> 1].attack_level_w(data);
}
u8 swp00_device::attack_level_r(offs_t offset)
{
- int chan = offset >> 1;
- return m_attack_level[chan];
+ return m_envelope[offset >> 1].attack_level_r();
}
void swp00_device::decay_speed_w(offs_t offset, u8 data)
{
- int chan = offset >> 1;
- if(m_decay_speed[chan] == data)
- return;
-
m_stream->update();
- m_decay_speed[chan] = data;
-
- if(data & 0x80)
- m_decay[chan] = true;
-
- logerror("decay_speed[%02x] = %02x\n", chan, m_decay_speed[chan]);
+ m_envelope[offset >> 1].decay_speed_w(data);
}
u8 swp00_device::decay_speed_r(offs_t offset)
{
- int chan = offset >> 1;
- return m_decay_speed[chan];
+ return m_envelope[offset >> 1].decay_speed_r();
}
void swp00_device::decay_level_w(offs_t offset, u8 data)
{
- int chan = offset >> 1;
- if(m_decay_level[chan] == data)
- return;
m_stream->update();
- m_decay_level[chan] = data;
- logerror("decay_level[%02x] = %02x\n", chan, m_decay_level[chan]);
+ m_envelope[offset >> 1].decay_level_w(data);
}
u8 swp00_device::decay_level_r(offs_t offset)
{
- int chan = offset >> 1;
- return m_decay_level[chan];
+ return m_envelope[offset >> 1].decay_level_r();
}
template<int sel> void swp00_device::pitch_w(offs_t offset, u8 data)
{
- int chan = offset >> 1;
- u16 old = m_pitch[chan];
m_stream->update();
- m_pitch[chan] = (m_pitch[chan] & ~(0xff << (8*sel))) | (data << (8*sel));
- if(m_pitch[chan] == old)
- return;
- // if(!sel)
- // logerror("pitch[%02x] = %04x\n", chan, m_pitch[chan]);
+ m_streaming[offset >> 1].pitch_w<sel>(data);
}
template<int sel> u8 swp00_device::pitch_r(offs_t offset)
{
- int chan = offset >> 1;
- return m_pitch[chan] >> (8*sel);
-}
-
-template<int sel> void swp00_device::sample_start_w(offs_t offset, u8 data)
-{
- int chan = offset >> 1;
- m_stream->update();
-
- m_sample_start[chan] = (m_sample_start[chan] & ~(0xff << (8*sel))) | (data << (8*sel));
- // if(!sel)
- // logerror("sample_start[%02x] = %04x\n", chan, m_sample_start[chan]);
-}
-
-template<int sel> u8 swp00_device::sample_start_r(offs_t offset)
-{
- int chan = offset >> 1;
- return m_sample_start[chan] >> (8*sel);
+ return m_streaming[offset >> 1].pitch_r<sel>();
}
-template<int sel> void swp00_device::sample_end_w(offs_t offset, u8 data)
+template<int sel> void swp00_device::start_w(offs_t offset, u8 data)
{
- int chan = offset >> 1;
m_stream->update();
-
- m_sample_end[chan] = (m_sample_end[chan] & ~(0xff << (8*sel))) | (data << (8*sel));
- // if(!sel)
- // logerror("sample_end[%02x] = %04x\n", chan, m_sample_end[chan]);
+ m_streaming[offset >> 1].start_w<sel>(data);
}
-template<int sel> u8 swp00_device::sample_end_r(offs_t offset)
+template<int sel> u8 swp00_device::start_r(offs_t offset)
{
- int chan = offset >> 1;
- return m_sample_end[chan] >> (8*sel);
+ return m_streaming[offset >> 1].start_r<sel>();
}
-void swp00_device::sample_dpcm_and_format_w(offs_t offset, u8 data)
+template<int sel> void swp00_device::phase_w(offs_t offset, u8 data)
{
- int chan = offset >> 1;
m_stream->update();
-
- m_sample_dpcm_and_format[chan] = data;
- // logerror("sample_dpcm_and_format[%02x] = %02x\n", chan, m_sample_dpcm_and_format[chan]);
+ m_streaming[offset >> 1].phase_w<sel>(data);
}
-u8 swp00_device::sample_dpcm_and_format_r(offs_t offset)
+template<int sel> u8 swp00_device::phase_r(offs_t offset)
{
- int chan = offset >> 1;
- return m_sample_dpcm_and_format[chan];
+ return m_streaming[offset >> 1].phase_r<sel>();
}
-template<int sel> void swp00_device::sample_address_w(offs_t offset, u8 data)
+template<int sel> void swp00_device::loop_w(offs_t offset, u8 data)
{
- int chan = offset >> 1;
m_stream->update();
-
- m_sample_address[chan] = (m_sample_address[chan] & ~(0xff << (8*sel))) | (data << (8*sel));
- // if(!sel)
- // logerror("sample_address[%02x] = %04x\n", chan, m_sample_address[chan]);
+ m_streaming[offset >> 1].loop_w<sel>(data);
}
-template<int sel> u8 swp00_device::sample_address_r(offs_t offset)
+template<int sel> u8 swp00_device::loop_r(offs_t offset)
{
- int chan = offset >> 1;
- return m_sample_address[chan] >> (8*sel);
+ return m_streaming[offset >> 1].loop_r<sel>();
}
-void swp00_device::lfo_step_w(offs_t offset, u8 data)
+void swp00_device::format_w(offs_t offset, u8 data)
{
- int chan = offset >> 1;
- if(m_lfo_step[chan] == data)
- return;
m_stream->update();
-
- m_lfo_step[chan] = data;
- // logerror("lfo_step[%02x] = %02x\n", chan, m_lfo_step[chan]);
+ m_streaming[offset >> 1].format_w(data);
}
-u8 swp00_device::lfo_step_r(offs_t offset)
+u8 swp00_device::format_r(offs_t offset)
{
- int chan = offset >> 1;
- return m_lfo_step[chan];
+ return m_streaming[offset >> 1].format_r();
}
-void swp00_device::lfo_pmod_depth_w(offs_t offset, u8 data)
+template<int sel> void swp00_device::address_w(offs_t offset, u8 data)
{
- int chan = offset >> 1;
- if(m_lfo_pmod_depth[chan] == data)
- return;
m_stream->update();
-
- m_lfo_pmod_depth[chan] = data;
- // logerror("lfo_pmod_depth[%02x] = %02x\n", chan, m_lfo_pmod_depth[chan]);
+ m_streaming[offset >> 1].address_w<sel>(data);
}
-u8 swp00_device::lfo_pmod_depth_r(offs_t offset)
+template<int sel> u8 swp00_device::address_r(offs_t offset)
{
- int chan = offset >> 1;
- return m_lfo_pmod_depth[chan];
+ return m_streaming[offset >> 1].address_r<sel>();
}
void swp00_device::keyon(int chan)
{
m_stream->update();
- logerror("keyon %02x a=%02x/%02x d=%02x/%02x glo=%02x pan=%02x [%x %x %x %x]\n", chan, m_attack_speed[chan], m_attack_level[chan], m_decay_speed[chan], m_decay_level[chan], m_glo_level[chan], m_panning[chan], m_sample_start[chan], m_sample_end[chan], m_sample_address[chan], m_sample_dpcm_and_format[chan]);
- m_lfo_phase[chan] = 0;
- m_sample_pos[chan] = -m_sample_start[chan] << 15;
-
- m_active[chan] = true;
- m_decay[chan] = false;
- m_decay_done[chan] = false;
-
- m_dpcm_current[chan] = 0;
- m_dpcm_next[chan] = 0;
- m_dpcm_address[chan] = m_sample_address[chan] - m_sample_start[chan];
- m_dpcm_sum[chan] = 0;
-
- m_lpf_value[chan] = m_lpf_target_value[chan];
- m_lpf_timer[chan] = 0x4000000;
- m_lpf_ha[chan] = 0;
- m_lpf_hb[chan] = 0;
-
- m_glo_level_cur[chan] = m_glo_level[chan] << 4;
- m_pan_l[chan] = panmap[m_panning[chan] >> 4];
- m_pan_r[chan] = panmap[m_panning[chan] & 15];
-
- if(m_decay_speed[chan] & 0x80) {
- m_envelope_level[chan] = 0;
- m_decay[chan] = true;
- } else if((m_attack_speed[chan] & 0x80) || m_attack_level[chan])
- m_envelope_level[chan] = m_attack_level[chan] << 20;
- else
- m_envelope_level[chan] = 0x8000000;
+
+ logerror("keyon %02x: %s\n", chan, m_streaming[chan].describe());
+ m_streaming[chan].keyon();
+ m_envelope [chan].keyon();
+ m_filter [chan].keyon();
+ m_lfo [chan].keyon();
+ m_mixer [chan].keyon();
}
template<int sel> void swp00_device::keyon_w(u8 data)
@@ -996,10 +1703,7 @@ u8 swp00_device::waverom_access_r()
u8 swp00_device::waverom_val_r()
{
- u8 val = read_byte(m_sample_address[0x1f]);
- logerror("waverom read adr=%08x -> %02x\n", m_sample_address[0x1f], val);
- m_sample_address[0x1f] = (m_sample_address[0x1f] + 1) & 0xffffff;
- return val;
+ return read_byte(m_streaming[0x1f].sample_address_get());
}
void swp00_device::meg_control_w(u8 data)
@@ -1019,39 +1723,29 @@ u8 swp00_device::state_r()
m_stream->update();
int chan = m_state_adr & 0x1f;
- switch(m_state_adr & 0xe0) {
- case 0x00: // lpf value
- return (m_lpf_value[chan] >> 20) | (m_lpf_done[chan] ? 0x80 : 0x00);
+ switch(m_state_adr >> 5) {
+ case 0: // lpf k
+ return m_filter[chan].status();
- case 0x40: { // Envelope state
- if(!m_active[chan])
- return 0xff;
+ case 1: // sample counter
+ return 0xc0 | (m_sample_counter >> 13);
- u8 vol;
- if(m_decay[chan] || m_attack_level[chan] || (m_attack_speed[chan] & 0x80))
- vol = m_envelope_level[chan] >> 22;
- else
- vol = 0;
+ case 2: // Envelope state
+ return m_envelope[chan].status();
- if(m_decay_done[chan])
- vol |= 0x40;
- if(m_decay[chan])
- vol |= 0x80;
+ case 3: // global level
+ return m_mixer[chan].status_glo();
- return vol;
+ case 4: // panning l
+ return m_mixer[chan].status_panl();
+
+ case 5: // panning r
+ return m_mixer[chan].status_panr();
}
- case 0x60: // global level
- return (m_glo_level_cur[chan] >> 6) | ((m_glo_level_cur[chan] == (m_glo_level[chan] << 4)) ? 0x80 : 0x00);
-
- case 0x80: // panning l
- return (m_pan_l[chan] >> 6) | ((m_pan_l[chan] == panmap[m_panning[chan] >> 4]) ? 0x80 : 0x00);
+ // 6 and 7 are semi-random values between c0 and ff with a lot of c0 and ff
- case 0xa0: // panning r
- return (m_pan_r[chan] >> 6) | ((m_pan_r[chan] == panmap[m_panning[chan] & 15]) ? 0x80 : 0x00);
- }
-
- logerror("state %02x unsupported\n");
+ logerror("state %d unsupported\n", m_state_adr >> 5);
return 0;
}
@@ -1141,7 +1835,6 @@ template<size_t size> s32 swp00_device::delay_block<size>::rlfo(int offreg, u32
s32 val0 = m_buffer[pos & (size - 1)];
s32 val1 = m_buffer[(pos + 1) & (size - 1)];
- // fprintf(stderr, "lfo %02x %x %x\n", offreg, val0, val1);
return s32((val1 * s64(lfo_i_frac) + val0 * s64(0x400000 - lfo_i_frac)) >> 22);
}
@@ -1156,7 +1849,6 @@ template<size_t size> s32 swp00_device::delay_block<size>::rlfo2(int offreg, s32
s32 val0 = m_buffer[pos & (size - 1)];
s32 val1 = m_buffer[(pos + 1) & (size - 1)];
- // fprintf(stderr, "lfo %02x %x %x\n", offreg, val0, val1);
return s32((val1 * s64(lfo_i_frac) + val0 * s64(0x800 - lfo_i_frac)) >> 11);
}
@@ -1254,11 +1946,6 @@ s32 swp00_device::saturate(s32 value)
return value;
}
-double v2f2(s32 value)
-{
- return (1.0 - (value & 0xffffff) / 33554432.0) / (1 << (value >> 24));
-}
-
void swp00_device::sound_stream_update(sound_stream &stream)
{
const delay_block brev(this, m_rev_buffer);
@@ -1272,162 +1959,20 @@ void swp00_device::sound_stream_update(sound_stream &stream)
s32 var_l = 0, var_r = 0;
for(int chan = 0; chan != 32; chan++) {
- if(!m_active[chan])
- continue;
+ auto [amod, fmod, lmod] = m_lfo[chan].step();
- u32 lfo_phase = m_lfo_phase[chan] >> 7;
- s32 lfo_p_phase = lfo_phase ^ (m_lfo_step[chan] & 0x40 ? lfo_shape_centered_tri : lfo_shape_centered_saw)[lfo_phase >> 18];
- s32 lfo_fa_phase = lfo_phase ^ (m_lfo_step[chan] & 0x40 ? lfo_shape_offset_tri : lfo_shape_offset_saw )[lfo_phase >> 18];
-
- s16 val0, val1;
- u32 base_address = m_sample_address[chan];
- s32 spos = m_sample_pos[chan] >> 15;
- switch(m_sample_dpcm_and_format[chan] >> 6) {
- case 0: { // 16-bits linear
- offs_t adr = base_address + (spos << 1);
- val0 = read_word(adr);
- val1 = read_word(adr+2);
- break;
- }
-
- case 1: { // 12-bits linear
- offs_t adr = base_address + (spos >> 2)*6;
- switch(spos & 3) {
- case 0: { // Cabc ..AB .... ....
- u16 w0 = read_word(adr);
- u16 w1 = read_word(adr+2);
- val0 = (w0 & 0x0fff) << 4;
- val1 = ((w0 & 0xf000) >> 8) | ((w1 & 0x00ff) << 8);
- break;
- }
- case 1: { // c... BCab ...A ....
- u16 w0 = read_word(adr);
- u16 w1 = read_word(adr+2);
- u16 w2 = read_word(adr+4);
- val0 = ((w0 & 0xf000) >> 8) | ((w1 & 0x00ff) << 8);
- val1 = ((w1 & 0xff00) >> 4) | ((w2 & 0x000f) << 12);
- break;
- }
- case 2: { // .... bc.. ABCa ....
- u16 w1 = read_word(adr+2);
- u16 w2 = read_word(adr+4);
- val0 = ((w1 & 0xff00) >> 4) | ((w2 & 0x000f) << 12);
- val1 = w2 & 0xfff0;
- break;
- }
- case 3: { // .... .... abc. .ABC
- u16 w2 = read_word(adr+4);
- u16 w3 = read_word(adr+6);
- val0 = w2 & 0xfff0;
- val1 = (w3 & 0x0fff) << 4;
- break;
- }
- }
- break;
- }
-
- case 2: // 8-bits linear
- val0 = (read_byte(base_address + spos) << 8);
- val1 = (read_byte(base_address + spos + 1) << 8);
- break;
-
- case 3: { // 8-bits delta-pcm
- u8 offset = dpcm_offset[m_sample_dpcm_and_format[chan] & 3];
- u8 scale = (m_sample_dpcm_and_format[chan] >> 2) & 7;
- u32 target_address = base_address + spos + 1;
- while(m_dpcm_address[chan] <= target_address) {
- m_dpcm_current[chan] = m_dpcm_next[chan];
- m_dpcm_sum[chan] += m_dpcm[read_byte(m_dpcm_address[chan])] - offset;
- s32 sample = (m_dpcm_sum[chan] << scale) >> 3;
- m_dpcm_address[chan] ++;
- if(sample < -0x8000)
- sample = -0x8000;
- else if(sample > 0x7fff)
- sample = 0x7fff;
- m_dpcm_next[chan] = sample;
- }
- val0 = m_dpcm_current[chan];
- val1 = m_dpcm_next[chan];
- break;
- }
- }
-
- s32 mul = m_sample_pos[chan] & 0x7fff;
- s32 sample = (val1 * mul + val0 * (0x8000 - mul)) >> 7;
-
- s32 lpf_value = m_lpf_value[chan] + ((lfo_fa_phase * (m_lfo_famod_depth[chan] >> 5)) << (m_lfo_step[chan] & 0x40 ? 2 : 1));
-
- m_lpf_ha[chan] += lpffpapply(lpf_value, sample - 2*fpapply(m_lpf_feedback[chan], m_lpf_ha[chan]) - m_lpf_hb[chan]);
- m_lpf_hb[chan] += lpffpapply(lpf_value, m_lpf_ha[chan]);
-
- sample = m_lpf_hb[chan];
-
- s32 envelope_level;
- if(m_decay[chan] || m_attack_level[chan] || (m_attack_speed[chan] & 0x80))
- envelope_level = m_envelope_level[chan];
- else
- envelope_level = 0;
-
- s32 tremolo_level = (lfo_fa_phase * (m_lfo_famod_depth[chan] & 0x1f)) << ((m_lfo_step[chan] & 0x40) ? 3 : 2);
-
- dry_l += fpapply(envelope_level + (m_glo_level_cur[chan] << 16) + tremolo_level + (m_dry_level[chan] << 20) + (m_pan_l[chan] << 16), sample);
- dry_r += fpapply(envelope_level + (m_glo_level_cur[chan] << 16) + tremolo_level + (m_dry_level[chan] << 20) + (m_pan_r[chan] << 16), sample);
- rev += fpapply(envelope_level + (m_glo_level_cur[chan] << 16) + tremolo_level + (m_rev_level[chan] << 20), sample);
- cho_l += fpapply(envelope_level + (m_glo_level_cur[chan] << 16) + tremolo_level + (m_cho_level[chan] << 20) + (m_pan_l[chan] << 16), sample);
- cho_r += fpapply(envelope_level + (m_glo_level_cur[chan] << 16) + tremolo_level + (m_cho_level[chan] << 20) + (m_pan_r[chan] << 16), sample);
- var_l += fpapply(envelope_level + (m_glo_level_cur[chan] << 16) + tremolo_level + (m_var_level[chan] << 20) + (m_pan_l[chan] << 16), sample);
- var_r += fpapply(envelope_level + (m_glo_level_cur[chan] << 16) + tremolo_level + (m_var_level[chan] << 20) + (m_pan_r[chan] << 16), sample);
-
- m_lfo_phase[chan] = (m_lfo_phase[chan] + m_global_step[0x20 + (m_lfo_step[chan] & 0x3f)]) & 0x7ffffff;
-
- u32 sample_increment = ((m_pitch[chan] & 0xfff) << (8 + (s16(m_pitch[chan]) >> 12))) >> 4;
- m_sample_pos[chan] += (sample_increment * (0x800 + ((lfo_p_phase * m_lfo_pmod_depth[chan]) >> (m_lfo_step[chan] & 0x40 ? 18 : 19)))) >> 11;
+ if(!m_envelope[chan].active())
+ continue;
- if((m_sample_pos[chan] >> 15) >= m_sample_end[chan]) {
- if(!m_sample_end[chan])
- m_active[chan] = false;
- else {
- s32 prev = m_sample_pos[chan];
- do
- m_sample_pos[chan] -= m_sample_end[chan] << 15;
- while((m_sample_pos[chan] >> 15) >= m_sample_end[chan]);
- m_dpcm_address[chan] += (m_sample_pos[chan] >> 15) - (prev >> 15);
- m_dpcm_sum[chan] = 0;
- }
- }
+ auto [sample1, trigger_release] = m_streaming[chan].step(m_rom_cache, fmod);
+ if(trigger_release)
+ m_envelope[chan].trigger_release();
- if(m_lpf_speed[chan] & 0x80)
- m_lpf_done[chan] = istep(m_lpf_timer[chan], 0, m_global_step[m_lpf_speed[chan] & 0x7f] >> 1);
- else
- m_lpf_done[chan] = istep(m_lpf_value[chan], m_lpf_target_value[chan], m_global_step[m_lpf_speed[chan]] >> 1);
-
- istep(m_glo_level_cur[chan], m_glo_level[chan] << 4, 1);
- istep(m_pan_l[chan], panmap[m_panning[chan] >> 4], 1);
- istep(m_pan_r[chan], panmap[m_panning[chan] & 15], 1);
-
- if(m_decay[chan]) {
- if((m_decay_speed[chan] & 0x60) == 0x60)
- m_decay_done[chan] = fpstep(m_envelope_level[chan], m_decay_level[chan] << 20, decay_linear_step[m_decay_speed[chan] & 0x1f]);
- else
- m_decay_done[chan] = istep(m_envelope_level[chan], m_decay_level[chan] << 20, m_global_step[m_decay_speed[chan] & 0x7f] << 1);
- if(m_envelope_level[chan] & 0x8000000)
- m_active[chan] = false;
-
- } else if(m_attack_speed[chan] & 0x80)
- m_decay[chan] = fpstep(m_envelope_level[chan], 0, attack_linear_step[m_attack_speed[chan] & 0x7f]);
- else
- m_decay[chan] = istep(m_envelope_level[chan], 0, m_global_step[m_attack_speed[chan]] << 1);
+ s32 sample2 = m_filter[chan].step(sample1, lmod, m_sample_counter);
+ u32 envelope_level = m_envelope[chan].step(m_sample_counter);
+ m_mixer[chan].step(sample2, envelope_level, amod, dry_l, dry_r, rev, cho_l, cho_r, var_l, var_r);
}
- dry_l >>= 8;
- dry_r >>= 8;
- rev >>= 8;
- cho_l >>= 8;
- cho_r >>= 8;
- var_l >>= 8;
- var_r >>= 8;
-
-
// Variation block
// Update the output volume
m_var_vol = m9(m_var_vol, 0xbd) + m_const[0xbc];
@@ -1828,19 +2373,19 @@ void swp00_device::sound_stream_update(sound_stream &stream)
brev.w(0x48, m9(brev.r(0x36), 0x51) + m9(rev_base_r, 0x52));
s32 rev_out_r = m7(brev.r(0x36), 0x53) + m7(rev_base_r, 0x54);
-
// Scale the dry input
dry_l = m7(dry_l, 0xbe);
dry_r = m7(dry_r, 0x01);
-
// Add in the other channels
dry_l += m9v(rev_out_l, m_rev_vol) + m9v(m9(cho_out_l, 0x17), m_cho_vol) + m9v(m9(var_out_l, 0x18), m_var_vol);
dry_r += m9v(rev_out_r, m_rev_vol) + m9v(m9(cho_out_r, 0x0e), m_cho_vol) + m9v(m9(var_out_r, 0x0f), m_var_vol);
- stream.put_int(0, i, dry_l, 32768);
- stream.put_int(1, i, dry_r, 32768);
+ // 18-bits ADCs
+ stream.put_int_clamp(0, i, dry_l >> 6, 0x20000);
+ stream.put_int_clamp(1, i, dry_r >> 6, 0x20000);
m_buffer_offset --;
+ m_sample_counter ++;
}
}
diff --git a/src/devices/sound/swp00.h b/src/devices/sound/swp00.h
index 72730798e8a..5421df42c52 100644
--- a/src/devices/sound/swp00.h
+++ b/src/devices/sound/swp00.h
@@ -17,15 +17,166 @@ public:
swp00_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock = 33868800);
void map(address_map &map) ATTR_COLD;
+ void require_sync();
protected:
virtual void device_start() override ATTR_COLD;
virtual void device_reset() override ATTR_COLD;
virtual void sound_stream_update(sound_stream &stream) override;
virtual void rom_bank_pre_change() override;
- virtual void device_add_mconfig(machine_config &config) override ATTR_COLD;
private:
+ struct streaming_block {
+ static const std::array<s16, 256> dpcm_expand;
+ static const std::array<s32, 8> max_value;
+
+ u16 m_phase;
+ u16 m_start;
+ u16 m_loop;
+ u32 m_address;
+ u16 m_pitch;
+ u8 m_format;
+
+ s32 m_pos;
+ s32 m_pos_dec;
+ s16 m_dpcm_s0, m_dpcm_s1;
+ u32 m_dpcm_pos;
+ s32 m_dpcm_delta;
+
+ bool m_first, m_done;
+ s16 m_last;
+
+ void clear();
+ void keyon();
+ std::pair<s16, bool> step(memory_access<24, 0, 0, ENDIANNESS_LITTLE>::cache &wave, s32 fmod);
+
+ template<int sel> void phase_w(u8 data);
+ template<int sel> void start_w(u8 data);
+ template<int sel> void loop_w(u8 data);
+ template<int sel> void address_w(u8 data);
+ template<int sel> void pitch_w(u8 data);
+ void format_w(u8 data);
+
+ template<int sel> u8 phase_r() const;
+ template<int sel> u8 start_r() const;
+ template<int sel> u8 loop_r() const;
+ template<int sel> u8 address_r() const;
+ template<int sel> u8 pitch_r() const;
+ u8 format_r() const;
+
+ u32 sample_address_get();
+
+ void read_16(memory_access<24, 0, 0, ENDIANNESS_LITTLE>::cache &wave, s16 &val0, s16 &val1);
+ void read_12(memory_access<24, 0, 0, ENDIANNESS_LITTLE>::cache &wave, s16 &val0, s16 &val1);
+ void read_8 (memory_access<24, 0, 0, ENDIANNESS_LITTLE>::cache &wave, s16 &val0, s16 &val1);
+ void read_8c(memory_access<24, 0, 0, ENDIANNESS_LITTLE>::cache &wave, s16 &val0, s16 &val1);
+
+ void dpcm_step(u8 input);
+ void update_loop_size();
+
+ std::string describe() const;
+ };
+
+ struct envelope_block {
+ // Hardware values readable through internal read on variable 2, do not change
+ enum {
+ ATTACK = 0,
+ DECAY = 2,
+ DECAY_DONE = 3,
+ };
+
+ u8 m_attack_speed;
+ u8 m_attack_level;
+ u8 m_decay_speed;
+ u8 m_decay_level;
+ s32 m_envelope_level;
+ u8 m_envelope_mode;
+
+ void clear();
+ void keyon();
+ u8 status() const;
+ bool active() const;
+ u16 step(u32 sample_counter);
+ void trigger_release();
+
+ void attack_speed_w(u8 data);
+ void attack_level_w(u8 data);
+ void decay_speed_w(u8 data);
+ void decay_level_w(u8 data);
+
+ u8 attack_speed_r() const;
+ u8 attack_level_r() const;
+ u8 decay_speed_r() const;
+ u8 decay_level_r() const;
+ };
+
+ struct filter_block {
+ enum {
+ SWEEP_NONE,
+ SWEEP_UP,
+ SWEEP_DONE,
+ };
+
+ s32 m_q, m_b, m_l;
+ u16 m_k, m_k_target;
+ u16 m_info;
+ u8 m_speed, m_sweep;
+
+ void clear();
+ void keyon();
+ s32 step(s16 input, s32 lmod, u32 sample_counter);
+ u8 status() const;
+ template<int sel> void info_w(u8 data);
+ template<int sel> u8 info_r();
+ void speed_w(u8 data);
+ u8 speed_r();
+ };
+
+ struct lfo_block {
+ u32 m_counter;
+ u8 m_speed, m_lamod, m_fmod;
+
+ void clear();
+ void keyon();
+ std::tuple<u32, s32, s32> step();
+
+ void lamod_w(u8 data);
+ u8 lamod_r();
+ void speed_w(u8 data);
+ u8 speed_r();
+ void fmod_w(u8 data);
+ u8 fmod_r();
+ };
+
+ struct mixer_block {
+ u16 m_cglo, m_cpanl, m_cpanr;
+ u16 m_tglo, m_tpanl, m_tpanr;
+ u8 m_glo, m_pan, m_dry, m_rev, m_cho, m_var;
+
+ void clear();
+ void keyon();
+ u8 status_glo() const;
+ u8 status_panl() const;
+ u8 status_panr() const;
+ void step(s32 sample, u16 envelope, u16 amod,
+ s32 &dry_l, s32 &dry_r, s32 &rev, s32 &cho_l, s32 &cho_r, s32 &var_l, s32 &var_r);
+ static s32 volume_apply(s32 level, s32 sample);
+
+ void glo_w(u8 data);
+ void pan_w(u8 data);
+ void dry_w(u8 data);
+ void rev_w(u8 data);
+ void cho_w(u8 data);
+ void var_w(u8 data);
+
+ u8 glo_r();
+ u8 pan_r();
+ u8 dry_r();
+ u8 rev_r();
+ u8 cho_r();
+ u8 var_r();
+ };
+
template<size_t size> struct delay_block {
swp00_device *m_swp;
std::array<s32, size> &m_buffer;
@@ -38,18 +189,13 @@ private:
};
sound_stream *m_stream;
+ bool m_require_sync;
- static const std::array<s32, 0x80> attack_linear_step;
- static const std::array<s32, 0x20> decay_linear_step;
- static const std::array<s32, 16> panmap;
- static const std::array<u8, 4> dpcm_offset;
- std::array<s32, 0x80> m_global_step;
- std::array<s16, 0x100> m_dpcm;
-
- static const std::array<u32, 4> lfo_shape_centered_saw;
- static const std::array<u32, 4> lfo_shape_centered_tri;
- static const std::array<u32, 4> lfo_shape_offset_saw;
- static const std::array<u32, 4> lfo_shape_offset_tri;
+ std::array<streaming_block, 0x20> m_streaming;
+ std::array<envelope_block, 0x20> m_envelope;
+ std::array<filter_block, 0x20> m_filter;
+ std::array<lfo_block, 0x20> m_lfo;
+ std::array<mixer_block, 0x20> m_mixer;
// MEG reverb memory
std::array<s32, 0x20000> m_rev_buffer;
@@ -60,46 +206,8 @@ private:
std::array<u16, 0x40> m_offset;
std::array<u16, 0xc0> m_const;
- // AWM registers
- std::array<u16, 0x20> m_lpf_info;
- std::array<u8, 0x20> m_lpf_speed;
- std::array<u8, 0x20> m_lfo_famod_depth;
- std::array<u8, 0x20> m_rev_level;
- std::array<u8, 0x20> m_dry_level;
- std::array<u8, 0x20> m_cho_level;
- std::array<u8, 0x20> m_var_level;
- std::array<u8, 0x20> m_glo_level;
- std::array<u8, 0x20> m_panning;
- std::array<u8, 0x20> m_attack_speed;
- std::array<u8, 0x20> m_attack_level;
- std::array<u8, 0x20> m_decay_speed;
- std::array<u8, 0x20> m_decay_level;
- std::array<u16, 0x20> m_pitch;
- std::array<u16, 0x20> m_sample_start;
- std::array<u16, 0x20> m_sample_end;
- std::array<u8, 0x20> m_sample_dpcm_and_format;
- std::array<u32, 0x20> m_sample_address;
- std::array<u8, 0x20> m_lfo_step;
- std::array<u8, 0x20> m_lfo_pmod_depth;
-
- std::array<u32, 0x20> m_lfo_phase;
- std::array<s32, 0x20> m_sample_pos;
- std::array<s32, 0x20> m_envelope_level;
- std::array<s32, 0x20> m_glo_level_cur;
- std::array<s32, 0x20> m_pan_l;
- std::array<s32, 0x20> m_pan_r;
- std::array<s32, 0x20> m_lpf_feedback;
- std::array<s32, 0x20> m_lpf_target_value;
- std::array<s32, 0x20> m_lpf_value;
- std::array<s32, 0x20> m_lpf_timer;
- std::array<s32, 0x20> m_lpf_ha;
- std::array<s32, 0x20> m_lpf_hb;
- std::array<bool, 0x20> m_active, m_decay, m_decay_done, m_lpf_done;
- std::array<s16, 0x20> m_dpcm_current;
- std::array<s16, 0x20> m_dpcm_next;
- std::array<u32, 0x20> m_dpcm_address;
- std::array<s32, 0x20> m_dpcm_sum;
-
+ // Control registers
+ u32 m_sample_counter;
u16 m_waverom_val;
u8 m_waverom_access;
u8 m_state_adr;
@@ -141,20 +249,24 @@ private:
template<int sel> u8 lpf_info_r(offs_t offset);
void lpf_speed_w(offs_t offset, u8 data);
u8 lpf_speed_r(offs_t offset);
- void lfo_famod_depth_w(offs_t offset, u8 data);
- u8 lfo_famod_depth_r(offs_t offset);
- void rev_level_w(offs_t offset, u8 data);
- u8 rev_level_r(offs_t offset);
- void dry_level_w(offs_t offset, u8 data);
- u8 dry_level_r(offs_t offset);
- void cho_level_w(offs_t offset, u8 data);
- u8 cho_level_r(offs_t offset);
- void var_level_w(offs_t offset, u8 data);
- u8 var_level_r(offs_t offset);
- void glo_level_w(offs_t offset, u8 data);
- u8 glo_level_r(offs_t offset);
- void panning_w(offs_t offset, u8 data);
- u8 panning_r(offs_t offset);
+ void lfo_lamod_w(offs_t offset, u8 data);
+ u8 lfo_lamod_r(offs_t offset);
+ void lfo_speed_w(offs_t offset, u8 data);
+ u8 lfo_speed_r(offs_t offset);
+ void lfo_fmod_w(offs_t offset, u8 data);
+ u8 lfo_fmod_r(offs_t offset);
+ void rev_w(offs_t offset, u8 data);
+ u8 rev_r(offs_t offset);
+ void dry_w(offs_t offset, u8 data);
+ u8 dry_r(offs_t offset);
+ void cho_w(offs_t offset, u8 data);
+ u8 cho_r(offs_t offset);
+ void var_w(offs_t offset, u8 data);
+ u8 var_r(offs_t offset);
+ void glo_w(offs_t offset, u8 data);
+ u8 glo_r(offs_t offset);
+ void pan_w(offs_t offset, u8 data);
+ u8 pan_r(offs_t offset);
void attack_speed_w(offs_t offset, u8 data);
u8 attack_speed_r(offs_t offset);
void attack_level_w(offs_t offset, u8 data);
@@ -165,21 +277,16 @@ private:
u8 decay_level_r(offs_t offset);
template<int sel> void pitch_w(offs_t offset, u8 data);
template<int sel> u8 pitch_r(offs_t offset);
- template<int sel> void sample_start_w(offs_t offset, u8 data);
- template<int sel> u8 sample_start_r(offs_t offset);
- template<int sel> void sample_end_w(offs_t offset, u8 data);
- template<int sel> u8 sample_end_r(offs_t offset);
- void sample_dpcm_and_format_w(offs_t offset, u8 data);
- u8 sample_dpcm_and_format_r(offs_t offset);
- template<int sel> void sample_address_w(offs_t offset, u8 data);
- template<int sel> u8 sample_address_r(offs_t offset);
- void lfo_step_w(offs_t offset, u8 data);
- u8 lfo_step_r(offs_t offset);
- void lfo_pmod_depth_w(offs_t offset, u8 data);
- u8 lfo_pmod_depth_r(offs_t offset);
-
- void slot8_w(offs_t offset, u8 data);
- void slot9_w(offs_t offset, u8 data);
+ template<int sel> void phase_w(offs_t offset, u8 data);
+ template<int sel> u8 phase_r(offs_t offset);
+ template<int sel> void start_w(offs_t offset, u8 data);
+ template<int sel> u8 start_r(offs_t offset);
+ template<int sel> void loop_w(offs_t offset, u8 data);
+ template<int sel> u8 loop_r(offs_t offset);
+ template<int sel> void address_w(offs_t offset, u8 data);
+ template<int sel> u8 address_r(offs_t offset);
+ void format_w(offs_t offset, u8 data);
+ u8 format_r(offs_t offset);
// Internal state access
u8 state_r();
@@ -215,12 +322,14 @@ private:
}
// Other methods
+ static u16 interpolation_step(u32 speed, u32 sample_counter);
static bool istep(s32 &value, s32 limit, s32 step);
static bool fpstep(s32 &value, s32 limit, s32 step);
static s32 fpadd(s32 value, s32 step);
static s32 fpsub(s32 value, s32 step);
static s32 fpapply(s32 value, s32 sample);
static s32 lpffpapply(s32 value, s32 sample);
+ static s32 volume_apply(s32 level, s32 sample);
s32 rext(int reg) const;
static s32 m7v(s32 value, s32 mult);
diff --git a/src/mame/yamaha/ymmu10.cpp b/src/mame/yamaha/ymmu10.cpp
index f34b6821587..72ece78257c 100644
--- a/src/mame/yamaha/ymmu10.cpp
+++ b/src/mame/yamaha/ymmu10.cpp
@@ -6,9 +6,9 @@
tone module
Driver by R. Belmont and O. Galibert
- Essentially a screen-less MU10 (SWP00, identical wave rom) with a gate-array based
- hack to connect the wave rom space to the adcs so that effects can be applied to
- analog inputs. Entirely controlled through midi.
+ Essentially a screen-less MU50 (SWP00, identical wave rom) with a gate-array based
+ hack to connect the wave rom space to the adc so that effects can be applied to
+ the analog input. Entirely controlled through midi.
**************************************************************************************/
@@ -17,6 +17,7 @@
#include "bus/midi/midiinport.h"
#include "bus/midi/midioutport.h"
#include "cpu/h8/h83002.h"
+#include "sound/adc.h"
#include "sound/swp00.h"
#include "machine/nvram.h"
@@ -26,9 +27,6 @@
namespace {
-static INPUT_PORTS_START( mu10 )
-INPUT_PORTS_END
-
class mu10_state : public driver_device
{
public:
@@ -37,6 +35,8 @@ public:
, m_maincpu(*this, "maincpu")
, m_nvram(*this, "ram")
, m_swp00(*this, "swp00")
+ , m_adc(*this, "adc")
+ , m_port_ad(*this, "AD")
, m_ram(*this, "ram")
{ }
@@ -46,6 +46,8 @@ private:
required_device<h83002_device> m_maincpu;
required_device<nvram_device> m_nvram;
required_device<swp00_device> m_swp00;
+ required_device<adc16_device> m_adc;
+ required_ioport m_port_ad;
required_shared_ptr<u16> m_ram;
u8 cur_p6, cur_pa, cur_pb;
@@ -53,12 +55,15 @@ private:
u16 adc_battery_r();
u16 adc_midisw_r();
+ u8 ad_r(offs_t offset);
+
void p6_w(u8 data);
void pa_w(u8 data);
void pb_w(u8 data);
u8 pb_r();
void mu10_map(address_map &map) ATTR_COLD;
+ void swp00_map(address_map &map) ATTR_COLD;
virtual void machine_start() override ATTR_COLD;
virtual void machine_reset() override ATTR_COLD;
@@ -82,6 +87,25 @@ void mu10_state::mu10_map(address_map &map)
map(0x600000, 0x607fff).ram().share(m_ram); // 32K work RAM
}
+void mu10_state::swp00_map(address_map &map)
+{
+ map(0x000000, 0x3fffff).rom().region("swp00", 0);
+ map(0xc00000, 0xffffff).r(FUNC(mu10_state::ad_r));
+}
+
+static INPUT_PORTS_START( mu10 )
+ PORT_START("AD")
+ PORT_CONFNAME(0x08, 0x08, "A/D input connected")
+ PORT_CONFSETTING( 0x08, "No")
+ PORT_CONFSETTING( 0x00, "Yes")
+INPUT_PORTS_END
+
+u8 mu10_state::ad_r(offs_t offset)
+{
+ // Using mirror makes the install horrible slow
+ return offset & 1 ? m_adc->read8h() : m_adc->read8l();
+}
+
// Battery level
u16 mu10_state::adc_battery_r()
{
@@ -113,7 +137,7 @@ void mu10_state::pb_w(u8 data)
u8 mu10_state::pb_r()
{
// bit 3 = a/d plugged in
- return 8;
+ return m_port_ad->read();
}
void mu10_state::pa_w(u8 data)
@@ -127,6 +151,9 @@ void mu10_state::mu10(machine_config &config)
H83002(config, m_maincpu, 12_MHz_XTAL);
m_maincpu->set_addrmap(AS_PROGRAM, &mu10_state::mu10_map);
m_maincpu->read_adc<0>().set(FUNC(mu10_state::adc_battery_r));
+ m_maincpu->read_adc<1>().set_constant(0);
+ m_maincpu->read_adc<2>().set_constant(0);
+ m_maincpu->read_adc<3>().set_constant(0);
m_maincpu->read_adc<4>().set_constant(0);
m_maincpu->read_adc<5>().set_constant(0);
m_maincpu->read_adc<6>().set_constant(0);
@@ -139,10 +166,15 @@ void mu10_state::mu10(machine_config &config)
NVRAM(config, m_nvram, nvram_device::DEFAULT_NONE);
SPEAKER(config, "speaker", 2).front();
+ MICROPHONE(config, "ad", 1).front_center().add_route(0, m_adc, 1.0, 0);
+
+ ADC16(config, m_adc, 44100);
SWP00(config, m_swp00);
m_swp00->add_route(0, "speaker", 1.0, 0);
m_swp00->add_route(1, "speaker", 1.0, 1);
+ m_swp00->set_addrmap(0, &mu10_state::swp00_map);
+ m_swp00->require_sync();
auto &mdin(MIDI_PORT(config, "mdin"));
midiin_slot(mdin);
diff --git a/src/osd/modules/sound/pipewire_sound.cpp b/src/osd/modules/sound/pipewire_sound.cpp
index 809638dc245..b6ffd73160f 100644
--- a/src/osd/modules/sound/pipewire_sound.cpp
+++ b/src/osd/modules/sound/pipewire_sound.cpp
@@ -576,6 +576,9 @@ osd::audio_info sound_pipewire::get_information()
result.m_generation = m_generation;
uint32_t node = 0;
for(auto &inode : m_nodes) {
+ if(inode.second.m_sinks == 0 && inode.second.m_sources == 0)
+ continue;
+
result.m_nodes[node].m_name = inode.second.m_name;
result.m_nodes[node].m_display_name = inode.second.m_name;
result.m_nodes[node].m_id = inode.second.m_osdid;