DSPHLE: Do the resampling correctly for PCM16 and AFC. Added linear interpolation (that works, this time :P)

git-svn-id: https://dolphin-emu.googlecode.com/svn/trunk@3764 8ced0084-cf51-0410-be5f-012b33b47a6e
This commit is contained in:
hrydgard 2009-07-12 13:45:01 +00:00
parent 304b34a451
commit 0bdf6656c5
4 changed files with 119 additions and 96 deletions

View File

@ -82,7 +82,7 @@ CUCode_Zelda::CUCode_Zelda(CMailHandler& _rMailHandler, u32 _CRC)
} }
m_VoiceBuffer = new s32[256 * 1024]; m_VoiceBuffer = new s32[256 * 1024];
m_ResampleBuffer = new s32[256 * 1024]; m_ResampleBuffer = new s16[256 * 1024];
m_LeftBuffer = new s32[256 * 1024]; m_LeftBuffer = new s32[256 * 1024];
m_RightBuffer = new s32[256 * 1024]; m_RightBuffer = new s32[256 * 1024];

View File

@ -166,7 +166,7 @@ private:
// These are the only dynamically allocated things allowed in the ucode. // These are the only dynamically allocated things allowed in the ucode.
s32* m_VoiceBuffer; s32* m_VoiceBuffer;
s32* m_ResampleBuffer; s16* m_ResampleBuffer;
s32* m_LeftBuffer; s32* m_LeftBuffer;
s32* m_RightBuffer; s32* m_RightBuffer;
@ -237,11 +237,14 @@ private:
void RenderSynth_Constant(ZeldaVoicePB &PB, s32* _Buffer, int _Size); void RenderSynth_Constant(ZeldaVoicePB &PB, s32* _Buffer, int _Size);
void RenderSynth_RectWave(ZeldaVoicePB &PB, s32* _Buffer, int _Size); void RenderSynth_RectWave(ZeldaVoicePB &PB, s32* _Buffer, int _Size);
void RenderSynth_SawWave(ZeldaVoicePB &PB, s32* _Buffer, int _Size); void RenderSynth_SawWave(ZeldaVoicePB &PB, s32* _Buffer, int _Size);
void RenderVoice_PCM16(ZeldaVoicePB& PB, s32* _Buffer, int _Size); void RenderVoice_PCM16(ZeldaVoicePB& PB, s16* _Buffer, int _Size);
void RenderVoice_AFC(ZeldaVoicePB& PB, s32* _Buffer, int _Size); void RenderVoice_AFC(ZeldaVoicePB& PB, s16* _Buffer, int _Size);
void RenderVoice_Raw(ZeldaVoicePB& PB, s32* _Buffer, int _Size); void RenderVoice_Raw(ZeldaVoicePB& PB, s32* _Buffer, int _Size);
void Resample(ZeldaVoicePB &PB, int size, s32 *in, s32 *out); void Resample(ZeldaVoicePB &PB, int size, s16 *in, s32 *out, bool do_resample = false);
int ConvertRatio(int pb_ratio);
int SizeForResampling(ZeldaVoicePB &PB, int size, int ratio);
// Renders a voice and mixes it into LeftBuffer, RightBuffer // Renders a voice and mixes it into LeftBuffer, RightBuffer
void RenderAddVoice(ZeldaVoicePB& PB, s32* _LeftBuffer, s32* _RightBuffer, int _Size); void RenderAddVoice(ZeldaVoicePB& PB, s32* _LeftBuffer, s32* _RightBuffer, int _Size);

View File

@ -56,16 +56,58 @@ void CUCode_Zelda::WritebackVoicePB(u32 _Addr, ZeldaVoicePB& PB)
memory[i] = Common::swap16(((u16*)&PB)[i]); memory[i] = Common::swap16(((u16*)&PB)[i]);
} }
int CUCode_Zelda::ConvertRatio(int pb_ratio)
void CUCode_Zelda::RenderVoice_PCM16(ZeldaVoicePB &PB, s32* _Buffer, int _Size)
{ {
float ratioFactor = 32000.0f / (float)soundStream->GetMixer()->GetSampleRate(); float ratioFactor = 32000.0f / (float)soundStream->GetMixer()->GetSampleRate();
u32 _ratio = (pb_ratio << 16);
return (u64)((_ratio * ratioFactor) * 16) >> 16;
}
u32 _ratio = (PB.RatioInt << 16); int CUCode_Zelda::SizeForResampling(ZeldaVoicePB &PB, int size, int ratio) {
s64 ratio = (_ratio * ratioFactor) * 16; // This is the little calculation at the start of every sample decoder
// in the ucode.
return (PB.CurSampleFrac + size * ConvertRatio(PB.RatioInt)) >> 16;
}
u32 inpos[2] = {PB.CurSampleFrac << 16, 0}; // Simple resampler, linear interpolation.
int outpos = 0; // Any future state should be stored in PB.raw[0x3c to 0x3f].
// In must point 4 samples into a buffer.
void CUCode_Zelda::Resample(ZeldaVoicePB &PB, int size, s16 *in, s32 *out, bool do_resample)
{
if (!do_resample)
{
memcpy(out, in, size * sizeof(int));
return;
}
for (int i = 0; i < 4; i++)
{
in[i - 4] = (s16)PB.ResamplerOldData[i];
}
int ratio = ConvertRatio(PB.RatioInt);
int in_size = SizeForResampling(PB, size, ratio);
int position = PB.CurSampleFrac;
for (int i = 0; i < size; i++)
{
int int_pos = (position >> 16);
int frac = ((position & 0xFFFF) >> 1);
out[i] = (in[int_pos - 3] * (frac ^ 0x7FFF) + in[int_pos - 2] * frac) >> 15;
position += ratio;
}
for (int i = 0; i < 4; i++)
{
PB.ResamplerOldData[i] = (u16)(s16)in[in_size - 4 + i];
}
PB.CurSampleFrac = position & 0xFFFF;
}
void CUCode_Zelda::RenderVoice_PCM16(ZeldaVoicePB &PB, s16 *_Buffer, int _Size)
{
int _RealSize = SizeForResampling(PB, _Size, PB.RatioInt);
if (PB.KeyOff != 0) if (PB.KeyOff != 0)
return; return;
@ -76,13 +118,17 @@ void CUCode_Zelda::RenderVoice_PCM16(ZeldaVoicePB &PB, s32* _Buffer, int _Size)
PB.RemLength = PB.Length - PB.RestartPos; PB.RemLength = PB.Length - PB.RestartPos;
PB.CurAddr = PB.StartAddr + (PB.RestartPos << 1); PB.CurAddr = PB.StartAddr + (PB.RestartPos << 1);
PB.ReachedEnd = 0; PB.ReachedEnd = 0;
for (int i = 0; i < 4; i++)
PB.ResamplerOldData[i] = 0;
} }
int inpos = 0;
int outpos = 0; // Must be before _lRestart
_lRestart: _lRestart:
if (PB.ReachedEnd) if (PB.ReachedEnd)
{ {
PB.ReachedEnd = 0; PB.ReachedEnd = 0;
if (PB.RepeatMode == 0) if (PB.RepeatMode == 0)
{ {
PB.KeyOff = 1; PB.KeyOff = 1;
@ -95,7 +141,7 @@ _lRestart:
PB.RestartPos = PB.LoopStartPos; PB.RestartPos = PB.LoopStartPos;
PB.RemLength = PB.Length - PB.RestartPos; PB.RemLength = PB.Length - PB.RestartPos;
PB.CurAddr = PB.StartAddr + (PB.RestartPos << 1); PB.CurAddr = PB.StartAddr + (PB.RestartPos << 1);
inpos[1] = 0; inpos[0] = 0; inpos = 0;
} }
} }
@ -105,38 +151,31 @@ _lRestart:
else else
source = (s16*)(g_dspInitialize.pGetARAMPointer() + PB.CurAddr); source = (s16*)(g_dspInitialize.pGetARAMPointer() + PB.CurAddr);
for (; outpos < _Size;) for (; outpos < _RealSize;)
{ {
// Simple linear interpolation. _Buffer[outpos++] = (s16)Common::swap16(source[inpos]);
const s16 sample = (s16)Common::swap16(source[inpos[1]]); inpos++; // hm, above or below the if...
_Buffer[outpos++] = sample; if ((inpos + ((PB.CurAddr - PB.StartAddr) >> 1)) >= PB.Length)
(*(u64*)&inpos) += ratio;
if ((inpos[1] + ((PB.CurAddr - PB.StartAddr) >> 1)) >= PB.Length)
{ {
PB.ReachedEnd = 1; PB.ReachedEnd = 1;
goto _lRestart; goto _lRestart;
} }
} }
if (PB.RemLength < inpos[1]) if (PB.RemLength < inpos)
{ {
PB.RemLength = 0; PB.RemLength = 0;
PB.ReachedEnd = 1; PB.ReachedEnd = 1;
} }
else else
PB.RemLength -= inpos[1]; PB.RemLength -= inpos;
PB.CurAddr += inpos[1] << 1; PB.CurAddr += inpos << 1;
PB.CurSampleFrac = inpos[0]>>16;
// There should be a position fraction as well.
} }
void CUCode_Zelda::RenderVoice_AFC(ZeldaVoicePB &PB, s32* _Buffer, int _Size) void CUCode_Zelda::RenderVoice_AFC(ZeldaVoicePB &PB, s16 *_Buffer, int _Size)
{ {
float ratioFactor = 32000.0f / (float)soundStream->GetMixer()->GetSampleRate(); int _RealSize = SizeForResampling(PB, _Size, PB.RatioInt);
u32 _ratio = (PB.RatioInt << 16);
s64 ratio = (_ratio * ratioFactor) * 16;
// initialize "decoder" if the sample is played the first time // initialize "decoder" if the sample is played the first time
if (PB.NeedsReset != 0) if (PB.NeedsReset != 0)
@ -155,12 +194,15 @@ void CUCode_Zelda::RenderVoice_AFC(ZeldaVoicePB &PB, s32* _Buffer, int _Size)
PB.CurAddr = PB.StartAddr; PB.CurAddr = PB.StartAddr;
PB.ReachedEnd = 0; PB.ReachedEnd = 0;
PB.CurSampleFrac = 0; PB.CurSampleFrac = 0;
for (int i = 0; i < 4; i++)
PB.ResamplerOldData[i] = 0;
} }
if (PB.KeyOff != 0) // 0747 early out... i dunno if this can happen because we filter it above if (PB.KeyOff != 0) // 0747 early out... i dunno if this can happen because we filter it above
return; return;
// round upwards how many samples we need to copy, 0759 // Round upwards how many samples we need to copy, 0759
// u32 frac = NumberOfSamples & 0xF; // u32 frac = NumberOfSamples & 0xF;
// NumberOfSamples = (NumberOfSamples + 0xf) >> 4; // i think the lower 4 are the fraction // NumberOfSamples = (NumberOfSamples + 0xf) >> 4; // i think the lower 4 are the fraction
@ -173,6 +215,8 @@ void CUCode_Zelda::RenderVoice_AFC(ZeldaVoicePB &PB, s32* _Buffer, int _Size)
else else
source = g_dspInitialize.pGetARAMPointer(); source = g_dspInitialize.pGetARAMPointer();
int sampleCount = 0; // must be above restart.
restart: restart:
if (PB.ReachedEnd) if (PB.ReachedEnd)
{ {
@ -205,53 +249,38 @@ restart:
AFCdecodebuffer(m_AFCCoefTable, (char*)(source + (PB.CurAddr & ram_mask)), outbuf, (short*)&PB.YN2, (short*)&PB.YN1, PB.Format); AFCdecodebuffer(m_AFCCoefTable, (char*)(source + (PB.CurAddr & ram_mask)), outbuf, (short*)&PB.YN2, (short*)&PB.YN1, PB.Format);
PB.CurAddr += 9; PB.CurAddr += 9;
s64 TrueSamplePosition = (s64)(PB.Length - PB.RemLength) << 16; u32 SamplePosition = PB.Length - PB.RemLength;
TrueSamplePosition += PB.CurSampleFrac; while (sampleCount < _RealSize)
s64 delta = ratio >> 16; // 0x100000000ULL;
int sampleCount = 0;
while (sampleCount < _Size)
{ {
int SamplePosition = TrueSamplePosition >> 16;
_Buffer[sampleCount] = outbuf[SamplePosition & 15]; _Buffer[sampleCount] = outbuf[SamplePosition & 15];
sampleCount++; sampleCount++;
TrueSamplePosition += delta;
int TargetPosition = TrueSamplePosition >> 16; SamplePosition++;
PB.RemLength--;
// Decode forwards... if (PB.RemLength == 0)
while (SamplePosition < TargetPosition)
{ {
SamplePosition++; PB.ReachedEnd = 1;
PB.RemLength--; goto restart;
if (PB.RemLength == 0) }
{
PB.ReachedEnd = 1;
goto restart;
}
// Need new samples! // Need new samples!
if ((SamplePosition & 15) == 0) { if ((SamplePosition & 15) == 0) {
prev_yn1 = PB.YN1; prev_yn1 = PB.YN1;
prev_yn2 = PB.YN2; prev_yn2 = PB.YN2;
prev_addr = PB.CurAddr; prev_addr = PB.CurAddr;
AFCdecodebuffer(m_AFCCoefTable, (char*)(source + (PB.CurAddr & ram_mask)), outbuf, (short*)&PB.YN2, (short*)&PB.YN1, PB.Format); AFCdecodebuffer(m_AFCCoefTable, (char*)(source + (PB.CurAddr & ram_mask)), outbuf, (short*)&PB.YN2, (short*)&PB.YN1, PB.Format);
PB.CurAddr += 9; PB.CurAddr += 9;
}
} }
} }
// Here we should back off to the previous addr/yn1/yn2, since we didn't consume the full last block. // Here we should back off to the previous addr/yn1/yn2, since we didn't consume the full last block.
// We'll have to re-decode it the next time around. // We'll re-decode it the next time around.
// if (SamplePosition & 15) { PB.YN2 = prev_yn2;
PB.YN2 = prev_yn2; PB.YN1 = prev_yn1;
PB.YN1 = prev_yn1; PB.CurAddr = prev_addr;
PB.CurAddr = prev_addr;
// }
PB.NeedsReset = 0; PB.NeedsReset = 0;
PB.CurSampleFrac = TrueSamplePosition & 0xFFFF;
// write back // write back
// NumberOfSamples = (NumberOfSamples << 4) | frac; // missing fraction // NumberOfSamples = (NumberOfSamples << 4) | frac; // missing fraction
@ -268,8 +297,8 @@ restart:
void CUCode_Zelda::RenderVoice_Raw(ZeldaVoicePB &PB, s32* _Buffer, int _Size) void CUCode_Zelda::RenderVoice_Raw(ZeldaVoicePB &PB, s32* _Buffer, int _Size)
{ {
float ratioFactor = 32000.0f / (float)soundStream->GetMixer()->GetSampleRate(); float ratioFactor = 32000.0f / (float)soundStream->GetMixer()->GetSampleRate();
u32 _ratio = (PB.RatioInt << 16);// + PB.RatioFrac; u32 _ratio = (PB.RatioInt << 16);
s64 ratio = (_ratio * ratioFactor) * 16; // (s64)(((_ratio / 80) << 16) * ratioFactor); s64 ratio = (_ratio * ratioFactor) * 16;
s64 samples_to_read; s64 samples_to_read;
@ -364,8 +393,6 @@ restart:
void CUCode_Zelda::RenderAddVoice(ZeldaVoicePB &PB, s32* _LeftBuffer, s32* _RightBuffer, int _Size) void CUCode_Zelda::RenderAddVoice(ZeldaVoicePB &PB, s32* _LeftBuffer, s32* _RightBuffer, int _Size)
{ {
//static u16 lastLeft = 0x1FF, lastRight = 0x1FF;
if (PB.IsBlank) if (PB.IsBlank)
{ {
s32 sample = (s32)(s16)PB.FixedSample; s32 sample = (s32)(s16)PB.FixedSample;
@ -393,19 +420,19 @@ void CUCode_Zelda::RenderAddVoice(ZeldaVoicePB &PB, s32* _LeftBuffer, s32* _Righ
WARN_LOG(DSPHLE, "5 byte AFC - does it work?"); WARN_LOG(DSPHLE, "5 byte AFC - does it work?");
case 0x0009: // AFC with normal bitrate (32:9 compression). case 0x0009: // AFC with normal bitrate (32:9 compression).
RenderVoice_AFC(PB, m_ResampleBuffer, _Size); RenderVoice_AFC(PB, m_ResampleBuffer + 4, _Size);
Resample(PB, _Size, m_ResampleBuffer, m_VoiceBuffer); Resample(PB, _Size, m_ResampleBuffer + 4, m_VoiceBuffer, true);
break; break;
case 0x0008: // Likely PCM8 - normal PCM 8-bit audio. Used in Mario Kart DD. case 0x0008: // Likely PCM8 - normal PCM 8-bit audio. Used in Mario Kart DD.
WARN_LOG(DSPHLE, "Unimplemented MixAddVoice format in zelda %04x", PB.Format); WARN_LOG(DSPHLE, "Unimplemented MixAddVoice format in zelda %04x", PB.Format);
memset(m_ResampleBuffer, 0, _Size * sizeof(s32)); memset(m_ResampleBuffer + 4, 0, _Size * sizeof(s32));
Resample(PB, _Size, m_ResampleBuffer, m_VoiceBuffer); Resample(PB, _Size, m_ResampleBuffer + 4, m_VoiceBuffer);
break; break;
case 0x0010: // PCM16 - normal PCM 16-bit audio. case 0x0010: // PCM16 - normal PCM 16-bit audio.
RenderVoice_PCM16(PB, m_ResampleBuffer, _Size); RenderVoice_PCM16(PB, m_ResampleBuffer + 4, _Size);
Resample(PB, _Size, m_ResampleBuffer, m_VoiceBuffer); Resample(PB, _Size, m_ResampleBuffer + 4, m_VoiceBuffer, true);
break; break;
case 0x0020: case 0x0020:
@ -415,26 +442,26 @@ void CUCode_Zelda::RenderAddVoice(ZeldaVoicePB &PB, s32* _LeftBuffer, s32* _Righ
#if 0 // To hear something weird in ZWW, turn this on. #if 0 // To hear something weird in ZWW, turn this on.
// Caution: Use at your own risk. Sounds awful :) // Caution: Use at your own risk. Sounds awful :)
RenderVoice_Raw(PB, m_ResampleBuffer, _Size); RenderVoice_Raw(PB, m_ResampleBuffer + 4, _Size);
#else #else
// This is what 0x20 and 0x21 do on end of voice // This is what 0x20 and 0x21 do on end of voice
PB.RemLength = 0; PB.RemLength = 0;
PB.KeyOff = 1; PB.KeyOff = 1;
#endif #endif
Resample(PB, _Size, m_ResampleBuffer, m_VoiceBuffer); Resample(PB, _Size, m_ResampleBuffer + 4, m_VoiceBuffer);
break; break;
case 0x0021: case 0x0021:
// Raw sound from RAM. Important for Zelda WW. Really need to implement - missing it causes hangs. // Raw sound from RAM. Important for Zelda WW. Really need to implement - missing it causes hangs.
#if 0 // To hear something weird in ZWW, turn this on. #if 0 // To hear something weird in ZWW, turn this on.
// Caution: Use at your own risk. Sounds awful :) // Caution: Use at your own risk. Sounds awful :)
RenderVoice_Raw(PB, m_ResampleBuffer, _Size); RenderVoice_Raw(PB, m_ResampleBuffer + 4, _Size);
#else #else
// This is what 0x20 and 0x21 do on end of voice // This is what 0x20 and 0x21 do on end of voice
PB.RemLength = 0; PB.RemLength = 0;
PB.KeyOff = 1; PB.KeyOff = 1;
#endif #endif
Resample(PB, _Size, m_ResampleBuffer, m_VoiceBuffer); Resample(PB, _Size, m_ResampleBuffer + 4, m_VoiceBuffer);
break; break;
default: default:
@ -626,13 +653,6 @@ ContinueWithBlock:
} }
} }
void CUCode_Zelda::Resample(ZeldaVoicePB &PB, int size, s32 *in, s32 *out)
{
// TODO
memcpy(out, in, size * sizeof(int));
}
// size is in stereo samples. // size is in stereo samples.
void CUCode_Zelda::MixAdd(short *_Buffer, int _Size) void CUCode_Zelda::MixAdd(short *_Buffer, int _Size)
{ {

View File

@ -1260,16 +1260,16 @@ void 04eb_COMMAND_02() // sync frame
05b4 18fa lrrd $AX0.H, @$AR3 05b4 18fa lrrd $AX0.H, @$AR3
05b5 8600 tstaxh $AX0.H 05b5 8600 tstaxh $AX0.H
05b6 0294 05c6 jnz 0x05c6 05b6 0294 05c6 jnz 0x05c6
05b8 18fa lrrd $AX0.H, @$AR3 05b8 18fa lrrd $AX0.H, @$AR3
05b9 8600 tstaxh $AX0.H 05b9 8600 tstaxh $AX0.H
05ba 0294 05c6 jnz 0x05c6 05ba 0294 05c6 jnz 0x05c6
05bc 18fa lrrd $AX0.H, @$AR3 05bc 18fa lrrd $AX0.H, @$AR3
05bd 8600 tstaxh $AX0.H 05bd 8600 tstaxh $AX0.H
05be 0294 05c6 jnz 0x05c6 05be 0294 05c6 jnz 0x05c6
05c0 8100 clr $ACC0 05c0 8100 clr $ACC0
05c1 18fe lrrd $AC0.M, @$AR3 05c1 18fe lrrd $AC0.M, @$AR3
05c2 0280 7fff cmpi $AC0.M, #0x7fff 05c2 0280 7fff cmpi $AC0.M, #0x7fff
05c4 0295 05c8 jz 0x05c8 05c4 0295 05c8 jz 0x05c8
05c6 02bf 01fc call 0x01fc 05c6 02bf 01fc call 0x01fc
05c8 8100 clr $ACC0 05c8 8100 clr $ACC0
05c9 1c9e mrr $IX0, $AC0.M 05c9 1c9e mrr $IX0, $AC0.M
@ -1288,7 +1288,7 @@ void 04eb_COMMAND_02() // sync frame
05dc 199a lrrn $AX0.H, @$AR0 05dc 199a lrrn $AX0.H, @$AR0
05dd 6554 movr'ln $ACC1, $AX0.H : $AX0.H, @$AR0 05dd 6554 movr'ln $ACC1, $AX0.H : $AX0.H, @$AR0
05de 005e loop $AC0.M 05de 005e loop $AC0.M
05df 65ad movr'lsnm $ACC1, $AX0.H : $AX0.H, $AC1.M 05df 65ad movr'lsnm $ACC1, $AX0.H : $AX0.H, $AC1.M
05e0 00da 0485 lr $AX0.H, @0x0485 05e0 00da 0485 lr $AX0.H, @0x0485
05e2 8600 tstaxh $AX0.H 05e2 8600 tstaxh $AX0.H
05e3 0295 05f6 jz 0x05f6 05e3 0295 05f6 jz 0x05f6