diff --git a/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_AX.cpp b/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_AX.cpp index cb34c2532d..3675970917 100644 --- a/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_AX.cpp +++ b/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_AX.cpp @@ -17,6 +17,7 @@ #include "UCode_AX.h" #include "../../DSP.h" +#include "FileUtil.h" #define AX_GC #include "UCode_AX_Voice.h" @@ -29,6 +30,8 @@ CUCode_AX::CUCode_AX(DSPHLE* dsp_hle, u32 crc) WARN_LOG(DSPHLE, "Instantiating CUCode_AX: crc=%08x", crc); m_rMailHandler.PushMail(DSP_INIT); DSP::GenerateDSPInterruptFromDSPEmu(DSP::INT_DSP); + + LoadResamplingCoefficients(); } CUCode_AX::~CUCode_AX() @@ -40,6 +43,27 @@ CUCode_AX::~CUCode_AX() m_rMailHandler.Clear(); } +void CUCode_AX::LoadResamplingCoefficients() +{ + m_coeffs_available = false; + + std::string filename = File::GetUserPath(D_GCUSER_IDX) + "dsp_coef.bin"; + if (!File::Exists(filename)) + return; + + if (File::GetSize(filename) != 0x1000) + return; + + FILE* fp = fopen(filename.c_str(), "rb"); + fread(m_coeffs, 1, 0x1000, fp); + fclose(fp); + + for (u32 i = 0; i < 0x800; ++i) + m_coeffs[i] = Common::swap16(m_coeffs[i]); + + m_coeffs_available = true; +} + void CUCode_AX::SpawnAXThread(CUCode_AX* self) { self->AXThread(); @@ -405,7 +429,8 @@ void CUCode_AX::ProcessPBList(u32 pb_addr) { ApplyUpdatesForMs(pb, curr_ms); - ProcessVoice(pb, buffers, ConvertMixerControl(pb.mixer_control)); + ProcessVoice(pb, buffers, ConvertMixerControl(pb.mixer_control), + m_coeffs_available ? m_coeffs : NULL); // Forward the buffers for (u32 i = 0; i < sizeof (buffers.ptrs) / sizeof (buffers.ptrs[0]); ++i) diff --git a/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_AX.h b/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_AX.h index 7158c197e8..68248fef60 100644 --- a/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_AX.h +++ b/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_AX.h @@ -114,6 +114,14 @@ protected: std::condition_variable m_cmdlist_cv; std::mutex m_cmdlist_mutex; + // Table of coefficients for polyphase sample rate conversion. + // The coefficients aren't always available (they are part of the DSP DROM) + // so we also need to know if they are valid or not. + bool m_coeffs_available; + s16 m_coeffs[0x800]; + + void LoadResamplingCoefficients(); + // Copy a command list from memory to our temp buffer void CopyCmdList(u32 addr, u16 size); diff --git a/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_AXWii.cpp b/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_AXWii.cpp index 2377839e86..1fde3f74b4 100644 --- a/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_AXWii.cpp +++ b/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_AXWii.cpp @@ -259,7 +259,8 @@ void CUCode_AXWii::ProcessPBList(u32 pb_addr) if (!ReadPB(pb_addr, pb)) break; - ProcessVoice(pb, buffers, ConvertMixerControl(HILO_TO_32(pb.mixer_control))); + ProcessVoice(pb, buffers, ConvertMixerControl(HILO_TO_32(pb.mixer_control)), + m_coeffs_available ? m_coeffs : NULL); WritePB(pb_addr, pb); pb_addr = HILO_TO_32(pb.next_pb); diff --git a/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_AX_Voice.h b/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_AX_Voice.h index f731fc5360..649ad3c37f 100644 --- a/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_AX_Voice.h +++ b/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_AX_Voice.h @@ -245,13 +245,54 @@ u16 AcceleratorGetSample() // Read SAMPLES_PER_FRAME input samples from ARAM, decoding and converting rate // if required. -void GetInputSamples(PB_TYPE& pb, s16* samples) +void GetInputSamples(PB_TYPE& pb, s16* samples, const s16* coeffs) { u32 cur_addr = HILO_TO_32(pb.audio_addr.cur_addr); AcceleratorSetup(&pb, &cur_addr); - // TODO: support polyphase interpolation if coefficients are available. - if (pb.src_type == SRCTYPE_POLYPHASE || pb.src_type == SRCTYPE_LINEAR) + // If DSP DROM coefficients are available, support polyphase resampling. + if (coeffs && pb.src_type == SRCTYPE_POLYPHASE) + { + s16 temp[4]; + u32 idx = 0; + + u32 ratio = HILO_TO_32(pb.src.ratio); + u32 curr_pos = pb.src.cur_addr_frac; + + temp[idx++ & 3] = pb.src.last_samples[0]; + temp[idx++ & 3] = pb.src.last_samples[1]; + temp[idx++ & 3] = pb.src.last_samples[2]; + temp[idx++ & 3] = pb.src.last_samples[3]; + + for (u32 i = 0; i < SAMPLES_PER_FRAME; ++i) + { + curr_pos += ratio; + while (curr_pos >= 0x10000) + { + temp[idx++ & 3] = AcceleratorGetSample(); + curr_pos -= 0x10000; + } + + u16 curr_pos_frac = ((curr_pos & 0xFFFF) >> 9) << 2; + const s16* c = &coeffs[pb.coef_select * 0x200 + curr_pos_frac]; + + s64 t0 = temp[idx++ & 3]; + s64 t1 = temp[idx++ & 3]; + s64 t2 = temp[idx++ & 3]; + s64 t3 = temp[idx++ & 3]; + + s64 samp = (t0 * c[0] + t1 * c[1] + t2 * c[2] + t3 * c[3]) >> 15; + + samples[i] = (s16)samp; + } + + pb.src.last_samples[3] = temp[--idx & 3]; + pb.src.last_samples[2] = temp[--idx & 3]; + pb.src.last_samples[1] = temp[--idx & 3]; + pb.src.last_samples[0] = temp[--idx & 3]; + pb.src.cur_addr_frac = curr_pos & 0xFFFF; + } + else if (pb.src_type == SRCTYPE_LINEAR || (!coeffs && pb.src_type == SRCTYPE_POLYPHASE)) { // Convert the input to a higher or lower sample rate using a linear // interpolation algorithm. The input to output ratio is set in @@ -351,7 +392,7 @@ s16 LowPassFilter(s16* samples, u32 count, s16 yn1, u16 a0, u16 b0) // Process 1ms of audio (for AX GC) or 3ms of audio (for AX Wii) from a PB and // mix it to the output buffers. -void ProcessVoice(PB_TYPE& pb, const AXBuffers& buffers, AXMixControl mctrl) +void ProcessVoice(PB_TYPE& pb, const AXBuffers& buffers, AXMixControl mctrl, const s16* coeffs) { // If the voice is not running, nothing to do. if (!pb.running) @@ -359,7 +400,7 @@ void ProcessVoice(PB_TYPE& pb, const AXBuffers& buffers, AXMixControl mctrl) // Read input samples, performing sample rate conversion if needed. s16 samples[SAMPLES_PER_FRAME]; - GetInputSamples(pb, samples); + GetInputSamples(pb, samples, coeffs); // Apply a global volume ramp using the volume envelope parameters. for (u32 i = 0; i < SAMPLES_PER_FRAME; ++i)