diff options
author | 2025-01-06 18:30:26 +0100 | |
---|---|---|
committer | 2025-01-06 18:30:39 +0100 | |
commit | 71fe25f95be7030fbeb26dad6067a06b2fe38f59 (patch) | |
tree | 70ba7890180c47be112b01bbfca7f9109162d5f5 | |
parent | c56135a7f361bbe6686fe62c121f34d6ca92f33d (diff) |
zsg2: update stream before reading,
tms57002: fix input sample overflow
-rw-r--r-- | src/devices/cpu/tms57002/tms57002.cpp | 2 | ||||
-rw-r--r-- | src/devices/sound/zsg2.cpp | 200 | ||||
-rw-r--r-- | src/devices/sound/zsg2.h | 5 |
3 files changed, 104 insertions, 103 deletions
diff --git a/src/devices/cpu/tms57002/tms57002.cpp b/src/devices/cpu/tms57002/tms57002.cpp index 4adeed1cf85..593a74e3263 100644 --- a/src/devices/cpu/tms57002/tms57002.cpp +++ b/src/devices/cpu/tms57002/tms57002.cpp @@ -925,7 +925,7 @@ void tms57002_device::sound_stream_update(sound_stream &stream, std::vector<read assert(inputs[0].samples() == 1); assert(outputs[0].samples() == 1); - stream_buffer::sample_t in_scale = 32768.0 * ((st0 & ST0_SIM) ? 256.0 : 1.0); + stream_buffer::sample_t in_scale = 32767.0 * ((st0 & ST0_SIM) ? 256.0 : 1.0); si[0] = s32(inputs[0].get(0) * in_scale) & 0xffffff; si[1] = s32(inputs[1].get(0) * in_scale) & 0xffffff; si[2] = s32(inputs[2].get(0) * in_scale) & 0xffffff; diff --git a/src/devices/sound/zsg2.cpp b/src/devices/sound/zsg2.cpp index 66072775500..e578a09fbc4 100644 --- a/src/devices/sound/zsg2.cpp +++ b/src/devices/sound/zsg2.cpp @@ -1,91 +1,93 @@ // license:BSD-3-Clause // copyright-holders:Olivier Galibert, R. Belmont, hap, superctr /* - ZOOM ZSG-2 custom wavetable synthesizer - - Written by Olivier Galibert - MAME conversion by R. Belmont - Working emulation by The Talentuous Hands Of The Popularious hap - Properly working emulation by superctr - --------------------------------------------------------- - - Register map: - 000-5fe : Channel specific registers (48 channels) - (high) (low) - +000 : xxxxxxxx -------- : Start address (low) - +000 : -------- xxxxxxxx : Unknown register (usually cleared) - +002 : xxxxxxxx -------- : Address page - : -------- xxxxxxxx : Start address (high) - +004 : -------- -------- : Unknown register (usually cleared) - +006 : -----x-- -------- : Unknown bit, always set - +008 : xxxxxxxx xxxxxxxx : Frequency - +00a : xxxxxxxx -------- : DSP ch 3 (right) output gain - : -------- xxxxxxxx : Loop address (low) - +00c : xxxxxxxx xxxxxxxx : End address - +00e : xxxxxxxx -------- : DSP ch 2 (Left) output gain - : -------- xxxxxxxx : Loop address (high) - +010 : xxxxxxxx xxxxxxxx : Initial filter time constant - +012 : xxxxxxxx xxxxxxxx : Current filter time constant - +014 : xxxxxxxx xxxxxxxx : Initial volume - +016 : xxxxxxxx xxxxxxxx : Current volume? - +018 : xxxxxxxx xxxxxxxx : Target filter time constant - +01a : xxxxxxxx -------- : DSP ch 1 (chorus) output gain - : -------- xxxxxxxx : Filter ramping speed - +01c : xxxxxxxx xxxxxxxx : Target volume - +01e : xxxxxxxx -------- : DSP ch 0 (reverb) output gain - : -------- xxxxxxxx : Filter ramping speed - 600-604 : Key on flags (each bit corresponds to a channel) - 608-60c : Key off flags (each bit corresponds to a channel) - 618 : Unknown register (usually 0x5cbc is written) - 61a : Unknown register (usually 0x5cbc is written) - 620 : Unknown register (usually 0x0128 is written) - 628 : Unknown register (usually 0x0066 is written) - 630 : Unknown register (usually 0x0001 is written) - 638 : ROM readback address low - 63a : ROM readback address high - 63c : ROM readback word low - 63e : ROM readback word high - - --------------------------------------------------------- - - Additional notes on the sample format, reverse-engineered - by Olivier Galibert and David Haywood: - - The zoom sample rom is decomposed in 0x40000 bytes pages. Each page - starts by a header and is followed by compressed samples. - - The header is a vector of 16 bytes structures composed of 4 32bits - little-endian values representing: - - sample start position in bytes, always a multiple of 4 - - sample end position in bytes, minus 4, always... - - loop position in bytes, always.... - - flags, probably - - It is interesting to note that this header is *not* parsed by the - ZSG. The main program reads the rom through appropriate ZSG - commands, and use the results in subsequent register setups. It's - not even obvious that the ZSG cares about the pages, it may just - see the address space as linear. In the same line, the - interpretation of the flags is obviously dependent on the main - program, not the ZSG, but some of the bits are directly copied to - some of the registers. - - The samples are compressed with a 2:1 ratio. Each block of 4-bytes - becomes 4 16-bits samples. Reading the 4 bytes as a *little-endian* - 32bits values, the structure is: - - 42222222 51111111 60000000 ssss3333 - - 's' is a 4-bit scale value. '0000000', '1111111', '2222222' and - '6543333' are signed 7-bits values corresponding to the 4 samples. - To compute the final 16bits value, left-align and shift right by s. - Yes, that simple. - - --------------------------------------------------------- + +ZOOM ZSG-2 custom wavetable synthesizer + +Written by Olivier Galibert +MAME conversion by R. Belmont +Working emulation by The Talentuous Hands Of The Popularious hap +Properly working emulation by superctr +--------------------------------------------------------- + +Register map: +000-5fe : Channel specific registers (48 channels) + (high) (low) + +000 : xxxxxxxx -------- : Start address (low) + +000 : -------- xxxxxxxx : Unknown register (usually cleared) + +002 : xxxxxxxx -------- : Address page + : -------- xxxxxxxx : Start address (high) + +004 : -------- -------- : Unknown register (usually cleared) + +006 : -----x-- -------- : Unknown bit, always set + +008 : xxxxxxxx xxxxxxxx : Frequency + +00a : xxxxxxxx -------- : DSP ch 3 (right) output gain + : -------- xxxxxxxx : Loop address (low) + +00c : xxxxxxxx xxxxxxxx : End address + +00e : xxxxxxxx -------- : DSP ch 2 (Left) output gain + : -------- xxxxxxxx : Loop address (high) + +010 : xxxxxxxx xxxxxxxx : Initial filter time constant + +012 : xxxxxxxx xxxxxxxx : Current filter time constant + +014 : xxxxxxxx xxxxxxxx : Initial volume + +016 : xxxxxxxx xxxxxxxx : Current volume? + +018 : xxxxxxxx xxxxxxxx : Target filter time constant + +01a : xxxxxxxx -------- : DSP ch 1 (chorus) output gain + : -------- xxxxxxxx : Filter ramping speed + +01c : xxxxxxxx xxxxxxxx : Target volume + +01e : xxxxxxxx -------- : DSP ch 0 (reverb) output gain + : -------- xxxxxxxx : Filter ramping speed +600-604 : Key on flags (each bit corresponds to a channel) +608-60c : Key off flags (each bit corresponds to a channel) +618 : Unknown register (usually 0x5cbc is written) +61a : Unknown register (usually 0x5cbc is written) +620 : Unknown register (usually 0x0128 is written) +628 : Unknown register (usually 0x0066 is written) +630 : Unknown register (usually 0x0001 is written) +638 : ROM readback address low +63a : ROM readback address high +63c : ROM readback word low +63e : ROM readback word high + +--------------------------------------------------------- + +Additional notes on the sample format, reverse-engineered +by Olivier Galibert and David Haywood: + +The zoom sample rom is decomposed in 0x40000 bytes pages. Each page +starts by a header and is followed by compressed samples. + +The header is a vector of 16 bytes structures composed of 4 32bits +little-endian values representing: +- sample start position in bytes, always a multiple of 4 +- sample end position in bytes, minus 4, always... +- loop position in bytes, always.... +- flags, probably + +It is interesting to note that this header is *not* parsed by the +ZSG. The main program reads the rom through appropriate ZSG +commands, and use the results in subsequent register setups. It's +not even obvious that the ZSG cares about the pages, it may just +see the address space as linear. In the same line, the +interpretation of the flags is obviously dependent on the main +program, not the ZSG, but some of the bits are directly copied to +some of the registers. + +The samples are compressed with a 2:1 ratio. Each block of 4-bytes +becomes 4 16-bits samples. Reading the 4 bytes as a *little-endian* +32bits values, the structure is: + +42222222 51111111 60000000 ssss3333 + +'s' is a 4-bit scale value. '0000000', '1111111', '2222222' and +'6543333' are signed 7-bits values corresponding to the 4 samples. +To compute the final 16bits value, left-align and shift right by s. +Yes, that simple. + +--------------------------------------------------------- TODO: - Filter and ramping behavior might not be perfect. -- clicking / popping noises in gdarius, raystorm: maybe the sample ROMs are bad dumps? +- clicking / popping noises in gdarius, raystorm: maybe the sample ROMs + are bad dumps? - memory reads out of range sometimes */ @@ -290,7 +292,7 @@ void zsg2_device::sound_stream_update(sound_stream &stream, std::vector<read_str // loop over all channels for (auto & elem : m_chan) { - if(~elem.status & STATUS_ACTIVE) + if (~elem.status & STATUS_ACTIVE) continue; elem.step_ptr += elem.step; @@ -309,7 +311,7 @@ void zsg2_device::sound_stream_update(sound_stream &stream, std::vector<read_str } } - if(elem.cur_pos == elem.start_pos) + if (elem.cur_pos == elem.start_pos) elem.emphasis_filter_state = EMPHASIS_INITIAL_BIAS; elem.step_ptr &= 0xffff; @@ -320,19 +322,19 @@ void zsg2_device::sound_stream_update(sound_stream &stream, std::vector<read_str int32_t sample = elem.samples[sample_pos]; // linear interpolation (hardware certainly does something similar) - sample += ((uint16_t)(elem.step_ptr<<2&0xffff) * (int16_t)(elem.samples[sample_pos+1] - sample))>>16; + sample += ((uint16_t)(elem.step_ptr << 2 & 0xffff) * (int16_t)(elem.samples[sample_pos+1] - sample)) >> 16; // another filter... - elem.output_filter_state += (sample - (elem.output_filter_state>>16)) * elem.output_cutoff; + elem.output_filter_state += (sample - (elem.output_filter_state >> 16)) * elem.output_cutoff; sample = elem.output_filter_state >> 16; // To prevent DC bias, we need to slowly discharge the filter when the output filter cutoff is 0 - if(!elem.output_cutoff) + if (!elem.output_cutoff) elem.output_filter_state >>= 1; - sample = (sample * elem.vol)>>16; + sample = (sample * elem.vol) >> 16; - for(int output=0; output<4; output++) + for (int output = 0; output < 4; output++) { int output_gain = elem.output_gain[output] & 0x1f; // left / right int32_t output_sample = sample; @@ -340,19 +342,19 @@ void zsg2_device::sound_stream_update(sound_stream &stream, std::vector<read_str if (elem.output_gain[output] & 0x80) // perhaps ? output_sample = -output_sample; - mix[output] += (output_sample * m_gain_tab[output_gain&0x1f]) >> 16; + mix[output] += (output_sample * m_gain_tab[output_gain & 0x1f]) >> 16; } // Apply ramping every other update // It's possible key on is handled on the other sample - if(m_sample_count & 1) + if (m_sample_count & 1) { elem.vol = ramp(elem.vol, elem.vol_target, elem.vol_delta); elem.output_cutoff = ramp(elem.output_cutoff, elem.output_cutoff_target, elem.output_cutoff_delta); } } - for(int output=0; output<4; output++) + for (int output = 0; output < 4; output++) outputs[output].put_int_clamp(i, mix[output], 32768); } m_sample_count++; @@ -491,17 +493,17 @@ uint16_t zsg2_device::chan_r(int ch, int reg) // calculate this value, for now I'm generating an opproximate inverse. int16_t zsg2_device::get_ramp(uint8_t val) { - int16_t frac = val<<12; // sign extend - frac = ((frac>>12) ^ 8) << (val >> 4); + int16_t frac = val << 12; // sign extend + frac = ((frac >> 12) ^ 8) << (val >> 4); return (frac >> 4); } inline uint16_t zsg2_device::ramp(uint16_t current, uint16_t target, int16_t delta) { int32_t rampval = current + delta; - if(delta < 0 && rampval < target) + if (delta < 0 && rampval < target) rampval = target; - else if(delta >= 0 && rampval > target) + else if (delta >= 0 && rampval > target) rampval = target; return rampval; @@ -572,7 +574,7 @@ void zsg2_device::control_w(int reg, uint16_t data) break; default: - if(reg < 0x20) + if (reg < 0x20) m_reg[reg] = data; logerror("ZSG2 control %02X = %04X\n", reg, data & 0xffff); break; @@ -596,7 +598,7 @@ uint16_t zsg2_device::control_r(int reg) return read_memory(m_read_address) >> 16; default: - if(reg < 0x20) + if (reg < 0x20) return m_reg[reg]; break; } @@ -639,6 +641,8 @@ uint16_t zsg2_device::read(offs_t offset, uint16_t mem_mask) return 0; } + m_stream->update(); + if (offset < 0x300) { int chan = offset >> 4; diff --git a/src/devices/sound/zsg2.h b/src/devices/sound/zsg2.h index 6d4d1b7c081..91a06e27a2e 100644 --- a/src/devices/sound/zsg2.h +++ b/src/devices/sound/zsg2.h @@ -58,12 +58,9 @@ private: int16_t output_cutoff_delta; int32_t emphasis_filter_state; - int32_t output_filter_state; - // Attenuation for output channels - uint8_t output_gain[4]; - + uint8_t output_gain[4]; // Attenuation for output channels int16_t samples[5]; // +1 history }; |