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); };