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 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); INFO_LOG_FMT(DSPHLE, "Instantiating AXUCode: crc={:08x}", crc);
} }
@ -181,13 +182,11 @@ void AXUCode::HandleCommandList()
MixAUXSamples(1, 0, HILO_TO_32(addr)); MixAUXSamples(1, 0, HILO_TO_32(addr));
break; break;
case CMD_COMPRESSOR_TABLE_ADDR: case CMD_UNK_0A:
curr_idx += 2;
break;
case CMD_UNK_0B: case CMD_UNK_0B:
break; // TODO: check other versions
case CMD_UNK_0C: case CMD_UNK_0C:
break; // TODO: check other versions // nop in all 6 known ucodes we handle here
break;
case CMD_MORE: case CMD_MORE:
addr_hi = m_cmdlist[curr_idx++]; addr_hi = m_cmdlist[curr_idx++];
@ -224,16 +223,16 @@ void AXUCode::HandleCommandList()
SetOppositeLR(HILO_TO_32(addr)); SetOppositeLR(HILO_TO_32(addr));
break; break;
case CMD_UNK_12: case CMD_COMPRESSOR:
{ {
u16 samp_val = m_cmdlist[curr_idx++]; // 0x4e8a8b21 doesn't have this command, but it doesn't range-check
u16 idx = m_cmdlist[curr_idx++]; // 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_hi = m_cmdlist[curr_idx++];
addr_lo = m_cmdlist[curr_idx++]; addr_lo = m_cmdlist[curr_idx++];
// TODO RunCompressor(threshold, frames, HILO_TO_32(addr), 5);
// suppress warnings:
(void)samp_val;
(void)idx;
break; 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) void AXUCode::OutputSamples(u32 lr_addr, u32 surround_addr)
{ {
int surround_buffer[5 * 32]; int surround_buffer[5 * 32];

View File

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

View File

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

View File

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