From 5c3bcc8f5ff4fbf7d1b10fd669e83296a98dab28 Mon Sep 17 00:00:00 2001 From: Pierre Bourdon Date: Sun, 20 Jan 2013 07:08:15 +0100 Subject: [PATCH 01/28] Revert "Readd the old AXWii (rename the new version to NewAXWii) and set it as default" Put back the new AX Wii as the default in this development branch. --- Source/Core/Core/CMakeLists.txt | 3 +- Source/Core/Core/Core.vcxproj | 6 +- Source/Core/Core/Core.vcxproj.filters | 14 +- .../Core/Src/HW/DSPHLE/UCodes/UCode_AX.cpp | 4 - .../Core/Core/Src/HW/DSPHLE/UCodes/UCode_AX.h | 2 +- .../{UCode_AX_Structs.h => UCode_AXStructs.h} | 0 .../Core/Src/HW/DSPHLE/UCodes/UCode_AXWii.cpp | 478 +++++++++++------- .../Core/Src/HW/DSPHLE/UCodes/UCode_AXWii.h | 75 ++- .../Src/HW/DSPHLE/UCodes/UCode_AXWii_ADPCM.h | 92 ---- .../HW/DSPHLE/UCodes/UCode_AXWii_Structs.h | 365 ------------- .../Src/HW/DSPHLE/UCodes/UCode_AXWii_Voice.h | 271 ---------- .../Src/HW/DSPHLE/UCodes/UCode_AX_Voice.h | 2 +- .../Src/HW/DSPHLE/UCodes/UCode_NewAXWii.cpp | 383 -------------- .../Src/HW/DSPHLE/UCodes/UCode_NewAXWii.h | 80 --- .../Core/Core/Src/HW/DSPHLE/UCodes/UCodes.cpp | 11 +- 15 files changed, 354 insertions(+), 1432 deletions(-) rename Source/Core/Core/Src/HW/DSPHLE/UCodes/{UCode_AX_Structs.h => UCode_AXStructs.h} (100%) delete mode 100644 Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_AXWii_ADPCM.h delete mode 100644 Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_AXWii_Structs.h delete mode 100644 Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_AXWii_Voice.h delete mode 100644 Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_NewAXWii.cpp delete mode 100644 Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_NewAXWii.h diff --git a/Source/Core/Core/CMakeLists.txt b/Source/Core/Core/CMakeLists.txt index 6ffeeaec3a..79ad6b60cb 100644 --- a/Source/Core/Core/CMakeLists.txt +++ b/Source/Core/Core/CMakeLists.txt @@ -70,8 +70,7 @@ 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_NewAXWii.cpp + Src/HW/DSPHLE/UCodes/UCode_AXWii.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 bd3d97f11b..ddd8b0dfd8 100644 --- a/Source/Core/Core/Core.vcxproj +++ b/Source/Core/Core/Core.vcxproj @@ -261,7 +261,6 @@ - @@ -464,11 +463,8 @@ - + - - - diff --git a/Source/Core/Core/Core.vcxproj.filters b/Source/Core/Core/Core.vcxproj.filters index 246dafd972..a9de67dbb5 100644 --- a/Source/Core/Core/Core.vcxproj.filters +++ b/Source/Core/Core/Core.vcxproj.filters @@ -197,9 +197,6 @@ 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 @@ -736,21 +733,12 @@ 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 81ef7c6482..57032a774e 100644 --- a/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_AX.cpp +++ b/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_AX.cpp @@ -200,11 +200,7 @@ 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 8fd2a2af5f..7158c197e8 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_AX_Structs.h" +#include "UCode_AXStructs.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_AX_Structs.h b/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_AXStructs.h similarity index 100% rename from Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_AX_Structs.h rename to Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_AXStructs.h 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 f4effbba6d..8c74c7a1e7 100644 --- a/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_AXWii.cpp +++ b/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_AXWii.cpp @@ -21,247 +21,363 @@ #include "Mixer.h" #include "UCodes.h" -#include "UCode_AXWii_Structs.h" -#include "UCode_AX.h" // for some functions in CUCode_AX +#include "UCode_AXStructs.h" #include "UCode_AXWii.h" -#include "UCode_AXWii_Voice.h" + +#define AX_WII +#include "UCode_AX_Voice.h" CUCode_AXWii::CUCode_AXWii(DSPHLE *dsp_hle, u32 l_CRC) - : IUCode(dsp_hle, l_CRC) - , m_addressPBs(0xFFFFFFFF) + : CUCode_AX(dsp_hle, l_CRC) { - // we got loaded - m_rMailHandler.PushMail(DSP_INIT); - - templbuffer = new int[1024 * 1024]; - temprbuffer = new int[1024 * 1024]; - - wiisportsHack = m_CRC == 0xfa450138; + WARN_LOG(DSPHLE, "Instantiating CUCode_AXWii"); } CUCode_AXWii::~CUCode_AXWii() { - m_rMailHandler.Clear(); - delete [] templbuffer; - delete [] temprbuffer; } -void CUCode_AXWii::HandleMail(u32 _uMail) +void CUCode_AXWii::HandleCommandList() { - if (m_UploadSetupInProgress) + // 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) { - PrepareBootUCode(_uMail); - return; - } - else if ((_uMail & 0xFFFF0000) == MAIL_AX_ALIST) - { - // We are expected to get a new CmdBlock - DEBUG_LOG(DSPHLE, "GetNextCmdBlock (%ibytes)", (u16)_uMail); - } - else switch(_uMail) - { - case 0xCDD10000: // Action 0 - AX_ResumeTask() - m_rMailHandler.PushMail(DSP_RESUME); - break; + u16 cmd = m_cmdlist[curr_idx++]; - 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; + 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 0xCDD10002: // Action 2 - IROM_Reset(); ( WII: De Blob, Cursed Mountain,...) - DEBUG_LOG(DSPHLE,"DSP IROM - Reset!"); - m_DSPHLE->SetUCode(UCODE_ROM); - return; + case CMD_SETUP: + addr_hi = m_cmdlist[curr_idx++]; + addr_lo = m_cmdlist[curr_idx++]; + SetupProcessing(HILO_TO_32(addr)); + break; - case 0xCDD10003: // Action 3 - AX_GetNextCmdBlock() - 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; - default: - DEBUG_LOG(DSPHLE, " >>>> u32 MAIL : AXTask Mail (%08x)", _uMail); - AXTask(_uMail); - 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_AXWii::MixAdd(short* _pBuffer, int _iSize) +void CUCode_AXWii::SetupProcessing(u32 init_addr) { - AXPBWii PB; + // TODO: should be easily factorizable with AX + s16 init_data[60]; - if (_iSize > 1024 * 1024) - _iSize = 1024 * 1024; + for (u32 i = 0; i < 60; ++i) + init_data[i] = HLEMemory_Read_U16(init_addr + 2 * i); - memset(templbuffer, 0, _iSize * sizeof(int)); - memset(temprbuffer, 0, _iSize * sizeof(int)); + // 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 }, - u32 blockAddr = m_addressPBs; - if (!blockAddr) - return; + { 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 } + }; - for (int i = 0; i < NUMBER_OF_PBS; i++) + u32 init_idx = 0; + for (u32 i = 0; i < sizeof (buffers) / sizeof (buffers[0]); ++i) { - if (!ReadPB(blockAddr, PB)) - break; + s32 init_val = (s32)((init_data[init_idx] << 16) | init_data[init_idx + 1]); + s16 delta = (s16)init_data[init_idx + 2]; - if (wiisportsHack) - MixAddVoice(*(AXPBWiiSports*)&PB, templbuffer, temprbuffer, _iSize); + init_idx += 3; + + if (!init_val) + memset(buffers[i].ptr, 0, 3 * buffers[i].samples * sizeof (int)); else - MixAddVoice(PB, templbuffer, temprbuffer, _iSize); - - if (!WritePB(blockAddr, PB)) - break; - - // next PB, or done - blockAddr = (PB.next_pb_hi << 16) | PB.next_pb_lo; - if (!blockAddr) - break; - } - - // We write the sound to _pBuffer - if (_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; - else if (left > 32767) left = 32767; - if (right < -32767) right = -32767; - else if (right > 32767) right = 32767; - *_pBuffer++ = left; - *_pBuffer++ = right; + for (u32 j = 0; j < 3 * buffers[i].samples; ++j) + { + buffers[i].ptr[j] = init_val; + init_val += delta; + } } } } - -void CUCode_AXWii::Update(int cycles) +AXMixControl CUCode_AXWii::ConvertMixerControl(u32 mixer_control) { - if (NeedsResumeMail()) + 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) { - 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); + 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); } } -// 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) +void CUCode_AXWii::MixAUXSamples(int aux_id, u32 write_addr, u32 read_addr, u16 volume) { - u32 uAddress = _uMail; - //u32 Addr__AXStudio; - //u32 Addr__AXOutSBuffer; - bool bExecuteList = true; + int* buffers[3] = { 0 }; + int* main_buffers[3] = { + m_samples_left, + m_samples_right, + m_samples_surround + }; -/* - for (int i=0;i<64;i++) { - NOTICE_LOG(DSPHLE,"%x - %08x",uAddress+(i*4),HLEMemory_Read_U32(uAddress+(i*4))); - } -*/ - - while (bExecuteList) + switch (aux_id) { - u16 iCommand = HLEMemory_Read_U16(uAddress); - uAddress += 2; - //NOTICE_LOG(DSPHLE,"AXWII - AXLIST CMD %X",iCommand); + case 0: + buffers[0] = m_samples_auxA_left; + buffers[1] = m_samples_auxA_right; + buffers[2] = m_samples_auxA_surround; + break; - switch (iCommand) + 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) { - case 0x0000: - //Addr__AXStudio = HLEMemory_Read_U32(uAddress); - uAddress += 4; - break; + s64 new_val = main_buffers[i][j] + Common::swap32(*ptr++); + main_buffers[i][j] = (new_val * volume) >> 15; + } +} - case 0x0001: - uAddress += 4; - break; +void CUCode_AXWii::OutputSamples(u32 lr_addr, u32 surround_addr, u16 volume) +{ + int surround_buffer[3 * 32] = { 0 }; - case 0x0003: - uAddress += 4; - break; + 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)); - case 0x0004: - // PBs are here now - m_addressPBs = HLEMemory_Read_U32(uAddress); - if (soundStream) - soundStream->GetMixer()->SetHLEReady(true); -// soundStream->Update(); - uAddress += 4; - break; + short buffer[3 * 32 * 2]; - case 0x0005: - if (!wiisportsHack) - uAddress += 10; - break; + // 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]; - case 0x0006: - uAddress += 10; - break; + // Apply global volume. Cast to s64 to avoid overflow. + left = ((s64)left * volume) >> 15; + right = ((s64)right * volume) >> 15; - case 0x0007: // AXLIST_SBUFFER - //Addr__AXOutSBuffer = HLEMemory_Read_U32(uAddress); - uAddress += 10; - break; + if (left < -32767) left = -32767; + if (left > 32767) left = 32767; + if (right < -32767) right = -32767; + if (right > 32767) right = 32767; - case 0x0008: - uAddress += 26; - break; + m_samples_left[i] = left; + m_samples_right[i] = right; + } - case 0x000a: - uAddress += wiisportsHack ? 4 : 8; // AXLIST_COMPRESSORTABLE - break; + 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]); + } - case 0x000b: - uAddress += wiisportsHack ? 2 : 10; - break; + memcpy(HLEMemory_Get_Pointer(lr_addr), buffer, sizeof (buffer)); +} - case 0x000c: - uAddress += wiisportsHack ? 8 : 10; - break; +void CUCode_AXWii::OutputWMSamples(u32* addresses) +{ + int* buffers[] = { + m_samples_wm0, + m_samples_wm1, + m_samples_wm2, + m_samples_wm3 + }; - 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; + 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); } } - - 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_csMix); - - p.Do(m_addressPBs); - p.Do(wiisportsHack); + 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_AXWii.h b/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_AXWii.h index dc07e71a63..3a66c30868 100644 --- a/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_AXWii.h +++ b/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_AXWii.h @@ -12,44 +12,69 @@ // 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 +// Official Git repository and contact information can be found at // http://code.google.com/p/dolphin-emu/ -#ifndef _UCODE_AXWII -#define _UCODE_AXWII +#ifndef _UCODE_AXWII_H +#define _UCODE_AXWII_H -#include "UCode_AXWii_Structs.h" +#include "UCode_AX.h" -#define NUMBER_OF_PBS 128 - -class CUCode_AXWii : public IUCode +class CUCode_AXWii : public CUCode_AX { public: CUCode_AXWii(DSPHLE *dsp_hle, u32 _CRC); virtual ~CUCode_AXWii(); - void HandleMail(u32 _uMail); - void MixAdd(short* _pBuffer, int _iSize); - void Update(int cycles); - void DoState(PointerWrap &p); + 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 + enum CmdType { - MAIL_AX_ALIST = 0xBABE0000, + 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 }; - - // 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 deleted file mode 100644 index 9130bb9da4..0000000000 --- a/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_AXWii_ADPCM.h +++ /dev/null @@ -1,92 +0,0 @@ -// 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 deleted file mode 100644 index 7f082740de..0000000000 --- a/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_AXWii_Structs.h +++ /dev/null @@ -1,365 +0,0 @@ -// 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 deleted file mode 100644 index 55f7face27..0000000000 --- a/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_AXWii_Voice.h +++ /dev/null @@ -1,271 +0,0 @@ -// 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_AX_Voice.h b/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_AX_Voice.h index e39b38b100..15261b6184 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_AX_Structs.h" +#include "UCode_AXStructs.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 deleted file mode 100644 index 40ad6e1947..0000000000 --- a/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_NewAXWii.cpp +++ /dev/null @@ -1,383 +0,0 @@ -// 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 deleted file mode 100644 index 4c9bc5757c..0000000000 --- a/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_NewAXWii.h +++ /dev/null @@ -1,80 +0,0 @@ -// 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 86773ad020..c04bf41403 100644 --- a/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCodes.cpp +++ b/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCodes.cpp @@ -19,7 +19,6 @@ #include "UCode_AX.h" #include "UCode_AXWii.h" -#include "UCode_NewAXWii.h" #include "UCode_Zelda.h" #include "UCode_ROM.h" #include "UCode_CARD.h" @@ -27,12 +26,6 @@ #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) @@ -97,13 +90,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 AXWII(dsp_hle, _CRC); + return new CUCode_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 AXWII(dsp_hle, _CRC); + return new CUCode_AXWii(dsp_hle, _CRC); } else { From fb64cfd18ad855abb03896a14d52bb832db082b5 Mon Sep 17 00:00:00 2001 From: Pierre Bourdon Date: Sun, 20 Jan 2013 07:36:48 +0100 Subject: [PATCH 02/28] AXWii: fix L/R channels being swapped when outputting audio samples --- Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_AXWii.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) 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..8aec99d700 100644 --- a/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_AXWii.cpp +++ b/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_AXWii.cpp @@ -330,8 +330,8 @@ void CUCode_AXWii::OutputSamples(u32 lr_addr, u32 surround_addr, u16 volume) 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]); + buffer[2 * i] = Common::swap16(m_samples_right[i]); + buffer[2 * i + 1] = Common::swap16(m_samples_left[i]); } memcpy(HLEMemory_Get_Pointer(lr_addr), buffer, sizeof (buffer)); From 35249a8576c604ad7528c254c7b27e356f1a127b Mon Sep 17 00:00:00 2001 From: Pierre Bourdon Date: Sun, 20 Jan 2013 14:42:37 +0100 Subject: [PATCH 03/28] Handle 3ms at a time in AXWii instead of 3x1ms --- .../Core/Src/HW/DSPHLE/UCodes/UCode_AX.cpp | 2 +- .../Core/Src/HW/DSPHLE/UCodes/UCode_AXWii.cpp | 11 +------- .../Src/HW/DSPHLE/UCodes/UCode_AX_Voice.h | 26 ++++++++++++------- 3 files changed, 18 insertions(+), 21 deletions(-) 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..cb34c2532d 100644 --- a/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_AX.cpp +++ b/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_AX.cpp @@ -405,7 +405,7 @@ void CUCode_AX::ProcessPBList(u32 pb_addr) { ApplyUpdatesForMs(pb, curr_ms); - Process1ms(pb, buffers, ConvertMixerControl(pb.mixer_control)); + ProcessVoice(pb, buffers, ConvertMixerControl(pb.mixer_control)); // Forward the buffers for (u32 i = 0; i < sizeof (buffers.ptrs) / sizeof (buffers.ptrs[0]); ++i) 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 8aec99d700..1b1097c7fe 100644 --- a/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_AXWii.cpp +++ b/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_AXWii.cpp @@ -212,8 +212,6 @@ AXMixControl CUCode_AXWii::ConvertMixerControl(u32 mixer_control) void CUCode_AXWii::ProcessPBList(u32 pb_addr) { - const u32 spms = 32; - AXPBWii pb; while (pb_addr) @@ -236,14 +234,7 @@ void CUCode_AXWii::ProcessPBList(u32 pb_addr) 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; - } + ProcessVoice(pb, buffers, ConvertMixerControl(HILO_TO_32(pb.mixer_control))); WritePB(pb_addr, pb); pb_addr = HILO_TO_32(pb.next_pb); 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 15261b6184..578330ba34 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 @@ -32,8 +32,12 @@ #ifdef AX_GC # define PB_TYPE AXPB +# define MS_PER_FRAME 1 +# define SAMPLES_PER_FRAME 32 #else # define PB_TYPE AXPBWii +# define MS_PER_FRAME 3 +# define SAMPLES_PER_FRAME 96 #endif // Put all of that in an anonymous namespace to avoid stupid compilers merging @@ -232,7 +236,8 @@ u16 AcceleratorGetSample() return ret; } -// Read 32 input samples from ARAM, decoding and converting rate if required. +// Read SAMPLES_PER_FRAME input samples from ARAM, decoding and converting rate +// if required. void GetInputSamples(PB_TYPE& pb, s16* samples) { u32 cur_addr = HILO_TO_32(pb.audio_addr.cur_addr); @@ -260,7 +265,7 @@ void GetInputSamples(PB_TYPE& pb, s16* samples) s16 curr0 = pb.src.last_samples[2]; s16 curr1 = pb.src.last_samples[3]; - for (u32 i = 0; i < 32; ++i) + for (u32 i = 0; i < SAMPLES_PER_FRAME; ++i) { // Get our current fractional position, used to know how much of // curr0 and how much of curr1 the output sample should be. @@ -290,12 +295,12 @@ void GetInputSamples(PB_TYPE& pb, s16* samples) } else // SRCTYPE_NEAREST { - // No sample rate conversion here: simply read 32 samples from the + // No sample rate conversion here: simply read samples from the // accelerator to the output buffer. - for (u32 i = 0; i < 32; ++i) + for (u32 i = 0; i < SAMPLES_PER_FRAME; ++i) samples[i] = AcceleratorGetSample(); - memcpy(pb.src.last_samples, samples + 28, 4 * sizeof (u16)); + memcpy(pb.src.last_samples, samples + SAMPLES_PER_FRAME - 4, 4 * sizeof (u16)); } // Update current position in the PB. @@ -315,7 +320,7 @@ void MixAdd(int* out, const s16* input, u16* pvol, s16* dpop, bool ramp) if (!ramp) volume_delta = 0; - for (u32 i = 0; i < 32; ++i) + for (u32 i = 0; i < SAMPLES_PER_FRAME; ++i) { s64 sample = input[i]; sample *= volume; @@ -328,19 +333,20 @@ void MixAdd(int* out, const s16* input, u16* pvol, s16* dpop, bool ramp) } } -// Process 1ms of audio (32 samples) from a PB and mix it to the buffers. -void Process1ms(PB_TYPE& pb, const AXBuffers& buffers, AXMixControl mctrl) +// Process 1ms of audio (for AX GC) or 3ms of audio (for AX Wii) from a PB and +// mix it to the output buffers. +void ProcessVoice(PB_TYPE& pb, const AXBuffers& buffers, AXMixControl mctrl) { // If the voice is not running, nothing to do. if (!pb.running) return; // Read input samples, performing sample rate conversion if needed. - s16 samples[32]; + s16 samples[SAMPLES_PER_FRAME]; GetInputSamples(pb, samples); // Apply a global volume ramp using the volume envelope parameters. - for (u32 i = 0; i < 32; ++i) + for (u32 i = 0; i < SAMPLES_PER_FRAME; ++i) { s64 sample = 2 * (s16)samples[i] * (s16)pb.vol_env.cur_volume; samples[i] = (s16)(sample >> 16); From 25419ed827b48027bfb2cc66bef8c7d9b142b2ae Mon Sep 17 00:00:00 2001 From: Pierre Bourdon Date: Sun, 20 Jan 2013 15:32:55 +0100 Subject: [PATCH 04/28] Implement Wiimote sound mixing in AXWii --- .../Src/HW/DSPHLE/UCodes/UCode_AXStructs.h | 18 ++-- .../Core/Src/HW/DSPHLE/UCodes/UCode_AXWii.cpp | 10 +- .../Src/HW/DSPHLE/UCodes/UCode_AX_Voice.h | 93 +++++++++++++++---- 3 files changed, 94 insertions(+), 27 deletions(-) diff --git a/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_AXStructs.h b/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_AXStructs.h index c92196dd49..d82088b34f 100644 --- a/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_AXStructs.h +++ b/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_AXStructs.h @@ -161,15 +161,15 @@ struct PBDpopWii struct PBDpopWM { - s16 aMain0; - s16 aMain1; - s16 aMain2; - s16 aMain3; + s16 main0; + s16 main1; + s16 main2; + s16 main3; - s16 aAux0; - s16 aAux1; - s16 aAux2; - s16 aAux3; + s16 aux0; + s16 aux1; + s16 aux2; + s16 aux3; }; struct PBVolumeEnvelope @@ -216,7 +216,7 @@ struct PBSampleRateConverter struct PBSampleRateConverterWM { - u16 currentAddressFrac; + u16 cur_addr_frac; u16 last_samples[4]; }; 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 1b1097c7fe..84fab9108b 100644 --- a/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_AXWii.cpp +++ b/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_AXWii.cpp @@ -228,7 +228,15 @@ void CUCode_AXWii::ProcessPBList(u32 pb_addr) m_samples_auxB_surround, m_samples_auxC_left, m_samples_auxC_right, - m_samples_auxC_surround + m_samples_auxC_surround, + m_samples_wm0, + m_samples_aux0, + m_samples_wm1, + m_samples_aux1, + m_samples_wm2, + m_samples_aux2, + m_samples_wm3, + m_samples_aux3 }}; if (!ReadPB(pb_addr, pb)) 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 578330ba34..bcf69ddb4b 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 @@ -32,11 +32,9 @@ #ifdef AX_GC # define PB_TYPE AXPB -# define MS_PER_FRAME 1 # define SAMPLES_PER_FRAME 32 #else # define PB_TYPE AXPBWii -# define MS_PER_FRAME 3 # define SAMPLES_PER_FRAME 96 #endif @@ -69,13 +67,22 @@ union AXBuffers int* auxC_left; int* auxC_right; int* auxC_surround; + + int* wm_main0; + int* wm_aux0; + int* wm_main1; + int* wm_aux1; + int* wm_main2; + int* wm_aux2; + int* wm_main3; + int* wm_aux3; #endif }; #ifdef AX_GC int* ptrs[9]; #else - int* ptrs[12]; + int* ptrs[20]; #endif }; @@ -309,7 +316,7 @@ void GetInputSamples(PB_TYPE& pb, s16* samples) } // Add samples to an output buffer, with optional volume ramping. -void MixAdd(int* out, const s16* input, u16* pvol, s16* dpop, bool ramp) +void MixAdd(int* out, const s16* input, u32 count, u16* pvol, s16* dpop, bool ramp) { u16& volume = pvol[0]; u16 volume_delta = pvol[1]; @@ -320,7 +327,7 @@ void MixAdd(int* out, const s16* input, u16* pvol, s16* dpop, bool ramp) if (!ramp) volume_delta = 0; - for (u32 i = 0; i < SAMPLES_PER_FRAME; ++i) + for (u32 i = 0; i < count; ++i) { s64 sample = input[i]; sample *= volume; @@ -363,33 +370,33 @@ void ProcessVoice(PB_TYPE& pb, const AXBuffers& buffers, AXMixControl mctrl) // TODO: Handle DPL2 on AUXB. if (mctrl & MIX_L) - MixAdd(buffers.left, samples, &pb.mixer.left, &pb.dpop.left, mctrl & MIX_L_RAMP); + MixAdd(buffers.left, samples, SAMPLES_PER_FRAME, &pb.mixer.left, &pb.dpop.left, mctrl & MIX_L_RAMP); if (mctrl & MIX_R) - MixAdd(buffers.right, samples, &pb.mixer.right, &pb.dpop.right, mctrl & MIX_R_RAMP); + MixAdd(buffers.right, samples, SAMPLES_PER_FRAME, &pb.mixer.right, &pb.dpop.right, mctrl & MIX_R_RAMP); if (mctrl & MIX_S) - MixAdd(buffers.surround, samples, &pb.mixer.surround, &pb.dpop.surround, mctrl & MIX_S_RAMP); + MixAdd(buffers.surround, samples, SAMPLES_PER_FRAME, &pb.mixer.surround, &pb.dpop.surround, mctrl & MIX_S_RAMP); if (mctrl & MIX_AUXA_L) - MixAdd(buffers.auxA_left, samples, &pb.mixer.auxA_left, &pb.dpop.auxA_left, mctrl & MIX_AUXA_L_RAMP); + MixAdd(buffers.auxA_left, samples, SAMPLES_PER_FRAME, &pb.mixer.auxA_left, &pb.dpop.auxA_left, mctrl & MIX_AUXA_L_RAMP); if (mctrl & MIX_AUXA_R) - MixAdd(buffers.auxA_right, samples, &pb.mixer.auxA_right, &pb.dpop.auxA_right, mctrl & MIX_AUXA_R_RAMP); + MixAdd(buffers.auxA_right, samples, SAMPLES_PER_FRAME, &pb.mixer.auxA_right, &pb.dpop.auxA_right, mctrl & MIX_AUXA_R_RAMP); if (mctrl & MIX_AUXA_S) - MixAdd(buffers.auxA_surround, samples, &pb.mixer.auxA_surround, &pb.dpop.auxA_surround, mctrl & MIX_AUXA_S_RAMP); + MixAdd(buffers.auxA_surround, samples, SAMPLES_PER_FRAME, &pb.mixer.auxA_surround, &pb.dpop.auxA_surround, mctrl & MIX_AUXA_S_RAMP); if (mctrl & MIX_AUXB_L) - MixAdd(buffers.auxB_left, samples, &pb.mixer.auxB_left, &pb.dpop.auxB_left, mctrl & MIX_AUXB_L_RAMP); + MixAdd(buffers.auxB_left, samples, SAMPLES_PER_FRAME, &pb.mixer.auxB_left, &pb.dpop.auxB_left, mctrl & MIX_AUXB_L_RAMP); if (mctrl & MIX_AUXB_R) - MixAdd(buffers.auxB_right, samples, &pb.mixer.auxB_right, &pb.dpop.auxB_right, mctrl & MIX_AUXB_R_RAMP); + MixAdd(buffers.auxB_right, samples, SAMPLES_PER_FRAME, &pb.mixer.auxB_right, &pb.dpop.auxB_right, mctrl & MIX_AUXB_R_RAMP); if (mctrl & MIX_AUXB_S) - MixAdd(buffers.auxB_surround, samples, &pb.mixer.auxB_surround, &pb.dpop.auxB_surround, mctrl & MIX_AUXB_S_RAMP); + MixAdd(buffers.auxB_surround, samples, SAMPLES_PER_FRAME, &pb.mixer.auxB_surround, &pb.dpop.auxB_surround, mctrl & MIX_AUXB_S_RAMP); #ifdef AX_WII if (mctrl & MIX_AUXC_L) - MixAdd(buffers.auxC_left, samples, &pb.mixer.auxC_left, &pb.dpop.auxC_left, mctrl & MIX_AUXC_L_RAMP); + MixAdd(buffers.auxC_left, samples, SAMPLES_PER_FRAME, &pb.mixer.auxC_left, &pb.dpop.auxC_left, mctrl & MIX_AUXC_L_RAMP); if (mctrl & MIX_AUXC_R) - MixAdd(buffers.auxC_right, samples, &pb.mixer.auxC_right, &pb.dpop.auxC_right, mctrl & MIX_AUXC_R_RAMP); + MixAdd(buffers.auxC_right, samples, SAMPLES_PER_FRAME, &pb.mixer.auxC_right, &pb.dpop.auxC_right, mctrl & MIX_AUXC_R_RAMP); if (mctrl & MIX_AUXC_S) - MixAdd(buffers.auxC_surround, samples, &pb.mixer.auxC_surround, &pb.dpop.auxC_surround, mctrl & MIX_AUXC_S_RAMP); + MixAdd(buffers.auxC_surround, samples, SAMPLES_PER_FRAME, &pb.mixer.auxC_surround, &pb.dpop.auxC_surround, mctrl & MIX_AUXC_S_RAMP); #endif // Optionally, phase shift left or right channel to simulate 3D sound. @@ -397,6 +404,58 @@ void ProcessVoice(PB_TYPE& pb, const AXBuffers& buffers, AXMixControl mctrl) { // TODO } + +#ifdef AX_WII + // Wiimote mixing. + if (pb.remote) + { + // Interpolate 18 samples from the 96 samples we read before. The real + // DSP code does it using a polyphase interpolation, we just use a + // linear interpolation here. + s16 wm_samples[18]; + + s16 curr0 = pb.remote_src.last_samples[2]; + s16 curr1 = pb.remote_src.last_samples[3]; + + u32 ratio = 0x55555; // about 96/18 = 5.33333 + u32 curr_pos = pb.remote_src.cur_addr_frac; + for (u32 i = 0; i < 18; ++i) + { + s32 curr_frac_pos = curr_pos & 0xFFFF; + s16 sample = curr0 + (s16)(((curr1 - curr0) * (s32)curr_frac_pos) >> 16); + wm_samples[i] = sample; + + curr_pos += ratio; + curr0 = curr1; + curr1 = samples[curr_pos >> 16]; + } + pb.remote_src.last_samples[2] = curr0; + pb.remote_src.last_samples[3] = curr1; + pb.src.cur_addr_frac = curr_pos & 0xFFFF; + + // Mix to main[0-3] and aux[0-3] +#define WMCHAN_MIX_ON(n) ((pb.remote_mixer_control >> (2 * n)) & 3) +#define WMCHAN_MIX_RAMP(n) ((pb.remote_mixer_control >> (2 * n)) & 2) + + if (WMCHAN_MIX_ON(0)) + MixAdd(buffers.wm_main0, wm_samples, 18, &pb.remote_mixer.main0, &pb.remote_dpop.main0, WMCHAN_MIX_RAMP(0)); + if (WMCHAN_MIX_ON(1)) + MixAdd(buffers.wm_aux0, wm_samples, 18, &pb.remote_mixer.aux0, &pb.remote_dpop.aux0, WMCHAN_MIX_RAMP(1)); + if (WMCHAN_MIX_ON(2)) + MixAdd(buffers.wm_main1, wm_samples, 18, &pb.remote_mixer.main1, &pb.remote_dpop.main1, WMCHAN_MIX_RAMP(2)); + if (WMCHAN_MIX_ON(3)) + MixAdd(buffers.wm_aux1, wm_samples, 18, &pb.remote_mixer.aux1, &pb.remote_dpop.aux1, WMCHAN_MIX_RAMP(3)); + if (WMCHAN_MIX_ON(4)) + MixAdd(buffers.wm_main2, wm_samples, 18, &pb.remote_mixer.main2, &pb.remote_dpop.main2, WMCHAN_MIX_RAMP(4)); + if (WMCHAN_MIX_ON(5)) + MixAdd(buffers.wm_aux2, wm_samples, 18, &pb.remote_mixer.aux2, &pb.remote_dpop.aux2, WMCHAN_MIX_RAMP(5)); + if (WMCHAN_MIX_ON(6)) + MixAdd(buffers.wm_main3, wm_samples, 18, &pb.remote_mixer.main3, &pb.remote_dpop.main3, WMCHAN_MIX_RAMP(6)); + if (WMCHAN_MIX_ON(7)) + MixAdd(buffers.wm_aux3, wm_samples, 18, &pb.remote_mixer.aux3, &pb.remote_dpop.aux3, WMCHAN_MIX_RAMP(7)); + } + +#endif } } // namespace From c6629015048bc9d4aa18d297c0c41d3e26293843 Mon Sep 17 00:00:00 2001 From: Pierre Bourdon Date: Sun, 20 Jan 2013 16:15:42 +0100 Subject: [PATCH 05/28] Implement low-pass voice filtering, untested because I couldn't find any game using it --- .../Core/Src/HW/DSPHLE/UCodes/UCode_AXStructs.h | 6 +++--- .../Core/Core/Src/HW/DSPHLE/UCodes/UCode_AX_Voice.h | 13 ++++++++++--- 2 files changed, 13 insertions(+), 6 deletions(-) diff --git a/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_AXStructs.h b/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_AXStructs.h index d82088b34f..cc24bd78b7 100644 --- a/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_AXStructs.h +++ b/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_AXStructs.h @@ -230,9 +230,9 @@ struct PBADPCMLoopInfo struct PBLowPassFilter { u16 enabled; - u16 yn1; - u16 a0; - u16 b0; + s16 yn1; + s16 a0; + s16 b0; }; struct AXPB 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 bcf69ddb4b..fcc1bb7999 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 @@ -340,6 +340,15 @@ void MixAdd(int* out, const s16* input, u32 count, u16* pvol, s16* dpop, bool ra } } +// Execute a low pass filter on the samples using one history value. Returns +// the new history value. +s16 LowPassFilter(s16* samples, u32 count, s16 yn1, s16 a0, s16 b0) +{ + for (u32 i = 0; i < count; ++i) + yn1 = samples[i] = (s32)a0 * samples[i] - (s32)b0 * yn1; + return yn1; +} + // Process 1ms of audio (for AX GC) or 3ms of audio (for AX Wii) from a PB and // mix it to the output buffers. void ProcessVoice(PB_TYPE& pb, const AXBuffers& buffers, AXMixControl mctrl) @@ -362,9 +371,7 @@ void ProcessVoice(PB_TYPE& pb, const AXBuffers& buffers, AXMixControl mctrl) // Optionally, execute a low pass filter if (pb.lpf.enabled) - { - // TODO - } + pb.lpf.yn1 = LowPassFilter(samples, SAMPLES_PER_FRAME, pb.lpf.yn1, pb.lpf.a0, pb.lpf.b0); // Mix LRS, AUXA and AUXB depending on mixer_control // TODO: Handle DPL2 on AUXB. From 7252becf24583e6bf34f89889944611ccf343bb4 Mon Sep 17 00:00:00 2001 From: Pierre Bourdon Date: Sun, 20 Jan 2013 17:20:30 +0100 Subject: [PATCH 06/28] Fix the voice LPF implementation --- Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_AXStructs.h | 4 ++-- Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_AX_Voice.h | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_AXStructs.h b/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_AXStructs.h index cc24bd78b7..2ac9ddd2f0 100644 --- a/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_AXStructs.h +++ b/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_AXStructs.h @@ -231,8 +231,8 @@ struct PBLowPassFilter { u16 enabled; s16 yn1; - s16 a0; - s16 b0; + u16 a0; + u16 b0; }; struct AXPB 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 fcc1bb7999..f41c4c47ff 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 @@ -342,10 +342,10 @@ void MixAdd(int* out, const s16* input, u32 count, u16* pvol, s16* dpop, bool ra // Execute a low pass filter on the samples using one history value. Returns // the new history value. -s16 LowPassFilter(s16* samples, u32 count, s16 yn1, s16 a0, s16 b0) +s16 LowPassFilter(s16* samples, u32 count, s16 yn1, u16 a0, u16 b0) { for (u32 i = 0; i < count; ++i) - yn1 = samples[i] = (s32)a0 * samples[i] - (s32)b0 * yn1; + yn1 = samples[i] = (a0 * (s32)samples[i] + b0 * (s32)yn1) >> 15; return yn1; } From 4ef021e975293f7530c1f2115408aebca0a721e2 Mon Sep 17 00:00:00 2001 From: Pierre Bourdon Date: Sun, 20 Jan 2013 17:36:57 +0100 Subject: [PATCH 07/28] Better global volume adjustment on a voice. Fixes one of the 2 issues with the HOME button menu sound. --- Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_AX_Voice.h | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) 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 f41c4c47ff..f731fc5360 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 @@ -364,8 +364,7 @@ void ProcessVoice(PB_TYPE& pb, const AXBuffers& buffers, AXMixControl mctrl) // Apply a global volume ramp using the volume envelope parameters. for (u32 i = 0; i < SAMPLES_PER_FRAME; ++i) { - s64 sample = 2 * (s16)samples[i] * (s16)pb.vol_env.cur_volume; - samples[i] = (s16)(sample >> 16); + samples[i] = ((s32)samples[i] * pb.vol_env.cur_volume) >> 15; pb.vol_env.cur_volume += pb.vol_env.cur_volume_delta; } From 1612225e79fc17b748cb5736999d883d8300e541 Mon Sep 17 00:00:00 2001 From: Pierre Bourdon Date: Mon, 21 Jan 2013 11:51:55 +0100 Subject: [PATCH 08/28] Implement AXWii command 01: ADD_TO_LR --- .../Core/Src/HW/DSPHLE/UCodes/UCode_AXWii.cpp | 19 ++++++++++++++++++- .../Core/Src/HW/DSPHLE/UCodes/UCode_AXWii.h | 3 ++- 2 files changed, 20 insertions(+), 2 deletions(-) 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 84fab9108b..2377839e86 100644 --- a/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_AXWii.cpp +++ b/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_AXWii.cpp @@ -67,7 +67,12 @@ void CUCode_AXWii::HandleCommandList() SetupProcessing(HILO_TO_32(addr)); break; - case CMD_UNK_01: curr_idx += 2; break; + case CMD_ADD_TO_LR: + addr_hi = m_cmdlist[curr_idx++]; + addr_lo = m_cmdlist[curr_idx++]; + AddToLR(HILO_TO_32(addr)); + break; + case CMD_UNK_02: curr_idx += 2; break; case CMD_UNK_03: curr_idx += 2; break; @@ -182,6 +187,18 @@ void CUCode_AXWii::SetupProcessing(u32 init_addr) } } +void CUCode_AXWii::AddToLR(u32 val_addr) +{ + int* ptr = (int*)HLEMemory_Get_Pointer(val_addr); + for (int i = 0; i < 32 * 3; ++i) + { + int val = (int)Common::swap32(*ptr++); + + m_samples_left[i] += val; + m_samples_right[i] += val; + } +} + AXMixControl CUCode_AXWii::ConvertMixerControl(u32 mixer_control) { u32 ret = 0; 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..6ace48abc1 100644 --- a/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_AXWii.h +++ b/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_AXWii.h @@ -51,6 +51,7 @@ protected: virtual void HandleCommandList(); void SetupProcessing(u32 init_addr); + void AddToLR(u32 val_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); @@ -60,7 +61,7 @@ private: enum CmdType { CMD_SETUP = 0x00, - CMD_UNK_01 = 0x01, + CMD_ADD_TO_LR = 0x01, CMD_UNK_02 = 0x02, CMD_UNK_03 = 0x03, CMD_PROCESS = 0x04, From 9776f135e231d7da2cb31573c4cef65ae1674c33 Mon Sep 17 00:00:00 2001 From: Pierre Bourdon Date: Fri, 25 Jan 2013 08:25:28 +0100 Subject: [PATCH 09/28] Add polyphase resampling support in AX --- .../Core/Src/HW/DSPHLE/UCodes/UCode_AX.cpp | 27 +++++++++- .../Core/Core/Src/HW/DSPHLE/UCodes/UCode_AX.h | 8 +++ .../Core/Src/HW/DSPHLE/UCodes/UCode_AXWii.cpp | 3 +- .../Src/HW/DSPHLE/UCodes/UCode_AX_Voice.h | 51 +++++++++++++++++-- 4 files changed, 82 insertions(+), 7 deletions(-) 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 cb34c2532d..3675970917 100644 --- a/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_AX.cpp +++ b/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_AX.cpp @@ -17,6 +17,7 @@ #include "UCode_AX.h" #include "../../DSP.h" +#include "FileUtil.h" #define AX_GC #include "UCode_AX_Voice.h" @@ -29,6 +30,8 @@ CUCode_AX::CUCode_AX(DSPHLE* dsp_hle, u32 crc) WARN_LOG(DSPHLE, "Instantiating CUCode_AX: crc=%08x", crc); m_rMailHandler.PushMail(DSP_INIT); DSP::GenerateDSPInterruptFromDSPEmu(DSP::INT_DSP); + + LoadResamplingCoefficients(); } CUCode_AX::~CUCode_AX() @@ -40,6 +43,27 @@ CUCode_AX::~CUCode_AX() m_rMailHandler.Clear(); } +void CUCode_AX::LoadResamplingCoefficients() +{ + m_coeffs_available = false; + + std::string filename = File::GetUserPath(D_GCUSER_IDX) + "dsp_coef.bin"; + if (!File::Exists(filename)) + return; + + if (File::GetSize(filename) != 0x1000) + return; + + FILE* fp = fopen(filename.c_str(), "rb"); + fread(m_coeffs, 1, 0x1000, fp); + fclose(fp); + + for (u32 i = 0; i < 0x800; ++i) + m_coeffs[i] = Common::swap16(m_coeffs[i]); + + m_coeffs_available = true; +} + void CUCode_AX::SpawnAXThread(CUCode_AX* self) { self->AXThread(); @@ -405,7 +429,8 @@ void CUCode_AX::ProcessPBList(u32 pb_addr) { ApplyUpdatesForMs(pb, curr_ms); - ProcessVoice(pb, buffers, ConvertMixerControl(pb.mixer_control)); + ProcessVoice(pb, buffers, ConvertMixerControl(pb.mixer_control), + m_coeffs_available ? m_coeffs : NULL); // Forward the buffers for (u32 i = 0; i < sizeof (buffers.ptrs) / sizeof (buffers.ptrs[0]); ++i) 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..68248fef60 100644 --- a/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_AX.h +++ b/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_AX.h @@ -114,6 +114,14 @@ protected: std::condition_variable m_cmdlist_cv; std::mutex m_cmdlist_mutex; + // Table of coefficients for polyphase sample rate conversion. + // The coefficients aren't always available (they are part of the DSP DROM) + // so we also need to know if they are valid or not. + bool m_coeffs_available; + s16 m_coeffs[0x800]; + + void LoadResamplingCoefficients(); + // Copy a command list from memory to our temp buffer void CopyCmdList(u32 addr, u16 size); 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 2377839e86..1fde3f74b4 100644 --- a/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_AXWii.cpp +++ b/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_AXWii.cpp @@ -259,7 +259,8 @@ void CUCode_AXWii::ProcessPBList(u32 pb_addr) if (!ReadPB(pb_addr, pb)) break; - ProcessVoice(pb, buffers, ConvertMixerControl(HILO_TO_32(pb.mixer_control))); + ProcessVoice(pb, buffers, ConvertMixerControl(HILO_TO_32(pb.mixer_control)), + m_coeffs_available ? m_coeffs : NULL); WritePB(pb_addr, pb); pb_addr = HILO_TO_32(pb.next_pb); 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 f731fc5360..649ad3c37f 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 @@ -245,13 +245,54 @@ u16 AcceleratorGetSample() // Read SAMPLES_PER_FRAME input samples from ARAM, decoding and converting rate // if required. -void GetInputSamples(PB_TYPE& pb, s16* samples) +void GetInputSamples(PB_TYPE& pb, s16* samples, const s16* coeffs) { u32 cur_addr = HILO_TO_32(pb.audio_addr.cur_addr); AcceleratorSetup(&pb, &cur_addr); - // TODO: support polyphase interpolation if coefficients are available. - if (pb.src_type == SRCTYPE_POLYPHASE || pb.src_type == SRCTYPE_LINEAR) + // If DSP DROM coefficients are available, support polyphase resampling. + if (coeffs && pb.src_type == SRCTYPE_POLYPHASE) + { + s16 temp[4]; + u32 idx = 0; + + u32 ratio = HILO_TO_32(pb.src.ratio); + u32 curr_pos = pb.src.cur_addr_frac; + + temp[idx++ & 3] = pb.src.last_samples[0]; + temp[idx++ & 3] = pb.src.last_samples[1]; + temp[idx++ & 3] = pb.src.last_samples[2]; + temp[idx++ & 3] = pb.src.last_samples[3]; + + for (u32 i = 0; i < SAMPLES_PER_FRAME; ++i) + { + curr_pos += ratio; + while (curr_pos >= 0x10000) + { + temp[idx++ & 3] = AcceleratorGetSample(); + curr_pos -= 0x10000; + } + + u16 curr_pos_frac = ((curr_pos & 0xFFFF) >> 9) << 2; + const s16* c = &coeffs[pb.coef_select * 0x200 + curr_pos_frac]; + + s64 t0 = temp[idx++ & 3]; + s64 t1 = temp[idx++ & 3]; + s64 t2 = temp[idx++ & 3]; + s64 t3 = temp[idx++ & 3]; + + s64 samp = (t0 * c[0] + t1 * c[1] + t2 * c[2] + t3 * c[3]) >> 15; + + samples[i] = (s16)samp; + } + + pb.src.last_samples[3] = temp[--idx & 3]; + pb.src.last_samples[2] = temp[--idx & 3]; + pb.src.last_samples[1] = temp[--idx & 3]; + pb.src.last_samples[0] = temp[--idx & 3]; + pb.src.cur_addr_frac = curr_pos & 0xFFFF; + } + else if (pb.src_type == SRCTYPE_LINEAR || (!coeffs && pb.src_type == SRCTYPE_POLYPHASE)) { // Convert the input to a higher or lower sample rate using a linear // interpolation algorithm. The input to output ratio is set in @@ -351,7 +392,7 @@ s16 LowPassFilter(s16* samples, u32 count, s16 yn1, u16 a0, u16 b0) // Process 1ms of audio (for AX GC) or 3ms of audio (for AX Wii) from a PB and // mix it to the output buffers. -void ProcessVoice(PB_TYPE& pb, const AXBuffers& buffers, AXMixControl mctrl) +void ProcessVoice(PB_TYPE& pb, const AXBuffers& buffers, AXMixControl mctrl, const s16* coeffs) { // If the voice is not running, nothing to do. if (!pb.running) @@ -359,7 +400,7 @@ void ProcessVoice(PB_TYPE& pb, const AXBuffers& buffers, AXMixControl mctrl) // Read input samples, performing sample rate conversion if needed. s16 samples[SAMPLES_PER_FRAME]; - GetInputSamples(pb, samples); + GetInputSamples(pb, samples, coeffs); // Apply a global volume ramp using the volume envelope parameters. for (u32 i = 0; i < SAMPLES_PER_FRAME; ++i) From bad4f7f790b6038450488d1c23d8b65329cea4b4 Mon Sep 17 00:00:00 2001 From: Pierre Bourdon Date: Fri, 25 Jan 2013 19:16:07 +0100 Subject: [PATCH 10/28] Rewrite the linear interpolation SRC to give the exact same results as the one in AXWii --- .../Src/HW/DSPHLE/UCodes/UCode_AX_Voice.h | 64 +++++++++++++------ 1 file changed, 43 insertions(+), 21 deletions(-) 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 649ad3c37f..92c797ad14 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 @@ -307,38 +307,60 @@ void GetInputSamples(PB_TYPE& pb, s16* samples, const s16* coeffs) // samples. u32 curr_pos = pb.src.cur_addr_frac; - // These are the two samples between which we interpolate. The initial - // values are stored in the PB, and we update them when resampling the - // input data. - s16 curr0 = pb.src.last_samples[2]; - s16 curr1 = pb.src.last_samples[3]; + // This is the circular buffer containing samples to use for the + // interpolation. It is initialized with the values from the PB, and it + // will be stored back to the PB at the end. + s16 temp[4]; + u32 idx = 0; + + temp[idx++ & 3] = pb.src.last_samples[0]; + temp[idx++ & 3] = pb.src.last_samples[1]; + temp[idx++ & 3] = pb.src.last_samples[2]; + temp[idx++ & 3] = pb.src.last_samples[3]; for (u32 i = 0; i < SAMPLES_PER_FRAME; ++i) { - // Get our current fractional position, used to know how much of - // curr0 and how much of curr1 the output sample should be. - s32 curr_frac_pos = curr_pos & 0xFFFF; - - // Linear interpolation: s1 + (s2 - s1) * pos - s16 sample = curr0 + (s16)(((curr1 - curr0) * (s32)curr_frac_pos) >> 16); - samples[i] = sample; - curr_pos += ratio; - // While our current position is >= 1.0, shift to the next 2 - // samples for interpolation. - while ((curr_pos >> 16) != 0) + // While our current position is >= 1.0, push new samples to the + // circular buffer. + while (curr_pos >= 0x10000) { - curr0 = curr1; - curr1 = AcceleratorGetSample(); + temp[idx++ & 3] = AcceleratorGetSample(); curr_pos -= 0x10000; } + + // Get our current fractional position, used to know how much of + // curr0 and how much of curr1 the output sample should be. + u16 curr_frac = curr_pos & 0xFFFF; + u16 inv_curr_frac = -curr_frac; + + // Interpolate! If curr_frac is 0, we can simply take the last + // sample without any multiplying. + s16 sample; + if (curr_frac) + { + s32 s0 = temp[idx++ & 3]; + s32 s1 = temp[idx++ & 3]; + + sample = ((s0 * inv_curr_frac) + (s1 * curr_frac)) >> 16; + idx += 2; + } + else + { + sample = temp[idx++ & 3]; + idx += 3; + } + + samples[i] = sample; } - // Update the two last_samples values in the PB as well as the current + // Update the four last_samples values in the PB as well as the current // position. - pb.src.last_samples[2] = curr0; - pb.src.last_samples[3] = curr1; + pb.src.last_samples[3] = temp[--idx & 3]; + pb.src.last_samples[2] = temp[--idx & 3]; + pb.src.last_samples[1] = temp[--idx & 3]; + pb.src.last_samples[0] = temp[--idx & 3]; pb.src.cur_addr_frac = curr_pos & 0xFFFF; } else // SRCTYPE_NEAREST From 85b498ba97e829e561fe5b4cb48b30348e83d323 Mon Sep 17 00:00:00 2001 From: Pierre Bourdon Date: Fri, 29 Mar 2013 13:18:30 +0100 Subject: [PATCH 11/28] Update the right cur_addr_frac after wiimote audio resampling --- Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_AX_Voice.h | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) 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 0f3dcccb88..51c1c1e728 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 @@ -500,7 +500,7 @@ void ProcessVoice(PB_TYPE& pb, const AXBuffers& buffers, AXMixControl mctrl, con } pb.remote_src.last_samples[2] = curr0; pb.remote_src.last_samples[3] = curr1; - pb.src.cur_addr_frac = curr_pos & 0xFFFF; + pb.remote_src.cur_addr_frac = curr_pos & 0xFFFF; // Mix to main[0-3] and aux[0-3] #define WMCHAN_MIX_ON(n) ((pb.remote_mixer_control >> (2 * n)) & 3) @@ -523,7 +523,6 @@ void ProcessVoice(PB_TYPE& pb, const AXBuffers& buffers, AXMixControl mctrl, con if (WMCHAN_MIX_ON(7)) MixAdd(buffers.wm_aux3, wm_samples, 18, &pb.remote_mixer.aux3, &pb.remote_dpop.aux3, WMCHAN_MIX_RAMP(7)); } - #endif } From 4dc1ffbb207b5085cb490bec2b1da0f76d2832eb Mon Sep 17 00:00:00 2001 From: Pierre Bourdon Date: Fri, 29 Mar 2013 13:49:36 +0100 Subject: [PATCH 12/28] Refactor the resampling code to avoid having two polyphase resampling implementations (normal/wm) --- .../Src/HW/DSPHLE/UCodes/UCode_AXStructs.h | 4 +- .../Src/HW/DSPHLE/UCodes/UCode_AX_Voice.h | 151 +++++++++--------- 2 files changed, 79 insertions(+), 76 deletions(-) diff --git a/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_AXStructs.h b/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_AXStructs.h index 2ac9ddd2f0..5fc4806e26 100644 --- a/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_AXStructs.h +++ b/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_AXStructs.h @@ -211,13 +211,13 @@ struct PBSampleRateConverter u16 ratio_hi; // integer part of sampling ratio u16 ratio_lo; // fraction part of sampling ratio u16 cur_addr_frac; - u16 last_samples[4]; + s16 last_samples[4]; }; struct PBSampleRateConverterWM { u16 cur_addr_frac; - u16 last_samples[4]; + s16 last_samples[4]; }; struct PBADPCMLoopInfo 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 51c1c1e728..7bf060b0d4 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 @@ -243,38 +243,52 @@ u16 AcceleratorGetSample() return ret; } -// Read SAMPLES_PER_FRAME input samples from ARAM, decoding and converting rate -// if required. -void GetInputSamples(PB_TYPE& pb, s16* samples, const s16* coeffs) +// Reads samples from the input callback, resamples them to samples at +// the wanted sample rate (computed from the ratio, see below). +// +// If srctype is SRCTYPE_POLYPHASE, coefficients need to be provided as well +// (or the srctype will automatically be changed to LINEAR). +// +// Returns the current position after resampling (including fractional part). +// +// The input to output ratio is set in , which is a floating point num +// stored as a 32b integer: +// * Upper 16 bits of the ratio are the integer part +// * Lower 16 bits are the decimal part +// +// is a 32b integer structured in the same way as the ratio: the +// upper 16 bits are the integer part of the current position in the input +// stream, and the lower 16 bits are the decimal part. +// +// We start getting samples not from sample 0, but 0.. This +// avoids discontinuties in the audio stream, especially with very low ratios +// which interpolate a lot of values between two "real" samples. +u32 ResampleAudio(std::function input_callback, s16* output, u32 count, + s16* last_samples, u32 curr_pos, u32 ratio, int srctype, + const s16* coeffs) { - u32 cur_addr = HILO_TO_32(pb.audio_addr.cur_addr); - AcceleratorSetup(&pb, &cur_addr); - // If DSP DROM coefficients are available, support polyphase resampling. - if (coeffs && pb.src_type == SRCTYPE_POLYPHASE) + if (coeffs && srctype == SRCTYPE_POLYPHASE) { s16 temp[4]; u32 idx = 0; - u32 ratio = HILO_TO_32(pb.src.ratio); - u32 curr_pos = pb.src.cur_addr_frac; + temp[idx++ & 3] = last_samples[0]; + temp[idx++ & 3] = last_samples[1]; + temp[idx++ & 3] = last_samples[2]; + temp[idx++ & 3] = last_samples[3]; - temp[idx++ & 3] = pb.src.last_samples[0]; - temp[idx++ & 3] = pb.src.last_samples[1]; - temp[idx++ & 3] = pb.src.last_samples[2]; - temp[idx++ & 3] = pb.src.last_samples[3]; - - for (u32 i = 0; i < SAMPLES_PER_FRAME; ++i) + for (u32 i = 0; i < count; ++i) { curr_pos += ratio; while (curr_pos >= 0x10000) { - temp[idx++ & 3] = AcceleratorGetSample(); + temp[idx++ & 3] = input_callback(curr_pos >> 16); curr_pos -= 0x10000; } u16 curr_pos_frac = ((curr_pos & 0xFFFF) >> 9) << 2; - const s16* c = &coeffs[pb.coef_select * 0x200 + curr_pos_frac]; + const s16* c = &coeffs[curr_pos_frac]; s64 t0 = temp[idx++ & 3]; s64 t1 = temp[idx++ & 3]; @@ -283,42 +297,28 @@ void GetInputSamples(PB_TYPE& pb, s16* samples, const s16* coeffs) s64 samp = (t0 * c[0] + t1 * c[1] + t2 * c[2] + t3 * c[3]) >> 15; - samples[i] = (s16)samp; + output[i] = (s16)samp; } - pb.src.last_samples[3] = temp[--idx & 3]; - pb.src.last_samples[2] = temp[--idx & 3]; - pb.src.last_samples[1] = temp[--idx & 3]; - pb.src.last_samples[0] = temp[--idx & 3]; - pb.src.cur_addr_frac = curr_pos & 0xFFFF; + last_samples[3] = temp[--idx & 3]; + last_samples[2] = temp[--idx & 3]; + last_samples[1] = temp[--idx & 3]; + last_samples[0] = temp[--idx & 3]; } - else if (pb.src_type == SRCTYPE_LINEAR || (!coeffs && pb.src_type == SRCTYPE_POLYPHASE)) + else if (srctype == SRCTYPE_LINEAR || (!coeffs && srctype == SRCTYPE_POLYPHASE)) { - // Convert the input to a higher or lower sample rate using a linear - // interpolation algorithm. The input to output ratio is set in - // pb.src.ratio, which is a floating point num stored as a 32b integer: - // * Upper 16 bits of the ratio are the integer part - // * Lower 16 bits are the decimal part - u32 ratio = HILO_TO_32(pb.src.ratio); - - // We start getting samples not from sample 0, but 0.. - // This avoids discontinuties in the audio stream, especially with very - // low ratios which interpolate a lot of values between two "real" - // samples. - u32 curr_pos = pb.src.cur_addr_frac; - // This is the circular buffer containing samples to use for the // interpolation. It is initialized with the values from the PB, and it // will be stored back to the PB at the end. s16 temp[4]; u32 idx = 0; - temp[idx++ & 3] = pb.src.last_samples[0]; - temp[idx++ & 3] = pb.src.last_samples[1]; - temp[idx++ & 3] = pb.src.last_samples[2]; - temp[idx++ & 3] = pb.src.last_samples[3]; + temp[idx++ & 3] = last_samples[0]; + temp[idx++ & 3] = last_samples[1]; + temp[idx++ & 3] = last_samples[2]; + temp[idx++ & 3] = last_samples[3]; - for (u32 i = 0; i < SAMPLES_PER_FRAME; ++i) + for (u32 i = 0; i < count; ++i) { curr_pos += ratio; @@ -326,7 +326,7 @@ void GetInputSamples(PB_TYPE& pb, s16* samples, const s16* coeffs) // circular buffer. while (curr_pos >= 0x10000) { - temp[idx++ & 3] = AcceleratorGetSample(); + temp[idx++ & 3] = input_callback(curr_pos >> 16); curr_pos -= 0x10000; } @@ -352,27 +352,43 @@ void GetInputSamples(PB_TYPE& pb, s16* samples, const s16* coeffs) idx += 3; } - samples[i] = sample; + output[i] = sample; } - // Update the four last_samples values in the PB as well as the current - // position. - pb.src.last_samples[3] = temp[--idx & 3]; - pb.src.last_samples[2] = temp[--idx & 3]; - pb.src.last_samples[1] = temp[--idx & 3]; - pb.src.last_samples[0] = temp[--idx & 3]; - pb.src.cur_addr_frac = curr_pos & 0xFFFF; + // Update the four last_samples values. + last_samples[3] = temp[--idx & 3]; + last_samples[2] = temp[--idx & 3]; + last_samples[1] = temp[--idx & 3]; + last_samples[0] = temp[--idx & 3]; } else // SRCTYPE_NEAREST { // No sample rate conversion here: simply read samples from the // accelerator to the output buffer. - for (u32 i = 0; i < SAMPLES_PER_FRAME; ++i) - samples[i] = AcceleratorGetSample(); + for (u32 i = 0; i < count; ++i) + output[i] = input_callback(i); - memcpy(pb.src.last_samples, samples + SAMPLES_PER_FRAME - 4, 4 * sizeof (u16)); + memcpy(last_samples, output + count - 4, 4 * sizeof (u16)); } + return curr_pos; +} + +// Read SAMPLES_PER_FRAME input samples from ARAM, decoding and converting rate +// if required. +void GetInputSamples(PB_TYPE& pb, s16* samples, const s16* coeffs) +{ + u32 cur_addr = HILO_TO_32(pb.audio_addr.cur_addr); + AcceleratorSetup(&pb, &cur_addr); + + if (coeffs) + coeffs += pb.coef_select * 0x200; + u32 curr_pos = ResampleAudio([](u32) { return AcceleratorGetSample(); }, + samples, SAMPLES_PER_FRAME, pb.src.last_samples, + pb.src.cur_addr_frac, HILO_TO_32(pb.src.ratio), + pb.src_type, coeffs); + pb.src.cur_addr_frac = (curr_pos & 0xFFFF); + // Update current position in the PB. pb.audio_addr.cur_addr_hi = (u16)(cur_addr >> 16); pb.audio_addr.cur_addr_lo = (u16)(cur_addr & 0xFFFF); @@ -478,28 +494,15 @@ void ProcessVoice(PB_TYPE& pb, const AXBuffers& buffers, AXMixControl mctrl, con // Wiimote mixing. if (pb.remote) { - // Interpolate 18 samples from the 96 samples we read before. The real - // DSP code does it using a polyphase interpolation, we just use a - // linear interpolation here. + // Interpolate 18 samples from the 96 samples we read before. s16 wm_samples[18]; - s16 curr0 = pb.remote_src.last_samples[2]; - s16 curr1 = pb.remote_src.last_samples[3]; - - u32 ratio = 0x55555; // about 96/18 = 5.33333 - u32 curr_pos = pb.remote_src.cur_addr_frac; - for (u32 i = 0; i < 18; ++i) - { - s32 curr_frac_pos = curr_pos & 0xFFFF; - s16 sample = curr0 + (s16)(((curr1 - curr0) * (s32)curr_frac_pos) >> 16); - wm_samples[i] = sample; - - curr_pos += ratio; - curr0 = curr1; - curr1 = samples[curr_pos >> 16]; - } - pb.remote_src.last_samples[2] = curr0; - pb.remote_src.last_samples[3] = curr1; + // We use ratio 0x55555 == (5 * 65536 + 21845) / 65536 == 5.3333 which + // is the nearest we can get to 96/18 + u32 curr_pos = ResampleAudio([&samples](u32 i) { return samples[i]; }, + wm_samples, 18, pb.remote_src.last_samples, + pb.remote_src.cur_addr_frac, 0x55555, + SRCTYPE_POLYPHASE, coeffs); pb.remote_src.cur_addr_frac = curr_pos & 0xFFFF; // Mix to main[0-3] and aux[0-3] From a997824f680aaf9cc86adfd56e8bea865e5614dd Mon Sep 17 00:00:00 2001 From: Pierre Bourdon Date: Fri, 29 Mar 2013 13:51:52 +0100 Subject: [PATCH 13/28] Add missing header include --- Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_AX_Voice.h | 2 ++ 1 file changed, 2 insertions(+) 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 7bf060b0d4..64a01ca8fe 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 @@ -30,6 +30,8 @@ #include "UCode_AXStructs.h" #include "../../DSP.h" +#include + #ifdef AX_GC # define PB_TYPE AXPB # define SAMPLES_PER_FRAME 32 From e9b236be051bf0b4c4e06e5d6add0d946ed86554 Mon Sep 17 00:00:00 2001 From: Pierre Bourdon Date: Fri, 29 Mar 2013 13:55:55 +0100 Subject: [PATCH 14/28] OSX sucks at c++11 --- .../Core/Core/Src/HW/DSPHLE/UCodes/UCode_AX_Voice.h | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) 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 64a01ca8fe..0b5bf4a022 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 @@ -30,7 +30,14 @@ #include "UCode_AXStructs.h" #include "../../DSP.h" -#include +// I hate OSX. +#if defined(__APPLE__) +# include +using std::tr1::function; +#else +# include +using std::function; +#endif #ifdef AX_GC # define PB_TYPE AXPB @@ -265,7 +272,7 @@ u16 AcceleratorGetSample() // We start getting samples not from sample 0, but 0.. This // avoids discontinuties in the audio stream, especially with very low ratios // which interpolate a lot of values between two "real" samples. -u32 ResampleAudio(std::function input_callback, s16* output, u32 count, +u32 ResampleAudio(function input_callback, s16* output, u32 count, s16* last_samples, u32 curr_pos, u32 ratio, int srctype, const s16* coeffs) { From ef501137be4a019a18719e1ce802ec42ca68ab03 Mon Sep 17 00:00:00 2001 From: Pierre Bourdon Date: Fri, 29 Mar 2013 21:02:27 +0100 Subject: [PATCH 15/28] Fix audio glitching at the end of a voice because of bad non-looping sound handling in AXWii --- .../Src/HW/DSPHLE/UCodes/UCode_AX_Voice.h | 75 +++++++++++-------- 1 file changed, 45 insertions(+), 30 deletions(-) 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 0b5bf4a022..87c0d51f46 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 @@ -147,6 +147,7 @@ void DumpPB(const PB_TYPE& pb) static u32 acc_loop_addr, acc_end_addr; static u32* acc_cur_addr; static PB_TYPE* acc_pb; +static bool acc_end_reached; // Sets up the simulated accelerator. void AcceleratorSetup(PB_TYPE* pb, u32* cur_addr) @@ -155,6 +156,7 @@ void AcceleratorSetup(PB_TYPE* pb, u32* cur_addr) acc_loop_addr = HILO_TO_32(pb->audio_addr.loop_addr); acc_end_addr = HILO_TO_32(pb->audio_addr.end_addr); acc_cur_addr = cur_addr; + acc_end_reached = false; } // Reads a sample from the simulated accelerator. Also handles looping and @@ -164,6 +166,49 @@ u16 AcceleratorGetSample() { u16 ret; + // Have we reached the end address? + // + // On real hardware, this would raise an interrupt that is handled by the + // UCode. We simulate what this interrupt does here. + if ((*acc_cur_addr & ~1) == (acc_end_addr & ~1)) + { + // loop back to loop_addr. + *acc_cur_addr = acc_loop_addr; + + if (acc_pb->audio_addr.looping) + { + // Set the ADPCM infos to continue processing at loop_addr. + // + // For some reason, yn1 and yn2 aren't set if the voice is not of + // stream type. This is what the AX UCode does and I don't really + // know why. + acc_pb->adpcm.pred_scale = acc_pb->adpcm_loop_info.pred_scale; + if (!acc_pb->is_stream) + { + acc_pb->adpcm.yn1 = acc_pb->adpcm_loop_info.yn1; + acc_pb->adpcm.yn2 = acc_pb->adpcm_loop_info.yn2; + } + } + else + { + // Non looping voice reached the end -> running = 0. + acc_pb->running = 0; + +#ifdef AX_WII + // One of the few meaningful differences between AXGC and AXWii: + // while AXGC handles non looping voices ending by having 0000 + // samples at the loop address, AXWii has the 0000 samples + // internally in DRAM and use an internal pointer to it (loop addr + // does not contain 0000 samples on AXWii!). + acc_end_reached = true; +#endif + } + } + + // See above for explanations about acc_end_reached. + if (acc_end_reached) + return 0; + switch (acc_pb->audio_addr.sample_format) { case 0x00: // ADPCM @@ -219,36 +264,6 @@ u16 AcceleratorGetSample() return 0; } - // Have we reached the end address? - // - // On real hardware, this would raise an interrupt that is handled by the - // UCode. We simulate what this interrupt does here. - if ((*acc_cur_addr & ~1) == (acc_end_addr & ~1)) - { - // loop back to loop_addr. - *acc_cur_addr = acc_loop_addr; - - if (acc_pb->audio_addr.looping) - { - // Set the ADPCM infos to continue processing at loop_addr. - // - // For some reason, yn1 and yn2 aren't set if the voice is not of - // stream type. This is what the AX UCode does and I don't really - // know why. - acc_pb->adpcm.pred_scale = acc_pb->adpcm_loop_info.pred_scale; - if (!acc_pb->is_stream) - { - acc_pb->adpcm.yn1 = acc_pb->adpcm_loop_info.yn1; - acc_pb->adpcm.yn2 = acc_pb->adpcm_loop_info.yn2; - } - } - else - { - // Non looping voice reached the end -> running = 0. - acc_pb->running = 0; - } - } - return ret; } From 4b09f525f6e63a1e3686d84f12463ba127eeda36 Mon Sep 17 00:00:00 2001 From: Pierre Bourdon Date: Fri, 29 Mar 2013 22:22:24 +0100 Subject: [PATCH 16/28] Fix AUX volume mixing in AXWii: implement volume ramping and MixAdd properly. Home menu sounds now work properly. --- .../Core/Src/HW/DSPHLE/UCodes/UCode_AXWii.cpp | 28 +++++++++++++++++-- .../Core/Src/HW/DSPHLE/UCodes/UCode_AXWii.h | 4 +++ 2 files changed, 29 insertions(+), 3 deletions(-) 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 1fde3f74b4..8381564f3f 100644 --- a/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_AXWii.cpp +++ b/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_AXWii.cpp @@ -29,7 +29,8 @@ CUCode_AXWii::CUCode_AXWii(DSPHLE *dsp_hle, u32 l_CRC) - : CUCode_AX(dsp_hle, l_CRC) + : CUCode_AX(dsp_hle, l_CRC), + m_last_aux_volume(0x8000) { WARN_LOG(DSPHLE, "Instantiating CUCode_AXWii"); } @@ -269,6 +270,22 @@ void CUCode_AXWii::ProcessPBList(u32 pb_addr) void CUCode_AXWii::MixAUXSamples(int aux_id, u32 write_addr, u32 read_addr, u16 volume) { + u16 prev_volume = m_last_aux_volume; + + // Generate a volume ramp going from prev_volume to volume. + // + // The AXWii UCode uses integer arithmetic with 2 multipliers because it + // can't get enough precision to represent 1 / 96. We can use floating + // point arithmetic, so we will - it makes the code more readable and more + // precise. + u16 volume_ramp[96]; + float curr_volume = prev_volume; + for (int i = 0; i < 96; ++i) + { + volume_ramp[i] = (u16)curr_volume; + curr_volume += (volume - prev_volume) / 96.0; + } + int* buffers[3] = { 0 }; int* main_buffers[3] = { m_samples_left, @@ -311,9 +328,12 @@ void CUCode_AXWii::MixAUXSamples(int aux_id, u32 write_addr, u32 read_addr, u16 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; + s64 sample = (s64)(s32)Common::swap32(*ptr++); + sample *= volume_ramp[j]; + main_buffers[i][j] += (s32)(sample >> 15); } + + m_last_aux_volume = volume; } void CUCode_AXWii::OutputSamples(u32 lr_addr, u32 surround_addr, u16 volume) @@ -397,4 +417,6 @@ void CUCode_AXWii::DoState(PointerWrap &p) p.Do(m_samples_aux1); p.Do(m_samples_aux2); p.Do(m_samples_aux3); + + p.Do(m_last_aux_volume); } 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 6ace48abc1..031971a2a4 100644 --- a/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_AXWii.h +++ b/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_AXWii.h @@ -29,6 +29,7 @@ public: virtual void DoState(PointerWrap &p); protected: + // Additional AUX buffers int m_samples_auxC_left[32 * 3]; int m_samples_auxC_right[32 * 3]; int m_samples_auxC_surround[32 * 3]; @@ -43,6 +44,9 @@ protected: int m_samples_wm3[6 * 3]; int m_samples_aux3[6 * 3]; + // Last MixAUXSamples volume value. Used to generate volume ramps. + u16 m_last_aux_volume; + // 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. From c271082ec50bda616233fbf883ad0c58cda8fcd0 Mon Sep 17 00:00:00 2001 From: Pierre Bourdon Date: Sat, 30 Mar 2013 00:55:55 +0100 Subject: [PATCH 17/28] Add volume ramping for MAIN output, separate old volume values for each AUX channel and refactor --- .../Core/Src/HW/DSPHLE/UCodes/UCode_AXWii.cpp | 43 ++++++++++--------- .../Core/Src/HW/DSPHLE/UCodes/UCode_AXWii.h | 11 ++++- 2 files changed, 32 insertions(+), 22 deletions(-) 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 8381564f3f..df9a6b5409 100644 --- a/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_AXWii.cpp +++ b/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_AXWii.cpp @@ -30,8 +30,10 @@ CUCode_AXWii::CUCode_AXWii(DSPHLE *dsp_hle, u32 l_CRC) : CUCode_AX(dsp_hle, l_CRC), - m_last_aux_volume(0x8000) + m_last_main_volume(0x8000) { + for (int i = 0; i < 3; ++i) + m_last_aux_volumes[i] = 0x8000; WARN_LOG(DSPHLE, "Instantiating CUCode_AXWii"); } @@ -228,6 +230,16 @@ AXMixControl CUCode_AXWii::ConvertMixerControl(u32 mixer_control) return (AXMixControl)ret; } +void CUCode_AXWii::GenerateVolumeRamp(u16* output, u16 vol1, u16 vol2, size_t nvals) +{ + float curr = vol1; + for (size_t i = 0; i < nvals; ++i) + { + curr += (vol2 - vol1) / (float)nvals; + output[i] = curr; + } +} + void CUCode_AXWii::ProcessPBList(u32 pb_addr) { AXPBWii pb; @@ -270,21 +282,9 @@ void CUCode_AXWii::ProcessPBList(u32 pb_addr) void CUCode_AXWii::MixAUXSamples(int aux_id, u32 write_addr, u32 read_addr, u16 volume) { - u16 prev_volume = m_last_aux_volume; - - // Generate a volume ramp going from prev_volume to volume. - // - // The AXWii UCode uses integer arithmetic with 2 multipliers because it - // can't get enough precision to represent 1 / 96. We can use floating - // point arithmetic, so we will - it makes the code more readable and more - // precise. u16 volume_ramp[96]; - float curr_volume = prev_volume; - for (int i = 0; i < 96; ++i) - { - volume_ramp[i] = (u16)curr_volume; - curr_volume += (volume - prev_volume) / 96.0; - } + GenerateVolumeRamp(volume_ramp, m_last_aux_volumes[aux_id], volume, 96); + m_last_aux_volumes[aux_id] = volume; int* buffers[3] = { 0 }; int* main_buffers[3] = { @@ -332,12 +332,14 @@ void CUCode_AXWii::MixAUXSamples(int aux_id, u32 write_addr, u32 read_addr, u16 sample *= volume_ramp[j]; main_buffers[i][j] += (s32)(sample >> 15); } - - m_last_aux_volume = volume; } void CUCode_AXWii::OutputSamples(u32 lr_addr, u32 surround_addr, u16 volume) { + u16 volume_ramp[96]; + GenerateVolumeRamp(volume_ramp, m_last_main_volume, volume, 96); + m_last_main_volume = volume; + int surround_buffer[3 * 32] = { 0 }; for (u32 i = 0; i < 3 * 32; ++i) @@ -353,8 +355,8 @@ void CUCode_AXWii::OutputSamples(u32 lr_addr, u32 surround_addr, u16 volume) 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; + left = ((s64)left * volume_ramp[i]) >> 15; + right = ((s64)right * volume_ramp[i]) >> 15; if (left < -32767) left = -32767; if (left > 32767) left = 32767; @@ -418,5 +420,6 @@ void CUCode_AXWii::DoState(PointerWrap &p) p.Do(m_samples_aux2); p.Do(m_samples_aux3); - p.Do(m_last_aux_volume); + p.Do(m_last_main_volume); + p.Do(m_last_aux_volumes); } 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 031971a2a4..c1d281d9c3 100644 --- a/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_AXWii.h +++ b/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_AXWii.h @@ -44,14 +44,21 @@ protected: int m_samples_wm3[6 * 3]; int m_samples_aux3[6 * 3]; - // Last MixAUXSamples volume value. Used to generate volume ramps. - u16 m_last_aux_volume; + // Last volume values for MAIN and AUX. Used to generate volume ramps to + // interpolate nicely between old and new volume values. + u16 m_last_main_volume; + u16 m_last_aux_volumes[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); + // Generate a volume ramp from vol1 to vol2, interpolating n volume values. + // Uses floating point arithmetic, which isn't exactly what the UCode does, + // but this gives better precision and nicer code. + void GenerateVolumeRamp(u16* output, u16 vol1, u16 vol2, size_t nvals); + virtual void HandleCommandList(); void SetupProcessing(u32 init_addr); From e3b0a2c9bf3a73016d641788687aad8b49e04d97 Mon Sep 17 00:00:00 2001 From: Pierre Bourdon Date: Sat, 30 Mar 2013 14:02:30 +0100 Subject: [PATCH 18/28] Add an option to run the AX processing on the CPU thread. Fixes timing issues causing audio glitches on Wii, and should improve the overall stability of AX HLE. --- .../Core/Src/HW/DSPHLE/UCodes/UCode_AX.cpp | 43 +++++++++++++++---- .../Core/Core/Src/HW/DSPHLE/UCodes/UCode_AX.h | 11 ++++- Source/Core/DolphinWX/Src/ConfigMain.cpp | 4 +- 3 files changed, 47 insertions(+), 11 deletions(-) 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 3675970917..ef7f2b61ca 100644 --- a/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_AX.cpp +++ b/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_AX.cpp @@ -18,27 +18,35 @@ #include "UCode_AX.h" #include "../../DSP.h" #include "FileUtil.h" +#include "ConfigManager.h" #define AX_GC #include "UCode_AX_Voice.h" CUCode_AX::CUCode_AX(DSPHLE* dsp_hle, u32 crc) : IUCode(dsp_hle, crc) + , m_work_available(false) , m_cmdlist_size(0) - , m_axthread(&SpawnAXThread, this) + , m_run_on_thread(SConfig::GetInstance().m_LocalCoreStartupParameter.bDSPThread) { WARN_LOG(DSPHLE, "Instantiating CUCode_AX: crc=%08x", crc); m_rMailHandler.PushMail(DSP_INIT); DSP::GenerateDSPInterruptFromDSPEmu(DSP::INT_DSP); LoadResamplingCoefficients(); + + if (m_run_on_thread) + m_axthread = std::thread(SpawnAXThread, this); } CUCode_AX::~CUCode_AX() { - m_cmdlist_size = (u16)-1; // Special value to signal end - NotifyAXThread(); - m_axthread.join(); + if (m_run_on_thread) + { + m_cmdlist_size = (u16)-1; // Special value to signal end + NotifyAXThread(); + m_axthread.join(); + } m_rMailHandler.Clear(); } @@ -85,20 +93,32 @@ void CUCode_AX::AXThread() m_processing.lock(); HandleCommandList(); m_cmdlist_size = 0; - - // Signal end of processing - m_rMailHandler.PushMail(DSP_YIELD); - DSP::GenerateDSPInterruptFromDSPEmu(DSP::INT_DSP); + SignalWorkEnd(); m_processing.unlock(); } } +void CUCode_AX::SignalWorkEnd() +{ + // Signal end of processing + m_rMailHandler.PushMail(DSP_YIELD); + DSP::GenerateDSPInterruptFromDSPEmu(DSP::INT_DSP); +} + void CUCode_AX::NotifyAXThread() { std::unique_lock lk(m_cmdlist_mutex); m_cmdlist_cv.notify_one(); } +void CUCode_AX::StartWorking() +{ + if (m_run_on_thread) + NotifyAXThread(); + else + m_work_available = true; +} + void CUCode_AX::HandleCommandList() { // Temp variables for addresses computation @@ -620,6 +640,7 @@ void CUCode_AX::HandleMail(u32 mail) if (next_is_cmdlist) { CopyCmdList(mail, cmdlist_size); + StartWorking(); NotifyAXThread(); } else if (m_UploadSetupInProgress) @@ -688,6 +709,12 @@ void CUCode_AX::Update(int cycles) m_rMailHandler.PushMail(DSP_RESUME); DSP::GenerateDSPInterruptFromDSPEmu(DSP::INT_DSP); } + else if (m_work_available) + { + HandleCommandList(); + m_cmdlist_size = 0; + SignalWorkEnd(); + } } void CUCode_AX::DoAXState(PointerWrap& p) 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 68248fef60..fbecac1990 100644 --- a/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_AX.h +++ b/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_AX.h @@ -102,11 +102,15 @@ protected: int m_samples_auxB_right[32 * 5]; int m_samples_auxB_surround[32 * 5]; + // This flag is set if there is anything to process. + bool m_work_available; + // Volatile because it's set by HandleMail and accessed in // HandleCommandList, which are running in two different threads. volatile u16 m_cmdlist[512]; volatile u32 m_cmdlist_size; + bool m_run_on_thread; std::thread m_axthread; // Sync objects @@ -130,13 +134,18 @@ protected: // versions of AX. AXMixControl ConvertMixerControl(u32 mixer_control); - // Send a notification to the AX thread to tell him a new cmdlist addr is + // Signal that we should start handling a command list. Dispatches to the + // AX thread if using a thread, else just sets a boolean flag. + void StartWorking(); + + // Send a notification to the AX thread to tell it a new cmdlist addr is // available for processing. void NotifyAXThread(); void AXThread(); virtual void HandleCommandList(); + void SignalWorkEnd(); void SetupProcessing(u32 init_addr); void DownloadAndMixWithVolume(u32 addr, u16 vol_main, u16 vol_auxa, u16 vol_auxb); diff --git a/Source/Core/DolphinWX/Src/ConfigMain.cpp b/Source/Core/DolphinWX/Src/ConfigMain.cpp index cbd780e6aa..c55b75f044 100644 --- a/Source/Core/DolphinWX/Src/ConfigMain.cpp +++ b/Source/Core/DolphinWX/Src/ConfigMain.cpp @@ -515,7 +515,7 @@ void CConfigMain::InitializeGUITooltips() InterfaceLang->SetToolTip(_("Change the language of the user interface.\nRequires restart.")); // Audio tooltips - DSPThread->SetToolTip(_("Run DSP LLE on a dedicated thread (not recommended).")); + DSPThread->SetToolTip(_("Run DSP HLE and LLE on a dedicated thread (not recommended: might cause audio glitches with HLE and freezes with LLE).")); BackendSelection->SetToolTip(_("Changing this will have no effect while the emulator is running!")); // Gamecube - Devices @@ -657,7 +657,7 @@ void CConfigMain::CreateGUIControls() // Audio page DSPEngine = new wxRadioBox(AudioPage, ID_DSPENGINE, _("DSP Emulator Engine"), wxDefaultPosition, wxDefaultSize, arrayStringFor_DSPEngine, 0, wxRA_SPECIFY_ROWS); - DSPThread = new wxCheckBox(AudioPage, ID_DSPTHREAD, _("DSP LLE on Thread")); + DSPThread = new wxCheckBox(AudioPage, ID_DSPTHREAD, _("DSP on Dedicated Thread")); DumpAudio = new wxCheckBox(AudioPage, ID_DUMP_AUDIO, _("Dump Audio"), wxDefaultPosition, wxDefaultSize, 0, wxDefaultValidator); DPL2Decoder = new wxCheckBox(AudioPage, ID_DPL2DECODER, _("Dolby Pro Logic II decoder")); From a813f9e13c697bc866ad7f0402dfbfeb3e4c0fee Mon Sep 17 00:00:00 2001 From: Pierre Bourdon Date: Sat, 30 Mar 2013 14:38:14 +0100 Subject: [PATCH 19/28] Support loading polyphase resampling coeffs from User and Sys in HLE --- .../Core/Src/HW/DSPHLE/UCodes/UCode_AX.cpp | 25 ++++++++++++++++--- 1 file changed, 21 insertions(+), 4 deletions(-) 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 ef7f2b61ca..b21e2f66f4 100644 --- a/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_AX.cpp +++ b/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_AX.cpp @@ -55,12 +55,29 @@ void CUCode_AX::LoadResamplingCoefficients() { m_coeffs_available = false; - std::string filename = File::GetUserPath(D_GCUSER_IDX) + "dsp_coef.bin"; - if (!File::Exists(filename)) + std::string filenames[] = { + File::GetUserPath(D_GCUSER_IDX) + "dsp_coef.bin", + File::GetSysDirectory() + "/GC/dsp_coef.bin" + }; + + size_t fidx; + std::string filename; + for (fidx = 0; fidx < sizeof (filenames) / sizeof (filenames[0]); ++fidx) + { + filename = filenames[fidx]; + if (!File::Exists(filename)) + continue; + + if (File::GetSize(filename) != 0x1000) + continue; + + break; + } + + if (fidx >= sizeof (filenames) / sizeof (filenames[0])) return; - if (File::GetSize(filename) != 0x1000) - return; + WARN_LOG(DSPHLE, "Loading polyphase resampling coeffs from %s", filename.c_str()); FILE* fp = fopen(filename.c_str(), "rb"); fread(m_coeffs, 1, 0x1000, fp); From 04f9c6793bea3b5bd7a57e21074e69ceca79e31d Mon Sep 17 00:00:00 2001 From: Pierre Bourdon Date: Sat, 30 Mar 2013 14:39:59 +0100 Subject: [PATCH 20/28] Fix the argument to the samples reading callback in voice processing - should fix issues with wiimote audio, untested --- Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_AX_Voice.h | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) 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 87c0d51f46..f36f5760e7 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 @@ -291,6 +291,8 @@ u32 ResampleAudio(function input_callback, s16* output, u32 count, s16* last_samples, u32 curr_pos, u32 ratio, int srctype, const s16* coeffs) { + int read_samples_count = 0; + // If DSP DROM coefficients are available, support polyphase resampling. if (coeffs && srctype == SRCTYPE_POLYPHASE) { @@ -307,7 +309,7 @@ u32 ResampleAudio(function input_callback, s16* output, u32 count, curr_pos += ratio; while (curr_pos >= 0x10000) { - temp[idx++ & 3] = input_callback(curr_pos >> 16); + temp[idx++ & 3] = input_callback(read_samples_count++); curr_pos -= 0x10000; } @@ -350,7 +352,7 @@ u32 ResampleAudio(function input_callback, s16* output, u32 count, // circular buffer. while (curr_pos >= 0x10000) { - temp[idx++ & 3] = input_callback(curr_pos >> 16); + temp[idx++ & 3] = input_callback(read_samples_count++); curr_pos -= 0x10000; } From 79c031624351c7e0d93879cc92c00a3c5e1c328e Mon Sep 17 00:00:00 2001 From: Pierre Bourdon Date: Sat, 30 Mar 2013 16:59:06 +0100 Subject: [PATCH 21/28] Disable the polyphase resampler - it causes audio glitches with non integer ratios --- Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_AX_Voice.h | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) 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 f36f5760e7..a93f69fb2c 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 @@ -293,8 +293,11 @@ u32 ResampleAudio(function input_callback, s16* output, u32 count, { int read_samples_count = 0; + // TODO(delroth): find out why the polyphase resampling algorithm causes + // audio glitches in Wii games with non integral ratios. + // If DSP DROM coefficients are available, support polyphase resampling. - if (coeffs && srctype == SRCTYPE_POLYPHASE) + if (0) // if (coeffs && srctype == SRCTYPE_POLYPHASE) { s16 temp[4]; u32 idx = 0; @@ -331,7 +334,7 @@ u32 ResampleAudio(function input_callback, s16* output, u32 count, last_samples[1] = temp[--idx & 3]; last_samples[0] = temp[--idx & 3]; } - else if (srctype == SRCTYPE_LINEAR || (!coeffs && srctype == SRCTYPE_POLYPHASE)) + else if (srctype == SRCTYPE_LINEAR || srctype == SRCTYPE_POLYPHASE) { // This is the circular buffer containing samples to use for the // interpolation. It is initialized with the values from the PB, and it From 276c457bed3e9737e769cc1224467f57642f4661 Mon Sep 17 00:00:00 2001 From: Pierre Bourdon Date: Sat, 30 Mar 2013 22:22:57 +0100 Subject: [PATCH 22/28] Basic framework to support the old AXWii version used in Wii Sports and Excite Truck --- .../Core/Src/HW/DSPHLE/UCodes/UCode_AX.cpp | 20 +++--- .../Core/Core/Src/HW/DSPHLE/UCodes/UCode_AX.h | 3 + .../Src/HW/DSPHLE/UCodes/UCode_AXStructs.h | 46 ------------- .../Core/Src/HW/DSPHLE/UCodes/UCode_AXWii.cpp | 65 +++++++++++++++++- .../Core/Src/HW/DSPHLE/UCodes/UCode_AXWii.h | 6 ++ .../Src/HW/DSPHLE/UCodes/UCode_AX_Voice.h | 67 ++++++++++--------- 6 files changed, 118 insertions(+), 89 deletions(-) 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 b21e2f66f4..9c7d57f813 100644 --- a/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_AX.cpp +++ b/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_AX.cpp @@ -307,19 +307,18 @@ void CUCode_AX::HandleCommandList() } } -static void ApplyUpdatesForMs(AXPB& pb, int curr_ms) +void CUCode_AX::ApplyUpdatesForMs(int curr_ms, u16* pb, u16* num_updates, u16* updates) { u32 start_idx = 0; for (int i = 0; i < curr_ms; ++i) - start_idx += pb.updates.num_updates[i]; + start_idx += num_updates[i]; - u32 update_addr = HILO_TO_32(pb.updates.data); - for (u32 i = start_idx; i < start_idx + pb.updates.num_updates[curr_ms]; ++i) + for (u32 i = start_idx; i < start_idx + num_updates[curr_ms]; ++i) { - u16 update_off = HLEMemory_Read_U16(update_addr + 4 * i); - u16 update_val = HLEMemory_Read_U16(update_addr + 4 * i + 2); + u16 update_off = Common::swap16(updates[2 * i]); + u16 update_val = Common::swap16(updates[2 * i + 1]); - ((u16*)&pb)[update_off] = update_val; + pb[update_off] = update_val; } } @@ -462,11 +461,14 @@ void CUCode_AX::ProcessPBList(u32 pb_addr) if (!ReadPB(pb_addr, pb)) break; + u32 updates_addr = HILO_TO_32(pb.updates.data); + u16* updates = (u16*)HLEMemory_Get_Pointer(updates_addr); + for (int curr_ms = 0; curr_ms < 5; ++curr_ms) { - ApplyUpdatesForMs(pb, curr_ms); + ApplyUpdatesForMs(curr_ms, (u16*)&pb, pb.updates.num_updates, updates); - ProcessVoice(pb, buffers, ConvertMixerControl(pb.mixer_control), + ProcessVoice(pb, buffers, spms, ConvertMixerControl(pb.mixer_control), m_coeffs_available ? m_coeffs : NULL); // Forward the buffers 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 fbecac1990..71f195bf33 100644 --- a/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_AX.h +++ b/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_AX.h @@ -134,6 +134,9 @@ protected: // versions of AX. AXMixControl ConvertMixerControl(u32 mixer_control); + // Apply updates to a PB. Generic, used in AX GC and AX Wii. + void ApplyUpdatesForMs(int curr_ms, u16* pb, u16* num_updates, u16* updates); + // Signal that we should start handling a command list. Dispatches to the // AX thread if using a thread, else just sets a boolean flag. void StartWorking(); diff --git a/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_AXStructs.h b/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_AXStructs.h index 5fc4806e26..1161847bc8 100644 --- a/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_AXStructs.h +++ b/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_AXStructs.h @@ -324,52 +324,6 @@ struct AXPBWii 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, 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 df9a6b5409..64d27ffbd1 100644 --- a/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_AXWii.cpp +++ b/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_AXWii.cpp @@ -35,6 +35,8 @@ CUCode_AXWii::CUCode_AXWii(DSPHLE *dsp_hle, u32 l_CRC) for (int i = 0; i < 3; ++i) m_last_aux_volumes[i] = 0x8000; WARN_LOG(DSPHLE, "Instantiating CUCode_AXWii"); + + m_old_axwii = (l_CRC == 0xfa450138); } CUCode_AXWii::~CUCode_AXWii() @@ -100,6 +102,8 @@ void CUCode_AXWii::HandleCommandList() case CMD_UNK_08: curr_idx += 13; break; case CMD_UNK_09: curr_idx += 13; break; + // TODO(delroth): figure this one out, it's used by almost every + // game I've tested so far. case CMD_UNK_0A: curr_idx += 4; break; case CMD_OUTPUT: @@ -240,6 +244,43 @@ void CUCode_AXWii::GenerateVolumeRamp(u16* output, u16 vol1, u16 vol2, size_t nv } } +bool CUCode_AXWii::ExtractUpdatesFields(AXPBWii& pb, u16* num_updates, u16* updates) +{ + u16* pb_mem = (u16*)&pb; + + if (!m_old_axwii) + return false; + + // Copy the num_updates field. + memcpy(num_updates, pb_mem + 41, 6); + + // Get the address of the updates data + u16 addr_hi = pb_mem[44]; + u16 addr_lo = pb_mem[45]; + u32 addr = HILO_TO_32(addr); + u16* ptr = (u16*)HLEMemory_Get_Pointer(addr); + + // Copy the updates data and change the offset to match a PB without + // updates data. + u32 updates_count = num_updates[0] + num_updates[1] + num_updates[2]; + for (u32 i = 0; i < updates_count; ++i) + { + u16 update_off = Common::swap16(ptr[2 * i]); + u16 update_val = Common::swap16(ptr[2 * i + 1]); + + if (update_off > 45) + update_off -= 5; + + updates[2 * i] = update_off; + updates[2 * i + 1] = update_val; + } + + // Remove the updates data from the PB + memmove(pb_mem + 41, pb_mem + 45, sizeof (pb) - 2 * 45); + + return true; +} + void CUCode_AXWii::ProcessPBList(u32 pb_addr) { AXPBWii pb; @@ -272,8 +313,28 @@ void CUCode_AXWii::ProcessPBList(u32 pb_addr) if (!ReadPB(pb_addr, pb)) break; - ProcessVoice(pb, buffers, ConvertMixerControl(HILO_TO_32(pb.mixer_control)), - m_coeffs_available ? m_coeffs : NULL); + u16 num_updates[3]; + u16 updates[1024]; + if (ExtractUpdatesFields(pb, num_updates, updates)) + { + for (int curr_ms = 0; curr_ms < 3; ++curr_ms) + { + ApplyUpdatesForMs(curr_ms, (u16*)&pb, num_updates, updates); + ProcessVoice(pb, buffers, 32, + ConvertMixerControl(HILO_TO_32(pb.mixer_control)), + m_coeffs_available ? m_coeffs : NULL); + + // Forward the buffers + for (u32 i = 0; i < sizeof (buffers.ptrs) / sizeof (buffers.ptrs[0]); ++i) + buffers.ptrs[i] += 32; + } + } + else + { + ProcessVoice(pb, buffers, 96, + ConvertMixerControl(HILO_TO_32(pb.mixer_control)), + m_coeffs_available ? m_coeffs : NULL); + } WritePB(pb_addr, pb); pb_addr = HILO_TO_32(pb.next_pb); 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 c1d281d9c3..ce09b14674 100644 --- a/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_AXWii.h +++ b/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_AXWii.h @@ -44,11 +44,17 @@ protected: int m_samples_wm3[6 * 3]; int m_samples_aux3[6 * 3]; + // Are we implementing an old version of AXWii which still has updates? + bool m_old_axwii; + // Last volume values for MAIN and AUX. Used to generate volume ramps to // interpolate nicely between old and new volume values. u16 m_last_main_volume; u16 m_last_aux_volumes[3]; + // If needed, extract the updates related fields from a PB. + bool ExtractUpdatesFields(AXPBWii& pb, u16* num_updates, u16* updates); + // 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. 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 a93f69fb2c..e364e42888 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 @@ -41,10 +41,10 @@ using std::function; #ifdef AX_GC # define PB_TYPE AXPB -# define SAMPLES_PER_FRAME 32 +# define MAX_SAMPLES_PER_FRAME 32 #else # define PB_TYPE AXPBWii -# define SAMPLES_PER_FRAME 96 +# define MAX_SAMPLES_PER_FRAME 96 #endif // Put all of that in an anonymous namespace to avoid stupid compilers merging @@ -403,9 +403,9 @@ u32 ResampleAudio(function input_callback, s16* output, u32 count, return curr_pos; } -// Read SAMPLES_PER_FRAME input samples from ARAM, decoding and converting rate +// Read input samples from ARAM, decoding and converting rate // if required. -void GetInputSamples(PB_TYPE& pb, s16* samples, const s16* coeffs) +void GetInputSamples(PB_TYPE& pb, s16* samples, u16 count, const s16* coeffs) { u32 cur_addr = HILO_TO_32(pb.audio_addr.cur_addr); AcceleratorSetup(&pb, &cur_addr); @@ -413,7 +413,7 @@ void GetInputSamples(PB_TYPE& pb, s16* samples, const s16* coeffs) if (coeffs) coeffs += pb.coef_select * 0x200; u32 curr_pos = ResampleAudio([](u32) { return AcceleratorGetSample(); }, - samples, SAMPLES_PER_FRAME, pb.src.last_samples, + samples, count, pb.src.last_samples, pb.src.cur_addr_frac, HILO_TO_32(pb.src.ratio), pb.src_type, coeffs); pb.src.cur_addr_frac = (curr_pos & 0xFFFF); @@ -459,18 +459,18 @@ s16 LowPassFilter(s16* samples, u32 count, s16 yn1, u16 a0, u16 b0) // Process 1ms of audio (for AX GC) or 3ms of audio (for AX Wii) from a PB and // mix it to the output buffers. -void ProcessVoice(PB_TYPE& pb, const AXBuffers& buffers, AXMixControl mctrl, const s16* coeffs) +void ProcessVoice(PB_TYPE& pb, const AXBuffers& buffers, u16 count, AXMixControl mctrl, const s16* coeffs) { // If the voice is not running, nothing to do. if (!pb.running) return; // Read input samples, performing sample rate conversion if needed. - s16 samples[SAMPLES_PER_FRAME]; - GetInputSamples(pb, samples, coeffs); + s16 samples[MAX_SAMPLES_PER_FRAME]; + GetInputSamples(pb, samples, count, coeffs); // Apply a global volume ramp using the volume envelope parameters. - for (u32 i = 0; i < SAMPLES_PER_FRAME; ++i) + for (u32 i = 0; i < count; ++i) { samples[i] = ((s32)samples[i] * pb.vol_env.cur_volume) >> 15; pb.vol_env.cur_volume += pb.vol_env.cur_volume_delta; @@ -478,39 +478,39 @@ void ProcessVoice(PB_TYPE& pb, const AXBuffers& buffers, AXMixControl mctrl, con // Optionally, execute a low pass filter if (pb.lpf.enabled) - pb.lpf.yn1 = LowPassFilter(samples, SAMPLES_PER_FRAME, pb.lpf.yn1, pb.lpf.a0, pb.lpf.b0); + pb.lpf.yn1 = LowPassFilter(samples, count, pb.lpf.yn1, pb.lpf.a0, pb.lpf.b0); // Mix LRS, AUXA and AUXB depending on mixer_control // TODO: Handle DPL2 on AUXB. if (mctrl & MIX_L) - MixAdd(buffers.left, samples, SAMPLES_PER_FRAME, &pb.mixer.left, &pb.dpop.left, mctrl & MIX_L_RAMP); + MixAdd(buffers.left, samples, count, &pb.mixer.left, &pb.dpop.left, mctrl & MIX_L_RAMP); if (mctrl & MIX_R) - MixAdd(buffers.right, samples, SAMPLES_PER_FRAME, &pb.mixer.right, &pb.dpop.right, mctrl & MIX_R_RAMP); + MixAdd(buffers.right, samples, count, &pb.mixer.right, &pb.dpop.right, mctrl & MIX_R_RAMP); if (mctrl & MIX_S) - MixAdd(buffers.surround, samples, SAMPLES_PER_FRAME, &pb.mixer.surround, &pb.dpop.surround, mctrl & MIX_S_RAMP); + MixAdd(buffers.surround, samples, count, &pb.mixer.surround, &pb.dpop.surround, mctrl & MIX_S_RAMP); if (mctrl & MIX_AUXA_L) - MixAdd(buffers.auxA_left, samples, SAMPLES_PER_FRAME, &pb.mixer.auxA_left, &pb.dpop.auxA_left, mctrl & MIX_AUXA_L_RAMP); + MixAdd(buffers.auxA_left, samples, count, &pb.mixer.auxA_left, &pb.dpop.auxA_left, mctrl & MIX_AUXA_L_RAMP); if (mctrl & MIX_AUXA_R) - MixAdd(buffers.auxA_right, samples, SAMPLES_PER_FRAME, &pb.mixer.auxA_right, &pb.dpop.auxA_right, mctrl & MIX_AUXA_R_RAMP); + MixAdd(buffers.auxA_right, samples, count, &pb.mixer.auxA_right, &pb.dpop.auxA_right, mctrl & MIX_AUXA_R_RAMP); if (mctrl & MIX_AUXA_S) - MixAdd(buffers.auxA_surround, samples, SAMPLES_PER_FRAME, &pb.mixer.auxA_surround, &pb.dpop.auxA_surround, mctrl & MIX_AUXA_S_RAMP); + MixAdd(buffers.auxA_surround, samples, count, &pb.mixer.auxA_surround, &pb.dpop.auxA_surround, mctrl & MIX_AUXA_S_RAMP); if (mctrl & MIX_AUXB_L) - MixAdd(buffers.auxB_left, samples, SAMPLES_PER_FRAME, &pb.mixer.auxB_left, &pb.dpop.auxB_left, mctrl & MIX_AUXB_L_RAMP); + MixAdd(buffers.auxB_left, samples, count, &pb.mixer.auxB_left, &pb.dpop.auxB_left, mctrl & MIX_AUXB_L_RAMP); if (mctrl & MIX_AUXB_R) - MixAdd(buffers.auxB_right, samples, SAMPLES_PER_FRAME, &pb.mixer.auxB_right, &pb.dpop.auxB_right, mctrl & MIX_AUXB_R_RAMP); + MixAdd(buffers.auxB_right, samples, count, &pb.mixer.auxB_right, &pb.dpop.auxB_right, mctrl & MIX_AUXB_R_RAMP); if (mctrl & MIX_AUXB_S) - MixAdd(buffers.auxB_surround, samples, SAMPLES_PER_FRAME, &pb.mixer.auxB_surround, &pb.dpop.auxB_surround, mctrl & MIX_AUXB_S_RAMP); + MixAdd(buffers.auxB_surround, samples, count, &pb.mixer.auxB_surround, &pb.dpop.auxB_surround, mctrl & MIX_AUXB_S_RAMP); #ifdef AX_WII if (mctrl & MIX_AUXC_L) - MixAdd(buffers.auxC_left, samples, SAMPLES_PER_FRAME, &pb.mixer.auxC_left, &pb.dpop.auxC_left, mctrl & MIX_AUXC_L_RAMP); + MixAdd(buffers.auxC_left, samples, count, &pb.mixer.auxC_left, &pb.dpop.auxC_left, mctrl & MIX_AUXC_L_RAMP); if (mctrl & MIX_AUXC_R) - MixAdd(buffers.auxC_right, samples, SAMPLES_PER_FRAME, &pb.mixer.auxC_right, &pb.dpop.auxC_right, mctrl & MIX_AUXC_R_RAMP); + MixAdd(buffers.auxC_right, samples, count, &pb.mixer.auxC_right, &pb.dpop.auxC_right, mctrl & MIX_AUXC_R_RAMP); if (mctrl & MIX_AUXC_S) - MixAdd(buffers.auxC_surround, samples, SAMPLES_PER_FRAME, &pb.mixer.auxC_surround, &pb.dpop.auxC_surround, mctrl & MIX_AUXC_S_RAMP); + MixAdd(buffers.auxC_surround, samples, count, &pb.mixer.auxC_surround, &pb.dpop.auxC_surround, mctrl & MIX_AUXC_S_RAMP); #endif // Optionally, phase shift left or right channel to simulate 3D sound. @@ -523,13 +523,16 @@ void ProcessVoice(PB_TYPE& pb, const AXBuffers& buffers, AXMixControl mctrl, con // Wiimote mixing. if (pb.remote) { - // Interpolate 18 samples from the 96 samples we read before. + // Old AXWii versions process ms per ms. + u16 wm_count = count == 96 ? 18 : 6; + + // Interpolate at most 18 samples from the 96 samples we read before. s16 wm_samples[18]; // We use ratio 0x55555 == (5 * 65536 + 21845) / 65536 == 5.3333 which // is the nearest we can get to 96/18 u32 curr_pos = ResampleAudio([&samples](u32 i) { return samples[i]; }, - wm_samples, 18, pb.remote_src.last_samples, + wm_samples, wm_count, pb.remote_src.last_samples, pb.remote_src.cur_addr_frac, 0x55555, SRCTYPE_POLYPHASE, coeffs); pb.remote_src.cur_addr_frac = curr_pos & 0xFFFF; @@ -539,21 +542,21 @@ void ProcessVoice(PB_TYPE& pb, const AXBuffers& buffers, AXMixControl mctrl, con #define WMCHAN_MIX_RAMP(n) ((pb.remote_mixer_control >> (2 * n)) & 2) if (WMCHAN_MIX_ON(0)) - MixAdd(buffers.wm_main0, wm_samples, 18, &pb.remote_mixer.main0, &pb.remote_dpop.main0, WMCHAN_MIX_RAMP(0)); + MixAdd(buffers.wm_main0, wm_samples, wm_count, &pb.remote_mixer.main0, &pb.remote_dpop.main0, WMCHAN_MIX_RAMP(0)); if (WMCHAN_MIX_ON(1)) - MixAdd(buffers.wm_aux0, wm_samples, 18, &pb.remote_mixer.aux0, &pb.remote_dpop.aux0, WMCHAN_MIX_RAMP(1)); + MixAdd(buffers.wm_aux0, wm_samples, wm_count, &pb.remote_mixer.aux0, &pb.remote_dpop.aux0, WMCHAN_MIX_RAMP(1)); if (WMCHAN_MIX_ON(2)) - MixAdd(buffers.wm_main1, wm_samples, 18, &pb.remote_mixer.main1, &pb.remote_dpop.main1, WMCHAN_MIX_RAMP(2)); + MixAdd(buffers.wm_main1, wm_samples, wm_count, &pb.remote_mixer.main1, &pb.remote_dpop.main1, WMCHAN_MIX_RAMP(2)); if (WMCHAN_MIX_ON(3)) - MixAdd(buffers.wm_aux1, wm_samples, 18, &pb.remote_mixer.aux1, &pb.remote_dpop.aux1, WMCHAN_MIX_RAMP(3)); + MixAdd(buffers.wm_aux1, wm_samples, wm_count, &pb.remote_mixer.aux1, &pb.remote_dpop.aux1, WMCHAN_MIX_RAMP(3)); if (WMCHAN_MIX_ON(4)) - MixAdd(buffers.wm_main2, wm_samples, 18, &pb.remote_mixer.main2, &pb.remote_dpop.main2, WMCHAN_MIX_RAMP(4)); + MixAdd(buffers.wm_main2, wm_samples, wm_count, &pb.remote_mixer.main2, &pb.remote_dpop.main2, WMCHAN_MIX_RAMP(4)); if (WMCHAN_MIX_ON(5)) - MixAdd(buffers.wm_aux2, wm_samples, 18, &pb.remote_mixer.aux2, &pb.remote_dpop.aux2, WMCHAN_MIX_RAMP(5)); + MixAdd(buffers.wm_aux2, wm_samples, wm_count, &pb.remote_mixer.aux2, &pb.remote_dpop.aux2, WMCHAN_MIX_RAMP(5)); if (WMCHAN_MIX_ON(6)) - MixAdd(buffers.wm_main3, wm_samples, 18, &pb.remote_mixer.main3, &pb.remote_dpop.main3, WMCHAN_MIX_RAMP(6)); + MixAdd(buffers.wm_main3, wm_samples, wm_count, &pb.remote_mixer.main3, &pb.remote_dpop.main3, WMCHAN_MIX_RAMP(6)); if (WMCHAN_MIX_ON(7)) - MixAdd(buffers.wm_aux3, wm_samples, 18, &pb.remote_mixer.aux3, &pb.remote_dpop.aux3, WMCHAN_MIX_RAMP(7)); + MixAdd(buffers.wm_aux3, wm_samples, wm_count, &pb.remote_mixer.aux3, &pb.remote_dpop.aux3, WMCHAN_MIX_RAMP(7)); } #endif } From 60b43eb8d34d0267732b686f9e02094f68a8ec48 Mon Sep 17 00:00:00 2001 From: Pierre Bourdon Date: Sun, 31 Mar 2013 00:25:00 +0100 Subject: [PATCH 23/28] Support the old AXWii version used in games like Wii Sports or Excite Truck --- .../Core/Src/HW/DSPHLE/UCodes/UCode_AXWii.cpp | 250 +++++++++++++----- .../Core/Src/HW/DSPHLE/UCodes/UCode_AXWii.h | 32 ++- 2 files changed, 206 insertions(+), 76 deletions(-) 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 64d27ffbd1..1e966d2d0a 100644 --- a/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_AXWii.cpp +++ b/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_AXWii.cpp @@ -50,8 +50,10 @@ void CUCode_AXWii::HandleCommandList() u16 addr2_hi, addr2_lo; u16 volume; + u32 pb_addr = 0; + // WARN_LOG(DSPHLE, "Command list:"); -// for (u32 i = 0; m_cmdlist[i] != CMD_END; ++i) +// for (u32 i = 0; m_cmdlist[i] != CMD_END_OLD; ++i) // WARN_LOG(DSPHLE, "%04x", m_cmdlist[i]); // WARN_LOG(DSPHLE, "-------------"); @@ -61,78 +63,160 @@ void CUCode_AXWii::HandleCommandList() { u16 cmd = m_cmdlist[curr_idx++]; - switch (cmd) + if (m_old_axwii) { - // 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_ADD_TO_LR: - addr_hi = m_cmdlist[curr_idx++]; - addr_lo = m_cmdlist[curr_idx++]; - AddToLR(HILO_TO_32(addr)); - 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; - - // TODO(delroth): figure this one out, it's used by almost every - // game I've tested so far. - 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: + switch (cmd) { - 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; - } + // 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_END: - end = true; - break; + case CMD_SETUP_OLD: + addr_hi = m_cmdlist[curr_idx++]; + addr_lo = m_cmdlist[curr_idx++]; + SetupProcessing(HILO_TO_32(addr)); + break; + + case CMD_ADD_TO_LR_OLD: + addr_hi = m_cmdlist[curr_idx++]; + addr_lo = m_cmdlist[curr_idx++]; + AddToLR(HILO_TO_32(addr)); + break; + + case CMD_UNK_02_OLD: curr_idx += 2; break; + case CMD_UNK_03_OLD: curr_idx += 2; break; + + case CMD_PB_ADDR_OLD: + addr_hi = m_cmdlist[curr_idx++]; + addr_lo = m_cmdlist[curr_idx++]; + pb_addr = HILO_TO_32(addr); + break; + + case CMD_PROCESS_OLD: + ProcessPBList(pb_addr); + break; + + case CMD_MIX_AUXA_OLD: + case CMD_MIX_AUXB_OLD: + case CMD_MIX_AUXC_OLD: + 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_OLD, HILO_TO_32(addr), HILO_TO_32(addr2), volume); + break; + + // These two go together and manipulate some AUX buffers. + case CMD_UNK_09_OLD: curr_idx += 13; break; + case CMD_UNK_0A_OLD: curr_idx += 13; break; + + // TODO(delroth): figure this one out, it's used by almost every + // game I've tested so far. + case CMD_UNK_0B_OLD: curr_idx += 4; break; + + case CMD_OUTPUT_OLD: + 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), 0x8000); + break; + + case CMD_UNK_0D_OLD: curr_idx += 5; break; + + case CMD_WM_OUTPUT_OLD: + { + 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_OLD: + end = true; + break; + } + } + else + { + 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_ADD_TO_LR: + addr_hi = m_cmdlist[curr_idx++]; + addr_lo = m_cmdlist[curr_idx++]; + AddToLR(HILO_TO_32(addr)); + 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; + + // TODO(delroth): figure this one out, it's used by almost every + // game I've tested so far. + 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; + } } } } @@ -244,7 +328,8 @@ void CUCode_AXWii::GenerateVolumeRamp(u16* output, u16 vol1, u16 vol2, size_t nv } } -bool CUCode_AXWii::ExtractUpdatesFields(AXPBWii& pb, u16* num_updates, u16* updates) +bool CUCode_AXWii::ExtractUpdatesFields(AXPBWii& pb, u16* num_updates, u16* updates, + u32* updates_addr) { u16* pb_mem = (u16*)&pb; @@ -260,6 +345,8 @@ bool CUCode_AXWii::ExtractUpdatesFields(AXPBWii& pb, u16* num_updates, u16* upda u32 addr = HILO_TO_32(addr); u16* ptr = (u16*)HLEMemory_Get_Pointer(addr); + *updates_addr = addr; + // Copy the updates data and change the offset to match a PB without // updates data. u32 updates_count = num_updates[0] + num_updates[1] + num_updates[2]; @@ -276,11 +363,26 @@ bool CUCode_AXWii::ExtractUpdatesFields(AXPBWii& pb, u16* num_updates, u16* upda } // Remove the updates data from the PB - memmove(pb_mem + 41, pb_mem + 45, sizeof (pb) - 2 * 45); + memmove(pb_mem + 41, pb_mem + 46, sizeof (pb) - 2 * 46); return true; } +void CUCode_AXWii::ReinjectUpdatesFields(AXPBWii& pb, u16* num_updates, u32 updates_addr) +{ + u16* pb_mem = (u16*)&pb; + + // Make some space + memmove(pb_mem + 46, pb_mem + 41, sizeof (pb) - 2 * 46); + + // Reinsert previous values + pb_mem[41] = num_updates[0]; + pb_mem[42] = num_updates[1]; + pb_mem[43] = num_updates[2]; + pb_mem[44] = updates_addr >> 16; + pb_mem[45] = updates_addr & 0xFFFF; +} + void CUCode_AXWii::ProcessPBList(u32 pb_addr) { AXPBWii pb; @@ -315,7 +417,8 @@ void CUCode_AXWii::ProcessPBList(u32 pb_addr) u16 num_updates[3]; u16 updates[1024]; - if (ExtractUpdatesFields(pb, num_updates, updates)) + u32 updates_addr; + if (ExtractUpdatesFields(pb, num_updates, updates, &updates_addr)) { for (int curr_ms = 0; curr_ms < 3; ++curr_ms) { @@ -328,6 +431,7 @@ void CUCode_AXWii::ProcessPBList(u32 pb_addr) for (u32 i = 0; i < sizeof (buffers.ptrs) / sizeof (buffers.ptrs[0]); ++i) buffers.ptrs[i] += 32; } + ReinjectUpdatesFields(pb, num_updates, updates_addr); } else { 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 ce09b14674..e5ba319787 100644 --- a/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_AXWii.h +++ b/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_AXWii.h @@ -52,8 +52,11 @@ protected: u16 m_last_main_volume; u16 m_last_aux_volumes[3]; - // If needed, extract the updates related fields from a PB. - bool ExtractUpdatesFields(AXPBWii& pb, u16* num_updates, u16* updates); + // If needed, extract the updates related fields from a PB. We need to + // reinject them afterwards so that the correct PB typs is written to RAM. + bool ExtractUpdatesFields(AXPBWii& pb, u16* num_updates, u16* updates, + u32* updates_addr); + void ReinjectUpdatesFields(AXPBWii& pb, u16* num_updates, u32 updates_addr); // Convert a mixer_control bitfield to our internal representation for that // value. Required because that bitfield has a different meaning in some @@ -91,7 +94,30 @@ private: CMD_OUTPUT = 0x0B, CMD_UNK_0C = 0x0C, CMD_WM_OUTPUT = 0x0D, - CMD_END = 0x0E + CMD_END = 0x0E, + }; + + // A lot of these are similar to the new version, but there is an offset in + // the command ids due to the PB_ADDR command (which was removed from the + // new AXWii). + enum CmdTypeOld + { + CMD_SETUP_OLD = 0x00, + CMD_ADD_TO_LR_OLD = 0x01, + CMD_UNK_02_OLD = 0x02, + CMD_UNK_03_OLD = 0x03, + CMD_PB_ADDR_OLD = 0x04, + CMD_PROCESS_OLD = 0x05, + CMD_MIX_AUXA_OLD = 0x06, + CMD_MIX_AUXB_OLD = 0x07, + CMD_MIX_AUXC_OLD = 0x08, + CMD_UNK_09_OLD = 0x09, + CMD_UNK_0A_OLD = 0x0A, + CMD_UNK_0B_OLD = 0x0B, + CMD_OUTPUT_OLD = 0x0C, // no volume! + CMD_UNK_0D_OLD = 0x0D, + CMD_WM_OUTPUT_OLD = 0x0E, + CMD_END_OLD = 0x0F }; }; From 5c67a0bcf681e8cfb86e368e06b093b616574297 Mon Sep 17 00:00:00 2001 From: Pierre Bourdon Date: Tue, 2 Apr 2013 02:53:19 +0200 Subject: [PATCH 24/28] Partial fix for the EA Wii games having no sound - implement the second OUTPUT command in AXWii. Sound in videos and musics still don't play. --- .../Core/Src/HW/DSPHLE/UCodes/UCode_AXWii.cpp | 35 +++++++++++++------ .../Core/Src/HW/DSPHLE/UCodes/UCode_AXWii.h | 7 ++-- 2 files changed, 28 insertions(+), 14 deletions(-) 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 1e966d2d0a..8e19a63d4c 100644 --- a/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_AXWii.cpp +++ b/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_AXWii.cpp @@ -53,7 +53,7 @@ void CUCode_AXWii::HandleCommandList() u32 pb_addr = 0; // WARN_LOG(DSPHLE, "Command list:"); -// for (u32 i = 0; m_cmdlist[i] != CMD_END_OLD; ++i) +// for (u32 i = 0; m_cmdlist[i] != CMD_END; ++i) // WARN_LOG(DSPHLE, "%04x", m_cmdlist[i]); // WARN_LOG(DSPHLE, "-------------"); @@ -115,15 +115,15 @@ void CUCode_AXWii::HandleCommandList() case CMD_UNK_0B_OLD: curr_idx += 4; break; case CMD_OUTPUT_OLD: + case CMD_OUTPUT_DPL2_OLD: 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), 0x8000); + OutputSamples(HILO_TO_32(addr2), HILO_TO_32(addr), 0x8000, + cmd == CMD_OUTPUT_DPL2_OLD); break; - case CMD_UNK_0D_OLD: curr_idx += 5; break; - case CMD_WM_OUTPUT_OLD: { u32 addresses[4] = { @@ -190,16 +190,16 @@ void CUCode_AXWii::HandleCommandList() case CMD_UNK_0A: curr_idx += 4; break; case CMD_OUTPUT: + case CMD_OUTPUT_DPL2: 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); + OutputSamples(HILO_TO_32(addr2), HILO_TO_32(addr), volume, + cmd == CMD_OUTPUT_DPL2); break; - case CMD_UNK_0C: curr_idx += 5; break; - case CMD_WM_OUTPUT: { u32 addresses[4] = { @@ -499,17 +499,26 @@ void CUCode_AXWii::MixAUXSamples(int aux_id, u32 write_addr, u32 read_addr, u16 } } -void CUCode_AXWii::OutputSamples(u32 lr_addr, u32 surround_addr, u16 volume) +void CUCode_AXWii::OutputSamples(u32 lr_addr, u32 surround_addr, u16 volume, + bool upload_auxc) { u16 volume_ramp[96]; GenerateVolumeRamp(volume_ramp, m_last_main_volume, volume, 96); m_last_main_volume = volume; - int surround_buffer[3 * 32] = { 0 }; + int upload_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)); + upload_buffer[i] = Common::swap32(m_samples_surround[i]); + memcpy(HLEMemory_Get_Pointer(surround_addr), upload_buffer, sizeof (upload_buffer)); + + if (upload_auxc) + { + surround_addr += sizeof (upload_buffer); + for (u32 i = 0; i < 3 * 32; ++i) + upload_buffer[i] = Common::swap32(m_samples_auxC_left[i]); + memcpy(HLEMemory_Get_Pointer(surround_addr), upload_buffer, sizeof (upload_buffer)); + } short buffer[3 * 32 * 2]; @@ -539,6 +548,10 @@ void CUCode_AXWii::OutputSamples(u32 lr_addr, u32 surround_addr, u16 volume) } memcpy(HLEMemory_Get_Pointer(lr_addr), buffer, sizeof (buffer)); + + // There should be a DSP_SYNC message sent here. However, it looks like not + // sending it does not cause any issue, and sending it actually causes some + // sounds to go at half speed. I have no idea why. } void CUCode_AXWii::OutputWMSamples(u32* addresses) 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 e5ba319787..b6e163541d 100644 --- a/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_AXWii.h +++ b/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_AXWii.h @@ -74,7 +74,8 @@ protected: void AddToLR(u32 val_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 OutputSamples(u32 lr_addr, u32 surround_addr, u16 volume, + bool upload_auxc); void OutputWMSamples(u32* addresses); // 4 addresses private: @@ -92,7 +93,7 @@ private: CMD_UNK_09 = 0x09, CMD_UNK_0A = 0x0A, CMD_OUTPUT = 0x0B, - CMD_UNK_0C = 0x0C, + CMD_OUTPUT_DPL2 = 0x0C, CMD_WM_OUTPUT = 0x0D, CMD_END = 0x0E, }; @@ -115,7 +116,7 @@ private: CMD_UNK_0A_OLD = 0x0A, CMD_UNK_0B_OLD = 0x0B, CMD_OUTPUT_OLD = 0x0C, // no volume! - CMD_UNK_0D_OLD = 0x0D, + CMD_OUTPUT_DPL2_OLD = 0x0D, CMD_WM_OUTPUT_OLD = 0x0E, CMD_END_OLD = 0x0F }; From 74dee41b874b1509b2680806d36c1276676349e4 Mon Sep 17 00:00:00 2001 From: Pierre Bourdon Date: Tue, 2 Apr 2013 03:10:29 +0200 Subject: [PATCH 25/28] Add implementation for command 02: SUB_TO_LR --- .../Core/Core/Src/HW/DSPHLE/UCodes/UCode_AXWii.cpp | 12 +++++++----- Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_AXWii.h | 6 +++--- 2 files changed, 10 insertions(+), 8 deletions(-) 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 8e19a63d4c..810194eed9 100644 --- a/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_AXWii.cpp +++ b/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_AXWii.cpp @@ -77,12 +77,12 @@ void CUCode_AXWii::HandleCommandList() break; case CMD_ADD_TO_LR_OLD: + case CMD_SUB_TO_LR_OLD: addr_hi = m_cmdlist[curr_idx++]; addr_lo = m_cmdlist[curr_idx++]; - AddToLR(HILO_TO_32(addr)); + AddToLR(HILO_TO_32(addr), cmd == CMD_SUB_TO_LR_OLD); break; - case CMD_UNK_02_OLD: curr_idx += 2; break; case CMD_UNK_03_OLD: curr_idx += 2; break; case CMD_PB_ADDR_OLD: @@ -156,12 +156,12 @@ void CUCode_AXWii::HandleCommandList() break; case CMD_ADD_TO_LR: + case CMD_SUB_TO_LR: addr_hi = m_cmdlist[curr_idx++]; addr_lo = m_cmdlist[curr_idx++]; - AddToLR(HILO_TO_32(addr)); + AddToLR(HILO_TO_32(addr), cmd == CMD_SUB_TO_LR); break; - case CMD_UNK_02: curr_idx += 2; break; case CMD_UNK_03: curr_idx += 2; break; case CMD_PROCESS: @@ -278,12 +278,14 @@ void CUCode_AXWii::SetupProcessing(u32 init_addr) } } -void CUCode_AXWii::AddToLR(u32 val_addr) +void CUCode_AXWii::AddToLR(u32 val_addr, bool neg) { int* ptr = (int*)HLEMemory_Get_Pointer(val_addr); for (int i = 0; i < 32 * 3; ++i) { int val = (int)Common::swap32(*ptr++); + if (neg) + val = -val; m_samples_left[i] += val; m_samples_right[i] += val; 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 b6e163541d..9c49f35a81 100644 --- a/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_AXWii.h +++ b/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_AXWii.h @@ -71,7 +71,7 @@ protected: virtual void HandleCommandList(); void SetupProcessing(u32 init_addr); - void AddToLR(u32 val_addr); + void AddToLR(u32 val_addr, bool neg); 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, @@ -83,7 +83,7 @@ private: { CMD_SETUP = 0x00, CMD_ADD_TO_LR = 0x01, - CMD_UNK_02 = 0x02, + CMD_SUB_TO_LR = 0x02, CMD_UNK_03 = 0x03, CMD_PROCESS = 0x04, CMD_MIX_AUXA = 0x05, @@ -105,7 +105,7 @@ private: { CMD_SETUP_OLD = 0x00, CMD_ADD_TO_LR_OLD = 0x01, - CMD_UNK_02_OLD = 0x02, + CMD_SUB_TO_LR_OLD = 0x02, CMD_UNK_03_OLD = 0x03, CMD_PB_ADDR_OLD = 0x04, CMD_PROCESS_OLD = 0x05, From 0220fd194072f596092cde1c76be0712d80af786 Mon Sep 17 00:00:00 2001 From: Pierre Bourdon Date: Tue, 2 Apr 2013 03:23:48 +0200 Subject: [PATCH 26/28] Implemented command 03: ADD_SUB_TO_LR --- .../Core/Src/HW/DSPHLE/UCodes/UCode_AXWii.cpp | 27 +++++++++++++++++-- .../Core/Src/HW/DSPHLE/UCodes/UCode_AXWii.h | 5 ++-- 2 files changed, 28 insertions(+), 4 deletions(-) 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 810194eed9..ec640ca038 100644 --- a/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_AXWii.cpp +++ b/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_AXWii.cpp @@ -83,7 +83,11 @@ void CUCode_AXWii::HandleCommandList() AddToLR(HILO_TO_32(addr), cmd == CMD_SUB_TO_LR_OLD); break; - case CMD_UNK_03_OLD: curr_idx += 2; break; + case CMD_ADD_SUB_TO_LR_OLD: + addr_hi = m_cmdlist[curr_idx++]; + addr_lo = m_cmdlist[curr_idx++]; + AddSubToLR(HILO_TO_32(addr)); + break; case CMD_PB_ADDR_OLD: addr_hi = m_cmdlist[curr_idx++]; @@ -162,7 +166,11 @@ void CUCode_AXWii::HandleCommandList() AddToLR(HILO_TO_32(addr), cmd == CMD_SUB_TO_LR); break; - case CMD_UNK_03: curr_idx += 2; break; + case CMD_ADD_SUB_TO_LR: + addr_hi = m_cmdlist[curr_idx++]; + addr_lo = m_cmdlist[curr_idx++]; + AddSubToLR(HILO_TO_32(addr)); + break; case CMD_PROCESS: addr_hi = m_cmdlist[curr_idx++]; @@ -292,6 +300,21 @@ void CUCode_AXWii::AddToLR(u32 val_addr, bool neg) } } +void CUCode_AXWii::AddSubToLR(u32 val_addr) +{ + int* ptr = (int*)HLEMemory_Get_Pointer(val_addr); + for (int i = 0; i < 32 * 3; ++i) + { + int val = (int)Common::swap32(*ptr++); + m_samples_left[i] += val; + } + for (int i = 0; i < 32 * 3; ++i) + { + int val = (int)Common::swap32(*ptr++); + m_samples_right[i] -= val; + } +} + AXMixControl CUCode_AXWii::ConvertMixerControl(u32 mixer_control) { u32 ret = 0; 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 9c49f35a81..8952961004 100644 --- a/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_AXWii.h +++ b/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_AXWii.h @@ -72,6 +72,7 @@ protected: void SetupProcessing(u32 init_addr); void AddToLR(u32 val_addr, bool neg); + void AddSubToLR(u32 val_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, @@ -84,7 +85,7 @@ private: CMD_SETUP = 0x00, CMD_ADD_TO_LR = 0x01, CMD_SUB_TO_LR = 0x02, - CMD_UNK_03 = 0x03, + CMD_ADD_SUB_TO_LR = 0x03, CMD_PROCESS = 0x04, CMD_MIX_AUXA = 0x05, CMD_MIX_AUXB = 0x06, @@ -106,7 +107,7 @@ private: CMD_SETUP_OLD = 0x00, CMD_ADD_TO_LR_OLD = 0x01, CMD_SUB_TO_LR_OLD = 0x02, - CMD_UNK_03_OLD = 0x03, + CMD_ADD_SUB_TO_LR_OLD = 0x03, CMD_PB_ADDR_OLD = 0x04, CMD_PROCESS_OLD = 0x05, CMD_MIX_AUXA_OLD = 0x06, From 6e708005dfd673e305bc568fcc8e4b4e5ac54adf Mon Sep 17 00:00:00 2001 From: Pierre Bourdon Date: Tue, 2 Apr 2013 04:12:17 +0200 Subject: [PATCH 27/28] Implement AXWii commands 08/09, aka. "upload AUXA/AUXB and use it as a temp buffer to mix to MAIN L/R/S and AUXC L", aka. "what the fuck were they thinking?!" --- .../Core/Src/HW/DSPHLE/UCodes/UCode_AXWii.cpp | 82 +++++++++++++++++-- .../Core/Src/HW/DSPHLE/UCodes/UCode_AXWii.h | 9 +- 2 files changed, 81 insertions(+), 10 deletions(-) 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 ec640ca038..41d782a01a 100644 --- a/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_AXWii.cpp +++ b/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_AXWii.cpp @@ -110,9 +110,22 @@ void CUCode_AXWii::HandleCommandList() MixAUXSamples(cmd - CMD_MIX_AUXA_OLD, HILO_TO_32(addr), HILO_TO_32(addr2), volume); break; - // These two go together and manipulate some AUX buffers. - case CMD_UNK_09_OLD: curr_idx += 13; break; - case CMD_UNK_0A_OLD: curr_idx += 13; break; + case CMD_UPL_AUXA_MIX_LRSC_OLD: + case CMD_UPL_AUXB_MIX_LRSC_OLD: + { + volume = m_cmdlist[curr_idx++]; + u32 addresses[6] = { + (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], + (u32)(m_cmdlist[curr_idx + 8] << 16) | m_cmdlist[curr_idx + 9], + (u32)(m_cmdlist[curr_idx + 10] << 16) | m_cmdlist[curr_idx + 11], + }; + curr_idx += 12; + UploadAUXMixLRSC(cmd == CMD_UPL_AUXB_MIX_LRSC_OLD, addresses, volume); + break; + } // TODO(delroth): figure this one out, it's used by almost every // game I've tested so far. @@ -189,9 +202,22 @@ void CUCode_AXWii::HandleCommandList() 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_UPL_AUXA_MIX_LRSC: + case CMD_UPL_AUXB_MIX_LRSC: + { + volume = m_cmdlist[curr_idx++]; + u32 addresses[6] = { + (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], + (u32)(m_cmdlist[curr_idx + 8] << 16) | m_cmdlist[curr_idx + 9], + (u32)(m_cmdlist[curr_idx + 10] << 16) | m_cmdlist[curr_idx + 11], + }; + curr_idx += 12; + UploadAUXMixLRSC(cmd == CMD_UPL_AUXB_MIX_LRSC, addresses, volume); + break; + } // TODO(delroth): figure this one out, it's used by almost every // game I've tested so far. @@ -524,6 +550,50 @@ void CUCode_AXWii::MixAUXSamples(int aux_id, u32 write_addr, u32 read_addr, u16 } } +void CUCode_AXWii::UploadAUXMixLRSC(int aux_id, u32* addresses, u16 volume) +{ + int* aux_left = aux_id ? m_samples_auxB_left : m_samples_auxA_left; + int* aux_right = aux_id ? m_samples_auxB_right : m_samples_auxA_right; + int* aux_surround = aux_id ? m_samples_auxB_surround : m_samples_auxA_surround; + int* auxc_buffer = aux_id ? m_samples_auxC_surround : m_samples_auxC_right; + + int* upload_ptr = (int*)HLEMemory_Get_Pointer(addresses[0]); + for (u32 i = 0; i < 96; ++i) + *upload_ptr++ = Common::swap32(aux_left[i]); + for (u32 i = 0; i < 96; ++i) + *upload_ptr++ = Common::swap32(aux_right[i]); + for (u32 i = 0; i < 96; ++i) + *upload_ptr++ = Common::swap32(aux_surround[i]); + + upload_ptr = (int*)HLEMemory_Get_Pointer(addresses[1]); + for (u32 i = 0; i < 96; ++i) + *upload_ptr++ = Common::swap32(auxc_buffer[i]); + + u16 volume_ramp[96]; + GenerateVolumeRamp(volume_ramp, m_last_aux_volumes[aux_id], volume, 96); + m_last_aux_volumes[aux_id] = volume; + + int* mix_dest[4] = { + m_samples_left, + m_samples_right, + m_samples_surround, + m_samples_auxC_left + }; + for (u32 mix_i = 0; mix_i < 4; ++mix_i) + { + int* dl_ptr = (int*)HLEMemory_Get_Pointer(addresses[2 + mix_i]); + for (u32 i = 0; i < 96; ++i) + aux_left[i] = Common::swap32(dl_ptr[i]); + + for (u32 i = 0; i < 96; ++i) + { + s64 sample = (s64)(s32)aux_left[i]; + sample *= volume_ramp[i]; + mix_dest[mix_i][i] += (s32)(sample >> 15); + } + } +} + void CUCode_AXWii::OutputSamples(u32 lr_addr, u32 surround_addr, u16 volume, bool upload_auxc) { 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 8952961004..6e4eb7ad4e 100644 --- a/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_AXWii.h +++ b/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_AXWii.h @@ -75,6 +75,7 @@ protected: void AddSubToLR(u32 val_addr); void ProcessPBList(u32 pb_addr); void MixAUXSamples(int aux_id, u32 write_addr, u32 read_addr, u16 volume); + void UploadAUXMixLRSC(int aux_id, u32* addresses, u16 volume); void OutputSamples(u32 lr_addr, u32 surround_addr, u16 volume, bool upload_auxc); void OutputWMSamples(u32* addresses); // 4 addresses @@ -90,8 +91,8 @@ private: CMD_MIX_AUXA = 0x05, CMD_MIX_AUXB = 0x06, CMD_MIX_AUXC = 0x07, - CMD_UNK_08 = 0x08, - CMD_UNK_09 = 0x09, + CMD_UPL_AUXA_MIX_LRSC = 0x08, + CMD_UPL_AUXB_MIX_LRSC = 0x09, CMD_UNK_0A = 0x0A, CMD_OUTPUT = 0x0B, CMD_OUTPUT_DPL2 = 0x0C, @@ -113,8 +114,8 @@ private: CMD_MIX_AUXA_OLD = 0x06, CMD_MIX_AUXB_OLD = 0x07, CMD_MIX_AUXC_OLD = 0x08, - CMD_UNK_09_OLD = 0x09, - CMD_UNK_0A_OLD = 0x0A, + CMD_UPL_AUXA_MIX_LRSC_OLD = 0x09, + CMD_UPL_AUXB_MIX_LRSC_OLD = 0x0a, CMD_UNK_0B_OLD = 0x0B, CMD_OUTPUT_OLD = 0x0C, // no volume! CMD_OUTPUT_DPL2_OLD = 0x0D, From 986013729142651df2f511d60f2877104589666e Mon Sep 17 00:00:00 2001 From: Pierre Bourdon Date: Tue, 2 Apr 2013 20:42:07 +0200 Subject: [PATCH 28/28] Initialize the audiocommon mixer on the first write to DSPCR, even if DSP is not enabled. Fixes issue 6192. --- Source/Core/Core/Src/HW/DSPHLE/DSPHLE.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/Core/Core/Src/HW/DSPHLE/DSPHLE.cpp b/Source/Core/Core/Src/HW/DSPHLE/DSPHLE.cpp index 6d2a1f9097..0232605f91 100644 --- a/Source/Core/Core/Src/HW/DSPHLE/DSPHLE.cpp +++ b/Source/Core/Core/Src/HW/DSPHLE/DSPHLE.cpp @@ -273,7 +273,7 @@ u16 DSPHLE::DSP_WriteControlRegister(unsigned short _Value) UDSPControl Temp(_Value); if (!m_InitMixer) { - if (!Temp.DSPHalt && Temp.DSPInit) + if (!Temp.DSPHalt) { InitMixer(); }