mirror of
https://github.com/dolphin-emu/dolphin.git
synced 2024-11-14 21:37:52 -07:00
AX: add support for biquad filtering
This fixes e.g. the overly loud wind in "I SPY: Spooky Mansion".
This commit is contained in:
parent
f85c4413bd
commit
9d2841be10
@ -438,7 +438,7 @@ void AXUCode::ProcessPBList(u32 pb_addr)
|
|||||||
|
|
||||||
ProcessVoice(static_cast<HLEAccelerator*>(m_accelerator.get()), pb, buffers, spms,
|
ProcessVoice(static_cast<HLEAccelerator*>(m_accelerator.get()), pb, buffers, spms,
|
||||||
ConvertMixerControl(pb.mixer_control),
|
ConvertMixerControl(pb.mixer_control),
|
||||||
m_coeffs_checksum ? m_coeffs.data() : nullptr);
|
m_coeffs_checksum ? m_coeffs.data() : nullptr, false);
|
||||||
|
|
||||||
// Forward the buffers
|
// Forward the buffers
|
||||||
for (auto& ptr : buffers.ptrs)
|
for (auto& ptr : buffers.ptrs)
|
||||||
|
@ -177,7 +177,7 @@ struct PBADPCMLoopInfo
|
|||||||
|
|
||||||
struct PBLowPassFilter
|
struct PBLowPassFilter
|
||||||
{
|
{
|
||||||
u16 enabled;
|
u16 on;
|
||||||
s16 yn1;
|
s16 yn1;
|
||||||
u16 a0;
|
u16 a0;
|
||||||
u16 b0;
|
u16 b0;
|
||||||
@ -215,16 +215,16 @@ struct AXPB
|
|||||||
|
|
||||||
struct PBBiquadFilter
|
struct PBBiquadFilter
|
||||||
{
|
{
|
||||||
u16 on; // on = 2, off = 0
|
u16 on;
|
||||||
u16 xn1; // History data
|
s16 xn1; // History data
|
||||||
u16 xn2;
|
s16 xn2;
|
||||||
u16 yn1;
|
s16 yn1;
|
||||||
u16 yn2;
|
s16 yn2;
|
||||||
u16 b0; // Filter coefficients
|
s16 b0; // Filter coefficients
|
||||||
u16 b1;
|
s16 b1;
|
||||||
u16 b2;
|
s16 b2;
|
||||||
u16 a1;
|
s16 a1;
|
||||||
u16 a2;
|
s16 a2;
|
||||||
};
|
};
|
||||||
|
|
||||||
union PBInfImpulseResponseWM
|
union PBInfImpulseResponseWM
|
||||||
|
@ -400,17 +400,45 @@ void MixAdd(int* out, const s16* input, u32 count, VolumeData* vd, s16* dpop, bo
|
|||||||
|
|
||||||
// Execute a low pass filter on the samples using one history value. Returns
|
// Execute a low pass filter on the samples using one history value. Returns
|
||||||
// the new history value.
|
// the new history value.
|
||||||
s16 LowPassFilter(s16* samples, u32 count, s16 yn1, u16 a0, u16 b0)
|
static void LowPassFilter(s16* samples, u32 count, PBLowPassFilter& f)
|
||||||
{
|
{
|
||||||
for (u32 i = 0; i < count; ++i)
|
for (u32 i = 0; i < count; ++i)
|
||||||
yn1 = samples[i] = (a0 * (s32)samples[i] + b0 * (s32)yn1) >> 15;
|
f.yn1 = samples[i] = (f.a0 * (s32)samples[i] + f.b0 * (s32)f.yn1) >> 15;
|
||||||
return yn1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef AX_WII
|
||||||
|
static void BiquadFilter(s16* samples, u32 count, PBBiquadFilter& f)
|
||||||
|
{
|
||||||
|
for (u32 i = 0; i < count; ++i)
|
||||||
|
{
|
||||||
|
s16 xn0 = samples[i];
|
||||||
|
s64 tmp = 0;
|
||||||
|
tmp += f.b0 * s32(xn0);
|
||||||
|
tmp += f.b1 * s32(f.xn1);
|
||||||
|
tmp += f.b2 * s32(f.xn2);
|
||||||
|
tmp += f.a1 * s32(f.yn1);
|
||||||
|
tmp += f.a2 * s32(f.yn2);
|
||||||
|
tmp <<= 2;
|
||||||
|
// CLRL
|
||||||
|
if (tmp & 0x10000)
|
||||||
|
tmp += 0x8000;
|
||||||
|
else
|
||||||
|
tmp += 0x7FFF;
|
||||||
|
tmp >>= 16;
|
||||||
|
s16 yn0 = s16(tmp);
|
||||||
|
f.xn2 = f.xn1;
|
||||||
|
f.yn2 = f.yn1;
|
||||||
|
f.xn1 = xn0;
|
||||||
|
f.yn1 = yn0;
|
||||||
|
samples[i] = yn0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
// Process 1ms of audio (for AX GC) or 3ms of audio (for AX Wii) from a PB and
|
// Process 1ms of audio (for AX GC) or 3ms of audio (for AX Wii) from a PB and
|
||||||
// mix it to the output buffers.
|
// mix it to the output buffers.
|
||||||
void ProcessVoice(HLEAccelerator* accelerator, PB_TYPE& pb, const AXBuffers& buffers, u16 count,
|
void ProcessVoice(HLEAccelerator* accelerator, PB_TYPE& pb, const AXBuffers& buffers, u16 count,
|
||||||
AXMixControl mctrl, const s16* coeffs)
|
AXMixControl mctrl, const s16* coeffs, bool new_filter)
|
||||||
{
|
{
|
||||||
// If the voice is not running, nothing to do.
|
// If the voice is not running, nothing to do.
|
||||||
if (pb.running != 1)
|
if (pb.running != 1)
|
||||||
@ -435,12 +463,19 @@ void ProcessVoice(HLEAccelerator* accelerator, PB_TYPE& pb, const AXBuffers& buf
|
|||||||
pb.vol_env.cur_volume += pb.vol_env.cur_volume_delta;
|
pb.vol_env.cur_volume += pb.vol_env.cur_volume_delta;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Optionally, execute a low pass filter
|
// Optionally, execute a low-pass and/or biquad filter.
|
||||||
if (pb.lpf.enabled)
|
if (pb.lpf.on != 0)
|
||||||
{
|
{
|
||||||
pb.lpf.yn1 = LowPassFilter(samples, count, pb.lpf.yn1, pb.lpf.a0, pb.lpf.b0);
|
LowPassFilter(samples, count, pb.lpf);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef AX_WII
|
||||||
|
if (new_filter && pb.biquad.on != 0)
|
||||||
|
{
|
||||||
|
BiquadFilter(samples, count, pb.biquad);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
// Mix LRS, AUXA and AUXB depending on mixer_control
|
// Mix LRS, AUXA and AUXB depending on mixer_control
|
||||||
// TODO: Handle DPL2 on AUXB.
|
// TODO: Handle DPL2 on AUXB.
|
||||||
|
|
||||||
|
@ -28,7 +28,8 @@ AXWiiUCode::AXWiiUCode(DSPHLE* dsphle, u32 crc)
|
|||||||
for (u16& volume : m_last_aux_volumes)
|
for (u16& volume : m_last_aux_volumes)
|
||||||
volume = 0x8000;
|
volume = 0x8000;
|
||||||
|
|
||||||
m_old_axwii = (crc == 0xfa450138) || (crc == 0x7699af32);
|
m_old_axwii = crc == 0xfa450138 || crc == 0x7699af32;
|
||||||
|
m_new_filter = crc == 0x347112ba || crc == 0x4cc52064;
|
||||||
|
|
||||||
m_accelerator = std::make_unique<HLEAccelerator>(dsphle->GetSystem().GetDSP());
|
m_accelerator = std::make_unique<HLEAccelerator>(dsphle->GetSystem().GetDSP());
|
||||||
}
|
}
|
||||||
@ -450,7 +451,7 @@ void AXWiiUCode::ProcessPBList(u32 pb_addr)
|
|||||||
ApplyUpdatesForMs(curr_ms, pb, num_updates, updates);
|
ApplyUpdatesForMs(curr_ms, pb, num_updates, updates);
|
||||||
ProcessVoice(static_cast<HLEAccelerator*>(m_accelerator.get()), pb, buffers, spms,
|
ProcessVoice(static_cast<HLEAccelerator*>(m_accelerator.get()), pb, buffers, spms,
|
||||||
ConvertMixerControl(HILO_TO_32(pb.mixer_control)),
|
ConvertMixerControl(HILO_TO_32(pb.mixer_control)),
|
||||||
m_coeffs_checksum ? m_coeffs.data() : nullptr);
|
m_coeffs_checksum ? m_coeffs.data() : nullptr, m_new_filter);
|
||||||
|
|
||||||
// Forward the buffers
|
// Forward the buffers
|
||||||
for (auto& ptr : buffers.ptrs)
|
for (auto& ptr : buffers.ptrs)
|
||||||
@ -462,7 +463,7 @@ void AXWiiUCode::ProcessPBList(u32 pb_addr)
|
|||||||
{
|
{
|
||||||
ProcessVoice(static_cast<HLEAccelerator*>(m_accelerator.get()), pb, buffers, 96,
|
ProcessVoice(static_cast<HLEAccelerator*>(m_accelerator.get()), pb, buffers, 96,
|
||||||
ConvertMixerControl(HILO_TO_32(pb.mixer_control)),
|
ConvertMixerControl(HILO_TO_32(pb.mixer_control)),
|
||||||
m_coeffs_checksum ? m_coeffs.data() : nullptr);
|
m_coeffs_checksum ? m_coeffs.data() : nullptr, m_new_filter);
|
||||||
}
|
}
|
||||||
|
|
||||||
WritePB(memory, pb_addr, pb, m_crc);
|
WritePB(memory, pb_addr, pb, m_crc);
|
||||||
|
@ -38,6 +38,9 @@ protected:
|
|||||||
// Are we implementing an old version of AXWii which still has updates?
|
// Are we implementing an old version of AXWii which still has updates?
|
||||||
bool m_old_axwii = false;
|
bool m_old_axwii = false;
|
||||||
|
|
||||||
|
// Late AXWii versions support Wiimote filtering and a biquad filter.
|
||||||
|
bool m_new_filter = false;
|
||||||
|
|
||||||
// Last volume values for MAIN and AUX. Used to generate volume ramps to
|
// Last volume values for MAIN and AUX. Used to generate volume ramps to
|
||||||
// interpolate nicely between old and new volume values.
|
// interpolate nicely between old and new volume values.
|
||||||
u16 m_last_main_volume = 0;
|
u16 m_last_main_volume = 0;
|
||||||
|
Loading…
Reference in New Issue
Block a user