diff --git a/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_AX.cpp b/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_AX.cpp index b21e2f66f4..9c7d57f813 100644 --- a/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_AX.cpp +++ b/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_AX.cpp @@ -307,19 +307,18 @@ void CUCode_AX::HandleCommandList() } } -static void ApplyUpdatesForMs(AXPB& pb, int curr_ms) +void CUCode_AX::ApplyUpdatesForMs(int curr_ms, u16* pb, u16* num_updates, u16* updates) { u32 start_idx = 0; for (int i = 0; i < curr_ms; ++i) - start_idx += pb.updates.num_updates[i]; + start_idx += num_updates[i]; - u32 update_addr = HILO_TO_32(pb.updates.data); - for (u32 i = start_idx; i < start_idx + pb.updates.num_updates[curr_ms]; ++i) + for (u32 i = start_idx; i < start_idx + num_updates[curr_ms]; ++i) { - u16 update_off = HLEMemory_Read_U16(update_addr + 4 * i); - u16 update_val = HLEMemory_Read_U16(update_addr + 4 * i + 2); + u16 update_off = Common::swap16(updates[2 * i]); + u16 update_val = Common::swap16(updates[2 * i + 1]); - ((u16*)&pb)[update_off] = update_val; + pb[update_off] = update_val; } } @@ -462,11 +461,14 @@ void CUCode_AX::ProcessPBList(u32 pb_addr) if (!ReadPB(pb_addr, pb)) break; + u32 updates_addr = HILO_TO_32(pb.updates.data); + u16* updates = (u16*)HLEMemory_Get_Pointer(updates_addr); + for (int curr_ms = 0; curr_ms < 5; ++curr_ms) { - ApplyUpdatesForMs(pb, curr_ms); + ApplyUpdatesForMs(curr_ms, (u16*)&pb, pb.updates.num_updates, updates); - ProcessVoice(pb, buffers, ConvertMixerControl(pb.mixer_control), + ProcessVoice(pb, buffers, spms, ConvertMixerControl(pb.mixer_control), m_coeffs_available ? m_coeffs : NULL); // Forward the buffers diff --git a/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_AX.h b/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_AX.h index fbecac1990..71f195bf33 100644 --- a/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_AX.h +++ b/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_AX.h @@ -134,6 +134,9 @@ protected: // versions of AX. AXMixControl ConvertMixerControl(u32 mixer_control); + // Apply updates to a PB. Generic, used in AX GC and AX Wii. + void ApplyUpdatesForMs(int curr_ms, u16* pb, u16* num_updates, u16* updates); + // Signal that we should start handling a command list. Dispatches to the // AX thread if using a thread, else just sets a boolean flag. void StartWorking(); diff --git a/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_AXStructs.h b/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_AXStructs.h index 5fc4806e26..1161847bc8 100644 --- a/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_AXStructs.h +++ b/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_AXStructs.h @@ -324,52 +324,6 @@ struct AXPBWii u16 pad[12]; // align us, captain! (32B) }; -// Seems like nintendo used an early version of AXWii and forgot to remove the update functionality ;p -struct PBUpdatesWiiSports -{ - u16 num_updates[3]; - u16 data_hi; - u16 data_lo; -}; - -struct AXPBWiiSports -{ - u16 next_pb_hi; - u16 next_pb_lo; - u16 this_pb_hi; - u16 this_pb_lo; - - u16 src_type; // Type of sample rate converter (none, 4-tap, linear) - u16 coef_select; // coef for the 4-tap src - u32 mixer_control; - - u16 running; // 1=RUN 0=STOP - u16 is_stream; // 1 = stream, 0 = one shot - - PBMixerWii mixer; - PBInitialTimeDelay initial_time_delay; - PBUpdatesWiiSports updates; - PBDpopWii dpop; - PBVolumeEnvelope vol_env; - PBAudioAddr audio_addr; - PBADPCMInfo adpcm; - PBSampleRateConverter src; - PBADPCMLoopInfo adpcm_loop_info; - PBLowPassFilter lpf; - PBBiquadFilter biquad; - - // WIIMOTE :D - u16 remote; - u16 remote_mixer_control; - - PBMixerWM remote_mixer; - PBDpopWM remote_dpop; - PBSampleRateConverterWM remote_src; - PBInfImpulseResponseWM remote_iir; - - u16 pad[7]; // align us, captain! (32B) -}; - // TODO: All these enums have changed a lot for wii enum { AUDIOFORMAT_ADPCM = 0, diff --git a/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_AXWii.cpp b/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_AXWii.cpp index df9a6b5409..64d27ffbd1 100644 --- a/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_AXWii.cpp +++ b/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_AXWii.cpp @@ -35,6 +35,8 @@ CUCode_AXWii::CUCode_AXWii(DSPHLE *dsp_hle, u32 l_CRC) for (int i = 0; i < 3; ++i) m_last_aux_volumes[i] = 0x8000; WARN_LOG(DSPHLE, "Instantiating CUCode_AXWii"); + + m_old_axwii = (l_CRC == 0xfa450138); } CUCode_AXWii::~CUCode_AXWii() @@ -100,6 +102,8 @@ void CUCode_AXWii::HandleCommandList() case CMD_UNK_08: curr_idx += 13; break; case CMD_UNK_09: curr_idx += 13; break; + // TODO(delroth): figure this one out, it's used by almost every + // game I've tested so far. case CMD_UNK_0A: curr_idx += 4; break; case CMD_OUTPUT: @@ -240,6 +244,43 @@ void CUCode_AXWii::GenerateVolumeRamp(u16* output, u16 vol1, u16 vol2, size_t nv } } +bool CUCode_AXWii::ExtractUpdatesFields(AXPBWii& pb, u16* num_updates, u16* updates) +{ + u16* pb_mem = (u16*)&pb; + + if (!m_old_axwii) + return false; + + // Copy the num_updates field. + memcpy(num_updates, pb_mem + 41, 6); + + // Get the address of the updates data + u16 addr_hi = pb_mem[44]; + u16 addr_lo = pb_mem[45]; + u32 addr = HILO_TO_32(addr); + u16* ptr = (u16*)HLEMemory_Get_Pointer(addr); + + // Copy the updates data and change the offset to match a PB without + // updates data. + u32 updates_count = num_updates[0] + num_updates[1] + num_updates[2]; + for (u32 i = 0; i < updates_count; ++i) + { + u16 update_off = Common::swap16(ptr[2 * i]); + u16 update_val = Common::swap16(ptr[2 * i + 1]); + + if (update_off > 45) + update_off -= 5; + + updates[2 * i] = update_off; + updates[2 * i + 1] = update_val; + } + + // Remove the updates data from the PB + memmove(pb_mem + 41, pb_mem + 45, sizeof (pb) - 2 * 45); + + return true; +} + void CUCode_AXWii::ProcessPBList(u32 pb_addr) { AXPBWii pb; @@ -272,8 +313,28 @@ void CUCode_AXWii::ProcessPBList(u32 pb_addr) if (!ReadPB(pb_addr, pb)) break; - ProcessVoice(pb, buffers, ConvertMixerControl(HILO_TO_32(pb.mixer_control)), - m_coeffs_available ? m_coeffs : NULL); + u16 num_updates[3]; + u16 updates[1024]; + if (ExtractUpdatesFields(pb, num_updates, updates)) + { + for (int curr_ms = 0; curr_ms < 3; ++curr_ms) + { + ApplyUpdatesForMs(curr_ms, (u16*)&pb, num_updates, updates); + ProcessVoice(pb, buffers, 32, + ConvertMixerControl(HILO_TO_32(pb.mixer_control)), + m_coeffs_available ? m_coeffs : NULL); + + // Forward the buffers + for (u32 i = 0; i < sizeof (buffers.ptrs) / sizeof (buffers.ptrs[0]); ++i) + buffers.ptrs[i] += 32; + } + } + else + { + ProcessVoice(pb, buffers, 96, + ConvertMixerControl(HILO_TO_32(pb.mixer_control)), + m_coeffs_available ? m_coeffs : NULL); + } WritePB(pb_addr, pb); pb_addr = HILO_TO_32(pb.next_pb); diff --git a/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_AXWii.h b/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_AXWii.h index c1d281d9c3..ce09b14674 100644 --- a/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_AXWii.h +++ b/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_AXWii.h @@ -44,11 +44,17 @@ protected: int m_samples_wm3[6 * 3]; int m_samples_aux3[6 * 3]; + // Are we implementing an old version of AXWii which still has updates? + bool m_old_axwii; + // Last volume values for MAIN and AUX. Used to generate volume ramps to // interpolate nicely between old and new volume values. u16 m_last_main_volume; u16 m_last_aux_volumes[3]; + // If needed, extract the updates related fields from a PB. + bool ExtractUpdatesFields(AXPBWii& pb, u16* num_updates, u16* updates); + // Convert a mixer_control bitfield to our internal representation for that // value. Required because that bitfield has a different meaning in some // versions of AX. diff --git a/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_AX_Voice.h b/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_AX_Voice.h index a93f69fb2c..e364e42888 100644 --- a/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_AX_Voice.h +++ b/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_AX_Voice.h @@ -41,10 +41,10 @@ using std::function; #ifdef AX_GC # define PB_TYPE AXPB -# define SAMPLES_PER_FRAME 32 +# define MAX_SAMPLES_PER_FRAME 32 #else # define PB_TYPE AXPBWii -# define SAMPLES_PER_FRAME 96 +# define MAX_SAMPLES_PER_FRAME 96 #endif // Put all of that in an anonymous namespace to avoid stupid compilers merging @@ -403,9 +403,9 @@ u32 ResampleAudio(function input_callback, s16* output, u32 count, return curr_pos; } -// Read SAMPLES_PER_FRAME input samples from ARAM, decoding and converting rate +// Read input samples from ARAM, decoding and converting rate // if required. -void GetInputSamples(PB_TYPE& pb, s16* samples, const s16* coeffs) +void GetInputSamples(PB_TYPE& pb, s16* samples, u16 count, const s16* coeffs) { u32 cur_addr = HILO_TO_32(pb.audio_addr.cur_addr); AcceleratorSetup(&pb, &cur_addr); @@ -413,7 +413,7 @@ void GetInputSamples(PB_TYPE& pb, s16* samples, const s16* coeffs) if (coeffs) coeffs += pb.coef_select * 0x200; u32 curr_pos = ResampleAudio([](u32) { return AcceleratorGetSample(); }, - samples, SAMPLES_PER_FRAME, pb.src.last_samples, + samples, count, pb.src.last_samples, pb.src.cur_addr_frac, HILO_TO_32(pb.src.ratio), pb.src_type, coeffs); pb.src.cur_addr_frac = (curr_pos & 0xFFFF); @@ -459,18 +459,18 @@ s16 LowPassFilter(s16* samples, u32 count, s16 yn1, u16 a0, u16 b0) // Process 1ms of audio (for AX GC) or 3ms of audio (for AX Wii) from a PB and // mix it to the output buffers. -void ProcessVoice(PB_TYPE& pb, const AXBuffers& buffers, AXMixControl mctrl, const s16* coeffs) +void ProcessVoice(PB_TYPE& pb, const AXBuffers& buffers, u16 count, AXMixControl mctrl, const s16* coeffs) { // If the voice is not running, nothing to do. if (!pb.running) return; // Read input samples, performing sample rate conversion if needed. - s16 samples[SAMPLES_PER_FRAME]; - GetInputSamples(pb, samples, coeffs); + s16 samples[MAX_SAMPLES_PER_FRAME]; + GetInputSamples(pb, samples, count, coeffs); // Apply a global volume ramp using the volume envelope parameters. - for (u32 i = 0; i < SAMPLES_PER_FRAME; ++i) + for (u32 i = 0; i < count; ++i) { samples[i] = ((s32)samples[i] * pb.vol_env.cur_volume) >> 15; pb.vol_env.cur_volume += pb.vol_env.cur_volume_delta; @@ -478,39 +478,39 @@ void ProcessVoice(PB_TYPE& pb, const AXBuffers& buffers, AXMixControl mctrl, con // Optionally, execute a low pass filter if (pb.lpf.enabled) - pb.lpf.yn1 = LowPassFilter(samples, SAMPLES_PER_FRAME, pb.lpf.yn1, pb.lpf.a0, pb.lpf.b0); + pb.lpf.yn1 = LowPassFilter(samples, count, pb.lpf.yn1, pb.lpf.a0, pb.lpf.b0); // Mix LRS, AUXA and AUXB depending on mixer_control // TODO: Handle DPL2 on AUXB. if (mctrl & MIX_L) - MixAdd(buffers.left, samples, SAMPLES_PER_FRAME, &pb.mixer.left, &pb.dpop.left, mctrl & MIX_L_RAMP); + MixAdd(buffers.left, samples, count, &pb.mixer.left, &pb.dpop.left, mctrl & MIX_L_RAMP); if (mctrl & MIX_R) - MixAdd(buffers.right, samples, SAMPLES_PER_FRAME, &pb.mixer.right, &pb.dpop.right, mctrl & MIX_R_RAMP); + MixAdd(buffers.right, samples, count, &pb.mixer.right, &pb.dpop.right, mctrl & MIX_R_RAMP); if (mctrl & MIX_S) - MixAdd(buffers.surround, samples, SAMPLES_PER_FRAME, &pb.mixer.surround, &pb.dpop.surround, mctrl & MIX_S_RAMP); + MixAdd(buffers.surround, samples, count, &pb.mixer.surround, &pb.dpop.surround, mctrl & MIX_S_RAMP); if (mctrl & MIX_AUXA_L) - MixAdd(buffers.auxA_left, samples, SAMPLES_PER_FRAME, &pb.mixer.auxA_left, &pb.dpop.auxA_left, mctrl & MIX_AUXA_L_RAMP); + MixAdd(buffers.auxA_left, samples, count, &pb.mixer.auxA_left, &pb.dpop.auxA_left, mctrl & MIX_AUXA_L_RAMP); if (mctrl & MIX_AUXA_R) - MixAdd(buffers.auxA_right, samples, SAMPLES_PER_FRAME, &pb.mixer.auxA_right, &pb.dpop.auxA_right, mctrl & MIX_AUXA_R_RAMP); + MixAdd(buffers.auxA_right, samples, count, &pb.mixer.auxA_right, &pb.dpop.auxA_right, mctrl & MIX_AUXA_R_RAMP); if (mctrl & MIX_AUXA_S) - MixAdd(buffers.auxA_surround, samples, SAMPLES_PER_FRAME, &pb.mixer.auxA_surround, &pb.dpop.auxA_surround, mctrl & MIX_AUXA_S_RAMP); + MixAdd(buffers.auxA_surround, samples, count, &pb.mixer.auxA_surround, &pb.dpop.auxA_surround, mctrl & MIX_AUXA_S_RAMP); if (mctrl & MIX_AUXB_L) - MixAdd(buffers.auxB_left, samples, SAMPLES_PER_FRAME, &pb.mixer.auxB_left, &pb.dpop.auxB_left, mctrl & MIX_AUXB_L_RAMP); + MixAdd(buffers.auxB_left, samples, count, &pb.mixer.auxB_left, &pb.dpop.auxB_left, mctrl & MIX_AUXB_L_RAMP); if (mctrl & MIX_AUXB_R) - MixAdd(buffers.auxB_right, samples, SAMPLES_PER_FRAME, &pb.mixer.auxB_right, &pb.dpop.auxB_right, mctrl & MIX_AUXB_R_RAMP); + MixAdd(buffers.auxB_right, samples, count, &pb.mixer.auxB_right, &pb.dpop.auxB_right, mctrl & MIX_AUXB_R_RAMP); if (mctrl & MIX_AUXB_S) - MixAdd(buffers.auxB_surround, samples, SAMPLES_PER_FRAME, &pb.mixer.auxB_surround, &pb.dpop.auxB_surround, mctrl & MIX_AUXB_S_RAMP); + MixAdd(buffers.auxB_surround, samples, count, &pb.mixer.auxB_surround, &pb.dpop.auxB_surround, mctrl & MIX_AUXB_S_RAMP); #ifdef AX_WII if (mctrl & MIX_AUXC_L) - MixAdd(buffers.auxC_left, samples, SAMPLES_PER_FRAME, &pb.mixer.auxC_left, &pb.dpop.auxC_left, mctrl & MIX_AUXC_L_RAMP); + MixAdd(buffers.auxC_left, samples, count, &pb.mixer.auxC_left, &pb.dpop.auxC_left, mctrl & MIX_AUXC_L_RAMP); if (mctrl & MIX_AUXC_R) - MixAdd(buffers.auxC_right, samples, SAMPLES_PER_FRAME, &pb.mixer.auxC_right, &pb.dpop.auxC_right, mctrl & MIX_AUXC_R_RAMP); + MixAdd(buffers.auxC_right, samples, count, &pb.mixer.auxC_right, &pb.dpop.auxC_right, mctrl & MIX_AUXC_R_RAMP); if (mctrl & MIX_AUXC_S) - MixAdd(buffers.auxC_surround, samples, SAMPLES_PER_FRAME, &pb.mixer.auxC_surround, &pb.dpop.auxC_surround, mctrl & MIX_AUXC_S_RAMP); + MixAdd(buffers.auxC_surround, samples, count, &pb.mixer.auxC_surround, &pb.dpop.auxC_surround, mctrl & MIX_AUXC_S_RAMP); #endif // Optionally, phase shift left or right channel to simulate 3D sound. @@ -523,13 +523,16 @@ void ProcessVoice(PB_TYPE& pb, const AXBuffers& buffers, AXMixControl mctrl, con // Wiimote mixing. if (pb.remote) { - // Interpolate 18 samples from the 96 samples we read before. + // Old AXWii versions process ms per ms. + u16 wm_count = count == 96 ? 18 : 6; + + // Interpolate at most 18 samples from the 96 samples we read before. s16 wm_samples[18]; // We use ratio 0x55555 == (5 * 65536 + 21845) / 65536 == 5.3333 which // is the nearest we can get to 96/18 u32 curr_pos = ResampleAudio([&samples](u32 i) { return samples[i]; }, - wm_samples, 18, pb.remote_src.last_samples, + wm_samples, wm_count, pb.remote_src.last_samples, pb.remote_src.cur_addr_frac, 0x55555, SRCTYPE_POLYPHASE, coeffs); pb.remote_src.cur_addr_frac = curr_pos & 0xFFFF; @@ -539,21 +542,21 @@ void ProcessVoice(PB_TYPE& pb, const AXBuffers& buffers, AXMixControl mctrl, con #define WMCHAN_MIX_RAMP(n) ((pb.remote_mixer_control >> (2 * n)) & 2) if (WMCHAN_MIX_ON(0)) - MixAdd(buffers.wm_main0, wm_samples, 18, &pb.remote_mixer.main0, &pb.remote_dpop.main0, WMCHAN_MIX_RAMP(0)); + MixAdd(buffers.wm_main0, wm_samples, wm_count, &pb.remote_mixer.main0, &pb.remote_dpop.main0, WMCHAN_MIX_RAMP(0)); if (WMCHAN_MIX_ON(1)) - MixAdd(buffers.wm_aux0, wm_samples, 18, &pb.remote_mixer.aux0, &pb.remote_dpop.aux0, WMCHAN_MIX_RAMP(1)); + MixAdd(buffers.wm_aux0, wm_samples, wm_count, &pb.remote_mixer.aux0, &pb.remote_dpop.aux0, WMCHAN_MIX_RAMP(1)); if (WMCHAN_MIX_ON(2)) - MixAdd(buffers.wm_main1, wm_samples, 18, &pb.remote_mixer.main1, &pb.remote_dpop.main1, WMCHAN_MIX_RAMP(2)); + MixAdd(buffers.wm_main1, wm_samples, wm_count, &pb.remote_mixer.main1, &pb.remote_dpop.main1, WMCHAN_MIX_RAMP(2)); if (WMCHAN_MIX_ON(3)) - MixAdd(buffers.wm_aux1, wm_samples, 18, &pb.remote_mixer.aux1, &pb.remote_dpop.aux1, WMCHAN_MIX_RAMP(3)); + MixAdd(buffers.wm_aux1, wm_samples, wm_count, &pb.remote_mixer.aux1, &pb.remote_dpop.aux1, WMCHAN_MIX_RAMP(3)); if (WMCHAN_MIX_ON(4)) - MixAdd(buffers.wm_main2, wm_samples, 18, &pb.remote_mixer.main2, &pb.remote_dpop.main2, WMCHAN_MIX_RAMP(4)); + MixAdd(buffers.wm_main2, wm_samples, wm_count, &pb.remote_mixer.main2, &pb.remote_dpop.main2, WMCHAN_MIX_RAMP(4)); if (WMCHAN_MIX_ON(5)) - MixAdd(buffers.wm_aux2, wm_samples, 18, &pb.remote_mixer.aux2, &pb.remote_dpop.aux2, WMCHAN_MIX_RAMP(5)); + MixAdd(buffers.wm_aux2, wm_samples, wm_count, &pb.remote_mixer.aux2, &pb.remote_dpop.aux2, WMCHAN_MIX_RAMP(5)); if (WMCHAN_MIX_ON(6)) - MixAdd(buffers.wm_main3, wm_samples, 18, &pb.remote_mixer.main3, &pb.remote_dpop.main3, WMCHAN_MIX_RAMP(6)); + MixAdd(buffers.wm_main3, wm_samples, wm_count, &pb.remote_mixer.main3, &pb.remote_dpop.main3, WMCHAN_MIX_RAMP(6)); if (WMCHAN_MIX_ON(7)) - MixAdd(buffers.wm_aux3, wm_samples, 18, &pb.remote_mixer.aux3, &pb.remote_dpop.aux3, WMCHAN_MIX_RAMP(7)); + MixAdd(buffers.wm_aux3, wm_samples, wm_count, &pb.remote_mixer.aux3, &pb.remote_dpop.aux3, WMCHAN_MIX_RAMP(7)); } #endif }