diff options
| author | 2025-11-14 14:29:54 +0100 | |
|---|---|---|
| committer | 2025-11-14 16:50:27 +0100 | |
| commit | b75872cb98a17df42cdb36a19e093ebeabf49f49 (patch) | |
| tree | 32bdb5cf7724efcc1dfa113a0b3a8104cf45e86a | |
| parent | 66ca47301c3c0eaf126f3b836c572d41ed69c6cf (diff) | |
swp00: Overhaul of the synthesis part, effects to follow
| -rw-r--r-- | src/devices/sound/adc.cpp | 42 | ||||
| -rw-r--r-- | src/devices/sound/adc.h | 17 | ||||
| -rw-r--r-- | src/devices/sound/swp00.cpp | 1757 | ||||
| -rw-r--r-- | src/devices/sound/swp00.h | 271 | ||||
| -rw-r--r-- | src/mame/yamaha/ymmu10.cpp | 46 | ||||
| -rw-r--r-- | src/osd/modules/sound/pipewire_sound.cpp | 3 |
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; |
