diff --git a/Source/Core/Core/CMakeLists.txt b/Source/Core/Core/CMakeLists.txt index 734d966781..93def26ec2 100644 --- a/Source/Core/Core/CMakeLists.txt +++ b/Source/Core/Core/CMakeLists.txt @@ -71,7 +71,8 @@ set(SRCS Src/ActionReplay.cpp Src/HW/CPU.cpp Src/HW/DSP.cpp Src/HW/DSPHLE/UCodes/UCode_AX.cpp - Src/HW/DSPHLE/UCodes/UCode_AXWii.cpp + Src/HW/DSPHLE/UCodes/UCode_AXWii.cpp + Src/HW/DSPHLE/UCodes/UCode_NewAXWii.cpp Src/HW/DSPHLE/UCodes/UCode_CARD.cpp Src/HW/DSPHLE/UCodes/UCode_InitAudioSystem.cpp Src/HW/DSPHLE/UCodes/UCode_ROM.cpp diff --git a/Source/Core/Core/Core.vcxproj b/Source/Core/Core/Core.vcxproj index dca0d072fc..763308db73 100644 --- a/Source/Core/Core/Core.vcxproj +++ b/Source/Core/Core/Core.vcxproj @@ -262,6 +262,7 @@ + @@ -459,8 +460,11 @@ - + + + + diff --git a/Source/Core/Core/Core.vcxproj.filters b/Source/Core/Core/Core.vcxproj.filters index 68953aef07..ad6db227d5 100644 --- a/Source/Core/Core/Core.vcxproj.filters +++ b/Source/Core/Core/Core.vcxproj.filters @@ -197,6 +197,9 @@ HW %28Flipper/Hollywood%29\DSP Interface + HLE\HLE\uCodes + + HW %28Flipper/Hollywood%29\DSP Interface + HLE\HLE\uCodes + HW %28Flipper/Hollywood%29\DSP Interface + HLE\HLE\uCodes @@ -730,12 +733,21 @@ HW %28Flipper/Hollywood%29\DSP Interface + HLE\HLE\uCodes - + HW %28Flipper/Hollywood%29\DSP Interface + HLE\HLE\uCodes HW %28Flipper/Hollywood%29\DSP Interface + HLE\HLE\uCodes + + HW %28Flipper/Hollywood%29\DSP Interface + HLE\HLE\uCodes + + + HW %28Flipper/Hollywood%29\DSP Interface + HLE\HLE\uCodes + + + HW %28Flipper/Hollywood%29\DSP Interface + HLE\HLE\uCodes + HW %28Flipper/Hollywood%29\DSP Interface + HLE\HLE\uCodes diff --git a/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_AX.cpp b/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_AX.cpp index 57032a774e..81ef7c6482 100644 --- a/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_AX.cpp +++ b/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_AX.cpp @@ -200,7 +200,11 @@ void CUCode_AX::HandleCommandList() u16 idx = m_cmdlist[curr_idx++]; addr_hi = m_cmdlist[curr_idx++]; addr_lo = m_cmdlist[curr_idx++]; + // TODO + (void)samp_val; + (void)idx; + break; } diff --git a/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_AX.h b/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_AX.h index 7158c197e8..8fd2a2af5f 100644 --- a/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_AX.h +++ b/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_AX.h @@ -27,7 +27,7 @@ #define _UCODE_AX_H #include "UCodes.h" -#include "UCode_AXStructs.h" +#include "UCode_AX_Structs.h" // We can't directly use the mixer_control field from the PB because it does // not mean the same in all AX versions. The AX UCode converts the diff --git a/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_AXWii.cpp b/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_AXWii.cpp index 8c74c7a1e7..f4effbba6d 100644 --- a/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_AXWii.cpp +++ b/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_AXWii.cpp @@ -21,363 +21,247 @@ #include "Mixer.h" #include "UCodes.h" -#include "UCode_AXStructs.h" +#include "UCode_AXWii_Structs.h" +#include "UCode_AX.h" // for some functions in CUCode_AX #include "UCode_AXWii.h" - -#define AX_WII -#include "UCode_AX_Voice.h" +#include "UCode_AXWii_Voice.h" CUCode_AXWii::CUCode_AXWii(DSPHLE *dsp_hle, u32 l_CRC) - : CUCode_AX(dsp_hle, l_CRC) + : IUCode(dsp_hle, l_CRC) + , m_addressPBs(0xFFFFFFFF) { - WARN_LOG(DSPHLE, "Instantiating CUCode_AXWii"); + // we got loaded + m_rMailHandler.PushMail(DSP_INIT); + + templbuffer = new int[1024 * 1024]; + temprbuffer = new int[1024 * 1024]; + + wiisportsHack = m_CRC == 0xfa450138; } CUCode_AXWii::~CUCode_AXWii() { + m_rMailHandler.Clear(); + delete [] templbuffer; + delete [] temprbuffer; } -void CUCode_AXWii::HandleCommandList() +void CUCode_AXWii::HandleMail(u32 _uMail) { - // Temp variables for addresses computation - u16 addr_hi, addr_lo; - u16 addr2_hi, addr2_lo; - u16 volume; - -// WARN_LOG(DSPHLE, "Command list:"); -// for (u32 i = 0; m_cmdlist[i] != CMD_END; ++i) -// WARN_LOG(DSPHLE, "%04x", m_cmdlist[i]); -// WARN_LOG(DSPHLE, "-------------"); - - u32 curr_idx = 0; - bool end = false; - while (!end) + if (m_UploadSetupInProgress) { - u16 cmd = m_cmdlist[curr_idx++]; - - switch (cmd) - { - // Some of these commands are unknown, or unused in this AX HLE. - // We still need to skip their arguments using "curr_idx += N". - - case CMD_SETUP: - addr_hi = m_cmdlist[curr_idx++]; - addr_lo = m_cmdlist[curr_idx++]; - SetupProcessing(HILO_TO_32(addr)); - break; - - case CMD_UNK_01: curr_idx += 2; break; - case CMD_UNK_02: curr_idx += 2; break; - case CMD_UNK_03: curr_idx += 2; break; - - case CMD_PROCESS: - addr_hi = m_cmdlist[curr_idx++]; - addr_lo = m_cmdlist[curr_idx++]; - ProcessPBList(HILO_TO_32(addr)); - break; - - case CMD_MIX_AUXA: - case CMD_MIX_AUXB: - case CMD_MIX_AUXC: - volume = m_cmdlist[curr_idx++]; - addr_hi = m_cmdlist[curr_idx++]; - addr_lo = m_cmdlist[curr_idx++]; - addr2_hi = m_cmdlist[curr_idx++]; - addr2_lo = m_cmdlist[curr_idx++]; - MixAUXSamples(cmd - CMD_MIX_AUXA, HILO_TO_32(addr), HILO_TO_32(addr2), volume); - break; - - // These two go together and manipulate some AUX buffers. - case CMD_UNK_08: curr_idx += 13; break; - case CMD_UNK_09: curr_idx += 13; break; - - case CMD_UNK_0A: curr_idx += 4; break; - - case CMD_OUTPUT: - volume = m_cmdlist[curr_idx++]; - addr_hi = m_cmdlist[curr_idx++]; - addr_lo = m_cmdlist[curr_idx++]; - addr2_hi = m_cmdlist[curr_idx++]; - addr2_lo = m_cmdlist[curr_idx++]; - OutputSamples(HILO_TO_32(addr2), HILO_TO_32(addr), volume); - break; - - case CMD_UNK_0C: curr_idx += 5; break; - - case CMD_WM_OUTPUT: - { - u32 addresses[4] = { - (u32)(m_cmdlist[curr_idx + 0] << 16) | m_cmdlist[curr_idx + 1], - (u32)(m_cmdlist[curr_idx + 2] << 16) | m_cmdlist[curr_idx + 3], - (u32)(m_cmdlist[curr_idx + 4] << 16) | m_cmdlist[curr_idx + 5], - (u32)(m_cmdlist[curr_idx + 6] << 16) | m_cmdlist[curr_idx + 7], - }; - curr_idx += 8; - OutputWMSamples(addresses); - break; - } - - case CMD_END: - end = true; - break; - } + PrepareBootUCode(_uMail); + return; } -} - -void CUCode_AXWii::SetupProcessing(u32 init_addr) -{ - // TODO: should be easily factorizable with AX - s16 init_data[60]; - - for (u32 i = 0; i < 60; ++i) - init_data[i] = HLEMemory_Read_U16(init_addr + 2 * i); - - // List of all buffers we have to initialize - struct { - int* ptr; - u32 samples; - } buffers[] = { - { m_samples_left, 32 }, - { m_samples_right, 32 }, - { m_samples_surround, 32 }, - { m_samples_auxA_left, 32 }, - { m_samples_auxA_right, 32 }, - { m_samples_auxA_surround, 32 }, - { m_samples_auxB_left, 32 }, - { m_samples_auxB_right, 32 }, - { m_samples_auxB_surround, 32 }, - { m_samples_auxC_left, 32 }, - { m_samples_auxC_right, 32 }, - { m_samples_auxC_surround, 32 }, - - { m_samples_wm0, 6 }, - { m_samples_aux0, 6 }, - { m_samples_wm1, 6 }, - { m_samples_aux1, 6 }, - { m_samples_wm2, 6 }, - { m_samples_aux2, 6 }, - { m_samples_wm3, 6 }, - { m_samples_aux3, 6 } - }; - - u32 init_idx = 0; - for (u32 i = 0; i < sizeof (buffers) / sizeof (buffers[0]); ++i) + else if ((_uMail & 0xFFFF0000) == MAIL_AX_ALIST) { - s32 init_val = (s32)((init_data[init_idx] << 16) | init_data[init_idx + 1]); - s16 delta = (s16)init_data[init_idx + 2]; - - init_idx += 3; - - if (!init_val) - memset(buffers[i].ptr, 0, 3 * buffers[i].samples * sizeof (int)); - else - { - for (u32 j = 0; j < 3 * buffers[i].samples; ++j) - { - buffers[i].ptr[j] = init_val; - init_val += delta; - } - } + // We are expected to get a new CmdBlock + DEBUG_LOG(DSPHLE, "GetNextCmdBlock (%ibytes)", (u16)_uMail); } -} - -AXMixControl CUCode_AXWii::ConvertMixerControl(u32 mixer_control) -{ - u32 ret = 0; - - if (mixer_control & 0x00000001) ret |= MIX_L; - if (mixer_control & 0x00000002) ret |= MIX_R; - if (mixer_control & 0x00000004) ret |= MIX_L_RAMP | MIX_R_RAMP; - if (mixer_control & 0x00000008) ret |= MIX_S; - if (mixer_control & 0x00000010) ret |= MIX_S_RAMP; - if (mixer_control & 0x00010000) ret |= MIX_AUXA_L; - if (mixer_control & 0x00020000) ret |= MIX_AUXA_R; - if (mixer_control & 0x00040000) ret |= MIX_AUXA_L_RAMP | MIX_AUXA_R_RAMP; - if (mixer_control & 0x00080000) ret |= MIX_AUXA_S; - if (mixer_control & 0x00100000) ret |= MIX_AUXA_S_RAMP; - if (mixer_control & 0x00200000) ret |= MIX_AUXB_L; - if (mixer_control & 0x00400000) ret |= MIX_AUXB_R; - if (mixer_control & 0x00800000) ret |= MIX_AUXB_L_RAMP | MIX_AUXB_R_RAMP; - if (mixer_control & 0x01000000) ret |= MIX_AUXB_S; - if (mixer_control & 0x02000000) ret |= MIX_AUXB_S_RAMP; - if (mixer_control & 0x04000000) ret |= MIX_AUXC_L; - if (mixer_control & 0x08000000) ret |= MIX_AUXC_R; - if (mixer_control & 0x10000000) ret |= MIX_AUXC_L_RAMP | MIX_AUXC_R_RAMP; - if (mixer_control & 0x20000000) ret |= MIX_AUXC_S; - if (mixer_control & 0x40000000) ret |= MIX_AUXC_S_RAMP; - - return (AXMixControl)ret; -} - -void CUCode_AXWii::ProcessPBList(u32 pb_addr) -{ - const u32 spms = 32; - - AXPBWii pb; - - while (pb_addr) + else switch(_uMail) { - AXBuffers buffers = {{ - m_samples_left, - m_samples_right, - m_samples_surround, - m_samples_auxA_left, - m_samples_auxA_right, - m_samples_auxA_surround, - m_samples_auxB_left, - m_samples_auxB_right, - m_samples_auxB_surround, - m_samples_auxC_left, - m_samples_auxC_right, - m_samples_auxC_surround - }}; - - if (!ReadPB(pb_addr, pb)) + case 0xCDD10000: // Action 0 - AX_ResumeTask() + m_rMailHandler.PushMail(DSP_RESUME); break; - for (int curr_ms = 0; curr_ms < 3; ++curr_ms) - { - Process1ms(pb, buffers, ConvertMixerControl(HILO_TO_32(pb.mixer_control))); + case 0xCDD10001: // Action 1 - new ucode upload + DEBUG_LOG(DSPHLE,"DSP IROM - New Ucode!"); + // TODO find a better way to protect from HLEMixer? + soundStream->GetMixer()->SetHLEReady(false); + m_UploadSetupInProgress = true; + break; - // Forward the buffers - for (u32 i = 0; i < sizeof (buffers.ptrs) / sizeof (buffers.ptrs[0]); ++i) - buffers.ptrs[i] += spms; - } + case 0xCDD10002: // Action 2 - IROM_Reset(); ( WII: De Blob, Cursed Mountain,...) + DEBUG_LOG(DSPHLE,"DSP IROM - Reset!"); + m_DSPHLE->SetUCode(UCODE_ROM); + return; - WritePB(pb_addr, pb); - pb_addr = HILO_TO_32(pb.next_pb); + case 0xCDD10003: // Action 3 - AX_GetNextCmdBlock() + break; + + default: + DEBUG_LOG(DSPHLE, " >>>> u32 MAIL : AXTask Mail (%08x)", _uMail); + AXTask(_uMail); + break; } } -void CUCode_AXWii::MixAUXSamples(int aux_id, u32 write_addr, u32 read_addr, u16 volume) +void CUCode_AXWii::MixAdd(short* _pBuffer, int _iSize) { - int* buffers[3] = { 0 }; - int* main_buffers[3] = { - m_samples_left, - m_samples_right, - m_samples_surround - }; + AXPBWii PB; - switch (aux_id) + if (_iSize > 1024 * 1024) + _iSize = 1024 * 1024; + + memset(templbuffer, 0, _iSize * sizeof(int)); + memset(temprbuffer, 0, _iSize * sizeof(int)); + + u32 blockAddr = m_addressPBs; + if (!blockAddr) + return; + + for (int i = 0; i < NUMBER_OF_PBS; i++) { - case 0: - buffers[0] = m_samples_auxA_left; - buffers[1] = m_samples_auxA_right; - buffers[2] = m_samples_auxA_surround; - break; + if (!ReadPB(blockAddr, PB)) + break; - case 1: - buffers[0] = m_samples_auxB_left; - buffers[1] = m_samples_auxB_right; - buffers[2] = m_samples_auxB_surround; - break; + if (wiisportsHack) + MixAddVoice(*(AXPBWiiSports*)&PB, templbuffer, temprbuffer, _iSize); + else + MixAddVoice(PB, templbuffer, temprbuffer, _iSize); - case 2: - buffers[0] = m_samples_auxC_left; - buffers[1] = m_samples_auxC_right; - buffers[2] = m_samples_auxC_surround; - break; - } + if (!WritePB(blockAddr, PB)) + break; + + // next PB, or done + blockAddr = (PB.next_pb_hi << 16) | PB.next_pb_lo; + if (!blockAddr) + break; + } - // Send the content of AUX buffers to the CPU - if (write_addr) + // We write the sound to _pBuffer + if (_pBuffer) { - int* ptr = (int*)HLEMemory_Get_Pointer(write_addr); - for (u32 i = 0; i < 3; ++i) - for (u32 j = 0; j < 3 * 32; ++j) - *ptr++ = Common::swap32(buffers[i][j]); - } - - // Then read the buffers from the CPU and add to our main buffers. - int* ptr = (int*)HLEMemory_Get_Pointer(read_addr); - for (u32 i = 0; i < 3; ++i) - for (u32 j = 0; j < 3 * 32; ++j) + for (int i = 0; i < _iSize; i++) { - s64 new_val = main_buffers[i][j] + Common::swap32(*ptr++); - main_buffers[i][j] = (new_val * volume) >> 15; - } -} - -void CUCode_AXWii::OutputSamples(u32 lr_addr, u32 surround_addr, u16 volume) -{ - int surround_buffer[3 * 32] = { 0 }; - - for (u32 i = 0; i < 3 * 32; ++i) - surround_buffer[i] = Common::swap32(m_samples_surround[i]); - memcpy(HLEMemory_Get_Pointer(surround_addr), surround_buffer, sizeof (surround_buffer)); - - short buffer[3 * 32 * 2]; - - // Clamp internal buffers to 16 bits. - for (u32 i = 0; i < 3 * 32; ++i) - { - int left = m_samples_left[i]; - int right = m_samples_right[i]; - - // Apply global volume. Cast to s64 to avoid overflow. - left = ((s64)left * volume) >> 15; - right = ((s64)right * volume) >> 15; - - if (left < -32767) left = -32767; - if (left > 32767) left = 32767; - if (right < -32767) right = -32767; - if (right > 32767) right = 32767; - - m_samples_left[i] = left; - m_samples_right[i] = right; - } - - for (u32 i = 0; i < 3 * 32; ++i) - { - buffer[2 * i] = Common::swap16(m_samples_left[i]); - buffer[2 * i + 1] = Common::swap16(m_samples_right[i]); - } - - memcpy(HLEMemory_Get_Pointer(lr_addr), buffer, sizeof (buffer)); -} - -void CUCode_AXWii::OutputWMSamples(u32* addresses) -{ - int* buffers[] = { - m_samples_wm0, - m_samples_wm1, - m_samples_wm2, - m_samples_wm3 - }; - - for (u32 i = 0; i < 4; ++i) - { - int* in = buffers[i]; - u16* out = (u16*)HLEMemory_Get_Pointer(addresses[i]); - for (u32 j = 0; j < 3 * 6; ++j) - { - int sample = in[j]; - if (sample < -32767) sample = -32767; - if (sample > 32767) sample = 32767; - out[j] = Common::swap16((u16)sample); + // 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; + else if (left > 32767) left = 32767; + if (right < -32767) right = -32767; + else if (right > 32767) right = 32767; + *_pBuffer++ = left; + *_pBuffer++ = right; } } } + +void CUCode_AXWii::Update(int cycles) +{ + if (NeedsResumeMail()) + { + m_rMailHandler.PushMail(DSP_RESUME); + DSP::GenerateDSPInterruptFromDSPEmu(DSP::INT_DSP); + } + // check if we have to send something + else if (!m_rMailHandler.IsEmpty()) + { + DSP::GenerateDSPInterruptFromDSPEmu(DSP::INT_DSP); + } +} + +// 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; + //u32 Addr__AXStudio; + //u32 Addr__AXOutSBuffer; + bool bExecuteList = true; + +/* + for (int i=0;i<64;i++) { + NOTICE_LOG(DSPHLE,"%x - %08x",uAddress+(i*4),HLEMemory_Read_U32(uAddress+(i*4))); + } +*/ + + while (bExecuteList) + { + u16 iCommand = HLEMemory_Read_U16(uAddress); + uAddress += 2; + //NOTICE_LOG(DSPHLE,"AXWII - AXLIST CMD %X",iCommand); + + switch (iCommand) + { + case 0x0000: + //Addr__AXStudio = HLEMemory_Read_U32(uAddress); + uAddress += 4; + break; + + case 0x0001: + uAddress += 4; + break; + + case 0x0003: + uAddress += 4; + break; + + case 0x0004: + // PBs are here now + m_addressPBs = HLEMemory_Read_U32(uAddress); + if (soundStream) + soundStream->GetMixer()->SetHLEReady(true); +// soundStream->Update(); + uAddress += 4; + break; + + case 0x0005: + if (!wiisportsHack) + uAddress += 10; + break; + + case 0x0006: + uAddress += 10; + break; + + case 0x0007: // AXLIST_SBUFFER + //Addr__AXOutSBuffer = HLEMemory_Read_U32(uAddress); + uAddress += 10; + break; + + case 0x0008: + uAddress += 26; + break; + + case 0x000a: + uAddress += wiisportsHack ? 4 : 8; // AXLIST_COMPRESSORTABLE + break; + + case 0x000b: + uAddress += wiisportsHack ? 2 : 10; + break; + + case 0x000c: + uAddress += wiisportsHack ? 8 : 10; + break; + + case 0x000d: + uAddress += 16; + break; + + case 0x000e: + if (wiisportsHack) + uAddress += 16; + else + bExecuteList = false; + break; + + case 0x000f: // only for Wii Sports uCode + bExecuteList = false; + break; + + default: + INFO_LOG(DSPHLE,"DSPHLE - AXwii - AXLIST - Unknown CMD: %x",iCommand); + // unknown command so stop the execution of this TaskList + bExecuteList = false; + break; + } + } + + m_rMailHandler.PushMail(DSP_YIELD); //its here in case there is a CMD fuckup + return true; +} + void CUCode_AXWii::DoState(PointerWrap &p) { - std::lock_guard lk(m_processing); + std::lock_guard lk(m_csMix); + + p.Do(m_addressPBs); + p.Do(wiisportsHack); DoStateShared(p); - DoAXState(p); - - p.Do(m_samples_auxC_left); - p.Do(m_samples_auxC_right); - p.Do(m_samples_auxC_surround); - - p.Do(m_samples_wm0); - p.Do(m_samples_wm1); - p.Do(m_samples_wm2); - p.Do(m_samples_wm3); - - p.Do(m_samples_aux0); - p.Do(m_samples_aux1); - p.Do(m_samples_aux2); - p.Do(m_samples_aux3); } diff --git a/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_AXWii.h b/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_AXWii.h index 3a66c30868..dc07e71a63 100644 --- a/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_AXWii.h +++ b/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_AXWii.h @@ -12,69 +12,44 @@ // A copy of the GPL 2.0 should have been included with the program. // If not, see http://www.gnu.org/licenses/ -// Official Git repository and contact information can be found at +// Official SVN repository and contact information can be found at // http://code.google.com/p/dolphin-emu/ -#ifndef _UCODE_AXWII_H -#define _UCODE_AXWII_H +#ifndef _UCODE_AXWII +#define _UCODE_AXWII -#include "UCode_AX.h" +#include "UCode_AXWii_Structs.h" -class CUCode_AXWii : public CUCode_AX +#define NUMBER_OF_PBS 128 + +class CUCode_AXWii : public IUCode { public: CUCode_AXWii(DSPHLE *dsp_hle, u32 _CRC); virtual ~CUCode_AXWii(); - virtual void DoState(PointerWrap &p); - -protected: - int m_samples_auxC_left[32 * 3]; - int m_samples_auxC_right[32 * 3]; - int m_samples_auxC_surround[32 * 3]; - - // Wiimote buffers - int m_samples_wm0[6 * 3]; - int m_samples_aux0[6 * 3]; - int m_samples_wm1[6 * 3]; - int m_samples_aux1[6 * 3]; - int m_samples_wm2[6 * 3]; - int m_samples_aux2[6 * 3]; - int m_samples_wm3[6 * 3]; - int m_samples_aux3[6 * 3]; - - // Convert a mixer_control bitfield to our internal representation for that - // value. Required because that bitfield has a different meaning in some - // versions of AX. - AXMixControl ConvertMixerControl(u32 mixer_control); - - virtual void HandleCommandList(); - - void SetupProcessing(u32 init_addr); - void ProcessPBList(u32 pb_addr); - void MixAUXSamples(int aux_id, u32 write_addr, u32 read_addr, u16 volume); - void OutputSamples(u32 lr_addr, u32 surround_addr, u16 volume); - void OutputWMSamples(u32* addresses); // 4 addresses + void HandleMail(u32 _uMail); + void MixAdd(short* _pBuffer, int _iSize); + void Update(int cycles); + void DoState(PointerWrap &p); private: - enum CmdType + enum { - CMD_SETUP = 0x00, - CMD_UNK_01 = 0x01, - CMD_UNK_02 = 0x02, - CMD_UNK_03 = 0x03, - CMD_PROCESS = 0x04, - CMD_MIX_AUXA = 0x05, - CMD_MIX_AUXB = 0x06, - CMD_MIX_AUXC = 0x07, - CMD_UNK_08 = 0x08, - CMD_UNK_09 = 0x09, - CMD_UNK_0A = 0x0A, - CMD_OUTPUT = 0x0B, - CMD_UNK_0C = 0x0C, - CMD_WM_OUTPUT = 0x0D, - CMD_END = 0x0E + MAIL_AX_ALIST = 0xBABE0000, }; + + // PBs + u32 m_addressPBs; + + bool wiisportsHack; + + int *templbuffer; + int *temprbuffer; + + // ax task message handler + bool AXTask(u32& _uMail); + void SendMail(u32 _uMail); }; #endif // _UCODE_AXWII diff --git a/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_AXWii_ADPCM.h b/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_AXWii_ADPCM.h new file mode 100644 index 0000000000..9130bb9da4 --- /dev/null +++ b/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_AXWii_ADPCM.h @@ -0,0 +1,92 @@ +// Copyright (C) 2003 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 "../../DSP.h" + +static inline s16 ADPCM_Step(PBADPCMInfo &adpcm, u32& samplePos, u32 newSamplePos, u16 frac) +{ + while (samplePos < newSamplePos) + { + if ((samplePos & 15) == 0) + { + adpcm.pred_scale = DSP::ReadARAM((samplePos & ~15) >> 1); + samplePos += 2; + newSamplePos += 2; + } + + int scale = 1 << (adpcm.pred_scale & 0xF); + int coef_idx = (adpcm.pred_scale >> 4) & 7; + + s32 coef1 = adpcm.coefs[coef_idx * 2 + 0]; + s32 coef2 = adpcm.coefs[coef_idx * 2 + 1]; + + int temp = (samplePos & 1) ? + (DSP::ReadARAM(samplePos >> 1) & 0xF) : + (DSP::ReadARAM(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; +} + +// TODO: WTF is going on here?!? +// Volume control (ramping) +static 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 + if (x >= 0x8000) x = 0x8000; // clamp to 32768; + return x; // update volume +} + +#endif // _UCODE_AX_ADPCM_H diff --git a/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_AXWii_Structs.h b/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_AXWii_Structs.h new file mode 100644 index 0000000000..7f082740de --- /dev/null +++ b/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_AXWii_Structs.h @@ -0,0 +1,365 @@ +// Copyright (C) 2003 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_STRUCTS_H +#define _UCODE_AX_STRUCTS_H + +struct PBMixer +{ + u16 left; + u16 left_delta; + u16 right; + u16 right_delta; + + u16 unknown3[8]; + u16 unknown4[6]; +}; + +struct PBMixerWii +{ + // volume mixing values in .15, 0x8000 = ca. 1.0 + u16 left; + u16 left_delta; + u16 right; + u16 right_delta; + + u16 auxA_left; + u16 auxA_left_delta; + u16 auxA_right; + u16 auxA_right_delta; + + u16 auxB_left; + u16 auxB_left_delta; + u16 auxB_right; + u16 auxB_right_delta; + + // Note: the following elements usage changes a little in DPL2 mode + // TODO: implement and comment it in the mixer + u16 auxC_left; + u16 auxC_left_delta; + u16 auxC_right; + u16 auxC_right_delta; + + u16 surround; + u16 surround_delta; + u16 auxA_surround; + u16 auxA_surround_delta; + u16 auxB_surround; + u16 auxB_surround_delta; + u16 auxC_surround; + u16 auxC_surround_delta; +}; + +struct PBMixerWM +{ + u16 main0; + u16 main0_delta; + u16 aux0; + u16 aux0_delta; + + u16 main1; + u16 main1_delta; + u16 aux1; + u16 aux1_delta; + + u16 main2; + u16 main2_delta; + u16 aux2; + u16 aux2_delta; + + u16 main3; + u16 main3_delta; + u16 aux3; + u16 aux3_delta; +}; + +struct PBInitialTimeDelay +{ + u16 on; + u16 addrMemHigh; + u16 addrMemLow; + u16 offsetLeft; + u16 offsetRight; + u16 targetLeft; + u16 targetRight; +}; + +// Update data - read these each 1ms subframe and use them! +// It seems that to provide higher time precisions for MIDI events, some games +// use this thing to update the parameter blocks per 1ms sub-block (a block is 5ms). +// Using this data should fix games that are missing MIDI notes. +struct PBUpdates +{ + u16 num_updates[5]; + u16 data_hi; // These point to main RAM. Not sure about the structure of the data. + u16 data_lo; +}; + +// The DSP stores the final sample values for each voice after every frame of processing. +// The values are then accumulated for all dropped voices, added to the next frame of audio, +// and ramped down on a per-sample basis to provide a gentle "roll off." +struct PBDpop +{ + s16 unknown[9]; +}; + +struct PBDpopWii +{ + s16 left; + s16 auxA_left; + s16 auxB_left; + s16 auxC_left; + + s16 right; + s16 auxA_right; + s16 auxB_right; + s16 auxC_right; + + s16 surround; + s16 auxA_surround; + s16 auxB_surround; + s16 auxC_surround; +}; + +struct PBDpopWM +{ + s16 aMain0; + s16 aMain1; + s16 aMain2; + s16 aMain3; + + s16 aAux0; + s16 aAux1; + s16 aAux2; + s16 aAux3; +}; + +struct PBVolumeEnvelope +{ + u16 cur_volume; // volume at start of frame + s16 cur_volume_delta; // signed per sample delta (96 samples per frame) +}; + +struct PBUnknown2 +{ + u16 unknown_reserved[3]; +}; + +struct PBAudioAddr +{ + u16 looping; + u16 sample_format; + u16 loop_addr_hi; // Start of loop (this will point to a shared "zero" buffer if one-shot mode is active) + u16 loop_addr_lo; + u16 end_addr_hi; // End of sample (and loop), inclusive + u16 end_addr_lo; + u16 cur_addr_hi; + u16 cur_addr_lo; +}; + +struct PBADPCMInfo +{ + s16 coefs[16]; + u16 gain; + u16 pred_scale; + s16 yn1; + s16 yn2; +}; + +struct PBSampleRateConverter +{ + // ratio = (f32)ratio * 0x10000; + // valid range is 1/512 to 4.0000 + u16 ratio_hi; // integer part of sampling ratio + u16 ratio_lo; // fraction part of sampling ratio + u16 cur_addr_frac; + u16 last_samples[4]; +}; + +struct PBSampleRateConverterWM +{ + u16 currentAddressFrac; + u16 last_samples[4]; +}; + +struct PBADPCMLoopInfo +{ + u16 pred_scale; + u16 yn1; + u16 yn2; +}; + +struct AXPB +{ + u16 next_pb_hi; + u16 next_pb_lo; + u16 this_pb_hi; + u16 this_pb_lo; + + u16 src_type; // Type of sample rate converter (none, ?, linear) + u16 coef_select; + u16 mixer_control; + + u16 running; // 1=RUN 0=STOP + u16 is_stream; // 1 = stream, 0 = one shot + + PBMixer mixer; + PBInitialTimeDelay initial_time_delay; + PBUpdates updates; + PBDpop dpop; + PBVolumeEnvelope vol_env; + PBUnknown2 unknown3; + PBAudioAddr audio_addr; + PBADPCMInfo adpcm; + PBSampleRateConverter src; + PBADPCMLoopInfo adpcm_loop_info; + u16 unknown_maybe_padding[3]; +}; + +struct PBLowPassFilter +{ + u16 enabled; + u16 yn1; + u16 a0; + u16 b0; +}; + +struct PBBiquadFilter +{ + + u16 on; // on = 2, off = 0 + u16 xn1; // History data + u16 xn2; + u16 yn1; + u16 yn2; + u16 b0; // Filter coefficients + u16 b1; + u16 b2; + u16 a1; + u16 a2; + +}; + +union PBInfImpulseResponseWM +{ + PBLowPassFilter lpf; + PBBiquadFilter biquad; +}; + +struct AXPBWii +{ + u16 next_pb_hi; + u16 next_pb_lo; + u16 this_pb_hi; + u16 this_pb_lo; + + u16 src_type; // Type of sample rate converter (none, 4-tap, linear) + u16 coef_select; // coef for the 4-tap src + u32 mixer_control; + + u16 running; // 1=RUN 0=STOP + u16 is_stream; // 1 = stream, 0 = one shot + + PBMixerWii mixer; + PBInitialTimeDelay initial_time_delay; + PBDpopWii dpop; + PBVolumeEnvelope vol_env; + PBAudioAddr audio_addr; + PBADPCMInfo adpcm; + PBSampleRateConverter src; + PBADPCMLoopInfo adpcm_loop_info; + PBLowPassFilter lpf; + PBBiquadFilter biquad; + + // WIIMOTE :D + u16 remote; + u16 remote_mixer_control; + + PBMixerWM remote_mixer; + PBDpopWM remote_dpop; + PBSampleRateConverterWM remote_src; + PBInfImpulseResponseWM remote_iir; + + u16 pad[12]; // align us, captain! (32B) +}; + +// Seems like nintendo used an early version of AXWii and forgot to remove the update functionality ;p +struct PBUpdatesWiiSports +{ + u16 num_updates[3]; + u16 data_hi; + u16 data_lo; +}; + +struct AXPBWiiSports +{ + u16 next_pb_hi; + u16 next_pb_lo; + u16 this_pb_hi; + u16 this_pb_lo; + + u16 src_type; // Type of sample rate converter (none, 4-tap, linear) + u16 coef_select; // coef for the 4-tap src + u32 mixer_control; + + u16 running; // 1=RUN 0=STOP + u16 is_stream; // 1 = stream, 0 = one shot + + PBMixerWii mixer; + PBInitialTimeDelay initial_time_delay; + PBUpdatesWiiSports updates; + PBDpopWii dpop; + PBVolumeEnvelope vol_env; + PBAudioAddr audio_addr; + PBADPCMInfo adpcm; + PBSampleRateConverter src; + PBADPCMLoopInfo adpcm_loop_info; + PBLowPassFilter lpf; + PBBiquadFilter biquad; + + // WIIMOTE :D + u16 remote; + u16 remote_mixer_control; + + PBMixerWM remote_mixer; + PBDpopWM remote_dpop; + PBSampleRateConverterWM remote_src; + PBInfImpulseResponseWM remote_iir; + + u16 pad[7]; // align us, captain! (32B) +}; + +// TODO: All these enums have changed a lot for wii +enum { + AUDIOFORMAT_ADPCM = 0, + AUDIOFORMAT_PCM8 = 0x19, + AUDIOFORMAT_PCM16 = 0xA, +}; + +enum { + SRCTYPE_LINEAR = 1, + SRCTYPE_NEAREST = 2, + MIXCONTROL_RAMPING = 8, +}; + +// Both may be used at once +enum { + FILTER_LOWPASS = 1, + FILTER_BIQUAD = 2, +}; + +#endif // _UCODE_AX_STRUCTS_H diff --git a/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_AXWii_Voice.h b/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_AXWii_Voice.h new file mode 100644 index 0000000000..55f7face27 --- /dev/null +++ b/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_AXWii_Voice.h @@ -0,0 +1,271 @@ +// Copyright (C) 2003 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_VOICE_H +#define _UCODE_AXWII_VOICE_H + +#include "UCodes.h" +#include "UCode_AXWii_ADPCM.h" +#include "UCode_AX.h" +#include "Mixer.h" +#include "../../AudioInterface.h" + +// MRAM -> ARAM for GC +inline bool ReadPB(u32 addr, AXPB &PB) +{ + const u16* PB_in_mram = (const u16*)Memory::GetPointer(addr); + if (PB_in_mram == NULL) + return false; + u16* PB_in_aram = (u16*)&PB; + + for (size_t p = 0; p < (sizeof(AXPB) >> 1); p++) + { + PB_in_aram[p] = Common::swap16(PB_in_mram[p]); + } + + return true; +} + +// MRAM -> ARAM for Wii +inline bool ReadPB(u32 addr, AXPBWii &PB) +{ + const u16* PB_in_mram = (const u16*)Memory::GetPointer(addr); + if (PB_in_mram == NULL) + return false; + u16* PB_in_aram = (u16*)&PB; + + // preswap the mixer_control + PB.mixer_control = ((u32)PB_in_mram[7] << 16) | ((u32)PB_in_mram[6] >> 16); + + for (size_t p = 0; p < (sizeof(AXPBWii) >> 1); p++) + { + PB_in_aram[p] = Common::swap16(PB_in_mram[p]); + } + + return true; +} + +// ARAM -> MRAM for GC +inline bool WritePB(u32 addr, AXPB &PB) +{ + const u16* PB_in_aram = (const u16*)&PB; + u16* PB_in_mram = (u16*)Memory::GetPointer(addr); + if (PB_in_mram == NULL) + return false; + + for (size_t p = 0; p < (sizeof(AXPB) >> 1); p++) + { + PB_in_mram[p] = Common::swap16(PB_in_aram[p]); + } + + return true; +} + +// ARAM -> MRAM for Wii +inline bool WritePB(u32 addr, AXPBWii &PB) +{ + const u16* PB_in_aram = (const u16*)&PB; + u16* PB_in_mram = (u16*)Memory::GetPointer(addr); + if (PB_in_mram == NULL) + return false; + + // preswap the mixer_control + *(u32*)&PB_in_mram[6] = (PB.mixer_control << 16) | (PB.mixer_control >> 16); + + for (size_t p = 0; p < (sizeof(AXPBWii) >> 1); p++) + { + PB_in_mram[p] = Common::swap16(PB_in_aram[p]); + } + + return true; +} + +////////////////////////////////////////////////////////////////////////// +// TODO: fix handling of gc/wii PB differences +// TODO: generally fix up the mess - looks crazy and kinda wrong +template +inline void MixAddVoice(ParamBlockType &pb, + int *templbuffer, int *temprbuffer, + int _iSize) +{ + if (pb.running) + { + const u32 ratio = (u32)(((pb.src.ratio_hi << 16) + pb.src.ratio_lo) + * /*ratioFactor:*/((float)AudioInterface::GetAIDSampleRate() / (float)soundStream->GetMixer()->GetSampleRate())); + 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; + + 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.adpcm_loop_info.pred_scale <= 0x7F + ) + { + 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; + u32 oldFrac = frac; + frac += ratio; + u32 newSamplePos = samplePos + (frac >> 16); //whole number of frac + + // ======================================================================================= + // Process sample format + switch (pb.audio_addr.sample_format) + { + case AUDIOFORMAT_PCM8: + pb.adpcm.yn2 = ((s8)DSP::ReadARAM(samplePos)) << 8; //current sample + pb.adpcm.yn1 = ((s8)DSP::ReadARAM(samplePos + 1)) << 8; //next sample + + if (pb.src_type == SRCTYPE_NEAREST) + sample = pb.adpcm.yn2; + else // linear interpolation + sample = (pb.adpcm.yn1 * (u16)oldFrac + pb.adpcm.yn2 * (u16)(0xFFFF - oldFrac) + pb.adpcm.yn2) >> 16; + + samplePos = newSamplePos; + break; + + case AUDIOFORMAT_PCM16: + pb.adpcm.yn2 = (s16)(u16)((DSP::ReadARAM(samplePos * 2) << 8) | (DSP::ReadARAM((samplePos * 2 + 1)))); //current sample + pb.adpcm.yn1 = (s16)(u16)((DSP::ReadARAM((samplePos + 1) * 2) << 8) | (DSP::ReadARAM(((samplePos + 1) * 2 + 1)))); //next sample + + if (pb.src_type == SRCTYPE_NEAREST) + sample = pb.adpcm.yn2; + else // linear interpolation + sample = (pb.adpcm.yn1 * (u16)oldFrac + pb.adpcm.yn2 * (u16)(0xFFFF - oldFrac) + pb.adpcm.yn2) >> 16; + + samplePos = newSamplePos; + break; + + case AUDIOFORMAT_ADPCM: + ADPCM_Step(pb.adpcm, samplePos, newSamplePos, frac); + + if (pb.src_type == SRCTYPE_NEAREST) + sample = pb.adpcm.yn2; + else // linear interpolation + sample = (pb.adpcm.yn1 * (u16)frac + pb.adpcm.yn2 * (u16)(0xFFFF - frac) + pb.adpcm.yn2) >> 16; //adpcm moves on 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.left >> 5; + int rightmix = pb.mixer.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) + { + if ((samplePos & ~0x1f) == (sampleEnd & ~0x1f) || (pb.audio_addr.sample_format != AUDIOFORMAT_ADPCM)) + samplePos = loopPos; + if ((!pb.is_stream) && (pb.audio_addr.sample_format == AUDIOFORMAT_ADPCM)) + { + 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; + samplePos = loopPos; + //samplePos = samplePos - sampleEnd + loopPos; + memset(&pb.dpop, 0, sizeof(pb.dpop)); + memset(pb.src.last_samples, 0, 8); + break; + } + } + } // end of the _iSize loop + + // Update volume + pb.mixer.left = ADPCM_Vol(pb.mixer.left, pb.mixer.left_delta); + pb.mixer.right = ADPCM_Vol(pb.mixer.right, pb.mixer.right_delta); + + 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) +} + +#endif diff --git a/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_AXStructs.h b/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_AX_Structs.h similarity index 100% rename from Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_AXStructs.h rename to Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_AX_Structs.h diff --git a/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_AX_Voice.h b/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_AX_Voice.h index 625b761b80..349dc7e03b 100644 --- a/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_AX_Voice.h +++ b/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_AX_Voice.h @@ -27,7 +27,7 @@ #endif #include "Common.h" -#include "UCode_AXStructs.h" +#include "UCode_AX_Structs.h" #include "../../DSP.h" #ifdef AX_GC diff --git a/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_NewAXWii.cpp b/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_NewAXWii.cpp new file mode 100644 index 0000000000..40ad6e1947 --- /dev/null +++ b/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_NewAXWii.cpp @@ -0,0 +1,383 @@ +// Copyright (C) 2003 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 "Mixer.h" + +#include "UCodes.h" +#include "UCode_AX_Structs.h" +#include "UCode_NewAXWii.h" + +#define AX_WII +#include "UCode_AX_Voice.h" + + +CUCode_NewAXWii::CUCode_NewAXWii(DSPHLE *dsp_hle, u32 l_CRC) + : CUCode_AX(dsp_hle, l_CRC) +{ + WARN_LOG(DSPHLE, "Instantiating CUCode_NewAXWii"); +} + +CUCode_NewAXWii::~CUCode_NewAXWii() +{ +} + +void CUCode_NewAXWii::HandleCommandList() +{ + // Temp variables for addresses computation + u16 addr_hi, addr_lo; + u16 addr2_hi, addr2_lo; + u16 volume; + +// WARN_LOG(DSPHLE, "Command list:"); +// for (u32 i = 0; m_cmdlist[i] != CMD_END; ++i) +// WARN_LOG(DSPHLE, "%04x", m_cmdlist[i]); +// WARN_LOG(DSPHLE, "-------------"); + + u32 curr_idx = 0; + bool end = false; + while (!end) + { + u16 cmd = m_cmdlist[curr_idx++]; + + switch (cmd) + { + // Some of these commands are unknown, or unused in this AX HLE. + // We still need to skip their arguments using "curr_idx += N". + + case CMD_SETUP: + addr_hi = m_cmdlist[curr_idx++]; + addr_lo = m_cmdlist[curr_idx++]; + SetupProcessing(HILO_TO_32(addr)); + break; + + case CMD_UNK_01: curr_idx += 2; break; + case CMD_UNK_02: curr_idx += 2; break; + case CMD_UNK_03: curr_idx += 2; break; + + case CMD_PROCESS: + addr_hi = m_cmdlist[curr_idx++]; + addr_lo = m_cmdlist[curr_idx++]; + ProcessPBList(HILO_TO_32(addr)); + break; + + case CMD_MIX_AUXA: + case CMD_MIX_AUXB: + case CMD_MIX_AUXC: + volume = m_cmdlist[curr_idx++]; + addr_hi = m_cmdlist[curr_idx++]; + addr_lo = m_cmdlist[curr_idx++]; + addr2_hi = m_cmdlist[curr_idx++]; + addr2_lo = m_cmdlist[curr_idx++]; + MixAUXSamples(cmd - CMD_MIX_AUXA, HILO_TO_32(addr), HILO_TO_32(addr2), volume); + break; + + // These two go together and manipulate some AUX buffers. + case CMD_UNK_08: curr_idx += 13; break; + case CMD_UNK_09: curr_idx += 13; break; + + case CMD_UNK_0A: curr_idx += 4; break; + + case CMD_OUTPUT: + volume = m_cmdlist[curr_idx++]; + addr_hi = m_cmdlist[curr_idx++]; + addr_lo = m_cmdlist[curr_idx++]; + addr2_hi = m_cmdlist[curr_idx++]; + addr2_lo = m_cmdlist[curr_idx++]; + OutputSamples(HILO_TO_32(addr2), HILO_TO_32(addr), volume); + break; + + case CMD_UNK_0C: curr_idx += 5; break; + + case CMD_WM_OUTPUT: + { + u32 addresses[4] = { + (u32)(m_cmdlist[curr_idx + 0] << 16) | m_cmdlist[curr_idx + 1], + (u32)(m_cmdlist[curr_idx + 2] << 16) | m_cmdlist[curr_idx + 3], + (u32)(m_cmdlist[curr_idx + 4] << 16) | m_cmdlist[curr_idx + 5], + (u32)(m_cmdlist[curr_idx + 6] << 16) | m_cmdlist[curr_idx + 7], + }; + curr_idx += 8; + OutputWMSamples(addresses); + break; + } + + case CMD_END: + end = true; + break; + } + } +} + +void CUCode_NewAXWii::SetupProcessing(u32 init_addr) +{ + // TODO: should be easily factorizable with AX + s16 init_data[60]; + + for (u32 i = 0; i < 60; ++i) + init_data[i] = HLEMemory_Read_U16(init_addr + 2 * i); + + // List of all buffers we have to initialize + struct { + int* ptr; + u32 samples; + } buffers[] = { + { m_samples_left, 32 }, + { m_samples_right, 32 }, + { m_samples_surround, 32 }, + { m_samples_auxA_left, 32 }, + { m_samples_auxA_right, 32 }, + { m_samples_auxA_surround, 32 }, + { m_samples_auxB_left, 32 }, + { m_samples_auxB_right, 32 }, + { m_samples_auxB_surround, 32 }, + { m_samples_auxC_left, 32 }, + { m_samples_auxC_right, 32 }, + { m_samples_auxC_surround, 32 }, + + { m_samples_wm0, 6 }, + { m_samples_aux0, 6 }, + { m_samples_wm1, 6 }, + { m_samples_aux1, 6 }, + { m_samples_wm2, 6 }, + { m_samples_aux2, 6 }, + { m_samples_wm3, 6 }, + { m_samples_aux3, 6 } + }; + + u32 init_idx = 0; + for (u32 i = 0; i < sizeof (buffers) / sizeof (buffers[0]); ++i) + { + s32 init_val = (s32)((init_data[init_idx] << 16) | init_data[init_idx + 1]); + s16 delta = (s16)init_data[init_idx + 2]; + + init_idx += 3; + + if (!init_val) + memset(buffers[i].ptr, 0, 3 * buffers[i].samples * sizeof (int)); + else + { + for (u32 j = 0; j < 3 * buffers[i].samples; ++j) + { + buffers[i].ptr[j] = init_val; + init_val += delta; + } + } + } +} + +AXMixControl CUCode_NewAXWii::ConvertMixerControl(u32 mixer_control) +{ + u32 ret = 0; + + if (mixer_control & 0x00000001) ret |= MIX_L; + if (mixer_control & 0x00000002) ret |= MIX_R; + if (mixer_control & 0x00000004) ret |= MIX_L_RAMP | MIX_R_RAMP; + if (mixer_control & 0x00000008) ret |= MIX_S; + if (mixer_control & 0x00000010) ret |= MIX_S_RAMP; + if (mixer_control & 0x00010000) ret |= MIX_AUXA_L; + if (mixer_control & 0x00020000) ret |= MIX_AUXA_R; + if (mixer_control & 0x00040000) ret |= MIX_AUXA_L_RAMP | MIX_AUXA_R_RAMP; + if (mixer_control & 0x00080000) ret |= MIX_AUXA_S; + if (mixer_control & 0x00100000) ret |= MIX_AUXA_S_RAMP; + if (mixer_control & 0x00200000) ret |= MIX_AUXB_L; + if (mixer_control & 0x00400000) ret |= MIX_AUXB_R; + if (mixer_control & 0x00800000) ret |= MIX_AUXB_L_RAMP | MIX_AUXB_R_RAMP; + if (mixer_control & 0x01000000) ret |= MIX_AUXB_S; + if (mixer_control & 0x02000000) ret |= MIX_AUXB_S_RAMP; + if (mixer_control & 0x04000000) ret |= MIX_AUXC_L; + if (mixer_control & 0x08000000) ret |= MIX_AUXC_R; + if (mixer_control & 0x10000000) ret |= MIX_AUXC_L_RAMP | MIX_AUXC_R_RAMP; + if (mixer_control & 0x20000000) ret |= MIX_AUXC_S; + if (mixer_control & 0x40000000) ret |= MIX_AUXC_S_RAMP; + + return (AXMixControl)ret; +} + +void CUCode_NewAXWii::ProcessPBList(u32 pb_addr) +{ + const u32 spms = 32; + + AXPBWii pb; + + while (pb_addr) + { + AXBuffers buffers = {{ + m_samples_left, + m_samples_right, + m_samples_surround, + m_samples_auxA_left, + m_samples_auxA_right, + m_samples_auxA_surround, + m_samples_auxB_left, + m_samples_auxB_right, + m_samples_auxB_surround, + m_samples_auxC_left, + m_samples_auxC_right, + m_samples_auxC_surround + }}; + + if (!ReadPB(pb_addr, pb)) + break; + + for (int curr_ms = 0; curr_ms < 3; ++curr_ms) + { + Process1ms(pb, buffers, ConvertMixerControl(HILO_TO_32(pb.mixer_control))); + + // Forward the buffers + for (u32 i = 0; i < sizeof (buffers.ptrs) / sizeof (buffers.ptrs[0]); ++i) + buffers.ptrs[i] += spms; + } + + WritePB(pb_addr, pb); + pb_addr = HILO_TO_32(pb.next_pb); + } +} + +void CUCode_NewAXWii::MixAUXSamples(int aux_id, u32 write_addr, u32 read_addr, u16 volume) +{ + int* buffers[3] = { 0 }; + int* main_buffers[3] = { + m_samples_left, + m_samples_right, + m_samples_surround + }; + + switch (aux_id) + { + case 0: + buffers[0] = m_samples_auxA_left; + buffers[1] = m_samples_auxA_right; + buffers[2] = m_samples_auxA_surround; + break; + + case 1: + buffers[0] = m_samples_auxB_left; + buffers[1] = m_samples_auxB_right; + buffers[2] = m_samples_auxB_surround; + break; + + case 2: + buffers[0] = m_samples_auxC_left; + buffers[1] = m_samples_auxC_right; + buffers[2] = m_samples_auxC_surround; + break; + } + + // Send the content of AUX buffers to the CPU + if (write_addr) + { + int* ptr = (int*)HLEMemory_Get_Pointer(write_addr); + for (u32 i = 0; i < 3; ++i) + for (u32 j = 0; j < 3 * 32; ++j) + *ptr++ = Common::swap32(buffers[i][j]); + } + + // Then read the buffers from the CPU and add to our main buffers. + int* ptr = (int*)HLEMemory_Get_Pointer(read_addr); + for (u32 i = 0; i < 3; ++i) + for (u32 j = 0; j < 3 * 32; ++j) + { + s64 new_val = main_buffers[i][j] + Common::swap32(*ptr++); + main_buffers[i][j] = (new_val * volume) >> 15; + } +} + +void CUCode_NewAXWii::OutputSamples(u32 lr_addr, u32 surround_addr, u16 volume) +{ + int surround_buffer[3 * 32] = { 0 }; + + for (u32 i = 0; i < 3 * 32; ++i) + surround_buffer[i] = Common::swap32(m_samples_surround[i]); + memcpy(HLEMemory_Get_Pointer(surround_addr), surround_buffer, sizeof (surround_buffer)); + + short buffer[3 * 32 * 2]; + + // Clamp internal buffers to 16 bits. + for (u32 i = 0; i < 3 * 32; ++i) + { + int left = m_samples_left[i]; + int right = m_samples_right[i]; + + // Apply global volume. Cast to s64 to avoid overflow. + left = ((s64)left * volume) >> 15; + right = ((s64)right * volume) >> 15; + + if (left < -32767) left = -32767; + if (left > 32767) left = 32767; + if (right < -32767) right = -32767; + if (right > 32767) right = 32767; + + m_samples_left[i] = left; + m_samples_right[i] = right; + } + + for (u32 i = 0; i < 3 * 32; ++i) + { + buffer[2 * i] = Common::swap16(m_samples_left[i]); + buffer[2 * i + 1] = Common::swap16(m_samples_right[i]); + } + + memcpy(HLEMemory_Get_Pointer(lr_addr), buffer, sizeof (buffer)); +} + +void CUCode_NewAXWii::OutputWMSamples(u32* addresses) +{ + int* buffers[] = { + m_samples_wm0, + m_samples_wm1, + m_samples_wm2, + m_samples_wm3 + }; + + for (u32 i = 0; i < 4; ++i) + { + int* in = buffers[i]; + u16* out = (u16*)HLEMemory_Get_Pointer(addresses[i]); + for (u32 j = 0; j < 3 * 6; ++j) + { + int sample = in[j]; + if (sample < -32767) sample = -32767; + if (sample > 32767) sample = 32767; + out[j] = Common::swap16((u16)sample); + } + } +} + +void CUCode_NewAXWii::DoState(PointerWrap &p) +{ + std::lock_guard lk(m_processing); + + DoStateShared(p); + DoAXState(p); + + p.Do(m_samples_auxC_left); + p.Do(m_samples_auxC_right); + p.Do(m_samples_auxC_surround); + + p.Do(m_samples_wm0); + p.Do(m_samples_wm1); + p.Do(m_samples_wm2); + p.Do(m_samples_wm3); + + p.Do(m_samples_aux0); + p.Do(m_samples_aux1); + p.Do(m_samples_aux2); + p.Do(m_samples_aux3); +} diff --git a/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_NewAXWii.h b/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_NewAXWii.h new file mode 100644 index 0000000000..4c9bc5757c --- /dev/null +++ b/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_NewAXWii.h @@ -0,0 +1,80 @@ +// Copyright (C) 2003 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 Git repository and contact information can be found at +// http://code.google.com/p/dolphin-emu/ + +#ifndef _UCODE_NEWAXWII_H +#define _UCODE_NEWAXWII_H + +#include "UCode_AX.h" + +class CUCode_NewAXWii : public CUCode_AX +{ +public: + CUCode_NewAXWii(DSPHLE *dsp_hle, u32 _CRC); + virtual ~CUCode_NewAXWii(); + + virtual void DoState(PointerWrap &p); + +protected: + int m_samples_auxC_left[32 * 3]; + int m_samples_auxC_right[32 * 3]; + int m_samples_auxC_surround[32 * 3]; + + // Wiimote buffers + int m_samples_wm0[6 * 3]; + int m_samples_aux0[6 * 3]; + int m_samples_wm1[6 * 3]; + int m_samples_aux1[6 * 3]; + int m_samples_wm2[6 * 3]; + int m_samples_aux2[6 * 3]; + int m_samples_wm3[6 * 3]; + int m_samples_aux3[6 * 3]; + + // Convert a mixer_control bitfield to our internal representation for that + // value. Required because that bitfield has a different meaning in some + // versions of AX. + AXMixControl ConvertMixerControl(u32 mixer_control); + + virtual void HandleCommandList(); + + void SetupProcessing(u32 init_addr); + void ProcessPBList(u32 pb_addr); + void MixAUXSamples(int aux_id, u32 write_addr, u32 read_addr, u16 volume); + void OutputSamples(u32 lr_addr, u32 surround_addr, u16 volume); + void OutputWMSamples(u32* addresses); // 4 addresses + +private: + enum CmdType + { + CMD_SETUP = 0x00, + CMD_UNK_01 = 0x01, + CMD_UNK_02 = 0x02, + CMD_UNK_03 = 0x03, + CMD_PROCESS = 0x04, + CMD_MIX_AUXA = 0x05, + CMD_MIX_AUXB = 0x06, + CMD_MIX_AUXC = 0x07, + CMD_UNK_08 = 0x08, + CMD_UNK_09 = 0x09, + CMD_UNK_0A = 0x0A, + CMD_OUTPUT = 0x0B, + CMD_UNK_0C = 0x0C, + CMD_WM_OUTPUT = 0x0D, + CMD_END = 0x0E + }; +}; + +#endif // _UCODE_AXWII diff --git a/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCodes.cpp b/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCodes.cpp index c04bf41403..86773ad020 100644 --- a/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCodes.cpp +++ b/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCodes.cpp @@ -19,6 +19,7 @@ #include "UCode_AX.h" #include "UCode_AXWii.h" +#include "UCode_NewAXWii.h" #include "UCode_Zelda.h" #include "UCode_ROM.h" #include "UCode_CARD.h" @@ -26,6 +27,12 @@ #include "UCode_GBA.h" #include "Hash.h" +#if 0 +# define AXWII CUCode_NewAXWii +#else +# define AXWII CUCode_AXWii +#endif + IUCode* UCodeFactory(u32 _CRC, DSPHLE *dsp_hle, bool bWii) { switch (_CRC) @@ -90,13 +97,13 @@ IUCode* UCodeFactory(u32 _CRC, DSPHLE *dsp_hle, bool bWii) case 0x4cc52064: // Bleach: Versus Crusade case 0xd9c4bf34: // WiiMenu INFO_LOG(DSPHLE, "CRC %08x: Wii - AXWii chosen", _CRC); - return new CUCode_AXWii(dsp_hle, _CRC); + return new AXWII(dsp_hle, _CRC); default: if (bWii) { PanicAlert("DSPHLE: Unknown ucode (CRC = %08x) - forcing AXWii.\n\nTry LLE emulator if this is homebrew.", _CRC); - return new CUCode_AXWii(dsp_hle, _CRC); + return new AXWII(dsp_hle, _CRC); } else {