diff --git a/Source/Plugins/Plugin_DSP_HLE/Plugin_DSP_HLE.vcproj b/Source/Plugins/Plugin_DSP_HLE/Plugin_DSP_HLE.vcproj index 53658de20e..dc1a5a7261 100644 --- a/Source/Plugins/Plugin_DSP_HLE/Plugin_DSP_HLE.vcproj +++ b/Source/Plugins/Plugin_DSP_HLE/Plugin_DSP_HLE.vcproj @@ -676,10 +676,6 @@ RelativePath=".\Src\UCodes\UCode_Zelda.h" > - - @@ -688,6 +684,10 @@ RelativePath=".\Src\UCodes\UCode_Zelda_ADPCM.h" > + + = 48000)) - { - PV4l=PV3l; - PV3l=PV2l; - PV2l=PV1l; - PV1l=*(samples++); //32bit processing - num_stereo_samples--; - acc-=48000; - } - - // defaults to nearest - s32 DataL = PV1l; - - if (m_mode == 1) { //linear - DataL = PV1l + ((PV2l - PV1l)*acc)/48000; - } - else if (m_mode == 2) {//cubic - s32 a0l = PV1l - PV2l - PV4l + PV3l; - s32 a1l = PV4l - PV3l - a0l; - s32 a2l = PV1l - PV4l; - s32 a3l = PV2l; - - s32 t0l = ((a0l )*acc)/48000; - s32 t1l = ((t0l+a1l)*acc)/48000; - s32 t2l = ((t1l+a2l)*acc)/48000; - s32 t3l = ((t2l+a3l)); - - DataL = t3l; - } - - int l = DataL; - if (l < -32767) l = -32767; - if (l > 32767) l = 32767; - sample_queue.push(l); - m_queueSize += 1; - } - } - - FixedSizeQueue sample_queue; - int m_queueSize; - int m_mode; -}; -*/ - CUCode_Zelda::CUCode_Zelda(CMailHandler& _rMailHandler, u32 _CRC) : IUCode(_rMailHandler) , m_CRC(_CRC) @@ -536,7 +477,7 @@ void CUCode_Zelda::ExecuteList() // Read AFC coef table u16 *TempPtr = (u16*) g_dspInitialize.pGetMemoryPointer(m_AFCCoefTableAddr); for (int i = 0; i < 32; i++) - m_AFCCoefTable[i] = Common::swap16(TempPtr[i]); + m_AFCCoefTable[i] = (s16)Common::swap16(TempPtr[i]); DEBUG_LOG(DSPHLE, "DsetupTable"); DEBUG_LOG(DSPHLE, "Num voice param blocks: %i", m_NumVoices); @@ -686,7 +627,7 @@ void CUCode_Zelda::MixAdd(short* _Buffer, int _Size) for (u32 i = 0; i < m_NumVoices; i++) { u32 flags = m_SyncFlags[(i >> 4) & 0xF]; - if (!(flags & 0x8000)) + if (!(flags & 1 << (15 - (i & 0xF)))) continue; ZeldaVoicePB pb; diff --git a/Source/Plugins/Plugin_DSP_HLE/Src/UCodes/UCode_Zelda.h b/Source/Plugins/Plugin_DSP_HLE/Src/UCodes/UCode_Zelda.h index 91c8481bed..75bd653389 100644 --- a/Source/Plugins/Plugin_DSP_HLE/Src/UCodes/UCode_Zelda.h +++ b/Source/Plugins/Plugin_DSP_HLE/Src/UCodes/UCode_Zelda.h @@ -46,8 +46,8 @@ struct ZeldaVoicePB u32 CurAddr; // 0x38 | current address u32 RemLength; // 0x3A | remaining length u16 Unk3C[0x2A]; // 0x3C | unknown - u16 YN1; // 0x66 | YN1 - u16 YN2; // 0x67 | YN2 + u16 YN2; // 0x66 | YN2 + u16 YN1; // 0x67 | YN1 u16 Unk68[0x18]; // 0x68 | unknown // Read-only part @@ -111,7 +111,7 @@ private: s32* m_LeftBuffer; s32* m_RightBuffer; - u16 m_AFCCoefTable[32]; + s16 m_AFCCoefTable[32]; bool m_bSyncInProgress; u32 m_MaxVoice; @@ -172,6 +172,7 @@ private: void WritebackVoicePB(u32 _Addr, ZeldaVoicePB& PB); void MixAddVoice_PCM16(ZeldaVoicePB& PB, s32* _Buffer, int _Size); + void MixAddVoice_AFC(ZeldaVoicePB& PB, s32* _Buffer, int _Size); void MixAddVoice(ZeldaVoicePB& PB, s32* _LeftBuffer, s32* _RightBuffer, int _Size); }; diff --git a/Source/Plugins/Plugin_DSP_HLE/Src/UCodes/UCode_Zelda_Voice.cpp b/Source/Plugins/Plugin_DSP_HLE/Src/UCodes/UCode_Zelda_Voice.cpp new file mode 100644 index 0000000000..3b91b740b4 --- /dev/null +++ b/Source/Plugins/Plugin_DSP_HLE/Src/UCodes/UCode_Zelda_Voice.cpp @@ -0,0 +1,329 @@ +// Copyright (C) 2003-2008 Dolphin Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official SVN repository and contact information can be found at +// http://code.google.com/p/dolphin-emu/ + +#include "../Globals.h" +#include "UCodes.h" +#include "UCode_Zelda.h" +#include "UCode_Zelda_ADPCM.h" + +#include "../main.h" +#include "Mixer.h" + + +class CResampler +{ +public: + CResampler(short* samples, int num_stereo_samples, int core_sample_rate) + : m_mode(1) + , m_queueSize(0) + { + int PV1l=0,PV2l=0,PV3l=0,PV4l=0; + int acc=0; + + while (num_stereo_samples) + { + acc += core_sample_rate; + while (num_stereo_samples && (acc >= 48000)) + { + PV4l=PV3l; + PV3l=PV2l; + PV2l=PV1l; + PV1l=*(samples++); //32bit processing + num_stereo_samples--; + acc-=48000; + } + + // defaults to nearest + s32 DataL = PV1l; + + if (m_mode == 1) { //linear + DataL = PV1l + ((PV2l - PV1l)*acc)/48000; + } + else if (m_mode == 2) {//cubic + s32 a0l = PV1l - PV2l - PV4l + PV3l; + s32 a1l = PV4l - PV3l - a0l; + s32 a2l = PV1l - PV4l; + s32 a3l = PV2l; + + s32 t0l = ((a0l )*acc)/48000; + s32 t1l = ((t0l+a1l)*acc)/48000; + s32 t2l = ((t1l+a2l)*acc)/48000; + s32 t3l = ((t2l+a3l)); + + DataL = t3l; + } + + int l = DataL; + if (l < -32767) l = -32767; + if (l > 32767) l = 32767; + sample_queue.push(l); + m_queueSize += 1; + } + } + + FixedSizeQueue sample_queue; + int m_queueSize; + int m_mode; +}; + + +void CUCode_Zelda::ReadVoicePB(u32 _Addr, ZeldaVoicePB& PB) +{ + u16 *memory = (u16*)g_dspInitialize.pGetMemoryPointer(_Addr); + + // Perform byteswap + for (int i = 0; i < (0x180 / 2); i++) + ((u16*)&PB)[i] = Common::swap16(memory[i]); + + PB.RestartPos = (PB.RestartPos << 16) | (PB.RestartPos >> 16); + PB.CurAddr = (PB.CurAddr << 16) | (PB.CurAddr >> 16); + PB.RemLength = (PB.RemLength << 16) | (PB.RemLength >> 16); + PB.LoopStartPos = (PB.LoopStartPos << 16) | (PB.LoopStartPos >> 16); + PB.Length = (PB.Length << 16) | (PB.Length >> 16); + PB.StartAddr = (PB.StartAddr << 16) | (PB.StartAddr >> 16); + PB.UnkAddr = (PB.UnkAddr << 16) | (PB.UnkAddr >> 16); +} + +void CUCode_Zelda::WritebackVoicePB(u32 _Addr, ZeldaVoicePB& PB) +{ + u16 *memory = (u16*)g_dspInitialize.pGetMemoryPointer(_Addr); + + PB.RestartPos = (PB.RestartPos << 16) | (PB.RestartPos >> 16); + PB.CurAddr = (PB.CurAddr << 16) | (PB.CurAddr >> 16); + PB.RemLength = (PB.RemLength << 16) | (PB.RemLength >> 16); + + // Perform byteswap + // Only the first 0x100 bytes are written back + for (int i = 0; i < (0x100 / 2); i++) + memory[i] = Common::swap16(((u16*)&PB)[i]); +} + +void CUCode_Zelda::MixAddVoice_PCM16(ZeldaVoicePB &PB, s32* _Buffer, int _Size) +{ + float ratioFactor = 32000.0f / (float)soundStream->GetMixer()->GetSampleRate(); + u32 _ratio = (((PB.RatioInt * 80) + PB.RatioFrac) << 4) & 0xFFFF0000; + u64 ratio = (u64)(((_ratio / 80) << 16) * ratioFactor); + u32 pos[2] = {0, 0}; + int i = 0; + + if (PB.KeyOff != 0) + return; + + if (PB.NeedsReset) + { + PB.RemLength = PB.Length - PB.RestartPos; + PB.CurAddr = PB.StartAddr + (PB.RestartPos << 1); + PB.ReachedEnd = 0; + } + +_lRestart: + if (PB.ReachedEnd) + { + PB.ReachedEnd = 0; + + if (PB.RepeatMode == 0) + { + PB.KeyOff = 1; + PB.RemLength = 0; + PB.CurAddr = PB.StartAddr + (PB.RestartPos << 1) + PB.Length; + return; + } + else + { + PB.RestartPos = PB.LoopStartPos; + PB.RemLength = PB.Length - PB.RestartPos; + PB.CurAddr = PB.StartAddr + (PB.RestartPos << 1); + pos[1] = 0; pos[0] = 0; + } + } + + s16 *source; + if (m_CRC == 0xD643001F) + source = (s16*)(g_dspInitialize.pGetMemoryPointer(m_DMABaseAddr) + PB.CurAddr); + else + source = (s16*)(g_dspInitialize.pGetARAMPointer() + PB.CurAddr); + + for (; i < _Size;) + { + s16 sample = Common::swap16(source[pos[1]]); + + _Buffer[i++] = (s32)sample; + + (*(u64*)&pos) += ratio; + if ((pos[1] + ((PB.CurAddr - PB.StartAddr) >> 1)) >= PB.Length) + { + PB.ReachedEnd = 1; + goto _lRestart; + } + } + + if (PB.RemLength < pos[1]) + { + PB.RemLength = 0; + PB.ReachedEnd = 1; + } + else + PB.RemLength -= pos[1]; + + PB.CurAddr += pos[1] << 1; +} + +void CUCode_Zelda::MixAddVoice_AFC(ZeldaVoicePB &PB, s32* _Buffer, int _Size) +{ + // initialize "decoder" if the sample is played the first time + if (PB.NeedsReset != 0) + { + // This is 0717_ReadOutPBStuff + + // increment 4fb + + // zelda: + // perhaps init or "has played before" + PB.CurBlock = 0x00; + PB.YN2 = 0x00; // history1 + PB.YN1 = 0x00; // history2 + + // samplerate? length? num of samples? i dunno... + // Likely length... + PB.RemLength = PB.Length; + + // Copy ARAM addr from r to rw area. + PB.CurAddr = PB.StartAddr; + } + + if (PB.KeyOff != 0) // 0747 early out... i dunno if this can happen because we filter it above + return; + + u32 Addr = PB.CurAddr; + u32 NumberOfSamples = PB.RemLength; + + // round upwards how many samples we need to copy, 0759 + NumberOfSamples = (NumberOfSamples + 0xf) >> 4; // i think the lower 4 are the fraction + u32 frac = NumberOfSamples & 0xF; + + u8 inBuffer[9]; + short outbuf[16]; + u32 sampleCount = 0; + + u8 *source; + if (m_CRC == 0xD643001F) + source = g_dspInitialize.pGetMemoryPointer(m_DMABaseAddr); + else + source = g_dspInitialize.pGetARAMPointer(); + + // It must be something like this: + + // The PB contains a small sample buffer of 0x4D decoded samples. + // If it's empty or "used", decode to it. + // Then, resample from this buffer to the output as you go. When it needs + // wrapping, decode more. + + while (NumberOfSamples > 0) + { + for (int i = 0; i < 9; i++) + { + // inBuffer[i] = g_dspInitialize.pARAM_Read_U8(ARAMAddr); + inBuffer[i] = source[Addr]; + Addr++; + } + + AFCdecodebuffer(m_AFCCoefTable, (char*)inBuffer, outbuf, (short*)&PB.YN2, (short*)&PB.YN1, 9); + CResampler Sampler(outbuf, 16, 48000); + + while (Sampler.m_queueSize > 0) + { + int sample = Sampler.sample_queue.front(); + Sampler.sample_queue.pop(); + Sampler.m_queueSize -= 1; + + // templbuffer[sampleCount] += sample; + // temprbuffer[sampleCount] += sample; + _Buffer[sampleCount] = sample; + sampleCount++; + + if (sampleCount > _Size) + break; + } + + if (sampleCount > _Size) + break; + + NumberOfSamples--; + } + + if (NumberOfSamples == 0) + { + PB.KeyOff = 1; // we are done ?? + } + + // write back + NumberOfSamples = (NumberOfSamples << 4); // missing fraction + + PB.CurAddr = Addr; + PB.RemLength = NumberOfSamples; + + + // i think pTest[0x3a] and pTest[0x3b] got an update after you have decoded some samples... + // just decrement them with the number of samples you have played + // and incrrease the ARAM Offset in pTest[0x38], pTest[0x39] + + + // end of block (Zelda 03b2) + PB.NeedsReset = 0; +} + +void CUCode_Zelda::MixAddVoice(ZeldaVoicePB &PB, s32* _LeftBuffer, s32* _RightBuffer, int _Size) +{ + memset(m_TempBuffer, 0, _Size * sizeof(s32)); + + if (PB.IsBlank) + { + s32 sample = (s32)(s16)PB.FixedSample; + for (int i = 0; i < _Size; i++) + m_TempBuffer[i] = sample; + } + else + { + switch (PB.Format) + { + case 0x0005: // AFC / unknown + case 0x0009: // AFC / ADPCM + MixAddVoice_AFC(PB, m_TempBuffer, _Size); + break; + + case 0x0010: // PCM16 + MixAddVoice_PCM16(PB, m_TempBuffer, _Size); + break; + } + + PB.NeedsReset = 0; + } + + for (int i = 0; i < _Size; i++) + { + s32 left = _LeftBuffer[i] + m_TempBuffer[i]; + s32 right = _RightBuffer[i] + m_TempBuffer[i]; + + if (left < -32768) left = -32768; + if (left > 32767) left = 32767; + _LeftBuffer[i] = left; + + if (right < -32768) right = -32768; + if (right > 32767) right = 32767; + _RightBuffer[i] = right; + } +} diff --git a/Source/Plugins/Plugin_DSP_HLE/Src/UCodes/UCode_Zelda_Voice.h b/Source/Plugins/Plugin_DSP_HLE/Src/UCodes/UCode_Zelda_Voice.h deleted file mode 100644 index 4879ccc617..0000000000 --- a/Source/Plugins/Plugin_DSP_HLE/Src/UCodes/UCode_Zelda_Voice.h +++ /dev/null @@ -1,161 +0,0 @@ -// Copyright (C) 2003-2008 Dolphin Project. - -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, version 2.0. - -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License 2.0 for more details. - -// A copy of the GPL 2.0 should have been included with the program. -// If not, see http://www.gnu.org/licenses/ - -// Official SVN repository and contact information can be found at -// http://code.google.com/p/dolphin-emu/ - -#ifndef _UCODE_ZELDA_VOICE_H -#define _UCODE_ZELDA_VOICE_H - -#include "../main.h" -#include "Mixer.h" - -void CUCode_Zelda::ReadVoicePB(u32 _Addr, ZeldaVoicePB& PB) -{ - u16 *memory = (u16*)g_dspInitialize.pGetMemoryPointer(_Addr); - - // Perform byteswap - for (int i = 0; i < (0x180 / 2); i++) - ((u16*)&PB)[i] = Common::swap16(memory[i]); - - PB.RestartPos = (PB.RestartPos << 16) | (PB.RestartPos >> 16); - PB.CurAddr = (PB.CurAddr << 16) | (PB.CurAddr >> 16); - PB.RemLength = (PB.RemLength << 16) | (PB.RemLength >> 16); - PB.LoopStartPos = (PB.LoopStartPos << 16) | (PB.LoopStartPos >> 16); - PB.Length = (PB.Length << 16) | (PB.Length >> 16); - PB.StartAddr = (PB.StartAddr << 16) | (PB.StartAddr >> 16); - PB.UnkAddr = (PB.UnkAddr << 16) | (PB.UnkAddr >> 16); -} - -void CUCode_Zelda::WritebackVoicePB(u32 _Addr, ZeldaVoicePB& PB) -{ - u16 *memory = (u16*)g_dspInitialize.pGetMemoryPointer(_Addr); - - PB.RestartPos = (PB.RestartPos << 16) | (PB.RestartPos >> 16); - PB.CurAddr = (PB.CurAddr << 16) | (PB.CurAddr >> 16); - PB.RemLength = (PB.RemLength << 16) | (PB.RemLength >> 16); - - // Perform byteswap - // Only the first 0x100 bytes are written back - for (int i = 0; i < (0x100 / 2); i++) - memory[i] = Common::swap16(((u16*)&PB)[i]); -} - -void CUCode_Zelda::MixAddVoice_PCM16(ZeldaVoicePB &PB, s32* _Buffer, int _Size) -{ - float ratioFactor = 32000.0f / (float)soundStream->GetMixer()->GetSampleRate(); - u32 _ratio = (((PB.RatioInt * 80) + PB.RatioFrac) << 4) & 0xFFFF0000; - u64 ratio = (u64)(((_ratio / 80) << 16) * ratioFactor); - u32 pos[2] = {0, 0}; - - if (PB.KeyOff != 0) - return; - - if (PB.NeedsReset) - { - PB.RemLength = PB.Length - PB.RestartPos; - PB.CurAddr = PB.StartAddr + (PB.RestartPos << 1); - PB.ReachedEnd = 0; - } - -_lRestart: - if (PB.ReachedEnd) - { - PB.ReachedEnd = 0; - - if (PB.RepeatMode == 0) - { - PB.KeyOff = 1; - PB.RemLength = 0; - PB.CurAddr = PB.StartAddr + (PB.RestartPos << 1) + PB.Length; - return; - } - else - { - PB.RestartPos = PB.LoopStartPos; - PB.RemLength = PB.Length - PB.RestartPos; - PB.CurAddr = PB.StartAddr + (PB.RestartPos << 1); - pos[1] = 0; pos[0] = 0; - } - } - - s16 *source; - if (m_CRC == 0xD643001F) - source = (s16*)(g_dspInitialize.pGetMemoryPointer(m_DMABaseAddr) + PB.CurAddr); - else - source = (s16*)(g_dspInitialize.pGetARAMPointer() + PB.CurAddr); - - for (int i = 0; i < _Size; i++) - { - s16 sample = Common::swap16(source[pos[1]]); - - _Buffer[i] = (s32)sample; - - (*(u64*)&pos) += ratio; - if ((pos[1] + ((PB.CurAddr - PB.StartAddr) >> 1)) >= PB.Length) - { - PB.ReachedEnd = 1; - _Size -= i + 1; - goto _lRestart; - } - } - - PB.RemLength -= pos[1]; - PB.CurAddr += pos[1] * 2; -} - -void CUCode_Zelda::MixAddVoice(ZeldaVoicePB &PB, s32* _LeftBuffer, s32* _RightBuffer, int _Size) -{ - //float ratioFactor = 32000.0f / (float)soundStream->GetMixer()->GetSampleRate(); - memset(m_TempBuffer, 0, _Size * sizeof(s32)); - - if (PB.IsBlank) - { - s32 sample = (s32)(s16)PB.FixedSample; - for (int i = 0; i < _Size; i++) - m_TempBuffer[i] = sample; - } - else - { - switch (PB.Format) - { - case 0x0005: // AFC / unknown - case 0x0009: // AFC / ADPCM - // coming soon! - return; - - case 0x0010: // PCM16 - MixAddVoice_PCM16(PB, m_TempBuffer, _Size); - break; - } - - PB.NeedsReset = 0; - } - - for (int i = 0; i < _Size; i++) - { - s32 left = _LeftBuffer[i] + m_TempBuffer[i]; - s32 right = _RightBuffer[i] + m_TempBuffer[i]; - - if (left < -32768) left = -32768; - if (left > 32767) left = 32767; - _LeftBuffer[i] = left; - - if (right < -32768) right = -32768; - if (right > 32767) right = 32767; - _RightBuffer[i] = right; - } -} - -#endif