diff --git a/Source/Plugins/Plugin_DSP_HLE/Plugin_DSP_HLE.vcproj b/Source/Plugins/Plugin_DSP_HLE/Plugin_DSP_HLE.vcproj index 322fcbdc00..53658de20e 100644 --- a/Source/Plugins/Plugin_DSP_HLE/Plugin_DSP_HLE.vcproj +++ b/Source/Plugins/Plugin_DSP_HLE/Plugin_DSP_HLE.vcproj @@ -1,7 +1,7 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + @@ -805,14 +809,6 @@ UsePrecompiledHeader="1" /> - - - @@ -822,7 +818,7 @@ /> + + + > 16) & 0xF; + m_MaxVoice = (n + 1) << 4; + m_SyncFlags[n] = _uMail & 0xFFFF; + m_bSyncInProgress = false; - case ReadingFrameSync: - { - int Slot = (_uMail >> 16) & 0x000F; - m_PBMask[Slot] = _uMail & 0xFFFF; - m_MailState = WaitForMail; + // Normally, we should mix to the buffers used by the game. + // We don't do it currently for a simple reason: + // if the game runs fast all the time, then it's OK, + // but if it runs slow, sound can become choppy. + // This problem won't happen when mixing to the buffer + // provided by MixAdd(), because the size of this buffer + // is automatically adjusted if the game runs slow. +#if 0 + if (m_SyncFlags[n] & 0x8000) + { + for (; m_CurVoice < m_MaxVoice; m_CurVoice++) + { + if (m_CurVoice >= m_NumVoices) + break; - // Zelda UC 05db - m_MaxSyncedPB = (Slot+1) << 4; - } - break; + MixVoice(m_CurVoice); + } + } + else +#endif + m_CurVoice = m_MaxVoice; - case ReadingMessage: - { - if (m_step < 0 || m_step >= sizeof(m_Buffer)/4) - PanicAlert("m_step out of range"); + if (m_CurVoice >= m_NumVoices) + { + m_CurBuffer++; - ((u32*)m_Buffer)[m_step] = _uMail; - m_step++; + m_rMailHandler.PushMail(DSP_SYNC); + g_dspInitialize.pGenerateDSPInterrupt(); + m_rMailHandler.PushMail(0xF355FF00 | m_CurBuffer); + + m_CurVoice = 0; - if (m_step >= m_numSteps) - { - ExecuteList(); - m_MailState = WaitForMail; - m_step = 0; - } - } - break; + if (m_CurBuffer == m_NumBuffers) + { + m_rMailHandler.PushMail(DSP_FRAME_END); + g_dspInitialize.pGenerateDSPInterrupt(); + + soundStream->GetMixer()->SetHLEReady(true); + DEBUG_LOG(DSPHLE, "Update the SoundThread to be in sync"); + soundStream->Update(); //do it in this thread to avoid sync problems + + m_bSyncCmdPending = false; + + } + } + } + else + { + m_bSyncInProgress = false; + } + + return; - case ReadingSystemMsg: - { - _dbg_assert_msg_(DSPHLE, (_uMail & 0xFFFF0000) == 0xCDD10000, "This is not a system msg", _uMail); - m_MailState = WaitForMail; - } - break; } + + if (m_bListInProgress) + { + if (m_step < 0 || m_step >= sizeof(m_Buffer)/4) + PanicAlert("m_step out of range"); + + ((u32*)m_Buffer)[m_step] = _uMail; + m_step++; + + if (m_step >= m_numSteps) + { + ExecuteList(); + m_bListInProgress = false; + } + + return; + } + + if (_uMail == 0) + { + m_bSyncInProgress = true; + } + else if ((_uMail >> 16) == 0) + { + m_bListInProgress = true; + m_numSteps = _uMail; + m_step = 0; + } + else if ((_uMail >> 16) == 0xCDD1) // A 0xCDD1000X mail should come right after we send a DSP_SYNCEND mail + { + // The low part of the mail tells the operation to perform + switch (_uMail & 0xFFFF) + { + case 0x0003: // Do nothing + return; + + case 0x0000: // Halt + case 0x0001: // Dump memory? and halt + case 0x0002: // Do something and halt + WARN_LOG(DSPHLE, "Zelda uCode: received halting operation %04X", _uMail & 0xFFFF); + return; + + default: // Invalid (the real ucode would likely crash) + WARN_LOG(DSPHLE, "Zelda uCode: received invalid operation %04X", _uMail & 0xFFFF); + return; + } + } + else + { + WARN_LOG(DSPHLE, "Zelda uCode: unknown mail %08X", _uMail); + } } +#if 0 void CUCode_Zelda::MixAdd(short* _pBuffer, int _iSize) { if (m_NumberOfFramesToRender > 0) @@ -410,24 +500,13 @@ void CUCode_Zelda::MixAdd(short* _pBuffer, int _iSize) m_CurrentFrameToRender++; - // sync, we are ready - m_rMailHandler.PushMail(DSP_SYNC); - g_dspInitialize.pGenerateDSPInterrupt(); - m_rMailHandler.PushMail(0xF3550000 | m_CurrentFrameToRender | 0xFF00); - - if (m_CurrentFrameToRender == m_NumberOfFramesToRender) - { - /* - afaik zelda hasnt registered a handler to FRAME_END... so skip it - m_rMailHandler.PushMail(DSP_FRAME_END); - g_dspInitialize.pGenerateDSPInterrupt(); - m_MailState = ReadingSystemMsg; - */ - m_NumberOfFramesToRender = 0; - m_CurrentFrameToRender = 0; - } - } + // make sure we never read outside the buffer by mistake. + // Before deleting extra reads in ExecuteList, we were getting these + // values. + memset(m_Buffer, 0xcc, sizeof(m_Buffer)); + } } +#endif // zelda debug ..803F6418 void CUCode_Zelda::ExecuteList() @@ -438,6 +517,7 @@ void CUCode_Zelda::ExecuteList() u32 CmdMail = Read32(); u32 Command = (CmdMail >> 24) & 0x7f; u32 Sync = CmdMail >> 16; + u32 ExtraData = CmdMail & 0xFFFF; DEBUG_LOG(DSPHLE, "=============================================================================="); DEBUG_LOG(DSPHLE, "Zelda UCode - execute dlist (cmd: 0x%04x : sync: 0x%04x)", Command, Sync); @@ -447,55 +527,128 @@ void CUCode_Zelda::ExecuteList() // DsetupTable ... zelda ww jumps to 0x0095 case 0x01: { - m_NumPBs = (CmdMail & 0xFFFF); - if (m_NumPBs > 0x40) - { - PanicAlert("(m_NumPBs > 0x40) to much PBs"); - m_NumPBs = 0x40; - } + u16 *TempPtr; + int i; + // num_param_blocks = ExtraData; + // u32 tmp[4]; + //param_blocks_ptr = tmp[0] = Read32(); + // tmp[1] = Read32(); + // tmp[2] = Read32(); + //param_blocks2_ptr = tmp[3] = Read32(); - m_PBAddress = Read32(); - u32 DSPADPCM_FILTER = Read32(); - u32 DSPRES_FILTER = Read32(); - m_PBAddress2 = Read32(); + m_NumVoices = ExtraData; - // What is this stuff? - u16 Buffer[0x280]; - for (int i = 0; i < 0x280; i++) - { - Buffer[i] = Memory_Read_U16(DSPADPCM_FILTER + (i*2)); - } + m_VoicePBsAddr = Read32() & 0x7FFFFFFF; + m_UnkTableAddr = Read32() & 0x7FFFFFFF; + m_AFCCoefTableAddr = Read32() & 0x7FFFFFFF; + m_ReverbPBsAddr = Read32() & 0x7FFFFFFF; // WARNING: reverb PBs are very different from voice PBs! - u16* pTmp = (u16*)m_AFCCoefTable; - for (int i = 0; i < 0x20; i++) - { - pTmp[i] = Memory_Read_U16(DSPRES_FILTER + (i*2)); - } + // Read AFC coef table + TempPtr = (u16*) g_dspInitialize.pGetMemoryPointer(m_AFCCoefTableAddr); + for (i = 0; i < 32; i++) + m_AFCCoefTable[i] = Common::swap16(TempPtr[i]); DEBUG_LOG(DSPHLE, "DsetupTable"); - DEBUG_LOG(DSPHLE, "Param Blocks 1: 0x%08x", m_PBAddress); - DEBUG_LOG(DSPHLE, "DSPADPCM_FILTER (size: 0x500): 0x%08x", DSPADPCM_FILTER); - DEBUG_LOG(DSPHLE, "DSPRES_FILTER (size: 0x40): 0x%08x", DSPRES_FILTER); - DEBUG_LOG(DSPHLE, "Param Blocks 2: 0x%08x", m_PBAddress2); - } - break; + DEBUG_LOG(DSPHLE, "Num voice param blocks: %i", m_NumVoices); + DEBUG_LOG(DSPHLE, "Voice param blocks address: 0x%08x", m_VoicePBsAddr); - // SyncFrame ... zelda ww jumps to 0x0243 - // SyncFrame doesn't send a 0xDCD10004 SYNC at all ... just the 0xDCD10005 for "frame end" - case 0x02: - { - m_MixingBufferLeft = Read32(); - m_MixingBufferRight = Read32(); + // This points to some strange data table. + DEBUG_LOG(DSPHLE, "DSPRES_FILTER (size: 0x40): 0x%08x", m_UnkTableAddr); - m_NumberOfFramesToRender = (CmdMail >> 16) & 0xFF; - m_MaxSyncedPB = 0; + // Zelda WW: This points to a 64-byte array of coefficients, which are EXACTLY the same + // as the AFC ADPCM coef array in decode.c of the in_cube winamp plugin, + // which can play Zelda audio. + // There's also a lot more table-looking data immediately after - maybe alternative + // tables? I wonder where the parameter blocks are? + DEBUG_LOG(DSPHLE, "DSPADPCM_FILTER (size: 0x500): 0x%08x", m_AFCCoefTableAddr); + DEBUG_LOG(DSPHLE, "Reverb param blocks address: 0x%08x", m_ReverbPBsAddr); + } + break; + + // SyncFrame ... zelda ww jumps to 0x0243 + case 0x02: + { + //u32 tmp[2]; + //tmp[0] = Read32(); + //tmp[1] = Read32(); + + // We're ready to mix + // soundStream->GetMixer()->SetHLEReady(true); + // DEBUG_LOG(DSPHLE, "Update the SoundThread to be in sync"); + //soundStream->Update(); //do it in this thread to avoid sync problems + + m_bSyncCmdPending = true; + m_CurBuffer = 0; + m_NumBuffers = (CmdMail >> 16) & 0xFF; + + // Addresses for right & left buffers in main memory + // Each buffer is 160 bytes long. The number of (both left & right) buffers + // is set by the first mail of the list. + m_RightBuffersAddr = Read32() & 0x7FFFFFFF; + m_LeftBuffersAddr = Read32() & 0x7FFFFFFF; DEBUG_LOG(DSPHLE, "DsyncFrame"); - DEBUG_LOG(DSPHLE, "Left Mixing Buffer: 0x%08x", m_MixingBufferLeft); - DEBUG_LOG(DSPHLE, "Right Mixing Buffer: 0x%08x", m_MixingBufferRight); + // These alternate between three sets of mixing buffers. They are all three fairly near, + // but not at, the ADMA read addresses. + DEBUG_LOG(DSPHLE, "Right buffer address: 0x%08x", m_RightBuffersAddr); + DEBUG_LOG(DSPHLE, "Left buffer address: 0x%08x", m_LeftBuffersAddr); + //DEBUG_LOG(DSPHLE, "DSPADPCM_FILTER (size: 0x500): 0x%08x", tmp[2]); // wtf? - // This is where we should render. - soundStream->GetMixer()->SetHLEReady(true); + // Let's log the parameter blocks. + // Copy and byteswap the parameter blocks. + + // For some reason, in Z:WW we get no param blocks until in-game, + // while Zelda Four Swords happily sets param blocks as soon as the title screen comes up. + // Looks like it's playing midi music. +#if 0 + DEBUG_LOG(DSPHLE, "Param block at %08x:", param_blocks_ptr); + CopyPBsFromRAM(); + for (int i = 0; i < num_param_blocks; i++) + { + const ZPB &pb = zpbs[i]; + // The only thing that consistently looks like a pointer in the param blocks. + u32 addr = (pb.addr_high << 16) | pb.addr_low; + if (addr) + { + DEBUG_LOG(DSPHLE, "Param block: ==== %i ( %08x ) ====", i, GetParamBlockAddr(i)); + DEBUG_LOG(DSPHLE, "Addr: %08x Type: %i", addr, pb.type); + + // Got one! Read from ARAM, dump to file. + // I can't get the below to produce anything resembling sane audio :( + //addr *= 2; + /* + int size = 0x10000; + u8 *temp = new u8[size]; + for (int i = 0; i < size; i++) { + temp[i] = g_dspInitialize.pARAM_Read_U8(addr + i); + } + s16 *audio = new s16[size * 4]; + int aoff = 0; + short hist1 = 0, hist2 = 0; + for (int i = 0; i < size; i+=9) + { + AFCdecodebuffer(temp + i, audio + aoff, &hist1, &hist2); + aoff += 16; + } + char fname[256]; + sprintf(fname, "%08x.bin", addr); + if (File::Exists(fname)) + continue; + + FILE *f = fopen(fname, "wb"); + fwrite(audio, 1, size*4, f); + fclose(f); + + sprintf(fname, "%08x_raw.bin", addr); + + f = fopen(fname, "wb"); + fwrite(temp, 1, size, f); + fclose(f); + */ + } + } + CopyPBsToRAM(); +#endif } return; @@ -515,26 +668,18 @@ void CUCode_Zelda::ExecuteList() u32 tmp[1]; tmp[0] = Read32(); DEBUG_LOG(DSPHLE, "DSetDolbyDelay"); - DEBUG_LOG(DSPHLE, "DOLBY2_DELAY_BUF (size 0x960): 0x%08x", tmp[0]); + DEBUG_LOG(DSPHLE, "DOLBY2_DELAY_BUF (size 0x960): 0x%08x", tmp); } break; - // Set VARAM - // Luigi__: in the real Zelda ucode, this opcode is dummy - // however, in the ucode used by SMG it isn't + // This opcode, in the SMG ucode, sets the base address for audio data transfers from main memory (using DMA). + // In the Zelda ucode, it is dummy, because this ucode uses accelerator for audio data transfers. case 0x0e: { - DEBUG_LOG(DSPHLE, "Set VARAM - ???"); - /* - 00b0 0080 037d lri $AR0, #0x037d - 00b2 0e01 lris $AC0.M, #0x01 - 00b3 02bf 0065 call 0x0065 - 00b5 00de 037d lr $AC0.M, @0x037d - 00b7 0240 7fff andi $AC0.M, #0x7fff - 00b9 00fe 037d sr @0x037d, $AC0.M - 00bb 029f 0041 jmp 0x0041 - */ - // + m_DMABaseAddr = Read32() & 0x7FFFFFFF; + + DEBUG_LOG(DSPHLE, "DsetDMABaseAddr"); + DEBUG_LOG(DSPHLE, "DMA base address: 0x%08x", m_DMABaseAddr); } break; @@ -550,6 +695,7 @@ void CUCode_Zelda::ExecuteList() m_rMailHandler.PushMail(0xF3550000 | Sync); } +#if 0 void CUCode_Zelda::CopyPBsFromRAM() { for (u32 i = 0; i < m_NumPBs; i++) @@ -578,18 +724,7 @@ void CUCode_Zelda::CopyPBsToRAM() } } } - -// Decoder from in_cube by hcs/destop/etc. Haven't yet found a valid use for it. - -// Looking at in_cube, it seems to be 9 bytes of input = 16 samples of output. -// Different from AX ADPCM which is 8 bytes of input = 14 samples of output. -// input = location of encoded source samples -// out = location of destination buffer (16 bits / sample) - -// I am sure that there are 5 bytes of input for 16 samples output too... just check the UCode -// if (type == 5) -> 5 input bytes -// if (type == 9) -> 9 input bytes -// +#endif /////////////////////////////////////////////////////////////////////////////////////////////////// // @@ -615,65 +750,65 @@ void CUCode_Zelda::DumpPB(const ZPB& _rPB) } } -void write32le(int in, unsigned char * buf) { - buf[0]=in&0xff; - buf[1]=(in>>8)&0xff; - buf[2]=(in>>16)&0xff; - buf[3]=(in>>24)&0xff; -} - -int CUCode_Zelda::DumpAFC(u8* pIn, const int size, const int srate) +// size is in stereo samples. +void CUCode_Zelda::MixAdd(short* _Buffer, int _Size) { - unsigned char inbuf[9]; - short outbuf[16]; - FILE * outfile; - int sizeleft; - int outsize,outsizetotal; - short hist=0,hist2=0; + if (_Size > 256 * 1024) + _Size = 256 * 1024; - unsigned char wavhead[44] = { - 0x52, 0x49, 0x46, 0x46, 0x00, 0x00, 0x00, 0x00, 0x57, 0x41, 0x56, 0x45, 0x66, 0x6D, 0x74, 0x20, - 0x10, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x02, 0x00, 0x10, 0x00, 0x64, 0x61, 0x74, 0x61, 0x00, 0x00, 0x00, 0x00 - }; + memset(m_LeftBuffer, 0, _Size * sizeof(s32)); + memset(m_RightBuffer, 0, _Size * sizeof(s32)); - outfile = fopen("d:/supa.wav","wb"); - if (!outfile) return 1; + for (u32 i = 0; i < m_NumVoices; i++) + { + u32 flags = m_SyncFlags[(i >> 4) & 0xF]; + if (!(flags & 0x8000)) + continue; - outsize = size/9*16*2; - outsizetotal = outsize+8; - write32le(outsizetotal,wavhead+4); - write32le(outsize,wavhead+40); - write32le(srate,wavhead+24); - write32le(srate*2,wavhead+28); - if (fwrite(wavhead,1,44,outfile)!=44) return 1; + ZeldaVoicePB pb; + ReadVoicePB(m_VoicePBsAddr + (i * 0x180), pb); - for (sizeleft=size;sizeleft>=9;sizeleft-=9) { - memcpy(inbuf, pIn, 9); - pIn += 9; + if (pb.Status == 0) + continue; + if (pb.KeyOff != 0) + continue; - AFCdecodebuffer(m_AFCCoefTable, (char*)inbuf,outbuf,&hist,&hist2,9); + MixAddVoice(pb, m_LeftBuffer, m_RightBuffer, _Size); + WritebackVoicePB(m_VoicePBsAddr + (i * 0x180), pb); + } - if (fwrite(outbuf,1,16*2,outfile) != 16*2) - return 1; - } + if (_Buffer) + { + for (u32 i = 0; i < _Size; i++) + { + s32 left = (s32)_Buffer[0] + m_LeftBuffer[i]; + s32 right = (s32)_Buffer[1] + m_RightBuffer[i]; - if (fclose(outfile)==EOF) return 1; + if (left < -32768) left = -32768; + if (left > 32767) left = 32767; + _Buffer[0] = (short)left; - return 0; -} + if (right < -32768) right = -32768; + if (right > 32767) right = 32767; + _Buffer[1] = (short)right; + + _Buffer += 2; + } + } +} void CUCode_Zelda::DoState(PointerWrap &p) { - p.Do(m_MailState); - p.Do(m_PBMask); - p.Do(m_NumPBs); - p.Do(m_PBAddress); - p.Do(m_MaxSyncedPB); - p.Do(m_PBs); + //p.Do(m_MailState); + //p.Do(m_PBMask); + //p.Do(m_NumPBs); + //p.Do(m_PBAddress); + //p.Do(m_MaxSyncedPB); + //p.Do(m_PBs); p.Do(m_readOffset); - p.Do(m_NumberOfFramesToRender); - p.Do(m_CurrentFrameToRender); + //p.Do(m_NumberOfFramesToRender); + //p.Do(m_CurrentFrameToRender); p.Do(m_numSteps); p.Do(m_step); p.Do(m_Buffer); } + 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 c8d8c9db5f..9783e1bd13 100644 --- a/Source/Plugins/Plugin_DSP_HLE/Src/UCodes/UCode_Zelda.h +++ b/Source/Plugins/Plugin_DSP_HLE/Src/UCodes/UCode_Zelda.h @@ -21,6 +21,46 @@ #include "Common.h" #include "UCodes.h" + +struct ZeldaVoicePB +{ + // Read-Write part + u16 Status; // 0x00 | 1 = play, 0 = stop + u16 KeyOff; // 0x01 | writing 1 stops voice? + u16 RatioInt; // 0x02 | delta? ratio? integer part + u16 Unk03; // 0x03 | unknown + u16 NeedsReset; // 0x04 | indicates if some values in PB need to be reset + u16 ReachedEnd; // 0x05 | set to 1 when end reached + u16 IsBlank; // 0x06 | 0 = normal sound, 1 = samples are always the same + u16 Unk07[0x29]; // 0x07 | unknown + u16 RatioFrac; // 0x30 | ??? ratio fractional part + u16 Unk31; // 0x31 | unknown + u16 CurBlock; // 0x32 | current block? + u16 FixedSample; // 0x33 | sample value for "blank" voices + u32 RestartPos; // 0x34 | restart pos + u16 Unk36[2]; // 0x36 | unknown + u32 CurAddr; // 0x38 | current address + u32 RemLength; // 0x3A | remaining length + u16 Unk3C[0x2A]; // 0x3C | unknown + u16 YN1; // 0x66 | YN1 + u16 YN2; // 0x67 | YN2 + u16 Unk68[0x18]; // 0x68 | unknwon + + // Read-only part + u16 Format; // 0x80 | audio format + u16 RepeatMode; // 0x81 | 0 = one-shot, non zero = loop + u16 Unk82[0x6]; // 0x82 | unknown + u32 LoopStartPos; // 0x88 | loopstart pos + u32 Length; // 0x8A | sound length + u32 StartAddr; // 0x8C | sound start address + u32 UnkAddr; // 0x8E | ??? + u16 Padding[0x30]; // 0x90 | padding +}; + +// Here's a piece of pure guesswork, looking at random supposedly-PBs +// from Zelda Four Swords. + +// These are 0x180 bytes large. struct ZPB { // R/W data ============= @@ -60,7 +100,7 @@ namespace { class CUCode_Zelda : public IUCode { public: - CUCode_Zelda(CMailHandler& _rMailHandler); + CUCode_Zelda(CMailHandler& _rMailHandler, u32 _CRC); virtual ~CUCode_Zelda(); void HandleMail(u32 _uMail); @@ -81,6 +121,13 @@ public: void DumpPB(const ZPB& _rPB); int DumpAFC(u8* pIn, const int size, const int srate); + u32 Read32() + { + u32 res = *(u32*)&m_Buffer[m_readOffset]; + m_readOffset += 4; + return res; + } + private: enum EDSP_Codes { @@ -92,15 +139,43 @@ private: DSP_FRAME_END = 0xDCD10005, }; - // AFC CoefTable - s16 m_AFCCoefTable[32]; + u32 m_CRC; - // Command 0x2: SyncFrame - int m_NumberOfFramesToRender; - int m_CurrentFrameToRender; + s32* m_TempBuffer; + s32* m_LeftBuffer; + s32* m_RightBuffer; - // List in progress + u16 m_AFCCoefTable[32]; + + bool m_bSyncInProgress; + u32 m_MaxVoice; + u32 m_SyncFlags[16]; + + u32 m_NumVoices; + + bool m_bSyncCmdPending; + u32 m_CurVoice; + u32 m_CurBuffer; + u32 m_NumBuffers; + + // Those are set by command 0x1 (DsetupTable) + u32 m_VoicePBsAddr; + u32 m_UnkTableAddr; + u32 m_AFCCoefTableAddr; + u32 m_ReverbPBsAddr; + + u32 m_RightBuffersAddr; + u32 m_LeftBuffersAddr; + //u32 m_unkAddr; + u32 m_pos; + + // Only in SMG ucode + // Set by command 0xE (DsetDMABaseAddr) + u32 m_DMABaseAddr; + + // List, buffer management ===================== u32 m_numSteps; + bool m_bListInProgress; u32 m_step; u8 m_Buffer[1024]; void ExecuteList(); @@ -126,16 +201,12 @@ private: u32 m_MixingBufferLeft; u32 m_MixingBufferRight; - u32 m_MaxSyncedPB; - ZPB m_PBs[0x40]; + void ReadVoicePB(u32 _Addr, ZeldaVoicePB& PB); + void WritebackVoicePB(u32 _Addr, ZeldaVoicePB& PB); - u32 Read32() - { - u32 res = *(u32*)&m_Buffer[m_readOffset]; - m_readOffset += 4; - return res; - } + void MixAddVoice_PCM16(ZeldaVoicePB& PB, s32* _Buffer, int _Size); + void MixAddVoice(ZeldaVoicePB& PB, s32* _LeftBuffer, s32* _RightBuffer, int _Size); }; #endif 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 new file mode 100644 index 0000000000..4879ccc617 --- /dev/null +++ b/Source/Plugins/Plugin_DSP_HLE/Src/UCodes/UCode_Zelda_Voice.h @@ -0,0 +1,161 @@ +// 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 diff --git a/Source/Plugins/Plugin_DSP_HLE/Src/UCodes/UCodes.cpp b/Source/Plugins/Plugin_DSP_HLE/Src/UCodes/UCodes.cpp index 7c2e8973f1..1615198e44 100644 --- a/Source/Plugins/Plugin_DSP_HLE/Src/UCodes/UCodes.cpp +++ b/Source/Plugins/Plugin_DSP_HLE/Src/UCodes/UCodes.cpp @@ -65,13 +65,13 @@ IUCode* UCodeFactory(u32 _CRC, CMailHandler& _rMailHandler) case 0x56d36052: // Mario Sunshine case 0x2fcdf1ec: // Mario Kart, zelda 4 swords INFO_LOG(CONSOLE, "Zelda ucode chosen\n"); - return new CUCode_Zelda(_rMailHandler); + return new CUCode_Zelda(_rMailHandler, _CRC); // WII CRCs case 0x6c3f6f94: // zelda - PAL case 0xd643001f: // mario galaxy - PAL INFO_LOG(CONSOLE, "Zelda Wii ucode chosen\n"); - return new CUCode_Zelda(_rMailHandler); + return new CUCode_Zelda(_rMailHandler, _CRC); case 0x5ef56da3: // AX demo case 0x347112ba: // raving rabbits