diff --git a/Source/Plugins/Plugin_DSP_NULL/Src/UCodes/UCode_AXWii.cpp b/Source/Plugins/Plugin_DSP_NULL/Src/UCodes/UCode_AXWii.cpp new file mode 100644 index 0000000000..bf262b8a8f --- /dev/null +++ b/Source/Plugins/Plugin_DSP_NULL/Src/UCodes/UCode_AXWii.cpp @@ -0,0 +1,372 @@ +// 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 "StringUtil.h" + +#include "../MailHandler.h" + +#include "UCodes.h" +#include "UCode_AXStructs.h" +#include "UCode_AX.h" // for some functions in CUCode_AX +#include "UCode_AXWii.h" +#include "UCode_AX_Voice.h" + + +// ------------------------------------------------------------------ +// Declarations +// ----------- +extern bool gSequenced; + +// ----------- + + +CUCode_AXWii::CUCode_AXWii(CMailHandler& _rMailHandler, u32 l_CRC) + : IUCode(_rMailHandler) + , m_addressPBs(0xFFFFFFFF) + , _CRC(l_CRC) +{ + // we got loaded + m_rMailHandler.PushMail(0xDCD10000); + m_rMailHandler.PushMail(0x80000000); // handshake ??? only (crc == 0xe2136399) needs it ... + + templbuffer = new int[1024 * 1024]; + temprbuffer = new int[1024 * 1024]; + + lCUCode_AX = new CUCode_AX(_rMailHandler); + lCUCode_AX->_CRC = l_CRC; +} + +CUCode_AXWii::~CUCode_AXWii() +{ + m_rMailHandler.Clear(); + delete [] templbuffer; + delete [] temprbuffer; +} + +void CUCode_AXWii::HandleMail(u32 _uMail) +{ + if ((_uMail & 0xFFFF0000) == MAIL_AX_ALIST) + { + // a new List + } + else + { + AXTask(_uMail); + } +} + + +void CUCode_AXWii::MixAdd(short* _pBuffer, int _iSize) +{ + if(_CRC == 0xfa450138) + { + AXParamBlockWii PBs[NUMBER_OF_PBS]; + MixAdd_( _pBuffer, _iSize, PBs); + } + else + { + AXParamBlockWii_ PBs[NUMBER_OF_PBS]; + MixAdd_(_pBuffer, _iSize, PBs); + } +} + + +template +void CUCode_AXWii::MixAdd_(short* _pBuffer, int _iSize, ParamBlockType &PBs) +{ + //AXParamBlockWii PBs[NUMBER_OF_PBS]; + + // read out pbs + int numberOfPBs = ReadOutPBsWii(m_addressPBs, PBs, NUMBER_OF_PBS); + + if (_iSize > 1024 * 1024) + _iSize = 1024 * 1024; + + // write zeroes to the beginning of templbuffer + memset(templbuffer, 0, _iSize * sizeof(int)); + memset(temprbuffer, 0, _iSize * sizeof(int)); + + // ------------------------------------------- + // write logging data to debugger + + // --------------------------------------------------------------------------------------- + /* Make the updates we are told to do. See comments to the GC version in UCode_AX.cpp */ + // ------------ + for (int i = 0; i < numberOfPBs; i++) + { + u16 *pDest = (u16 *)&PBs[i]; + u16 upd0 = pDest[41]; u16 upd1 = pDest[42]; u16 upd2 = pDest[43]; // num_updates + u16 upd_hi = pDest[44]; // update addr + u16 upd_lo = pDest[45]; + int numupd = upd0 + upd1 + upd2; + if(numupd > 64) numupd = 64; // prevent to high values + const u32 updaddr = (u32)(upd_hi << 16) | upd_lo; + int on = false, off = false; + for (int j = 0; j < numupd; j++) // make alll updates + { + const u16 updpar = Memory_Read_U16(updaddr); + const u16 upddata = Memory_Read_U16(updaddr + 2); + // some safety checks, I hope it's enough + if( ( (updaddr > 0x80000000 && updaddr < 0x817fffff) + || (updaddr > 0x90000000 && updaddr < 0x93ffffff) ) + && updpar < 127 && updpar > 3 && upddata >= 0 // updpar > 3 because we don't want to change + // 0-3, those are important + //&& (upd0 || upd1 || upd2) // We should use these in some way to I think + // but I don't know how or when + && gSequenced) // on and off option + { + //PanicAlert("Update %i: %i = %04x", i, updpar, upddata); + //DebugLog("Update: %i = %04x", updpar, upddata); + pDest[updpar] = upddata; + } + if (updpar == 7 && upddata == 1) on++; + if (updpar == 7 && upddata == 1) off++; + } + // hack: if we get both an on and an off select on rather than off + if (on > 0 && off > 0) pDest[7] = 1; + } + + //PrintFile(1, "%08x %04x %04x\n", updaddr, updpar, upddata); + // ------------ + + + for (int i = 0; i < numberOfPBs; i++) + { + MixAddVoice(PBs[i], templbuffer, temprbuffer, _iSize, true); + } + + WriteBackPBsWii(m_addressPBs, PBs, numberOfPBs); + // We write the sound to _pBuffer + for (int i = 0; i < _iSize; i++) + { + // Clamp into 16-bit. Maybe we should add a volume compressor here. + int left = templbuffer[i] + _pBuffer[0]; + int right = temprbuffer[i] + _pBuffer[1]; + if (left < -32767) left = -32767; + if (left > 32767) left = 32767; + if (right < -32767) right = -32767; + if (right > 32767) right = 32767; + *_pBuffer++ = left; + *_pBuffer++ = right; + } + +} + + +void CUCode_AXWii::Update() +{ + // check if we have to sent something + if (!m_rMailHandler.IsEmpty()) + { + g_dspInitialize.pGenerateDSPInterrupt(); + } +} + + +// Shortcut +void CUCode_AXWii::SaveLog(const char* _fmt, ...) +{ +} + + +// AX seems to bootup one task only and waits for resume-callbacks +// everytime the DSP has "spare time" it sends a resume-mail to the CPU +// and the __DSPHandler calls a AX-Callback which generates a new AXFrame +bool CUCode_AXWii::AXTask(u32& _uMail) +{ + u32 uAddress = _uMail; + SaveLog("Begin"); + SaveLog("====================================================================="); + SaveLog("%08x: AXTask - AXCommandList-Addr", uAddress); + + u32 Addr__AXStudio; + u32 Addr__AXOutSBuffer; + // u32 Addr__AXOutSBuffer_1; + // u32 Addr__AXOutSBuffer_2; + u32 Addr__A; + // u32 Addr__12; + u32 Addr__5_1; + u32 Addr__5_2; + u32 Addr__6; + // u32 Addr__9; + + bool bExecuteList = true; + + if (false) + { + // PanicAlert("%i", sizeof(AXParamBlockWii)); // 252 ?? + FILE *f = fopen("D:\\axdump.txt", "a"); + if (!f) + f = fopen("D:\\axdump.txt", "w"); + + u32 addr = uAddress; + for (int i = 0; i < 100; i++) { + fprintf(f, "%02x\n", Memory_Read_U16(addr + i * 2)); + } + fprintf(f, "===========------------------------------------------------=\n"); + fclose(f); + } + else + { + // PanicAlert("%i", sizeof(AXParamBlock)); // 192 + } + + while (bExecuteList) + { + static int last_valid_command = 0; + u16 iCommand = Memory_Read_U16(uAddress); + uAddress += 2; + switch (iCommand) + { + case 0x0000: //00 + Addr__AXStudio = Memory_Read_U32(uAddress); + uAddress += 4; + SaveLog("%08x : AXLIST studio address: %08x", uAddress, Addr__AXStudio); + break; + + case 0x0001: + { + u32 address = Memory_Read_U32(uAddress); + uAddress += 4; + SaveLog("%08x : AXLIST 1: %08x", uAddress, address); + } + break; + + case 0x0003: + { + u32 address = Memory_Read_U32(uAddress); + uAddress += 4; + SaveLog("%08x : AXLIST 3: %08x", uAddress, address); + } + break; + + case 0x0004: // PBs are here now + m_addressPBs = Memory_Read_U32(uAddress); + lCUCode_AX->m_addressPBs = m_addressPBs; // for the sake of logging + SaveLog("%08x : AXLIST PB address: %08x", uAddress, m_addressPBs); + uAddress += 4; + break; + + case 0x0005: + if(Memory_Read_U16(uAddress) > 25) // this occured in Wii Sports + { + Addr__5_1 = Memory_Read_U32(uAddress); + uAddress += 4; + Addr__5_2 = Memory_Read_U32(uAddress); + uAddress += 4; + + uAddress += 2; + SaveLog("%08x : AXLIST 5_1 5_2 addresses: %08x %08x", uAddress, Addr__5_1, Addr__5_2); + } + else + { + uAddress += 2; + SaveLog("%08x : AXLIST Empty 0x0005", uAddress); + } + break; + + case 0x0006: + Addr__6 = Memory_Read_U32(uAddress); + uAddress += 10; + SaveLog("%08x : AXLIST 6 address: %08x", uAddress, Addr__6); + break; + +/**/ case 0x0007: // AXLIST_SBUFFER + Addr__AXOutSBuffer = Memory_Read_U32(uAddress); + uAddress += 10; + // uAddress += 12; + SaveLog("%08x : AXLIST OutSBuffer (0x0007) address: %08x", uAddress, Addr__AXOutSBuffer); + break; + +/* case 0x0009: + Addr__9 = Memory_Read_U32(uAddress); + uAddress += 4; + DebugLog("AXLIST 6 address: %08x", Addr__9); + break;*/ + + case 0x000a: // AXLIST_COMPRESSORTABLE + Addr__A = Memory_Read_U32(uAddress); + uAddress += 4; + //Addr__A = Memory_Read_U32(uAddress); + uAddress += 4; + SaveLog("%08x : AXLIST CompressorTable address: %08x", uAddress, Addr__A); + break; + + case 0x000b: + uAddress += 2; // one 0x8000 in rabbids + uAddress += 4 * 2; // then two RAM addressses + break; + + case 0x000c: + uAddress += 2; // one 0x8000 in rabbids + uAddress += 4 * 2; // then two RAM addressses + break; + + case 0x000d: + uAddress += 4 * 4; + break; + + case 0x000e: + // This is the end. + bExecuteList = false; + SaveLog("%08x : AXLIST end, wii stylee.", uAddress); + break; + + default: + { + static bool bFirst = true; + if (bFirst == true) + { + // A little more descreet way to say that there is a problem, that also let you + // take a look at the mail (and possible previous mails) in the debugger + SaveLog("%08x : Unknown AX-Command 0x%04x", uAddress, iCommand); + bExecuteList = false; + break; + + char szTemp[2048]; + sprintf(szTemp, "Unknown AX-Command 0x%04x (address: 0x%08x). Last valid: %02x\n", + iCommand, uAddress - 2, last_valid_command); + int num = -32; + while (num < 64+32) + { + char szTemp2[128] = ""; + sprintf(szTemp2, "%s0x%04x\n", num == 0 ? ">>" : " ", Memory_Read_U16(uAddress + num)); + strcat(szTemp, szTemp2); + num += 2; + } + + // Wii AX will always show this + PanicAlert(szTemp); + // bFirst = false; + } + + // unknown command so stop the execution of this TaskList + bExecuteList = false; + } + break; + } + if (bExecuteList) + last_valid_command = iCommand; + } + SaveLog("AXTask - done, send resume"); + SaveLog("====================================================================="); + SaveLog("End"); + + // i hope resume is okay AX + m_rMailHandler.PushMail(0xDCD10001); + return true; +} diff --git a/Source/Plugins/Plugin_DSP_NULL/Src/UCodes/UCode_AXWii.h b/Source/Plugins/Plugin_DSP_NULL/Src/UCodes/UCode_AXWii.h new file mode 100644 index 0000000000..477426cc91 --- /dev/null +++ b/Source/Plugins/Plugin_DSP_NULL/Src/UCodes/UCode_AXWii.h @@ -0,0 +1,64 @@ +// 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_AXWII +#define _UCODE_AXWII + +#include "UCode_AXStructs.h" + +#define NUMBER_OF_PBS 128 + +class CUCode_AXWii : public IUCode +{ +public: + CUCode_AXWii(CMailHandler& _rMailHandler, u32 _CRC); + virtual ~CUCode_AXWii(); + + void HandleMail(u32 _uMail); + void MixAdd(short* _pBuffer, int _iSize); + template + //void Logging(short* _pBuffer, int _iSize, int a, bool Wii, ParamBlockType &PBs, int numberOfPBs); + void MixAdd_(short* _pBuffer, int _iSize, ParamBlockType &PBs); + void Update(); + + // The logging function for the debugger + void Logging(short* _pBuffer, int _iSize, int a); + CUCode_AX * lCUCode_AX; // we need the logging functions in there + +private: + enum + { + MAIL_AX_ALIST = 0xBABE0000, + }; + + // PBs + u32 m_addressPBs; + u32 _CRC; + + int *templbuffer; + int *temprbuffer; + + // ax task message handler + bool AXTask(u32& _uMail); + void SaveLog(const char* _fmt, ...); + void SendMail(u32 _uMail); +}; + +//int ReadOutPBsWii(u32 pbs_address, AXParamBlockWii* _pPBs, int _num); +//void WriteBackPBsWii(u32 pbs_address, AXParamBlockWii* _pPBs, int _num); + +#endif // _UCODE_AXWII diff --git a/Source/Plugins/Plugin_DSP_NULL/Src/UCodes/UCode_AX_ADPCM.h b/Source/Plugins/Plugin_DSP_NULL/Src/UCodes/UCode_AX_ADPCM.h new file mode 100644 index 0000000000..acad346b3e --- /dev/null +++ b/Source/Plugins/Plugin_DSP_NULL/Src/UCodes/UCode_AX_ADPCM.h @@ -0,0 +1,92 @@ +// 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_AX_ADPCM_H +#define _UCODE_AX_ADPCM_H +#include "../Globals.h" + +inline s16 ADPCM_Step(PBADPCMInfo &adpcm, u32& samplePos, u32 newSamplePos, u16 frac) +{ + while (samplePos < newSamplePos) + { + if ((samplePos & 15) == 0) + { + adpcm.pred_scale = g_dspInitialize.pARAM_Read_U8((samplePos & ~15) >> 1); + samplePos += 2; + newSamplePos += 2; + } + + int scale = 1 << (adpcm.pred_scale & 0xF); + int coef_idx = adpcm.pred_scale >> 4; + + s32 coef1 = adpcm.coefs[coef_idx * 2 + 0]; + s32 coef2 = adpcm.coefs[coef_idx * 2 + 1]; + + int temp = (samplePos & 1) ? + (g_dspInitialize.pARAM_Read_U8(samplePos >> 1) & 0xF) : + (g_dspInitialize.pARAM_Read_U8(samplePos >> 1) >> 4); + + if (temp >= 8) + temp -= 16; + + // 0x400 = 0.5 in 11-bit fixed point + int val = (scale * temp) + ((0x400 + coef1 * adpcm.yn1 + coef2 * adpcm.yn2) >> 11); + + if (val > 0x7FFF) + val = 0x7FFF; + else if (val < -0x7FFF) + val = -0x7FFF; + + adpcm.yn2 = adpcm.yn1; + adpcm.yn1 = val; + + samplePos++; + } + + return adpcm.yn1; +} + +// ======================================================================================= +// Volume control (ramping) +// -------------- +inline u16 ADPCM_Vol(u16 vol, u16 delta) +{ + int x = vol; + if (delta && delta < 0x5000) + x += delta * 20 * 8; // unsure what the right step is + //x += 1 * 20 * 8; + else if (delta && delta > 0x5000) + //x -= (0x10000 - delta); // this is to small, it's often 1 + x -= (0x10000 - delta) * 20 * 16; // if this was 20 * 8 the sounds in Fire Emblem and Paper Mario + // did not have time to go to zero before the were closed + //x -= 1 * 20 * 16; + + // make lower limits + if (x < 0) x = 0; + //if (pb.mixer_control < 1000 && x < pb.mixer_control) x = pb.mixer_control; // does this make + // any sense? + + // make upper limits + //if (mixer_control > 1000 && x > mixer_control) x = mixer_control; // maybe mixer_control also + // has a volume target? + //if (x >= 0x7fff) x = 0x7fff; // this seems a little high + if (x >= 0x4e20) x = 0x4e20; // add a definitive limit at 20 000 + return x; // update volume +} +// ============== + +#endif // _UCODE_AX_ADPCM_H diff --git a/Source/Plugins/Plugin_DSP_NULL/Src/UCodes/UCode_AX_Voice.h b/Source/Plugins/Plugin_DSP_NULL/Src/UCodes/UCode_AX_Voice.h new file mode 100644 index 0000000000..688786f0b7 --- /dev/null +++ b/Source/Plugins/Plugin_DSP_NULL/Src/UCodes/UCode_AX_Voice.h @@ -0,0 +1,396 @@ +// 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_AX_VOICE_H +#define _UCODE_AX_VOICE_H + +#include "UCode_AX_ADPCM.h" +#include "UCode_AX.h" + +// ---------------------------------------------------- +// Externals +// ----------- +extern bool gSSBM; +extern bool gSSBMremedy1; +extern bool gSSBMremedy2; +extern bool gSequenced; +extern bool gVolume; +extern bool gReset; +extern bool gSequenced; +extern float ratioFactor; + + +template +inline int ReadOutPBsWii(u32 pbs_address, ParamBlockType& _pPBs, int _num) +{ + int count = 0; + u32 blockAddr = pbs_address; + u32 pAddr = 0; + + // reading and 'halfword' swap + for (int i = 0; i < _num; i++) + { + const short *pSrc = (const short *)g_dspInitialize.pGetMemoryPointer(blockAddr); + pAddr = blockAddr; + + if (pSrc != NULL) + { + short *pDest = (short *)&_pPBs[i]; + for (u32 p = 0; p < sizeof(AXParamBlockWii) / 2; p++) + { + if(p == 6 || p == 7) pDest[p] = pSrc[p]; // control for the u32 + else pDest[p] = Common::swap16(pSrc[p]); + +#if defined(HAVE_WX) && HAVE_WX + #if defined(_DEBUG) || defined(DEBUGFAST) + if(m_frame) m_frame->gLastBlock = blockAddr + p*2 + 2; // save last block location + #endif +#endif + } + + _pPBs[i].mixer_control = Common::swap32(_pPBs[i].mixer_control); + blockAddr = (_pPBs[i].next_pb_hi << 16) | _pPBs[i].next_pb_lo; + count++; + + // Detect the last mail by checking when next_pb = 0 + u32 next_pb = (Common::swap16(pSrc[0]) << 16) | Common::swap16(pSrc[1]); + if(next_pb == 0) break; + } + else + break; + } + + // return the number of read PBs + return count; +} + +template +inline void WriteBackPBsWii(u32 pbs_address, ParamBlockType& _pPBs, int _num) +//void WriteBackPBsWii(u32 pbs_address, AXParamBlockWii* _pPBs, int _num) +{ + u32 blockAddr = pbs_address; + + // write back and 'halfword'swap + for (int i = 0; i < _num; i++) + { + short* pSrc = (short*)&_pPBs[i]; + short* pDest = (short*)g_dspInitialize.pGetMemoryPointer(blockAddr); + _pPBs[i].mixer_control = Common::swap32(_pPBs[i].mixer_control); + for (size_t p = 0; p < sizeof(AXParamBlockWii) / 2; p++) + { + if(p == 6 || p == 7) pDest[p] = pSrc[p]; // control for the u32 + else pDest[p] = Common::swap16(pSrc[p]); + } + + // next block + blockAddr = (_pPBs[i].next_pb_hi << 16) | _pPBs[i].next_pb_lo; + } +} + + +template +inline void MixAddVoice(ParamBlockType &pb, int *templbuffer, int *temprbuffer, int _iSize, bool Wii) +{ + DoVoiceHacks(pb, Wii); + + // ============= + if (pb.running) + { + + // ======================================================================================= + // Read initial parameters + // ------------ + //constants + const u32 ratio = (u32)(((pb.src.ratio_hi << 16) + pb.src.ratio_lo) * ratioFactor); + u32 sampleEnd = (pb.audio_addr.end_addr_hi << 16) | pb.audio_addr.end_addr_lo; + u32 loopPos = (pb.audio_addr.loop_addr_hi << 16) | pb.audio_addr.loop_addr_lo; + + //variables + u32 samplePos = (pb.audio_addr.cur_addr_hi << 16) | pb.audio_addr.cur_addr_lo; + u32 frac = pb.src.cur_addr_frac; + // ============= + + // ======================================================================================= + // Handle No-SRC streams - No src streams have pb.src_type == 2 and have pb.src.ratio_hi = 0 + // and pb.src.ratio_lo = 0. We handle that by setting the sampling ratio integer to 1. This + // makes samplePos update in the correct way. I'm unsure how we are actually supposed to + // detect that this setting. Updates did not fix this automatically. + // --------------------------------------------------------------------------------------- + // Stream settings + // src_type = 2 (most other games have src_type = 0) + // ------------ + // Affected games: + // Baten Kaitos - Eternal Wings (2003) + // Baten Kaitos - Origins (2006)? + // Soul Calibur 2: The movie music use src_type 2 but it needs no adjustment, perhaps + // the sound format plays in to, Baten use ADPCM, SC2 use PCM16 + // ------------ + //if (pb.src_type == 2 && (pb.src.ratio_hi == 0 && pb.src.ratio_lo == 0)) + if (pb.running && (pb.src.ratio_hi == 0 && pb.src.ratio_lo == 0)) + { + pb.src.ratio_hi = 1; + } + // ============= + + + // ======================================================================================= + // Games that use looping to play non-looping music streams - SSBM has info in all + // pb.adpcm_loop_info parameters but has pb.audio_addr.looping = 0. If we treat these streams + // like any other looping streams the music works. I'm unsure how we are actually supposed to + // detect that these kinds of blocks should be looping. It seems like pb.mixer_control == 0 may + // identify these types of blocks. Updates did not write any looping values. + // -------------- + if ( + (pb.adpcm_loop_info.pred_scale || pb.adpcm_loop_info.yn1 || pb.adpcm_loop_info.yn2) + && pb.mixer_control == 0 + ) + { + pb.audio_addr.looping = 1; + } + // ============== + + // Top Spin 3 Wii + if(pb.audio_addr.sample_format > 25) pb.audio_addr.sample_format = 0; + + // ======================================================================================= + // Walk through _iSize. _iSize = numSamples. If the game goes slow _iSize will be higher to + // compensate for that. _iSize can be as low as 100 or as high as 2000 some cases. + for (int s = 0; s < _iSize; s++) + { + int sample = 0; + frac += ratio; + u32 newSamplePos = samplePos + (frac >> 16); //whole number of frac + + // ======================================================================================= + // Process sample format + // -------------- + switch (pb.audio_addr.sample_format) + { + case AUDIOFORMAT_PCM8: + // TODO - the linear interpolation code below is somewhat suspicious + pb.adpcm.yn2 = pb.adpcm.yn1; //save last sample + pb.adpcm.yn1 = ((s8)g_dspInitialize.pARAM_Read_U8(samplePos)) << 8; + + if (pb.src_type == SRCTYPE_NEAREST) + { + sample = pb.adpcm.yn1; + } + else //linear interpolation + { + sample = (pb.adpcm.yn1 * (u16)frac + pb.adpcm.yn2 * (u16)(0xFFFF - frac)) >> 16; + } + + samplePos = newSamplePos; + break; + + case AUDIOFORMAT_PCM16: + // TODO - the linear interpolation code below is somewhat suspicious + pb.adpcm.yn2 = pb.adpcm.yn1; //save last sample + pb.adpcm.yn1 = (s16)(u16)((g_dspInitialize.pARAM_Read_U8(samplePos * 2) << 8) | (g_dspInitialize.pARAM_Read_U8((samplePos * 2 + 1)))); + if (pb.src_type == SRCTYPE_NEAREST) + sample = pb.adpcm.yn1; + else //linear interpolation + sample = (pb.adpcm.yn1 * (u16)frac + pb.adpcm.yn2 * (u16)(0xFFFF - frac)) >> 16; + + samplePos = newSamplePos; + break; + + case AUDIOFORMAT_ADPCM: + sample = ADPCM_Step(pb.adpcm, samplePos, newSamplePos, frac); + break; + + default: + break; + } + // ================ + + // =================================================================== + // Overall volume control. In addition to this there is also separate volume settings to + // different channels (left, right etc). + frac &= 0xffff; + + int vol = pb.vol_env.cur_volume >> 9; + sample = sample * vol >> 8; + + if (pb.mixer_control & MIXCONTROL_RAMPING) + { + int x = pb.vol_env.cur_volume; + x += pb.vol_env.cur_volume_delta; // I'm not sure about this, can anybody find a game + // that use this? Or how does it work? + if (x < 0) x = 0; + if (x >= 0x7fff) x = 0x7fff; + pb.vol_env.cur_volume = x; // maybe not per sample?? :P + } + + int leftmix = pb.mixer.volume_left >> 5; + int rightmix = pb.mixer.volume_right >> 5; + int left = sample * leftmix >> 8; + int right = sample * rightmix >> 8; + //adpcm has to walk from oldSamplePos to samplePos here + templbuffer[s] += left; + temprbuffer[s] += right; + // =============== + + + // =================================================================== + // Control the behavior when we reach the end of the sample + if (samplePos >= sampleEnd) + { + if (pb.audio_addr.looping == 1) + { + samplePos = loopPos; + if (pb.audio_addr.sample_format == AUDIOFORMAT_ADPCM) + { + if (!pb.is_stream) + { + pb.adpcm.yn1 = pb.adpcm_loop_info.yn1; + pb.adpcm.yn2 = pb.adpcm_loop_info.yn2; + pb.adpcm.pred_scale = pb.adpcm_loop_info.pred_scale; + } + } + } + else + { + pb.running = 0; + break; + } + } + // =============== + } // end of the _iSize loop + + // Update volume + //if (sizeof(ParamBlockType) == sizeof(AXParamBlock)) // this is not needed anymore I think + if (gVolume) // allow us to turn this off in the debugger + { + pb.mixer.volume_left = ADPCM_Vol(pb.mixer.volume_left, pb.mixer.unknown); + pb.mixer.volume_right = ADPCM_Vol(pb.mixer.volume_right, pb.mixer.unknown2); + } + + pb.src.cur_addr_frac = (u16)frac; + pb.audio_addr.cur_addr_hi = samplePos >> 16; + pb.audio_addr.cur_addr_lo = (u16)samplePos; + + } // if (pb.running) +} + + + +// ================================================ +// Voice hacks +// -------------- +template +inline void DoVoiceHacks(ParamBlockType &pb, bool Wii) +{ + // get necessary values + const u32 sampleEnd = (pb.audio_addr.end_addr_hi << 16) | pb.audio_addr.end_addr_lo; + const u32 loopPos = (pb.audio_addr.loop_addr_hi << 16) | pb.audio_addr.loop_addr_lo; + const u32 updaddr = (u32)(pb.updates.data_hi << 16) | pb.updates.data_lo; + const u16 updpar = Memory_Read_U16(updaddr); + const u16 upddata = Memory_Read_U16(updaddr + 2); + + // ======================================================================================= + /* Fix problems introduced with the SSBM fix. Sometimes when a music stream ended sampleEnd + would end up outside of bounds while the block was still playing resulting in noise + a strange noise. This should take care of that. + */ + // ------------ + if ( + (sampleEnd > (0x017fffff * 2) || loopPos > (0x017fffff * 2)) // ARAM bounds in nibbles + && gSSBMremedy1 + && !Wii + ) + { + pb.running = 0; + + // also reset all values if it makes any difference + pb.audio_addr.cur_addr_hi = 0; pb.audio_addr.cur_addr_lo = 0; + pb.audio_addr.end_addr_hi = 0; pb.audio_addr.end_addr_lo = 0; + pb.audio_addr.loop_addr_hi = 0; pb.audio_addr.loop_addr_lo = 0; + + pb.src.cur_addr_frac = 0; pb.src.ratio_hi = 0; pb.src.ratio_lo = 0; + pb.adpcm.pred_scale = 0; pb.adpcm.yn1 = 0; pb.adpcm.yn2 = 0; + + pb.audio_addr.looping = 0; + pb.adpcm_loop_info.pred_scale = 0; + pb.adpcm_loop_info.yn1 = 0; pb.adpcm_loop_info.yn2 = 0; + } + + /* + // the fact that no settings are reset (except running) after a SSBM type music stream or another + looping block (for example in Battle Stadium DON) has ended could cause loud garbled sound to be + played from one or more blocks. Perhaps it was in conjunction with the old sequenced music fix below, + I'm not sure. This was an attempt to prevent that anyway by resetting all. But I'm not sure if this + is needed anymore. Please try to play SSBM without it and see if it works anyway. + */ + if ( + // detect blocks that have recently been running that we should reset + pb.running == 0 && pb.audio_addr.looping == 1 + //pb.running == 0 && pb.adpcm_loop_info.pred_scale + + // this prevents us from ruining sequenced music blocks, may not be needed + /* + && !(pb.updates.num_updates[0] || pb.updates.num_updates[1] || pb.updates.num_updates[2] + || pb.updates.num_updates[3] || pb.updates.num_updates[4]) + */ + && !(updpar || upddata) + + && pb.mixer_control == 0 // only use this in SSBM + + && gSSBMremedy2 // let us turn this fix on and off + && !Wii + ) + { + // reset the detection values + pb.audio_addr.looping = 0; + pb.adpcm_loop_info.pred_scale = 0; + pb.adpcm_loop_info.yn1 = 0; pb.adpcm_loop_info.yn2 = 0; + + //pb.audio_addr.cur_addr_hi = 0; pb.audio_addr.cur_addr_lo = 0; + //pb.audio_addr.end_addr_hi = 0; pb.audio_addr.end_addr_lo = 0; + //pb.audio_addr.loop_addr_hi = 0; pb.audio_addr.loop_addr_lo = 0; + + //pb.src.cur_addr_frac = 0; PBs[i].src.ratio_hi = 0; PBs[i].src.ratio_lo = 0; + //pb.adpcm.pred_scale = 0; pb.adpcm.yn1 = 0; pb.adpcm.yn2 = 0; + } + + // ============= + + + // ======================================================================================= + // Reset all values + // ------------ + if (gReset + && (pb.running || pb.audio_addr.looping || pb.adpcm_loop_info.pred_scale) + ) + { + pb.running = 0; + + pb.audio_addr.cur_addr_hi = 0; pb.audio_addr.cur_addr_lo = 0; + pb.audio_addr.end_addr_hi = 0; pb.audio_addr.end_addr_lo = 0; + pb.audio_addr.loop_addr_hi = 0; pb.audio_addr.loop_addr_lo = 0; + + pb.src.cur_addr_frac = 0; pb.src.ratio_hi = 0; pb.src.ratio_lo = 0; + pb.adpcm.pred_scale = 0; pb.adpcm.yn1 = 0; pb.adpcm.yn2 = 0; + + pb.audio_addr.looping = 0; + pb.adpcm_loop_info.pred_scale = 0; + pb.adpcm_loop_info.yn1 = 0; pb.adpcm_loop_info.yn2 = 0; + } +} + + +#endif // _UCODE_AX_VOICE_H