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

View File

@ -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];
};

View File

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

View File

@ -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<class ParamBlockType>
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;

View File

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

View File

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