diff options
author | 2018-08-18 15:36:39 -0400 | |
---|---|---|
committer | 2018-08-18 15:36:39 -0400 | |
commit | c07731b3d4b49117233e1c0fadbc2663319e391b (patch) | |
tree | b20c8678818a0154a9aa0d50997dc6953398792a /src/devices/sound/qsoundhle.cpp | |
parent | f5a6bfbe2d2dbd9e733c95d82450f5a3d4027b5e (diff) |
Revert "-qsound_hle: Updated based on actual DSP16 code to give identical output. [superctr, ValleyBell]"
This reverts commit 484a29e60ba5cc805122544cc72c75d293a5c9ce.
Diffstat (limited to 'src/devices/sound/qsoundhle.cpp')
-rw-r--r-- | src/devices/sound/qsoundhle.cpp | 610 |
1 files changed, 119 insertions, 491 deletions
diff --git a/src/devices/sound/qsoundhle.cpp b/src/devices/sound/qsoundhle.cpp index 4465adcacc6..8d182bb4b1a 100644 --- a/src/devices/sound/qsoundhle.cpp +++ b/src/devices/sound/qsoundhle.cpp @@ -1,13 +1,25 @@ // license:BSD-3-Clause -// copyright-holders:superctr, Valley Bell +// copyright-holders:Paul Leaman, Miguel Angel Horna /*************************************************************************** - Capcom QSound DL-1425 (HLE) + Capcom System QSoundâ„¢ (HLE) =========================== - Driver by superctr with thanks to Valley Bell. + Driver by Paul Leaman and Miguel Angel Horna - Based on disassembled DSP code. + A 16 channel stereo sample player. + + QSpace position is simulated by panning the sound in the stereo space. + + Many thanks to CAB (the author of Amuse), without whom this probably would + never have been finished. + + TODO: + - hook up the DSP! + - is master volume really linear? + - understand higher bits of reg 0 + - understand reg 9 + - understand other writes to $90-$ff area Links: https://siliconpr0n.org/map/capcom/dl-1425 @@ -20,16 +32,19 @@ #include <algorithm> #include <limits> + +namespace { + +constexpr u32 saturate(s64 val) +{ + return std::min<s64>(std::max<s64>(val, std::numeric_limits<s32>::min()), std::numeric_limits<s32>::max()); +} + +} // anonymous namespace + // device type definition DEFINE_DEVICE_TYPE(QSOUND_HLE, qsound_hle_device, "qsound_hle", "QSound (HLE)") -// DSP internal ROM region -ROM_START( qsound_hle ) - ROM_REGION16_LE( 0x2000, "dsp", 0 ) - // removing WORD_SWAP from original definition - ROM_LOAD16_WORD( "dl-1425.bin", 0x0000, 0x2000, CRC(d6cf5ef5) SHA1(555f50fe5cdf127619da7d854c03f4a244a0c501) ) - ROM_IGNORE( 0x4000 ) -ROM_END //************************************************************************** // LIVE DEVICE @@ -39,16 +54,16 @@ ROM_END // qsound_hle_device - constructor //------------------------------------------------- -qsound_hle_device::qsound_hle_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) +qsound_hle_device::qsound_hle_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock) : device_t(mconfig, QSOUND_HLE, tag, owner, clock) , device_sound_interface(mconfig, *this) , device_rom_interface(mconfig, *this, 24) , m_stream(nullptr) - , m_dsp_rom(*this, "dsp") - , m_data_latch(0) + , m_data(0) { } + //------------------------------------------------- // rom_bank_updated - the rom bank has changed //------------------------------------------------- @@ -58,6 +73,7 @@ void qsound_hle_device::rom_bank_updated() m_stream->update(); } + //------------------------------------------------- // device_start - device-specific startup //------------------------------------------------- @@ -66,90 +82,25 @@ void qsound_hle_device::device_start() { m_stream = stream_alloc(0, 2, clock() / 2 / 1248); // DSP program uses 1248 machine cycles per iteration - init_register_map(); + // create pan table + for (int i = 0; i < 33; i++) + m_pan_table[i] = (int)((256 / sqrt(32.0)) * sqrt((double)i)); - // state save - // PCM registers - for (int j = 0; j < 16; j++) // PCM voices - { - save_item(NAME(m_voice[j].m_bank), j); - save_item(NAME(m_voice[j].m_addr), j); - save_item(NAME(m_voice[j].m_phase), j); - save_item(NAME(m_voice[j].m_rate), j); - save_item(NAME(m_voice[j].m_loop_len), j); - save_item(NAME(m_voice[j].m_end_addr), j); - save_item(NAME(m_voice[j].m_volume), j); - save_item(NAME(m_voice[j].m_echo), j); - } + // init sound regs + memset(m_channel, 0, sizeof(m_channel)); - for (int j = 0; j < 3; j++) // ADPCM voices - { - save_item(NAME(m_adpcm[j].m_start_addr), j); - save_item(NAME(m_adpcm[j].m_end_addr), j); - save_item(NAME(m_adpcm[j].m_bank), j); - save_item(NAME(m_adpcm[j].m_volume), j); - save_item(NAME(m_adpcm[j].m_flag), j); - save_item(NAME(m_adpcm[j].m_cur_vol), j); - save_item(NAME(m_adpcm[j].m_step_size), j); - save_item(NAME(m_adpcm[j].m_cur_addr), j); - } - - // PCM voices - save_item(NAME(m_voice_pan)); + for (int adr = 0x80; adr < 0x90; adr++) + write_data(adr, 0x120); - // QSound registers - save_item(NAME(m_echo.m_end_pos)); - save_item(NAME(m_echo.m_feedback)); - save_item(NAME(m_echo.m_length)); - save_item(NAME(m_echo.m_last_sample)); - save_item(NAME(m_echo.m_delay_line)); - save_item(NAME(m_echo.m_delay_pos)); - - for (int j = 0; j < 2; j++) // left, right + // state save + for (int i = 0; i < 16; i++) { - save_item(NAME(m_filter[j].m_tap_count), j); - save_item(NAME(m_filter[j].m_delay_pos), j); - save_item(NAME(m_filter[j].m_table_pos), j); - save_item(NAME(m_filter[j].m_taps), j); - save_item(NAME(m_filter[j].m_delay_line), j); - - save_item(NAME(m_alt_filter[j].m_tap_count), j); - save_item(NAME(m_alt_filter[j].m_delay_pos), j); - save_item(NAME(m_alt_filter[j].m_table_pos), j); - save_item(NAME(m_alt_filter[j].m_taps), j); - save_item(NAME(m_alt_filter[j].m_delay_line), j); - - save_item(NAME(m_wet[j].m_delay), j); - save_item(NAME(m_wet[j].m_volume), j); - save_item(NAME(m_wet[j].m_write_pos), j); - save_item(NAME(m_wet[j].m_read_pos), j); - save_item(NAME(m_wet[j].m_delay_line), j); - - save_item(NAME(m_dry[j].m_delay), j); - save_item(NAME(m_dry[j].m_volume), j); - save_item(NAME(m_dry[j].m_write_pos), j); - save_item(NAME(m_dry[j].m_read_pos), j); - save_item(NAME(m_dry[j].m_delay_line), j); + save_item(NAME(m_channel[i].reg), i); + save_item(NAME(m_channel[i].lvol), i); + save_item(NAME(m_channel[i].rvol), i); } - - save_item(NAME(m_state)); - save_item(NAME(m_next_state)); - save_item(NAME(m_delay_update)); - save_item(NAME(m_state_counter)); - save_item(NAME(m_ready_flag)); - save_item(NAME(m_data_latch)); - save_item(NAME(m_out)); } -//------------------------------------------------- -// rom_region - return a pointer to the device's -// internal ROM region -//------------------------------------------------- - -const tiny_rom_entry *qsound_hle_device::device_rom_region() const -{ - return ROM_NAME( qsound_hle ); -} //------------------------------------------------- // device_reset - device-specific reset @@ -157,12 +108,11 @@ const tiny_rom_entry *qsound_hle_device::device_rom_region() const void qsound_hle_device::device_reset() { - m_ready_flag = 0; - m_out[0] = m_out[1] = 0; - m_state = STATE_BOOT; - m_state_counter = 0; + for (qsound_channel &ch : m_channel) + std::fill(std::begin(ch.reg), std::end(ch.reg), 0U); } + //------------------------------------------------- // sound_stream_update - handle a stream update //------------------------------------------------- @@ -170,14 +120,36 @@ void qsound_hle_device::device_reset() void qsound_hle_device::sound_stream_update(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples) { // Clear the buffers - std::fill_n(outputs[0], samples, 0); - std::fill_n(outputs[1], samples, 0); + memset(outputs[0], 0, samples * sizeof(*outputs[0])); + memset(outputs[1], 0, samples * sizeof(*outputs[1])); - for (int i = 0; i < samples; i ++) + for (unsigned n = 0; ARRAY_LENGTH(m_channel) > n; ++n) { - update_sample(); - outputs[0][i] = m_out[0]; - outputs[1][i] = m_out[1]; + qsound_channel &ch(m_channel[n]); + stream_sample_t *lmix(outputs[0]); + stream_sample_t *rmix(outputs[1]); + + // Go through the buffer and add voice contributions + offs_t const bank(m_channel[(n + ARRAY_LENGTH(m_channel) - 1) & (ARRAY_LENGTH(m_channel) - 1)].reg[0] & 0x7fff); + for (int i = 0; i < samples; i++) + { + // current sample address (bank comes from previous channel) + offs_t const addr(ch.reg[1] | (bank << 16)); + + // update based on playback rate + s64 updated(s32(u32(ch.reg[2]) << 4) + s32((u32(ch.reg[1]) << 16) | ch.reg[3])); + ch.reg[3] = u16(saturate(updated)); + if (updated >= s32(u32(ch.reg[5]) << 16)) + updated -= s32(u32(ch.reg[4]) << 16); + ch.reg[1] = u16(saturate(updated) >> 16); + + // get the scaled sample + s32 const scaled(s32(s16(ch.reg[6])) * read_sample(addr)); + + // apply simple panning + *lmix++ += (((scaled >> 8) * ch.lvol) >> 14); + *rmix++ += (((scaled >> 8) * ch.rvol) >> 14); + } } } @@ -187,16 +159,16 @@ WRITE8_MEMBER(qsound_hle_device::qsound_w) switch (offset) { case 0: - m_data_latch = (m_data_latch & 0x00ff) | (data << 8); + m_data = (m_data & 0x00ff) | (data << 8); break; case 1: - m_data_latch = (m_data_latch & 0xff00) | data; + m_data = (m_data & 0xff00) | data; break; case 2: m_stream->update(); - write_data(data, m_data_latch); + write_data(data, m_data); break; default: @@ -208,414 +180,70 @@ WRITE8_MEMBER(qsound_hle_device::qsound_w) READ8_MEMBER(qsound_hle_device::qsound_r) { - // ready bit (0x00 = busy, 0x80 == ready) - m_stream->update(); - return m_ready_flag; -} - - -void qsound_hle_device::write_data(uint8_t address, uint16_t data) -{ - uint16_t *destination = m_register_map[address]; - if (destination) - *destination = data; - m_ready_flag = 0; -} - -void qsound_hle_device::init_register_map() -{ - // unused registers - std::fill(std::begin(m_register_map), std::end(m_register_map), nullptr); - - // PCM registers - for (int i = 0; i < 16; i++) // PCM voices - { - m_register_map[(i << 3) + 0] = (uint16_t*)&m_voice[(i + 1) % 16].m_bank; // Bank applies to the next channel - m_register_map[(i << 3) + 1] = (uint16_t*)&m_voice[i].m_addr; // Current sample position and start position. - m_register_map[(i << 3) + 2] = (uint16_t*)&m_voice[i].m_rate; // 4.12 fixed point decimal. - m_register_map[(i << 3) + 3] = (uint16_t*)&m_voice[i].m_phase; - m_register_map[(i << 3) + 4] = (uint16_t*)&m_voice[i].m_loop_len; - m_register_map[(i << 3) + 5] = (uint16_t*)&m_voice[i].m_end_addr; - m_register_map[(i << 3) + 6] = (uint16_t*)&m_voice[i].m_volume; - m_register_map[(i << 3) + 7] = nullptr; // unused - m_register_map[i + 0x80] = (uint16_t*)&m_voice_pan[i]; - m_register_map[i + 0xba] = (uint16_t*)&m_voice[i].m_echo; - } - - // ADPCM registers - for (int i = 0; i < 3; i++) // ADPCM voices - { - // ADPCM sample rate is fixed to 8khz. (one channel is updated every third sample) - m_register_map[(i << 2) + 0xca] = (uint16_t*)&m_adpcm[i].m_start_addr; - m_register_map[(i << 2) + 0xcb] = (uint16_t*)&m_adpcm[i].m_end_addr; - m_register_map[(i << 2) + 0xcc] = (uint16_t*)&m_adpcm[i].m_bank; - m_register_map[(i << 2) + 0xcd] = (uint16_t*)&m_adpcm[i].m_volume; - m_register_map[i + 0xd6] = (uint16_t*)&m_adpcm[i].m_flag; // non-zero to start ADPCM playback - m_register_map[i + 0x90] = (uint16_t*)&m_voice_pan[16 + i]; - } - - // QSound registers - m_register_map[0x93] = (uint16_t*)&m_echo.m_feedback; - m_register_map[0xd9] = (uint16_t*)&m_echo.m_end_pos; - m_register_map[0xe2] = (uint16_t*)&m_delay_update; // non-zero to update delays - m_register_map[0xe3] = (uint16_t*)&m_next_state; - for (int i = 0; i < 2; i++) // left, right - { - // Wet - m_register_map[(i << 1) + 0xda] = (uint16_t*)&m_filter[i].m_table_pos; - m_register_map[(i << 1) + 0xde] = (uint16_t*)&m_wet[i].m_delay; - m_register_map[(i << 1) + 0xe4] = (uint16_t*)&m_wet[i].m_volume; - // Dry - m_register_map[(i << 1) + 0xdb] = (uint16_t*)&m_alt_filter[i].m_table_pos; - m_register_map[(i << 1) + 0xdf] = (uint16_t*)&m_dry[i].m_delay; - m_register_map[(i << 1) + 0xe5] = (uint16_t*)&m_dry[i].m_volume; - } + /* Port ready bit (0x80 if ready) */ + return 0x80; } -int16_t qsound_hle_device::read_sample(uint16_t bank, uint16_t address) -{ - bank &= 0x7FFF; - const uint32_t rom_addr = (bank << 16) | (address << 0); - const uint8_t sample_data = read_byte(rom_addr); - return (int16_t)(sample_data << 8); // bit0-7 is tied to ground -} - -/********************************************************************/ - -// updates one DSP sample -void qsound_hle_device::update_sample() -{ - switch (m_state) - { - default: - case STATE_INIT1: - case STATE_INIT2: - return state_init(); - case STATE_REFRESH1: - return state_refresh_filter_1(); - case STATE_REFRESH2: - return state_refresh_filter_2(); - case STATE_NORMAL1: - case STATE_NORMAL2: - return state_normal_update(); - } -} -// Initialization routine -void qsound_hle_device::state_init() +void qsound_hle_device::write_data(u8 address, u16 data) { - int mode = (m_state == STATE_INIT2) ? 1 : 0; + int ch = 0, reg; - // we're busy for 4 samples, including the filter refresh. - if (m_state_counter >= 2) + if (address < 0x80) { - m_state_counter = 0; - m_state = m_next_state; - return; + ch = address >> 3; + reg = address & 7; } - else if (m_state_counter == 1) + else if (address < 0x90) { - m_state_counter++; - return; + ch = address & 0xf; + reg = 8; } - - std::fill(std::begin(m_voice), std::end(m_voice), qsound_voice()); - std::fill(std::begin(m_adpcm), std::end(m_adpcm), qsound_adpcm()); - std::fill(std::begin(m_filter), std::end(m_filter), qsound_fir()); - std::fill(std::begin(m_alt_filter), std::end(m_alt_filter), qsound_fir()); - std::fill(std::begin(m_wet), std::end(m_wet), qsound_delay()); - std::fill(std::begin(m_dry), std::end(m_dry), qsound_delay()); - m_echo = qsound_echo(); - - for (int i = 0; i < 19; i++) + else if (address >= 0xba && address < 0xca) { - m_voice_pan[i] = DATA_PAN_TAB + 0x10; - m_voice_output[i] = 0; - } - - for (int i = 0; i < 16; i++) - m_voice[i].m_bank = 0x8000; - for (int i = 0; i < 3; i++) - m_adpcm[i].m_bank = 0x8000; - - if (mode == 0) - { - // mode 1 - m_wet[0].m_delay = 0; - m_dry[0].m_delay = 46; - m_wet[1].m_delay = 0; - m_dry[1].m_delay = 48; - m_filter[0].m_table_pos = DATA_FILTER_TAB + (FILTER_ENTRY_SIZE*1); - m_filter[1].m_table_pos = DATA_FILTER_TAB + (FILTER_ENTRY_SIZE*2); - m_echo.m_end_pos = DELAY_BASE_OFFSET + 6; - m_next_state = STATE_REFRESH1; + ch = address - 0xba; + reg = 9; } else { - // mode 2 - m_wet[0].m_delay = 1; - m_dry[0].m_delay = 0; - m_wet[1].m_delay = 0; - m_dry[1].m_delay = 0; - m_filter[0].m_table_pos = 0xf73; - m_filter[1].m_table_pos = 0xfa4; - m_alt_filter[0].m_table_pos = 0xf73; - m_alt_filter[1].m_table_pos = 0xfa4; - m_echo.m_end_pos = DELAY_BASE_OFFSET2 + 6; - m_next_state = STATE_REFRESH2; + // unknown + reg = address; } - m_wet[0].m_volume = 0x3fff; - m_dry[0].m_volume = 0x3fff; - m_wet[1].m_volume = 0x3fff; - m_dry[1].m_volume = 0x3fff; - - m_delay_update = 1; - m_ready_flag = 0; - m_state_counter = 1; -} - -// Updates filter parameters for mode 1 -void qsound_hle_device::state_refresh_filter_1() -{ - for (int ch = 0; ch < 2; ch++) + switch (reg) { - m_filter[ch].m_delay_pos = 0; - m_filter[ch].m_tap_count = 95; + case 0: // bank + case 1: // current sample + case 2: // playback rate + case 3: // sample interval counter + case 4: // loop offset + case 5: // end sample + case 6: // channel volume + case 7: // unused + m_channel[ch].reg[reg] = data; + break; - for (int i = 0; i < 95; i++) - m_filter[ch].m_taps[i] = read_dsp_rom(m_filter[ch].m_table_pos + i); - } - - m_state = m_next_state = STATE_NORMAL1; -} - -// Updates filter parameters for mode 2 -void qsound_hle_device::state_refresh_filter_2() -{ - for (int ch = 0; ch < 2; ch++) + case 8: { - m_filter[ch].m_delay_pos = 0; - m_filter[ch].m_tap_count = 45; - - for (int i = 0; i < 45; i++) - m_filter[ch].m_taps[i] = (int16_t)read_dsp_rom(m_filter[ch].m_table_pos + i); - - m_alt_filter[ch].m_delay_pos = 0; - m_alt_filter[ch].m_tap_count = 44; + // panning (left=0x0110, centre=0x0120, right=0x0130) + // looks like it doesn't write other values than that + int pan = (data & 0x3f) - 0x10; + if (pan > 0x20) + pan = 0x20; + if (pan < 0) + pan = 0; - for (int i = 0; i < 44; i++) - m_alt_filter[ch].m_taps[i] = (int16_t)read_dsp_rom(m_alt_filter[ch].m_table_pos + i); + m_channel[ch].rvol = m_pan_table[pan]; + m_channel[ch].lvol = m_pan_table[0x20 - pan]; + break; } - m_state = m_next_state = STATE_NORMAL2; -} - -// Updates a PCM voice. There are 16 voices, each are updated every sample -// with full rate and volume control. -int16_t qsound_hle_device::qsound_voice::update(qsound_hle_device &dsp, int32_t *echo_out) -{ - // Read sample from rom and apply volume - const int16_t output = (m_volume * dsp.read_sample(m_bank, m_addr)) >> 14; - - *echo_out += (output * m_echo) << 2; - - // Add delta to the phase and loop back if required - int32_t new_phase = m_rate + ((m_addr << 12) | (m_phase >> 4)); + case 9: + // unknown + break; - if ((new_phase >> 12) >= m_end_addr) - new_phase -= (m_loop_len << 12); - - new_phase = std::min<int32_t>(std::max<int32_t>(new_phase, -0x8000000), 0x7FFFFFF); - m_addr = new_phase >> 12; - m_phase = (new_phase << 4)&0xffff; - - return output; -} - -// Updates an ADPCM voice. There are 3 voices, one is updated every sample -// (effectively making the ADPCM rate 1/3 of the master sample rate), and -// volume is set when starting samples only. -// The ADPCM algorithm is supposedly similar to Yamaha ADPCM. It also seems -// like Capcom never used it, so this was not emulated in the earlier QSound -// emulators. -int16_t qsound_hle_device::qsound_adpcm::update(qsound_hle_device &dsp, int16_t curr_sample, int nibble) -{ - int8_t step; - if (!nibble) - { - // Mute voice when it reaches the end address. - if (m_cur_addr == m_end_addr) - m_cur_vol = 0; - - // Playback start flag - if (m_flag) - { - curr_sample = 0; - m_flag = 0; - m_step_size = 10; - m_cur_vol = m_volume; - m_cur_addr = m_start_addr; - } - - // get top nibble - step = dsp.read_sample(m_bank, m_cur_addr) >> 8; + default: + //logerror("%s: write_data %02x = %04x\n", machine().describe_context(), address, data); + break; } - else - { - // get bottom nibble - step = dsp.read_sample(m_bank, m_cur_addr++) >> 4; - } - - // shift with sign extend - step >>= 4; - - // delta = (0.5 + abs(step)) * m_step_size - int32_t delta = ((1 + abs(step << 1)) * m_step_size) >> 1; - if (step <= 0) - delta = -delta; - delta += curr_sample; - delta = std::min<int32_t>(std::max<int32_t>(delta, -32768), 32767); - - m_step_size = (dsp.read_dsp_rom(DATA_ADPCM_TAB + 8 + step) * m_step_size) >> 6; - m_step_size = std::min<int16_t>(std::max<int16_t>(m_step_size, 1), 2000); - - return (delta * m_cur_vol) >> 16; -} - -// The echo effect is pretty simple. A moving average filter is used on -// the output from the delay line to smooth samples over time. -int16_t qsound_hle_device::qsound_echo::apply(int32_t input) -{ - // get average of last 2 samples from the delay line - int32_t old_sample = m_delay_line[m_delay_pos]; - const int32_t last_sample = m_last_sample; - m_last_sample = old_sample; - old_sample = (old_sample + last_sample) >> 1; - - // add current sample to the delay line - int32_t new_sample = input + ((old_sample * m_feedback) << 2); - m_delay_line[m_delay_pos++] = new_sample >> 16; - - if (m_delay_pos >= m_length) - m_delay_pos = 0; - - return old_sample; -} - -// Process a sample update -void qsound_hle_device::state_normal_update() -{ - m_ready_flag = 0x80; - - // recalculate echo length - if (m_state == STATE_NORMAL2) - m_echo.m_length = m_echo.m_end_pos - DELAY_BASE_OFFSET2; - else - m_echo.m_length = m_echo.m_end_pos - DELAY_BASE_OFFSET; - - m_echo.m_length = std::min<int16_t>(std::max<int16_t>(m_echo.m_length, 0), 1024); - - // update PCM voices - int32_t echo_input = 0; - for (int i = 0; i < 16; i++) - m_voice_output[i] = m_voice[i].update(*this, &echo_input); - - // update ADPCM voices (one every third sample) - const int adpcm_voice = m_state_counter % 3; - m_adpcm[adpcm_voice].update(*this, m_voice_output[16 + adpcm_voice], m_state_counter / 3); - - int16_t echo_output = m_echo.apply(echo_input); - - // now, we do the magic stuff - for (int ch = 0; ch < 2; ch++) - { - // Echo is output on the unfiltered component of the left channel and - // the filtered component of the right channel. - int32_t wet = (ch == 1) ? echo_output << 16 : 0; - int32_t dry = (ch == 0) ? echo_output << 16 : 0; - - for (int i = 0; i < 19; i++) - { - uint16_t pan_index = m_voice_pan[i] + (ch * PAN_TABLE_CH_OFFSET); - - // Apply different volume tables on the dry and wet inputs. - dry -= (m_voice_output[i] * (int16_t)read_dsp_rom(pan_index + PAN_TABLE_DRY)) << 2; - wet -= (m_voice_output[i] * (int16_t)read_dsp_rom(pan_index + PAN_TABLE_WET)) << 2; - } - // Apply FIR filter on 'wet' input - wet = m_filter[ch].apply(wet >> 16); - - // in mode 2, we do this on the 'dry' input too - if (m_state == STATE_NORMAL2) - dry = m_alt_filter[ch].apply(dry >> 16); - - // output goes through a delay line and attenuation - int32_t output = (m_wet[ch].apply(wet) + m_dry[ch].apply(dry)) << 2; - - // DSP round function - output = (output + 0x8000) & ~0xffff; - m_out[ch] = output >> 16; - - if (m_delay_update) - { - m_wet[ch].update(); - m_dry[ch].update(); - } - } - - m_delay_update = 0; - - // after 6 samples, the next state is executed. - m_state_counter++; - if (m_state_counter > 5) - { - m_state_counter = 0; - m_state = m_next_state; - } -} - -// Apply the FIR filter used as the Q1 transfer function -int32_t qsound_hle_device::qsound_fir::apply(int16_t input) -{ - int32_t output = 0, tap = 0; - for (; tap < (m_tap_count - 1); tap++) - { - output -= (m_taps[tap] * m_delay_line[m_delay_pos++]) << 2; - - if (m_delay_pos >= m_tap_count - 1) - m_delay_pos = 0; - } - - output -= (m_taps[tap] * input) << 2; - - m_delay_line[m_delay_pos++] = input; - if (m_delay_pos >= m_tap_count - 1) - m_delay_pos = 0; - - return output; -} - -// Apply delay line and component volume -int32_t qsound_hle_device::qsound_delay::apply(const int32_t input) -{ - m_delay_line[m_write_pos++] = input >> 16; - if (m_write_pos >= 51) - m_write_pos = 0; - - const int32_t output = m_delay_line[m_read_pos++] * m_volume; - if (m_read_pos >= 51) - m_read_pos = 0; - - return output; -} - -// Update the delay read position to match new delay length -void qsound_hle_device::qsound_delay::update() -{ - const int16_t new_read_pos = (m_write_pos - m_delay) % 51; - if (new_read_pos < 0) - m_read_pos = new_read_pos + 51; - else - m_read_pos = new_read_pos; } |