DSPHLE: add support for dynamic range compression

The CPU-side AX library enables it by default and uses hardcoded parameters.
CMD_COMPRESSOR_TABLE_ADDR (0x0A) was incorrect. It's always a nop on the
GameCube and was probably confused with the Wii version.
This commit is contained in:
Tillmann Karras 2021-07-21 07:43:48 +01:00
parent cba4b6ca32
commit 09faf0987d
4 changed files with 81 additions and 25 deletions

View File

@ -23,7 +23,8 @@
namespace DSP::HLE
{
AXUCode::AXUCode(DSPHLE* dsphle, u32 crc) : UCodeInterface(dsphle, crc), m_cmdlist_size(0)
AXUCode::AXUCode(DSPHLE* dsphle, u32 crc)
: UCodeInterface(dsphle, crc), m_cmdlist_size(0), m_compressor_pos(0)
{
INFO_LOG_FMT(DSPHLE, "Instantiating AXUCode: crc={:08x}", crc);
}
@ -181,13 +182,11 @@ void AXUCode::HandleCommandList()
MixAUXSamples(1, 0, HILO_TO_32(addr));
break;
case CMD_COMPRESSOR_TABLE_ADDR:
curr_idx += 2;
break;
case CMD_UNK_0A:
case CMD_UNK_0B:
break; // TODO: check other versions
case CMD_UNK_0C:
break; // TODO: check other versions
// nop in all 6 known ucodes we handle here
break;
case CMD_MORE:
addr_hi = m_cmdlist[curr_idx++];
@ -224,16 +223,16 @@ void AXUCode::HandleCommandList()
SetOppositeLR(HILO_TO_32(addr));
break;
case CMD_UNK_12:
case CMD_COMPRESSOR:
{
u16 samp_val = m_cmdlist[curr_idx++];
u16 idx = m_cmdlist[curr_idx++];
// 0x4e8a8b21 doesn't have this command, but it doesn't range-check
// the value properly and ends up jumping into a mixer function
ASSERT(m_crc != 0x4e8a8b21);
u16 threshold = m_cmdlist[curr_idx++];
u16 frames = m_cmdlist[curr_idx++];
addr_hi = m_cmdlist[curr_idx++];
addr_lo = m_cmdlist[curr_idx++];
// TODO
// suppress warnings:
(void)samp_val;
(void)idx;
RunCompressor(threshold, frames, HILO_TO_32(addr), 5);
break;
}
@ -508,6 +507,52 @@ void AXUCode::SetMainLR(u32 src_addr)
}
}
void AXUCode::RunCompressor(u16 threshold, u16 release_frames, u32 table_addr, u32 millis)
{
// check for L/R samples exceeding the threshold
bool triggered = false;
for (u32 i = 0; i < 32 * millis; ++i)
{
if (std::abs(m_samples_left[i]) > int(threshold) ||
std::abs(m_samples_right[i]) > int(threshold))
{
triggered = true;
break;
}
}
const u32 frame_byte_size = 32 * millis * sizeof(s16);
u32 table_offset = 0;
if (triggered)
{
// one attack frame based on previous frame
table_offset = m_compressor_pos * frame_byte_size;
// next frame will start release
m_compressor_pos = release_frames;
}
else if (m_compressor_pos)
{
// release
--m_compressor_pos;
// the release ramps are located after the attack ramps
constexpr u32 ATTACK_ENTRY_COUNT = 11;
table_offset = (ATTACK_ENTRY_COUNT + m_compressor_pos) * frame_byte_size;
}
else
{
return;
}
// apply the selected ramp
u16* ramp = (u16*)HLEMemory_Get_Pointer(table_addr + table_offset);
for (u32 i = 0; i < 32 * millis; ++i)
{
u16 coef = Common::swap16(*ramp++);
m_samples_left[i] = (s64(m_samples_left[i]) * coef) >> 15;
m_samples_right[i] = (s64(m_samples_right[i]) * coef) >> 15;
}
}
void AXUCode::OutputSamples(u32 lr_addr, u32 surround_addr)
{
int surround_buffer[5 * 32];

View File

@ -98,6 +98,8 @@ protected:
bool m_coeffs_available;
s16 m_coeffs[0x800];
u16 m_compressor_pos;
void LoadResamplingCoefficients();
// Copy a command list from memory to our temp buffer
@ -138,6 +140,7 @@ protected:
void MixAUXSamples(int aux_id, u32 write_addr, u32 read_addr);
void UploadLRS(u32 dst_addr);
void SetMainLR(u32 src_addr);
void RunCompressor(u16 threshold, u16 release_stages, u32 table_addr, u32 millis);
void OutputSamples(u32 out_addr, u32 surround_addr);
void MixAUXBLR(u32 ul_addr, u32 dl_addr);
void SetOppositeLR(u32 src_addr);
@ -160,7 +163,7 @@ private:
CMD_SET_LR = 0x07,
CMD_UNK_08 = 0x08,
CMD_MIX_AUXB_NOWRITE = 0x09,
CMD_COMPRESSOR_TABLE_ADDR = 0x0A,
CMD_UNK_0A = 0x0A,
CMD_UNK_0B = 0x0B,
CMD_UNK_0C = 0x0C,
CMD_MORE = 0x0D,
@ -168,7 +171,7 @@ private:
CMD_END = 0x0F,
CMD_MIX_AUXB_LR = 0x10,
CMD_SET_OPPOSITE_LR = 0x11,
CMD_UNK_12 = 0x12,
CMD_COMPRESSOR = 0x12,
CMD_SEND_AUX_AND_MIX = 0x13,
};
};

View File

@ -113,11 +113,15 @@ void AXWiiUCode::HandleCommandList()
break;
}
// TODO(delroth): figure this one out, it's used by almost every
// game I've tested so far.
case CMD_UNK_0B_OLD:
curr_idx += 4;
case CMD_COMPRESSOR_OLD:
{
u16 threshold = m_cmdlist[curr_idx++];
u16 frames = m_cmdlist[curr_idx++];
addr_hi = m_cmdlist[curr_idx++];
addr_lo = m_cmdlist[curr_idx++];
RunCompressor(threshold, frames, HILO_TO_32(addr), 3);
break;
}
case CMD_OUTPUT_OLD:
case CMD_OUTPUT_DPL2_OLD:
@ -206,11 +210,15 @@ void AXWiiUCode::HandleCommandList()
break;
}
// TODO(delroth): figure this one out, it's used by almost every
// game I've tested so far.
case CMD_UNK_0A:
curr_idx += 4;
case CMD_COMPRESSOR:
{
u16 threshold = m_cmdlist[curr_idx++];
u16 frames = m_cmdlist[curr_idx++];
addr_hi = m_cmdlist[curr_idx++];
addr_lo = m_cmdlist[curr_idx++];
RunCompressor(threshold, frames, HILO_TO_32(addr), 3);
break;
}
case CMD_OUTPUT:
case CMD_OUTPUT_DPL2:

View File

@ -82,7 +82,7 @@ private:
CMD_MIX_AUXC = 0x07,
CMD_UPL_AUXA_MIX_LRSC = 0x08,
CMD_UPL_AUXB_MIX_LRSC = 0x09,
CMD_UNK_0A = 0x0A,
CMD_COMPRESSOR = 0x0A,
CMD_OUTPUT = 0x0B,
CMD_OUTPUT_DPL2 = 0x0C,
CMD_WM_OUTPUT = 0x0D,
@ -105,7 +105,7 @@ private:
CMD_MIX_AUXC_OLD = 0x08,
CMD_UPL_AUXA_MIX_LRSC_OLD = 0x09,
CMD_UPL_AUXB_MIX_LRSC_OLD = 0x0a,
CMD_UNK_0B_OLD = 0x0B,
CMD_COMPRESSOR_OLD = 0x0B,
CMD_OUTPUT_OLD = 0x0C, // no volume!
CMD_OUTPUT_DPL2_OLD = 0x0D,
CMD_WM_OUTPUT_OLD = 0x0E,