From eb54721475d2d5a1809409cbd5e5a438e1050b8d Mon Sep 17 00:00:00 2001 From: Tillmann Karras Date: Wed, 4 Sep 2024 22:21:32 +0100 Subject: [PATCH] DSPHLE/AX: fix low-pass/biquad clipping The low-pass and biquad filters run in set40 mode where accessing ac#.m returns the value of ac#.hm clamped to 16 bits. This fixes the crackling in "Need for Speed: Nitro" (issue 13610). Also make the lower bound match hardware (-0x8000 instead of -0x7FFF). --- Source/Core/Core/HW/DSPHLE/UCodes/AX.cpp | 4 ++-- Source/Core/Core/HW/DSPHLE/UCodes/AXVoice.h | 20 ++++++++++++-------- Source/Core/Core/HW/DSPHLE/UCodes/AXWii.cpp | 17 +++++++++-------- 3 files changed, 23 insertions(+), 18 deletions(-) diff --git a/Source/Core/Core/HW/DSPHLE/UCodes/AX.cpp b/Source/Core/Core/HW/DSPHLE/UCodes/AX.cpp index 3b9161a52d..84705edd7c 100644 --- a/Source/Core/Core/HW/DSPHLE/UCodes/AX.cpp +++ b/Source/Core/Core/HW/DSPHLE/UCodes/AX.cpp @@ -578,8 +578,8 @@ void AXUCode::OutputSamples(u32 lr_addr, u32 surround_addr) // Output samples clamped to 16 bits and interlaced RLRLRLRLRL... for (u32 i = 0; i < 5 * 32; ++i) { - int left = std::clamp(m_samples_main_left[i], -32767, 32767); - int right = std::clamp(m_samples_main_right[i], -32767, 32767); + s16 left = ClampS16(m_samples_main_left[i]); + s16 right = ClampS16(m_samples_main_right[i]); buffer[2 * i + 0] = Common::swap16(right); buffer[2 * i + 1] = Common::swap16(left); diff --git a/Source/Core/Core/HW/DSPHLE/UCodes/AXVoice.h b/Source/Core/Core/HW/DSPHLE/UCodes/AXVoice.h index ad491196e2..677e6a7989 100644 --- a/Source/Core/Core/HW/DSPHLE/UCodes/AXVoice.h +++ b/Source/Core/Core/HW/DSPHLE/UCodes/AXVoice.h @@ -372,6 +372,11 @@ void GetInputSamples(HLEAccelerator* accelerator, PB_TYPE& pb, s16* samples, u16 pb.adpcm.pred_scale = accelerator->GetPredScale(); } +s16 ClampS16(s64 sample) +{ + return std::clamp(sample, -0x8000, 0x7FFF); +} + // Add samples to an output buffer, with optional volume ramping. void MixAdd(int* out, const s16* input, u32 count, VolumeData* vd, s16* dpop, bool ramp) { @@ -389,21 +394,20 @@ void MixAdd(int* out, const s16* input, u32 count, VolumeData* vd, s16* dpop, bo s64 sample = input[i]; sample *= volume; sample >>= 15; - sample = std::clamp((s32)sample, -32767, 32767); // -32768 ? + s16 sample16 = ClampS16((s32)sample); - out[i] += (s16)sample; + out[i] += sample16; volume += volume_delta; - *dpop = (s16)sample; + *dpop = sample16; } } -// Execute a low pass filter on the samples using one history value. Returns -// the new history value. +// Execute a low pass filter on the samples using one history value. static void LowPassFilter(s16* samples, u32 count, PBLowPassFilter& f) { for (u32 i = 0; i < count; ++i) - f.yn1 = samples[i] = (f.a0 * (s32)samples[i] + f.b0 * (s32)f.yn1) >> 15; + f.yn1 = samples[i] = ClampS16((f.a0 * (s32)samples[i] + f.b0 * (s32)f.yn1) >> 15); } #ifdef AX_WII @@ -425,7 +429,7 @@ static void BiquadFilter(s16* samples, u32 count, PBBiquadFilter& f) else tmp += 0x7FFF; tmp >>= 16; - s16 yn0 = s16(tmp); + s16 yn0 = ClampS16(tmp); f.xn2 = f.xn1; f.yn2 = f.yn1; f.xn1 = xn0; @@ -459,7 +463,7 @@ void ProcessVoice(HLEAccelerator* accelerator, PB_TYPE& pb, const AXBuffers& buf const s32 volume = (u16)pb.vol_env.cur_volume; #endif const s32 sample = ((s32)samples[i] * volume) >> 15; - samples[i] = std::clamp(sample, -32767, 32767); // -32768 ? + samples[i] = ClampS16(sample); pb.vol_env.cur_volume += pb.vol_env.cur_volume_delta; } diff --git a/Source/Core/Core/HW/DSPHLE/UCodes/AXWii.cpp b/Source/Core/Core/HW/DSPHLE/UCodes/AXWii.cpp index 8ddd119517..e41f13a5ef 100644 --- a/Source/Core/Core/HW/DSPHLE/UCodes/AXWii.cpp +++ b/Source/Core/Core/HW/DSPHLE/UCodes/AXWii.cpp @@ -602,15 +602,16 @@ void AXWiiUCode::OutputSamples(u32 lr_addr, u32 surround_addr, u16 volume, bool // Clamp internal buffers to 16 bits. for (size_t i = 0; i < volume_ramp.size(); ++i) { - int left = m_samples_main_left[i]; - int right = m_samples_main_right[i]; + // Cast to s64 to avoid overflow. + s64 left = m_samples_main_left[i]; + s64 right = m_samples_main_right[i]; - // Apply global volume. Cast to s64 to avoid overflow. - left = ((s64)left * volume_ramp[i]) >> 15; - right = ((s64)right * volume_ramp[i]) >> 15; + // Apply global volume. + left = (left * volume_ramp[i]) >> 15; + right = (right * volume_ramp[i]) >> 15; - m_samples_main_left[i] = std::clamp(left, -32767, 32767); - m_samples_main_right[i] = std::clamp(right, -32767, 32767); + m_samples_main_left[i] = ClampS16(left); + m_samples_main_right[i] = ClampS16(right); } std::array buffer; @@ -635,7 +636,7 @@ void AXWiiUCode::OutputWMSamples(u32* addresses) u16* out = (u16*)HLEMemory_Get_Pointer(memory, addresses[i]); for (u32 j = 0; j < 3 * 6; ++j) { - int sample = std::clamp(in[j], -32767, 32767); + s16 sample = ClampS16(in[j]); out[j] = Common::swap16((u16)sample); } }