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