SPU: Port reverb algorithm from Mednafen-PSX

This commit is contained in:
Connor McLaughlin 2020-04-25 00:37:02 +10:00
parent 6e6c11f8bf
commit 191c3450cc
3 changed files with 240 additions and 170 deletions

View file

@ -2,7 +2,7 @@
#include "types.h" #include "types.h"
static constexpr u32 SAVE_STATE_MAGIC = 0x43435544; static constexpr u32 SAVE_STATE_MAGIC = 0x43435544;
static constexpr u32 SAVE_STATE_VERSION = 24; static constexpr u32 SAVE_STATE_VERSION = 25;
#pragma pack(push, 4) #pragma pack(push, 4)
struct SAVE_STATE_HEADER struct SAVE_STATE_HEADER

View file

@ -28,7 +28,6 @@ void SPU::Initialize(System* system, DMA* dma, InterruptController* interrupt_co
void SPU::Reset() void SPU::Reset()
{ {
m_tick_counter = 0;
m_ticks_carry = 0; m_ticks_carry = 0;
m_SPUCNT.bits = 0; m_SPUCNT.bits = 0;
@ -57,11 +56,10 @@ void SPU::Reset()
m_reverb_on_register = 0; m_reverb_on_register = 0;
m_reverb_registers = {}; m_reverb_registers = {};
m_reverb_registers.mBASE = 0; m_reverb_registers.mBASE = 0;
m_reverb_current_address = ZeroExtend32(m_reverb_registers.mBASE) * 8; m_reverb_base_address = m_reverb_current_address = ZeroExtend32(m_reverb_registers.mBASE) << 2;
m_reverb_left_input = 0; m_reverb_downsample_buffer = {};
m_reverb_left_output = 0; m_reverb_upsample_buffer = {};
m_reverb_right_input = 0; m_reverb_resample_buffer_position = 0;
m_reverb_right_output = 0;
for (u32 i = 0; i < NUM_VOICES; i++) for (u32 i = 0; i < NUM_VOICES; i++)
{ {
@ -85,7 +83,6 @@ void SPU::Reset()
bool SPU::DoState(StateWrapper& sw) bool SPU::DoState(StateWrapper& sw)
{ {
sw.Do(&m_tick_counter);
sw.Do(&m_ticks_carry); sw.Do(&m_ticks_carry);
sw.Do(&m_SPUCNT.bits); sw.Do(&m_SPUCNT.bits);
sw.Do(&m_SPUSTAT.bits); sw.Do(&m_SPUSTAT.bits);
@ -110,15 +107,17 @@ bool SPU::DoState(StateWrapper& sw)
sw.Do(&m_noise_count); sw.Do(&m_noise_count);
sw.Do(&m_noise_level); sw.Do(&m_noise_level);
sw.Do(&m_reverb_on_register); sw.Do(&m_reverb_on_register);
sw.Do(&m_reverb_base_address);
sw.Do(&m_reverb_current_address); sw.Do(&m_reverb_current_address);
sw.Do(&m_reverb_registers.vLOUT); sw.Do(&m_reverb_registers.vLOUT);
sw.Do(&m_reverb_registers.vROUT); sw.Do(&m_reverb_registers.vROUT);
sw.Do(&m_reverb_registers.mBASE); sw.Do(&m_reverb_registers.mBASE);
sw.DoArray(m_reverb_registers.rev, NUM_REVERB_REGS); sw.DoArray(m_reverb_registers.rev, NUM_REVERB_REGS);
sw.Do(&m_reverb_left_input); for (u32 i = 0; i < 2; i++)
sw.Do(&m_reverb_right_input); sw.DoArray(m_reverb_downsample_buffer.data(), m_reverb_downsample_buffer.size());
sw.Do(&m_reverb_left_output); for (u32 i = 0; i < 2; i++)
sw.Do(&m_reverb_right_output); sw.DoArray(m_reverb_upsample_buffer.data(), m_reverb_upsample_buffer.size());
sw.Do(&m_reverb_resample_buffer_position);
for (u32 i = 0; i < NUM_VOICES; i++) for (u32 i = 0; i < NUM_VOICES; i++)
{ {
Voice& v = m_voices[i]; Voice& v = m_voices[i];
@ -396,7 +395,8 @@ void SPU::WriteRegister(u32 offset, u16 value)
Log_DebugPrintf("SPU reverb base address < 0x%04X", ZeroExtend32(value)); Log_DebugPrintf("SPU reverb base address < 0x%04X", ZeroExtend32(value));
m_tick_event->InvokeEarly(); m_tick_event->InvokeEarly();
m_reverb_registers.mBASE = value; m_reverb_registers.mBASE = value;
m_reverb_current_address = ZeroExtend32(m_reverb_registers.mBASE) * 8u; m_reverb_base_address = ZeroExtend32(value << 2) & 0x3FFFFu;
m_reverb_current_address = m_reverb_base_address;
} }
break; break;
@ -731,20 +731,12 @@ void SPU::Execute(TickCount ticks)
} }
// Compute reverb. // Compute reverb.
m_tick_counter++; s32 reverb_out_left, reverb_out_right;
if ((m_tick_counter & 1u) != 0) ProcessReverb(Clamp16(reverb_in_left), Clamp16(reverb_in_right), &reverb_out_left, &reverb_out_right);
{
m_reverb_left_input = Clamp16(reverb_in_left);
}
else
{
m_reverb_right_input = Clamp16(reverb_in_right);
DoReverb();
}
// Mix in reverb. // Mix in reverb.
left_sum += m_reverb_left_output; left_sum += reverb_out_left;
right_sum += m_reverb_right_output; right_sum += reverb_out_right;
// Apply main volume before clamping. // Apply main volume before clamping.
*(output_frame++) = Clamp16(ApplyVolume(left_sum, m_main_volume_left.current_level)); *(output_frame++) = Clamp16(ApplyVolume(left_sum, m_main_volume_left.current_level));
@ -1513,143 +1505,208 @@ void SPU::UpdateNoise()
m_noise_level = (m_noise_level << 1) | noise_wave_add[(m_noise_level >> 10) & 63u]; m_noise_level = (m_noise_level << 1) | noise_wave_add[(m_noise_level >> 10) & 63u];
} }
/************************************************************************/
/* Reverb algorithm from Mednafen-PSX */
/************************************************************************/
u32 SPU::ReverbMemoryAddress(u32 address) const u32 SPU::ReverbMemoryAddress(u32 address) const
{ {
// Ensures address does not leave the reverb work area. // Ensures address does not leave the reverb work area.
const u32 mBASE = ZeroExtend32(m_reverb_registers.mBASE) * 8; static constexpr u32 MASK = (RAM_SIZE - 1) / 2;
const u32 relative_address = (address - mBASE) % (RAM_SIZE - mBASE); u32 offset = m_reverb_current_address + (address & MASK);
return (mBASE + relative_address) & 0x7FFFEu; offset += m_reverb_base_address & ((s32)(offset << 13) >> 31);
// We address RAM in bytes. TODO: Change this to words.
return (offset & MASK) * 2u;
} }
s16 SPU::ReverbRead(u32 address) s16 SPU::ReverbRead(u32 address, s32 offset)
{ {
const u32 real_address = ReverbMemoryAddress(m_reverb_current_address + address); // TODO: This should check interrupts.
const u32 real_address = ReverbMemoryAddress((address << 2) + offset);
// TODO: Should this check interrupts?
s16 data; s16 data;
std::memcpy(&data, &m_ram[real_address & RAM_MASK], sizeof(data)); std::memcpy(&data, &m_ram[real_address], sizeof(data));
return data; return data;
} }
void SPU::ReverbWrite(u32 address, s16 data) void SPU::ReverbWrite(u32 address, s16 data)
{ {
if (!m_SPUCNT.reverb_master_enable) // TODO: This should check interrupts.
return; const u32 real_address = ReverbMemoryAddress(address << 2);
std::memcpy(&m_ram[real_address], &data, sizeof(data));
// TODO: Should this check interrupts?
const u32 real_address = ReverbMemoryAddress(m_reverb_current_address + address);
std::memcpy(&m_ram[real_address & RAM_MASK], &data, sizeof(data));
} }
// Implements saturated add, subtract and multiply for reverb computations. // Zeroes optimized out; middle removed too(it's 16384)
struct ReverbSample static constexpr std::array<s16, 20> s_reverb_resample_coefficients = {
{ -1, 2, -10, 35, -103, 266, -616, 1332, -2960, 10246, 10246, -2960, 1332, -616, 266, -103, 35, -10, 2, -1,
s16 value;
static ALWAYS_INLINE s16 OpAdd(s16 lhs, s16 rhs)
{
s32 result = s32(lhs) + s32(rhs);
return s16((result < -32768) ? -32768 : ((result > 32767) ? 32767 : result));
}
static ALWAYS_INLINE s16 OpSub(s16 lhs, s16 rhs)
{
s32 result = s32(lhs) - s32(rhs);
return s16((result < -32768) ? -32768 : ((result > 32767) ? 32767 : result));
}
static ALWAYS_INLINE s16 OpMul(s16 lhs, s16 rhs) { return s16((s32(lhs) * s32(rhs)) >> 15); }
ALWAYS_INLINE ReverbSample operator+(ReverbSample rhs) const { return ReverbSample{OpAdd(value, rhs.value)}; }
ALWAYS_INLINE ReverbSample operator-(ReverbSample rhs) const { return ReverbSample{OpSub(value, rhs.value)}; }
ALWAYS_INLINE ReverbSample operator*(ReverbSample rhs) const { return ReverbSample{OpMul(value, rhs.value)}; }
ALWAYS_INLINE ReverbSample& operator+=(ReverbSample rhs)
{
value = OpAdd(value, rhs.value);
return *this;
}
ALWAYS_INLINE ReverbSample& operator-=(ReverbSample rhs)
{
value = OpSub(value, rhs.value);
return *this;
}
ALWAYS_INLINE ReverbSample& operator*=(ReverbSample rhs)
{
value = OpMul(value, rhs.value);
return *this;
}
}; };
static s16 s_last_reverb_input[2];
static s32 s_last_reverb_output[2];
void SPU::DoReverb() ALWAYS_INLINE static s32 Reverb4422(const s16* src)
{ {
const ReverbSample Lin(ReverbSample{m_reverb_left_input} * ReverbSample{m_reverb_registers.vLIN}); s32 out = 0; // 32-bits is adequate(it won't overflow)
const ReverbSample Rin(ReverbSample{m_reverb_right_input} * ReverbSample{m_reverb_registers.vRIN}); for (u32 i = 0; i < 20; i++)
out += s_reverb_resample_coefficients[i] * src[i * 2];
#define R(name) \ // Middle non-zero
ReverbSample { m_reverb_registers.name } out += 0x4000 * src[19];
#define Rm(name) (u32(m_reverb_registers.name) * 8u) out >>= 15;
#define MR(addr) \ return std::clamp<s32>(out, -32768, 32767);
ReverbSample { ReverbRead(addr) } }
#define MW(addr, value_) ReverbWrite((addr), (value_).value)
// [mLSAME] = (Lin + [dLSAME]*vWALL - [mLSAME-2])*vIIR + [mLSAME-2] ;L-to-L template<bool phase>
MW(Rm(mLSAME), ((Lin + (MR(Rm(dLSAME)) * R(vWALL)) - MR(Rm(mLSAME) - 2)) * R(vIIR)) + MR(Rm(mLSAME) - 2)); ALWAYS_INLINE static s32 Reverb2244(const s16* src)
{
s32 out; // 32-bits is adequate(it won't overflow)
if (phase)
{
// Middle non-zero
out = src[9];
}
else
{
out = 0;
for (u32 i = 0; i < 20; i++)
out += s_reverb_resample_coefficients[i] * src[i];
// [mRSAME] = (Rin + [dRSAME]*vWALL - [mRSAME-2])*vIIR + [mRSAME-2] ;R-to-R out >>= 14;
MW(Rm(mLSAME), ((Rin + (MR(Rm(dRSAME)) * R(vWALL)) - MR(Rm(mRSAME) - 2)) * R(vIIR)) + MR(Rm(mRSAME) - 2)); out = std::clamp<s32>(out, -32768, 32767);
}
// [mLDIFF] = (Lin + [dRDIFF]*vWALL - [mLDIFF-2])*vIIR + [mLDIFF-2] ;R-to-L return out;
MW(Rm(mLDIFF), ((Lin + (MR(Rm(dRDIFF)) * R(vWALL)) - MR(Rm(mLDIFF) - 2)) * R(vIIR)) + MR(Rm(mLDIFF) - 2)); }
// [mRDIFF] = (Rin + [dLDIFF]*vWALL - [mRDIFF-2])*vIIR + [mRDIFF-2] ;L-to-R ALWAYS_INLINE static s16 ReverbSat(s32 val)
MW(Rm(mRDIFF), ((Rin + (MR(Rm(dLDIFF)) * R(vWALL)) - MR(Rm(mRDIFF) - 2)) * R(vIIR)) + MR(Rm(mRDIFF) - 2)); {
return static_cast<s16>(std::clamp<s32>(val, -0x8000, 0x7FFF));
}
// Lout = vCOMB1 * [mLCOMB1] + vCOMB2 * [mLCOMB2] + vCOMB3 * [mLCOMB3] + vCOMB4 * [mLCOMB4] ALWAYS_INLINE static s32 IIASM(const s16 IIR_ALPHA, const s16 insamp)
ReverbSample Lout{(R(vCOMB1) * MR(Rm(mLCOMB1))) + (R(vCOMB2) * MR(Rm(mLCOMB2))) + (R(vCOMB3) * MR(Rm(mLCOMB3))) + {
(R(vCOMB4) * MR(Rm(mLCOMB4)))}; if (IIR_ALPHA == -32768)
{
if (insamp == -32768)
return 0;
else
return insamp * -65536;
}
else
return insamp * (32768 - IIR_ALPHA);
}
// Rout = vCOMB1 * [mRCOMB1] + vCOMB2 * [mRCOMB2] + vCOMB3 * [mRCOMB3] + vCOMB4 * [mRCOMB4] void SPU::ComputeReverb()
ReverbSample Rout{(R(vCOMB1) * MR(Rm(mRCOMB1))) + (R(vCOMB2) * MR(Rm(mRCOMB2))) + (R(vCOMB3) * MR(Rm(mRCOMB3))) + {
(R(vCOMB4) * MR(Rm(mRCOMB4)))}; std::array<s32, 2> downsampled;
for (unsigned lr = 0; lr < 2; lr++)
downsampled[lr] = Reverb4422(&m_reverb_downsample_buffer[lr][(m_reverb_resample_buffer_position - 39) & 0x3F]);
// Lout = Lout - vAPF1 * [mLAPF1 - dAPF1], [mLAPF1] = Lout, Lout = Lout * vAPF1 + [mLAPF1 - dAPF1] if (m_SPUCNT.reverb_master_enable)
Lout = Lout - (R(vAPF1) * MR(Rm(mLAPF1) - Rm(dAPF1))); {
MW(Rm(mLAPF1), Lout); const s16 IIR_INPUT_A0 =
Lout = (Lout * R(vAPF1)) + MR(Rm(mLAPF1) - Rm(dAPF1)); ReverbSat(((ReverbRead(m_reverb_registers.IIR_SRC_A0) * m_reverb_registers.IIR_COEF) >> 15) +
((downsampled[0] * m_reverb_registers.IN_COEF_L) >> 15));
const s16 IIR_INPUT_A1 =
ReverbSat(((ReverbRead(m_reverb_registers.IIR_SRC_A1) * m_reverb_registers.IIR_COEF) >> 15) +
((downsampled[1] * m_reverb_registers.IN_COEF_R) >> 15));
const s16 IIR_INPUT_B0 =
ReverbSat(((ReverbRead(m_reverb_registers.IIR_SRC_B0) * m_reverb_registers.IIR_COEF) >> 15) +
((downsampled[0] * m_reverb_registers.IN_COEF_L) >> 15));
const s16 IIR_INPUT_B1 =
ReverbSat(((ReverbRead(m_reverb_registers.IIR_SRC_B1) * m_reverb_registers.IIR_COEF) >> 15) +
((downsampled[1] * m_reverb_registers.IN_COEF_R) >> 15));
// Rout = Rout - vAPF1 * [mRAPF1 - dAPF1], [mRAPF1] = Rout, Rout = Rout * vAPF1 + [mRAPF1 - dAPF1] const s16 IIR_A0 =
Rout = Rout - (R(vAPF1) * MR(Rm(mRAPF1) - Rm(dAPF1))); ReverbSat((((IIR_INPUT_A0 * m_reverb_registers.IIR_ALPHA) >> 14) +
MW(Rm(mRAPF1), Rout); (IIASM(m_reverb_registers.IIR_ALPHA, ReverbRead(m_reverb_registers.IIR_DEST_A0, -1)) >> 14)) >>
Rout = (Rout * R(vAPF1)) + MR(Rm(mRAPF1) - Rm(dAPF1)); 1);
const s16 IIR_A1 =
ReverbSat((((IIR_INPUT_A1 * m_reverb_registers.IIR_ALPHA) >> 14) +
(IIASM(m_reverb_registers.IIR_ALPHA, ReverbRead(m_reverb_registers.IIR_DEST_A1, -1)) >> 14)) >>
1);
const s16 IIR_B0 =
ReverbSat((((IIR_INPUT_B0 * m_reverb_registers.IIR_ALPHA) >> 14) +
(IIASM(m_reverb_registers.IIR_ALPHA, ReverbRead(m_reverb_registers.IIR_DEST_B0, -1)) >> 14)) >>
1);
const s16 IIR_B1 =
ReverbSat((((IIR_INPUT_B1 * m_reverb_registers.IIR_ALPHA) >> 14) +
(IIASM(m_reverb_registers.IIR_ALPHA, ReverbRead(m_reverb_registers.IIR_DEST_B1, -1)) >> 14)) >>
1);
// Lout = Lout - vAPF2 * [mLAPF2 - dAPF2], [mLAPF2] = Lout, Lout = Lout * vAPF2 + [mLAPF2 - dAPF2] ReverbWrite(m_reverb_registers.IIR_DEST_A0, IIR_A0);
Lout = Lout - (R(vAPF2) * MR(Rm(mLAPF2) - Rm(dAPF2))); ReverbWrite(m_reverb_registers.IIR_DEST_A1, IIR_A1);
MW(Rm(mLAPF2), Lout); ReverbWrite(m_reverb_registers.IIR_DEST_B0, IIR_B0);
Lout = (Lout * R(vAPF2)) + MR(Rm(mLAPF2) - Rm(dAPF2)); ReverbWrite(m_reverb_registers.IIR_DEST_B1, IIR_B1);
// Rout = Rout - vAPF2 * [mRAPF2 - dAPF2], [mRAPF2] = Rout, Rout = Rout * vAPF2 + [mRAPF2 - dAPF2] const s16 ACC0 = ReverbSat((((ReverbRead(m_reverb_registers.ACC_SRC_A0) * m_reverb_registers.ACC_COEF_A) >> 14) +
Rout = Rout - (R(vAPF2) * MR(Rm(mRAPF2) - Rm(dAPF2))); ((ReverbRead(m_reverb_registers.ACC_SRC_B0) * m_reverb_registers.ACC_COEF_B) >> 14) +
MW(Rm(mRAPF2), Rout); ((ReverbRead(m_reverb_registers.ACC_SRC_C0) * m_reverb_registers.ACC_COEF_C) >> 14) +
Rout = (Rout * R(vAPF2)) + MR(Rm(mRAPF2) - Rm(dAPF2)); ((ReverbRead(m_reverb_registers.ACC_SRC_D0) * m_reverb_registers.ACC_COEF_D) >> 14)) >>
1);
// LeftOutput = Lout*vLOUT const s16 ACC1 = ReverbSat((((ReverbRead(m_reverb_registers.ACC_SRC_A1) * m_reverb_registers.ACC_COEF_A) >> 14) +
m_reverb_left_output = (Lout * R(vLOUT)).value; ((ReverbRead(m_reverb_registers.ACC_SRC_B1) * m_reverb_registers.ACC_COEF_B) >> 14) +
((ReverbRead(m_reverb_registers.ACC_SRC_C1) * m_reverb_registers.ACC_COEF_C) >> 14) +
((ReverbRead(m_reverb_registers.ACC_SRC_D1) * m_reverb_registers.ACC_COEF_D) >> 14)) >>
1);
// RightOutput = Rout*vROUT const s16 FB_A0 = ReverbRead(m_reverb_registers.MIX_DEST_A0 - m_reverb_registers.FB_SRC_A);
m_reverb_right_output = (Rout * R(vROUT)).value; const s16 FB_A1 = ReverbRead(m_reverb_registers.MIX_DEST_A1 - m_reverb_registers.FB_SRC_A);
const s16 FB_B0 = ReverbRead(m_reverb_registers.MIX_DEST_B0 - m_reverb_registers.FB_SRC_B);
const s16 FB_B1 = ReverbRead(m_reverb_registers.MIX_DEST_B1 - m_reverb_registers.FB_SRC_B);
// BufferAddress = MAX(mBASE, (BufferAddress+2) AND 7FFFEh) ReverbWrite(m_reverb_registers.MIX_DEST_A0, ReverbSat(ACC0 - ((FB_A0 * m_reverb_registers.FB_ALPHA) >> 15)));
m_reverb_current_address = ReverbMemoryAddress(m_reverb_current_address + 2); ReverbWrite(m_reverb_registers.MIX_DEST_A1, ReverbSat(ACC1 - ((FB_A1 * m_reverb_registers.FB_ALPHA) >> 15)));
#undef MW ReverbWrite(m_reverb_registers.MIX_DEST_B0,
#undef MR ReverbSat(((m_reverb_registers.FB_ALPHA * ACC0) >> 15) -
#undef Rm ((FB_A0 * (s16)(0x8000 ^ m_reverb_registers.FB_ALPHA)) >> 15) -
#undef R ((FB_B0 * m_reverb_registers.FB_X) >> 15)));
ReverbWrite(m_reverb_registers.MIX_DEST_B1,
ReverbSat(((m_reverb_registers.FB_ALPHA * ACC1) >> 15) -
((FB_A1 * (s16)(0x8000 ^ m_reverb_registers.FB_ALPHA)) >> 15) -
((FB_B1 * m_reverb_registers.FB_X) >> 15)));
}
m_reverb_upsample_buffer[0][(m_reverb_resample_buffer_position >> 1) | 0x20] =
m_reverb_upsample_buffer[0][m_reverb_resample_buffer_position >> 1] =
(ReverbRead(m_reverb_registers.MIX_DEST_A0) + ReverbRead(m_reverb_registers.MIX_DEST_B0)) >> 1;
m_reverb_upsample_buffer[1][(m_reverb_resample_buffer_position >> 1) | 0x20] =
m_reverb_upsample_buffer[1][m_reverb_resample_buffer_position >> 1] =
(ReverbRead(m_reverb_registers.MIX_DEST_A1) + ReverbRead(m_reverb_registers.MIX_DEST_B1)) >> 1;
m_reverb_current_address = (m_reverb_current_address + 1) & 0x3FFFFu;
if (m_reverb_current_address == 0)
m_reverb_current_address = m_reverb_base_address;
}
void SPU::ProcessReverb(s16 left_in, s16 right_in, s32* left_out, s32* right_out)
{
s_last_reverb_input[0] = left_in;
s_last_reverb_input[1] = right_in;
m_reverb_downsample_buffer[0][m_reverb_resample_buffer_position | 0x00] = left_in;
m_reverb_downsample_buffer[0][m_reverb_resample_buffer_position | 0x40] = left_in;
m_reverb_downsample_buffer[1][m_reverb_resample_buffer_position | 0x00] = right_in;
m_reverb_downsample_buffer[1][m_reverb_resample_buffer_position | 0x40] = right_in;
s32 out[2];
if (m_reverb_resample_buffer_position & 1u)
{
ComputeReverb();
for (u32 i = 0; i < 2; i++)
out[i] = Reverb2244<true>(&m_reverb_upsample_buffer[i][((m_reverb_resample_buffer_position - 39) & 0x3F) >> 1]);
}
else
{
for (u32 i = 0; i < 2; i++)
out[i] = Reverb2244<false>(&m_reverb_upsample_buffer[i][((m_reverb_resample_buffer_position - 39) & 0x3F) >> 1]);
}
m_reverb_resample_buffer_position = (m_reverb_resample_buffer_position + 1) & 0x3F;
s_last_reverb_output[0] = *left_out = ApplyVolume(out[0], m_reverb_registers.vLOUT);
s_last_reverb_output[1] = *right_out = ApplyVolume(out[1], m_reverb_registers.vROUT);
} }
void SPU::EnsureCDAudioSpace(u32 remaining_frames) void SPU::EnsureCDAudioSpace(u32 remaining_frames)
@ -1817,8 +1874,8 @@ void SPU::DrawDebugStateWindow()
m_SPUCNT.external_audio_reverb ? "Yes" : "No"); m_SPUCNT.external_audio_reverb ? "Yes" : "No");
ImGui::Text("Current Address: 0x%08X", m_reverb_current_address); ImGui::Text("Current Address: 0x%08X", m_reverb_current_address);
ImGui::Text("Current Volume: Input (%d, %d) Output (%d, %d)", m_reverb_left_input, m_reverb_right_input, ImGui::Text("Current Volume: Input (%d, %d) Output (%d, %d)", s_last_reverb_input[0], s_last_reverb_input[1],
m_reverb_left_output, m_reverb_right_output); s_last_reverb_output[0], s_last_reverb_output[1]);
ImGui::Text("Pitch Modulation: "); ImGui::Text("Pitch Modulation: ");
for (u32 i = 1; i < NUM_VOICES; i++) for (u32 i = 1; i < NUM_VOICES; i++)
@ -1830,5 +1887,18 @@ void SPU::DrawDebugStateWindow()
} }
} }
if (ImGui::CollapsingHeader("Hacks", ImGuiTreeNodeFlags_DefaultOpen))
{
if (ImGui::Button("Key Off All Voices"))
{
for (u32 i = 0; i < NUM_VOICES; i++)
{
m_voices[i].KeyOff();
m_voices[i].adsr_envelope.counter = 0;
m_voices[i].regs.adsr_volume = 0;
}
}
}
ImGui::End(); ImGui::End();
} }

View file

@ -292,38 +292,38 @@ private:
{ {
struct struct
{ {
u16 dAPF1; u16 FB_SRC_A;
u16 dAPF2; u16 FB_SRC_B;
s16 vIIR; s16 IIR_ALPHA;
s16 vCOMB1; s16 ACC_COEF_A;
s16 vCOMB2; s16 ACC_COEF_B;
s16 vCOMB3; s16 ACC_COEF_C;
s16 vCOMB4; s16 ACC_COEF_D;
s16 vWALL; s16 IIR_COEF;
s16 vAPF1; s16 FB_ALPHA;
s16 vAPF2; s16 FB_X;
u16 mLSAME; u16 IIR_DEST_A0;
u16 mRSAME; u16 IIR_DEST_A1;
u16 mLCOMB1; u16 ACC_SRC_A0;
u16 mRCOMB1; u16 ACC_SRC_A1;
u16 mLCOMB2; u16 ACC_SRC_B0;
u16 mRCOMB2; u16 ACC_SRC_B1;
u16 dLSAME; u16 IIR_SRC_A0;
u16 dRSAME; u16 IIR_SRC_A1;
u16 mLDIFF; u16 IIR_DEST_B0;
u16 mRDIFF; u16 IIR_DEST_B1;
u16 mLCOMB3; u16 ACC_SRC_C0;
u16 mRCOMB3; u16 ACC_SRC_C1;
u16 mLCOMB4; u16 ACC_SRC_D0;
u16 mRCOMB4; u16 ACC_SRC_D1;
u16 dLDIFF; u16 IIR_SRC_B1;
u16 dRDIFF; u16 IIR_SRC_B0;
u16 mLAPF1; u16 MIX_DEST_A0;
u16 mRAPF1; u16 MIX_DEST_A1;
u16 mLAPF2; u16 MIX_DEST_B0;
u16 mRAPF2; u16 MIX_DEST_B1;
s16 vLIN; s16 IN_COEF_L;
s16 vRIN; s16 IN_COEF_R;
}; };
u16 rev[NUM_REVERB_REGS]; u16 rev[NUM_REVERB_REGS];
@ -366,9 +366,10 @@ private:
void UpdateNoise(); void UpdateNoise();
u32 ReverbMemoryAddress(u32 address) const; u32 ReverbMemoryAddress(u32 address) const;
s16 ReverbRead(u32 address); s16 ReverbRead(u32 address, s32 offset = 0);
void ReverbWrite(u32 address, s16 data); void ReverbWrite(u32 address, s16 data);
void DoReverb(); void ComputeReverb();
void ProcessReverb(s16 left_in, s16 right_in, s32* left_out, s32* right_out);
void Execute(TickCount ticks); void Execute(TickCount ticks);
void UpdateEventInterval(); void UpdateEventInterval();
@ -384,7 +385,6 @@ private:
std::unique_ptr<TimingEvent> m_tick_event; std::unique_ptr<TimingEvent> m_tick_event;
std::unique_ptr<TimingEvent> m_transfer_event; std::unique_ptr<TimingEvent> m_transfer_event;
std::unique_ptr<Common::WAVWriter> m_dump_writer; std::unique_ptr<Common::WAVWriter> m_dump_writer;
u32 m_tick_counter = 0;
TickCount m_ticks_carry = 0; TickCount m_ticks_carry = 0;
SPUCNT m_SPUCNT = {}; SPUCNT m_SPUCNT = {};
@ -418,12 +418,12 @@ private:
u32 m_noise_level = 0; u32 m_noise_level = 0;
u32 m_reverb_on_register = 0; u32 m_reverb_on_register = 0;
u32 m_reverb_base_address = 0;
u32 m_reverb_current_address = 0; u32 m_reverb_current_address = 0;
ReverbRegisters m_reverb_registers{}; ReverbRegisters m_reverb_registers{};
s16 m_reverb_left_input = 0; std::array<std::array<s16, 128>, 2> m_reverb_downsample_buffer;
s16 m_reverb_right_input = 0; std::array<std::array<s16, 64>, 2> m_reverb_upsample_buffer;
s16 m_reverb_left_output = 0; s32 m_reverb_resample_buffer_position = 0;
s16 m_reverb_right_output = 0;
std::array<Voice, NUM_VOICES> m_voices{}; std::array<Voice, NUM_VOICES> m_voices{};