diff --git a/Source/Core/Core/HW/DSPHLE/UCodes/AX.cpp b/Source/Core/Core/HW/DSPHLE/UCodes/AX.cpp index 040328964e..271fe64550 100644 --- a/Source/Core/Core/HW/DSPHLE/UCodes/AX.cpp +++ b/Source/Core/Core/HW/DSPHLE/UCodes/AX.cpp @@ -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]; diff --git a/Source/Core/Core/HW/DSPHLE/UCodes/AX.h b/Source/Core/Core/HW/DSPHLE/UCodes/AX.h index ee1b8f4998..dea125d78c 100644 --- a/Source/Core/Core/HW/DSPHLE/UCodes/AX.h +++ b/Source/Core/Core/HW/DSPHLE/UCodes/AX.h @@ -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, }; }; diff --git a/Source/Core/Core/HW/DSPHLE/UCodes/AXWii.cpp b/Source/Core/Core/HW/DSPHLE/UCodes/AXWii.cpp index 2a3df476f9..f5839a055a 100644 --- a/Source/Core/Core/HW/DSPHLE/UCodes/AXWii.cpp +++ b/Source/Core/Core/HW/DSPHLE/UCodes/AXWii.cpp @@ -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: diff --git a/Source/Core/Core/HW/DSPHLE/UCodes/AXWii.h b/Source/Core/Core/HW/DSPHLE/UCodes/AXWii.h index fda5964b04..95e06034e5 100644 --- a/Source/Core/Core/HW/DSPHLE/UCodes/AXWii.h +++ b/Source/Core/Core/HW/DSPHLE/UCodes/AXWii.h @@ -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,