Basic framework to support the old AXWii version used in Wii Sports and Excite Truck

This commit is contained in:
Pierre Bourdon 2013-03-30 22:22:57 +01:00
parent 79c0316243
commit 276c457bed
6 changed files with 118 additions and 89 deletions

View File

@ -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

View File

@ -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();

View File

@ -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,

View File

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

View File

@ -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.

View File

@ -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<s16(u32)> input_callback, s16* output, u32 count,
return curr_pos;
}
// Read SAMPLES_PER_FRAME input samples from ARAM, decoding and converting rate
// Read <count> 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
}