Add AUX mixing support as well as a send&return effect implementation

This commit is contained in:
Pierre Bourdon 2012-11-14 17:55:16 +01:00
parent 61c1fab2c6
commit a630357c9e
6 changed files with 183 additions and 55 deletions

View File

@ -158,6 +158,11 @@ void CUCode_AX::MixAdd(short* _pBuffer, int _iSize)
memset(temprbuffer, 0, _iSize * sizeof(int)); memset(temprbuffer, 0, _iSize * sizeof(int));
AXPB PB; AXPB PB;
AXBuffers buffers = {{
templbuffer,
temprbuffer,
NULL
}};
for (int x = 0; x < numPBaddr; x++) for (int x = 0; x < numPBaddr; x++)
{ {
@ -175,7 +180,7 @@ void CUCode_AX::MixAdd(short* _pBuffer, int _iSize)
if (m_CRC != 0x3389a79e) if (m_CRC != 0x3389a79e)
VoiceHacks(PB); VoiceHacks(PB);
MixAddVoice(PB, templbuffer, temprbuffer, _iSize); MixAddVoice(PB, buffers, _iSize);
if (!WritePB(blockAddr, PB)) if (!WritePB(blockAddr, PB))
break; break;

View File

@ -25,7 +25,16 @@ struct PBMixer
u16 right; u16 right;
u16 right_delta; 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]; u16 unknown4[6];
}; };

View File

@ -101,15 +101,21 @@ void CUCode_AXWii::MixAdd(short* _pBuffer, int _iSize)
if (!blockAddr) if (!blockAddr)
return; return;
AXBuffers buffers = {{
templbuffer,
temprbuffer,
NULL
}};
for (int i = 0; i < NUMBER_OF_PBS; i++) for (int i = 0; i < NUMBER_OF_PBS; i++)
{ {
if (!ReadPB(blockAddr, PB)) if (!ReadPB(blockAddr, PB))
break; break;
if (wiisportsHack) if (wiisportsHack)
MixAddVoice(*(AXPBWiiSports*)&PB, templbuffer, temprbuffer, _iSize); MixAddVoice(*(AXPBWiiSports*)&PB, buffers, _iSize);
else else
MixAddVoice(PB, templbuffer, temprbuffer, _iSize); MixAddVoice(PB, buffers, _iSize);
if (!WritePB(blockAddr, PB)) if (!WritePB(blockAddr, PB))
break; break;

View File

@ -94,12 +94,26 @@ inline bool WritePB(u32 addr, AXPBWii &PB)
return true; 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: fix handling of gc/wii PB differences
// TODO: generally fix up the mess - looks crazy and kinda wrong // TODO: generally fix up the mess - looks crazy and kinda wrong
template<class ParamBlockType> template<class ParamBlockType>
inline void MixAddVoice(ParamBlockType &pb, inline void MixAddVoice(ParamBlockType &pb, const AXBuffers& buffers,
int *templbuffer, int *temprbuffer,
int _iSize, bool resample = true) int _iSize, bool resample = true)
{ {
if (pb.running) if (pb.running)
@ -230,11 +244,25 @@ inline void MixAddVoice(ParamBlockType &pb,
int leftmix = pb.mixer.left >> 5; int leftmix = pb.mixer.left >> 5;
int rightmix = pb.mixer.right >> 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 left = sample * leftmix >> 8;
int right = sample * rightmix >> 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 // adpcm has to walk from oldSamplePos to samplePos here
templbuffer[s] += left; if ((pb.mixer_control & 1) && buffers.left) buffers.left[s] += left;
temprbuffer[s] += right; 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 // Control the behavior when we reach the end of the sample
if (samplePos >= sampleEnd) if (samplePos >= sampleEnd)
@ -265,6 +293,10 @@ inline void MixAddVoice(ParamBlockType &pb,
// Update volume // Update volume
pb.mixer.left = ADPCM_Vol(pb.mixer.left, pb.mixer.left_delta); 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.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.src.cur_addr_frac = (u16)frac;
pb.audio_addr.cur_addr_hi = samplePos >> 16; pb.audio_addr.cur_addr_hi = samplePos >> 16;

View File

@ -80,7 +80,9 @@ void CUCode_NewAX::NotifyAXThread()
void CUCode_NewAX::HandleCommandList() void CUCode_NewAX::HandleCommandList()
{ {
// Temp variables for addresses computation
u16 addr_hi, addr_lo; u16 addr_hi, addr_lo;
u16 addr2_hi, addr2_lo;
u32 pb_addr = 0; 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. // A lot of these commands are unknown, or unused in this AX HLE.
// We still need to skip their arguments using "curr_idx += N". // 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_hi = m_cmdlist[curr_idx++];
addr_lo = m_cmdlist[curr_idx++]; addr_lo = m_cmdlist[curr_idx++];
SetupProcessing(HILO_TO_32(addr)); SetupProcessing(HILO_TO_32(addr));
@ -114,9 +116,22 @@ void CUCode_NewAX::HandleCommandList()
ProcessPBList(pb_addr); ProcessPBList(pb_addr);
break; break;
case CMD_UNK_04: curr_idx += 4; break; case CMD_MIX_AUXA:
case CMD_UNK_05: curr_idx += 4; break; case CMD_MIX_AUXB:
case CMD_UNK_06: curr_idx += 2; break; // 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_SBUFFER_ADDR: curr_idx += 2; break;
case CMD_UNK_08: curr_idx += 10; break; // TODO: check case CMD_UNK_08: curr_idx += 10; break; // TODO: check
case CMD_UNK_09: curr_idx += 2; break; 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_UNK_0D: curr_idx += 2; break;
case CMD_OUTPUT: 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; curr_idx += 2;
addr_hi = m_cmdlist[curr_idx++]; 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) 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 // List of all buffers we have to initialize
s32 left_init = (s32)((studio_data[0] << 16) | studio_data[1]); int* buffers[] = {
s16 left_delta = (s16)studio_data[2]; m_samples_left,
if (!left_init) m_samples_right,
memset(m_samples_left, 0, sizeof (m_samples_left)); m_samples_surround,
else m_samples_auxA_left,
{ m_samples_auxA_right,
for (u32 i = 0; i < 32 * 5; ++i) m_samples_auxA_surround,
{ m_samples_auxB_left,
m_samples_left[i] = left_init; m_samples_auxB_right,
left_init -= left_delta; m_samples_auxB_surround
} };
}
// studio_data[3, 4, 5] are for right samples volume ramping u32 init_idx = 0;
s32 right_init = (s32)((studio_data[0] << 16) | studio_data[1]); for (u32 i = 0; i < sizeof (buffers) / sizeof (buffers[0]); ++i)
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) 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; for (u32 j = 0; j < 32 * 5; ++j)
right_init -= right_delta; {
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 // Samples per millisecond. In theory DSP sampling rate can be changed from
// 32KHz to 48KHz, but AX always process at 32KHz. // 32KHz to 48KHz, but AX always process at 32KHz.
u32 spms = 32; const u32 spms = 32;
AXPB pb; AXPB pb;
while (pb_addr) 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)) if (!ReadPB(pb_addr, pb))
break; break;
@ -292,18 +322,62 @@ void CUCode_NewAX::ProcessPBList(u32 pb_addr)
if (m_CRC != 0x3389a79e) if (m_CRC != 0x3389a79e)
VoiceHacks(pb); VoiceHacks(pb);
MixAddVoice(pb, m_samples_left + spms * curr_ms, MixAddVoice(pb, buffers, spms, false);
m_samples_right + spms * curr_ms, 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); WritePB(pb_addr, pb);
pb_addr = HILO_TO_32(pb.next_pb); pb_addr = HILO_TO_32(pb.next_pb);
} }
}
// Clamp to 16 bits. void CUCode_NewAX::MixAUXSamples(bool AUXA, u32 write_addr, u32 read_addr)
// TODO: check the clamping is done in this command and not in a later {
// command. int buffers[3][5 * 32];
for (u32 i = 0; i < 5 * spms; ++i)
// 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 left = m_samples_left[i];
int right = m_samples_right[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_left[i] = left;
m_samples_right[i] = right; 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) for (u32 i = 0; i < 5 * 32; ++i)
{ {

View File

@ -51,13 +51,13 @@ private:
enum CmdType enum CmdType
{ {
CMD_STUDIO_ADDR = 0x00, CMD_SETUP = 0x00,
CMD_UNK_01 = 0x01, CMD_UNK_01 = 0x01,
CMD_PB_ADDR = 0x02, CMD_PB_ADDR = 0x02,
CMD_PROCESS = 0x03, CMD_PROCESS = 0x03,
CMD_UNK_04 = 0x04, CMD_MIX_AUXA = 0x04,
CMD_UNK_05 = 0x05, CMD_MIX_AUXB = 0x05,
CMD_UNK_06 = 0x06, CMD_MIX_AUXB_NOWRITE = 0x06,
CMD_SBUFFER_ADDR = 0x07, CMD_SBUFFER_ADDR = 0x07,
CMD_UNK_08 = 0x08, CMD_UNK_08 = 0x08,
CMD_UNK_09 = 0x09, CMD_UNK_09 = 0x09,
@ -76,6 +76,13 @@ private:
// 32 * 5 because 32 samples per millisecond, for 5 milliseconds. // 32 * 5 because 32 samples per millisecond, for 5 milliseconds.
int m_samples_left[32 * 5]; int m_samples_left[32 * 5];
int m_samples_right[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 // Volatile because it's set by HandleMail and accessed in
// HandleCommandList, which are running in two different threads. // HandleCommandList, which are running in two different threads.
@ -100,6 +107,7 @@ private:
void HandleCommandList(); void HandleCommandList();
void SetupProcessing(u32 studio_addr); void SetupProcessing(u32 studio_addr);
void ProcessPBList(u32 pb_addr); void ProcessPBList(u32 pb_addr);
void MixAUXSamples(bool AUXA, u32 write_addr, u32 read_addr);
void OutputSamples(u32 out_addr); void OutputSamples(u32 out_addr);
}; };