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