From 319591674482f447ae86dfa5d1172c64f4d12731 Mon Sep 17 00:00:00 2001 From: Pierre Bourdon Date: Wed, 14 Nov 2012 05:08:04 +0100 Subject: [PATCH 01/44] Basic infrastructure for the new AX HLE, currently not doing anything besides answering mails --- Source/Core/Core/CMakeLists.txt | 1 + .../Core/Src/HW/DSPHLE/UCodes/UCode_NewAX.cpp | 106 ++++++++++++++++++ .../Core/Src/HW/DSPHLE/UCodes/UCode_NewAX.h | 51 +++++++++ .../Core/Core/Src/HW/DSPHLE/UCodes/UCodes.cpp | 3 +- 4 files changed, 160 insertions(+), 1 deletion(-) create mode 100644 Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_NewAX.cpp create mode 100644 Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_NewAX.h diff --git a/Source/Core/Core/CMakeLists.txt b/Source/Core/Core/CMakeLists.txt index 734d966781..3182d248f8 100644 --- a/Source/Core/Core/CMakeLists.txt +++ b/Source/Core/Core/CMakeLists.txt @@ -74,6 +74,7 @@ set(SRCS Src/ActionReplay.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_NewAX.cpp Src/HW/DSPHLE/UCodes/UCode_ROM.cpp Src/HW/DSPHLE/UCodes/UCodes.cpp Src/HW/DSPHLE/UCodes/UCode_GBA.cpp diff --git a/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_NewAX.cpp b/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_NewAX.cpp new file mode 100644 index 0000000000..28ef93a9bb --- /dev/null +++ b/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_NewAX.cpp @@ -0,0 +1,106 @@ +// 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/ + +#include "UCode_NewAX.h" +#include "../../DSP.h" + +CUCode_NewAX::CUCode_NewAX(DSPHLE* dsp_hle, u32 crc) + : IUCode(dsp_hle, crc) +{ + m_rMailHandler.PushMail(DSP_INIT); + DSP::GenerateDSPInterruptFromDSPEmu(DSP::INT_DSP); +} + +CUCode_NewAX::~CUCode_NewAX() +{ + m_rMailHandler.Clear(); +} + +void CUCode_NewAX::HandleCommandList(u32 addr) +{ + // Signal end of processing + m_rMailHandler.PushMail(DSP_YIELD); + DSP::GenerateDSPInterruptFromDSPEmu(DSP::INT_DSP); +} + +void CUCode_NewAX::HandleMail(u32 mail) +{ + // Indicates if the next message is a command list address. + static bool next_is_cmdlist = false; + bool set_next_is_cmdlist = false; + + if (next_is_cmdlist) + { + HandleCommandList(mail); + } + else if (m_UploadSetupInProgress) + { + PrepareBootUCode(mail); + } + else if (mail == MAIL_RESUME) + { + // Acknowledge the resume request + m_rMailHandler.PushMail(DSP_RESUME); + DSP::GenerateDSPInterruptFromDSPEmu(DSP::INT_DSP); + } + else if (mail == MAIL_NEWUCODE) + { + soundStream->GetMixer()->SetHLEReady(false); + m_UploadSetupInProgress = true; + } + else if (mail == MAIL_RESET) + { + m_DSPHLE->SetUCode(UCODE_ROM); + } + else if (mail == MAIL_CONTINUE) + { + // We don't have to do anything here - the CPU does not wait for a ACK + // and sends a cmdlist mail just after. + } + else if ((mail & MAIL_CMDLIST_MASK) == MAIL_CMDLIST) + { + // A command list address is going to be sent next. + set_next_is_cmdlist = true; + } + else + { + ERROR_LOG(DSPHLE, "Unknown mail sent to AX::HandleMail: %08x", mail); + } + + next_is_cmdlist = set_next_is_cmdlist; +} + +void CUCode_NewAX::MixAdd(short* out_buffer, int nsamples) +{ + // nsamples * 2 for left and right audio channel + memset(out_buffer, 0, nsamples * 2 * sizeof (short)); +} + +void CUCode_NewAX::Update(int cycles) +{ + // Used for UCode switching. + if (NeedsResumeMail()) + { + m_rMailHandler.PushMail(DSP_RESUME); + DSP::GenerateDSPInterruptFromDSPEmu(DSP::INT_DSP); + } +} + +void CUCode_NewAX::DoState(PointerWrap& p) +{ + DoStateShared(p); +} diff --git a/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_NewAX.h b/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_NewAX.h new file mode 100644 index 0000000000..769f58d9d1 --- /dev/null +++ b/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_NewAX.h @@ -0,0 +1,51 @@ +// 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_NEWAX_H +#define _UCODE_NEWAX_H + +#include "UCodes.h" +#include "UCode_AXStructs.h" + +class CUCode_NewAX : public IUCode +{ +public: + CUCode_NewAX(DSPHLE* dsp_hle, u32 crc); + virtual ~CUCode_NewAX(); + + void HandleMail(u32 mail); + void MixAdd(short* out_buffer, int nsamples); + void Update(int cycles); + void DoState(PointerWrap& p); + +private: + enum MailType + { + MAIL_RESUME = 0xCDD10000, + MAIL_NEWUCODE = 0xCDD10001, + MAIL_RESET = 0xCDD10002, + MAIL_CONTINUE = 0xCDD10003, + + // CPU sends 0xBABE0000 | cmdlist_size to the DSP + MAIL_CMDLIST = 0xBABE0000, + MAIL_CMDLIST_MASK = 0xFFFF0000 + }; + + void HandleCommandList(u32 addr); +}; + +#endif // !_UCODE_NEWAX_H diff --git a/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCodes.cpp b/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCodes.cpp index c04bf41403..58e6198205 100644 --- a/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCodes.cpp +++ b/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCodes.cpp @@ -19,6 +19,7 @@ #include "UCode_AX.h" #include "UCode_AXWii.h" +#include "UCode_NewAX.h" #include "UCode_Zelda.h" #include "UCode_ROM.h" #include "UCode_CARD.h" @@ -57,7 +58,7 @@ IUCode* UCodeFactory(u32 _CRC, DSPHLE *dsp_hle, bool bWii) case 0xe2136399: // billy hatcher, dragonballz, mario party 5, TMNT, ava1080 case 0x3389a79e: // MP1/MP2 Wii (Metroid Prime Trilogy) INFO_LOG(DSPHLE, "CRC %08x: AX ucode chosen", _CRC); - return new CUCode_AX(dsp_hle, _CRC); + return new CUCode_NewAX(dsp_hle, _CRC); case 0x6ba3b3ea: // IPL - PAL case 0x24b22038: // IPL - NTSC/NTSC-JAP From 9e813502acf9d7240c648275277689007a637b16 Mon Sep 17 00:00:00 2001 From: Pierre Bourdon Date: Wed, 14 Nov 2012 05:33:04 +0100 Subject: [PATCH 02/44] Create a new thread for AX and make it handle the command lists --- .../Core/Src/HW/DSPHLE/UCodes/UCode_NewAX.cpp | 51 +++++++++++++++++-- .../Core/Src/HW/DSPHLE/UCodes/UCode_NewAX.h | 16 ++++++ 2 files changed, 63 insertions(+), 4 deletions(-) diff --git a/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_NewAX.cpp b/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_NewAX.cpp index 28ef93a9bb..117dac63cb 100644 --- a/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_NewAX.cpp +++ b/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_NewAX.cpp @@ -20,6 +20,8 @@ CUCode_NewAX::CUCode_NewAX(DSPHLE* dsp_hle, u32 crc) : IUCode(dsp_hle, crc) + , m_cmdlist_addr(0) + , m_axthread(&CUCode_NewAX::AXThread, this) { m_rMailHandler.PushMail(DSP_INIT); DSP::GenerateDSPInterruptFromDSPEmu(DSP::INT_DSP); @@ -27,14 +29,46 @@ CUCode_NewAX::CUCode_NewAX(DSPHLE* dsp_hle, u32 crc) CUCode_NewAX::~CUCode_NewAX() { + m_cmdlist_addr = (u32)-1; // Special value to signal end + NotifyAXThread(); + m_axthread.join(); + m_rMailHandler.Clear(); } +void CUCode_NewAX::AXThread() +{ + while (true) + { + { + std::unique_lock lk(m_cmdlist_mutex); + while (m_cmdlist_addr == 0) + m_cmdlist_cv.wait(lk); + } + + if (m_cmdlist_addr == (u32)-1) // End of thread signal + break; + + m_processing.lock(); + HandleCommandList(m_cmdlist_addr); + m_cmdlist_addr = 0; + + // Signal end of processing + m_rMailHandler.PushMail(DSP_YIELD); + DSP::GenerateDSPInterruptFromDSPEmu(DSP::INT_DSP); + m_processing.unlock(); + } +} + +void CUCode_NewAX::NotifyAXThread() +{ + std::unique_lock lk(m_cmdlist_mutex); + m_cmdlist_cv.notify_one(); +} + void CUCode_NewAX::HandleCommandList(u32 addr) { - // Signal end of processing - m_rMailHandler.PushMail(DSP_YIELD); - DSP::GenerateDSPInterruptFromDSPEmu(DSP::INT_DSP); + WARN_LOG(DSPHLE, "TODO: HandleCommandList(%08x)", addr); } void CUCode_NewAX::HandleMail(u32 mail) @@ -43,9 +77,15 @@ void CUCode_NewAX::HandleMail(u32 mail) static bool next_is_cmdlist = false; bool set_next_is_cmdlist = false; + // Wait for DSP processing to be done before answering any mail. This is + // safe to do because it matches what the DSP does on real hardware: there + // is no interrupt when a mail from CPU is received. + m_processing.lock(); + if (next_is_cmdlist) { - HandleCommandList(mail); + m_cmdlist_addr = mail; + NotifyAXThread(); } else if (m_UploadSetupInProgress) { @@ -81,6 +121,7 @@ void CUCode_NewAX::HandleMail(u32 mail) ERROR_LOG(DSPHLE, "Unknown mail sent to AX::HandleMail: %08x", mail); } + m_processing.unlock(); next_is_cmdlist = set_next_is_cmdlist; } @@ -102,5 +143,7 @@ void CUCode_NewAX::Update(int cycles) void CUCode_NewAX::DoState(PointerWrap& p) { + std::lock_guard lk(m_processing); + DoStateShared(p); } diff --git a/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_NewAX.h b/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_NewAX.h index 769f58d9d1..a1f881bd38 100644 --- a/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_NewAX.h +++ b/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_NewAX.h @@ -45,6 +45,22 @@ private: MAIL_CMDLIST_MASK = 0xFFFF0000 }; + // Volatile because it's set by HandleMail and accessed in + // HandleCommandList, which are running in two different threads. + volatile u32 m_cmdlist_addr; + + std::thread m_axthread; + + // Sync objects + std::mutex m_processing; + std::condition_variable m_cmdlist_cv; + std::mutex m_cmdlist_mutex; + + // Send a notification to the AX thread to tell him a new cmdlist addr is + // available for processing. + void NotifyAXThread(); + + void AXThread(); void HandleCommandList(u32 addr); }; From 7535c6d9038684c2a27f7307dd4f2439829b5a2f Mon Sep 17 00:00:00 2001 From: Pierre Bourdon Date: Wed, 14 Nov 2012 06:15:55 +0100 Subject: [PATCH 03/44] Parse the AX command list in the AX thread --- .../Core/Src/HW/DSPHLE/UCodes/UCode_NewAX.cpp | 97 +++++++++++++++++-- .../Core/Src/HW/DSPHLE/UCodes/UCode_NewAX.h | 35 ++++++- 2 files changed, 119 insertions(+), 13 deletions(-) diff --git a/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_NewAX.cpp b/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_NewAX.cpp index 117dac63cb..31799f2675 100644 --- a/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_NewAX.cpp +++ b/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_NewAX.cpp @@ -20,7 +20,7 @@ CUCode_NewAX::CUCode_NewAX(DSPHLE* dsp_hle, u32 crc) : IUCode(dsp_hle, crc) - , m_cmdlist_addr(0) + , m_cmdlist_size(0) , m_axthread(&CUCode_NewAX::AXThread, this) { m_rMailHandler.PushMail(DSP_INIT); @@ -29,7 +29,7 @@ CUCode_NewAX::CUCode_NewAX(DSPHLE* dsp_hle, u32 crc) CUCode_NewAX::~CUCode_NewAX() { - m_cmdlist_addr = (u32)-1; // Special value to signal end + m_cmdlist_size = (u16)-1; // Special value to signal end NotifyAXThread(); m_axthread.join(); @@ -42,16 +42,16 @@ void CUCode_NewAX::AXThread() { { std::unique_lock lk(m_cmdlist_mutex); - while (m_cmdlist_addr == 0) + while (m_cmdlist_size == 0) m_cmdlist_cv.wait(lk); } - if (m_cmdlist_addr == (u32)-1) // End of thread signal + if (m_cmdlist_size == (u16)-1) // End of thread signal break; m_processing.lock(); - HandleCommandList(m_cmdlist_addr); - m_cmdlist_addr = 0; + HandleCommandList(); + m_cmdlist_size = 0; // Signal end of processing m_rMailHandler.PushMail(DSP_YIELD); @@ -66,15 +66,78 @@ void CUCode_NewAX::NotifyAXThread() m_cmdlist_cv.notify_one(); } -void CUCode_NewAX::HandleCommandList(u32 addr) +void CUCode_NewAX::HandleCommandList() { - WARN_LOG(DSPHLE, "TODO: HandleCommandList(%08x)", addr); + u16 pb_addr_hi, pb_addr_lo; + u32 pb_addr = 0; + + u32 curr_idx = 0; + bool end = false; + while (!end) + { + u16 cmd = m_cmdlist[curr_idx++]; + + switch (cmd) + { + + // A lot of these commands are unknown, or unused in this AX HLE. + // We still need to skip their arguments using "curr_idx += N". + + case CMD_STUDIO_ADDR: curr_idx += 2; break; + case CMD_UNK_01: curr_idx += 5; break; + + case CMD_PB_ADDR: + pb_addr_hi = m_cmdlist[curr_idx++]; + pb_addr_lo = m_cmdlist[curr_idx++]; + pb_addr = (pb_addr_hi << 16) | pb_addr_lo; + + WARN_LOG(DSPHLE, "PB addr: %08x", pb_addr); + break; + + case CMD_PROCESS: + ProcessPB(pb_addr); + break; + + case CMD_UNK_04: curr_idx += 4; break; + case CMD_UNK_05: curr_idx += 4; break; + case CMD_UNK_06: curr_idx += 2; break; + case CMD_SBUFFER_ADDR: curr_idx += 2; break; + case CMD_UNK_08: curr_idx += 10; break; // TODO: check + case CMD_UNK_09: curr_idx += 2; break; + case CMD_COMPRESSOR_TABLE_ADDR: curr_idx += 2; break; + case CMD_UNK_0B: break; // TODO: check other versions + case CMD_UNK_0C: break; // TODO: check other versions + case CMD_UNK_0D: curr_idx += 2; break; + case CMD_UNK_0E: curr_idx += 4; break; + + case CMD_END: + end = true; + break; + + case CMD_UNK_10: curr_idx += 4; break; + case CMD_UNK_11: curr_idx += 2; break; + case CMD_UNK_12: curr_idx += 1; break; + case CMD_UNK_13: curr_idx += 12; break; + + default: + ERROR_LOG(DSPHLE, "Unknown command in AX cmdlist: %04x", cmd); + end = true; + break; + } + } +} + +void CUCode_NewAX::ProcessPB(u32 pb_addr) +{ + NOTICE_LOG(DSPHLE, "TODO: process pb %08x", pb_addr); } void CUCode_NewAX::HandleMail(u32 mail) { // Indicates if the next message is a command list address. static bool next_is_cmdlist = false; + static u16 cmdlist_size = 0; + bool set_next_is_cmdlist = false; // Wait for DSP processing to be done before answering any mail. This is @@ -84,7 +147,7 @@ void CUCode_NewAX::HandleMail(u32 mail) if (next_is_cmdlist) { - m_cmdlist_addr = mail; + CopyCmdList(mail, cmdlist_size); NotifyAXThread(); } else if (m_UploadSetupInProgress) @@ -97,7 +160,7 @@ void CUCode_NewAX::HandleMail(u32 mail) m_rMailHandler.PushMail(DSP_RESUME); DSP::GenerateDSPInterruptFromDSPEmu(DSP::INT_DSP); } - else if (mail == MAIL_NEWUCODE) + else if (mail == MAIL_NEW_UCODE) { soundStream->GetMixer()->SetHLEReady(false); m_UploadSetupInProgress = true; @@ -115,6 +178,7 @@ void CUCode_NewAX::HandleMail(u32 mail) { // A command list address is going to be sent next. set_next_is_cmdlist = true; + cmdlist_size = (u16)(mail & ~MAIL_CMDLIST_MASK); } else { @@ -125,6 +189,19 @@ void CUCode_NewAX::HandleMail(u32 mail) next_is_cmdlist = set_next_is_cmdlist; } +void CUCode_NewAX::CopyCmdList(u32 addr, u16 size) +{ + if (size >= (sizeof (m_cmdlist) / sizeof (u16))) + { + ERROR_LOG(DSPHLE, "Command list at %08x is too large: size=%d", addr, size); + return; + } + + for (u32 i = 0; i < size; ++i, addr += 2) + m_cmdlist[i] = HLEMemory_Read_U16(addr); + m_cmdlist_size = size; +} + void CUCode_NewAX::MixAdd(short* out_buffer, int nsamples) { // nsamples * 2 for left and right audio channel diff --git a/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_NewAX.h b/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_NewAX.h index a1f881bd38..92184fe38f 100644 --- a/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_NewAX.h +++ b/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_NewAX.h @@ -36,7 +36,7 @@ private: enum MailType { MAIL_RESUME = 0xCDD10000, - MAIL_NEWUCODE = 0xCDD10001, + MAIL_NEW_UCODE = 0xCDD10001, MAIL_RESET = 0xCDD10002, MAIL_CONTINUE = 0xCDD10003, @@ -45,9 +45,34 @@ private: MAIL_CMDLIST_MASK = 0xFFFF0000 }; + enum CmdType + { + CMD_STUDIO_ADDR = 0x00, + CMD_UNK_01 = 0x01, + CMD_PB_ADDR = 0x02, + CMD_PROCESS = 0x03, + CMD_UNK_04 = 0x04, + CMD_UNK_05 = 0x05, + CMD_UNK_06 = 0x06, + CMD_SBUFFER_ADDR = 0x07, + CMD_UNK_08 = 0x08, + CMD_UNK_09 = 0x09, + CMD_COMPRESSOR_TABLE_ADDR = 0x0A, + CMD_UNK_0B = 0x0B, + CMD_UNK_0C = 0x0C, + CMD_UNK_0D = 0x0D, + CMD_UNK_0E = 0x0E, + CMD_END = 0x0F, + CMD_UNK_10 = 0x10, + CMD_UNK_11 = 0x11, + CMD_UNK_12 = 0x12, + CMD_UNK_13 = 0x13, + }; + // Volatile because it's set by HandleMail and accessed in // HandleCommandList, which are running in two different threads. - volatile u32 m_cmdlist_addr; + volatile u16 m_cmdlist[512]; + volatile u32 m_cmdlist_size; std::thread m_axthread; @@ -56,12 +81,16 @@ private: std::condition_variable m_cmdlist_cv; std::mutex m_cmdlist_mutex; + // Copy a command list from memory to our temp buffer + void CopyCmdList(u32 addr, u16 size); + // Send a notification to the AX thread to tell him a new cmdlist addr is // available for processing. void NotifyAXThread(); void AXThread(); - void HandleCommandList(u32 addr); + void HandleCommandList(); + void ProcessPB(u32 pb_addr); }; #endif // !_UCODE_NEWAX_H From 18f3630af50ce5ec5e0be97d7634daaf32d776bb Mon Sep 17 00:00:00 2001 From: Pierre Bourdon Date: Wed, 14 Nov 2012 06:53:36 +0100 Subject: [PATCH 04/44] PBs processing is now done, just need the output to be able to test --- .../Core/Src/HW/DSPHLE/UCodes/UCode_NewAX.cpp | 116 +++++++++++++++++- .../Core/Src/HW/DSPHLE/UCodes/UCode_NewAX.h | 2 +- 2 files changed, 114 insertions(+), 4 deletions(-) diff --git a/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_NewAX.cpp b/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_NewAX.cpp index 31799f2675..e4a05c42ba 100644 --- a/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_NewAX.cpp +++ b/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_NewAX.cpp @@ -16,6 +16,7 @@ // http://code.google.com/p/dolphin-emu/ #include "UCode_NewAX.h" +#include "UCode_AX_Voice.h" #include "../../DSP.h" CUCode_NewAX::CUCode_NewAX(DSPHLE* dsp_hle, u32 crc) @@ -95,7 +96,7 @@ void CUCode_NewAX::HandleCommandList() break; case CMD_PROCESS: - ProcessPB(pb_addr); + ProcessPBList(pb_addr); break; case CMD_UNK_04: curr_idx += 4; break; @@ -127,9 +128,118 @@ void CUCode_NewAX::HandleCommandList() } } -void CUCode_NewAX::ProcessPB(u32 pb_addr) +// From old UCode_AX.cpp. +static void VoiceHacks(AXPB &pb) { - NOTICE_LOG(DSPHLE, "TODO: process pb %08x", pb_addr); + // get necessary values + const u32 sampleEnd = (pb.audio_addr.end_addr_hi << 16) | pb.audio_addr.end_addr_lo; + const u32 loopPos = (pb.audio_addr.loop_addr_hi << 16) | pb.audio_addr.loop_addr_lo; + // const u32 updaddr = (u32)(pb.updates.data_hi << 16) | pb.updates.data_lo; + // const u16 updpar = HLEMemory_Read_U16(updaddr); + // const u16 upddata = HLEMemory_Read_U16(updaddr + 2); + + // ======================================================================================= + /* Fix problems introduced with the SSBM fix. Sometimes when a music stream ended sampleEnd + would end up outside of bounds while the block was still playing resulting in noise + a strange noise. This should take care of that. + */ + if ((sampleEnd > (0x017fffff * 2) || loopPos > (0x017fffff * 2))) // ARAM bounds in nibbles + { + pb.running = 0; + + // also reset all values if it makes any difference + pb.audio_addr.cur_addr_hi = 0; pb.audio_addr.cur_addr_lo = 0; + pb.audio_addr.end_addr_hi = 0; pb.audio_addr.end_addr_lo = 0; + pb.audio_addr.loop_addr_hi = 0; pb.audio_addr.loop_addr_lo = 0; + + pb.src.cur_addr_frac = 0; pb.src.ratio_hi = 0; pb.src.ratio_lo = 0; + pb.adpcm.pred_scale = 0; pb.adpcm.yn1 = 0; pb.adpcm.yn2 = 0; + + pb.audio_addr.looping = 0; + pb.adpcm_loop_info.pred_scale = 0; + pb.adpcm_loop_info.yn1 = 0; pb.adpcm_loop_info.yn2 = 0; + } + + /* + // the fact that no settings are reset (except running) after a SSBM type music stream or another + looping block (for example in Battle Stadium DON) has ended could cause loud garbled sound to be + played from one or more blocks. Perhaps it was in conjunction with the old sequenced music fix below, + I'm not sure. This was an attempt to prevent that anyway by resetting all. But I'm not sure if this + is needed anymore. Please try to play SSBM without it and see if it works anyway. + */ + if ( + // detect blocks that have recently been running that we should reset + pb.running == 0 && pb.audio_addr.looping == 1 + //pb.running == 0 && pb.adpcm_loop_info.pred_scale + + // this prevents us from ruining sequenced music blocks, may not be needed + /* + && !(pb.updates.num_updates[0] || pb.updates.num_updates[1] || pb.updates.num_updates[2] + || pb.updates.num_updates[3] || pb.updates.num_updates[4]) + */ + //&& !(updpar || upddata) + + && pb.mixer_control == 0 // only use this in SSBM + ) + { + // reset the detection values + pb.audio_addr.looping = 0; + pb.adpcm_loop_info.pred_scale = 0; + pb.adpcm_loop_info.yn1 = 0; pb.adpcm_loop_info.yn2 = 0; + + //pb.audio_addr.cur_addr_hi = 0; pb.audio_addr.cur_addr_lo = 0; + //pb.audio_addr.end_addr_hi = 0; pb.audio_addr.end_addr_lo = 0; + //pb.audio_addr.loop_addr_hi = 0; pb.audio_addr.loop_addr_lo = 0; + + //pb.src.cur_addr_frac = 0; PBs[i].src.ratio_hi = 0; PBs[i].src.ratio_lo = 0; + //pb.adpcm.pred_scale = 0; pb.adpcm.yn1 = 0; pb.adpcm.yn2 = 0; + } +} + +static void ApplyUpdatesForMs(AXPB& pb, int curr_ms) +{ + u32 start_idx = 0; + for (int i = 0; i < curr_ms; ++i) + start_idx += pb.updates.num_updates[i]; + + u32 update_addr = (pb.updates.data_hi << 16) | pb.updates.data_lo; + for (u32 i = start_idx; i < start_idx + pb.updates.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*)&pb)[update_off] = update_val; + } +} + +void CUCode_NewAX::ProcessPBList(u32 pb_addr) +{ + static int tmp_mix_buffer_left[5 * 32], tmp_mix_buffer_right[5 * 32]; + + AXPB pb; + + while (pb_addr) + { + if (!ReadPB(pb_addr, pb)) + break; + + for (int curr_ms = 0; curr_ms < 5; ++curr_ms) + { + ApplyUpdatesForMs(pb, curr_ms); + + // TODO: is that still needed? + if (m_CRC != 0x3389a79e) + VoiceHacks(pb); + + MixAddVoice(pb, tmp_mix_buffer_left + 32 * curr_ms, + tmp_mix_buffer_right + 32 * curr_ms, 32); + } + + WritePB(pb_addr, pb); + pb_addr = (pb.next_pb_hi << 16) | pb.next_pb_lo; + } + + // TODO: write the 5ms back to a buffer the audio interface can read from } void CUCode_NewAX::HandleMail(u32 mail) diff --git a/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_NewAX.h b/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_NewAX.h index 92184fe38f..70a06a7408 100644 --- a/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_NewAX.h +++ b/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_NewAX.h @@ -90,7 +90,7 @@ private: void AXThread(); void HandleCommandList(); - void ProcessPB(u32 pb_addr); + void ProcessPBList(u32 pb_addr); }; #endif // !_UCODE_NEWAX_H From 0b275c20af99ca601616d350e7cebf407470aea2 Mon Sep 17 00:00:00 2001 From: Pierre Bourdon Date: Wed, 14 Nov 2012 11:20:54 +0100 Subject: [PATCH 05/44] Added output support to NewAX. Now working fine with Tales of Symphonia. --- .../Core/Src/HW/DSPHLE/UCodes/UCode_NewAX.cpp | 92 +++++++++++++++---- .../Core/Src/HW/DSPHLE/UCodes/UCode_NewAX.h | 8 +- 2 files changed, 83 insertions(+), 17 deletions(-) diff --git a/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_NewAX.cpp b/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_NewAX.cpp index e4a05c42ba..ea9dd57e3c 100644 --- a/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_NewAX.cpp +++ b/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_NewAX.cpp @@ -19,6 +19,12 @@ #include "UCode_AX_Voice.h" #include "../../DSP.h" +// Useful macro to convert xxx_hi + xxx_lo to xxx for 32 bits. +#define HILO_TO_32(name) \ + ((name##_hi << 16) | name##_lo) + +#define MIXBUF_MAX_SAMPLES 16000 // 500ms of stereo audio + CUCode_NewAX::CUCode_NewAX(DSPHLE* dsp_hle, u32 crc) : IUCode(dsp_hle, crc) , m_cmdlist_size(0) @@ -69,7 +75,8 @@ void CUCode_NewAX::NotifyAXThread() void CUCode_NewAX::HandleCommandList() { - u16 pb_addr_hi, pb_addr_lo; + u16 addr_hi, addr_lo; + u32 pb_addr = 0; u32 curr_idx = 0; @@ -84,15 +91,18 @@ void CUCode_NewAX::HandleCommandList() // A lot of these commands are unknown, or unused in this AX HLE. // We still need to skip their arguments using "curr_idx += N". - case CMD_STUDIO_ADDR: curr_idx += 2; break; + case CMD_STUDIO_ADDR: + addr_hi = m_cmdlist[curr_idx++]; + addr_lo = m_cmdlist[curr_idx++]; + SetupProcessing(HILO_TO_32(addr)); + break; + case CMD_UNK_01: curr_idx += 5; break; case CMD_PB_ADDR: - pb_addr_hi = m_cmdlist[curr_idx++]; - pb_addr_lo = m_cmdlist[curr_idx++]; - pb_addr = (pb_addr_hi << 16) | pb_addr_lo; - - WARN_LOG(DSPHLE, "PB addr: %08x", pb_addr); + addr_hi = m_cmdlist[curr_idx++]; + addr_lo = m_cmdlist[curr_idx++]; + pb_addr = HILO_TO_32(addr); break; case CMD_PROCESS: @@ -109,7 +119,15 @@ void CUCode_NewAX::HandleCommandList() case CMD_UNK_0B: break; // TODO: check other versions case CMD_UNK_0C: break; // TODO: check other versions case CMD_UNK_0D: curr_idx += 2; break; - case CMD_UNK_0E: curr_idx += 4; break; + + case CMD_OUTPUT: + // Skip the first address, we don't know what it's used for. + curr_idx += 2; + + addr_hi = m_cmdlist[curr_idx++]; + addr_lo = m_cmdlist[curr_idx++]; + OutputSamples(HILO_TO_32(addr)); + break; case CMD_END: end = true; @@ -202,7 +220,7 @@ static void ApplyUpdatesForMs(AXPB& pb, int curr_ms) for (int i = 0; i < curr_ms; ++i) start_idx += pb.updates.num_updates[i]; - u32 update_addr = (pb.updates.data_hi << 16) | pb.updates.data_lo; + u32 update_addr = HILO_TO_32(pb.updates.data); for (u32 i = start_idx; i < start_idx + pb.updates.num_updates[curr_ms]; ++i) { u16 update_off = HLEMemory_Read_U16(update_addr + 4 * i); @@ -212,9 +230,22 @@ static void ApplyUpdatesForMs(AXPB& pb, int curr_ms) } } +void CUCode_NewAX::SetupProcessing(u32 studio_addr) +{ + // Initialize to 0. Real hardware initializes using values from studio_addr + // (to have volume ramps instead of 0), but we don't emulate this yet. + + (void)studio_addr; + + memset(m_samples_left, 0, sizeof (m_samples_left)); + memset(m_samples_right, 0, sizeof (m_samples_right)); +} + void CUCode_NewAX::ProcessPBList(u32 pb_addr) { - static int tmp_mix_buffer_left[5 * 32], tmp_mix_buffer_right[5 * 32]; + // Samples per millisecond. In theory DSP sampling rate can be changed from + // 32KHz to 48KHz, but AX always process at 32KHz. + u32 spms = 32; AXPB pb; @@ -231,15 +262,44 @@ void CUCode_NewAX::ProcessPBList(u32 pb_addr) if (m_CRC != 0x3389a79e) VoiceHacks(pb); - MixAddVoice(pb, tmp_mix_buffer_left + 32 * curr_ms, - tmp_mix_buffer_right + 32 * curr_ms, 32); + MixAddVoice(pb, m_samples_left + spms * curr_ms, + m_samples_right + spms * curr_ms, spms); } WritePB(pb_addr, pb); - pb_addr = (pb.next_pb_hi << 16) | pb.next_pb_lo; + pb_addr = HILO_TO_32(pb.next_pb); } - // TODO: write the 5ms back to a buffer the audio interface can read from + // Clamp to 16 bits. + // TODO: check the clamping is done in this command and not in a later + // command. + for (u32 i = 0; i < 5 * spms; ++i) + { + int left = m_samples_left[i]; + int right = m_samples_right[i]; + + 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; + } +} + +void CUCode_NewAX::OutputSamples(u32 out_addr) +{ + // 32 samples per ms, 5 ms, 2 channels + short buffer[5 * 32 * 2]; + + for (u32 i = 0; i < 5 * 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(out_addr), buffer, sizeof (buffer)); } void CUCode_NewAX::HandleMail(u32 mail) @@ -314,8 +374,8 @@ void CUCode_NewAX::CopyCmdList(u32 addr, u16 size) void CUCode_NewAX::MixAdd(short* out_buffer, int nsamples) { - // nsamples * 2 for left and right audio channel - memset(out_buffer, 0, nsamples * 2 * sizeof (short)); + // Should never be called: we do not set HLE as ready. + // We accurately send samples to RAM instead of directly to the mixer. } void CUCode_NewAX::Update(int cycles) diff --git a/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_NewAX.h b/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_NewAX.h index 70a06a7408..12886c5724 100644 --- a/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_NewAX.h +++ b/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_NewAX.h @@ -61,7 +61,7 @@ private: CMD_UNK_0B = 0x0B, CMD_UNK_0C = 0x0C, CMD_UNK_0D = 0x0D, - CMD_UNK_0E = 0x0E, + CMD_OUTPUT = 0x0E, CMD_END = 0x0F, CMD_UNK_10 = 0x10, CMD_UNK_11 = 0x11, @@ -69,6 +69,10 @@ private: CMD_UNK_13 = 0x13, }; + // 32 * 5 because 32 samples per millisecond, for 5 milliseconds. + int m_samples_left[32 * 5]; + int m_samples_right[32 * 5]; + // Volatile because it's set by HandleMail and accessed in // HandleCommandList, which are running in two different threads. volatile u16 m_cmdlist[512]; @@ -90,7 +94,9 @@ private: void AXThread(); void HandleCommandList(); + void SetupProcessing(u32 studio_addr); void ProcessPBList(u32 pb_addr); + void OutputSamples(u32 out_addr); }; #endif // !_UCODE_NEWAX_H From 6a1241c03e3adc3a74b5f6dda9ea42f7316ce049 Mon Sep 17 00:00:00 2001 From: Pierre Bourdon Date: Wed, 14 Nov 2012 11:25:27 +0100 Subject: [PATCH 06/44] Make a hack optional to fix 48KHz output mode with NewAX --- .../Core/Core/Src/HW/DSPHLE/UCodes/UCode_AX_Voice.h | 11 ++++++++--- Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_NewAX.cpp | 2 +- 2 files changed, 9 insertions(+), 4 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 bf5e4d9b6b..1d35ed797a 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 @@ -100,12 +100,17 @@ inline bool WritePB(u32 addr, AXPBWii &PB) template inline void MixAddVoice(ParamBlockType &pb, int *templbuffer, int *temprbuffer, - int _iSize) + int _iSize, bool resample = true) { if (pb.running) { - const u32 ratio = (u32)(((pb.src.ratio_hi << 16) + pb.src.ratio_lo) - * /*ratioFactor:*/((float)AudioInterface::GetAIDSampleRate() / (float)soundStream->GetMixer()->GetSampleRate())); + float ratioFactor; + if (resample) + ratioFactor = (float)AudioInterface::GetAIDSampleRate() / (float)soundStream->GetMixer()->GetSampleRate(); + else + ratioFactor = 1.0; + + const u32 ratio = (u32)(((pb.src.ratio_hi << 16) + pb.src.ratio_lo) * ratioFactor); u32 sampleEnd = (pb.audio_addr.end_addr_hi << 16) | pb.audio_addr.end_addr_lo; u32 loopPos = (pb.audio_addr.loop_addr_hi << 16) | pb.audio_addr.loop_addr_lo; diff --git a/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_NewAX.cpp b/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_NewAX.cpp index ea9dd57e3c..7df82287f7 100644 --- a/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_NewAX.cpp +++ b/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_NewAX.cpp @@ -263,7 +263,7 @@ void CUCode_NewAX::ProcessPBList(u32 pb_addr) VoiceHacks(pb); MixAddVoice(pb, m_samples_left + spms * curr_ms, - m_samples_right + spms * curr_ms, spms); + m_samples_right + spms * curr_ms, spms, false); } WritePB(pb_addr, pb); From 24d430eba0026b2efbbed180c8b3b53b50e947a7 Mon Sep 17 00:00:00 2001 From: Pierre Bourdon Date: Wed, 14 Nov 2012 11:32:57 +0100 Subject: [PATCH 07/44] Add NewAX to the VC++ project files --- Source/Core/Core/Core.vcxproj | 4 +++- Source/Core/Core/Core.vcxproj.filters | 8 +++++++- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/Source/Core/Core/Core.vcxproj b/Source/Core/Core/Core.vcxproj index e03d40e43a..fb2d0bafdf 100644 --- a/Source/Core/Core/Core.vcxproj +++ b/Source/Core/Core/Core.vcxproj @@ -265,6 +265,7 @@ + @@ -466,6 +467,7 @@ + @@ -592,4 +594,4 @@ - \ No newline at end of file + diff --git a/Source/Core/Core/Core.vcxproj.filters b/Source/Core/Core/Core.vcxproj.filters index 020cf76ad0..482b2bd9b5 100644 --- a/Source/Core/Core/Core.vcxproj.filters +++ b/Source/Core/Core/Core.vcxproj.filters @@ -206,6 +206,9 @@ HW %28Flipper/Hollywood%29\DSP Interface + HLE\HLE\uCodes + + HW %28Flipper/Hollywood%29\DSP Interface + HLE\HLE\uCodes + HW %28Flipper/Hollywood%29\DSP Interface + HLE\HLE\uCodes @@ -748,6 +751,9 @@ HW %28Flipper/Hollywood%29\DSP Interface + HLE\HLE\uCodes + + HW %28Flipper/Hollywood%29\DSP Interface + HLE\HLE\uCodes + HW %28Flipper/Hollywood%29\DSP Interface + HLE\HLE\uCodes @@ -1174,4 +1180,4 @@ {3e9e6e83-c1bf-45f9-aeff-231f98f60d29} - \ No newline at end of file + From db46ccd1757f763bbbf7108a8c47ed060b1df1c5 Mon Sep 17 00:00:00 2001 From: Pierre Bourdon Date: Wed, 14 Nov 2012 12:08:41 +0100 Subject: [PATCH 08/44] More accurate mixing buffers initialization using parameters from command 00 --- .../Core/Src/HW/DSPHLE/UCodes/UCode_NewAX.cpp | 35 ++++++++++++++++--- 1 file changed, 30 insertions(+), 5 deletions(-) diff --git a/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_NewAX.cpp b/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_NewAX.cpp index 7df82287f7..1ee0c799f3 100644 --- a/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_NewAX.cpp +++ b/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_NewAX.cpp @@ -232,13 +232,38 @@ static void ApplyUpdatesForMs(AXPB& pb, int curr_ms) void CUCode_NewAX::SetupProcessing(u32 studio_addr) { - // Initialize to 0. Real hardware initializes using values from studio_addr - // (to have volume ramps instead of 0), but we don't emulate this yet. + u16 studio_data[0x20]; - (void)studio_addr; + for (u32 i = 0; i < 0x20; ++i) + studio_data[i] = HLEMemory_Read_U16(studio_addr + 2 * i); - memset(m_samples_left, 0, sizeof (m_samples_left)); - memset(m_samples_right, 0, sizeof (m_samples_right)); + // studio_data[0, 1, 2] are for left samples volume ramping + s32 left_init = (s32)((studio_data[0] << 16) | studio_data[1]); + s16 left_delta = (s16)studio_data[2]; + if (!left_init) + memset(m_samples_left, 0, sizeof (m_samples_left)); + else + { + for (u32 i = 0; i < 32 * 5; ++i) + { + m_samples_left[i] = left_init; + left_init -= left_delta; + } + } + + // studio_data[3, 4, 5] are for right samples volume ramping + s32 right_init = (s32)((studio_data[0] << 16) | studio_data[1]); + s16 right_delta = (s16)studio_data[2]; + if (!right_init) + memset(m_samples_right, 0, sizeof (m_samples_right)); + else + { + for (u32 i = 0; i < 32 * 5; ++i) + { + m_samples_right[i] = right_init; + right_init -= right_delta; + } + } } void CUCode_NewAX::ProcessPBList(u32 pb_addr) From 61c1fab2c62e2f841590e91cbcd4450f5f432bfa Mon Sep 17 00:00:00 2001 From: Pierre Bourdon Date: Wed, 14 Nov 2012 12:13:19 +0100 Subject: [PATCH 09/44] Fix Windows support again: std::thread can't take a member pointer with StdThread.h from Common --- Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_NewAX.cpp | 7 ++++++- Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_NewAX.h | 4 ++++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_NewAX.cpp b/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_NewAX.cpp index 1ee0c799f3..0662511f92 100644 --- a/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_NewAX.cpp +++ b/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_NewAX.cpp @@ -28,7 +28,7 @@ CUCode_NewAX::CUCode_NewAX(DSPHLE* dsp_hle, u32 crc) : IUCode(dsp_hle, crc) , m_cmdlist_size(0) - , m_axthread(&CUCode_NewAX::AXThread, this) + , m_axthread(&SpawnAXThread, this) { m_rMailHandler.PushMail(DSP_INIT); DSP::GenerateDSPInterruptFromDSPEmu(DSP::INT_DSP); @@ -43,6 +43,11 @@ CUCode_NewAX::~CUCode_NewAX() m_rMailHandler.Clear(); } +void CUCode_NewAX::SpawnAXThread(CUCode_NewAX* self) +{ + self->AXThread(); +} + void CUCode_NewAX::AXThread() { while (true) diff --git a/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_NewAX.h b/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_NewAX.h index 12886c5724..118d64e7ab 100644 --- a/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_NewAX.h +++ b/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_NewAX.h @@ -32,6 +32,10 @@ public: void Update(int cycles); void DoState(PointerWrap& p); + // Needed because StdThread.h std::thread implem does not support member + // pointers. + static void SpawnAXThread(CUCode_NewAX* self); + private: enum MailType { From a630357c9e4e73032604e30ed59a446aea9489d9 Mon Sep 17 00:00:00 2001 From: Pierre Bourdon Date: Wed, 14 Nov 2012 17:55:16 +0100 Subject: [PATCH 10/44] Add AUX mixing support as well as a send&return effect implementation --- .../Core/Src/HW/DSPHLE/UCodes/UCode_AX.cpp | 7 +- .../Src/HW/DSPHLE/UCodes/UCode_AXStructs.h | 11 +- .../Core/Src/HW/DSPHLE/UCodes/UCode_AXWii.cpp | 10 +- .../Src/HW/DSPHLE/UCodes/UCode_AX_Voice.h | 40 ++++- .../Core/Src/HW/DSPHLE/UCodes/UCode_NewAX.cpp | 154 +++++++++++++----- .../Core/Src/HW/DSPHLE/UCodes/UCode_NewAX.h | 16 +- 6 files changed, 183 insertions(+), 55 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 74c9d24db4..d990d282d7 100644 --- a/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_AX.cpp +++ b/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_AX.cpp @@ -158,6 +158,11 @@ void CUCode_AX::MixAdd(short* _pBuffer, int _iSize) memset(temprbuffer, 0, _iSize * sizeof(int)); AXPB PB; + AXBuffers buffers = {{ + templbuffer, + temprbuffer, + NULL + }}; for (int x = 0; x < numPBaddr; x++) { @@ -175,7 +180,7 @@ void CUCode_AX::MixAdd(short* _pBuffer, int _iSize) if (m_CRC != 0x3389a79e) VoiceHacks(PB); - MixAddVoice(PB, templbuffer, temprbuffer, _iSize); + MixAddVoice(PB, buffers, _iSize); if (!WritePB(blockAddr, PB)) break; 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 7f082740de..3217448268 100644 --- a/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_AXStructs.h +++ b/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_AXStructs.h @@ -25,7 +25,16 @@ struct PBMixer u16 right; u16 right_delta; - u16 unknown3[8]; + 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; + u16 unknown4[6]; }; 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 03b1d3b75b..62d3bac6e4 100644 --- a/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_AXWii.cpp +++ b/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_AXWii.cpp @@ -101,15 +101,21 @@ void CUCode_AXWii::MixAdd(short* _pBuffer, int _iSize) if (!blockAddr) return; + AXBuffers buffers = {{ + templbuffer, + temprbuffer, + NULL + }}; + for (int i = 0; i < NUMBER_OF_PBS; i++) { if (!ReadPB(blockAddr, PB)) break; if (wiisportsHack) - MixAddVoice(*(AXPBWiiSports*)&PB, templbuffer, temprbuffer, _iSize); + MixAddVoice(*(AXPBWiiSports*)&PB, buffers, _iSize); else - MixAddVoice(PB, templbuffer, temprbuffer, _iSize); + MixAddVoice(PB, buffers, _iSize); if (!WritePB(blockAddr, PB)) break; 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 1d35ed797a..f59693698c 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 @@ -94,12 +94,26 @@ inline bool WritePB(u32 addr, AXPBWii &PB) return true; } +union AXBuffers +{ + struct + { + int* left; + int* right; + int* auxA_left; + int* auxA_right; + int* auxB_left; + int* auxB_right; + }; + + int* ptrs[6]; +}; + ////////////////////////////////////////////////////////////////////////// // 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, +inline void MixAddVoice(ParamBlockType &pb, const AXBuffers& buffers, int _iSize, bool resample = true) { if (pb.running) @@ -230,11 +244,25 @@ inline void MixAddVoice(ParamBlockType &pb, int leftmix = pb.mixer.left >> 5; int rightmix = pb.mixer.right >> 5; + int auxAleftmix = pb.mixer.auxA_left >> 5; + int auxArightmix= pb.mixer.auxA_right >> 5; + int auxBleftmix = pb.mixer.auxB_left >> 5; + int auxBrightmix= pb.mixer.auxB_right >> 5; + int left = sample * leftmix >> 8; int right = sample * rightmix >> 8; + int auxAleft = sample * auxAleftmix >> 8; + int auxAright = sample * auxArightmix >> 8; + int auxBleft = sample * auxBleftmix >> 8; + int auxBright = sample * auxBrightmix >> 8; + // adpcm has to walk from oldSamplePos to samplePos here - templbuffer[s] += left; - temprbuffer[s] += right; + if ((pb.mixer_control & 1) && buffers.left) buffers.left[s] += left; + if ((pb.mixer_control & 2) && buffers.right) buffers.right [s] += right; + if ((pb.mixer_control & 16) && buffers.auxA_left) buffers.auxA_left[s] += auxAleft; + if ((pb.mixer_control & 32) && buffers.auxA_right) buffers.auxA_right[s] += auxAright; + if ((pb.mixer_control & 512) && buffers.auxB_left) buffers.auxB_left[s] += auxBleft; + if ((pb.mixer_control & 1024) && buffers.auxB_right) buffers.auxB_right[s] += auxBright; // Control the behavior when we reach the end of the sample if (samplePos >= sampleEnd) @@ -265,6 +293,10 @@ inline void MixAddVoice(ParamBlockType &pb, // 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.mixer.auxA_left = ADPCM_Vol(pb.mixer.auxA_left, pb.mixer.auxA_left_delta); + pb.mixer.auxA_right = ADPCM_Vol(pb.mixer.auxA_right, pb.mixer.auxA_right_delta); + pb.mixer.auxB_left = ADPCM_Vol(pb.mixer.auxB_left, pb.mixer.auxB_left_delta); + pb.mixer.auxB_right = ADPCM_Vol(pb.mixer.auxB_right, pb.mixer.auxB_right_delta); pb.src.cur_addr_frac = (u16)frac; pb.audio_addr.cur_addr_hi = samplePos >> 16; diff --git a/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_NewAX.cpp b/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_NewAX.cpp index 0662511f92..6549c7dcad 100644 --- a/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_NewAX.cpp +++ b/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_NewAX.cpp @@ -80,7 +80,9 @@ void CUCode_NewAX::NotifyAXThread() void CUCode_NewAX::HandleCommandList() { + // Temp variables for addresses computation u16 addr_hi, addr_lo; + u16 addr2_hi, addr2_lo; u32 pb_addr = 0; @@ -96,7 +98,7 @@ void CUCode_NewAX::HandleCommandList() // A lot of these commands are unknown, or unused in this AX HLE. // We still need to skip their arguments using "curr_idx += N". - case CMD_STUDIO_ADDR: + case CMD_SETUP: addr_hi = m_cmdlist[curr_idx++]; addr_lo = m_cmdlist[curr_idx++]; SetupProcessing(HILO_TO_32(addr)); @@ -114,9 +116,22 @@ void CUCode_NewAX::HandleCommandList() ProcessPBList(pb_addr); break; - case CMD_UNK_04: curr_idx += 4; break; - case CMD_UNK_05: curr_idx += 4; break; - case CMD_UNK_06: curr_idx += 2; break; + case CMD_MIX_AUXA: + case CMD_MIX_AUXB: + // These two commands are handled almost the same internally. + 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)); + break; + + case CMD_MIX_AUXB_NOWRITE: + addr_hi = m_cmdlist[curr_idx++]; + addr_lo = m_cmdlist[curr_idx++]; + MixAUXSamples(false, 0, HILO_TO_32(addr)); + break; + case CMD_SBUFFER_ADDR: curr_idx += 2; break; case CMD_UNK_08: curr_idx += 10; break; // TODO: check case CMD_UNK_09: curr_idx += 2; break; @@ -126,7 +141,8 @@ void CUCode_NewAX::HandleCommandList() case CMD_UNK_0D: curr_idx += 2; break; case CMD_OUTPUT: - // Skip the first address, we don't know what it's used for. + // Skip the first address, it is used for surround audio + // output, which we don't support yet. curr_idx += 2; addr_hi = m_cmdlist[curr_idx++]; @@ -235,38 +251,43 @@ static void ApplyUpdatesForMs(AXPB& pb, int curr_ms) } } -void CUCode_NewAX::SetupProcessing(u32 studio_addr) +void CUCode_NewAX::SetupProcessing(u32 init_addr) { - u16 studio_data[0x20]; + u16 init_data[0x20]; for (u32 i = 0; i < 0x20; ++i) - studio_data[i] = HLEMemory_Read_U16(studio_addr + 2 * i); + init_data[i] = HLEMemory_Read_U16(init_addr + 2 * i); - // studio_data[0, 1, 2] are for left samples volume ramping - s32 left_init = (s32)((studio_data[0] << 16) | studio_data[1]); - s16 left_delta = (s16)studio_data[2]; - if (!left_init) - memset(m_samples_left, 0, sizeof (m_samples_left)); - else - { - for (u32 i = 0; i < 32 * 5; ++i) - { - m_samples_left[i] = left_init; - left_init -= left_delta; - } - } + // List of all buffers we have to initialize + int* 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 + }; - // studio_data[3, 4, 5] are for right samples volume ramping - s32 right_init = (s32)((studio_data[0] << 16) | studio_data[1]); - s16 right_delta = (s16)studio_data[2]; - if (!right_init) - memset(m_samples_right, 0, sizeof (m_samples_right)); - else + u32 init_idx = 0; + for (u32 i = 0; i < sizeof (buffers) / sizeof (buffers[0]); ++i) { - for (u32 i = 0; i < 32 * 5; ++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], 0, 5 * 32 * sizeof (int)); + else { - m_samples_right[i] = right_init; - right_init -= right_delta; + for (u32 j = 0; j < 32 * 5; ++j) + { + buffers[i][j] = init_val; + init_val -= delta; + } } } } @@ -275,12 +296,21 @@ void CUCode_NewAX::ProcessPBList(u32 pb_addr) { // Samples per millisecond. In theory DSP sampling rate can be changed from // 32KHz to 48KHz, but AX always process at 32KHz. - u32 spms = 32; + const u32 spms = 32; AXPB pb; while (pb_addr) { + AXBuffers buffers = {{ + m_samples_left, + m_samples_right, + m_samples_auxA_left, + m_samples_auxA_right, + m_samples_auxB_left, + m_samples_auxB_right + }}; + if (!ReadPB(pb_addr, pb)) break; @@ -292,18 +322,62 @@ void CUCode_NewAX::ProcessPBList(u32 pb_addr) if (m_CRC != 0x3389a79e) VoiceHacks(pb); - MixAddVoice(pb, m_samples_left + spms * curr_ms, - m_samples_right + spms * curr_ms, spms, false); + MixAddVoice(pb, buffers, spms, false); + + // 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); } +} - // Clamp to 16 bits. - // TODO: check the clamping is done in this command and not in a later - // command. - for (u32 i = 0; i < 5 * spms; ++i) +void CUCode_NewAX::MixAUXSamples(bool AUXA, u32 write_addr, u32 read_addr) +{ + int buffers[3][5 * 32]; + + // First, we need to send the contents of our AUX buffers to the CPU. + if (write_addr) + { + for (u32 i = 0; i < 5 * 32; ++i) + { + if (AUXA) + { + buffers[0][i] = Common::swap32(m_samples_auxA_left[i]); + buffers[1][i] = Common::swap32(m_samples_auxA_right[i]); + buffers[2][i] = Common::swap32(m_samples_auxA_surround[i]); + } + else + { + buffers[0][i] = Common::swap32(m_samples_auxB_left[i]); + buffers[1][i] = Common::swap32(m_samples_auxB_right[i]); + buffers[2][i] = Common::swap32(m_samples_auxB_surround[i]); + } + } + memset(buffers, 0, sizeof (buffers)); + memcpy(HLEMemory_Get_Pointer(write_addr), buffers, sizeof (buffers)); + } + + // Then, we read the new buffers from the CPU and add to our current + // buffers. + memcpy(buffers, HLEMemory_Get_Pointer(read_addr), sizeof (buffers)); + for (u32 i = 0; i < 5 * 32; ++i) + { + m_samples_left[i] += Common::swap32(buffers[0][i]); + m_samples_right[i] += Common::swap32(buffers[1][i]); + m_samples_surround[i] += Common::swap32(buffers[2][i]); + } +} + +void CUCode_NewAX::OutputSamples(u32 out_addr) +{ + // 32 samples per ms, 5 ms, 2 channels + short buffer[5 * 32 * 2]; + + // Clamp internal buffers to 16 bits. + for (u32 i = 0; i < 5 * 32; ++i) { int left = m_samples_left[i]; int right = m_samples_right[i]; @@ -316,12 +390,6 @@ void CUCode_NewAX::ProcessPBList(u32 pb_addr) m_samples_left[i] = left; m_samples_right[i] = right; } -} - -void CUCode_NewAX::OutputSamples(u32 out_addr) -{ - // 32 samples per ms, 5 ms, 2 channels - short buffer[5 * 32 * 2]; for (u32 i = 0; i < 5 * 32; ++i) { diff --git a/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_NewAX.h b/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_NewAX.h index 118d64e7ab..af4a2a4895 100644 --- a/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_NewAX.h +++ b/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_NewAX.h @@ -51,13 +51,13 @@ private: enum CmdType { - CMD_STUDIO_ADDR = 0x00, + CMD_SETUP = 0x00, CMD_UNK_01 = 0x01, CMD_PB_ADDR = 0x02, CMD_PROCESS = 0x03, - CMD_UNK_04 = 0x04, - CMD_UNK_05 = 0x05, - CMD_UNK_06 = 0x06, + CMD_MIX_AUXA = 0x04, + CMD_MIX_AUXB = 0x05, + CMD_MIX_AUXB_NOWRITE = 0x06, CMD_SBUFFER_ADDR = 0x07, CMD_UNK_08 = 0x08, CMD_UNK_09 = 0x09, @@ -76,6 +76,13 @@ private: // 32 * 5 because 32 samples per millisecond, for 5 milliseconds. int m_samples_left[32 * 5]; int m_samples_right[32 * 5]; + int m_samples_surround[32 * 5]; + int m_samples_auxA_left[32 * 5]; + int m_samples_auxA_right[32 * 5]; + int m_samples_auxA_surround[32 * 5]; + int m_samples_auxB_left[32 * 5]; + int m_samples_auxB_right[32 * 5]; + int m_samples_auxB_surround[32 * 5]; // Volatile because it's set by HandleMail and accessed in // HandleCommandList, which are running in two different threads. @@ -100,6 +107,7 @@ private: void HandleCommandList(); void SetupProcessing(u32 studio_addr); void ProcessPBList(u32 pb_addr); + void MixAUXSamples(bool AUXA, u32 write_addr, u32 read_addr); void OutputSamples(u32 out_addr); }; From f84f15c5bf84f862c3c4e889b68633cb711e1fb0 Mon Sep 17 00:00:00 2001 From: Pierre Bourdon Date: Wed, 14 Nov 2012 18:03:55 +0100 Subject: [PATCH 11/44] Oops, forgot a memset which removed all AUX effects --- Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_NewAX.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_NewAX.cpp b/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_NewAX.cpp index 6549c7dcad..c482a8ce8a 100644 --- a/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_NewAX.cpp +++ b/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_NewAX.cpp @@ -356,7 +356,6 @@ void CUCode_NewAX::MixAUXSamples(bool AUXA, u32 write_addr, u32 read_addr) buffers[2][i] = Common::swap32(m_samples_auxB_surround[i]); } } - memset(buffers, 0, sizeof (buffers)); memcpy(HLEMemory_Get_Pointer(write_addr), buffers, sizeof (buffers)); } From 531cc6aaf31af3d22635bb2581f1c91ebaf0c253 Mon Sep 17 00:00:00 2001 From: Pierre Bourdon Date: Wed, 14 Nov 2012 18:08:29 +0100 Subject: [PATCH 12/44] Fixed AUXB_NOWRITE command number and implemented UPLOAD_LRS command (06) --- .../Core/Src/HW/DSPHLE/UCodes/UCode_NewAX.cpp | 25 ++++++++++++++++--- .../Core/Src/HW/DSPHLE/UCodes/UCode_NewAX.h | 5 ++-- 2 files changed, 25 insertions(+), 5 deletions(-) diff --git a/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_NewAX.cpp b/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_NewAX.cpp index c482a8ce8a..9bc43b0f04 100644 --- a/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_NewAX.cpp +++ b/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_NewAX.cpp @@ -126,15 +126,21 @@ void CUCode_NewAX::HandleCommandList() MixAUXSamples(cmd == CMD_MIX_AUXA, HILO_TO_32(addr), HILO_TO_32(addr2)); break; + case CMD_UPLOAD_LRS: + addr_hi = m_cmdlist[curr_idx++]; + addr_lo = m_cmdlist[curr_idx++]; + UploadLRS(HILO_TO_32(addr)); + break; + + case CMD_SBUFFER_ADDR: curr_idx += 2; break; + case CMD_UNK_08: curr_idx += 10; break; // TODO: check + case CMD_MIX_AUXB_NOWRITE: addr_hi = m_cmdlist[curr_idx++]; addr_lo = m_cmdlist[curr_idx++]; MixAUXSamples(false, 0, HILO_TO_32(addr)); break; - case CMD_SBUFFER_ADDR: curr_idx += 2; break; - case CMD_UNK_08: curr_idx += 10; break; // TODO: check - case CMD_UNK_09: curr_idx += 2; break; case CMD_COMPRESSOR_TABLE_ADDR: curr_idx += 2; break; case CMD_UNK_0B: break; // TODO: check other versions case CMD_UNK_0C: break; // TODO: check other versions @@ -370,6 +376,19 @@ void CUCode_NewAX::MixAUXSamples(bool AUXA, u32 write_addr, u32 read_addr) } } +void CUCode_NewAX::UploadLRS(u32 dst_addr) +{ + int buffers[3][5 * 32]; + + for (u32 i = 0; i < 5 * 32; ++i) + { + buffers[0][i] = Common::swap32(m_samples_left[i]); + buffers[1][i] = Common::swap32(m_samples_right[i]); + buffers[2][i] = Common::swap32(m_samples_surround[i]); + } + memcpy(HLEMemory_Get_Pointer(dst_addr), buffers, sizeof (buffers)); +} + void CUCode_NewAX::OutputSamples(u32 out_addr) { // 32 samples per ms, 5 ms, 2 channels diff --git a/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_NewAX.h b/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_NewAX.h index af4a2a4895..91aa495c0e 100644 --- a/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_NewAX.h +++ b/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_NewAX.h @@ -57,10 +57,10 @@ private: CMD_PROCESS = 0x03, CMD_MIX_AUXA = 0x04, CMD_MIX_AUXB = 0x05, - CMD_MIX_AUXB_NOWRITE = 0x06, + CMD_UPLOAD_LRS = 0x06, CMD_SBUFFER_ADDR = 0x07, CMD_UNK_08 = 0x08, - CMD_UNK_09 = 0x09, + CMD_MIX_AUXB_NOWRITE = 0x09, CMD_COMPRESSOR_TABLE_ADDR = 0x0A, CMD_UNK_0B = 0x0B, CMD_UNK_0C = 0x0C, @@ -108,6 +108,7 @@ private: void SetupProcessing(u32 studio_addr); void ProcessPBList(u32 pb_addr); void MixAUXSamples(bool AUXA, u32 write_addr, u32 read_addr); + void UploadLRS(u32 dst_addr); void OutputSamples(u32 out_addr); }; From aa90f799b7a8a3dc71ccae435ff1b9c7d2ccc15e Mon Sep 17 00:00:00 2001 From: Pierre Bourdon Date: Thu, 15 Nov 2012 16:30:05 +0100 Subject: [PATCH 13/44] Re-implementation of the AX voice mixing algorithm. Now with 100% less WTF. --- Source/Core/Core/Core.vcxproj | 1 + Source/Core/Core/Core.vcxproj.filters | 3 + .../Src/HW/DSPHLE/UCodes/UCode_AXStructs.h | 47 ++- .../Src/HW/DSPHLE/UCodes/UCode_AX_Voice.h | 2 +- .../Core/Src/HW/DSPHLE/UCodes/UCode_NewAX.cpp | 87 +---- .../Src/HW/DSPHLE/UCodes/UCode_NewAX_Voice.h | 304 ++++++++++++++++++ 6 files changed, 353 insertions(+), 91 deletions(-) create mode 100644 Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_NewAX_Voice.h diff --git a/Source/Core/Core/Core.vcxproj b/Source/Core/Core/Core.vcxproj index fb2d0bafdf..caab0a1e6d 100644 --- a/Source/Core/Core/Core.vcxproj +++ b/Source/Core/Core/Core.vcxproj @@ -468,6 +468,7 @@ + diff --git a/Source/Core/Core/Core.vcxproj.filters b/Source/Core/Core/Core.vcxproj.filters index 482b2bd9b5..e0df3bd31f 100644 --- a/Source/Core/Core/Core.vcxproj.filters +++ b/Source/Core/Core/Core.vcxproj.filters @@ -754,6 +754,9 @@ HW %28Flipper/Hollywood%29\DSP Interface + HLE\HLE\uCodes + + HW %28Flipper/Hollywood%29\DSP Interface + HLE\HLE\uCodes + HW %28Flipper/Hollywood%29\DSP Interface + HLE\HLE\uCodes 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 3217448268..e7701e4698 100644 --- a/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_AXStructs.h +++ b/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_AXStructs.h @@ -35,7 +35,12 @@ struct PBMixer u16 auxB_right; u16 auxB_right_delta; - u16 unknown4[6]; + u16 auxB_surround; + u16 auxB_surround_delta; + u16 surround; + u16 surround_delta; + u16 auxA_surround; + u16 auxA_surround_delta; }; struct PBMixerWii @@ -212,6 +217,14 @@ struct PBADPCMLoopInfo u16 yn2; }; +struct PBLowPassFilter +{ + u16 enabled; + u16 yn1; + u16 a0; + u16 b0; +}; + struct AXPB { u16 next_pb_hi; @@ -236,15 +249,9 @@ struct AXPB PBADPCMInfo adpcm; PBSampleRateConverter src; PBADPCMLoopInfo adpcm_loop_info; - u16 unknown_maybe_padding[3]; -}; + PBLowPassFilter lpf; -struct PBLowPassFilter -{ - u16 enabled; - u16 yn1; - u16 a0; - u16 b0; + u16 padding[25]; }; struct PBBiquadFilter @@ -360,9 +367,29 @@ enum { }; enum { + SRCTYPE_POLYPHASE = 0, SRCTYPE_LINEAR = 1, SRCTYPE_NEAREST = 2, - MIXCONTROL_RAMPING = 8, +}; + +enum { + MIX_L = 0x0001, + MIX_R = 0x0002, + MIX_S = 0x0004, + MIX_RAMP = 0x0008, + + MIX_AUXA_L = 0x0010, + MIX_AUXA_R = 0x0020, + MIX_AUXA_RAMPLR = 0x0040, + MIX_AUXA_S = 0x0080, + MIX_AUXA_RAMPS = 0x0100, + + MIX_AUXB_L = 0x0200, + MIX_AUXB_R = 0x0400, + MIX_AUXB_RAMPLR = 0x0800, + MIX_AUXB_S = 0x1000, + MIX_AUXB_RAMPS = 0x2000, + MIX_AUXB_DPL2 = 0x4000 }; // Both may be used at once 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 f59693698c..f401cc630f 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 @@ -230,7 +230,7 @@ inline void MixAddVoice(ParamBlockType &pb, const AXBuffers& buffers, int vol = pb.vol_env.cur_volume >> 9; sample = sample * vol >> 8; - if (pb.mixer_control & MIXCONTROL_RAMPING) + if (pb.mixer_control & MIX_RAMP) { 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 diff --git a/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_NewAX.cpp b/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_NewAX.cpp index 9bc43b0f04..a7e9c561f0 100644 --- a/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_NewAX.cpp +++ b/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_NewAX.cpp @@ -16,13 +16,9 @@ // http://code.google.com/p/dolphin-emu/ #include "UCode_NewAX.h" -#include "UCode_AX_Voice.h" +#include "UCode_NewAX_Voice.h" #include "../../DSP.h" -// Useful macro to convert xxx_hi + xxx_lo to xxx for 32 bits. -#define HILO_TO_32(name) \ - ((name##_hi << 16) | name##_lo) - #define MIXBUF_MAX_SAMPLES 16000 // 500ms of stereo audio CUCode_NewAX::CUCode_NewAX(DSPHLE* dsp_hle, u32 crc) @@ -173,74 +169,6 @@ void CUCode_NewAX::HandleCommandList() } } -// From old UCode_AX.cpp. -static void VoiceHacks(AXPB &pb) -{ - // get necessary values - const u32 sampleEnd = (pb.audio_addr.end_addr_hi << 16) | pb.audio_addr.end_addr_lo; - const u32 loopPos = (pb.audio_addr.loop_addr_hi << 16) | pb.audio_addr.loop_addr_lo; - // const u32 updaddr = (u32)(pb.updates.data_hi << 16) | pb.updates.data_lo; - // const u16 updpar = HLEMemory_Read_U16(updaddr); - // const u16 upddata = HLEMemory_Read_U16(updaddr + 2); - - // ======================================================================================= - /* Fix problems introduced with the SSBM fix. Sometimes when a music stream ended sampleEnd - would end up outside of bounds while the block was still playing resulting in noise - a strange noise. This should take care of that. - */ - if ((sampleEnd > (0x017fffff * 2) || loopPos > (0x017fffff * 2))) // ARAM bounds in nibbles - { - pb.running = 0; - - // also reset all values if it makes any difference - pb.audio_addr.cur_addr_hi = 0; pb.audio_addr.cur_addr_lo = 0; - pb.audio_addr.end_addr_hi = 0; pb.audio_addr.end_addr_lo = 0; - pb.audio_addr.loop_addr_hi = 0; pb.audio_addr.loop_addr_lo = 0; - - pb.src.cur_addr_frac = 0; pb.src.ratio_hi = 0; pb.src.ratio_lo = 0; - pb.adpcm.pred_scale = 0; pb.adpcm.yn1 = 0; pb.adpcm.yn2 = 0; - - pb.audio_addr.looping = 0; - pb.adpcm_loop_info.pred_scale = 0; - pb.adpcm_loop_info.yn1 = 0; pb.adpcm_loop_info.yn2 = 0; - } - - /* - // the fact that no settings are reset (except running) after a SSBM type music stream or another - looping block (for example in Battle Stadium DON) has ended could cause loud garbled sound to be - played from one or more blocks. Perhaps it was in conjunction with the old sequenced music fix below, - I'm not sure. This was an attempt to prevent that anyway by resetting all. But I'm not sure if this - is needed anymore. Please try to play SSBM without it and see if it works anyway. - */ - if ( - // detect blocks that have recently been running that we should reset - pb.running == 0 && pb.audio_addr.looping == 1 - //pb.running == 0 && pb.adpcm_loop_info.pred_scale - - // this prevents us from ruining sequenced music blocks, may not be needed - /* - && !(pb.updates.num_updates[0] || pb.updates.num_updates[1] || pb.updates.num_updates[2] - || pb.updates.num_updates[3] || pb.updates.num_updates[4]) - */ - //&& !(updpar || upddata) - - && pb.mixer_control == 0 // only use this in SSBM - ) - { - // reset the detection values - pb.audio_addr.looping = 0; - pb.adpcm_loop_info.pred_scale = 0; - pb.adpcm_loop_info.yn1 = 0; pb.adpcm_loop_info.yn2 = 0; - - //pb.audio_addr.cur_addr_hi = 0; pb.audio_addr.cur_addr_lo = 0; - //pb.audio_addr.end_addr_hi = 0; pb.audio_addr.end_addr_lo = 0; - //pb.audio_addr.loop_addr_hi = 0; pb.audio_addr.loop_addr_lo = 0; - - //pb.src.cur_addr_frac = 0; PBs[i].src.ratio_hi = 0; PBs[i].src.ratio_lo = 0; - //pb.adpcm.pred_scale = 0; pb.adpcm.yn1 = 0; pb.adpcm.yn2 = 0; - } -} - static void ApplyUpdatesForMs(AXPB& pb, int curr_ms) { u32 start_idx = 0; @@ -292,7 +220,7 @@ void CUCode_NewAX::SetupProcessing(u32 init_addr) for (u32 j = 0; j < 32 * 5; ++j) { buffers[i][j] = init_val; - init_val -= delta; + init_val += delta; } } } @@ -311,10 +239,13 @@ void CUCode_NewAX::ProcessPBList(u32 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_right, + m_samples_auxB_surround }}; if (!ReadPB(pb_addr, pb)) @@ -324,11 +255,7 @@ void CUCode_NewAX::ProcessPBList(u32 pb_addr) { ApplyUpdatesForMs(pb, curr_ms); - // TODO: is that still needed? - if (m_CRC != 0x3389a79e) - VoiceHacks(pb); - - MixAddVoice(pb, buffers, spms, false); + Process1ms(pb, buffers); // 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_NewAX_Voice.h b/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_NewAX_Voice.h new file mode 100644 index 0000000000..8ebbec2fd9 --- /dev/null +++ b/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_NewAX_Voice.h @@ -0,0 +1,304 @@ +// 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_NEWAX_VOICE_H +#define _UCODE_NEWAX_VOICE_H + +#include "Common.h" +#include "UCode_AXStructs.h" +#include "../../DSP.h" + +// Useful macro to convert xxx_hi + xxx_lo to xxx for 32 bits. +#define HILO_TO_32(name) \ + ((name##_hi << 16) | name##_lo) + +// Used to pass a large amount of buffers to the mixing function. +union AXBuffers +{ + struct + { + int* left; + int* right; + int* surround; + + int* auxA_left; + int* auxA_right; + int* auxA_surround; + + int* auxB_left; + int* auxB_right; + int* auxB_surround; + }; + + int* ptrs[9]; +}; + +// Read a PB from MRAM/ARAM +inline bool ReadPB(u32 addr, AXPB& pb) +{ + u16* dst = (u16*)&pb; + const u16* src = (const u16*)Memory::GetPointer(addr); + if (!src) + return false; + + for (u32 i = 0; i < sizeof (pb) / sizeof (u16); ++i) + dst[i] = Common::swap16(src[i]); + + return true; +} + +// Write a PB back to MRAM/ARAM +inline bool WritePB(u32 addr, const AXPB& pb) +{ + const u16* src = (const u16*)&pb; + u16* dst = (u16*)Memory::GetPointer(addr); + if (!dst) + return false; + + for (u32 i = 0; i < sizeof (pb) / sizeof (u16); ++i) + dst[i] = Common::swap16(src[i]); + + return true; +} + +// Simulated accelerator state. +static u32 acc_loop_addr, acc_end_addr; +static u32* acc_cur_addr; +static AXPB* acc_pb; + +// Sets up the simulated accelerator. +inline void AcceleratorSetup(AXPB* pb, u32* cur_addr) +{ + acc_pb = pb; + 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; +} + +// Reads a sample from the simulated accelerator. +inline u16 AcceleratorGetSample() +{ + u16 ret; + + switch (acc_pb->audio_addr.sample_format) + { + case 0x00: // ADPCM + { + if ((*acc_cur_addr & 15) == 0) + { + acc_pb->adpcm.pred_scale = DSP::ReadARAM((*acc_cur_addr & ~15) >> 1); + *acc_cur_addr += 2; + } + + int scale = 1 << (acc_pb->adpcm.pred_scale & 0xF); + int coef_idx = (acc_pb->adpcm.pred_scale >> 4) & 0x7; + + s32 coef1 = acc_pb->adpcm.coefs[coef_idx * 2 + 0]; + s32 coef2 = acc_pb->adpcm.coefs[coef_idx * 2 + 1]; + + int temp = (*acc_cur_addr & 1) ? + (DSP::ReadARAM(*acc_cur_addr >> 1) & 0xF) : + (DSP::ReadARAM(*acc_cur_addr >> 1) >> 4); + + if (temp >= 8) + temp -= 16; + + int val = (scale * temp) + ((0x400 + coef1 * acc_pb->adpcm.yn1 + coef2 * acc_pb->adpcm.yn2) >> 11); + + if (val > 0x7FFF) val = 0x7FFF; + else if (val < -0x7FFF) val = -0x7FFF; + + acc_pb->adpcm.yn2 = acc_pb->adpcm.yn1; + acc_pb->adpcm.yn1 = val; + *acc_cur_addr += 1; + ret = val; + break; + } + + case 0x0A: // 16-bit PCM audio + ret = (DSP::ReadARAM(*acc_cur_addr * 2) << 8) | DSP::ReadARAM(*acc_cur_addr * 2 + 1); + acc_pb->adpcm.yn2 = acc_pb->adpcm.yn1; + acc_pb->adpcm.yn1 = ret; + *acc_cur_addr += 1; + break; + + case 0x19: // 8-bit PCM audio + ret = DSP::ReadARAM(*acc_cur_addr) << 8; + acc_pb->adpcm.yn2 = acc_pb->adpcm.yn1; + acc_pb->adpcm.yn1 = ret; + *acc_cur_addr += 1; + break; + + default: + ERROR_LOG(DSPHLE, "Unknown sample format: %d", acc_pb->audio_addr.sample_format); + return 0; + } + + if (*acc_cur_addr >= acc_end_addr) + { + if ((*acc_cur_addr & ~0x1F) == (acc_end_addr & ~0x1F)) + *acc_cur_addr = acc_loop_addr; + + // Simulate an ACC overflow interrupt. + if (acc_pb->audio_addr.looping) + { + 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 + { + acc_pb->running = 0; + } + } + + return ret; +} + +// Read 32 input samples from ARAM, decoding and converting rate if required. +inline void GetInputSamples(AXPB& pb, s16* samples) +{ + 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) + { + u32 ratio = HILO_TO_32(pb.src.ratio); + + u32 curr_pos = pb.src.cur_addr_frac; + u32 real_samples_needed = (32 * ratio + curr_pos) >> 16; + s16 real_samples[130]; // Max supported ratio is 4 + + real_samples[0] = pb.src.last_samples[2]; + real_samples[1] = pb.src.last_samples[3]; + for (u32 i = 0; i < real_samples_needed; ++i) + real_samples[i + 2] = AcceleratorGetSample(); + + for (u32 i = 0; i < 32; ++i) + { + u32 curr_int_pos = (curr_pos >> 16); + s32 curr_frac_pos = curr_pos & 0xFFFF; + s16 samp1 = real_samples[curr_int_pos]; + s16 samp2 = real_samples[curr_int_pos + 1]; + + s16 sample = samp1 + (s16)(((samp2 - samp1) * (s32)curr_frac_pos) >> 16); + samples[i] = sample; + + curr_pos += ratio; + } + + if (real_samples_needed >= 2) + memcpy(pb.src.last_samples, &real_samples[real_samples_needed + 2 - 4], 4 * sizeof (u16)); + else + { + memmove(pb.src.last_samples, &pb.src.last_samples[real_samples_needed], (4 - real_samples_needed) * sizeof (u16)); + memcpy(&pb.src.last_samples[4 - real_samples_needed], &real_samples[2], real_samples_needed * sizeof (u16)); + } + pb.src.cur_addr_frac = curr_pos & 0xFFFF; + } + else // SRCTYPE_NEAREST + { + for (u32 i = 0; i < 32; ++i) + samples[i] = AcceleratorGetSample(); + + memcpy(pb.src.last_samples, samples + 28, 4 * sizeof (u16)); + } + + pb.audio_addr.cur_addr_hi = (u16)(cur_addr >> 16); + pb.audio_addr.cur_addr_lo = (u16)(cur_addr & 0xFFFF); +} + +// Mix samples to an output buffer, with optional volume ramping. +inline void MixAdd(int* out, const s16* input, u16* pvol, bool ramp) +{ + u16& volume = pvol[0]; + u16 volume_delta = pvol[1]; + if (!ramp) + volume_delta = 0; + + for (u32 i = 0; i < 32; ++i) + { + s64 sample = 2 * (s16)input[i] * (s16)volume; + out[i] += (s32)(sample >> 16); + volume += volume_delta; + } +} + +// Process 1ms of audio from a PB and mix it to the buffers. +inline void Process1ms(AXPB& pb, const AXBuffers& buffers) +{ + // 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]; + GetInputSamples(pb, samples); + + // Apply a global volume ramp using the volume envelope parameters. + for (u32 i = 0; i < 32; ++i) + { + s64 sample = 2 * (s16)samples[i] * (s16)pb.vol_env.cur_volume; + samples[i] = (s16)(sample >> 16); + pb.vol_env.cur_volume += pb.vol_env.cur_volume_delta; + } + + // Optionally, execute a low pass filter + if (pb.lpf.enabled) + { + // TODO + } + + // Mix LRS, AUXA and AUXB depending on mixer_control + // TODO: Handle DPL2 on AUXB. + + // HACK: at the moment we don't mix surround into left and right, so always + // mix left and right in order to have sound even if a game uses surround + // only. + //if (pb.mixer_control & MIX_L) + MixAdd(buffers.left, samples, &pb.mixer.left, pb.mixer_control & MIX_RAMP); + //if (pb.mixer_control & MIX_R) + MixAdd(buffers.right, samples, &pb.mixer.right, pb.mixer_control & MIX_RAMP); + if (pb.mixer_control & MIX_S) + MixAdd(buffers.surround, samples, &pb.mixer.surround, pb.mixer_control & MIX_RAMP); + + if (pb.mixer_control & MIX_AUXA_L) + MixAdd(buffers.auxA_left, samples, &pb.mixer.auxA_left, pb.mixer_control & MIX_AUXA_RAMPLR); + if (pb.mixer_control & MIX_AUXA_R) + MixAdd(buffers.auxA_right, samples, &pb.mixer.auxA_right, pb.mixer_control & MIX_AUXA_RAMPLR); + if (pb.mixer_control & MIX_AUXA_S) + MixAdd(buffers.auxA_surround, samples, &pb.mixer.auxA_surround, pb.mixer_control & MIX_AUXA_RAMPS); + + if (pb.mixer_control & MIX_AUXB_L) + MixAdd(buffers.auxB_left, samples, &pb.mixer.auxB_left, pb.mixer_control & MIX_AUXB_RAMPLR); + if (pb.mixer_control & MIX_AUXB_R) + MixAdd(buffers.auxB_right, samples, &pb.mixer.auxB_right, pb.mixer_control & MIX_AUXB_RAMPLR); + if (pb.mixer_control & MIX_AUXB_S) + MixAdd(buffers.auxB_surround, samples, &pb.mixer.auxB_surround, pb.mixer_control & MIX_AUXB_RAMPS); + + // Optionally, phase shift left or right channel to simulate 3D sound. + if (pb.initial_time_delay.on) + { + // TODO + } +} + +#endif // !_UCODE_NEWAX_VOICE_H From 5a2d8661d7113ed2fd1c51d5f4e10b5ce385459a Mon Sep 17 00:00:00 2001 From: Pierre Bourdon Date: Thu, 15 Nov 2012 16:57:33 +0100 Subject: [PATCH 14/44] Add comments explaining how the voice processing works --- .../Src/HW/DSPHLE/UCodes/UCode_NewAX_Voice.h | 59 +++++++++++++++++-- 1 file changed, 53 insertions(+), 6 deletions(-) diff --git a/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_NewAX_Voice.h b/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_NewAX_Voice.h index 8ebbec2fd9..7c455d527e 100644 --- a/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_NewAX_Voice.h +++ b/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_NewAX_Voice.h @@ -89,7 +89,9 @@ inline void AcceleratorSetup(AXPB* pb, u32* cur_addr) acc_cur_addr = cur_addr; } -// Reads a sample from the simulated accelerator. +// Reads a sample from the simulated accelerator. Also handles looping and +// disabling streams that reached the end (this is done by an exception raised +// by the accelerator on real hardware). inline u16 AcceleratorGetSample() { u16 ret; @@ -98,6 +100,7 @@ inline u16 AcceleratorGetSample() { case 0x00: // ADPCM { + // ADPCM decoding, not much to explain here. if ((*acc_cur_addr & 15) == 0) { acc_pb->adpcm.pred_scale = DSP::ReadARAM((*acc_cur_addr & ~15) >> 1); @@ -148,14 +151,24 @@ inline 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 >= acc_end_addr) { + // If we are really at the end (and we don't simply have cur_addr > + // end_addr all the time), loop back to loop_addr. if ((*acc_cur_addr & ~0x1F) == (acc_end_addr & ~0x1F)) *acc_cur_addr = acc_loop_addr; - // Simulate an ACC overflow interrupt. 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) { @@ -165,6 +178,7 @@ inline u16 AcceleratorGetSample() } else { + // Non looping voice reached the end -> running = 0. acc_pb->running = 0; } } @@ -181,12 +195,31 @@ inline void GetInputSamples(AXPB& pb, s16* samples) // TODO: support polyphase interpolation if coefficients are available. if (pb.src_type == SRCTYPE_POLYPHASE || pb.src_type == SRCTYPE_LINEAR) { + // 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; - u32 real_samples_needed = (32 * ratio + curr_pos) >> 16; - s16 real_samples[130]; // Max supported ratio is 4 + // Compute the number of real samples we will need to read from the + // data source. We need to output 32 samples, so we need to read + // 32 * ratio + curr_pos samples. The maximum possible ratio available + // on the DSP is 4.0, so at most we will read 128 real samples. + s16 real_samples[130]; + u32 real_samples_needed = (32 * ratio + curr_pos) >> 16; + + // The first two real samples are the ones we read at the previous + // iteration. That way we can interpolate before having read 2 new + // samples from the accelerator. + // + // The next real samples are read from the accelerator. real_samples[0] = pb.src.last_samples[2]; real_samples[1] = pb.src.last_samples[3]; for (u32 i = 0; i < real_samples_needed; ++i) @@ -194,17 +227,24 @@ inline void GetInputSamples(AXPB& pb, s16* samples) for (u32 i = 0; i < 32; ++i) { + // Get our current integer and fractional position. The integer + // position is used to get the two samples around us. The + // fractional position is used to compute the linear interpolation + // between these two samples. u32 curr_int_pos = (curr_pos >> 16); s32 curr_frac_pos = curr_pos & 0xFFFF; s16 samp1 = real_samples[curr_int_pos]; s16 samp2 = real_samples[curr_int_pos + 1]; + // Linear interpolation: s1 + (s2 - s1) * pos s16 sample = samp1 + (s16)(((samp2 - samp1) * (s32)curr_frac_pos) >> 16); samples[i] = sample; curr_pos += ratio; } + // Update the last_samples array. A bit tricky because we can't know + // for sure we have more than 4 real samples in our array. if (real_samples_needed >= 2) memcpy(pb.src.last_samples, &real_samples[real_samples_needed + 2 - 4], 4 * sizeof (u16)); else @@ -216,21 +256,28 @@ inline void GetInputSamples(AXPB& pb, s16* samples) } else // SRCTYPE_NEAREST { + // No sample rate conversion here: simply read 32 samples from the + // accelerator to the output buffer. for (u32 i = 0; i < 32; ++i) samples[i] = AcceleratorGetSample(); memcpy(pb.src.last_samples, samples + 28, 4 * sizeof (u16)); } + // 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); } -// Mix samples to an output buffer, with optional volume ramping. +// Add samples to an output buffer, with optional volume ramping. inline void MixAdd(int* out, const s16* input, u16* pvol, bool ramp) { u16& volume = pvol[0]; u16 volume_delta = pvol[1]; + + // If volume ramping is disabled, set volume_delta to 0. That way, the + // mixing loop can avoid testing if volume ramping is enabled at each step, + // and just add volume_delta. if (!ramp) volume_delta = 0; @@ -242,7 +289,7 @@ inline void MixAdd(int* out, const s16* input, u16* pvol, bool ramp) } } -// Process 1ms of audio from a PB and mix it to the buffers. +// Process 1ms of audio (32 samples) from a PB and mix it to the buffers. inline void Process1ms(AXPB& pb, const AXBuffers& buffers) { // If the voice is not running, nothing to do. From c8b2ba1bc6086f9ac954c84b39bbd62ac582000d Mon Sep 17 00:00:00 2001 From: Pierre Bourdon Date: Fri, 16 Nov 2012 07:51:19 +0100 Subject: [PATCH 15/44] Implement the MORE command (0D) used to read more commands from the CPU --- .../Core/Core/Src/HW/DSPHLE/UCodes/UCode_NewAX.cpp | 14 +++++++++++--- .../Core/Core/Src/HW/DSPHLE/UCodes/UCode_NewAX.h | 2 +- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_NewAX.cpp b/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_NewAX.cpp index a7e9c561f0..ccb1c25661 100644 --- a/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_NewAX.cpp +++ b/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_NewAX.cpp @@ -79,6 +79,7 @@ void CUCode_NewAX::HandleCommandList() // Temp variables for addresses computation u16 addr_hi, addr_lo; u16 addr2_hi, addr2_lo; + u16 size; u32 pb_addr = 0; @@ -90,8 +91,7 @@ void CUCode_NewAX::HandleCommandList() switch (cmd) { - - // A lot of these commands are unknown, or unused in this AX HLE. + // 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: @@ -140,7 +140,15 @@ void CUCode_NewAX::HandleCommandList() case CMD_COMPRESSOR_TABLE_ADDR: curr_idx += 2; break; case CMD_UNK_0B: break; // TODO: check other versions case CMD_UNK_0C: break; // TODO: check other versions - case CMD_UNK_0D: curr_idx += 2; break; + + case CMD_MORE: + addr_hi = m_cmdlist[curr_idx++]; + addr_lo = m_cmdlist[curr_idx++]; + size = m_cmdlist[curr_idx++]; + + CopyCmdList(HILO_TO_32(addr), size); + curr_idx = 0; + break; case CMD_OUTPUT: // Skip the first address, it is used for surround audio diff --git a/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_NewAX.h b/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_NewAX.h index 91aa495c0e..a6cbe071de 100644 --- a/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_NewAX.h +++ b/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_NewAX.h @@ -64,7 +64,7 @@ private: CMD_COMPRESSOR_TABLE_ADDR = 0x0A, CMD_UNK_0B = 0x0B, CMD_UNK_0C = 0x0C, - CMD_UNK_0D = 0x0D, + CMD_MORE = 0x0D, CMD_OUTPUT = 0x0E, CMD_END = 0x0F, CMD_UNK_10 = 0x10, From 3541d33c255164a9a274341bedbb8cfe5add3794 Mon Sep 17 00:00:00 2001 From: Pierre Bourdon Date: Mon, 19 Nov 2012 20:10:37 +0100 Subject: [PATCH 16/44] Support both versions of the mixer_control bitfield. Fixes Skies of Arcadia music being muted (and sounds being mixed only on the left audio channel), this time without a hack. --- .../Src/HW/DSPHLE/UCodes/UCode_AXStructs.h | 20 ------ .../Src/HW/DSPHLE/UCodes/UCode_AX_Voice.h | 2 +- .../Core/Src/HW/DSPHLE/UCodes/UCode_NewAX.cpp | 58 ++++++++++++++++- .../Core/Src/HW/DSPHLE/UCodes/UCode_NewAX.h | 6 ++ .../Src/HW/DSPHLE/UCodes/UCode_NewAX_Voice.h | 65 +++++++++++++------ 5 files changed, 108 insertions(+), 43 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 e7701e4698..c9751afec2 100644 --- a/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_AXStructs.h +++ b/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_AXStructs.h @@ -372,26 +372,6 @@ enum { SRCTYPE_NEAREST = 2, }; -enum { - MIX_L = 0x0001, - MIX_R = 0x0002, - MIX_S = 0x0004, - MIX_RAMP = 0x0008, - - MIX_AUXA_L = 0x0010, - MIX_AUXA_R = 0x0020, - MIX_AUXA_RAMPLR = 0x0040, - MIX_AUXA_S = 0x0080, - MIX_AUXA_RAMPS = 0x0100, - - MIX_AUXB_L = 0x0200, - MIX_AUXB_R = 0x0400, - MIX_AUXB_RAMPLR = 0x0800, - MIX_AUXB_S = 0x1000, - MIX_AUXB_RAMPS = 0x2000, - MIX_AUXB_DPL2 = 0x4000 -}; - // Both may be used at once enum { FILTER_LOWPASS = 1, 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 f401cc630f..8315b702db 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 @@ -230,7 +230,7 @@ inline void MixAddVoice(ParamBlockType &pb, const AXBuffers& buffers, int vol = pb.vol_env.cur_volume >> 9; sample = sample * vol >> 8; - if (pb.mixer_control & MIX_RAMP) + if (pb.mixer_control & 8) { 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 diff --git a/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_NewAX.cpp b/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_NewAX.cpp index ccb1c25661..4279fabc29 100644 --- a/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_NewAX.cpp +++ b/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_NewAX.cpp @@ -19,13 +19,12 @@ #include "UCode_NewAX_Voice.h" #include "../../DSP.h" -#define MIXBUF_MAX_SAMPLES 16000 // 500ms of stereo audio - CUCode_NewAX::CUCode_NewAX(DSPHLE* dsp_hle, u32 crc) : IUCode(dsp_hle, crc) , m_cmdlist_size(0) , m_axthread(&SpawnAXThread, this) { + WARN_LOG(DSPHLE, "Instantiating CUCode_NewAX: crc=%08x", crc); m_rMailHandler.PushMail(DSP_INIT); DSP::GenerateDSPInterruptFromDSPEmu(DSP::INT_DSP); } @@ -83,6 +82,11 @@ void CUCode_NewAX::HandleCommandList() u32 pb_addr = 0; +// 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) @@ -193,6 +197,54 @@ static void ApplyUpdatesForMs(AXPB& pb, int curr_ms) } } +AXMixControl CUCode_NewAX::ConvertMixerControl(u32 mixer_control) +{ + u32 ret = 0; + + // TODO: find other UCode versions with different mixer_control values + if (m_CRC == 0x4e8a8b21) + { + ret |= MIX_L | MIX_R; + if (mixer_control & 0x0001) ret |= MIX_AUXA_L | MIX_AUXA_R; + if (mixer_control & 0x0002) ret |= MIX_AUXB_L | MIX_AUXB_R; + if (mixer_control & 0x0004) + { + ret |= MIX_S; + if (ret & MIX_AUXA_L) ret |= MIX_AUXA_S; + if (ret & MIX_AUXB_L) ret |= MIX_AUXB_S; + } + if (mixer_control & 0x0008) + { + ret |= MIX_L_RAMP | MIX_R_RAMP; + if (ret & MIX_AUXA_L) ret |= MIX_AUXA_L_RAMP | MIX_AUXA_R_RAMP; + if (ret & MIX_AUXB_L) ret |= MIX_AUXB_L_RAMP | MIX_AUXB_R_RAMP; + if (ret & MIX_AUXA_S) ret |= MIX_AUXA_S_RAMP; + if (ret & MIX_AUXB_S) ret |= MIX_AUXB_S_RAMP; + } + } + else + { + if (mixer_control & 0x0001) ret |= MIX_L; + if (mixer_control & 0x0002) ret |= MIX_R; + if (mixer_control & 0x0004) ret |= MIX_S; + if (mixer_control & 0x0008) ret |= MIX_L_RAMP | MIX_R_RAMP | MIX_S_RAMP; + if (mixer_control & 0x0010) ret |= MIX_AUXA_L; + if (mixer_control & 0x0020) ret |= MIX_AUXA_R; + if (mixer_control & 0x0040) ret |= MIX_AUXA_L_RAMP | MIX_AUXA_R_RAMP; + if (mixer_control & 0x0080) ret |= MIX_AUXA_S; + if (mixer_control & 0x0100) ret |= MIX_AUXA_S_RAMP; + if (mixer_control & 0x0200) ret |= MIX_AUXB_L; + if (mixer_control & 0x0400) ret |= MIX_AUXB_R; + if (mixer_control & 0x0800) ret |= MIX_AUXB_L_RAMP | MIX_AUXB_R_RAMP; + if (mixer_control & 0x1000) ret |= MIX_AUXB_S; + if (mixer_control & 0x2000) ret |= MIX_AUXB_S_RAMP; + + // TODO: 0x4000 is used for Dolby Pro 2 sound mixing + } + + return (AXMixControl)ret; +} + void CUCode_NewAX::SetupProcessing(u32 init_addr) { u16 init_data[0x20]; @@ -263,7 +315,7 @@ void CUCode_NewAX::ProcessPBList(u32 pb_addr) { ApplyUpdatesForMs(pb, curr_ms); - Process1ms(pb, buffers); + Process1ms(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_NewAX.h b/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_NewAX.h index a6cbe071de..0aaa9b605c 100644 --- a/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_NewAX.h +++ b/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_NewAX.h @@ -20,6 +20,7 @@ #include "UCodes.h" #include "UCode_AXStructs.h" +#include "UCode_NewAX_Voice.h" class CUCode_NewAX : public IUCode { @@ -99,6 +100,11 @@ private: // Copy a command list from memory to our temp buffer void CopyCmdList(u32 addr, u16 size); + // 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); + // Send a notification to the AX thread to tell him a new cmdlist addr is // available for processing. void NotifyAXThread(); diff --git a/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_NewAX_Voice.h b/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_NewAX_Voice.h index 7c455d527e..82e749dbf0 100644 --- a/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_NewAX_Voice.h +++ b/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_NewAX_Voice.h @@ -47,6 +47,33 @@ union AXBuffers int* ptrs[9]; }; +// 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 +// mixer_control value to an AXMixControl bitfield. +enum AXMixControl +{ + MIX_L = 0x00001, + MIX_L_RAMP = 0x00002, + MIX_R = 0x00004, + MIX_R_RAMP = 0x00008, + MIX_S = 0x00010, + MIX_S_RAMP = 0x00020, + + MIX_AUXA_L = 0x00040, + MIX_AUXA_L_RAMP = 0x00080, + MIX_AUXA_R = 0x00100, + MIX_AUXA_R_RAMP = 0x00200, + MIX_AUXA_S = 0x00400, + MIX_AUXA_S_RAMP = 0x00800, + + MIX_AUXB_L = 0x01000, + MIX_AUXB_L_RAMP = 0x02000, + MIX_AUXB_R = 0x04000, + MIX_AUXB_R_RAMP = 0x08000, + MIX_AUXB_S = 0x10000, + MIX_AUXB_S_RAMP = 0x20000 +}; + // Read a PB from MRAM/ARAM inline bool ReadPB(u32 addr, AXPB& pb) { @@ -290,7 +317,7 @@ inline void MixAdd(int* out, const s16* input, u16* pvol, bool ramp) } // Process 1ms of audio (32 samples) from a PB and mix it to the buffers. -inline void Process1ms(AXPB& pb, const AXBuffers& buffers) +inline void Process1ms(AXPB& pb, const AXBuffers& buffers, AXMixControl mctrl) { // If the voice is not running, nothing to do. if (!pb.running) @@ -320,26 +347,26 @@ inline void Process1ms(AXPB& pb, const AXBuffers& buffers) // HACK: at the moment we don't mix surround into left and right, so always // mix left and right in order to have sound even if a game uses surround // only. - //if (pb.mixer_control & MIX_L) - MixAdd(buffers.left, samples, &pb.mixer.left, pb.mixer_control & MIX_RAMP); - //if (pb.mixer_control & MIX_R) - MixAdd(buffers.right, samples, &pb.mixer.right, pb.mixer_control & MIX_RAMP); - if (pb.mixer_control & MIX_S) - MixAdd(buffers.surround, samples, &pb.mixer.surround, pb.mixer_control & MIX_RAMP); + if (mctrl & MIX_L) + MixAdd(buffers.left, samples, &pb.mixer.left, mctrl & MIX_L_RAMP); + if (mctrl & MIX_R) + MixAdd(buffers.right, samples, &pb.mixer.right, mctrl & MIX_R_RAMP); + if (mctrl & MIX_S) + MixAdd(buffers.surround, samples, &pb.mixer.surround, mctrl & MIX_S_RAMP); - if (pb.mixer_control & MIX_AUXA_L) - MixAdd(buffers.auxA_left, samples, &pb.mixer.auxA_left, pb.mixer_control & MIX_AUXA_RAMPLR); - if (pb.mixer_control & MIX_AUXA_R) - MixAdd(buffers.auxA_right, samples, &pb.mixer.auxA_right, pb.mixer_control & MIX_AUXA_RAMPLR); - if (pb.mixer_control & MIX_AUXA_S) - MixAdd(buffers.auxA_surround, samples, &pb.mixer.auxA_surround, pb.mixer_control & MIX_AUXA_RAMPS); + if (mctrl & MIX_AUXA_L) + MixAdd(buffers.auxA_left, samples, &pb.mixer.auxA_left, mctrl & MIX_AUXA_L_RAMP); + if (mctrl & MIX_AUXA_R) + MixAdd(buffers.auxA_right, samples, &pb.mixer.auxA_right, mctrl & MIX_AUXA_R_RAMP); + if (mctrl & MIX_AUXA_S) + MixAdd(buffers.auxA_surround, samples, &pb.mixer.auxA_surround, mctrl & MIX_AUXA_S_RAMP); - if (pb.mixer_control & MIX_AUXB_L) - MixAdd(buffers.auxB_left, samples, &pb.mixer.auxB_left, pb.mixer_control & MIX_AUXB_RAMPLR); - if (pb.mixer_control & MIX_AUXB_R) - MixAdd(buffers.auxB_right, samples, &pb.mixer.auxB_right, pb.mixer_control & MIX_AUXB_RAMPLR); - if (pb.mixer_control & MIX_AUXB_S) - MixAdd(buffers.auxB_surround, samples, &pb.mixer.auxB_surround, pb.mixer_control & MIX_AUXB_RAMPS); + if (mctrl & MIX_AUXB_L) + MixAdd(buffers.auxB_left, samples, &pb.mixer.auxB_left, mctrl & MIX_AUXB_L_RAMP); + if (mctrl & MIX_AUXB_R) + MixAdd(buffers.auxB_right, samples, &pb.mixer.auxB_right, mctrl & MIX_AUXB_R_RAMP); + if (mctrl & MIX_AUXB_S) + MixAdd(buffers.auxB_surround, samples, &pb.mixer.auxB_surround, mctrl & MIX_AUXB_S_RAMP); // Optionally, phase shift left or right channel to simulate 3D sound. if (pb.initial_time_delay.on) From 4f88fee560d99ae9fbdb78e98a1a2abb678c9abb Mon Sep 17 00:00:00 2001 From: Pierre Bourdon Date: Mon, 19 Nov 2012 20:25:57 +0100 Subject: [PATCH 17/44] Added a small TODO list of things missing in NewAX --- Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_NewAX.h | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_NewAX.h b/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_NewAX.h index 0aaa9b605c..2614ea1389 100644 --- a/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_NewAX.h +++ b/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_NewAX.h @@ -15,6 +15,15 @@ // Official Git repository and contact information can be found at // http://code.google.com/p/dolphin-emu/ +// High-level emulation for the AX Gamecube UCode. +// +// TODO: +// * Depop support +// * ITD support +// * Polyphase sample interpolation support (not very useful) +// * Surround sound mixing +// * Dolby Pro 2 mixing with recent AX versions + #ifndef _UCODE_NEWAX_H #define _UCODE_NEWAX_H From e750bed2a996bfe13cc4f33656f967209b94c5c5 Mon Sep 17 00:00:00 2001 From: Pierre Bourdon Date: Mon, 19 Nov 2012 22:03:56 +0100 Subject: [PATCH 18/44] Rename NewAX -> AX and remove the old code. Time to work on AXWii. --- Source/Core/Core/CMakeLists.txt | 1 - Source/Core/Core/Core.vcxproj | 3 - Source/Core/Core/Core.vcxproj.filters | 9 - .../Core/Src/HW/DSPHLE/UCodes/UCode_AX.cpp | 828 +++++++++--------- .../Core/Core/Src/HW/DSPHLE/UCodes/UCode_AX.h | 127 ++- .../Src/HW/DSPHLE/UCodes/UCode_AXStructs.h | 3 +- .../Core/Src/HW/DSPHLE/UCodes/UCode_AXWii.cpp | 9 +- .../Src/HW/DSPHLE/UCodes/UCode_AX_Voice.h | 602 +++++++------ .../Core/Src/HW/DSPHLE/UCodes/UCode_NewAX.cpp | 499 ----------- .../Core/Src/HW/DSPHLE/UCodes/UCode_NewAX.h | 130 --- .../Src/HW/DSPHLE/UCodes/UCode_NewAX_Voice.h | 378 -------- .../Core/Core/Src/HW/DSPHLE/UCodes/UCodes.cpp | 3 +- 12 files changed, 859 insertions(+), 1733 deletions(-) delete mode 100644 Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_NewAX.cpp delete mode 100644 Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_NewAX.h delete mode 100644 Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_NewAX_Voice.h diff --git a/Source/Core/Core/CMakeLists.txt b/Source/Core/Core/CMakeLists.txt index 3182d248f8..734d966781 100644 --- a/Source/Core/Core/CMakeLists.txt +++ b/Source/Core/Core/CMakeLists.txt @@ -74,7 +74,6 @@ set(SRCS Src/ActionReplay.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_NewAX.cpp Src/HW/DSPHLE/UCodes/UCode_ROM.cpp Src/HW/DSPHLE/UCodes/UCodes.cpp Src/HW/DSPHLE/UCodes/UCode_GBA.cpp diff --git a/Source/Core/Core/Core.vcxproj b/Source/Core/Core/Core.vcxproj index caab0a1e6d..d83fa7fcc7 100644 --- a/Source/Core/Core/Core.vcxproj +++ b/Source/Core/Core/Core.vcxproj @@ -265,7 +265,6 @@ - @@ -467,8 +466,6 @@ - - diff --git a/Source/Core/Core/Core.vcxproj.filters b/Source/Core/Core/Core.vcxproj.filters index e0df3bd31f..8e2592450c 100644 --- a/Source/Core/Core/Core.vcxproj.filters +++ b/Source/Core/Core/Core.vcxproj.filters @@ -206,9 +206,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 @@ -751,12 +748,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 - 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 d990d282d7..8c2e05dce4 100644 --- a/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_AX.cpp +++ b/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_AX.cpp @@ -12,480 +12,488 @@ // 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/ -#include "FileUtil.h" // For IsDirectory() -#include "StringUtil.h" // For StringFromFormat() -#include - -#include "Mixer.h" -#include "../MailHandler.h" -#include "../../DSP.h" -#include "UCodes.h" -#include "UCode_AXStructs.h" #include "UCode_AX.h" #include "UCode_AX_Voice.h" +#include "../../DSP.h" -CUCode_AX::CUCode_AX(DSPHLE *dsp_hle, u32 l_CRC) - : IUCode(dsp_hle, l_CRC) - , m_addressPBs(0xFFFFFFFF) +CUCode_AX::CUCode_AX(DSPHLE* dsp_hle, u32 crc) + : IUCode(dsp_hle, crc) + , m_cmdlist_size(0) + , m_axthread(&SpawnAXThread, this) { - // we got loaded + WARN_LOG(DSPHLE, "Instantiating CUCode_AX: crc=%08x", crc); m_rMailHandler.PushMail(DSP_INIT); - - templbuffer = new int[1024 * 1024]; - temprbuffer = new int[1024 * 1024]; + DSP::GenerateDSPInterruptFromDSPEmu(DSP::INT_DSP); } CUCode_AX::~CUCode_AX() { + m_cmdlist_size = (u16)-1; // Special value to signal end + NotifyAXThread(); + m_axthread.join(); + m_rMailHandler.Clear(); - delete [] templbuffer; - delete [] temprbuffer; } -// Needs A LOT of love! -static void ProcessUpdates(AXPB &PB) +void CUCode_AX::SpawnAXThread(CUCode_AX* self) { - // Make the updates we are told to do. When there are multiple updates for a block they - // are placed in memory directly following updaddr. They are mostly for initial time - // delays, sometimes for the FIR filter or channel volumes. We do all of them at once here. - // If we get both an on and an off update we chose on. Perhaps that makes the RE1 music - // work better. - int numupd = PB.updates.num_updates[0] - + PB.updates.num_updates[1] - + PB.updates.num_updates[2] - + PB.updates.num_updates[3] - + PB.updates.num_updates[4]; - if (numupd > 64) numupd = 64; // prevent crazy values TODO: LOL WHAT - const u32 updaddr = (u32)(PB.updates.data_hi << 16) | PB.updates.data_lo; - int on = 0, off = 0; - for (int j = 0; j < numupd; j++) + self->AXThread(); +} + +void CUCode_AX::AXThread() +{ + while (true) { - const u16 updpar = HLEMemory_Read_U16(updaddr + j*4); - const u16 upddata = HLEMemory_Read_U16(updaddr + j*4 + 2); - // some safety checks, I hope it's enough - if (updaddr > 0x80000000 && updaddr < 0x817fffff - && updpar < 63 && updpar > 3 // updpar > 3 because we don't want to change - // 0-3, those are important - //&& (upd0 || upd1 || upd2 || upd3 || upd4) // We should use these in some way to I think - // but I don't know how or when - ) { - ((u16*)&PB)[updpar] = upddata; // WTF ABOUNDS! + std::unique_lock lk(m_cmdlist_mutex); + while (m_cmdlist_size == 0) + m_cmdlist_cv.wait(lk); } - if (updpar == 7 && upddata != 0) on++; - if (updpar == 7 && upddata == 0) off++; - } - // hack: if we get both an on and an off select on rather than off - if (on > 0 && off > 0) PB.running = 1; -} -static void VoiceHacks(AXPB &pb) -{ - // get necessary values - const u32 sampleEnd = (pb.audio_addr.end_addr_hi << 16) | pb.audio_addr.end_addr_lo; - const u32 loopPos = (pb.audio_addr.loop_addr_hi << 16) | pb.audio_addr.loop_addr_lo; - // const u32 updaddr = (u32)(pb.updates.data_hi << 16) | pb.updates.data_lo; - // const u16 updpar = HLEMemory_Read_U16(updaddr); - // const u16 upddata = HLEMemory_Read_U16(updaddr + 2); + if (m_cmdlist_size == (u16)-1) // End of thread signal + break; - // ======================================================================================= - /* Fix problems introduced with the SSBM fix. Sometimes when a music stream ended sampleEnd - would end up outside of bounds while the block was still playing resulting in noise - a strange noise. This should take care of that. - */ - if ((sampleEnd > (0x017fffff * 2) || loopPos > (0x017fffff * 2))) // ARAM bounds in nibbles - { - pb.running = 0; + m_processing.lock(); + HandleCommandList(); + m_cmdlist_size = 0; - // also reset all values if it makes any difference - pb.audio_addr.cur_addr_hi = 0; pb.audio_addr.cur_addr_lo = 0; - pb.audio_addr.end_addr_hi = 0; pb.audio_addr.end_addr_lo = 0; - pb.audio_addr.loop_addr_hi = 0; pb.audio_addr.loop_addr_lo = 0; - - pb.src.cur_addr_frac = 0; pb.src.ratio_hi = 0; pb.src.ratio_lo = 0; - pb.adpcm.pred_scale = 0; pb.adpcm.yn1 = 0; pb.adpcm.yn2 = 0; - - pb.audio_addr.looping = 0; - pb.adpcm_loop_info.pred_scale = 0; - pb.adpcm_loop_info.yn1 = 0; pb.adpcm_loop_info.yn2 = 0; - } - - /* - // the fact that no settings are reset (except running) after a SSBM type music stream or another - looping block (for example in Battle Stadium DON) has ended could cause loud garbled sound to be - played from one or more blocks. Perhaps it was in conjunction with the old sequenced music fix below, - I'm not sure. This was an attempt to prevent that anyway by resetting all. But I'm not sure if this - is needed anymore. Please try to play SSBM without it and see if it works anyway. - */ - if ( - // detect blocks that have recently been running that we should reset - pb.running == 0 && pb.audio_addr.looping == 1 - //pb.running == 0 && pb.adpcm_loop_info.pred_scale - - // this prevents us from ruining sequenced music blocks, may not be needed - /* - && !(pb.updates.num_updates[0] || pb.updates.num_updates[1] || pb.updates.num_updates[2] - || pb.updates.num_updates[3] || pb.updates.num_updates[4]) - */ - //&& !(updpar || upddata) - - && pb.mixer_control == 0 // only use this in SSBM - ) - { - // reset the detection values - pb.audio_addr.looping = 0; - pb.adpcm_loop_info.pred_scale = 0; - pb.adpcm_loop_info.yn1 = 0; pb.adpcm_loop_info.yn2 = 0; - - //pb.audio_addr.cur_addr_hi = 0; pb.audio_addr.cur_addr_lo = 0; - //pb.audio_addr.end_addr_hi = 0; pb.audio_addr.end_addr_lo = 0; - //pb.audio_addr.loop_addr_hi = 0; pb.audio_addr.loop_addr_lo = 0; - - //pb.src.cur_addr_frac = 0; PBs[i].src.ratio_hi = 0; PBs[i].src.ratio_lo = 0; - //pb.adpcm.pred_scale = 0; pb.adpcm.yn1 = 0; pb.adpcm.yn2 = 0; + // Signal end of processing + m_rMailHandler.PushMail(DSP_YIELD); + DSP::GenerateDSPInterruptFromDSPEmu(DSP::INT_DSP); + m_processing.unlock(); } } -void CUCode_AX::MixAdd(short* _pBuffer, int _iSize) +void CUCode_AX::NotifyAXThread() { - if (_iSize > 1024 * 1024) - _iSize = 1024 * 1024; + std::unique_lock lk(m_cmdlist_mutex); + m_cmdlist_cv.notify_one(); +} - memset(templbuffer, 0, _iSize * sizeof(int)); - memset(temprbuffer, 0, _iSize * sizeof(int)); +void CUCode_AX::HandleCommandList() +{ + // Temp variables for addresses computation + u16 addr_hi, addr_lo; + u16 addr2_hi, addr2_lo; + u16 size; - AXPB PB; - AXBuffers buffers = {{ - templbuffer, - temprbuffer, - NULL - }}; + u32 pb_addr = 0; - for (int x = 0; x < numPBaddr; x++) +// 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) { - //u32 blockAddr = m_addressPBs; - u32 blockAddr = PBaddr[x]; + u16 cmd = m_cmdlist[curr_idx++]; - if (!blockAddr) - return; - - for (int i = 0; i < NUMBER_OF_PBS; i++) + switch (cmd) { - if (!ReadPB(blockAddr, PB)) + // 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; - if (m_CRC != 0x3389a79e) - VoiceHacks(PB); + case CMD_UNK_01: curr_idx += 5; break; - MixAddVoice(PB, buffers, _iSize); - - if (!WritePB(blockAddr, PB)) + case CMD_PB_ADDR: + addr_hi = m_cmdlist[curr_idx++]; + addr_lo = m_cmdlist[curr_idx++]; + pb_addr = HILO_TO_32(addr); break; - // next PB, or done - blockAddr = (PB.next_pb_hi << 16) | PB.next_pb_lo; - if (!blockAddr) + case CMD_PROCESS: + ProcessPBList(pb_addr); + break; + + case CMD_MIX_AUXA: + case CMD_MIX_AUXB: + // These two commands are handled almost the same internally. + 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)); + break; + + case CMD_UPLOAD_LRS: + addr_hi = m_cmdlist[curr_idx++]; + addr_lo = m_cmdlist[curr_idx++]; + UploadLRS(HILO_TO_32(addr)); + break; + + case CMD_SBUFFER_ADDR: curr_idx += 2; break; + case CMD_UNK_08: curr_idx += 10; break; // TODO: check + + case CMD_MIX_AUXB_NOWRITE: + addr_hi = m_cmdlist[curr_idx++]; + addr_lo = m_cmdlist[curr_idx++]; + MixAUXSamples(false, 0, HILO_TO_32(addr)); + break; + + case CMD_COMPRESSOR_TABLE_ADDR: curr_idx += 2; break; + case CMD_UNK_0B: break; // TODO: check other versions + case CMD_UNK_0C: break; // TODO: check other versions + + case CMD_MORE: + addr_hi = m_cmdlist[curr_idx++]; + addr_lo = m_cmdlist[curr_idx++]; + size = m_cmdlist[curr_idx++]; + + CopyCmdList(HILO_TO_32(addr), size); + curr_idx = 0; + break; + + case CMD_OUTPUT: + // Skip the first address, it is used for surround audio + // output, which we don't support yet. + curr_idx += 2; + + addr_hi = m_cmdlist[curr_idx++]; + addr_lo = m_cmdlist[curr_idx++]; + OutputSamples(HILO_TO_32(addr)); + break; + + case CMD_END: + end = true; + break; + + case CMD_UNK_10: curr_idx += 4; break; + case CMD_UNK_11: curr_idx += 2; break; + case CMD_UNK_12: curr_idx += 1; break; + case CMD_UNK_13: curr_idx += 12; break; + + default: + ERROR_LOG(DSPHLE, "Unknown command in AX cmdlist: %04x", cmd); + end = true; break; } } +} - if (_pBuffer) +static void ApplyUpdatesForMs(AXPB& pb, int curr_ms) +{ + u32 start_idx = 0; + for (int i = 0; i < curr_ms; ++i) + start_idx += pb.updates.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 (int i = 0; i < _iSize; i++) - { - // Clamp into 16-bit. Maybe we should add a volume compressor here. - int left = templbuffer[i] + _pBuffer[0]; - int right = temprbuffer[i] + _pBuffer[1]; - if (left < -32767) left = -32767; - if (left > 32767) left = 32767; - if (right < -32767) right = -32767; - if (right > 32767) right = 32767; - *_pBuffer++ = left; - *_pBuffer++ = right; - } + u16 update_off = HLEMemory_Read_U16(update_addr + 4 * i); + u16 update_val = HLEMemory_Read_U16(update_addr + 4 * i + 2); + + ((u16*)&pb)[update_off] = update_val; } } - -// ------------------------------------------------------------------------------ -// Handle incoming mail -void CUCode_AX::HandleMail(u32 _uMail) +AXMixControl CUCode_AX::ConvertMixerControl(u32 mixer_control) { - if (m_UploadSetupInProgress) + u32 ret = 0; + + // TODO: find other UCode versions with different mixer_control values + if (m_CRC == 0x4e8a8b21) { - PrepareBootUCode(_uMail); - return; + ret |= MIX_L | MIX_R; + if (mixer_control & 0x0001) ret |= MIX_AUXA_L | MIX_AUXA_R; + if (mixer_control & 0x0002) ret |= MIX_AUXB_L | MIX_AUXB_R; + if (mixer_control & 0x0004) + { + ret |= MIX_S; + if (ret & MIX_AUXA_L) ret |= MIX_AUXA_S; + if (ret & MIX_AUXB_L) ret |= MIX_AUXB_S; + } + if (mixer_control & 0x0008) + { + ret |= MIX_L_RAMP | MIX_R_RAMP; + if (ret & MIX_AUXA_L) ret |= MIX_AUXA_L_RAMP | MIX_AUXA_R_RAMP; + if (ret & MIX_AUXB_L) ret |= MIX_AUXB_L_RAMP | MIX_AUXB_R_RAMP; + if (ret & MIX_AUXA_S) ret |= MIX_AUXA_S_RAMP; + if (ret & MIX_AUXB_S) ret |= MIX_AUXB_S_RAMP; + } } - else { - if ((_uMail & 0xFFFF0000) == MAIL_AX_ALIST) - { - // We are expected to get a new CmdBlock - DEBUG_LOG(DSPHLE, "GetNextCmdBlock (%ibytes)", (u16)_uMail); - } - else if (_uMail == 0xCDD10000) // Action 0 - AX_ResumeTask(); - { - m_rMailHandler.PushMail(DSP_RESUME); - } - else if (_uMail == 0xCDD10001) // Action 1 - new ucode upload ( GC: BayBlade S.T.B,...) - { - DEBUG_LOG(DSPHLE,"DSP IROM - New Ucode!"); - // TODO find a better way to protect from HLEMixer? - soundStream->GetMixer()->SetHLEReady(false); - m_UploadSetupInProgress = true; - } - else if (_uMail == 0xCDD10002) // Action 2 - IROM_Reset(); ( GC: NFS Carbon, FF Crystal Chronicles,...) - { - DEBUG_LOG(DSPHLE,"DSP IROM - Reset!"); - m_DSPHLE->SetUCode(UCODE_ROM); - return; - } - else if (_uMail == 0xCDD10003) // Action 3 - AX_GetNextCmdBlock(); - { - } + else + { + if (mixer_control & 0x0001) ret |= MIX_L; + if (mixer_control & 0x0002) ret |= MIX_R; + if (mixer_control & 0x0004) ret |= MIX_S; + if (mixer_control & 0x0008) ret |= MIX_L_RAMP | MIX_R_RAMP | MIX_S_RAMP; + if (mixer_control & 0x0010) ret |= MIX_AUXA_L; + if (mixer_control & 0x0020) ret |= MIX_AUXA_R; + if (mixer_control & 0x0040) ret |= MIX_AUXA_L_RAMP | MIX_AUXA_R_RAMP; + if (mixer_control & 0x0080) ret |= MIX_AUXA_S; + if (mixer_control & 0x0100) ret |= MIX_AUXA_S_RAMP; + if (mixer_control & 0x0200) ret |= MIX_AUXB_L; + if (mixer_control & 0x0400) ret |= MIX_AUXB_R; + if (mixer_control & 0x0800) ret |= MIX_AUXB_L_RAMP | MIX_AUXB_R_RAMP; + if (mixer_control & 0x1000) ret |= MIX_AUXB_S; + if (mixer_control & 0x2000) ret |= MIX_AUXB_S_RAMP; + + // TODO: 0x4000 is used for Dolby Pro 2 sound mixing + } + + return (AXMixControl)ret; +} + +void CUCode_AX::SetupProcessing(u32 init_addr) +{ + u16 init_data[0x20]; + + for (u32 i = 0; i < 0x20; ++i) + init_data[i] = HLEMemory_Read_U16(init_addr + 2 * i); + + // List of all buffers we have to initialize + int* 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 + }; + + 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], 0, 5 * 32 * sizeof (int)); else { - DEBUG_LOG(DSPHLE, " >>>> u32 MAIL : AXTask Mail (%08x)", _uMail); - AXTask(_uMail); + for (u32 j = 0; j < 32 * 5; ++j) + { + buffers[i][j] = init_val; + init_val += delta; + } } } } +void CUCode_AX::ProcessPBList(u32 pb_addr) +{ + // Samples per millisecond. In theory DSP sampling rate can be changed from + // 32KHz to 48KHz, but AX always process at 32KHz. + const u32 spms = 32; + + AXPB 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 + }}; + + if (!ReadPB(pb_addr, pb)) + break; + + for (int curr_ms = 0; curr_ms < 5; ++curr_ms) + { + ApplyUpdatesForMs(pb, curr_ms); + + Process1ms(pb, buffers, ConvertMixerControl(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_AX::MixAUXSamples(bool AUXA, u32 write_addr, u32 read_addr) +{ + int buffers[3][5 * 32]; + + // First, we need to send the contents of our AUX buffers to the CPU. + if (write_addr) + { + for (u32 i = 0; i < 5 * 32; ++i) + { + if (AUXA) + { + buffers[0][i] = Common::swap32(m_samples_auxA_left[i]); + buffers[1][i] = Common::swap32(m_samples_auxA_right[i]); + buffers[2][i] = Common::swap32(m_samples_auxA_surround[i]); + } + else + { + buffers[0][i] = Common::swap32(m_samples_auxB_left[i]); + buffers[1][i] = Common::swap32(m_samples_auxB_right[i]); + buffers[2][i] = Common::swap32(m_samples_auxB_surround[i]); + } + } + memcpy(HLEMemory_Get_Pointer(write_addr), buffers, sizeof (buffers)); + } + + // Then, we read the new buffers from the CPU and add to our current + // buffers. + memcpy(buffers, HLEMemory_Get_Pointer(read_addr), sizeof (buffers)); + for (u32 i = 0; i < 5 * 32; ++i) + { + m_samples_left[i] += Common::swap32(buffers[0][i]); + m_samples_right[i] += Common::swap32(buffers[1][i]); + m_samples_surround[i] += Common::swap32(buffers[2][i]); + } +} + +void CUCode_AX::UploadLRS(u32 dst_addr) +{ + int buffers[3][5 * 32]; + + for (u32 i = 0; i < 5 * 32; ++i) + { + buffers[0][i] = Common::swap32(m_samples_left[i]); + buffers[1][i] = Common::swap32(m_samples_right[i]); + buffers[2][i] = Common::swap32(m_samples_surround[i]); + } + memcpy(HLEMemory_Get_Pointer(dst_addr), buffers, sizeof (buffers)); +} + +void CUCode_AX::OutputSamples(u32 out_addr) +{ + // 32 samples per ms, 5 ms, 2 channels + short buffer[5 * 32 * 2]; + + // Clamp internal buffers to 16 bits. + for (u32 i = 0; i < 5 * 32; ++i) + { + int left = m_samples_left[i]; + int right = m_samples_right[i]; + + 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 < 5 * 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(out_addr), buffer, sizeof (buffer)); +} + +void CUCode_AX::HandleMail(u32 mail) +{ + // Indicates if the next message is a command list address. + static bool next_is_cmdlist = false; + static u16 cmdlist_size = 0; + + bool set_next_is_cmdlist = false; + + // Wait for DSP processing to be done before answering any mail. This is + // safe to do because it matches what the DSP does on real hardware: there + // is no interrupt when a mail from CPU is received. + m_processing.lock(); + + if (next_is_cmdlist) + { + CopyCmdList(mail, cmdlist_size); + NotifyAXThread(); + } + else if (m_UploadSetupInProgress) + { + PrepareBootUCode(mail); + } + else if (mail == MAIL_RESUME) + { + // Acknowledge the resume request + m_rMailHandler.PushMail(DSP_RESUME); + DSP::GenerateDSPInterruptFromDSPEmu(DSP::INT_DSP); + } + else if (mail == MAIL_NEW_UCODE) + { + soundStream->GetMixer()->SetHLEReady(false); + m_UploadSetupInProgress = true; + } + else if (mail == MAIL_RESET) + { + m_DSPHLE->SetUCode(UCODE_ROM); + } + else if (mail == MAIL_CONTINUE) + { + // We don't have to do anything here - the CPU does not wait for a ACK + // and sends a cmdlist mail just after. + } + else if ((mail & MAIL_CMDLIST_MASK) == MAIL_CMDLIST) + { + // A command list address is going to be sent next. + set_next_is_cmdlist = true; + cmdlist_size = (u16)(mail & ~MAIL_CMDLIST_MASK); + } + else + { + ERROR_LOG(DSPHLE, "Unknown mail sent to AX::HandleMail: %08x", mail); + } + + m_processing.unlock(); + next_is_cmdlist = set_next_is_cmdlist; +} + +void CUCode_AX::CopyCmdList(u32 addr, u16 size) +{ + if (size >= (sizeof (m_cmdlist) / sizeof (u16))) + { + ERROR_LOG(DSPHLE, "Command list at %08x is too large: size=%d", addr, size); + return; + } + + for (u32 i = 0; i < size; ++i, addr += 2) + m_cmdlist[i] = HLEMemory_Read_U16(addr); + m_cmdlist_size = size; +} + +void CUCode_AX::MixAdd(short* out_buffer, int nsamples) +{ + // Should never be called: we do not set HLE as ready. + // We accurately send samples to RAM instead of directly to the mixer. +} -// ------------------------------------------------------------------------------ -// Update with DSP Interrupt void CUCode_AX::Update(int cycles) { + // Used for UCode switching. if (NeedsResumeMail()) { m_rMailHandler.PushMail(DSP_RESUME); DSP::GenerateDSPInterruptFromDSPEmu(DSP::INT_DSP); } - // check if we have to send something - else if (!m_rMailHandler.IsEmpty()) - { - DSP::GenerateDSPInterruptFromDSPEmu(DSP::INT_DSP); - } } -// ============================================ -// AX seems to bootup one task only and waits for resume-callbacks -// everytime the DSP has "spare time" it sends a resume-mail to the CPU -// and the __DSPHandler calls a AX-Callback which generates a new AXFrame -bool CUCode_AX::AXTask(u32& _uMail) +void CUCode_AX::DoState(PointerWrap& p) { - u32 uAddress = _uMail; - DEBUG_LOG(DSPHLE, "Begin"); - DEBUG_LOG(DSPHLE, "====================================================================="); - DEBUG_LOG(DSPHLE, "%08x : AXTask - AXCommandList-Addr:", uAddress); - - u32 Addr__AXStudio; - u32 Addr__AXOutSBuffer; - u32 Addr__AXOutSBuffer_1; - u32 Addr__AXOutSBuffer_2; - u32 Addr__A; - u32 Addr__12; - u32 Addr__4_1; - u32 Addr__4_2; - //u32 Addr__4_3; - //u32 Addr__4_4; - u32 Addr__5_1; - u32 Addr__5_2; - u32 Addr__6; - u32 Addr__9; - - bool bExecuteList = true; - - numPBaddr = 0; - - while (bExecuteList) - { - static int last_valid_command = 0; - u16 iCommand = HLEMemory_Read_U16(uAddress); - uAddress += 2; - - switch (iCommand) - { - case AXLIST_STUDIOADDR: //00 - Addr__AXStudio = HLEMemory_Read_U32(uAddress); - uAddress += 4; - DEBUG_LOG(DSPHLE, "%08x : AXLIST studio address: %08x", uAddress, Addr__AXStudio); - break; - - case 0x001: // 2byte x 10 - { - u32 address = HLEMemory_Read_U32(uAddress); - uAddress += 4; - u16 param1 = HLEMemory_Read_U16(uAddress); - uAddress += 2; - u16 param2 = HLEMemory_Read_U16(uAddress); - uAddress += 2; - u16 param3 = HLEMemory_Read_U16(uAddress); - uAddress += 2; - DEBUG_LOG(DSPHLE, "%08x : AXLIST 1: %08x, %04x, %04x, %04x", uAddress, address, param1, param2, param3); - } - break; - - // - // Somewhere we should be getting a bitmask of AX_SYNC values - // that tells us what has been updated - // Dunno if important - // - case AXLIST_PBADDR: //02 - { - PBaddr[numPBaddr] = HLEMemory_Read_U32(uAddress); - numPBaddr++; - - // HACK: process updates right now instead of waiting until - // Premix is called. Some games using sequenced music (Tales of - // Symphonia for example) thought PBs were unused because we - // were too slow to update them and set them as running. This - // happens because Premix is basically completely desync-ed - // from the emulation core (it's running in the audio thread). - // Fixing this would require rewriting most of the AX HLE. - u32 block_addr = uAddress; - AXPB pb; - for (int i = 0; block_addr && i < NUMBER_OF_PBS; i++) - { - if (!ReadPB(block_addr, pb)) - break; - ProcessUpdates(pb); - WritePB(block_addr, pb); - block_addr = (pb.next_pb_hi << 16) | pb.next_pb_lo; - } - - m_addressPBs = HLEMemory_Read_U32(uAddress); // left in for now - uAddress += 4; - soundStream->GetMixer()->SetHLEReady(true); - DEBUG_LOG(DSPHLE, "%08x : AXLIST PB address: %08x", uAddress, m_addressPBs); - } - break; - - case 0x0003: - DEBUG_LOG(DSPHLE, "%08x : AXLIST command 0x0003 ????", uAddress); - break; - - case 0x0004: // AUX? - Addr__4_1 = HLEMemory_Read_U32(uAddress); - uAddress += 4; - Addr__4_2 = HLEMemory_Read_U32(uAddress); - uAddress += 4; - DEBUG_LOG(DSPHLE, "%08x : AXLIST 4_1 4_2 addresses: %08x %08x", uAddress, Addr__4_1, Addr__4_2); - break; - - case 0x0005: - Addr__5_1 = HLEMemory_Read_U32(uAddress); - uAddress += 4; - Addr__5_2 = HLEMemory_Read_U32(uAddress); - uAddress += 4; - DEBUG_LOG(DSPHLE, "%08x : AXLIST 5_1 5_2 addresses: %08x %08x", uAddress, Addr__5_1, Addr__5_2); - break; - - case 0x0006: - Addr__6 = HLEMemory_Read_U32(uAddress); - uAddress += 4; - DEBUG_LOG(DSPHLE, "%08x : AXLIST 6 address: %08x", uAddress, Addr__6); - break; - - case AXLIST_SBUFFER: - Addr__AXOutSBuffer = HLEMemory_Read_U32(uAddress); - uAddress += 4; - DEBUG_LOG(DSPHLE, "%08x : AXLIST OutSBuffer address: %08x", uAddress, Addr__AXOutSBuffer); - break; - - case 0x0009: - Addr__9 = HLEMemory_Read_U32(uAddress); - uAddress += 4; - DEBUG_LOG(DSPHLE, "%08x : AXLIST 6 address: %08x", uAddress, Addr__9); - break; - - case AXLIST_COMPRESSORTABLE: // 0xa - Addr__A = HLEMemory_Read_U32(uAddress); - uAddress += 4; - DEBUG_LOG(DSPHLE, "%08x : AXLIST CompressorTable address: %08x", uAddress, Addr__A); - break; - - case 0x000e: - Addr__AXOutSBuffer_1 = HLEMemory_Read_U32(uAddress); - uAddress += 4; - - // Addr__AXOutSBuffer_2 is the address in RAM that we are supposed to mix to. - // Although we don't, currently. - Addr__AXOutSBuffer_2 = HLEMemory_Read_U32(uAddress); - uAddress += 4; - DEBUG_LOG(DSPHLE, "%08x : AXLIST sbuf2 addresses: %08x %08x", uAddress, Addr__AXOutSBuffer_1, Addr__AXOutSBuffer_2); - break; - - case AXLIST_END: - bExecuteList = false; - DEBUG_LOG(DSPHLE, "%08x : AXLIST end", uAddress); - break; - - case 0x0010: //Super Monkey Ball 2 - DEBUG_LOG(DSPHLE, "%08x : AXLIST 0x0010", uAddress); - //should probably read/skip stuff here - uAddress += 8; - break; - - case 0x0011: - uAddress += 4; - break; - - case 0x0012: - Addr__12 = HLEMemory_Read_U16(uAddress); - uAddress += 2; - break; - - case 0x0013: - uAddress += 6 * 4; // 6 Addresses. - break; - - default: - { - static bool bFirst = true; - if (bFirst) - { - char szTemp[2048]; - sprintf(szTemp, "Unknown AX-Command 0x%x (address: 0x%08x). Last valid: %02x\n", - iCommand, uAddress - 2, last_valid_command); - int num = -32; - while (num < 64+32) - { - char szTemp2[128] = ""; - sprintf(szTemp2, "%s0x%04x\n", num == 0 ? ">>" : " ", HLEMemory_Read_U16(uAddress + num)); - strcat(szTemp, szTemp2); - num += 2; - } - - PanicAlert("%s", szTemp); - // bFirst = false; - } - - // unknown command so stop the execution of this TaskList - bExecuteList = false; - } - break; - } - if (bExecuteList) - last_valid_command = iCommand; - } - DEBUG_LOG(DSPHLE, "AXTask - done, send resume"); - DEBUG_LOG(DSPHLE, "====================================================================="); - DEBUG_LOG(DSPHLE, "End"); - - m_rMailHandler.PushMail(DSP_YIELD); - return true; -} - -void CUCode_AX::DoState(PointerWrap &p) -{ - std::lock_guard lk(m_csMix); - - p.Do(numPBaddr); - p.Do(m_addressPBs); - p.Do(PBaddr); + std::lock_guard lk(m_processing); DoStateShared(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 edb52a2801..29509661ed 100644 --- a/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_AX.h +++ b/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_AX.h @@ -12,52 +12,119 @@ // 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_AX -#define _UCODE_AX +// High-level emulation for the AX Gamecube UCode. +// +// TODO: +// * Depop support +// * ITD support +// * Polyphase sample interpolation support (not very useful) +// * Surround sound mixing +// * Dolby Pro 2 mixing with recent AX versions -#include +#ifndef _UCODE_AX_H +#define _UCODE_AX_H + +#include "UCodes.h" #include "UCode_AXStructs.h" +#include "UCode_AX_Voice.h" -enum -{ - NUMBER_OF_PBS = 128 -}; - -class CUCode_AX : public IUCode +class CUCode_AX : public IUCode { public: - CUCode_AX(DSPHLE *dsp_hle, u32 _CRC); + CUCode_AX(DSPHLE* dsp_hle, u32 crc); virtual ~CUCode_AX(); - void HandleMail(u32 _uMail); - void MixAdd(short* _pBuffer, int _iSize); + void HandleMail(u32 mail); + void MixAdd(short* out_buffer, int nsamples); void Update(int cycles); - void DoState(PointerWrap &p); + void DoState(PointerWrap& p); - // PBs - u8 numPBaddr; - u32 PBaddr[8]; //2 needed for MP2 - u32 m_addressPBs; + // Needed because StdThread.h std::thread implem does not support member + // pointers. + static void SpawnAXThread(CUCode_AX* self); private: - enum + enum MailType { - MAIL_AX_ALIST = 0xBABE0000, - AXLIST_STUDIOADDR = 0x0000, - AXLIST_PBADDR = 0x0002, - AXLIST_SBUFFER = 0x0007, - AXLIST_COMPRESSORTABLE = 0x000A, - AXLIST_END = 0x000F + MAIL_RESUME = 0xCDD10000, + MAIL_NEW_UCODE = 0xCDD10001, + MAIL_RESET = 0xCDD10002, + MAIL_CONTINUE = 0xCDD10003, + + // CPU sends 0xBABE0000 | cmdlist_size to the DSP + MAIL_CMDLIST = 0xBABE0000, + MAIL_CMDLIST_MASK = 0xFFFF0000 }; - int *templbuffer; - int *temprbuffer; + enum CmdType + { + CMD_SETUP = 0x00, + CMD_UNK_01 = 0x01, + CMD_PB_ADDR = 0x02, + CMD_PROCESS = 0x03, + CMD_MIX_AUXA = 0x04, + CMD_MIX_AUXB = 0x05, + CMD_UPLOAD_LRS = 0x06, + CMD_SBUFFER_ADDR = 0x07, + CMD_UNK_08 = 0x08, + CMD_MIX_AUXB_NOWRITE = 0x09, + CMD_COMPRESSOR_TABLE_ADDR = 0x0A, + CMD_UNK_0B = 0x0B, + CMD_UNK_0C = 0x0C, + CMD_MORE = 0x0D, + CMD_OUTPUT = 0x0E, + CMD_END = 0x0F, + CMD_UNK_10 = 0x10, + CMD_UNK_11 = 0x11, + CMD_UNK_12 = 0x12, + CMD_UNK_13 = 0x13, + }; - // ax task message handler - bool AXTask(u32& _uMail); + // 32 * 5 because 32 samples per millisecond, for 5 milliseconds. + int m_samples_left[32 * 5]; + int m_samples_right[32 * 5]; + int m_samples_surround[32 * 5]; + int m_samples_auxA_left[32 * 5]; + int m_samples_auxA_right[32 * 5]; + int m_samples_auxA_surround[32 * 5]; + int m_samples_auxB_left[32 * 5]; + int m_samples_auxB_right[32 * 5]; + int m_samples_auxB_surround[32 * 5]; + + // 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; + + std::thread m_axthread; + + // Sync objects + std::mutex m_processing; + std::condition_variable m_cmdlist_cv; + std::mutex m_cmdlist_mutex; + + // Copy a command list from memory to our temp buffer + void CopyCmdList(u32 addr, u16 size); + + // 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); + + // Send a notification to the AX thread to tell him a new cmdlist addr is + // available for processing. + void NotifyAXThread(); + + void AXThread(); + void HandleCommandList(); + void SetupProcessing(u32 studio_addr); + void ProcessPBList(u32 pb_addr); + void MixAUXSamples(bool AUXA, u32 write_addr, u32 read_addr); + void UploadLRS(u32 dst_addr); + void OutputSamples(u32 out_addr); }; -#endif // _UCODE_AX +#endif // !_UCODE_AX_H 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 c9751afec2..e3c6b7165b 100644 --- a/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_AXStructs.h +++ b/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_AXStructs.h @@ -285,7 +285,8 @@ struct AXPBWii 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 mixer_control_hi; + u16 mixer_control_lo; u16 running; // 1=RUN 0=STOP u16 is_stream; // 1 = stream, 0 = one shot 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 62d3bac6e4..73abc392df 100644 --- a/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_AXWii.cpp +++ b/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_AXWii.cpp @@ -22,7 +22,6 @@ #include "UCodes.h" #include "UCode_AXStructs.h" -#include "UCode_AX.h" // for some functions in CUCode_AX #include "UCode_AXWii.h" #include "UCode_AX_Voice.h" @@ -112,10 +111,10 @@ void CUCode_AXWii::MixAdd(short* _pBuffer, int _iSize) if (!ReadPB(blockAddr, PB)) break; - if (wiisportsHack) - MixAddVoice(*(AXPBWiiSports*)&PB, buffers, _iSize); - else - MixAddVoice(PB, buffers, _iSize); +// if (wiisportsHack) +// MixAddVoice(*(AXPBWiiSports*)&PB, buffers, _iSize); +// else +// MixAddVoice(PB, buffers, _iSize); if (!WritePB(blockAddr, PB)) break; 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 8315b702db..20fea41677 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 @@ -12,297 +12,369 @@ // 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_AX_VOICE_H #define _UCODE_AX_VOICE_H -#include "UCodes.h" -#include "UCode_AX_ADPCM.h" -#include "UCode_AX.h" -#include "Mixer.h" -#include "../../AudioInterface.h" +#include "Common.h" +#include "UCode_AXStructs.h" +#include "../../DSP.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; -} +// Useful macro to convert xxx_hi + xxx_lo to xxx for 32 bits. +#define HILO_TO_32(name) \ + ((name##_hi << 16) | name##_lo) +// Used to pass a large amount of buffers to the mixing function. union AXBuffers { struct { int* left; int* right; + int* surround; + int* auxA_left; int* auxA_right; + int* auxA_surround; + int* auxB_left; int* auxB_right; + int* auxB_surround; }; - int* ptrs[6]; + int* ptrs[9]; }; -////////////////////////////////////////////////////////////////////////// -// 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, const AXBuffers& buffers, - int _iSize, bool resample = true) +// 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 +// mixer_control value to an AXMixControl bitfield. +enum AXMixControl { - if (pb.running) - { - float ratioFactor; - if (resample) - ratioFactor = (float)AudioInterface::GetAIDSampleRate() / (float)soundStream->GetMixer()->GetSampleRate(); - else - ratioFactor = 1.0; + MIX_L = 0x00001, + MIX_L_RAMP = 0x00002, + MIX_R = 0x00004, + MIX_R_RAMP = 0x00008, + MIX_S = 0x00010, + MIX_S_RAMP = 0x00020, - const u32 ratio = (u32)(((pb.src.ratio_hi << 16) + pb.src.ratio_lo) * ratioFactor); - u32 sampleEnd = (pb.audio_addr.end_addr_hi << 16) | pb.audio_addr.end_addr_lo; - u32 loopPos = (pb.audio_addr.loop_addr_hi << 16) | pb.audio_addr.loop_addr_lo; + MIX_AUXA_L = 0x00040, + MIX_AUXA_L_RAMP = 0x00080, + MIX_AUXA_R = 0x00100, + MIX_AUXA_R_RAMP = 0x00200, + MIX_AUXA_S = 0x00400, + MIX_AUXA_S_RAMP = 0x00800, - u32 samplePos = (pb.audio_addr.cur_addr_hi << 16) | pb.audio_addr.cur_addr_lo; - u32 frac = pb.src.cur_addr_frac; + MIX_AUXB_L = 0x01000, + MIX_AUXB_L_RAMP = 0x02000, + MIX_AUXB_R = 0x04000, + MIX_AUXB_R_RAMP = 0x08000, + MIX_AUXB_S = 0x10000, + MIX_AUXB_S_RAMP = 0x20000 +}; - // ======================================================================================= - // 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; - } +// Read a PB from MRAM/ARAM +template +bool ReadPB(u32 addr, PBType& pb) +{ + u16* dst = (u16*)&pb; + const u16* src = (const u16*)Memory::GetPointer(addr); + if (!src) + return false; - // ======================================================================================= - // 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; - } + for (u32 i = 0; i < sizeof (pb) / sizeof (u16); ++i) + dst[i] = Common::swap16(src[i]); - - - // 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 & 8) - { - 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 auxAleftmix = pb.mixer.auxA_left >> 5; - int auxArightmix= pb.mixer.auxA_right >> 5; - int auxBleftmix = pb.mixer.auxB_left >> 5; - int auxBrightmix= pb.mixer.auxB_right >> 5; - - int left = sample * leftmix >> 8; - int right = sample * rightmix >> 8; - int auxAleft = sample * auxAleftmix >> 8; - int auxAright = sample * auxArightmix >> 8; - int auxBleft = sample * auxBleftmix >> 8; - int auxBright = sample * auxBrightmix >> 8; - - // adpcm has to walk from oldSamplePos to samplePos here - if ((pb.mixer_control & 1) && buffers.left) buffers.left[s] += left; - if ((pb.mixer_control & 2) && buffers.right) buffers.right [s] += right; - if ((pb.mixer_control & 16) && buffers.auxA_left) buffers.auxA_left[s] += auxAleft; - if ((pb.mixer_control & 32) && buffers.auxA_right) buffers.auxA_right[s] += auxAright; - if ((pb.mixer_control & 512) && buffers.auxB_left) buffers.auxB_left[s] += auxBleft; - if ((pb.mixer_control & 1024) && buffers.auxB_right) buffers.auxB_right[s] += auxBright; - - // 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.mixer.auxA_left = ADPCM_Vol(pb.mixer.auxA_left, pb.mixer.auxA_left_delta); - pb.mixer.auxA_right = ADPCM_Vol(pb.mixer.auxA_right, pb.mixer.auxA_right_delta); - pb.mixer.auxB_left = ADPCM_Vol(pb.mixer.auxB_left, pb.mixer.auxB_left_delta); - pb.mixer.auxB_right = ADPCM_Vol(pb.mixer.auxB_right, pb.mixer.auxB_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) + return true; } -#endif +// Write a PB back to MRAM/ARAM +template +inline bool WritePB(u32 addr, const PBType& pb) +{ + const u16* src = (const u16*)&pb; + u16* dst = (u16*)Memory::GetPointer(addr); + if (!dst) + return false; + + for (u32 i = 0; i < sizeof (pb) / sizeof (u16); ++i) + dst[i] = Common::swap16(src[i]); + + return true; +} + +// Simulated accelerator state. +static u32 acc_loop_addr, acc_end_addr; +static u32* acc_cur_addr; +static AXPB* acc_pb; + +// Sets up the simulated accelerator. +inline void AcceleratorSetup(AXPB* pb, u32* cur_addr) +{ + acc_pb = pb; + 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; +} + +// Reads a sample from the simulated accelerator. Also handles looping and +// disabling streams that reached the end (this is done by an exception raised +// by the accelerator on real hardware). +inline u16 AcceleratorGetSample() +{ + u16 ret; + + switch (acc_pb->audio_addr.sample_format) + { + case 0x00: // ADPCM + { + // ADPCM decoding, not much to explain here. + if ((*acc_cur_addr & 15) == 0) + { + acc_pb->adpcm.pred_scale = DSP::ReadARAM((*acc_cur_addr & ~15) >> 1); + *acc_cur_addr += 2; + } + + int scale = 1 << (acc_pb->adpcm.pred_scale & 0xF); + int coef_idx = (acc_pb->adpcm.pred_scale >> 4) & 0x7; + + s32 coef1 = acc_pb->adpcm.coefs[coef_idx * 2 + 0]; + s32 coef2 = acc_pb->adpcm.coefs[coef_idx * 2 + 1]; + + int temp = (*acc_cur_addr & 1) ? + (DSP::ReadARAM(*acc_cur_addr >> 1) & 0xF) : + (DSP::ReadARAM(*acc_cur_addr >> 1) >> 4); + + if (temp >= 8) + temp -= 16; + + int val = (scale * temp) + ((0x400 + coef1 * acc_pb->adpcm.yn1 + coef2 * acc_pb->adpcm.yn2) >> 11); + + if (val > 0x7FFF) val = 0x7FFF; + else if (val < -0x7FFF) val = -0x7FFF; + + acc_pb->adpcm.yn2 = acc_pb->adpcm.yn1; + acc_pb->adpcm.yn1 = val; + *acc_cur_addr += 1; + ret = val; + break; + } + + case 0x0A: // 16-bit PCM audio + ret = (DSP::ReadARAM(*acc_cur_addr * 2) << 8) | DSP::ReadARAM(*acc_cur_addr * 2 + 1); + acc_pb->adpcm.yn2 = acc_pb->adpcm.yn1; + acc_pb->adpcm.yn1 = ret; + *acc_cur_addr += 1; + break; + + case 0x19: // 8-bit PCM audio + ret = DSP::ReadARAM(*acc_cur_addr) << 8; + acc_pb->adpcm.yn2 = acc_pb->adpcm.yn1; + acc_pb->adpcm.yn1 = ret; + *acc_cur_addr += 1; + break; + + default: + ERROR_LOG(DSPHLE, "Unknown sample format: %d", acc_pb->audio_addr.sample_format); + 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 >= acc_end_addr) + { + // If we are really at the end (and we don't simply have cur_addr > + // end_addr all the time), loop back to loop_addr. + if ((*acc_cur_addr & ~0x1F) == (acc_end_addr & ~0x1F)) + *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; +} + +// Read 32 input samples from ARAM, decoding and converting rate if required. +inline void GetInputSamples(AXPB& pb, s16* samples) +{ + 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) + { + // 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; + + // Compute the number of real samples we will need to read from the + // data source. We need to output 32 samples, so we need to read + // 32 * ratio + curr_pos samples. The maximum possible ratio available + // on the DSP is 4.0, so at most we will read 128 real samples. + s16 real_samples[130]; + u32 real_samples_needed = (32 * ratio + curr_pos) >> 16; + + // The first two real samples are the ones we read at the previous + // iteration. That way we can interpolate before having read 2 new + // samples from the accelerator. + // + // The next real samples are read from the accelerator. + real_samples[0] = pb.src.last_samples[2]; + real_samples[1] = pb.src.last_samples[3]; + for (u32 i = 0; i < real_samples_needed; ++i) + real_samples[i + 2] = AcceleratorGetSample(); + + for (u32 i = 0; i < 32; ++i) + { + // Get our current integer and fractional position. The integer + // position is used to get the two samples around us. The + // fractional position is used to compute the linear interpolation + // between these two samples. + u32 curr_int_pos = (curr_pos >> 16); + s32 curr_frac_pos = curr_pos & 0xFFFF; + s16 samp1 = real_samples[curr_int_pos]; + s16 samp2 = real_samples[curr_int_pos + 1]; + + // Linear interpolation: s1 + (s2 - s1) * pos + s16 sample = samp1 + (s16)(((samp2 - samp1) * (s32)curr_frac_pos) >> 16); + samples[i] = sample; + + curr_pos += ratio; + } + + // Update the last_samples array. A bit tricky because we can't know + // for sure we have more than 4 real samples in our array. + if (real_samples_needed >= 2) + memcpy(pb.src.last_samples, &real_samples[real_samples_needed + 2 - 4], 4 * sizeof (u16)); + else + { + memmove(pb.src.last_samples, &pb.src.last_samples[real_samples_needed], (4 - real_samples_needed) * sizeof (u16)); + memcpy(&pb.src.last_samples[4 - real_samples_needed], &real_samples[2], real_samples_needed * sizeof (u16)); + } + pb.src.cur_addr_frac = curr_pos & 0xFFFF; + } + else // SRCTYPE_NEAREST + { + // No sample rate conversion here: simply read 32 samples from the + // accelerator to the output buffer. + for (u32 i = 0; i < 32; ++i) + samples[i] = AcceleratorGetSample(); + + memcpy(pb.src.last_samples, samples + 28, 4 * sizeof (u16)); + } + + // 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); +} + +// Add samples to an output buffer, with optional volume ramping. +inline void MixAdd(int* out, const s16* input, u16* pvol, bool ramp) +{ + u16& volume = pvol[0]; + u16 volume_delta = pvol[1]; + + // If volume ramping is disabled, set volume_delta to 0. That way, the + // mixing loop can avoid testing if volume ramping is enabled at each step, + // and just add volume_delta. + if (!ramp) + volume_delta = 0; + + for (u32 i = 0; i < 32; ++i) + { + s64 sample = 2 * (s16)input[i] * (s16)volume; + out[i] += (s32)(sample >> 16); + volume += volume_delta; + } +} + +// Process 1ms of audio (32 samples) from a PB and mix it to the buffers. +inline void Process1ms(AXPB& 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]; + GetInputSamples(pb, samples); + + // Apply a global volume ramp using the volume envelope parameters. + for (u32 i = 0; i < 32; ++i) + { + s64 sample = 2 * (s16)samples[i] * (s16)pb.vol_env.cur_volume; + samples[i] = (s16)(sample >> 16); + pb.vol_env.cur_volume += pb.vol_env.cur_volume_delta; + } + + // Optionally, execute a low pass filter + if (pb.lpf.enabled) + { + // TODO + } + + // Mix LRS, AUXA and AUXB depending on mixer_control + // TODO: Handle DPL2 on AUXB. + + // HACK: at the moment we don't mix surround into left and right, so always + // mix left and right in order to have sound even if a game uses surround + // only. + if (mctrl & MIX_L) + MixAdd(buffers.left, samples, &pb.mixer.left, mctrl & MIX_L_RAMP); + if (mctrl & MIX_R) + MixAdd(buffers.right, samples, &pb.mixer.right, mctrl & MIX_R_RAMP); + if (mctrl & MIX_S) + MixAdd(buffers.surround, samples, &pb.mixer.surround, mctrl & MIX_S_RAMP); + + if (mctrl & MIX_AUXA_L) + MixAdd(buffers.auxA_left, samples, &pb.mixer.auxA_left, mctrl & MIX_AUXA_L_RAMP); + if (mctrl & MIX_AUXA_R) + MixAdd(buffers.auxA_right, samples, &pb.mixer.auxA_right, mctrl & MIX_AUXA_R_RAMP); + if (mctrl & MIX_AUXA_S) + MixAdd(buffers.auxA_surround, samples, &pb.mixer.auxA_surround, mctrl & MIX_AUXA_S_RAMP); + + if (mctrl & MIX_AUXB_L) + MixAdd(buffers.auxB_left, samples, &pb.mixer.auxB_left, mctrl & MIX_AUXB_L_RAMP); + if (mctrl & MIX_AUXB_R) + MixAdd(buffers.auxB_right, samples, &pb.mixer.auxB_right, mctrl & MIX_AUXB_R_RAMP); + if (mctrl & MIX_AUXB_S) + MixAdd(buffers.auxB_surround, samples, &pb.mixer.auxB_surround, mctrl & MIX_AUXB_S_RAMP); + + // Optionally, phase shift left or right channel to simulate 3D sound. + if (pb.initial_time_delay.on) + { + // TODO + } +} + +#endif // !_UCODE_AX_VOICE_H diff --git a/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_NewAX.cpp b/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_NewAX.cpp deleted file mode 100644 index 4279fabc29..0000000000 --- a/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_NewAX.cpp +++ /dev/null @@ -1,499 +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/ - -#include "UCode_NewAX.h" -#include "UCode_NewAX_Voice.h" -#include "../../DSP.h" - -CUCode_NewAX::CUCode_NewAX(DSPHLE* dsp_hle, u32 crc) - : IUCode(dsp_hle, crc) - , m_cmdlist_size(0) - , m_axthread(&SpawnAXThread, this) -{ - WARN_LOG(DSPHLE, "Instantiating CUCode_NewAX: crc=%08x", crc); - m_rMailHandler.PushMail(DSP_INIT); - DSP::GenerateDSPInterruptFromDSPEmu(DSP::INT_DSP); -} - -CUCode_NewAX::~CUCode_NewAX() -{ - m_cmdlist_size = (u16)-1; // Special value to signal end - NotifyAXThread(); - m_axthread.join(); - - m_rMailHandler.Clear(); -} - -void CUCode_NewAX::SpawnAXThread(CUCode_NewAX* self) -{ - self->AXThread(); -} - -void CUCode_NewAX::AXThread() -{ - while (true) - { - { - std::unique_lock lk(m_cmdlist_mutex); - while (m_cmdlist_size == 0) - m_cmdlist_cv.wait(lk); - } - - if (m_cmdlist_size == (u16)-1) // End of thread signal - break; - - m_processing.lock(); - HandleCommandList(); - m_cmdlist_size = 0; - - // Signal end of processing - m_rMailHandler.PushMail(DSP_YIELD); - DSP::GenerateDSPInterruptFromDSPEmu(DSP::INT_DSP); - m_processing.unlock(); - } -} - -void CUCode_NewAX::NotifyAXThread() -{ - std::unique_lock lk(m_cmdlist_mutex); - m_cmdlist_cv.notify_one(); -} - -void CUCode_NewAX::HandleCommandList() -{ - // Temp variables for addresses computation - u16 addr_hi, addr_lo; - u16 addr2_hi, addr2_lo; - u16 size; - - u32 pb_addr = 0; - -// 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 += 5; break; - - case CMD_PB_ADDR: - addr_hi = m_cmdlist[curr_idx++]; - addr_lo = m_cmdlist[curr_idx++]; - pb_addr = HILO_TO_32(addr); - break; - - case CMD_PROCESS: - ProcessPBList(pb_addr); - break; - - case CMD_MIX_AUXA: - case CMD_MIX_AUXB: - // These two commands are handled almost the same internally. - 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)); - break; - - case CMD_UPLOAD_LRS: - addr_hi = m_cmdlist[curr_idx++]; - addr_lo = m_cmdlist[curr_idx++]; - UploadLRS(HILO_TO_32(addr)); - break; - - case CMD_SBUFFER_ADDR: curr_idx += 2; break; - case CMD_UNK_08: curr_idx += 10; break; // TODO: check - - case CMD_MIX_AUXB_NOWRITE: - addr_hi = m_cmdlist[curr_idx++]; - addr_lo = m_cmdlist[curr_idx++]; - MixAUXSamples(false, 0, HILO_TO_32(addr)); - break; - - case CMD_COMPRESSOR_TABLE_ADDR: curr_idx += 2; break; - case CMD_UNK_0B: break; // TODO: check other versions - case CMD_UNK_0C: break; // TODO: check other versions - - case CMD_MORE: - addr_hi = m_cmdlist[curr_idx++]; - addr_lo = m_cmdlist[curr_idx++]; - size = m_cmdlist[curr_idx++]; - - CopyCmdList(HILO_TO_32(addr), size); - curr_idx = 0; - break; - - case CMD_OUTPUT: - // Skip the first address, it is used for surround audio - // output, which we don't support yet. - curr_idx += 2; - - addr_hi = m_cmdlist[curr_idx++]; - addr_lo = m_cmdlist[curr_idx++]; - OutputSamples(HILO_TO_32(addr)); - break; - - case CMD_END: - end = true; - break; - - case CMD_UNK_10: curr_idx += 4; break; - case CMD_UNK_11: curr_idx += 2; break; - case CMD_UNK_12: curr_idx += 1; break; - case CMD_UNK_13: curr_idx += 12; break; - - default: - ERROR_LOG(DSPHLE, "Unknown command in AX cmdlist: %04x", cmd); - end = true; - break; - } - } -} - -static void ApplyUpdatesForMs(AXPB& pb, int curr_ms) -{ - u32 start_idx = 0; - for (int i = 0; i < curr_ms; ++i) - start_idx += pb.updates.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) - { - u16 update_off = HLEMemory_Read_U16(update_addr + 4 * i); - u16 update_val = HLEMemory_Read_U16(update_addr + 4 * i + 2); - - ((u16*)&pb)[update_off] = update_val; - } -} - -AXMixControl CUCode_NewAX::ConvertMixerControl(u32 mixer_control) -{ - u32 ret = 0; - - // TODO: find other UCode versions with different mixer_control values - if (m_CRC == 0x4e8a8b21) - { - ret |= MIX_L | MIX_R; - if (mixer_control & 0x0001) ret |= MIX_AUXA_L | MIX_AUXA_R; - if (mixer_control & 0x0002) ret |= MIX_AUXB_L | MIX_AUXB_R; - if (mixer_control & 0x0004) - { - ret |= MIX_S; - if (ret & MIX_AUXA_L) ret |= MIX_AUXA_S; - if (ret & MIX_AUXB_L) ret |= MIX_AUXB_S; - } - if (mixer_control & 0x0008) - { - ret |= MIX_L_RAMP | MIX_R_RAMP; - if (ret & MIX_AUXA_L) ret |= MIX_AUXA_L_RAMP | MIX_AUXA_R_RAMP; - if (ret & MIX_AUXB_L) ret |= MIX_AUXB_L_RAMP | MIX_AUXB_R_RAMP; - if (ret & MIX_AUXA_S) ret |= MIX_AUXA_S_RAMP; - if (ret & MIX_AUXB_S) ret |= MIX_AUXB_S_RAMP; - } - } - else - { - if (mixer_control & 0x0001) ret |= MIX_L; - if (mixer_control & 0x0002) ret |= MIX_R; - if (mixer_control & 0x0004) ret |= MIX_S; - if (mixer_control & 0x0008) ret |= MIX_L_RAMP | MIX_R_RAMP | MIX_S_RAMP; - if (mixer_control & 0x0010) ret |= MIX_AUXA_L; - if (mixer_control & 0x0020) ret |= MIX_AUXA_R; - if (mixer_control & 0x0040) ret |= MIX_AUXA_L_RAMP | MIX_AUXA_R_RAMP; - if (mixer_control & 0x0080) ret |= MIX_AUXA_S; - if (mixer_control & 0x0100) ret |= MIX_AUXA_S_RAMP; - if (mixer_control & 0x0200) ret |= MIX_AUXB_L; - if (mixer_control & 0x0400) ret |= MIX_AUXB_R; - if (mixer_control & 0x0800) ret |= MIX_AUXB_L_RAMP | MIX_AUXB_R_RAMP; - if (mixer_control & 0x1000) ret |= MIX_AUXB_S; - if (mixer_control & 0x2000) ret |= MIX_AUXB_S_RAMP; - - // TODO: 0x4000 is used for Dolby Pro 2 sound mixing - } - - return (AXMixControl)ret; -} - -void CUCode_NewAX::SetupProcessing(u32 init_addr) -{ - u16 init_data[0x20]; - - for (u32 i = 0; i < 0x20; ++i) - init_data[i] = HLEMemory_Read_U16(init_addr + 2 * i); - - // List of all buffers we have to initialize - int* 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 - }; - - 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], 0, 5 * 32 * sizeof (int)); - else - { - for (u32 j = 0; j < 32 * 5; ++j) - { - buffers[i][j] = init_val; - init_val += delta; - } - } - } -} - -void CUCode_NewAX::ProcessPBList(u32 pb_addr) -{ - // Samples per millisecond. In theory DSP sampling rate can be changed from - // 32KHz to 48KHz, but AX always process at 32KHz. - const u32 spms = 32; - - AXPB 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 - }}; - - if (!ReadPB(pb_addr, pb)) - break; - - for (int curr_ms = 0; curr_ms < 5; ++curr_ms) - { - ApplyUpdatesForMs(pb, curr_ms); - - Process1ms(pb, buffers, ConvertMixerControl(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_NewAX::MixAUXSamples(bool AUXA, u32 write_addr, u32 read_addr) -{ - int buffers[3][5 * 32]; - - // First, we need to send the contents of our AUX buffers to the CPU. - if (write_addr) - { - for (u32 i = 0; i < 5 * 32; ++i) - { - if (AUXA) - { - buffers[0][i] = Common::swap32(m_samples_auxA_left[i]); - buffers[1][i] = Common::swap32(m_samples_auxA_right[i]); - buffers[2][i] = Common::swap32(m_samples_auxA_surround[i]); - } - else - { - buffers[0][i] = Common::swap32(m_samples_auxB_left[i]); - buffers[1][i] = Common::swap32(m_samples_auxB_right[i]); - buffers[2][i] = Common::swap32(m_samples_auxB_surround[i]); - } - } - memcpy(HLEMemory_Get_Pointer(write_addr), buffers, sizeof (buffers)); - } - - // Then, we read the new buffers from the CPU and add to our current - // buffers. - memcpy(buffers, HLEMemory_Get_Pointer(read_addr), sizeof (buffers)); - for (u32 i = 0; i < 5 * 32; ++i) - { - m_samples_left[i] += Common::swap32(buffers[0][i]); - m_samples_right[i] += Common::swap32(buffers[1][i]); - m_samples_surround[i] += Common::swap32(buffers[2][i]); - } -} - -void CUCode_NewAX::UploadLRS(u32 dst_addr) -{ - int buffers[3][5 * 32]; - - for (u32 i = 0; i < 5 * 32; ++i) - { - buffers[0][i] = Common::swap32(m_samples_left[i]); - buffers[1][i] = Common::swap32(m_samples_right[i]); - buffers[2][i] = Common::swap32(m_samples_surround[i]); - } - memcpy(HLEMemory_Get_Pointer(dst_addr), buffers, sizeof (buffers)); -} - -void CUCode_NewAX::OutputSamples(u32 out_addr) -{ - // 32 samples per ms, 5 ms, 2 channels - short buffer[5 * 32 * 2]; - - // Clamp internal buffers to 16 bits. - for (u32 i = 0; i < 5 * 32; ++i) - { - int left = m_samples_left[i]; - int right = m_samples_right[i]; - - 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 < 5 * 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(out_addr), buffer, sizeof (buffer)); -} - -void CUCode_NewAX::HandleMail(u32 mail) -{ - // Indicates if the next message is a command list address. - static bool next_is_cmdlist = false; - static u16 cmdlist_size = 0; - - bool set_next_is_cmdlist = false; - - // Wait for DSP processing to be done before answering any mail. This is - // safe to do because it matches what the DSP does on real hardware: there - // is no interrupt when a mail from CPU is received. - m_processing.lock(); - - if (next_is_cmdlist) - { - CopyCmdList(mail, cmdlist_size); - NotifyAXThread(); - } - else if (m_UploadSetupInProgress) - { - PrepareBootUCode(mail); - } - else if (mail == MAIL_RESUME) - { - // Acknowledge the resume request - m_rMailHandler.PushMail(DSP_RESUME); - DSP::GenerateDSPInterruptFromDSPEmu(DSP::INT_DSP); - } - else if (mail == MAIL_NEW_UCODE) - { - soundStream->GetMixer()->SetHLEReady(false); - m_UploadSetupInProgress = true; - } - else if (mail == MAIL_RESET) - { - m_DSPHLE->SetUCode(UCODE_ROM); - } - else if (mail == MAIL_CONTINUE) - { - // We don't have to do anything here - the CPU does not wait for a ACK - // and sends a cmdlist mail just after. - } - else if ((mail & MAIL_CMDLIST_MASK) == MAIL_CMDLIST) - { - // A command list address is going to be sent next. - set_next_is_cmdlist = true; - cmdlist_size = (u16)(mail & ~MAIL_CMDLIST_MASK); - } - else - { - ERROR_LOG(DSPHLE, "Unknown mail sent to AX::HandleMail: %08x", mail); - } - - m_processing.unlock(); - next_is_cmdlist = set_next_is_cmdlist; -} - -void CUCode_NewAX::CopyCmdList(u32 addr, u16 size) -{ - if (size >= (sizeof (m_cmdlist) / sizeof (u16))) - { - ERROR_LOG(DSPHLE, "Command list at %08x is too large: size=%d", addr, size); - return; - } - - for (u32 i = 0; i < size; ++i, addr += 2) - m_cmdlist[i] = HLEMemory_Read_U16(addr); - m_cmdlist_size = size; -} - -void CUCode_NewAX::MixAdd(short* out_buffer, int nsamples) -{ - // Should never be called: we do not set HLE as ready. - // We accurately send samples to RAM instead of directly to the mixer. -} - -void CUCode_NewAX::Update(int cycles) -{ - // Used for UCode switching. - if (NeedsResumeMail()) - { - m_rMailHandler.PushMail(DSP_RESUME); - DSP::GenerateDSPInterruptFromDSPEmu(DSP::INT_DSP); - } -} - -void CUCode_NewAX::DoState(PointerWrap& p) -{ - std::lock_guard lk(m_processing); - - DoStateShared(p); -} diff --git a/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_NewAX.h b/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_NewAX.h deleted file mode 100644 index 2614ea1389..0000000000 --- a/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_NewAX.h +++ /dev/null @@ -1,130 +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/ - -// High-level emulation for the AX Gamecube UCode. -// -// TODO: -// * Depop support -// * ITD support -// * Polyphase sample interpolation support (not very useful) -// * Surround sound mixing -// * Dolby Pro 2 mixing with recent AX versions - -#ifndef _UCODE_NEWAX_H -#define _UCODE_NEWAX_H - -#include "UCodes.h" -#include "UCode_AXStructs.h" -#include "UCode_NewAX_Voice.h" - -class CUCode_NewAX : public IUCode -{ -public: - CUCode_NewAX(DSPHLE* dsp_hle, u32 crc); - virtual ~CUCode_NewAX(); - - void HandleMail(u32 mail); - void MixAdd(short* out_buffer, int nsamples); - void Update(int cycles); - void DoState(PointerWrap& p); - - // Needed because StdThread.h std::thread implem does not support member - // pointers. - static void SpawnAXThread(CUCode_NewAX* self); - -private: - enum MailType - { - MAIL_RESUME = 0xCDD10000, - MAIL_NEW_UCODE = 0xCDD10001, - MAIL_RESET = 0xCDD10002, - MAIL_CONTINUE = 0xCDD10003, - - // CPU sends 0xBABE0000 | cmdlist_size to the DSP - MAIL_CMDLIST = 0xBABE0000, - MAIL_CMDLIST_MASK = 0xFFFF0000 - }; - - enum CmdType - { - CMD_SETUP = 0x00, - CMD_UNK_01 = 0x01, - CMD_PB_ADDR = 0x02, - CMD_PROCESS = 0x03, - CMD_MIX_AUXA = 0x04, - CMD_MIX_AUXB = 0x05, - CMD_UPLOAD_LRS = 0x06, - CMD_SBUFFER_ADDR = 0x07, - CMD_UNK_08 = 0x08, - CMD_MIX_AUXB_NOWRITE = 0x09, - CMD_COMPRESSOR_TABLE_ADDR = 0x0A, - CMD_UNK_0B = 0x0B, - CMD_UNK_0C = 0x0C, - CMD_MORE = 0x0D, - CMD_OUTPUT = 0x0E, - CMD_END = 0x0F, - CMD_UNK_10 = 0x10, - CMD_UNK_11 = 0x11, - CMD_UNK_12 = 0x12, - CMD_UNK_13 = 0x13, - }; - - // 32 * 5 because 32 samples per millisecond, for 5 milliseconds. - int m_samples_left[32 * 5]; - int m_samples_right[32 * 5]; - int m_samples_surround[32 * 5]; - int m_samples_auxA_left[32 * 5]; - int m_samples_auxA_right[32 * 5]; - int m_samples_auxA_surround[32 * 5]; - int m_samples_auxB_left[32 * 5]; - int m_samples_auxB_right[32 * 5]; - int m_samples_auxB_surround[32 * 5]; - - // 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; - - std::thread m_axthread; - - // Sync objects - std::mutex m_processing; - std::condition_variable m_cmdlist_cv; - std::mutex m_cmdlist_mutex; - - // Copy a command list from memory to our temp buffer - void CopyCmdList(u32 addr, u16 size); - - // 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); - - // Send a notification to the AX thread to tell him a new cmdlist addr is - // available for processing. - void NotifyAXThread(); - - void AXThread(); - void HandleCommandList(); - void SetupProcessing(u32 studio_addr); - void ProcessPBList(u32 pb_addr); - void MixAUXSamples(bool AUXA, u32 write_addr, u32 read_addr); - void UploadLRS(u32 dst_addr); - void OutputSamples(u32 out_addr); -}; - -#endif // !_UCODE_NEWAX_H diff --git a/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_NewAX_Voice.h b/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_NewAX_Voice.h deleted file mode 100644 index 82e749dbf0..0000000000 --- a/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_NewAX_Voice.h +++ /dev/null @@ -1,378 +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_NEWAX_VOICE_H -#define _UCODE_NEWAX_VOICE_H - -#include "Common.h" -#include "UCode_AXStructs.h" -#include "../../DSP.h" - -// Useful macro to convert xxx_hi + xxx_lo to xxx for 32 bits. -#define HILO_TO_32(name) \ - ((name##_hi << 16) | name##_lo) - -// Used to pass a large amount of buffers to the mixing function. -union AXBuffers -{ - struct - { - int* left; - int* right; - int* surround; - - int* auxA_left; - int* auxA_right; - int* auxA_surround; - - int* auxB_left; - int* auxB_right; - int* auxB_surround; - }; - - int* ptrs[9]; -}; - -// 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 -// mixer_control value to an AXMixControl bitfield. -enum AXMixControl -{ - MIX_L = 0x00001, - MIX_L_RAMP = 0x00002, - MIX_R = 0x00004, - MIX_R_RAMP = 0x00008, - MIX_S = 0x00010, - MIX_S_RAMP = 0x00020, - - MIX_AUXA_L = 0x00040, - MIX_AUXA_L_RAMP = 0x00080, - MIX_AUXA_R = 0x00100, - MIX_AUXA_R_RAMP = 0x00200, - MIX_AUXA_S = 0x00400, - MIX_AUXA_S_RAMP = 0x00800, - - MIX_AUXB_L = 0x01000, - MIX_AUXB_L_RAMP = 0x02000, - MIX_AUXB_R = 0x04000, - MIX_AUXB_R_RAMP = 0x08000, - MIX_AUXB_S = 0x10000, - MIX_AUXB_S_RAMP = 0x20000 -}; - -// Read a PB from MRAM/ARAM -inline bool ReadPB(u32 addr, AXPB& pb) -{ - u16* dst = (u16*)&pb; - const u16* src = (const u16*)Memory::GetPointer(addr); - if (!src) - return false; - - for (u32 i = 0; i < sizeof (pb) / sizeof (u16); ++i) - dst[i] = Common::swap16(src[i]); - - return true; -} - -// Write a PB back to MRAM/ARAM -inline bool WritePB(u32 addr, const AXPB& pb) -{ - const u16* src = (const u16*)&pb; - u16* dst = (u16*)Memory::GetPointer(addr); - if (!dst) - return false; - - for (u32 i = 0; i < sizeof (pb) / sizeof (u16); ++i) - dst[i] = Common::swap16(src[i]); - - return true; -} - -// Simulated accelerator state. -static u32 acc_loop_addr, acc_end_addr; -static u32* acc_cur_addr; -static AXPB* acc_pb; - -// Sets up the simulated accelerator. -inline void AcceleratorSetup(AXPB* pb, u32* cur_addr) -{ - acc_pb = pb; - 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; -} - -// Reads a sample from the simulated accelerator. Also handles looping and -// disabling streams that reached the end (this is done by an exception raised -// by the accelerator on real hardware). -inline u16 AcceleratorGetSample() -{ - u16 ret; - - switch (acc_pb->audio_addr.sample_format) - { - case 0x00: // ADPCM - { - // ADPCM decoding, not much to explain here. - if ((*acc_cur_addr & 15) == 0) - { - acc_pb->adpcm.pred_scale = DSP::ReadARAM((*acc_cur_addr & ~15) >> 1); - *acc_cur_addr += 2; - } - - int scale = 1 << (acc_pb->adpcm.pred_scale & 0xF); - int coef_idx = (acc_pb->adpcm.pred_scale >> 4) & 0x7; - - s32 coef1 = acc_pb->adpcm.coefs[coef_idx * 2 + 0]; - s32 coef2 = acc_pb->adpcm.coefs[coef_idx * 2 + 1]; - - int temp = (*acc_cur_addr & 1) ? - (DSP::ReadARAM(*acc_cur_addr >> 1) & 0xF) : - (DSP::ReadARAM(*acc_cur_addr >> 1) >> 4); - - if (temp >= 8) - temp -= 16; - - int val = (scale * temp) + ((0x400 + coef1 * acc_pb->adpcm.yn1 + coef2 * acc_pb->adpcm.yn2) >> 11); - - if (val > 0x7FFF) val = 0x7FFF; - else if (val < -0x7FFF) val = -0x7FFF; - - acc_pb->adpcm.yn2 = acc_pb->adpcm.yn1; - acc_pb->adpcm.yn1 = val; - *acc_cur_addr += 1; - ret = val; - break; - } - - case 0x0A: // 16-bit PCM audio - ret = (DSP::ReadARAM(*acc_cur_addr * 2) << 8) | DSP::ReadARAM(*acc_cur_addr * 2 + 1); - acc_pb->adpcm.yn2 = acc_pb->adpcm.yn1; - acc_pb->adpcm.yn1 = ret; - *acc_cur_addr += 1; - break; - - case 0x19: // 8-bit PCM audio - ret = DSP::ReadARAM(*acc_cur_addr) << 8; - acc_pb->adpcm.yn2 = acc_pb->adpcm.yn1; - acc_pb->adpcm.yn1 = ret; - *acc_cur_addr += 1; - break; - - default: - ERROR_LOG(DSPHLE, "Unknown sample format: %d", acc_pb->audio_addr.sample_format); - 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 >= acc_end_addr) - { - // If we are really at the end (and we don't simply have cur_addr > - // end_addr all the time), loop back to loop_addr. - if ((*acc_cur_addr & ~0x1F) == (acc_end_addr & ~0x1F)) - *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; -} - -// Read 32 input samples from ARAM, decoding and converting rate if required. -inline void GetInputSamples(AXPB& pb, s16* samples) -{ - 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) - { - // 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; - - // Compute the number of real samples we will need to read from the - // data source. We need to output 32 samples, so we need to read - // 32 * ratio + curr_pos samples. The maximum possible ratio available - // on the DSP is 4.0, so at most we will read 128 real samples. - s16 real_samples[130]; - u32 real_samples_needed = (32 * ratio + curr_pos) >> 16; - - // The first two real samples are the ones we read at the previous - // iteration. That way we can interpolate before having read 2 new - // samples from the accelerator. - // - // The next real samples are read from the accelerator. - real_samples[0] = pb.src.last_samples[2]; - real_samples[1] = pb.src.last_samples[3]; - for (u32 i = 0; i < real_samples_needed; ++i) - real_samples[i + 2] = AcceleratorGetSample(); - - for (u32 i = 0; i < 32; ++i) - { - // Get our current integer and fractional position. The integer - // position is used to get the two samples around us. The - // fractional position is used to compute the linear interpolation - // between these two samples. - u32 curr_int_pos = (curr_pos >> 16); - s32 curr_frac_pos = curr_pos & 0xFFFF; - s16 samp1 = real_samples[curr_int_pos]; - s16 samp2 = real_samples[curr_int_pos + 1]; - - // Linear interpolation: s1 + (s2 - s1) * pos - s16 sample = samp1 + (s16)(((samp2 - samp1) * (s32)curr_frac_pos) >> 16); - samples[i] = sample; - - curr_pos += ratio; - } - - // Update the last_samples array. A bit tricky because we can't know - // for sure we have more than 4 real samples in our array. - if (real_samples_needed >= 2) - memcpy(pb.src.last_samples, &real_samples[real_samples_needed + 2 - 4], 4 * sizeof (u16)); - else - { - memmove(pb.src.last_samples, &pb.src.last_samples[real_samples_needed], (4 - real_samples_needed) * sizeof (u16)); - memcpy(&pb.src.last_samples[4 - real_samples_needed], &real_samples[2], real_samples_needed * sizeof (u16)); - } - pb.src.cur_addr_frac = curr_pos & 0xFFFF; - } - else // SRCTYPE_NEAREST - { - // No sample rate conversion here: simply read 32 samples from the - // accelerator to the output buffer. - for (u32 i = 0; i < 32; ++i) - samples[i] = AcceleratorGetSample(); - - memcpy(pb.src.last_samples, samples + 28, 4 * sizeof (u16)); - } - - // 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); -} - -// Add samples to an output buffer, with optional volume ramping. -inline void MixAdd(int* out, const s16* input, u16* pvol, bool ramp) -{ - u16& volume = pvol[0]; - u16 volume_delta = pvol[1]; - - // If volume ramping is disabled, set volume_delta to 0. That way, the - // mixing loop can avoid testing if volume ramping is enabled at each step, - // and just add volume_delta. - if (!ramp) - volume_delta = 0; - - for (u32 i = 0; i < 32; ++i) - { - s64 sample = 2 * (s16)input[i] * (s16)volume; - out[i] += (s32)(sample >> 16); - volume += volume_delta; - } -} - -// Process 1ms of audio (32 samples) from a PB and mix it to the buffers. -inline void Process1ms(AXPB& 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]; - GetInputSamples(pb, samples); - - // Apply a global volume ramp using the volume envelope parameters. - for (u32 i = 0; i < 32; ++i) - { - s64 sample = 2 * (s16)samples[i] * (s16)pb.vol_env.cur_volume; - samples[i] = (s16)(sample >> 16); - pb.vol_env.cur_volume += pb.vol_env.cur_volume_delta; - } - - // Optionally, execute a low pass filter - if (pb.lpf.enabled) - { - // TODO - } - - // Mix LRS, AUXA and AUXB depending on mixer_control - // TODO: Handle DPL2 on AUXB. - - // HACK: at the moment we don't mix surround into left and right, so always - // mix left and right in order to have sound even if a game uses surround - // only. - if (mctrl & MIX_L) - MixAdd(buffers.left, samples, &pb.mixer.left, mctrl & MIX_L_RAMP); - if (mctrl & MIX_R) - MixAdd(buffers.right, samples, &pb.mixer.right, mctrl & MIX_R_RAMP); - if (mctrl & MIX_S) - MixAdd(buffers.surround, samples, &pb.mixer.surround, mctrl & MIX_S_RAMP); - - if (mctrl & MIX_AUXA_L) - MixAdd(buffers.auxA_left, samples, &pb.mixer.auxA_left, mctrl & MIX_AUXA_L_RAMP); - if (mctrl & MIX_AUXA_R) - MixAdd(buffers.auxA_right, samples, &pb.mixer.auxA_right, mctrl & MIX_AUXA_R_RAMP); - if (mctrl & MIX_AUXA_S) - MixAdd(buffers.auxA_surround, samples, &pb.mixer.auxA_surround, mctrl & MIX_AUXA_S_RAMP); - - if (mctrl & MIX_AUXB_L) - MixAdd(buffers.auxB_left, samples, &pb.mixer.auxB_left, mctrl & MIX_AUXB_L_RAMP); - if (mctrl & MIX_AUXB_R) - MixAdd(buffers.auxB_right, samples, &pb.mixer.auxB_right, mctrl & MIX_AUXB_R_RAMP); - if (mctrl & MIX_AUXB_S) - MixAdd(buffers.auxB_surround, samples, &pb.mixer.auxB_surround, mctrl & MIX_AUXB_S_RAMP); - - // Optionally, phase shift left or right channel to simulate 3D sound. - if (pb.initial_time_delay.on) - { - // TODO - } -} - -#endif // !_UCODE_NEWAX_VOICE_H diff --git a/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCodes.cpp b/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCodes.cpp index 58e6198205..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_NewAX.h" #include "UCode_Zelda.h" #include "UCode_ROM.h" #include "UCode_CARD.h" @@ -58,7 +57,7 @@ IUCode* UCodeFactory(u32 _CRC, DSPHLE *dsp_hle, bool bWii) case 0xe2136399: // billy hatcher, dragonballz, mario party 5, TMNT, ava1080 case 0x3389a79e: // MP1/MP2 Wii (Metroid Prime Trilogy) INFO_LOG(DSPHLE, "CRC %08x: AX ucode chosen", _CRC); - return new CUCode_NewAX(dsp_hle, _CRC); + return new CUCode_AX(dsp_hle, _CRC); case 0x6ba3b3ea: // IPL - PAL case 0x24b22038: // IPL - NTSC/NTSC-JAP From 954c55e35afbb1191f97e6da2f2b05be3696e2c4 Mon Sep 17 00:00:00 2001 From: Pierre Bourdon Date: Tue, 20 Nov 2012 03:13:55 +0100 Subject: [PATCH 19/44] New AXWii now working properly in some games I tested --- .../Core/Src/HW/DSPHLE/UCodes/UCode_AX.cpp | 61 +-- .../Core/Core/Src/HW/DSPHLE/UCodes/UCode_AX.h | 104 ++-- .../Core/Src/HW/DSPHLE/UCodes/UCode_AXWii.cpp | 451 ++++++++++-------- .../Core/Src/HW/DSPHLE/UCodes/UCode_AXWii.h | 64 ++- .../Src/HW/DSPHLE/UCodes/UCode_AX_Voice.h | 74 +-- 5 files changed, 429 insertions(+), 325 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 8c2e05dce4..54bd50830d 100644 --- a/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_AX.cpp +++ b/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_AX.cpp @@ -16,9 +16,11 @@ // http://code.google.com/p/dolphin-emu/ #include "UCode_AX.h" -#include "UCode_AX_Voice.h" #include "../../DSP.h" +#define AX_GC +#include "UCode_AX_Voice.h" + CUCode_AX::CUCode_AX(DSPHLE* dsp_hle, u32 crc) : IUCode(dsp_hle, crc) , m_cmdlist_size(0) @@ -123,7 +125,7 @@ void CUCode_AX::HandleCommandList() 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)); + MixAUXSamples(cmd - CMD_MIX_AUXA, HILO_TO_32(addr), HILO_TO_32(addr2)); break; case CMD_UPLOAD_LRS: @@ -327,40 +329,41 @@ void CUCode_AX::ProcessPBList(u32 pb_addr) } } -void CUCode_AX::MixAUXSamples(bool AUXA, u32 write_addr, u32 read_addr) +void CUCode_AX::MixAUXSamples(int aux_id, u32 write_addr, u32 read_addr) { - int buffers[3][5 * 32]; + int temp[3][5 * 32]; + int* buffers[3] = { 0 }; + + 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; + } // First, we need to send the contents of our AUX buffers to the CPU. if (write_addr) { - for (u32 i = 0; i < 5 * 32; ++i) - { - if (AUXA) - { - buffers[0][i] = Common::swap32(m_samples_auxA_left[i]); - buffers[1][i] = Common::swap32(m_samples_auxA_right[i]); - buffers[2][i] = Common::swap32(m_samples_auxA_surround[i]); - } - else - { - buffers[0][i] = Common::swap32(m_samples_auxB_left[i]); - buffers[1][i] = Common::swap32(m_samples_auxB_right[i]); - buffers[2][i] = Common::swap32(m_samples_auxB_surround[i]); - } - } - memcpy(HLEMemory_Get_Pointer(write_addr), buffers, sizeof (buffers)); + for (u32 i = 0; i < 3 * 32; ++i) + for (u32 j = 0; j < 3; ++j) + temp[j][i] = Common::swap32(buffers[j][i]); + memcpy(HLEMemory_Get_Pointer(write_addr), temp, sizeof (temp)); } - // Then, we read the new buffers from the CPU and add to our current - // buffers. - memcpy(buffers, HLEMemory_Get_Pointer(read_addr), sizeof (buffers)); + // Then, we read the new temp from the CPU and add to our current + // temp. + memcpy(temp, HLEMemory_Get_Pointer(read_addr), sizeof (temp)); for (u32 i = 0; i < 5 * 32; ++i) - { - m_samples_left[i] += Common::swap32(buffers[0][i]); - m_samples_right[i] += Common::swap32(buffers[1][i]); - m_samples_surround[i] += Common::swap32(buffers[2][i]); - } + for (u32 j = 0; j < 3; ++j) + buffers[j][i] += Common::swap32(temp[j][i]); } void CUCode_AX::UploadLRS(u32 dst_addr) @@ -495,5 +498,7 @@ void CUCode_AX::DoState(PointerWrap& p) { std::lock_guard lk(m_processing); + // TODO + DoStateShared(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 29509661ed..23bc786111 100644 --- a/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_AX.h +++ b/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_AX.h @@ -29,7 +29,40 @@ #include "UCodes.h" #include "UCode_AXStructs.h" -#include "UCode_AX_Voice.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 +// mixer_control value to an AXMixControl bitfield. +enum AXMixControl +{ + MIX_L = 0x000001, + MIX_L_RAMP = 0x000002, + MIX_R = 0x000004, + MIX_R_RAMP = 0x000008, + MIX_S = 0x000010, + MIX_S_RAMP = 0x000020, + + MIX_AUXA_L = 0x000040, + MIX_AUXA_L_RAMP = 0x000080, + MIX_AUXA_R = 0x000100, + MIX_AUXA_R_RAMP = 0x000200, + MIX_AUXA_S = 0x000400, + MIX_AUXA_S_RAMP = 0x000800, + + MIX_AUXB_L = 0x001000, + MIX_AUXB_L_RAMP = 0x002000, + MIX_AUXB_R = 0x004000, + MIX_AUXB_R_RAMP = 0x008000, + MIX_AUXB_S = 0x010000, + MIX_AUXB_S_RAMP = 0x020000, + + MIX_AUXC_L = 0x040000, + MIX_AUXC_L_RAMP = 0x080000, + MIX_AUXC_R = 0x100000, + MIX_AUXC_R_RAMP = 0x200000, + MIX_AUXC_S = 0x400000, + MIX_AUXC_S_RAMP = 0x800000 +}; class CUCode_AX : public IUCode { @@ -37,16 +70,16 @@ public: CUCode_AX(DSPHLE* dsp_hle, u32 crc); virtual ~CUCode_AX(); - void HandleMail(u32 mail); - void MixAdd(short* out_buffer, int nsamples); - void Update(int cycles); - void DoState(PointerWrap& p); + virtual void HandleMail(u32 mail); + virtual void MixAdd(short* out_buffer, int nsamples); + virtual void Update(int cycles); + virtual void DoState(PointerWrap& p); // Needed because StdThread.h std::thread implem does not support member // pointers. static void SpawnAXThread(CUCode_AX* self); -private: +protected: enum MailType { MAIL_RESUME = 0xCDD10000, @@ -59,31 +92,7 @@ private: MAIL_CMDLIST_MASK = 0xFFFF0000 }; - enum CmdType - { - CMD_SETUP = 0x00, - CMD_UNK_01 = 0x01, - CMD_PB_ADDR = 0x02, - CMD_PROCESS = 0x03, - CMD_MIX_AUXA = 0x04, - CMD_MIX_AUXB = 0x05, - CMD_UPLOAD_LRS = 0x06, - CMD_SBUFFER_ADDR = 0x07, - CMD_UNK_08 = 0x08, - CMD_MIX_AUXB_NOWRITE = 0x09, - CMD_COMPRESSOR_TABLE_ADDR = 0x0A, - CMD_UNK_0B = 0x0B, - CMD_UNK_0C = 0x0C, - CMD_MORE = 0x0D, - CMD_OUTPUT = 0x0E, - CMD_END = 0x0F, - CMD_UNK_10 = 0x10, - CMD_UNK_11 = 0x11, - CMD_UNK_12 = 0x12, - CMD_UNK_13 = 0x13, - }; - - // 32 * 5 because 32 samples per millisecond, for 5 milliseconds. + // 32 * 5 because 32 samples per millisecond, for max 5 milliseconds. int m_samples_left[32 * 5]; int m_samples_right[32 * 5]; int m_samples_surround[32 * 5]; @@ -119,12 +128,39 @@ private: void NotifyAXThread(); void AXThread(); - void HandleCommandList(); - void SetupProcessing(u32 studio_addr); + + virtual void HandleCommandList(); + + void SetupProcessing(u32 init_addr); void ProcessPBList(u32 pb_addr); - void MixAUXSamples(bool AUXA, u32 write_addr, u32 read_addr); + void MixAUXSamples(int aux_id, u32 write_addr, u32 read_addr); void UploadLRS(u32 dst_addr); void OutputSamples(u32 out_addr); + +private: + enum CmdType + { + CMD_SETUP = 0x00, + CMD_UNK_01 = 0x01, + CMD_PB_ADDR = 0x02, + CMD_PROCESS = 0x03, + CMD_MIX_AUXA = 0x04, + CMD_MIX_AUXB = 0x05, + CMD_UPLOAD_LRS = 0x06, + CMD_SBUFFER_ADDR = 0x07, + CMD_UNK_08 = 0x08, + CMD_MIX_AUXB_NOWRITE = 0x09, + CMD_COMPRESSOR_TABLE_ADDR = 0x0A, + CMD_UNK_0B = 0x0B, + CMD_UNK_0C = 0x0C, + CMD_MORE = 0x0D, + CMD_OUTPUT = 0x0E, + CMD_END = 0x0F, + CMD_UNK_10 = 0x10, + CMD_UNK_11 = 0x11, + CMD_UNK_12 = 0x12, + CMD_UNK_13 = 0x13, + }; }; #endif // !_UCODE_AX_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 73abc392df..a47d9b279b 100644 --- a/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_AXWii.cpp +++ b/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_AXWii.cpp @@ -23,249 +23,294 @@ #include "UCodes.h" #include "UCode_AXStructs.h" #include "UCode_AXWii.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; - - case 0xCDD10002: // Action 2 - IROM_Reset(); ( WII: De Blob, Cursed Mountain,...) - DEBUG_LOG(DSPHLE,"DSP IROM - Reset!"); - m_DSPHLE->SetUCode(UCODE_ROM); - return; - - case 0xCDD10003: // Action 3 - AX_GetNextCmdBlock() - break; - - default: - DEBUG_LOG(DSPHLE, " >>>> u32 MAIL : AXTask Mail (%08x)", _uMail); - AXTask(_uMail); - break; - } -} - -void CUCode_AXWii::MixAdd(short* _pBuffer, int _iSize) -{ - AXPBWii PB; - - if (_iSize > 1024 * 1024) - _iSize = 1024 * 1024; - - memset(templbuffer, 0, _iSize * sizeof(int)); - memset(temprbuffer, 0, _iSize * sizeof(int)); - - u32 blockAddr = m_addressPBs; - if (!blockAddr) - return; - - AXBuffers buffers = {{ - templbuffer, - temprbuffer, - NULL - }}; - - for (int i = 0; i < NUMBER_OF_PBS; i++) - { - if (!ReadPB(blockAddr, PB)) - break; - -// if (wiisportsHack) -// MixAddVoice(*(AXPBWiiSports*)&PB, buffers, _iSize); -// else -// MixAddVoice(PB, buffers, _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++) + switch (cmd) { - // 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; + // 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: + curr_idx++; // TODO: Unknown u16 + 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)); + 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_UNK_0D: + // Wiimote output? + curr_idx += 8; + break; + + case CMD_END: + end = true; + break; } } } - -void CUCode_AXWii::Update(int cycles) +void CUCode_AXWii::SetupProcessing(u32 init_addr) { - if (NeedsResumeMail()) + // 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 + int* 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 + }; + + u32 init_idx = 0; + for (u32 i = 0; i < sizeof (buffers) / sizeof (buffers[0]); ++i) { - 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); + 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], 0, 3 * 32 * sizeof (int)); + else + { + for (u32 j = 0; j < 3 * 32; ++j) + { + buffers[i][j] = init_val; + init_val += delta; + } + } } } -// 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) +AXMixControl CUCode_AXWii::ConvertMixerControl(u32 mixer_control) { - u32 uAddress = _uMail; - u32 Addr__AXStudio; - u32 Addr__AXOutSBuffer; - bool bExecuteList = true; + u32 ret = 0; -/* - for (int i=0;i<64;i++) { - NOTICE_LOG(DSPHLE,"%x - %08x",uAddress+(i*4),HLEMemory_Read_U32(uAddress+(i*4))); - } -*/ + 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; - while (bExecuteList) + return (AXMixControl)ret; +} + +void CUCode_AXWii::ProcessPBList(u32 pb_addr) +{ + const u32 spms = 32; + + AXPBWii pb; + + while (pb_addr) { - u16 iCommand = HLEMemory_Read_U16(uAddress); - uAddress += 2; - //NOTICE_LOG(DSPHLE,"AXWII - AXLIST CMD %X",iCommand); + 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 + }}; - switch (iCommand) + if (!ReadPB(pb_addr, pb)) + break; + + for (int curr_ms = 0; curr_ms < 3; ++curr_ms) { - case 0x0000: - Addr__AXStudio = HLEMemory_Read_U32(uAddress); - uAddress += 4; - break; + Process1ms(pb, buffers, ConvertMixerControl(HILO_TO_32(pb.mixer_control))); - case 0x0001: - uAddress += 4; - break; - - case 0x0003: - uAddress += 4; - break; - - case 0x0004: - // PBs are here now - m_addressPBs = HLEMemory_Read_U32(uAddress); - soundStream->GetMixer()->SetHLEReady(true); -// soundStream->Update(); - uAddress += 4; - break; - - case 0x0005: - if (!wiisportsHack) - uAddress += 10; - break; - - case 0x0006: - uAddress += 10; - break; - - case 0x0007: // AXLIST_SBUFFER - Addr__AXOutSBuffer = HLEMemory_Read_U32(uAddress); - uAddress += 10; - break; - - case 0x0008: - uAddress += 26; - break; - - case 0x000a: - uAddress += wiisportsHack ? 4 : 8; // AXLIST_COMPRESSORTABLE - break; - - case 0x000b: - uAddress += wiisportsHack ? 2 : 10; - break; - - case 0x000c: - uAddress += wiisportsHack ? 8 : 10; - break; - - case 0x000d: - uAddress += 16; - break; - - case 0x000e: - if (wiisportsHack) - uAddress += 16; - else - bExecuteList = false; - break; - - case 0x000f: // only for Wii Sports uCode - bExecuteList = false; - break; - - default: - INFO_LOG(DSPHLE,"DSPHLE - AXwii - AXLIST - Unknown CMD: %x",iCommand); - // unknown command so stop the execution of this TaskList - bExecuteList = false; - break; + // 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_AXWii::MixAUXSamples(int aux_id, u32 write_addr, u32 read_addr) +{ + int temp[3][3 * 32]; + int* buffers[3] = { 0 }; + + 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; } - m_rMailHandler.PushMail(DSP_YIELD); //its here in case there is a CMD fuckup - return true; + // Send the content of AUX buffers to the CPU + if (write_addr) + { + for (u32 i = 0; i < 3 * 32; ++i) + for (u32 j = 0; j < 3; ++j) + temp[j][i] = Common::swap32(buffers[j][i]); + memcpy(HLEMemory_Get_Pointer(write_addr), temp, sizeof (temp)); + } + + // Then read the buffers from the CPU and add to our current buffers. + memcpy(temp, HLEMemory_Get_Pointer(read_addr), sizeof (temp)); + for (u32 i = 0; i < 3 * 32; ++i) + for (u32 j = 0; j < 3; ++j) + buffers[j][i] += Common::swap32(temp[j][i]); +} + +void CUCode_AXWii::OutputSamples(u32 lr_addr, u32 surround_addr, u16 volume) +{ + int surround_buffer[3 * 32] = { 0 }; + + for (u32 i = 0; i < 3 * 32; ++i) + surround_buffer[i] = Common::swap32(m_samples_surround[i]); + memcpy(HLEMemory_Get_Pointer(surround_addr), surround_buffer, sizeof (surround_buffer)); + + short buffer[3 * 32 * 2]; + + // Clamp internal buffers to 16 bits. + for (u32 i = 0; i < 3 * 32; ++i) + { + int left = m_samples_left[i]; + int right = m_samples_right[i]; + + if (left < -32767) left = -32767; + if (left > 32767) left = 32767; + if (right < -32767) right = -32767; + if (right > 32767) right = 32767; + + m_samples_left[i] = left; + m_samples_right[i] = right; + } + + for (u32 i = 0; i < 3 * 32; ++i) + { + buffer[2 * i] = Common::swap16(m_samples_left[i]); + buffer[2 * i + 1] = Common::swap16(m_samples_right[i]); + } + + memcpy(HLEMemory_Get_Pointer(lr_addr), buffer, sizeof (buffer)); } void CUCode_AXWii::DoState(PointerWrap &p) { std::lock_guard lk(m_csMix); - p.Do(m_addressPBs); - p.Do(wiisportsHack); + // TODO DoStateShared(p); } 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 1e6cffcba0..d488bd046c 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,58 @@ // 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_AXStructs.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]; + + // 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); + void OutputSamples(u32 lr_addr, u32 surround_addr, u16 volume); 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_UNK_0D = 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_AX_Voice.h b/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_AX_Voice.h index 20fea41677..22ef0583ce 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 @@ -15,13 +15,27 @@ // Official Git repository and contact information can be found at // http://code.google.com/p/dolphin-emu/ +// This file is UGLY (full of #ifdef) so that it can be used with both GC and +// Wii version of AX. Maybe it would be better to abstract away the parts that +// can be made common. + #ifndef _UCODE_AX_VOICE_H #define _UCODE_AX_VOICE_H +#if !defined(AX_GC) && !defined(AX_WII) +#error UCode_AX_Voice.h included without specifying version +#endif + #include "Common.h" #include "UCode_AXStructs.h" #include "../../DSP.h" +#ifdef AX_GC +# define PB_TYPE AXPB +#else +# define PB_TYPE AXPBWii +#endif + // Useful macro to convert xxx_hi + xxx_lo to xxx for 32 bits. #define HILO_TO_32(name) \ ((name##_hi << 16) | name##_lo) @@ -42,41 +56,23 @@ union AXBuffers int* auxB_left; int* auxB_right; int* auxB_surround; + +#ifdef AX_WII + int* auxC_left; + int* auxC_right; + int* auxC_surround; +#endif }; +#ifdef AX_GC int* ptrs[9]; -}; - -// 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 -// mixer_control value to an AXMixControl bitfield. -enum AXMixControl -{ - MIX_L = 0x00001, - MIX_L_RAMP = 0x00002, - MIX_R = 0x00004, - MIX_R_RAMP = 0x00008, - MIX_S = 0x00010, - MIX_S_RAMP = 0x00020, - - MIX_AUXA_L = 0x00040, - MIX_AUXA_L_RAMP = 0x00080, - MIX_AUXA_R = 0x00100, - MIX_AUXA_R_RAMP = 0x00200, - MIX_AUXA_S = 0x00400, - MIX_AUXA_S_RAMP = 0x00800, - - MIX_AUXB_L = 0x01000, - MIX_AUXB_L_RAMP = 0x02000, - MIX_AUXB_R = 0x04000, - MIX_AUXB_R_RAMP = 0x08000, - MIX_AUXB_S = 0x10000, - MIX_AUXB_S_RAMP = 0x20000 +#else + int* ptrs[12]; +#endif }; // Read a PB from MRAM/ARAM -template -bool ReadPB(u32 addr, PBType& pb) +bool ReadPB(u32 addr, PB_TYPE& pb) { u16* dst = (u16*)&pb; const u16* src = (const u16*)Memory::GetPointer(addr); @@ -90,8 +86,7 @@ bool ReadPB(u32 addr, PBType& pb) } // Write a PB back to MRAM/ARAM -template -inline bool WritePB(u32 addr, const PBType& pb) +inline bool WritePB(u32 addr, const PB_TYPE& pb) { const u16* src = (const u16*)&pb; u16* dst = (u16*)Memory::GetPointer(addr); @@ -107,10 +102,10 @@ inline bool WritePB(u32 addr, const PBType& pb) // Simulated accelerator state. static u32 acc_loop_addr, acc_end_addr; static u32* acc_cur_addr; -static AXPB* acc_pb; +static PB_TYPE* acc_pb; // Sets up the simulated accelerator. -inline void AcceleratorSetup(AXPB* pb, u32* cur_addr) +inline void AcceleratorSetup(PB_TYPE* pb, u32* cur_addr) { acc_pb = pb; acc_loop_addr = HILO_TO_32(pb->audio_addr.loop_addr); @@ -216,7 +211,7 @@ inline u16 AcceleratorGetSample() } // Read 32 input samples from ARAM, decoding and converting rate if required. -inline void GetInputSamples(AXPB& pb, s16* samples) +inline void GetInputSamples(PB_TYPE& pb, s16* samples) { u32 cur_addr = HILO_TO_32(pb.audio_addr.cur_addr); AcceleratorSetup(&pb, &cur_addr); @@ -319,7 +314,7 @@ inline void MixAdd(int* out, const s16* input, u16* pvol, bool ramp) } // Process 1ms of audio (32 samples) from a PB and mix it to the buffers. -inline void Process1ms(AXPB& pb, const AXBuffers& buffers, AXMixControl mctrl) +void Process1ms(PB_TYPE& pb, const AXBuffers& buffers, AXMixControl mctrl) { // If the voice is not running, nothing to do. if (!pb.running) @@ -370,6 +365,15 @@ inline void Process1ms(AXPB& pb, const AXBuffers& buffers, AXMixControl mctrl) if (mctrl & MIX_AUXB_S) MixAdd(buffers.auxB_surround, samples, &pb.mixer.auxB_surround, mctrl & MIX_AUXB_S_RAMP); +#ifdef AX_WII + if (mctrl & MIX_AUXC_L) + MixAdd(buffers.auxC_left, samples, &pb.mixer.auxC_left, mctrl & MIX_AUXC_L_RAMP); + if (mctrl & MIX_AUXC_R) + MixAdd(buffers.auxC_right, samples, &pb.mixer.auxC_right, mctrl & MIX_AUXC_R_RAMP); + if (mctrl & MIX_AUXC_S) + MixAdd(buffers.auxC_surround, samples, &pb.mixer.auxC_surround, mctrl & MIX_AUXC_S_RAMP); +#endif + // Optionally, phase shift left or right channel to simulate 3D sound. if (pb.initial_time_delay.on) { From ae85159a9481a8eba49dcb9ec744c16dbbecd16d Mon Sep 17 00:00:00 2001 From: Pierre Bourdon Date: Tue, 20 Nov 2012 03:22:24 +0100 Subject: [PATCH 20/44] Add surround sound support to GC AX HLE --- .../Core/Src/HW/DSPHLE/UCodes/UCode_AX.cpp | 18 +++++++++++------- .../Core/Core/Src/HW/DSPHLE/UCodes/UCode_AX.h | 3 +-- 2 files changed, 12 insertions(+), 9 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 54bd50830d..8f372f3313 100644 --- a/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_AX.cpp +++ b/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_AX.cpp @@ -157,13 +157,11 @@ void CUCode_AX::HandleCommandList() break; case CMD_OUTPUT: - // Skip the first address, it is used for surround audio - // output, which we don't support yet. - curr_idx += 2; - addr_hi = m_cmdlist[curr_idx++]; addr_lo = m_cmdlist[curr_idx++]; - OutputSamples(HILO_TO_32(addr)); + addr2_hi = m_cmdlist[curr_idx++]; + addr2_lo = m_cmdlist[curr_idx++]; + OutputSamples(HILO_TO_32(addr2), HILO_TO_32(addr)); break; case CMD_END: @@ -379,8 +377,14 @@ void CUCode_AX::UploadLRS(u32 dst_addr) memcpy(HLEMemory_Get_Pointer(dst_addr), buffers, sizeof (buffers)); } -void CUCode_AX::OutputSamples(u32 out_addr) +void CUCode_AX::OutputSamples(u32 lr_addr, u32 surround_addr) { + int surround_buffer[5 * 32]; + + for (u32 i = 0; i < 5 * 32; ++i) + surround_buffer[i] = Common::swap32(m_samples_surround[i]); + memcpy(HLEMemory_Get_Pointer(surround_addr), surround_buffer, sizeof (surround_buffer)); + // 32 samples per ms, 5 ms, 2 channels short buffer[5 * 32 * 2]; @@ -405,7 +409,7 @@ void CUCode_AX::OutputSamples(u32 out_addr) buffer[2 * i + 1] = Common::swap16(m_samples_right[i]); } - memcpy(HLEMemory_Get_Pointer(out_addr), buffer, sizeof (buffer)); + memcpy(HLEMemory_Get_Pointer(lr_addr), buffer, sizeof (buffer)); } void CUCode_AX::HandleMail(u32 mail) 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 23bc786111..d5f07fc13a 100644 --- a/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_AX.h +++ b/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_AX.h @@ -21,7 +21,6 @@ // * Depop support // * ITD support // * Polyphase sample interpolation support (not very useful) -// * Surround sound mixing // * Dolby Pro 2 mixing with recent AX versions #ifndef _UCODE_AX_H @@ -135,7 +134,7 @@ protected: void ProcessPBList(u32 pb_addr); void MixAUXSamples(int aux_id, u32 write_addr, u32 read_addr); void UploadLRS(u32 dst_addr); - void OutputSamples(u32 out_addr); + void OutputSamples(u32 out_addr, u32 surround_addr); private: enum CmdType From 321e3a8421c00d3beede88fe73b402f37547f9c5 Mon Sep 17 00:00:00 2001 From: Pierre Bourdon Date: Tue, 20 Nov 2012 03:32:29 +0100 Subject: [PATCH 21/44] Add global volume handling for AUX mixing and L/R output in AXWii --- .../Core/Src/HW/DSPHLE/UCodes/UCode_AXWii.cpp | 15 +++++++++++---- .../Core/Core/Src/HW/DSPHLE/UCodes/UCode_AXWii.h | 2 +- 2 files changed, 12 insertions(+), 5 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 a47d9b279b..7f9879a8f6 100644 --- a/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_AXWii.cpp +++ b/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_AXWii.cpp @@ -80,12 +80,12 @@ void CUCode_AXWii::HandleCommandList() case CMD_MIX_AUXA: case CMD_MIX_AUXB: case CMD_MIX_AUXC: - curr_idx++; // TODO: Unknown u16 + 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)); + MixAUXSamples(cmd - CMD_MIX_AUXA, HILO_TO_32(addr), HILO_TO_32(addr2), volume); break; // These two go together and manipulate some AUX buffers. @@ -230,7 +230,7 @@ void CUCode_AXWii::ProcessPBList(u32 pb_addr) } } -void CUCode_AXWii::MixAUXSamples(int aux_id, u32 write_addr, u32 read_addr) +void CUCode_AXWii::MixAUXSamples(int aux_id, u32 write_addr, u32 read_addr, u16 volume) { int temp[3][3 * 32]; int* buffers[3] = { 0 }; @@ -269,7 +269,10 @@ void CUCode_AXWii::MixAUXSamples(int aux_id, u32 write_addr, u32 read_addr) memcpy(temp, HLEMemory_Get_Pointer(read_addr), sizeof (temp)); for (u32 i = 0; i < 3 * 32; ++i) for (u32 j = 0; j < 3; ++j) - buffers[j][i] += Common::swap32(temp[j][i]); + { + s64 new_val = buffers[j][i] + Common::swap32(temp[j][i]); + buffers[j][i] = (new_val * volume) >> 15; + } } void CUCode_AXWii::OutputSamples(u32 lr_addr, u32 surround_addr, u16 volume) @@ -288,6 +291,10 @@ void CUCode_AXWii::OutputSamples(u32 lr_addr, u32 surround_addr, u16 volume) 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; 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 d488bd046c..aee6c45ea5 100644 --- a/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_AXWii.h +++ b/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_AXWii.h @@ -42,7 +42,7 @@ protected: void SetupProcessing(u32 init_addr); void ProcessPBList(u32 pb_addr); - void MixAUXSamples(int aux_id, u32 write_addr, u32 read_addr); + void MixAUXSamples(int aux_id, u32 write_addr, u32 read_addr, u16 volume); void OutputSamples(u32 lr_addr, u32 surround_addr, u16 volume); private: From 0d3c3f633975a8d88a124bc5d03d9408df710f43 Mon Sep 17 00:00:00 2001 From: Pierre Bourdon Date: Tue, 20 Nov 2012 04:18:48 +0100 Subject: [PATCH 22/44] Fix a crash in AXWii with SRC ratio > 4 (which I thought was impossible, but AXWii changed the SRC algorithm) --- .../Core/Src/HW/DSPHLE/UCodes/UCode_AXWii.cpp | 2 +- .../Core/Src/HW/DSPHLE/UCodes/UCode_AX_Voice.h | 15 +++++++++------ 2 files changed, 10 insertions(+), 7 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 7f9879a8f6..3e67629226 100644 --- a/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_AXWii.cpp +++ b/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_AXWii.cpp @@ -315,7 +315,7 @@ void CUCode_AXWii::OutputSamples(u32 lr_addr, u32 surround_addr, u16 volume) void CUCode_AXWii::DoState(PointerWrap &p) { - std::lock_guard lk(m_csMix); + std::lock_guard lk(m_processing); // TODO 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 22ef0583ce..5f43fc5b6e 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 @@ -234,9 +234,15 @@ inline void GetInputSamples(PB_TYPE& pb, s16* samples) // Compute the number of real samples we will need to read from the // data source. We need to output 32 samples, so we need to read - // 32 * ratio + curr_pos samples. The maximum possible ratio available - // on the DSP is 4.0, so at most we will read 128 real samples. - s16 real_samples[130]; + // 32 * ratio + curr_pos samples. There does not seem to be a maximum + // value for the ratio in recent versions of AXWii (previously it was + // limited to 4.0), so we will limit it to 16.0 and clamp the ratio if + // needed. This is a HACK, and using another algorithm for linear + // interpolation might be a better idea. + if (ratio > 0x00100000) + ratio = 0x00100000; + + s16 real_samples[514]; u32 real_samples_needed = (32 * ratio + curr_pos) >> 16; // The first two real samples are the ones we read at the previous @@ -341,9 +347,6 @@ void Process1ms(PB_TYPE& pb, const AXBuffers& buffers, AXMixControl mctrl) // Mix LRS, AUXA and AUXB depending on mixer_control // TODO: Handle DPL2 on AUXB. - // HACK: at the moment we don't mix surround into left and right, so always - // mix left and right in order to have sound even if a game uses surround - // only. if (mctrl & MIX_L) MixAdd(buffers.left, samples, &pb.mixer.left, mctrl & MIX_L_RAMP); if (mctrl & MIX_R) From e858835c7ee9fa49ad99b5ab61d91e55d55940cc Mon Sep 17 00:00:00 2001 From: Pierre Bourdon Date: Tue, 20 Nov 2012 04:26:07 +0100 Subject: [PATCH 23/44] Fixing the hack fix (I can't multiply 32x32) --- Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_AX_Voice.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 5f43fc5b6e..d160b83c0f 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 @@ -242,7 +242,7 @@ inline void GetInputSamples(PB_TYPE& pb, s16* samples) if (ratio > 0x00100000) ratio = 0x00100000; - s16 real_samples[514]; + s16 real_samples[1026]; u32 real_samples_needed = (32 * ratio + curr_pos) >> 16; // The first two real samples are the ones we read at the previous From affdf08fd6e831f93f0b5d1e43ecd96765c2b32b Mon Sep 17 00:00:00 2001 From: Pierre Bourdon Date: Tue, 20 Nov 2012 09:49:27 +0100 Subject: [PATCH 24/44] Real fix for the crashes on Windows with AXWii --- Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_AX_Voice.h | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) 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 d160b83c0f..16df07e8a4 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 @@ -36,6 +36,10 @@ # define PB_TYPE AXPBWii #endif +// Put all of that in an anonymous namespace to avoid stupid compilers merging +// functions from AX GC and AX Wii. +namespace { + // Useful macro to convert xxx_hi + xxx_lo to xxx for 32 bits. #define HILO_TO_32(name) \ ((name##_hi << 16) | name##_lo) @@ -242,7 +246,7 @@ inline void GetInputSamples(PB_TYPE& pb, s16* samples) if (ratio > 0x00100000) ratio = 0x00100000; - s16 real_samples[1026]; + s16 real_samples[514]; u32 real_samples_needed = (32 * ratio + curr_pos) >> 16; // The first two real samples are the ones we read at the previous @@ -384,4 +388,6 @@ void Process1ms(PB_TYPE& pb, const AXBuffers& buffers, AXMixControl mctrl) } } +} // namespace + #endif // !_UCODE_AX_VOICE_H From 9d85052a6608ed089d4d657f6779537098d6e29f Mon Sep 17 00:00:00 2001 From: Pierre Bourdon Date: Tue, 20 Nov 2012 11:34:24 +0100 Subject: [PATCH 25/44] Implement Wiimote audio output in AXWii. Not very useful yet as no sound is currently being mixed to the wiimote channels. --- .../Core/Src/HW/DSPHLE/UCodes/UCode_AXWii.cpp | 79 ++++++++++++++----- .../Core/Src/HW/DSPHLE/UCodes/UCode_AXWii.h | 13 ++- 2 files changed, 73 insertions(+), 19 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 3e67629226..3eadc5a840 100644 --- a/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_AXWii.cpp +++ b/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_AXWii.cpp @@ -105,10 +105,18 @@ void CUCode_AXWii::HandleCommandList() case CMD_UNK_0C: curr_idx += 5; break; - case CMD_UNK_0D: - // Wiimote output? + 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; @@ -126,19 +134,31 @@ void CUCode_AXWii::SetupProcessing(u32 init_addr) init_data[i] = HLEMemory_Read_U16(init_addr + 2 * i); // List of all buffers we have to initialize - int* 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 + 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; @@ -150,12 +170,12 @@ void CUCode_AXWii::SetupProcessing(u32 init_addr) init_idx += 3; if (!init_val) - memset(buffers[i], 0, 3 * 32 * sizeof (int)); + memset(buffers[i].ptr, 0, 3 * buffers[i].samples * sizeof (int)); else { - for (u32 j = 0; j < 3 * 32; ++j) + for (u32 j = 0; j < 3 * buffers[i].samples; ++j) { - buffers[i][j] = init_val; + buffers[i].ptr[j] = init_val; init_val += delta; } } @@ -313,6 +333,29 @@ void CUCode_AXWii::OutputSamples(u32 lr_addr, u32 surround_addr, u16 volume) memcpy(HLEMemory_Get_Pointer(lr_addr), buffer, sizeof (buffer)); } +void CUCode_AXWii::OutputWMSamples(u32* addresses) +{ + int* buffers[] = { + m_samples_wm0, + m_samples_wm1, + m_samples_wm2, + m_samples_wm3 + }; + + for (u32 i = 0; i < 4; ++i) + { + int* in = buffers[i]; + u16* out = (u16*)HLEMemory_Get_Pointer(addresses[i]); + for (u32 j = 0; j < 3 * 6; ++j) + { + int sample = in[j]; + if (sample < -32767) sample = -32767; + if (sample > 32767) sample = 32767; + out[j] = Common::swap16((u16)sample); + } + } +} + void CUCode_AXWii::DoState(PointerWrap &p) { std::lock_guard lk(m_processing); 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 aee6c45ea5..3a66c30868 100644 --- a/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_AXWii.h +++ b/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_AXWii.h @@ -33,6 +33,16 @@ protected: 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. @@ -44,6 +54,7 @@ protected: 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 @@ -61,7 +72,7 @@ private: CMD_UNK_0A = 0x0A, CMD_OUTPUT = 0x0B, CMD_UNK_0C = 0x0C, - CMD_UNK_0D = 0x0D, + CMD_WM_OUTPUT = 0x0D, CMD_END = 0x0E }; }; From fb1ecd90b092d26a09d5abd67e438bcfe99e2486 Mon Sep 17 00:00:00 2001 From: Pierre Bourdon Date: Mon, 26 Nov 2012 23:34:18 +0100 Subject: [PATCH 26/44] Implemented command 13 in GC AX, used in some EA games (FIFA 06 now has broken sound, which is still better than no sound) --- .../Core/Src/HW/DSPHLE/UCodes/UCode_AX.cpp | 92 ++++++++++++++++++- .../Core/Core/Src/HW/DSPHLE/UCodes/UCode_AX.h | 4 +- 2 files changed, 90 insertions(+), 6 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 8f372f3313..435e321010 100644 --- a/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_AX.cpp +++ b/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_AX.cpp @@ -84,10 +84,12 @@ void CUCode_AX::HandleCommandList() u32 pb_addr = 0; -// 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, "-------------"); +#if 0 + 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, "-------------"); +#endif u32 curr_idx = 0; bool end = false; @@ -171,7 +173,40 @@ void CUCode_AX::HandleCommandList() case CMD_UNK_10: curr_idx += 4; break; case CMD_UNK_11: curr_idx += 2; break; case CMD_UNK_12: curr_idx += 1; break; - case CMD_UNK_13: curr_idx += 12; break; + + // Send the contents of MAIN LRS, AUXA LRS and AUXB S to RAM, and + // mix data to MAIN LR and AUXB LR. + case CMD_SEND_AUX_AND_MIX: + { + // Address for Main + AUXA LRS upload + u16 main_auxa_up_hi = m_cmdlist[curr_idx++]; + u16 main_auxa_up_lo = m_cmdlist[curr_idx++]; + + // Address for AUXB S upload + u16 auxb_s_up_hi = m_cmdlist[curr_idx++]; + u16 auxb_s_up_lo = m_cmdlist[curr_idx++]; + + // Address to read data for Main L + u16 main_l_dl_hi = m_cmdlist[curr_idx++]; + u16 main_l_dl_lo = m_cmdlist[curr_idx++]; + + // Address to read data for Main R + u16 main_r_dl_hi = m_cmdlist[curr_idx++]; + u16 main_r_dl_lo = m_cmdlist[curr_idx++]; + + // Address to read data for AUXB L + u16 auxb_l_dl_hi = m_cmdlist[curr_idx++]; + u16 auxb_l_dl_lo = m_cmdlist[curr_idx++]; + + // Address to read data for AUXB R + u16 auxb_r_dl_hi = m_cmdlist[curr_idx++]; + u16 auxb_r_dl_lo = m_cmdlist[curr_idx++]; + + SendAUXAndMix(HILO_TO_32(main_auxa_up), HILO_TO_32(auxb_s_up), + HILO_TO_32(main_l_dl), HILO_TO_32(main_r_dl), + HILO_TO_32(auxb_l_dl), HILO_TO_32(auxb_r_dl)); + break; + } default: ERROR_LOG(DSPHLE, "Unknown command in AX cmdlist: %04x", cmd); @@ -412,6 +447,53 @@ void CUCode_AX::OutputSamples(u32 lr_addr, u32 surround_addr) memcpy(HLEMemory_Get_Pointer(lr_addr), buffer, sizeof (buffer)); } +void CUCode_AX::SendAUXAndMix(u32 main_auxa_up, u32 auxb_s_up, u32 main_l_dl, + u32 main_r_dl, u32 auxb_l_dl, u32 auxb_r_dl) +{ + // Buffers to upload first + int* up_buffers[] = { + m_samples_left, + m_samples_right, + m_samples_surround, + m_samples_auxA_left, + m_samples_auxA_right, + m_samples_auxA_surround + }; + + // Upload Main LRS + AUXA LRS + int* ptr = (int*)HLEMemory_Get_Pointer(main_auxa_up); + for (u32 i = 0; i < sizeof (up_buffers) / sizeof (up_buffers[0]); ++i) + for (u32 j = 0; j < 32 * 5; ++j) + *ptr++ = Common::swap32(up_buffers[i][j]); + + // Upload AUXB S + ptr = (int*)HLEMemory_Get_Pointer(auxb_s_up); + for (u32 i = 0; i < 32 * 5; ++i) + *ptr++ = Common::swap32(m_samples_auxB_surround[i]); + + // Download buffers and addresses + int* dl_buffers[] = { + m_samples_left, + m_samples_right, + m_samples_auxB_left, + m_samples_auxB_right + }; + u32 dl_addrs[] = { + main_l_dl, + main_r_dl, + auxb_l_dl, + auxb_r_dl + }; + + // Download and mix + for (u32 i = 0; i < sizeof (dl_buffers) / sizeof (dl_buffers[0]); ++i) + { + int* dl_src = (int*)HLEMemory_Get_Pointer(dl_addrs[i]); + for (u32 j = 0; j < 32 * 5; ++j) + dl_buffers[i][j] += (int)Common::swap32(*dl_src++); + } +} + void CUCode_AX::HandleMail(u32 mail) { // Indicates if the next message is a command list address. 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 d5f07fc13a..a0c6374f95 100644 --- a/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_AX.h +++ b/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_AX.h @@ -135,6 +135,8 @@ protected: void MixAUXSamples(int aux_id, u32 write_addr, u32 read_addr); void UploadLRS(u32 dst_addr); void OutputSamples(u32 out_addr, u32 surround_addr); + void SendAUXAndMix(u32 main_auxa_up, u32 auxb_s_up, u32 main_l_dl, + u32 main_r_dl, u32 auxb_l_dl, u32 auxb_r_dl); private: enum CmdType @@ -158,7 +160,7 @@ private: CMD_UNK_10 = 0x10, CMD_UNK_11 = 0x11, CMD_UNK_12 = 0x12, - CMD_UNK_13 = 0x13, + CMD_SEND_AUX_AND_MIX = 0x13, }; }; From 706939e6328d894fcede80b5d9a44be6f7eb1de3 Mon Sep 17 00:00:00 2001 From: Pierre Bourdon Date: Tue, 27 Nov 2012 00:03:02 +0100 Subject: [PATCH 27/44] Implement command 10, used by FIFA 06 and linked to AUXB mixing. Sound still broken in that game. --- .../Core/Src/HW/DSPHLE/UCodes/UCode_AX.cpp | 34 ++++++++++++++++++- .../Core/Core/Src/HW/DSPHLE/UCodes/UCode_AX.h | 3 +- 2 files changed, 35 insertions(+), 2 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 435e321010..0f97893455 100644 --- a/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_AX.cpp +++ b/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_AX.cpp @@ -170,7 +170,14 @@ void CUCode_AX::HandleCommandList() end = true; break; - case CMD_UNK_10: curr_idx += 4; break; + case CMD_MIX_AUXB_LR: + addr_hi = m_cmdlist[curr_idx++]; + addr_lo = m_cmdlist[curr_idx++]; + addr2_hi = m_cmdlist[curr_idx++]; + addr2_lo = m_cmdlist[curr_idx++]; + MixAUXBLR(HILO_TO_32(addr), HILO_TO_32(addr2)); + break; + case CMD_UNK_11: curr_idx += 2; break; case CMD_UNK_12: curr_idx += 1; break; @@ -447,6 +454,31 @@ void CUCode_AX::OutputSamples(u32 lr_addr, u32 surround_addr) memcpy(HLEMemory_Get_Pointer(lr_addr), buffer, sizeof (buffer)); } +void CUCode_AX::MixAUXBLR(u32 ul_addr, u32 dl_addr) +{ + // Upload AUXB L/R + int* ptr = (int*)HLEMemory_Get_Pointer(ul_addr); + for (u32 i = 0; i < 5 * 32; ++i) + *ptr++ = Common::swap32(m_samples_auxB_left[i]); + for (u32 i = 0; i < 5 * 32; ++i) + *ptr++ = Common::swap32(m_samples_auxB_right[i]); + + // Mix AUXB L/R to MAIN L/R, and replace AUXB L/R + ptr = (int*)HLEMemory_Get_Pointer(dl_addr); + for (u32 i = 0; i < 5 * 32; ++i) + { + int samp = Common::swap32(*ptr++); + m_samples_auxB_left[i] = samp; + m_samples_left[i] += samp; + } + for (u32 i = 0; i < 5 * 32; ++i) + { + int samp = Common::swap32(*ptr++); + m_samples_auxB_right[i] = samp; + m_samples_right[i] += samp; + } +} + void CUCode_AX::SendAUXAndMix(u32 main_auxa_up, u32 auxb_s_up, u32 main_l_dl, u32 main_r_dl, u32 auxb_l_dl, u32 auxb_r_dl) { 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 a0c6374f95..4ac23f7968 100644 --- a/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_AX.h +++ b/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_AX.h @@ -135,6 +135,7 @@ protected: void MixAUXSamples(int aux_id, u32 write_addr, u32 read_addr); void UploadLRS(u32 dst_addr); void OutputSamples(u32 out_addr, u32 surround_addr); + void MixAUXBLR(u32 ul_addr, u32 dl_addr); void SendAUXAndMix(u32 main_auxa_up, u32 auxb_s_up, u32 main_l_dl, u32 main_r_dl, u32 auxb_l_dl, u32 auxb_r_dl); @@ -157,7 +158,7 @@ private: CMD_MORE = 0x0D, CMD_OUTPUT = 0x0E, CMD_END = 0x0F, - CMD_UNK_10 = 0x10, + CMD_MIX_AUXB_LR = 0x10, CMD_UNK_11 = 0x11, CMD_UNK_12 = 0x12, CMD_SEND_AUX_AND_MIX = 0x13, From 9b1a66245e316e8adce6e553d361f61f601f3e38 Mon Sep 17 00:00:00 2001 From: Pierre Bourdon Date: Tue, 27 Nov 2012 00:03:40 +0100 Subject: [PATCH 28/44] Fix an AUX mixing issue introduced when implementing AXWii. Now FIFA 06 sounds great with AX HLE. --- Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_AX.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 0f97893455..4352eea2ec 100644 --- a/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_AX.cpp +++ b/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_AX.cpp @@ -392,7 +392,7 @@ void CUCode_AX::MixAUXSamples(int aux_id, u32 write_addr, u32 read_addr) // First, we need to send the contents of our AUX buffers to the CPU. if (write_addr) { - for (u32 i = 0; i < 3 * 32; ++i) + for (u32 i = 0; i < 5 * 32; ++i) for (u32 j = 0; j < 3; ++j) temp[j][i] = Common::swap32(buffers[j][i]); memcpy(HLEMemory_Get_Pointer(write_addr), temp, sizeof (temp)); From 2120f536d55c37eae017d06c3e888ebcd5bdc018 Mon Sep 17 00:00:00 2001 From: Pierre Bourdon Date: Tue, 27 Nov 2012 20:36:14 +0100 Subject: [PATCH 29/44] Implement command 07 in GC AX (download data to main LR and reset main surround) --- .../Core/Src/HW/DSPHLE/UCodes/UCode_AX.cpp | 19 ++++++++++++++++++- .../Core/Core/Src/HW/DSPHLE/UCodes/UCode_AX.h | 3 ++- 2 files changed, 20 insertions(+), 2 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 4352eea2ec..91a7b03625 100644 --- a/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_AX.cpp +++ b/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_AX.cpp @@ -136,7 +136,12 @@ void CUCode_AX::HandleCommandList() UploadLRS(HILO_TO_32(addr)); break; - case CMD_SBUFFER_ADDR: curr_idx += 2; break; + case CMD_SET_LR: + addr_hi = m_cmdlist[curr_idx++]; + addr_lo = m_cmdlist[curr_idx++]; + SetMainLR(HILO_TO_32(addr)); + break; + case CMD_UNK_08: curr_idx += 10; break; // TODO: check case CMD_MIX_AUXB_NOWRITE: @@ -419,6 +424,18 @@ void CUCode_AX::UploadLRS(u32 dst_addr) memcpy(HLEMemory_Get_Pointer(dst_addr), buffers, sizeof (buffers)); } +void CUCode_AX::SetMainLR(u32 src_addr) +{ + int* ptr = (int*)HLEMemory_Get_Pointer(src_addr); + for (u32 i = 0; i < 5 * 32; ++i) + { + int samp = (int)Common::swap32(*ptr++); + m_samples_left[i] = samp; + m_samples_right[i] = samp; + m_samples_surround[i] = 0; + } +} + void CUCode_AX::OutputSamples(u32 lr_addr, u32 surround_addr) { int surround_buffer[5 * 32]; 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 4ac23f7968..32083f675a 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,7 @@ protected: void ProcessPBList(u32 pb_addr); void MixAUXSamples(int aux_id, u32 write_addr, u32 read_addr); void UploadLRS(u32 dst_addr); + void SetMainLR(u32 src_addr); void OutputSamples(u32 out_addr, u32 surround_addr); void MixAUXBLR(u32 ul_addr, u32 dl_addr); void SendAUXAndMix(u32 main_auxa_up, u32 auxb_s_up, u32 main_l_dl, @@ -149,7 +150,7 @@ private: CMD_MIX_AUXA = 0x04, CMD_MIX_AUXB = 0x05, CMD_UPLOAD_LRS = 0x06, - CMD_SBUFFER_ADDR = 0x07, + CMD_SET_LR = 0x07, CMD_UNK_08 = 0x08, CMD_MIX_AUXB_NOWRITE = 0x09, CMD_COMPRESSOR_TABLE_ADDR = 0x0A, From 9270b628309b7994c67daeac7d3b07db83a99787 Mon Sep 17 00:00:00 2001 From: Pierre Bourdon Date: Tue, 27 Nov 2012 20:36:34 +0100 Subject: [PATCH 30/44] Rewrite MixAUXSamples to make it more clean --- .../Core/Core/Src/HW/DSPHLE/UCodes/UCode_AX.cpp | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 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 91a7b03625..e62e40735b 100644 --- a/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_AX.cpp +++ b/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_AX.cpp @@ -376,7 +376,6 @@ void CUCode_AX::ProcessPBList(u32 pb_addr) void CUCode_AX::MixAUXSamples(int aux_id, u32 write_addr, u32 read_addr) { - int temp[3][5 * 32]; int* buffers[3] = { 0 }; switch (aux_id) @@ -397,18 +396,18 @@ void CUCode_AX::MixAUXSamples(int aux_id, u32 write_addr, u32 read_addr) // First, we need to send the contents of our AUX buffers to the CPU. if (write_addr) { - for (u32 i = 0; i < 5 * 32; ++i) - for (u32 j = 0; j < 3; ++j) - temp[j][i] = Common::swap32(buffers[j][i]); - memcpy(HLEMemory_Get_Pointer(write_addr), temp, sizeof (temp)); + int* ptr = (int*)HLEMemory_Get_Pointer(write_addr); + for (u32 i = 0; i < 3; ++i) + for (u32 j = 0; j < 5 * 32; ++j) + *ptr++ = Common::swap32(buffers[i][j]); } // Then, we read the new temp from the CPU and add to our current // temp. - memcpy(temp, HLEMemory_Get_Pointer(read_addr), sizeof (temp)); - for (u32 i = 0; i < 5 * 32; ++i) - for (u32 j = 0; j < 3; ++j) - buffers[j][i] += Common::swap32(temp[j][i]); + int* ptr = (int*)HLEMemory_Get_Pointer(read_addr); + for (u32 i = 0; i < 3; ++i) + for (u32 j = 0; j < 5 * 32; ++j) + buffers[i][j] += (int)Common::swap32(*ptr++); } void CUCode_AX::UploadLRS(u32 dst_addr) From 1a129abe0da5bd1ff51e2acf4aa14e5a5fe99b25 Mon Sep 17 00:00:00 2001 From: Pierre Bourdon Date: Tue, 27 Nov 2012 21:48:59 +0100 Subject: [PATCH 31/44] AUX return data should be mixed to main buffers, not AUX buffers. Fixes a regression introduced by r954c55e35afb, now EA games sound works again. --- .../Core/Src/HW/DSPHLE/UCodes/UCode_AX.cpp | 9 ++++--- .../Core/Src/HW/DSPHLE/UCodes/UCode_AXWii.cpp | 26 +++++++++++-------- 2 files changed, 21 insertions(+), 14 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 e62e40735b..089e0c7933 100644 --- a/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_AX.cpp +++ b/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_AX.cpp @@ -405,9 +405,12 @@ void CUCode_AX::MixAUXSamples(int aux_id, u32 write_addr, u32 read_addr) // Then, we read the new temp from the CPU and add to our current // temp. int* ptr = (int*)HLEMemory_Get_Pointer(read_addr); - for (u32 i = 0; i < 3; ++i) - for (u32 j = 0; j < 5 * 32; ++j) - buffers[i][j] += (int)Common::swap32(*ptr++); + for (u32 i = 0; i < 5 * 32; ++i) + m_samples_left[i] += (int)Common::swap32(*ptr++); + for (u32 i = 0; i < 5 * 32; ++i) + m_samples_right[i] += (int)Common::swap32(*ptr++); + for (u32 i = 0; i < 5 * 32; ++i) + m_samples_surround[i] += (int)Common::swap32(*ptr++); } void CUCode_AX::UploadLRS(u32 dst_addr) 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 3eadc5a840..42b2d510da 100644 --- a/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_AXWii.cpp +++ b/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_AXWii.cpp @@ -252,8 +252,12 @@ void CUCode_AXWii::ProcessPBList(u32 pb_addr) void CUCode_AXWii::MixAUXSamples(int aux_id, u32 write_addr, u32 read_addr, u16 volume) { - int temp[3][3 * 32]; int* buffers[3] = { 0 }; + int* main_buffers[3] = { + m_samples_left, + m_samples_right, + m_samples_surround + }; switch (aux_id) { @@ -279,19 +283,19 @@ void CUCode_AXWii::MixAUXSamples(int aux_id, u32 write_addr, u32 read_addr, u16 // Send the content of AUX buffers to the CPU if (write_addr) { - for (u32 i = 0; i < 3 * 32; ++i) - for (u32 j = 0; j < 3; ++j) - temp[j][i] = Common::swap32(buffers[j][i]); - memcpy(HLEMemory_Get_Pointer(write_addr), temp, sizeof (temp)); + 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 current buffers. - memcpy(temp, HLEMemory_Get_Pointer(read_addr), sizeof (temp)); - for (u32 i = 0; i < 3 * 32; ++i) - for (u32 j = 0; j < 3; ++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 = buffers[j][i] + Common::swap32(temp[j][i]); - buffers[j][i] = (new_val * volume) >> 15; + s64 new_val = main_buffers[i][j] + Common::swap32(*ptr++); + main_buffers[i][j] = (new_val * volume) >> 15; } } From f11a40f85806e2fa69ef2bb8d7513d580fedff69 Mon Sep 17 00:00:00 2001 From: Pierre Bourdon Date: Wed, 28 Nov 2012 01:48:27 +0100 Subject: [PATCH 32/44] Command 13 should upload only AUXA LRS, not MAIN LRS + AUXA LRS. Fixes more GC EA games sound/music (including FIFA 06, Madden 08). --- Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_AX.cpp | 5 +---- 1 file changed, 1 insertion(+), 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 089e0c7933..4bda7e2638 100644 --- a/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_AX.cpp +++ b/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_AX.cpp @@ -503,15 +503,12 @@ void CUCode_AX::SendAUXAndMix(u32 main_auxa_up, u32 auxb_s_up, u32 main_l_dl, { // Buffers to upload first int* up_buffers[] = { - m_samples_left, - m_samples_right, - m_samples_surround, m_samples_auxA_left, m_samples_auxA_right, m_samples_auxA_surround }; - // Upload Main LRS + AUXA LRS + // Upload AUXA LRS int* ptr = (int*)HLEMemory_Get_Pointer(main_auxa_up); for (u32 i = 0; i < sizeof (up_buffers) / sizeof (up_buffers[0]); ++i) for (u32 j = 0; j < 32 * 5; ++j) From 4cf285628460e7eef1d19b2ad32f05516a324e87 Mon Sep 17 00:00:00 2001 From: Pierre Bourdon Date: Wed, 28 Nov 2012 03:31:46 +0100 Subject: [PATCH 33/44] Improve savestates support in AX and AXWii --- .../Core/Src/HW/DSPHLE/UCodes/UCode_AX.cpp | 19 +++++++++++++++++-- .../Core/Core/Src/HW/DSPHLE/UCodes/UCode_AX.h | 3 +++ .../Core/Src/HW/DSPHLE/UCodes/UCode_AXWii.cpp | 17 +++++++++++++++-- 3 files changed, 35 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 4bda7e2638..eb2a71969b 100644 --- a/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_AX.cpp +++ b/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_AX.cpp @@ -628,11 +628,26 @@ void CUCode_AX::Update(int cycles) } } +void CUCode_AX::DoAXState(PointerWrap& p) +{ + p.Do(m_cmdlist); + p.Do(m_cmdlist_size); + + p.Do(m_samples_left); + p.Do(m_samples_right); + p.Do(m_samples_surround); + p.Do(m_samples_auxA_left); + p.Do(m_samples_auxA_right); + p.Do(m_samples_auxA_surround); + p.Do(m_samples_auxB_left); + p.Do(m_samples_auxB_right); + p.Do(m_samples_auxB_surround); +} + void CUCode_AX::DoState(PointerWrap& p) { std::lock_guard lk(m_processing); - // TODO - DoStateShared(p); + DoAXState(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 32083f675a..7155a245bb 100644 --- a/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_AX.h +++ b/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_AX.h @@ -140,6 +140,9 @@ protected: void SendAUXAndMix(u32 main_auxa_up, u32 auxb_s_up, u32 main_l_dl, u32 main_r_dl, u32 auxb_l_dl, u32 auxb_r_dl); + // Handle save states for main AX. + void DoAXState(PointerWrap& p); + private: enum CmdType { 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 42b2d510da..8c74c7a1e7 100644 --- a/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_AXWii.cpp +++ b/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_AXWii.cpp @@ -364,7 +364,20 @@ void CUCode_AXWii::DoState(PointerWrap &p) { std::lock_guard lk(m_processing); - // TODO - 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); } From 04b1ee0016f7ea492886aa9a3c93f096d3e6eec5 Mon Sep 17 00:00:00 2001 From: Pierre Bourdon Date: Wed, 28 Nov 2012 15:21:54 +0100 Subject: [PATCH 34/44] Implemented command 01 (download data and mix to MAIN/AUXA/AUXB with volume control). Fixes missing weapon sounds in Metroid Prime 2. --- .../Core/Src/HW/DSPHLE/UCodes/UCode_AX.cpp | 35 ++++++++++++++++++- .../Core/Core/Src/HW/DSPHLE/UCodes/UCode_AX.h | 3 +- 2 files changed, 36 insertions(+), 2 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 eb2a71969b..176050affa 100644 --- a/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_AX.cpp +++ b/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_AX.cpp @@ -108,7 +108,16 @@ void CUCode_AX::HandleCommandList() SetupProcessing(HILO_TO_32(addr)); break; - case CMD_UNK_01: curr_idx += 5; break; + case CMD_DL_AND_VOL_MIX: + { + addr_hi = m_cmdlist[curr_idx++]; + addr_lo = m_cmdlist[curr_idx++]; + u16 vol_main = m_cmdlist[curr_idx++]; + u16 vol_auxa = m_cmdlist[curr_idx++]; + u16 vol_auxb = m_cmdlist[curr_idx++]; + DownloadAndMixWithVolume(HILO_TO_32(addr), vol_main, vol_auxa, vol_auxb); + break; + } case CMD_PB_ADDR: addr_hi = m_cmdlist[curr_idx++]; @@ -333,6 +342,30 @@ void CUCode_AX::SetupProcessing(u32 init_addr) } } +void CUCode_AX::DownloadAndMixWithVolume(u32 addr, u16 vol_main, u16 vol_auxa, u16 vol_auxb) +{ + int* buffers_main[3] = { m_samples_left, m_samples_right, m_samples_surround }; + int* buffers_auxa[3] = { m_samples_auxA_left, m_samples_auxA_right, m_samples_auxA_surround }; + int* buffers_auxb[3] = { m_samples_auxB_left, m_samples_auxB_right, m_samples_auxB_surround }; + int** buffers[3] = { buffers_main, buffers_auxa, buffers_auxb }; + u16 volumes[3] = { vol_main, vol_auxa, vol_auxb }; + + for (u32 i = 0; i < 3; ++i) + { + int* ptr = (int*)HLEMemory_Get_Pointer(addr); + s16 volume = (s16)volumes[i]; + for (u32 j = 0; j < 3; ++j) + { + int* buffer = buffers[i][j]; + for (u32 k = 0; k < 5 * 32; ++k) + { + s64 sample = 2 * (s32)Common::swap32(*ptr++) * volume; + buffer[k] += (s32)(sample >> 16); + } + } + } +} + void CUCode_AX::ProcessPBList(u32 pb_addr) { // Samples per millisecond. In theory DSP sampling rate can be changed from 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 7155a245bb..7158c197e8 100644 --- a/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_AX.h +++ b/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_AX.h @@ -131,6 +131,7 @@ protected: virtual void HandleCommandList(); void SetupProcessing(u32 init_addr); + void DownloadAndMixWithVolume(u32 addr, u16 vol_main, u16 vol_auxa, u16 vol_auxb); void ProcessPBList(u32 pb_addr); void MixAUXSamples(int aux_id, u32 write_addr, u32 read_addr); void UploadLRS(u32 dst_addr); @@ -147,7 +148,7 @@ private: enum CmdType { CMD_SETUP = 0x00, - CMD_UNK_01 = 0x01, + CMD_DL_AND_VOL_MIX = 0x01, CMD_PB_ADDR = 0x02, CMD_PROCESS = 0x03, CMD_MIX_AUXA = 0x04, From 3ca77cf077eed275d15d4f4c55e3cffb2d6f4478 Mon Sep 17 00:00:00 2001 From: Pierre Bourdon Date: Sat, 1 Dec 2012 00:54:25 +0100 Subject: [PATCH 35/44] Fix samples clamping and interlacing in the OUTPUT command. Shouldn't have assumed previous AX HLE was doing something right. --- Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_AX.cpp | 12 +++--------- 1 file changed, 3 insertions(+), 9 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 176050affa..6464e47c72 100644 --- a/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_AX.cpp +++ b/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_AX.cpp @@ -482,7 +482,7 @@ void CUCode_AX::OutputSamples(u32 lr_addr, u32 surround_addr) // 32 samples per ms, 5 ms, 2 channels short buffer[5 * 32 * 2]; - // Clamp internal buffers to 16 bits. + // Output samples clamped to 16 bits and interlaced RLRLRLRLRL... for (u32 i = 0; i < 5 * 32; ++i) { int left = m_samples_left[i]; @@ -493,14 +493,8 @@ void CUCode_AX::OutputSamples(u32 lr_addr, u32 surround_addr) 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 < 5 * 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(right); + buffer[2 * i + 1] = Common::swap16(left); } memcpy(HLEMemory_Get_Pointer(lr_addr), buffer, sizeof (buffer)); From ed59f8ff1d9a527e47b2429734e94f7bf1aed12c Mon Sep 17 00:00:00 2001 From: Pierre Bourdon Date: Tue, 18 Dec 2012 05:55:40 +0100 Subject: [PATCH 36/44] Remove UCode_AX_ADPCM.h, now useless (incorporated into UCode_AX_Voice.h) --- Source/Core/Core/Core.vcxproj | 1 - Source/Core/Core/Core.vcxproj.filters | 3 - .../Src/HW/DSPHLE/UCodes/UCode_AX_ADPCM.h | 92 ------------------- 3 files changed, 96 deletions(-) delete mode 100644 Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_AX_ADPCM.h diff --git a/Source/Core/Core/Core.vcxproj b/Source/Core/Core/Core.vcxproj index d83fa7fcc7..dca0d072fc 100644 --- a/Source/Core/Core/Core.vcxproj +++ b/Source/Core/Core/Core.vcxproj @@ -461,7 +461,6 @@ - diff --git a/Source/Core/Core/Core.vcxproj.filters b/Source/Core/Core/Core.vcxproj.filters index 8e2592450c..68953aef07 100644 --- a/Source/Core/Core/Core.vcxproj.filters +++ b/Source/Core/Core/Core.vcxproj.filters @@ -727,9 +727,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 diff --git a/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_AX_ADPCM.h b/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_AX_ADPCM.h deleted file mode 100644 index 9130bb9da4..0000000000 --- a/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_AX_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 From 70b3980f46c8160f9d174496e03e30a95a58c181 Mon Sep 17 00:00:00 2001 From: Pierre Bourdon Date: Tue, 18 Dec 2012 06:58:47 +0100 Subject: [PATCH 37/44] Fix AX HLE command 12 arguments: takes 4 u16, not 1 --- Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_AX.cpp | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) 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 6464e47c72..0ad33870db 100644 --- a/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_AX.cpp +++ b/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_AX.cpp @@ -193,7 +193,16 @@ void CUCode_AX::HandleCommandList() break; case CMD_UNK_11: curr_idx += 2; break; - case CMD_UNK_12: curr_idx += 1; break; + + case CMD_UNK_12: + { + u16 samp_val = m_cmdlist[curr_idx++]; + u16 idx = m_cmdlist[curr_idx++]; + addr_hi = m_cmdlist[curr_idx++]; + addr_lo = m_cmdlist[curr_idx++]; + // TODO + break; + } // Send the contents of MAIN LRS, AUXA LRS and AUXB S to RAM, and // mix data to MAIN LR and AUXB LR. From 1abbfd4b151e51eb9f90f6a495e217eab8eed108 Mon Sep 17 00:00:00 2001 From: Pierre Bourdon Date: Tue, 18 Dec 2012 07:00:04 +0100 Subject: [PATCH 38/44] Remove some useless 'inline' attributes from the AX HLE voice mixing functions --- Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_AX_Voice.h | 10 +++++----- 1 file changed, 5 insertions(+), 5 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 16df07e8a4..16a9870b9e 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 @@ -90,7 +90,7 @@ bool ReadPB(u32 addr, PB_TYPE& pb) } // Write a PB back to MRAM/ARAM -inline bool WritePB(u32 addr, const PB_TYPE& pb) +bool WritePB(u32 addr, const PB_TYPE& pb) { const u16* src = (const u16*)&pb; u16* dst = (u16*)Memory::GetPointer(addr); @@ -109,7 +109,7 @@ static u32* acc_cur_addr; static PB_TYPE* acc_pb; // Sets up the simulated accelerator. -inline void AcceleratorSetup(PB_TYPE* pb, u32* cur_addr) +void AcceleratorSetup(PB_TYPE* pb, u32* cur_addr) { acc_pb = pb; acc_loop_addr = HILO_TO_32(pb->audio_addr.loop_addr); @@ -120,7 +120,7 @@ inline void AcceleratorSetup(PB_TYPE* pb, u32* cur_addr) // Reads a sample from the simulated accelerator. Also handles looping and // disabling streams that reached the end (this is done by an exception raised // by the accelerator on real hardware). -inline u16 AcceleratorGetSample() +u16 AcceleratorGetSample() { u16 ret; @@ -215,7 +215,7 @@ inline u16 AcceleratorGetSample() } // Read 32 input samples from ARAM, decoding and converting rate if required. -inline void GetInputSamples(PB_TYPE& pb, s16* samples) +void GetInputSamples(PB_TYPE& pb, s16* samples) { u32 cur_addr = HILO_TO_32(pb.audio_addr.cur_addr); AcceleratorSetup(&pb, &cur_addr); @@ -304,7 +304,7 @@ inline void GetInputSamples(PB_TYPE& pb, s16* samples) } // Add samples to an output buffer, with optional volume ramping. -inline void MixAdd(int* out, const s16* input, u16* pvol, bool ramp) +void MixAdd(int* out, const s16* input, u16* pvol, bool ramp) { u16& volume = pvol[0]; u16 volume_delta = pvol[1]; From faaaa97c9e6cea7fa865312ce07a9c222983e50a Mon Sep 17 00:00:00 2001 From: Pierre Bourdon Date: Tue, 18 Dec 2012 07:00:24 +0100 Subject: [PATCH 39/44] Added a utility function to dump an AX PB for debugging --- .../Src/HW/DSPHLE/UCodes/UCode_AX_Voice.h | 20 +++++++++++++++++++ 1 file changed, 20 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 16a9870b9e..155965dfea 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 @@ -103,6 +103,26 @@ bool WritePB(u32 addr, const PB_TYPE& pb) return true; } +// Dump the value of a PB for debugging +#define DUMP_U16(field) WARN_LOG(DSPHLE, " %04x (%s)", pb.field, #field) +#define DUMP_U32(field) WARN_LOG(DSPHLE, " %08x (%s)", HILO_TO_32(pb.field), #field) +void DumpPB(const PB_TYPE& pb) +{ + DUMP_U32(next_pb); + DUMP_U32(this_pb); + DUMP_U16(src_type); + DUMP_U16(coef_select); +#ifdef AX_GC + DUMP_U16(mixer_control); +#else + DUMP_U32(mixer_control); +#endif + DUMP_U16(running); + DUMP_U16(is_stream); + + // TODO: complete as needed +} + // Simulated accelerator state. static u32 acc_loop_addr, acc_end_addr; static u32* acc_cur_addr; From 1cecbaedced4d8a44461f1eadda5d3b1949634c1 Mon Sep 17 00:00:00 2001 From: Pierre Bourdon Date: Tue, 18 Dec 2012 07:01:22 +0100 Subject: [PATCH 40/44] Fix MixAdd with volume. The previous volume formula completely failed with volumes >= 1.0 (0x8000). Fixes Tomb Raider Legends (GC) music. --- 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 155965dfea..bf13c7114d 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 @@ -337,8 +337,11 @@ void MixAdd(int* out, const s16* input, u16* pvol, bool ramp) for (u32 i = 0; i < 32; ++i) { - s64 sample = 2 * (s16)input[i] * (s16)volume; - out[i] += (s32)(sample >> 16); + s64 sample = input[i]; + sample *= volume; + sample >>= 15; + + out[i] += (s32)sample; volume += volume_delta; } } From 2c10ca4e46e18696b39798b00866de3a29e09272 Mon Sep 17 00:00:00 2001 From: Pierre Bourdon Date: Tue, 18 Dec 2012 17:38:41 +0100 Subject: [PATCH 41/44] Rewrite the linear sample rate conversion algorithm to make it less hacky and to support arbitrarly big ratios --- .../Src/HW/DSPHLE/UCodes/UCode_AX_Voice.h | 60 +++++++------------ 1 file changed, 21 insertions(+), 39 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 bf13c7114d..a6d1f760a5 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 @@ -256,56 +256,38 @@ void GetInputSamples(PB_TYPE& pb, s16* samples) // samples. u32 curr_pos = pb.src.cur_addr_frac; - // Compute the number of real samples we will need to read from the - // data source. We need to output 32 samples, so we need to read - // 32 * ratio + curr_pos samples. There does not seem to be a maximum - // value for the ratio in recent versions of AXWii (previously it was - // limited to 4.0), so we will limit it to 16.0 and clamp the ratio if - // needed. This is a HACK, and using another algorithm for linear - // interpolation might be a better idea. - if (ratio > 0x00100000) - ratio = 0x00100000; - - s16 real_samples[514]; - u32 real_samples_needed = (32 * ratio + curr_pos) >> 16; - - // The first two real samples are the ones we read at the previous - // iteration. That way we can interpolate before having read 2 new - // samples from the accelerator. - // - // The next real samples are read from the accelerator. - real_samples[0] = pb.src.last_samples[2]; - real_samples[1] = pb.src.last_samples[3]; - for (u32 i = 0; i < real_samples_needed; ++i) - real_samples[i + 2] = AcceleratorGetSample(); + // 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]; for (u32 i = 0; i < 32; ++i) { - // Get our current integer and fractional position. The integer - // position is used to get the two samples around us. The - // fractional position is used to compute the linear interpolation - // between these two samples. - u32 curr_int_pos = (curr_pos >> 16); + // 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; - s16 samp1 = real_samples[curr_int_pos]; - s16 samp2 = real_samples[curr_int_pos + 1]; // Linear interpolation: s1 + (s2 - s1) * pos - s16 sample = samp1 + (s16)(((samp2 - samp1) * (s32)curr_frac_pos) >> 16); + 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) + { + curr0 = curr1; + curr1 = AcceleratorGetSample(); + curr_pos -= 0x10000; + } } - // Update the last_samples array. A bit tricky because we can't know - // for sure we have more than 4 real samples in our array. - if (real_samples_needed >= 2) - memcpy(pb.src.last_samples, &real_samples[real_samples_needed + 2 - 4], 4 * sizeof (u16)); - else - { - memmove(pb.src.last_samples, &pb.src.last_samples[real_samples_needed], (4 - real_samples_needed) * sizeof (u16)); - memcpy(&pb.src.last_samples[4 - real_samples_needed], &real_samples[2], real_samples_needed * sizeof (u16)); - } + // Update the two 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.cur_addr_frac = curr_pos & 0xFFFF; } else // SRCTYPE_NEAREST From 071b27f0bb248069cfb38f4da7cecee2015a60ad Mon Sep 17 00:00:00 2001 From: Pierre Bourdon Date: Wed, 19 Dec 2012 05:56:57 +0100 Subject: [PATCH 42/44] Fix volume mixing in DownloadAndMixWithVolume (same issue with >1.0 volumes) --- Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_AX.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 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 0ad33870db..57032a774e 100644 --- a/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_AX.cpp +++ b/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_AX.cpp @@ -362,14 +362,15 @@ void CUCode_AX::DownloadAndMixWithVolume(u32 addr, u16 vol_main, u16 vol_auxa, u for (u32 i = 0; i < 3; ++i) { int* ptr = (int*)HLEMemory_Get_Pointer(addr); - s16 volume = (s16)volumes[i]; + u16 volume = volumes[i]; for (u32 j = 0; j < 3; ++j) { int* buffer = buffers[i][j]; for (u32 k = 0; k < 5 * 32; ++k) { - s64 sample = 2 * (s32)Common::swap32(*ptr++) * volume; - buffer[k] += (s32)(sample >> 16); + s64 sample = (s64)(s32)Common::swap32(*ptr++); + sample *= volume; + buffer[k] += (s32)(sample >> 15); } } } From b0cbac174b0c4d419cb6f93b24bf18fc5090c42e Mon Sep 17 00:00:00 2001 From: Pierre Bourdon Date: Wed, 19 Dec 2012 08:31:17 +0100 Subject: [PATCH 43/44] Implemented dpop samples support --- .../Src/HW/DSPHLE/UCodes/UCode_AXStructs.h | 12 +++++++- .../Src/HW/DSPHLE/UCodes/UCode_AX_Voice.h | 30 ++++++++++--------- 2 files changed, 27 insertions(+), 15 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 e3c6b7165b..c92196dd49 100644 --- a/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_AXStructs.h +++ b/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_AXStructs.h @@ -128,7 +128,17 @@ struct PBUpdates // and ramped down on a per-sample basis to provide a gentle "roll off." struct PBDpop { - s16 unknown[9]; + s16 left; + s16 auxA_left; + s16 auxB_left; + + s16 right; + s16 auxA_right; + s16 auxB_right; + + s16 surround; + s16 auxA_surround; + s16 auxB_surround; }; struct PBDpopWii 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 a6d1f760a5..625b761b80 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 @@ -306,7 +306,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, bool ramp) +void MixAdd(int* out, const s16* input, u16* pvol, s16* dpop, bool ramp) { u16& volume = pvol[0]; u16 volume_delta = pvol[1]; @@ -323,8 +323,10 @@ void MixAdd(int* out, const s16* input, u16* pvol, bool ramp) sample *= volume; sample >>= 15; - out[i] += (s32)sample; + out[i] += (s16)sample; volume += volume_delta; + + *dpop = (s16)sample; } } @@ -357,33 +359,33 @@ void Process1ms(PB_TYPE& pb, const AXBuffers& buffers, AXMixControl mctrl) // TODO: Handle DPL2 on AUXB. if (mctrl & MIX_L) - MixAdd(buffers.left, samples, &pb.mixer.left, mctrl & MIX_L_RAMP); + MixAdd(buffers.left, samples, &pb.mixer.left, &pb.dpop.left, mctrl & MIX_L_RAMP); if (mctrl & MIX_R) - MixAdd(buffers.right, samples, &pb.mixer.right, mctrl & MIX_R_RAMP); + MixAdd(buffers.right, samples, &pb.mixer.right, &pb.dpop.right, mctrl & MIX_R_RAMP); if (mctrl & MIX_S) - MixAdd(buffers.surround, samples, &pb.mixer.surround, mctrl & MIX_S_RAMP); + MixAdd(buffers.surround, samples, &pb.mixer.surround, &pb.dpop.surround, mctrl & MIX_S_RAMP); if (mctrl & MIX_AUXA_L) - MixAdd(buffers.auxA_left, samples, &pb.mixer.auxA_left, mctrl & MIX_AUXA_L_RAMP); + MixAdd(buffers.auxA_left, samples, &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, mctrl & MIX_AUXA_R_RAMP); + MixAdd(buffers.auxA_right, samples, &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, mctrl & MIX_AUXA_S_RAMP); + MixAdd(buffers.auxA_surround, samples, &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, mctrl & MIX_AUXB_L_RAMP); + MixAdd(buffers.auxB_left, samples, &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, mctrl & MIX_AUXB_R_RAMP); + MixAdd(buffers.auxB_right, samples, &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, mctrl & MIX_AUXB_S_RAMP); + MixAdd(buffers.auxB_surround, samples, &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, mctrl & MIX_AUXC_L_RAMP); + MixAdd(buffers.auxC_left, samples, &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, mctrl & MIX_AUXC_R_RAMP); + MixAdd(buffers.auxC_right, samples, &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, mctrl & MIX_AUXC_S_RAMP); + MixAdd(buffers.auxC_surround, samples, &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. From 4a74da3e876b7ed9e96767da309280483995c6a8 Mon Sep 17 00:00:00 2001 From: Pierre Bourdon Date: Tue, 25 Dec 2012 13:30:55 +0100 Subject: [PATCH 44/44] Readd the old AXWii (rename the new version to NewAXWii) and set it as default --- 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 +- .../Core/Src/HW/DSPHLE/UCodes/UCode_AXWii.cpp | 522 +++++++----------- .../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 +++++++++ .../{UCode_AXStructs.h => UCode_AX_Structs.h} | 0 .../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, 1454 insertions(+), 376 deletions(-) create mode 100644 Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_AXWii_ADPCM.h create mode 100644 Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_AXWii_Structs.h create mode 100644 Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_AXWii_Voice.h rename Source/Core/Core/Src/HW/DSPHLE/UCodes/{UCode_AXStructs.h => UCode_AX_Structs.h} (100%) create mode 100644 Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_NewAXWii.cpp create 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 734d966781..93def26ec2 100644 --- a/Source/Core/Core/CMakeLists.txt +++ b/Source/Core/Core/CMakeLists.txt @@ -71,7 +71,8 @@ set(SRCS Src/ActionReplay.cpp Src/HW/CPU.cpp Src/HW/DSP.cpp Src/HW/DSPHLE/UCodes/UCode_AX.cpp - Src/HW/DSPHLE/UCodes/UCode_AXWii.cpp + Src/HW/DSPHLE/UCodes/UCode_AXWii.cpp + Src/HW/DSPHLE/UCodes/UCode_NewAXWii.cpp Src/HW/DSPHLE/UCodes/UCode_CARD.cpp Src/HW/DSPHLE/UCodes/UCode_InitAudioSystem.cpp Src/HW/DSPHLE/UCodes/UCode_ROM.cpp diff --git a/Source/Core/Core/Core.vcxproj b/Source/Core/Core/Core.vcxproj index dca0d072fc..763308db73 100644 --- a/Source/Core/Core/Core.vcxproj +++ b/Source/Core/Core/Core.vcxproj @@ -262,6 +262,7 @@ + @@ -459,8 +460,11 @@ - + + + + diff --git a/Source/Core/Core/Core.vcxproj.filters b/Source/Core/Core/Core.vcxproj.filters index 68953aef07..ad6db227d5 100644 --- a/Source/Core/Core/Core.vcxproj.filters +++ b/Source/Core/Core/Core.vcxproj.filters @@ -197,6 +197,9 @@ HW %28Flipper/Hollywood%29\DSP Interface + HLE\HLE\uCodes + + HW %28Flipper/Hollywood%29\DSP Interface + HLE\HLE\uCodes + HW %28Flipper/Hollywood%29\DSP Interface + HLE\HLE\uCodes @@ -730,12 +733,21 @@ HW %28Flipper/Hollywood%29\DSP Interface + HLE\HLE\uCodes - + HW %28Flipper/Hollywood%29\DSP Interface + HLE\HLE\uCodes HW %28Flipper/Hollywood%29\DSP Interface + HLE\HLE\uCodes + + HW %28Flipper/Hollywood%29\DSP Interface + HLE\HLE\uCodes + + + HW %28Flipper/Hollywood%29\DSP Interface + HLE\HLE\uCodes + + + HW %28Flipper/Hollywood%29\DSP Interface + HLE\HLE\uCodes + HW %28Flipper/Hollywood%29\DSP Interface + HLE\HLE\uCodes diff --git a/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_AX.cpp b/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_AX.cpp index 57032a774e..81ef7c6482 100644 --- a/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_AX.cpp +++ b/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_AX.cpp @@ -200,7 +200,11 @@ void CUCode_AX::HandleCommandList() u16 idx = m_cmdlist[curr_idx++]; addr_hi = m_cmdlist[curr_idx++]; addr_lo = m_cmdlist[curr_idx++]; + // TODO + (void)samp_val; + (void)idx; + break; } diff --git a/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_AX.h b/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_AX.h index 7158c197e8..8fd2a2af5f 100644 --- a/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_AX.h +++ b/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_AX.h @@ -27,7 +27,7 @@ #define _UCODE_AX_H #include "UCodes.h" -#include "UCode_AXStructs.h" +#include "UCode_AX_Structs.h" // We can't directly use the mixer_control field from the PB because it does // not mean the same in all AX versions. The AX UCode converts the diff --git a/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_AXWii.cpp b/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_AXWii.cpp index 8c74c7a1e7..f4effbba6d 100644 --- a/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_AXWii.cpp +++ b/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_AXWii.cpp @@ -21,363 +21,247 @@ #include "Mixer.h" #include "UCodes.h" -#include "UCode_AXStructs.h" +#include "UCode_AXWii_Structs.h" +#include "UCode_AX.h" // for some functions in CUCode_AX #include "UCode_AXWii.h" - -#define AX_WII -#include "UCode_AX_Voice.h" +#include "UCode_AXWii_Voice.h" CUCode_AXWii::CUCode_AXWii(DSPHLE *dsp_hle, u32 l_CRC) - : CUCode_AX(dsp_hle, l_CRC) + : IUCode(dsp_hle, l_CRC) + , m_addressPBs(0xFFFFFFFF) { - WARN_LOG(DSPHLE, "Instantiating CUCode_AXWii"); + // we got loaded + m_rMailHandler.PushMail(DSP_INIT); + + templbuffer = new int[1024 * 1024]; + temprbuffer = new int[1024 * 1024]; + + wiisportsHack = m_CRC == 0xfa450138; } CUCode_AXWii::~CUCode_AXWii() { + m_rMailHandler.Clear(); + delete [] templbuffer; + delete [] temprbuffer; } -void CUCode_AXWii::HandleCommandList() +void CUCode_AXWii::HandleMail(u32 _uMail) { - // Temp variables for addresses computation - u16 addr_hi, addr_lo; - u16 addr2_hi, addr2_lo; - u16 volume; - -// WARN_LOG(DSPHLE, "Command list:"); -// for (u32 i = 0; m_cmdlist[i] != CMD_END; ++i) -// WARN_LOG(DSPHLE, "%04x", m_cmdlist[i]); -// WARN_LOG(DSPHLE, "-------------"); - - u32 curr_idx = 0; - bool end = false; - while (!end) + if (m_UploadSetupInProgress) { - u16 cmd = m_cmdlist[curr_idx++]; - - switch (cmd) - { - // Some of these commands are unknown, or unused in this AX HLE. - // We still need to skip their arguments using "curr_idx += N". - - case CMD_SETUP: - addr_hi = m_cmdlist[curr_idx++]; - addr_lo = m_cmdlist[curr_idx++]; - SetupProcessing(HILO_TO_32(addr)); - break; - - case CMD_UNK_01: curr_idx += 2; break; - case CMD_UNK_02: curr_idx += 2; break; - case CMD_UNK_03: curr_idx += 2; break; - - case CMD_PROCESS: - addr_hi = m_cmdlist[curr_idx++]; - addr_lo = m_cmdlist[curr_idx++]; - ProcessPBList(HILO_TO_32(addr)); - break; - - case CMD_MIX_AUXA: - case CMD_MIX_AUXB: - case CMD_MIX_AUXC: - volume = m_cmdlist[curr_idx++]; - addr_hi = m_cmdlist[curr_idx++]; - addr_lo = m_cmdlist[curr_idx++]; - addr2_hi = m_cmdlist[curr_idx++]; - addr2_lo = m_cmdlist[curr_idx++]; - MixAUXSamples(cmd - CMD_MIX_AUXA, HILO_TO_32(addr), HILO_TO_32(addr2), volume); - break; - - // These two go together and manipulate some AUX buffers. - case CMD_UNK_08: curr_idx += 13; break; - case CMD_UNK_09: curr_idx += 13; break; - - case CMD_UNK_0A: curr_idx += 4; break; - - case CMD_OUTPUT: - volume = m_cmdlist[curr_idx++]; - addr_hi = m_cmdlist[curr_idx++]; - addr_lo = m_cmdlist[curr_idx++]; - addr2_hi = m_cmdlist[curr_idx++]; - addr2_lo = m_cmdlist[curr_idx++]; - OutputSamples(HILO_TO_32(addr2), HILO_TO_32(addr), volume); - break; - - case CMD_UNK_0C: curr_idx += 5; break; - - case CMD_WM_OUTPUT: - { - u32 addresses[4] = { - (u32)(m_cmdlist[curr_idx + 0] << 16) | m_cmdlist[curr_idx + 1], - (u32)(m_cmdlist[curr_idx + 2] << 16) | m_cmdlist[curr_idx + 3], - (u32)(m_cmdlist[curr_idx + 4] << 16) | m_cmdlist[curr_idx + 5], - (u32)(m_cmdlist[curr_idx + 6] << 16) | m_cmdlist[curr_idx + 7], - }; - curr_idx += 8; - OutputWMSamples(addresses); - break; - } - - case CMD_END: - end = true; - break; - } + PrepareBootUCode(_uMail); + return; } -} - -void CUCode_AXWii::SetupProcessing(u32 init_addr) -{ - // TODO: should be easily factorizable with AX - s16 init_data[60]; - - for (u32 i = 0; i < 60; ++i) - init_data[i] = HLEMemory_Read_U16(init_addr + 2 * i); - - // List of all buffers we have to initialize - struct { - int* ptr; - u32 samples; - } buffers[] = { - { m_samples_left, 32 }, - { m_samples_right, 32 }, - { m_samples_surround, 32 }, - { m_samples_auxA_left, 32 }, - { m_samples_auxA_right, 32 }, - { m_samples_auxA_surround, 32 }, - { m_samples_auxB_left, 32 }, - { m_samples_auxB_right, 32 }, - { m_samples_auxB_surround, 32 }, - { m_samples_auxC_left, 32 }, - { m_samples_auxC_right, 32 }, - { m_samples_auxC_surround, 32 }, - - { m_samples_wm0, 6 }, - { m_samples_aux0, 6 }, - { m_samples_wm1, 6 }, - { m_samples_aux1, 6 }, - { m_samples_wm2, 6 }, - { m_samples_aux2, 6 }, - { m_samples_wm3, 6 }, - { m_samples_aux3, 6 } - }; - - u32 init_idx = 0; - for (u32 i = 0; i < sizeof (buffers) / sizeof (buffers[0]); ++i) + else if ((_uMail & 0xFFFF0000) == MAIL_AX_ALIST) { - s32 init_val = (s32)((init_data[init_idx] << 16) | init_data[init_idx + 1]); - s16 delta = (s16)init_data[init_idx + 2]; - - init_idx += 3; - - if (!init_val) - memset(buffers[i].ptr, 0, 3 * buffers[i].samples * sizeof (int)); - else - { - for (u32 j = 0; j < 3 * buffers[i].samples; ++j) - { - buffers[i].ptr[j] = init_val; - init_val += delta; - } - } + // We are expected to get a new CmdBlock + DEBUG_LOG(DSPHLE, "GetNextCmdBlock (%ibytes)", (u16)_uMail); } -} - -AXMixControl CUCode_AXWii::ConvertMixerControl(u32 mixer_control) -{ - u32 ret = 0; - - if (mixer_control & 0x00000001) ret |= MIX_L; - if (mixer_control & 0x00000002) ret |= MIX_R; - if (mixer_control & 0x00000004) ret |= MIX_L_RAMP | MIX_R_RAMP; - if (mixer_control & 0x00000008) ret |= MIX_S; - if (mixer_control & 0x00000010) ret |= MIX_S_RAMP; - if (mixer_control & 0x00010000) ret |= MIX_AUXA_L; - if (mixer_control & 0x00020000) ret |= MIX_AUXA_R; - if (mixer_control & 0x00040000) ret |= MIX_AUXA_L_RAMP | MIX_AUXA_R_RAMP; - if (mixer_control & 0x00080000) ret |= MIX_AUXA_S; - if (mixer_control & 0x00100000) ret |= MIX_AUXA_S_RAMP; - if (mixer_control & 0x00200000) ret |= MIX_AUXB_L; - if (mixer_control & 0x00400000) ret |= MIX_AUXB_R; - if (mixer_control & 0x00800000) ret |= MIX_AUXB_L_RAMP | MIX_AUXB_R_RAMP; - if (mixer_control & 0x01000000) ret |= MIX_AUXB_S; - if (mixer_control & 0x02000000) ret |= MIX_AUXB_S_RAMP; - if (mixer_control & 0x04000000) ret |= MIX_AUXC_L; - if (mixer_control & 0x08000000) ret |= MIX_AUXC_R; - if (mixer_control & 0x10000000) ret |= MIX_AUXC_L_RAMP | MIX_AUXC_R_RAMP; - if (mixer_control & 0x20000000) ret |= MIX_AUXC_S; - if (mixer_control & 0x40000000) ret |= MIX_AUXC_S_RAMP; - - return (AXMixControl)ret; -} - -void CUCode_AXWii::ProcessPBList(u32 pb_addr) -{ - const u32 spms = 32; - - AXPBWii pb; - - while (pb_addr) + else switch(_uMail) { - AXBuffers buffers = {{ - m_samples_left, - m_samples_right, - m_samples_surround, - m_samples_auxA_left, - m_samples_auxA_right, - m_samples_auxA_surround, - m_samples_auxB_left, - m_samples_auxB_right, - m_samples_auxB_surround, - m_samples_auxC_left, - m_samples_auxC_right, - m_samples_auxC_surround - }}; - - if (!ReadPB(pb_addr, pb)) + case 0xCDD10000: // Action 0 - AX_ResumeTask() + m_rMailHandler.PushMail(DSP_RESUME); break; - for (int curr_ms = 0; curr_ms < 3; ++curr_ms) - { - Process1ms(pb, buffers, ConvertMixerControl(HILO_TO_32(pb.mixer_control))); + case 0xCDD10001: // Action 1 - new ucode upload + DEBUG_LOG(DSPHLE,"DSP IROM - New Ucode!"); + // TODO find a better way to protect from HLEMixer? + soundStream->GetMixer()->SetHLEReady(false); + m_UploadSetupInProgress = true; + break; - // Forward the buffers - for (u32 i = 0; i < sizeof (buffers.ptrs) / sizeof (buffers.ptrs[0]); ++i) - buffers.ptrs[i] += spms; - } + case 0xCDD10002: // Action 2 - IROM_Reset(); ( WII: De Blob, Cursed Mountain,...) + DEBUG_LOG(DSPHLE,"DSP IROM - Reset!"); + m_DSPHLE->SetUCode(UCODE_ROM); + return; - WritePB(pb_addr, pb); - pb_addr = HILO_TO_32(pb.next_pb); + case 0xCDD10003: // Action 3 - AX_GetNextCmdBlock() + break; + + default: + DEBUG_LOG(DSPHLE, " >>>> u32 MAIL : AXTask Mail (%08x)", _uMail); + AXTask(_uMail); + break; } } -void CUCode_AXWii::MixAUXSamples(int aux_id, u32 write_addr, u32 read_addr, u16 volume) +void CUCode_AXWii::MixAdd(short* _pBuffer, int _iSize) { - int* buffers[3] = { 0 }; - int* main_buffers[3] = { - m_samples_left, - m_samples_right, - m_samples_surround - }; + AXPBWii PB; - switch (aux_id) + if (_iSize > 1024 * 1024) + _iSize = 1024 * 1024; + + memset(templbuffer, 0, _iSize * sizeof(int)); + memset(temprbuffer, 0, _iSize * sizeof(int)); + + u32 blockAddr = m_addressPBs; + if (!blockAddr) + return; + + for (int i = 0; i < NUMBER_OF_PBS; i++) { - case 0: - buffers[0] = m_samples_auxA_left; - buffers[1] = m_samples_auxA_right; - buffers[2] = m_samples_auxA_surround; - break; + if (!ReadPB(blockAddr, PB)) + break; - case 1: - buffers[0] = m_samples_auxB_left; - buffers[1] = m_samples_auxB_right; - buffers[2] = m_samples_auxB_surround; - break; + if (wiisportsHack) + MixAddVoice(*(AXPBWiiSports*)&PB, templbuffer, temprbuffer, _iSize); + else + MixAddVoice(PB, templbuffer, temprbuffer, _iSize); - case 2: - buffers[0] = m_samples_auxC_left; - buffers[1] = m_samples_auxC_right; - buffers[2] = m_samples_auxC_surround; - break; - } + if (!WritePB(blockAddr, PB)) + break; + + // next PB, or done + blockAddr = (PB.next_pb_hi << 16) | PB.next_pb_lo; + if (!blockAddr) + break; + } - // Send the content of AUX buffers to the CPU - if (write_addr) + // We write the sound to _pBuffer + if (_pBuffer) { - int* ptr = (int*)HLEMemory_Get_Pointer(write_addr); - for (u32 i = 0; i < 3; ++i) - for (u32 j = 0; j < 3 * 32; ++j) - *ptr++ = Common::swap32(buffers[i][j]); - } - - // Then read the buffers from the CPU and add to our main buffers. - int* ptr = (int*)HLEMemory_Get_Pointer(read_addr); - for (u32 i = 0; i < 3; ++i) - for (u32 j = 0; j < 3 * 32; ++j) + for (int i = 0; i < _iSize; i++) { - s64 new_val = main_buffers[i][j] + Common::swap32(*ptr++); - main_buffers[i][j] = (new_val * volume) >> 15; - } -} - -void CUCode_AXWii::OutputSamples(u32 lr_addr, u32 surround_addr, u16 volume) -{ - int surround_buffer[3 * 32] = { 0 }; - - for (u32 i = 0; i < 3 * 32; ++i) - surround_buffer[i] = Common::swap32(m_samples_surround[i]); - memcpy(HLEMemory_Get_Pointer(surround_addr), surround_buffer, sizeof (surround_buffer)); - - short buffer[3 * 32 * 2]; - - // Clamp internal buffers to 16 bits. - for (u32 i = 0; i < 3 * 32; ++i) - { - int left = m_samples_left[i]; - int right = m_samples_right[i]; - - // Apply global volume. Cast to s64 to avoid overflow. - left = ((s64)left * volume) >> 15; - right = ((s64)right * volume) >> 15; - - if (left < -32767) left = -32767; - if (left > 32767) left = 32767; - if (right < -32767) right = -32767; - if (right > 32767) right = 32767; - - m_samples_left[i] = left; - m_samples_right[i] = right; - } - - for (u32 i = 0; i < 3 * 32; ++i) - { - buffer[2 * i] = Common::swap16(m_samples_left[i]); - buffer[2 * i + 1] = Common::swap16(m_samples_right[i]); - } - - memcpy(HLEMemory_Get_Pointer(lr_addr), buffer, sizeof (buffer)); -} - -void CUCode_AXWii::OutputWMSamples(u32* addresses) -{ - int* buffers[] = { - m_samples_wm0, - m_samples_wm1, - m_samples_wm2, - m_samples_wm3 - }; - - for (u32 i = 0; i < 4; ++i) - { - int* in = buffers[i]; - u16* out = (u16*)HLEMemory_Get_Pointer(addresses[i]); - for (u32 j = 0; j < 3 * 6; ++j) - { - int sample = in[j]; - if (sample < -32767) sample = -32767; - if (sample > 32767) sample = 32767; - out[j] = Common::swap16((u16)sample); + // Clamp into 16-bit. Maybe we should add a volume compressor here. + int left = templbuffer[i] + _pBuffer[0]; + int right = temprbuffer[i] + _pBuffer[1]; + if (left < -32767) left = -32767; + else if (left > 32767) left = 32767; + if (right < -32767) right = -32767; + else if (right > 32767) right = 32767; + *_pBuffer++ = left; + *_pBuffer++ = right; } } } + +void CUCode_AXWii::Update(int cycles) +{ + if (NeedsResumeMail()) + { + m_rMailHandler.PushMail(DSP_RESUME); + DSP::GenerateDSPInterruptFromDSPEmu(DSP::INT_DSP); + } + // check if we have to send something + else if (!m_rMailHandler.IsEmpty()) + { + DSP::GenerateDSPInterruptFromDSPEmu(DSP::INT_DSP); + } +} + +// AX seems to bootup one task only and waits for resume-callbacks +// everytime the DSP has "spare time" it sends a resume-mail to the CPU +// and the __DSPHandler calls a AX-Callback which generates a new AXFrame +bool CUCode_AXWii::AXTask(u32& _uMail) +{ + u32 uAddress = _uMail; + //u32 Addr__AXStudio; + //u32 Addr__AXOutSBuffer; + bool bExecuteList = true; + +/* + for (int i=0;i<64;i++) { + NOTICE_LOG(DSPHLE,"%x - %08x",uAddress+(i*4),HLEMemory_Read_U32(uAddress+(i*4))); + } +*/ + + while (bExecuteList) + { + u16 iCommand = HLEMemory_Read_U16(uAddress); + uAddress += 2; + //NOTICE_LOG(DSPHLE,"AXWII - AXLIST CMD %X",iCommand); + + switch (iCommand) + { + case 0x0000: + //Addr__AXStudio = HLEMemory_Read_U32(uAddress); + uAddress += 4; + break; + + case 0x0001: + uAddress += 4; + break; + + case 0x0003: + uAddress += 4; + break; + + case 0x0004: + // PBs are here now + m_addressPBs = HLEMemory_Read_U32(uAddress); + if (soundStream) + soundStream->GetMixer()->SetHLEReady(true); +// soundStream->Update(); + uAddress += 4; + break; + + case 0x0005: + if (!wiisportsHack) + uAddress += 10; + break; + + case 0x0006: + uAddress += 10; + break; + + case 0x0007: // AXLIST_SBUFFER + //Addr__AXOutSBuffer = HLEMemory_Read_U32(uAddress); + uAddress += 10; + break; + + case 0x0008: + uAddress += 26; + break; + + case 0x000a: + uAddress += wiisportsHack ? 4 : 8; // AXLIST_COMPRESSORTABLE + break; + + case 0x000b: + uAddress += wiisportsHack ? 2 : 10; + break; + + case 0x000c: + uAddress += wiisportsHack ? 8 : 10; + break; + + case 0x000d: + uAddress += 16; + break; + + case 0x000e: + if (wiisportsHack) + uAddress += 16; + else + bExecuteList = false; + break; + + case 0x000f: // only for Wii Sports uCode + bExecuteList = false; + break; + + default: + INFO_LOG(DSPHLE,"DSPHLE - AXwii - AXLIST - Unknown CMD: %x",iCommand); + // unknown command so stop the execution of this TaskList + bExecuteList = false; + break; + } + } + + m_rMailHandler.PushMail(DSP_YIELD); //its here in case there is a CMD fuckup + return true; +} + void CUCode_AXWii::DoState(PointerWrap &p) { - std::lock_guard lk(m_processing); + std::lock_guard lk(m_csMix); + + p.Do(m_addressPBs); + p.Do(wiisportsHack); DoStateShared(p); - DoAXState(p); - - p.Do(m_samples_auxC_left); - p.Do(m_samples_auxC_right); - p.Do(m_samples_auxC_surround); - - p.Do(m_samples_wm0); - p.Do(m_samples_wm1); - p.Do(m_samples_wm2); - p.Do(m_samples_wm3); - - p.Do(m_samples_aux0); - p.Do(m_samples_aux1); - p.Do(m_samples_aux2); - p.Do(m_samples_aux3); } diff --git a/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_AXWii.h b/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_AXWii.h index 3a66c30868..dc07e71a63 100644 --- a/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_AXWii.h +++ b/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_AXWii.h @@ -12,69 +12,44 @@ // A copy of the GPL 2.0 should have been included with the program. // If not, see http://www.gnu.org/licenses/ -// Official Git repository and contact information can be found at +// Official SVN repository and contact information can be found at // http://code.google.com/p/dolphin-emu/ -#ifndef _UCODE_AXWII_H -#define _UCODE_AXWII_H +#ifndef _UCODE_AXWII +#define _UCODE_AXWII -#include "UCode_AX.h" +#include "UCode_AXWii_Structs.h" -class CUCode_AXWii : public CUCode_AX +#define NUMBER_OF_PBS 128 + +class CUCode_AXWii : public IUCode { public: CUCode_AXWii(DSPHLE *dsp_hle, u32 _CRC); virtual ~CUCode_AXWii(); - virtual void DoState(PointerWrap &p); - -protected: - int m_samples_auxC_left[32 * 3]; - int m_samples_auxC_right[32 * 3]; - int m_samples_auxC_surround[32 * 3]; - - // Wiimote buffers - int m_samples_wm0[6 * 3]; - int m_samples_aux0[6 * 3]; - int m_samples_wm1[6 * 3]; - int m_samples_aux1[6 * 3]; - int m_samples_wm2[6 * 3]; - int m_samples_aux2[6 * 3]; - int m_samples_wm3[6 * 3]; - int m_samples_aux3[6 * 3]; - - // Convert a mixer_control bitfield to our internal representation for that - // value. Required because that bitfield has a different meaning in some - // versions of AX. - AXMixControl ConvertMixerControl(u32 mixer_control); - - virtual void HandleCommandList(); - - void SetupProcessing(u32 init_addr); - void ProcessPBList(u32 pb_addr); - void MixAUXSamples(int aux_id, u32 write_addr, u32 read_addr, u16 volume); - void OutputSamples(u32 lr_addr, u32 surround_addr, u16 volume); - void OutputWMSamples(u32* addresses); // 4 addresses + void HandleMail(u32 _uMail); + void MixAdd(short* _pBuffer, int _iSize); + void Update(int cycles); + void DoState(PointerWrap &p); private: - enum CmdType + enum { - CMD_SETUP = 0x00, - CMD_UNK_01 = 0x01, - CMD_UNK_02 = 0x02, - CMD_UNK_03 = 0x03, - CMD_PROCESS = 0x04, - CMD_MIX_AUXA = 0x05, - CMD_MIX_AUXB = 0x06, - CMD_MIX_AUXC = 0x07, - CMD_UNK_08 = 0x08, - CMD_UNK_09 = 0x09, - CMD_UNK_0A = 0x0A, - CMD_OUTPUT = 0x0B, - CMD_UNK_0C = 0x0C, - CMD_WM_OUTPUT = 0x0D, - CMD_END = 0x0E + MAIL_AX_ALIST = 0xBABE0000, }; + + // PBs + u32 m_addressPBs; + + bool wiisportsHack; + + int *templbuffer; + int *temprbuffer; + + // ax task message handler + bool AXTask(u32& _uMail); + void SendMail(u32 _uMail); }; #endif // _UCODE_AXWII diff --git a/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_AXWii_ADPCM.h b/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_AXWii_ADPCM.h new file mode 100644 index 0000000000..9130bb9da4 --- /dev/null +++ b/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_AXWii_ADPCM.h @@ -0,0 +1,92 @@ +// Copyright (C) 2003 Dolphin Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official SVN repository and contact information can be found at +// http://code.google.com/p/dolphin-emu/ + +#ifndef _UCODE_AX_ADPCM_H +#define _UCODE_AX_ADPCM_H + +#include "../../DSP.h" + +static inline s16 ADPCM_Step(PBADPCMInfo &adpcm, u32& samplePos, u32 newSamplePos, u16 frac) +{ + while (samplePos < newSamplePos) + { + if ((samplePos & 15) == 0) + { + adpcm.pred_scale = DSP::ReadARAM((samplePos & ~15) >> 1); + samplePos += 2; + newSamplePos += 2; + } + + int scale = 1 << (adpcm.pred_scale & 0xF); + int coef_idx = (adpcm.pred_scale >> 4) & 7; + + s32 coef1 = adpcm.coefs[coef_idx * 2 + 0]; + s32 coef2 = adpcm.coefs[coef_idx * 2 + 1]; + + int temp = (samplePos & 1) ? + (DSP::ReadARAM(samplePos >> 1) & 0xF) : + (DSP::ReadARAM(samplePos >> 1) >> 4); + + if (temp >= 8) + temp -= 16; + + // 0x400 = 0.5 in 11-bit fixed point + int val = (scale * temp) + ((0x400 + coef1 * adpcm.yn1 + coef2 * adpcm.yn2) >> 11); + + if (val > 0x7FFF) + val = 0x7FFF; + else if (val < -0x7FFF) + val = -0x7FFF; + + adpcm.yn2 = adpcm.yn1; + adpcm.yn1 = val; + + samplePos++; + } + + return adpcm.yn1; +} + +// TODO: WTF is going on here?!? +// Volume control (ramping) +static inline u16 ADPCM_Vol(u16 vol, u16 delta) +{ + int x = vol; + if (delta && delta < 0x5000) + x += delta * 20 * 8; // unsure what the right step is + //x += 1 * 20 * 8; + else if (delta && delta > 0x5000) + //x -= (0x10000 - delta); // this is to small, it's often 1 + x -= (0x10000 - delta) * 20 * 16; // if this was 20 * 8 the sounds in Fire Emblem and Paper Mario + // did not have time to go to zero before the were closed + //x -= 1 * 20 * 16; + + // make lower limits + if (x < 0) x = 0; + //if (pb.mixer_control < 1000 && x < pb.mixer_control) x = pb.mixer_control; // does this make + // any sense? + + // make upper limits + //if (mixer_control > 1000 && x > mixer_control) x = mixer_control; // maybe mixer_control also + // has a volume target? + //if (x >= 0x7fff) x = 0x7fff; // this seems a little high + //if (x >= 0x4e20) x = 0x4e20; // add a definitive limit at 20 000 + if (x >= 0x8000) x = 0x8000; // clamp to 32768; + return x; // update volume +} + +#endif // _UCODE_AX_ADPCM_H diff --git a/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_AXWii_Structs.h b/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_AXWii_Structs.h new file mode 100644 index 0000000000..7f082740de --- /dev/null +++ b/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_AXWii_Structs.h @@ -0,0 +1,365 @@ +// Copyright (C) 2003 Dolphin Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official SVN repository and contact information can be found at +// http://code.google.com/p/dolphin-emu/ + +#ifndef _UCODE_AX_STRUCTS_H +#define _UCODE_AX_STRUCTS_H + +struct PBMixer +{ + u16 left; + u16 left_delta; + u16 right; + u16 right_delta; + + u16 unknown3[8]; + u16 unknown4[6]; +}; + +struct PBMixerWii +{ + // volume mixing values in .15, 0x8000 = ca. 1.0 + u16 left; + u16 left_delta; + u16 right; + u16 right_delta; + + u16 auxA_left; + u16 auxA_left_delta; + u16 auxA_right; + u16 auxA_right_delta; + + u16 auxB_left; + u16 auxB_left_delta; + u16 auxB_right; + u16 auxB_right_delta; + + // Note: the following elements usage changes a little in DPL2 mode + // TODO: implement and comment it in the mixer + u16 auxC_left; + u16 auxC_left_delta; + u16 auxC_right; + u16 auxC_right_delta; + + u16 surround; + u16 surround_delta; + u16 auxA_surround; + u16 auxA_surround_delta; + u16 auxB_surround; + u16 auxB_surround_delta; + u16 auxC_surround; + u16 auxC_surround_delta; +}; + +struct PBMixerWM +{ + u16 main0; + u16 main0_delta; + u16 aux0; + u16 aux0_delta; + + u16 main1; + u16 main1_delta; + u16 aux1; + u16 aux1_delta; + + u16 main2; + u16 main2_delta; + u16 aux2; + u16 aux2_delta; + + u16 main3; + u16 main3_delta; + u16 aux3; + u16 aux3_delta; +}; + +struct PBInitialTimeDelay +{ + u16 on; + u16 addrMemHigh; + u16 addrMemLow; + u16 offsetLeft; + u16 offsetRight; + u16 targetLeft; + u16 targetRight; +}; + +// Update data - read these each 1ms subframe and use them! +// It seems that to provide higher time precisions for MIDI events, some games +// use this thing to update the parameter blocks per 1ms sub-block (a block is 5ms). +// Using this data should fix games that are missing MIDI notes. +struct PBUpdates +{ + u16 num_updates[5]; + u16 data_hi; // These point to main RAM. Not sure about the structure of the data. + u16 data_lo; +}; + +// The DSP stores the final sample values for each voice after every frame of processing. +// The values are then accumulated for all dropped voices, added to the next frame of audio, +// and ramped down on a per-sample basis to provide a gentle "roll off." +struct PBDpop +{ + s16 unknown[9]; +}; + +struct PBDpopWii +{ + s16 left; + s16 auxA_left; + s16 auxB_left; + s16 auxC_left; + + s16 right; + s16 auxA_right; + s16 auxB_right; + s16 auxC_right; + + s16 surround; + s16 auxA_surround; + s16 auxB_surround; + s16 auxC_surround; +}; + +struct PBDpopWM +{ + s16 aMain0; + s16 aMain1; + s16 aMain2; + s16 aMain3; + + s16 aAux0; + s16 aAux1; + s16 aAux2; + s16 aAux3; +}; + +struct PBVolumeEnvelope +{ + u16 cur_volume; // volume at start of frame + s16 cur_volume_delta; // signed per sample delta (96 samples per frame) +}; + +struct PBUnknown2 +{ + u16 unknown_reserved[3]; +}; + +struct PBAudioAddr +{ + u16 looping; + u16 sample_format; + u16 loop_addr_hi; // Start of loop (this will point to a shared "zero" buffer if one-shot mode is active) + u16 loop_addr_lo; + u16 end_addr_hi; // End of sample (and loop), inclusive + u16 end_addr_lo; + u16 cur_addr_hi; + u16 cur_addr_lo; +}; + +struct PBADPCMInfo +{ + s16 coefs[16]; + u16 gain; + u16 pred_scale; + s16 yn1; + s16 yn2; +}; + +struct PBSampleRateConverter +{ + // ratio = (f32)ratio * 0x10000; + // valid range is 1/512 to 4.0000 + u16 ratio_hi; // integer part of sampling ratio + u16 ratio_lo; // fraction part of sampling ratio + u16 cur_addr_frac; + u16 last_samples[4]; +}; + +struct PBSampleRateConverterWM +{ + u16 currentAddressFrac; + u16 last_samples[4]; +}; + +struct PBADPCMLoopInfo +{ + u16 pred_scale; + u16 yn1; + u16 yn2; +}; + +struct AXPB +{ + u16 next_pb_hi; + u16 next_pb_lo; + u16 this_pb_hi; + u16 this_pb_lo; + + u16 src_type; // Type of sample rate converter (none, ?, linear) + u16 coef_select; + u16 mixer_control; + + u16 running; // 1=RUN 0=STOP + u16 is_stream; // 1 = stream, 0 = one shot + + PBMixer mixer; + PBInitialTimeDelay initial_time_delay; + PBUpdates updates; + PBDpop dpop; + PBVolumeEnvelope vol_env; + PBUnknown2 unknown3; + PBAudioAddr audio_addr; + PBADPCMInfo adpcm; + PBSampleRateConverter src; + PBADPCMLoopInfo adpcm_loop_info; + u16 unknown_maybe_padding[3]; +}; + +struct PBLowPassFilter +{ + u16 enabled; + u16 yn1; + u16 a0; + u16 b0; +}; + +struct PBBiquadFilter +{ + + u16 on; // on = 2, off = 0 + u16 xn1; // History data + u16 xn2; + u16 yn1; + u16 yn2; + u16 b0; // Filter coefficients + u16 b1; + u16 b2; + u16 a1; + u16 a2; + +}; + +union PBInfImpulseResponseWM +{ + PBLowPassFilter lpf; + PBBiquadFilter biquad; +}; + +struct AXPBWii +{ + u16 next_pb_hi; + u16 next_pb_lo; + u16 this_pb_hi; + u16 this_pb_lo; + + u16 src_type; // Type of sample rate converter (none, 4-tap, linear) + u16 coef_select; // coef for the 4-tap src + u32 mixer_control; + + u16 running; // 1=RUN 0=STOP + u16 is_stream; // 1 = stream, 0 = one shot + + PBMixerWii mixer; + PBInitialTimeDelay initial_time_delay; + PBDpopWii dpop; + PBVolumeEnvelope vol_env; + PBAudioAddr audio_addr; + PBADPCMInfo adpcm; + PBSampleRateConverter src; + PBADPCMLoopInfo adpcm_loop_info; + PBLowPassFilter lpf; + PBBiquadFilter biquad; + + // WIIMOTE :D + u16 remote; + u16 remote_mixer_control; + + PBMixerWM remote_mixer; + PBDpopWM remote_dpop; + PBSampleRateConverterWM remote_src; + PBInfImpulseResponseWM remote_iir; + + u16 pad[12]; // align us, captain! (32B) +}; + +// Seems like nintendo used an early version of AXWii and forgot to remove the update functionality ;p +struct PBUpdatesWiiSports +{ + u16 num_updates[3]; + u16 data_hi; + u16 data_lo; +}; + +struct AXPBWiiSports +{ + u16 next_pb_hi; + u16 next_pb_lo; + u16 this_pb_hi; + u16 this_pb_lo; + + u16 src_type; // Type of sample rate converter (none, 4-tap, linear) + u16 coef_select; // coef for the 4-tap src + u32 mixer_control; + + u16 running; // 1=RUN 0=STOP + u16 is_stream; // 1 = stream, 0 = one shot + + PBMixerWii mixer; + PBInitialTimeDelay initial_time_delay; + PBUpdatesWiiSports updates; + PBDpopWii dpop; + PBVolumeEnvelope vol_env; + PBAudioAddr audio_addr; + PBADPCMInfo adpcm; + PBSampleRateConverter src; + PBADPCMLoopInfo adpcm_loop_info; + PBLowPassFilter lpf; + PBBiquadFilter biquad; + + // WIIMOTE :D + u16 remote; + u16 remote_mixer_control; + + PBMixerWM remote_mixer; + PBDpopWM remote_dpop; + PBSampleRateConverterWM remote_src; + PBInfImpulseResponseWM remote_iir; + + u16 pad[7]; // align us, captain! (32B) +}; + +// TODO: All these enums have changed a lot for wii +enum { + AUDIOFORMAT_ADPCM = 0, + AUDIOFORMAT_PCM8 = 0x19, + AUDIOFORMAT_PCM16 = 0xA, +}; + +enum { + SRCTYPE_LINEAR = 1, + SRCTYPE_NEAREST = 2, + MIXCONTROL_RAMPING = 8, +}; + +// Both may be used at once +enum { + FILTER_LOWPASS = 1, + FILTER_BIQUAD = 2, +}; + +#endif // _UCODE_AX_STRUCTS_H diff --git a/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_AXWii_Voice.h b/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_AXWii_Voice.h new file mode 100644 index 0000000000..55f7face27 --- /dev/null +++ b/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_AXWii_Voice.h @@ -0,0 +1,271 @@ +// Copyright (C) 2003 Dolphin Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official SVN repository and contact information can be found at +// http://code.google.com/p/dolphin-emu/ + +#ifndef _UCODE_AXWII_VOICE_H +#define _UCODE_AXWII_VOICE_H + +#include "UCodes.h" +#include "UCode_AXWii_ADPCM.h" +#include "UCode_AX.h" +#include "Mixer.h" +#include "../../AudioInterface.h" + +// MRAM -> ARAM for GC +inline bool ReadPB(u32 addr, AXPB &PB) +{ + const u16* PB_in_mram = (const u16*)Memory::GetPointer(addr); + if (PB_in_mram == NULL) + return false; + u16* PB_in_aram = (u16*)&PB; + + for (size_t p = 0; p < (sizeof(AXPB) >> 1); p++) + { + PB_in_aram[p] = Common::swap16(PB_in_mram[p]); + } + + return true; +} + +// MRAM -> ARAM for Wii +inline bool ReadPB(u32 addr, AXPBWii &PB) +{ + const u16* PB_in_mram = (const u16*)Memory::GetPointer(addr); + if (PB_in_mram == NULL) + return false; + u16* PB_in_aram = (u16*)&PB; + + // preswap the mixer_control + PB.mixer_control = ((u32)PB_in_mram[7] << 16) | ((u32)PB_in_mram[6] >> 16); + + for (size_t p = 0; p < (sizeof(AXPBWii) >> 1); p++) + { + PB_in_aram[p] = Common::swap16(PB_in_mram[p]); + } + + return true; +} + +// ARAM -> MRAM for GC +inline bool WritePB(u32 addr, AXPB &PB) +{ + const u16* PB_in_aram = (const u16*)&PB; + u16* PB_in_mram = (u16*)Memory::GetPointer(addr); + if (PB_in_mram == NULL) + return false; + + for (size_t p = 0; p < (sizeof(AXPB) >> 1); p++) + { + PB_in_mram[p] = Common::swap16(PB_in_aram[p]); + } + + return true; +} + +// ARAM -> MRAM for Wii +inline bool WritePB(u32 addr, AXPBWii &PB) +{ + const u16* PB_in_aram = (const u16*)&PB; + u16* PB_in_mram = (u16*)Memory::GetPointer(addr); + if (PB_in_mram == NULL) + return false; + + // preswap the mixer_control + *(u32*)&PB_in_mram[6] = (PB.mixer_control << 16) | (PB.mixer_control >> 16); + + for (size_t p = 0; p < (sizeof(AXPBWii) >> 1); p++) + { + PB_in_mram[p] = Common::swap16(PB_in_aram[p]); + } + + return true; +} + +////////////////////////////////////////////////////////////////////////// +// TODO: fix handling of gc/wii PB differences +// TODO: generally fix up the mess - looks crazy and kinda wrong +template +inline void MixAddVoice(ParamBlockType &pb, + int *templbuffer, int *temprbuffer, + int _iSize) +{ + if (pb.running) + { + const u32 ratio = (u32)(((pb.src.ratio_hi << 16) + pb.src.ratio_lo) + * /*ratioFactor:*/((float)AudioInterface::GetAIDSampleRate() / (float)soundStream->GetMixer()->GetSampleRate())); + u32 sampleEnd = (pb.audio_addr.end_addr_hi << 16) | pb.audio_addr.end_addr_lo; + u32 loopPos = (pb.audio_addr.loop_addr_hi << 16) | pb.audio_addr.loop_addr_lo; + + u32 samplePos = (pb.audio_addr.cur_addr_hi << 16) | pb.audio_addr.cur_addr_lo; + u32 frac = pb.src.cur_addr_frac; + + // ======================================================================================= + // Handle No-SRC streams - No src streams have pb.src_type == 2 and have pb.src.ratio_hi = 0 + // and pb.src.ratio_lo = 0. We handle that by setting the sampling ratio integer to 1. This + // makes samplePos update in the correct way. I'm unsure how we are actually supposed to + // detect that this setting. Updates did not fix this automatically. + // --------------------------------------------------------------------------------------- + // Stream settings + // src_type = 2 (most other games have src_type = 0) + // Affected games: + // Baten Kaitos - Eternal Wings (2003) + // Baten Kaitos - Origins (2006)? + // Soul Calibur 2: The movie music use src_type 2 but it needs no adjustment, perhaps + // the sound format plays in to, Baten use ADPCM, SC2 use PCM16 + //if (pb.src_type == 2 && (pb.src.ratio_hi == 0 && pb.src.ratio_lo == 0)) + if (pb.running && (pb.src.ratio_hi == 0 && pb.src.ratio_lo == 0)) + { + pb.src.ratio_hi = 1; + } + + // ======================================================================================= + // Games that use looping to play non-looping music streams - SSBM has info in all + // pb.adpcm_loop_info parameters but has pb.audio_addr.looping = 0. If we treat these streams + // like any other looping streams the music works. I'm unsure how we are actually supposed to + // detect that these kinds of blocks should be looping. It seems like pb.mixer_control == 0 may + // identify these types of blocks. Updates did not write any looping values. + if ( + (pb.adpcm_loop_info.pred_scale || pb.adpcm_loop_info.yn1 || pb.adpcm_loop_info.yn2) + && pb.mixer_control == 0 && pb.adpcm_loop_info.pred_scale <= 0x7F + ) + { + pb.audio_addr.looping = 1; + } + + + + // Top Spin 3 Wii + if (pb.audio_addr.sample_format > 25) + pb.audio_addr.sample_format = 0; + + // ======================================================================================= + // Walk through _iSize. _iSize = numSamples. If the game goes slow _iSize will be higher to + // compensate for that. _iSize can be as low as 100 or as high as 2000 some cases. + for (int s = 0; s < _iSize; s++) + { + int sample = 0; + u32 oldFrac = frac; + frac += ratio; + u32 newSamplePos = samplePos + (frac >> 16); //whole number of frac + + // ======================================================================================= + // Process sample format + switch (pb.audio_addr.sample_format) + { + case AUDIOFORMAT_PCM8: + pb.adpcm.yn2 = ((s8)DSP::ReadARAM(samplePos)) << 8; //current sample + pb.adpcm.yn1 = ((s8)DSP::ReadARAM(samplePos + 1)) << 8; //next sample + + if (pb.src_type == SRCTYPE_NEAREST) + sample = pb.adpcm.yn2; + else // linear interpolation + sample = (pb.adpcm.yn1 * (u16)oldFrac + pb.adpcm.yn2 * (u16)(0xFFFF - oldFrac) + pb.adpcm.yn2) >> 16; + + samplePos = newSamplePos; + break; + + case AUDIOFORMAT_PCM16: + pb.adpcm.yn2 = (s16)(u16)((DSP::ReadARAM(samplePos * 2) << 8) | (DSP::ReadARAM((samplePos * 2 + 1)))); //current sample + pb.adpcm.yn1 = (s16)(u16)((DSP::ReadARAM((samplePos + 1) * 2) << 8) | (DSP::ReadARAM(((samplePos + 1) * 2 + 1)))); //next sample + + if (pb.src_type == SRCTYPE_NEAREST) + sample = pb.adpcm.yn2; + else // linear interpolation + sample = (pb.adpcm.yn1 * (u16)oldFrac + pb.adpcm.yn2 * (u16)(0xFFFF - oldFrac) + pb.adpcm.yn2) >> 16; + + samplePos = newSamplePos; + break; + + case AUDIOFORMAT_ADPCM: + ADPCM_Step(pb.adpcm, samplePos, newSamplePos, frac); + + if (pb.src_type == SRCTYPE_NEAREST) + sample = pb.adpcm.yn2; + else // linear interpolation + sample = (pb.adpcm.yn1 * (u16)frac + pb.adpcm.yn2 * (u16)(0xFFFF - frac) + pb.adpcm.yn2) >> 16; //adpcm moves on frac + + break; + + default: + break; + } + + // =================================================================== + // Overall volume control. In addition to this there is also separate volume settings to + // different channels (left, right etc). + frac &= 0xffff; + + int vol = pb.vol_env.cur_volume >> 9; + sample = sample * vol >> 8; + + if (pb.mixer_control & MIXCONTROL_RAMPING) + { + int x = pb.vol_env.cur_volume; + x += pb.vol_env.cur_volume_delta; // I'm not sure about this, can anybody find a game + // that use this? Or how does it work? + if (x < 0) + x = 0; + if (x >= 0x7fff) + x = 0x7fff; + pb.vol_env.cur_volume = x; // maybe not per sample?? :P + } + + int leftmix = pb.mixer.left >> 5; + int rightmix = pb.mixer.right >> 5; + int left = sample * leftmix >> 8; + int right = sample * rightmix >> 8; + // adpcm has to walk from oldSamplePos to samplePos here + templbuffer[s] += left; + temprbuffer[s] += right; + + // Control the behavior when we reach the end of the sample + if (samplePos >= sampleEnd) + { + if (pb.audio_addr.looping == 1) + { + if ((samplePos & ~0x1f) == (sampleEnd & ~0x1f) || (pb.audio_addr.sample_format != AUDIOFORMAT_ADPCM)) + samplePos = loopPos; + if ((!pb.is_stream) && (pb.audio_addr.sample_format == AUDIOFORMAT_ADPCM)) + { + pb.adpcm.yn1 = pb.adpcm_loop_info.yn1; + pb.adpcm.yn2 = pb.adpcm_loop_info.yn2; + pb.adpcm.pred_scale = pb.adpcm_loop_info.pred_scale; + } + } + else + { + pb.running = 0; + samplePos = loopPos; + //samplePos = samplePos - sampleEnd + loopPos; + memset(&pb.dpop, 0, sizeof(pb.dpop)); + memset(pb.src.last_samples, 0, 8); + break; + } + } + } // end of the _iSize loop + + // Update volume + pb.mixer.left = ADPCM_Vol(pb.mixer.left, pb.mixer.left_delta); + pb.mixer.right = ADPCM_Vol(pb.mixer.right, pb.mixer.right_delta); + + pb.src.cur_addr_frac = (u16)frac; + pb.audio_addr.cur_addr_hi = samplePos >> 16; + pb.audio_addr.cur_addr_lo = (u16)samplePos; + + } // if (pb.running) +} + +#endif diff --git a/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_AXStructs.h b/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_AX_Structs.h similarity index 100% rename from Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_AXStructs.h rename to Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_AX_Structs.h diff --git a/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_AX_Voice.h b/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_AX_Voice.h index 625b761b80..349dc7e03b 100644 --- a/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_AX_Voice.h +++ b/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_AX_Voice.h @@ -27,7 +27,7 @@ #endif #include "Common.h" -#include "UCode_AXStructs.h" +#include "UCode_AX_Structs.h" #include "../../DSP.h" #ifdef AX_GC diff --git a/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_NewAXWii.cpp b/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_NewAXWii.cpp new file mode 100644 index 0000000000..40ad6e1947 --- /dev/null +++ b/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_NewAXWii.cpp @@ -0,0 +1,383 @@ +// Copyright (C) 2003 Dolphin Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official SVN repository and contact information can be found at +// http://code.google.com/p/dolphin-emu/ + +#include "StringUtil.h" + +#include "../MailHandler.h" +#include "Mixer.h" + +#include "UCodes.h" +#include "UCode_AX_Structs.h" +#include "UCode_NewAXWii.h" + +#define AX_WII +#include "UCode_AX_Voice.h" + + +CUCode_NewAXWii::CUCode_NewAXWii(DSPHLE *dsp_hle, u32 l_CRC) + : CUCode_AX(dsp_hle, l_CRC) +{ + WARN_LOG(DSPHLE, "Instantiating CUCode_NewAXWii"); +} + +CUCode_NewAXWii::~CUCode_NewAXWii() +{ +} + +void CUCode_NewAXWii::HandleCommandList() +{ + // Temp variables for addresses computation + u16 addr_hi, addr_lo; + u16 addr2_hi, addr2_lo; + u16 volume; + +// WARN_LOG(DSPHLE, "Command list:"); +// for (u32 i = 0; m_cmdlist[i] != CMD_END; ++i) +// WARN_LOG(DSPHLE, "%04x", m_cmdlist[i]); +// WARN_LOG(DSPHLE, "-------------"); + + u32 curr_idx = 0; + bool end = false; + while (!end) + { + u16 cmd = m_cmdlist[curr_idx++]; + + switch (cmd) + { + // Some of these commands are unknown, or unused in this AX HLE. + // We still need to skip their arguments using "curr_idx += N". + + case CMD_SETUP: + addr_hi = m_cmdlist[curr_idx++]; + addr_lo = m_cmdlist[curr_idx++]; + SetupProcessing(HILO_TO_32(addr)); + break; + + case CMD_UNK_01: curr_idx += 2; break; + case CMD_UNK_02: curr_idx += 2; break; + case CMD_UNK_03: curr_idx += 2; break; + + case CMD_PROCESS: + addr_hi = m_cmdlist[curr_idx++]; + addr_lo = m_cmdlist[curr_idx++]; + ProcessPBList(HILO_TO_32(addr)); + break; + + case CMD_MIX_AUXA: + case CMD_MIX_AUXB: + case CMD_MIX_AUXC: + volume = m_cmdlist[curr_idx++]; + addr_hi = m_cmdlist[curr_idx++]; + addr_lo = m_cmdlist[curr_idx++]; + addr2_hi = m_cmdlist[curr_idx++]; + addr2_lo = m_cmdlist[curr_idx++]; + MixAUXSamples(cmd - CMD_MIX_AUXA, HILO_TO_32(addr), HILO_TO_32(addr2), volume); + break; + + // These two go together and manipulate some AUX buffers. + case CMD_UNK_08: curr_idx += 13; break; + case CMD_UNK_09: curr_idx += 13; break; + + case CMD_UNK_0A: curr_idx += 4; break; + + case CMD_OUTPUT: + volume = m_cmdlist[curr_idx++]; + addr_hi = m_cmdlist[curr_idx++]; + addr_lo = m_cmdlist[curr_idx++]; + addr2_hi = m_cmdlist[curr_idx++]; + addr2_lo = m_cmdlist[curr_idx++]; + OutputSamples(HILO_TO_32(addr2), HILO_TO_32(addr), volume); + break; + + case CMD_UNK_0C: curr_idx += 5; break; + + case CMD_WM_OUTPUT: + { + u32 addresses[4] = { + (u32)(m_cmdlist[curr_idx + 0] << 16) | m_cmdlist[curr_idx + 1], + (u32)(m_cmdlist[curr_idx + 2] << 16) | m_cmdlist[curr_idx + 3], + (u32)(m_cmdlist[curr_idx + 4] << 16) | m_cmdlist[curr_idx + 5], + (u32)(m_cmdlist[curr_idx + 6] << 16) | m_cmdlist[curr_idx + 7], + }; + curr_idx += 8; + OutputWMSamples(addresses); + break; + } + + case CMD_END: + end = true; + break; + } + } +} + +void CUCode_NewAXWii::SetupProcessing(u32 init_addr) +{ + // TODO: should be easily factorizable with AX + s16 init_data[60]; + + for (u32 i = 0; i < 60; ++i) + init_data[i] = HLEMemory_Read_U16(init_addr + 2 * i); + + // List of all buffers we have to initialize + struct { + int* ptr; + u32 samples; + } buffers[] = { + { m_samples_left, 32 }, + { m_samples_right, 32 }, + { m_samples_surround, 32 }, + { m_samples_auxA_left, 32 }, + { m_samples_auxA_right, 32 }, + { m_samples_auxA_surround, 32 }, + { m_samples_auxB_left, 32 }, + { m_samples_auxB_right, 32 }, + { m_samples_auxB_surround, 32 }, + { m_samples_auxC_left, 32 }, + { m_samples_auxC_right, 32 }, + { m_samples_auxC_surround, 32 }, + + { m_samples_wm0, 6 }, + { m_samples_aux0, 6 }, + { m_samples_wm1, 6 }, + { m_samples_aux1, 6 }, + { m_samples_wm2, 6 }, + { m_samples_aux2, 6 }, + { m_samples_wm3, 6 }, + { m_samples_aux3, 6 } + }; + + u32 init_idx = 0; + for (u32 i = 0; i < sizeof (buffers) / sizeof (buffers[0]); ++i) + { + s32 init_val = (s32)((init_data[init_idx] << 16) | init_data[init_idx + 1]); + s16 delta = (s16)init_data[init_idx + 2]; + + init_idx += 3; + + if (!init_val) + memset(buffers[i].ptr, 0, 3 * buffers[i].samples * sizeof (int)); + else + { + for (u32 j = 0; j < 3 * buffers[i].samples; ++j) + { + buffers[i].ptr[j] = init_val; + init_val += delta; + } + } + } +} + +AXMixControl CUCode_NewAXWii::ConvertMixerControl(u32 mixer_control) +{ + u32 ret = 0; + + if (mixer_control & 0x00000001) ret |= MIX_L; + if (mixer_control & 0x00000002) ret |= MIX_R; + if (mixer_control & 0x00000004) ret |= MIX_L_RAMP | MIX_R_RAMP; + if (mixer_control & 0x00000008) ret |= MIX_S; + if (mixer_control & 0x00000010) ret |= MIX_S_RAMP; + if (mixer_control & 0x00010000) ret |= MIX_AUXA_L; + if (mixer_control & 0x00020000) ret |= MIX_AUXA_R; + if (mixer_control & 0x00040000) ret |= MIX_AUXA_L_RAMP | MIX_AUXA_R_RAMP; + if (mixer_control & 0x00080000) ret |= MIX_AUXA_S; + if (mixer_control & 0x00100000) ret |= MIX_AUXA_S_RAMP; + if (mixer_control & 0x00200000) ret |= MIX_AUXB_L; + if (mixer_control & 0x00400000) ret |= MIX_AUXB_R; + if (mixer_control & 0x00800000) ret |= MIX_AUXB_L_RAMP | MIX_AUXB_R_RAMP; + if (mixer_control & 0x01000000) ret |= MIX_AUXB_S; + if (mixer_control & 0x02000000) ret |= MIX_AUXB_S_RAMP; + if (mixer_control & 0x04000000) ret |= MIX_AUXC_L; + if (mixer_control & 0x08000000) ret |= MIX_AUXC_R; + if (mixer_control & 0x10000000) ret |= MIX_AUXC_L_RAMP | MIX_AUXC_R_RAMP; + if (mixer_control & 0x20000000) ret |= MIX_AUXC_S; + if (mixer_control & 0x40000000) ret |= MIX_AUXC_S_RAMP; + + return (AXMixControl)ret; +} + +void CUCode_NewAXWii::ProcessPBList(u32 pb_addr) +{ + const u32 spms = 32; + + AXPBWii pb; + + while (pb_addr) + { + AXBuffers buffers = {{ + m_samples_left, + m_samples_right, + m_samples_surround, + m_samples_auxA_left, + m_samples_auxA_right, + m_samples_auxA_surround, + m_samples_auxB_left, + m_samples_auxB_right, + m_samples_auxB_surround, + m_samples_auxC_left, + m_samples_auxC_right, + m_samples_auxC_surround + }}; + + if (!ReadPB(pb_addr, pb)) + break; + + for (int curr_ms = 0; curr_ms < 3; ++curr_ms) + { + Process1ms(pb, buffers, ConvertMixerControl(HILO_TO_32(pb.mixer_control))); + + // Forward the buffers + for (u32 i = 0; i < sizeof (buffers.ptrs) / sizeof (buffers.ptrs[0]); ++i) + buffers.ptrs[i] += spms; + } + + WritePB(pb_addr, pb); + pb_addr = HILO_TO_32(pb.next_pb); + } +} + +void CUCode_NewAXWii::MixAUXSamples(int aux_id, u32 write_addr, u32 read_addr, u16 volume) +{ + int* buffers[3] = { 0 }; + int* main_buffers[3] = { + m_samples_left, + m_samples_right, + m_samples_surround + }; + + switch (aux_id) + { + case 0: + buffers[0] = m_samples_auxA_left; + buffers[1] = m_samples_auxA_right; + buffers[2] = m_samples_auxA_surround; + break; + + case 1: + buffers[0] = m_samples_auxB_left; + buffers[1] = m_samples_auxB_right; + buffers[2] = m_samples_auxB_surround; + break; + + case 2: + buffers[0] = m_samples_auxC_left; + buffers[1] = m_samples_auxC_right; + buffers[2] = m_samples_auxC_surround; + break; + } + + // Send the content of AUX buffers to the CPU + if (write_addr) + { + int* ptr = (int*)HLEMemory_Get_Pointer(write_addr); + for (u32 i = 0; i < 3; ++i) + for (u32 j = 0; j < 3 * 32; ++j) + *ptr++ = Common::swap32(buffers[i][j]); + } + + // Then read the buffers from the CPU and add to our main buffers. + int* ptr = (int*)HLEMemory_Get_Pointer(read_addr); + for (u32 i = 0; i < 3; ++i) + for (u32 j = 0; j < 3 * 32; ++j) + { + s64 new_val = main_buffers[i][j] + Common::swap32(*ptr++); + main_buffers[i][j] = (new_val * volume) >> 15; + } +} + +void CUCode_NewAXWii::OutputSamples(u32 lr_addr, u32 surround_addr, u16 volume) +{ + int surround_buffer[3 * 32] = { 0 }; + + for (u32 i = 0; i < 3 * 32; ++i) + surround_buffer[i] = Common::swap32(m_samples_surround[i]); + memcpy(HLEMemory_Get_Pointer(surround_addr), surround_buffer, sizeof (surround_buffer)); + + short buffer[3 * 32 * 2]; + + // Clamp internal buffers to 16 bits. + for (u32 i = 0; i < 3 * 32; ++i) + { + int left = m_samples_left[i]; + int right = m_samples_right[i]; + + // Apply global volume. Cast to s64 to avoid overflow. + left = ((s64)left * volume) >> 15; + right = ((s64)right * volume) >> 15; + + if (left < -32767) left = -32767; + if (left > 32767) left = 32767; + if (right < -32767) right = -32767; + if (right > 32767) right = 32767; + + m_samples_left[i] = left; + m_samples_right[i] = right; + } + + for (u32 i = 0; i < 3 * 32; ++i) + { + buffer[2 * i] = Common::swap16(m_samples_left[i]); + buffer[2 * i + 1] = Common::swap16(m_samples_right[i]); + } + + memcpy(HLEMemory_Get_Pointer(lr_addr), buffer, sizeof (buffer)); +} + +void CUCode_NewAXWii::OutputWMSamples(u32* addresses) +{ + int* buffers[] = { + m_samples_wm0, + m_samples_wm1, + m_samples_wm2, + m_samples_wm3 + }; + + for (u32 i = 0; i < 4; ++i) + { + int* in = buffers[i]; + u16* out = (u16*)HLEMemory_Get_Pointer(addresses[i]); + for (u32 j = 0; j < 3 * 6; ++j) + { + int sample = in[j]; + if (sample < -32767) sample = -32767; + if (sample > 32767) sample = 32767; + out[j] = Common::swap16((u16)sample); + } + } +} + +void CUCode_NewAXWii::DoState(PointerWrap &p) +{ + std::lock_guard lk(m_processing); + + DoStateShared(p); + DoAXState(p); + + p.Do(m_samples_auxC_left); + p.Do(m_samples_auxC_right); + p.Do(m_samples_auxC_surround); + + p.Do(m_samples_wm0); + p.Do(m_samples_wm1); + p.Do(m_samples_wm2); + p.Do(m_samples_wm3); + + p.Do(m_samples_aux0); + p.Do(m_samples_aux1); + p.Do(m_samples_aux2); + p.Do(m_samples_aux3); +} diff --git a/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_NewAXWii.h b/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_NewAXWii.h new file mode 100644 index 0000000000..4c9bc5757c --- /dev/null +++ b/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_NewAXWii.h @@ -0,0 +1,80 @@ +// Copyright (C) 2003 Dolphin Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official Git repository and contact information can be found at +// http://code.google.com/p/dolphin-emu/ + +#ifndef _UCODE_NEWAXWII_H +#define _UCODE_NEWAXWII_H + +#include "UCode_AX.h" + +class CUCode_NewAXWii : public CUCode_AX +{ +public: + CUCode_NewAXWii(DSPHLE *dsp_hle, u32 _CRC); + virtual ~CUCode_NewAXWii(); + + virtual void DoState(PointerWrap &p); + +protected: + int m_samples_auxC_left[32 * 3]; + int m_samples_auxC_right[32 * 3]; + int m_samples_auxC_surround[32 * 3]; + + // Wiimote buffers + int m_samples_wm0[6 * 3]; + int m_samples_aux0[6 * 3]; + int m_samples_wm1[6 * 3]; + int m_samples_aux1[6 * 3]; + int m_samples_wm2[6 * 3]; + int m_samples_aux2[6 * 3]; + int m_samples_wm3[6 * 3]; + int m_samples_aux3[6 * 3]; + + // Convert a mixer_control bitfield to our internal representation for that + // value. Required because that bitfield has a different meaning in some + // versions of AX. + AXMixControl ConvertMixerControl(u32 mixer_control); + + virtual void HandleCommandList(); + + void SetupProcessing(u32 init_addr); + void ProcessPBList(u32 pb_addr); + void MixAUXSamples(int aux_id, u32 write_addr, u32 read_addr, u16 volume); + void OutputSamples(u32 lr_addr, u32 surround_addr, u16 volume); + void OutputWMSamples(u32* addresses); // 4 addresses + +private: + enum CmdType + { + CMD_SETUP = 0x00, + CMD_UNK_01 = 0x01, + CMD_UNK_02 = 0x02, + CMD_UNK_03 = 0x03, + CMD_PROCESS = 0x04, + CMD_MIX_AUXA = 0x05, + CMD_MIX_AUXB = 0x06, + CMD_MIX_AUXC = 0x07, + CMD_UNK_08 = 0x08, + CMD_UNK_09 = 0x09, + CMD_UNK_0A = 0x0A, + CMD_OUTPUT = 0x0B, + CMD_UNK_0C = 0x0C, + CMD_WM_OUTPUT = 0x0D, + CMD_END = 0x0E + }; +}; + +#endif // _UCODE_AXWII diff --git a/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCodes.cpp b/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCodes.cpp index c04bf41403..86773ad020 100644 --- a/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCodes.cpp +++ b/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCodes.cpp @@ -19,6 +19,7 @@ #include "UCode_AX.h" #include "UCode_AXWii.h" +#include "UCode_NewAXWii.h" #include "UCode_Zelda.h" #include "UCode_ROM.h" #include "UCode_CARD.h" @@ -26,6 +27,12 @@ #include "UCode_GBA.h" #include "Hash.h" +#if 0 +# define AXWII CUCode_NewAXWii +#else +# define AXWII CUCode_AXWii +#endif + IUCode* UCodeFactory(u32 _CRC, DSPHLE *dsp_hle, bool bWii) { switch (_CRC) @@ -90,13 +97,13 @@ IUCode* UCodeFactory(u32 _CRC, DSPHLE *dsp_hle, bool bWii) case 0x4cc52064: // Bleach: Versus Crusade case 0xd9c4bf34: // WiiMenu INFO_LOG(DSPHLE, "CRC %08x: Wii - AXWii chosen", _CRC); - return new CUCode_AXWii(dsp_hle, _CRC); + return new AXWII(dsp_hle, _CRC); default: if (bWii) { PanicAlert("DSPHLE: Unknown ucode (CRC = %08x) - forcing AXWii.\n\nTry LLE emulator if this is homebrew.", _CRC); - return new CUCode_AXWii(dsp_hle, _CRC); + return new AXWII(dsp_hle, _CRC); } else {