Add polyphase resampling support in AX

This commit is contained in:
Pierre Bourdon 2013-01-25 08:25:28 +01:00
parent 1612225e79
commit 9776f135e2
4 changed files with 82 additions and 7 deletions

View File

@ -17,6 +17,7 @@
#include "UCode_AX.h" #include "UCode_AX.h"
#include "../../DSP.h" #include "../../DSP.h"
#include "FileUtil.h"
#define AX_GC #define AX_GC
#include "UCode_AX_Voice.h" #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); WARN_LOG(DSPHLE, "Instantiating CUCode_AX: crc=%08x", crc);
m_rMailHandler.PushMail(DSP_INIT); m_rMailHandler.PushMail(DSP_INIT);
DSP::GenerateDSPInterruptFromDSPEmu(DSP::INT_DSP); DSP::GenerateDSPInterruptFromDSPEmu(DSP::INT_DSP);
LoadResamplingCoefficients();
} }
CUCode_AX::~CUCode_AX() CUCode_AX::~CUCode_AX()
@ -40,6 +43,27 @@ CUCode_AX::~CUCode_AX()
m_rMailHandler.Clear(); 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) void CUCode_AX::SpawnAXThread(CUCode_AX* self)
{ {
self->AXThread(); self->AXThread();
@ -405,7 +429,8 @@ void CUCode_AX::ProcessPBList(u32 pb_addr)
{ {
ApplyUpdatesForMs(pb, curr_ms); 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 // Forward the buffers
for (u32 i = 0; i < sizeof (buffers.ptrs) / sizeof (buffers.ptrs[0]); ++i) for (u32 i = 0; i < sizeof (buffers.ptrs) / sizeof (buffers.ptrs[0]); ++i)

View File

@ -114,6 +114,14 @@ protected:
std::condition_variable m_cmdlist_cv; std::condition_variable m_cmdlist_cv;
std::mutex m_cmdlist_mutex; 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 // Copy a command list from memory to our temp buffer
void CopyCmdList(u32 addr, u16 size); void CopyCmdList(u32 addr, u16 size);

View File

@ -259,7 +259,8 @@ void CUCode_AXWii::ProcessPBList(u32 pb_addr)
if (!ReadPB(pb_addr, pb)) if (!ReadPB(pb_addr, pb))
break; 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); WritePB(pb_addr, pb);
pb_addr = HILO_TO_32(pb.next_pb); pb_addr = HILO_TO_32(pb.next_pb);

View File

@ -245,13 +245,54 @@ u16 AcceleratorGetSample()
// Read SAMPLES_PER_FRAME input samples from ARAM, decoding and converting rate // Read SAMPLES_PER_FRAME input samples from ARAM, decoding and converting rate
// if required. // 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); u32 cur_addr = HILO_TO_32(pb.audio_addr.cur_addr);
AcceleratorSetup(&pb, &cur_addr); AcceleratorSetup(&pb, &cur_addr);
// TODO: support polyphase interpolation if coefficients are available. // If DSP DROM coefficients are available, support polyphase resampling.
if (pb.src_type == SRCTYPE_POLYPHASE || pb.src_type == SRCTYPE_LINEAR) 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 // Convert the input to a higher or lower sample rate using a linear
// interpolation algorithm. The input to output ratio is set in // 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 // 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(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 the voice is not running, nothing to do.
if (!pb.running) 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. // Read input samples, performing sample rate conversion if needed.
s16 samples[SAMPLES_PER_FRAME]; s16 samples[SAMPLES_PER_FRAME];
GetInputSamples(pb, samples); GetInputSamples(pb, samples, coeffs);
// Apply a global volume ramp using the volume envelope parameters. // Apply a global volume ramp using the volume envelope parameters.
for (u32 i = 0; i < SAMPLES_PER_FRAME; ++i) for (u32 i = 0; i < SAMPLES_PER_FRAME; ++i)