Add polyphase resampling support in AX
This commit is contained in:
parent
1612225e79
commit
9776f135e2
|
@ -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)
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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)
|
||||
|
|
Loading…
Reference in New Issue