mirror of
https://github.com/izzy2lost/dolphin.git
synced 2026-06-19 01:16:48 -07:00
Merge branch 'new-ax-hle'
GC and Wii games using the AX UCode should now work almost perfectly with DSP HLE. If you get any issue, make sure the "DSP on dedicated thread" option is disabled, and try setting framelimit to "Audio". As a side effect, DSP HLE should not desync anymore (making it usable in netplay and TAS) with AX games. Conflicts: Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_AX.h Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_AXWii.cpp
This commit is contained in:
@@ -69,8 +69,7 @@ set(SRCS Src/ActionReplay.cpp
|
||||
Src/HW/CPU.cpp
|
||||
Src/HW/DSP.cpp
|
||||
Src/HW/DSPHLE/UCodes/UCode_AX.cpp
|
||||
Src/HW/DSPHLE/UCodes/UCode_AXWii.cpp
|
||||
Src/HW/DSPHLE/UCodes/UCode_NewAXWii.cpp
|
||||
Src/HW/DSPHLE/UCodes/UCode_AXWii.cpp
|
||||
Src/HW/DSPHLE/UCodes/UCode_CARD.cpp
|
||||
Src/HW/DSPHLE/UCodes/UCode_InitAudioSystem.cpp
|
||||
Src/HW/DSPHLE/UCodes/UCode_ROM.cpp
|
||||
|
||||
@@ -261,7 +261,6 @@
|
||||
<ClCompile Include="Src\HW\DSPHLE\UCodes\UCodes.cpp" />
|
||||
<ClCompile Include="Src\HW\DSPHLE\UCodes\UCode_AX.cpp" />
|
||||
<ClCompile Include="Src\HW\DSPHLE\UCodes\UCode_AXWii.cpp" />
|
||||
<ClCompile Include="Src\HW\DSPHLE\UCodes\UCode_NewAXWii.cpp" />
|
||||
<ClCompile Include="Src\HW\DSPHLE\UCodes\UCode_CARD.cpp" />
|
||||
<ClCompile Include="Src\HW\DSPHLE\UCodes\UCode_GBA.cpp" />
|
||||
<ClCompile Include="Src\HW\DSPHLE\UCodes\UCode_InitAudioSystem.cpp" />
|
||||
@@ -465,11 +464,8 @@
|
||||
<ClInclude Include="Src\HW\DSPHLE\MailHandler.h" />
|
||||
<ClInclude Include="Src\HW\DSPHLE\UCodes\UCodes.h" />
|
||||
<ClInclude Include="Src\HW\DSPHLE\UCodes\UCode_AX.h" />
|
||||
<ClInclude Include="Src\HW\DSPHLE\UCodes\UCode_AX_Structs.h" />
|
||||
<ClInclude Include="Src\HW\DSPHLE\UCodes\UCode_AXStructs.h" />
|
||||
<ClInclude Include="Src\HW\DSPHLE\UCodes\UCode_AXWii.h" />
|
||||
<ClInclude Include="Src\HW\DSPHLE\UCodes\UCode_AXWii_Structs.h" />
|
||||
<ClInclude Include="Src\HW\DSPHLE\UCodes\UCode_AXWii_Voice.h" />
|
||||
<ClInclude Include="Src\HW\DSPHLE\UCodes\UCode_NewAXWii.h" />
|
||||
<ClInclude Include="Src\HW\DSPHLE\UCodes\UCode_AX_Voice.h" />
|
||||
<ClInclude Include="Src\HW\DSPHLE\UCodes\UCode_CARD.h" />
|
||||
<ClInclude Include="Src\HW\DSPHLE\UCodes\UCode_GBA.h" />
|
||||
@@ -602,4 +598,4 @@
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
<ImportGroup Label="ExtensionTargets">
|
||||
</ImportGroup>
|
||||
</Project>
|
||||
</Project>
|
||||
|
||||
@@ -196,9 +196,6 @@
|
||||
<ClCompile Include="Src\HW\DSPHLE\UCodes\UCode_AXWii.cpp">
|
||||
<Filter>HW %28Flipper/Hollywood%29\DSP Interface + HLE\HLE\uCodes</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="Src\HW\DSPHLE\UCodes\UCode_NewAXWii.cpp">
|
||||
<Filter>HW %28Flipper/Hollywood%29\DSP Interface + HLE\HLE\uCodes</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="Src\HW\DSPHLE\UCodes\UCode_CARD.cpp">
|
||||
<Filter>HW %28Flipper/Hollywood%29\DSP Interface + HLE\HLE\uCodes</Filter>
|
||||
</ClCompile>
|
||||
@@ -738,21 +735,12 @@
|
||||
<ClInclude Include="Src\HW\DSPHLE\UCodes\UCode_AX_Voice.h">
|
||||
<Filter>HW %28Flipper/Hollywood%29\DSP Interface + HLE\HLE\uCodes</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Src\HW\DSPHLE\UCodes\UCode_AX_Structs.h">
|
||||
<ClInclude Include="Src\HW\DSPHLE\UCodes\UCode_AXStructs.h">
|
||||
<Filter>HW %28Flipper/Hollywood%29\DSP Interface + HLE\HLE\uCodes</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Src\HW\DSPHLE\UCodes\UCode_AXWii.h">
|
||||
<Filter>HW %28Flipper/Hollywood%29\DSP Interface + HLE\HLE\uCodes</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Src\HW\DSPHLE\UCodes\UCode_AXWii_Structs.h">
|
||||
<Filter>HW %28Flipper/Hollywood%29\DSP Interface + HLE\HLE\uCodes</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Src\HW\DSPHLE\UCodes\UCode_AXWii_Voice.h">
|
||||
<Filter>HW %28Flipper/Hollywood%29\DSP Interface + HLE\HLE\uCodes</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Src\HW\DSPHLE\UCodes\UCode_NewAXWii.h">
|
||||
<Filter>HW %28Flipper/Hollywood%29\DSP Interface + HLE\HLE\uCodes</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Src\HW\DSPHLE\UCodes\UCode_CARD.h">
|
||||
<Filter>HW %28Flipper/Hollywood%29\DSP Interface + HLE\HLE\uCodes</Filter>
|
||||
</ClInclude>
|
||||
|
||||
@@ -273,7 +273,7 @@ u16 DSPHLE::DSP_WriteControlRegister(unsigned short _Value)
|
||||
UDSPControl Temp(_Value);
|
||||
if (!m_InitMixer)
|
||||
{
|
||||
if (!Temp.DSPHalt && Temp.DSPInit)
|
||||
if (!Temp.DSPHalt)
|
||||
{
|
||||
InitMixer();
|
||||
}
|
||||
|
||||
@@ -17,29 +17,78 @@
|
||||
|
||||
#include "UCode_AX.h"
|
||||
#include "../../DSP.h"
|
||||
#include "FileUtil.h"
|
||||
#include "ConfigManager.h"
|
||||
|
||||
#define AX_GC
|
||||
#include "UCode_AX_Voice.h"
|
||||
|
||||
CUCode_AX::CUCode_AX(DSPHLE* dsp_hle, u32 crc)
|
||||
: IUCode(dsp_hle, crc)
|
||||
, m_work_available(false)
|
||||
, m_cmdlist_size(0)
|
||||
, m_axthread(&SpawnAXThread, this)
|
||||
, m_run_on_thread(SConfig::GetInstance().m_LocalCoreStartupParameter.bDSPThread)
|
||||
{
|
||||
WARN_LOG(DSPHLE, "Instantiating CUCode_AX: crc=%08x", crc);
|
||||
m_rMailHandler.PushMail(DSP_INIT);
|
||||
DSP::GenerateDSPInterruptFromDSPEmu(DSP::INT_DSP);
|
||||
|
||||
LoadResamplingCoefficients();
|
||||
|
||||
if (m_run_on_thread)
|
||||
m_axthread = std::thread(SpawnAXThread, this);
|
||||
}
|
||||
|
||||
CUCode_AX::~CUCode_AX()
|
||||
{
|
||||
m_cmdlist_size = (u16)-1; // Special value to signal end
|
||||
NotifyAXThread();
|
||||
m_axthread.join();
|
||||
if (m_run_on_thread)
|
||||
{
|
||||
m_cmdlist_size = (u16)-1; // Special value to signal end
|
||||
NotifyAXThread();
|
||||
m_axthread.join();
|
||||
}
|
||||
|
||||
m_rMailHandler.Clear();
|
||||
}
|
||||
|
||||
void CUCode_AX::LoadResamplingCoefficients()
|
||||
{
|
||||
m_coeffs_available = false;
|
||||
|
||||
std::string filenames[] = {
|
||||
File::GetUserPath(D_GCUSER_IDX) + "dsp_coef.bin",
|
||||
File::GetSysDirectory() + "/GC/dsp_coef.bin"
|
||||
};
|
||||
|
||||
size_t fidx;
|
||||
std::string filename;
|
||||
for (fidx = 0; fidx < sizeof (filenames) / sizeof (filenames[0]); ++fidx)
|
||||
{
|
||||
filename = filenames[fidx];
|
||||
if (!File::Exists(filename))
|
||||
continue;
|
||||
|
||||
if (File::GetSize(filename) != 0x1000)
|
||||
continue;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
if (fidx >= sizeof (filenames) / sizeof (filenames[0]))
|
||||
return;
|
||||
|
||||
WARN_LOG(DSPHLE, "Loading polyphase resampling coeffs from %s", filename.c_str());
|
||||
|
||||
FILE* fp = fopen(filename.c_str(), "rb");
|
||||
fread(m_coeffs, 1, 0x1000, fp);
|
||||
fclose(fp);
|
||||
|
||||
for (u32 i = 0; i < 0x800; ++i)
|
||||
m_coeffs[i] = Common::swap16(m_coeffs[i]);
|
||||
|
||||
m_coeffs_available = true;
|
||||
}
|
||||
|
||||
void CUCode_AX::SpawnAXThread(CUCode_AX* self)
|
||||
{
|
||||
self->AXThread();
|
||||
@@ -61,20 +110,32 @@ void CUCode_AX::AXThread()
|
||||
m_processing.lock();
|
||||
HandleCommandList();
|
||||
m_cmdlist_size = 0;
|
||||
|
||||
// Signal end of processing
|
||||
m_rMailHandler.PushMail(DSP_YIELD);
|
||||
DSP::GenerateDSPInterruptFromDSPEmu(DSP::INT_DSP);
|
||||
SignalWorkEnd();
|
||||
m_processing.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
void CUCode_AX::SignalWorkEnd()
|
||||
{
|
||||
// Signal end of processing
|
||||
m_rMailHandler.PushMail(DSP_YIELD);
|
||||
DSP::GenerateDSPInterruptFromDSPEmu(DSP::INT_DSP);
|
||||
}
|
||||
|
||||
void CUCode_AX::NotifyAXThread()
|
||||
{
|
||||
std::unique_lock<std::mutex> lk(m_cmdlist_mutex);
|
||||
m_cmdlist_cv.notify_one();
|
||||
}
|
||||
|
||||
void CUCode_AX::StartWorking()
|
||||
{
|
||||
if (m_run_on_thread)
|
||||
NotifyAXThread();
|
||||
else
|
||||
m_work_available = true;
|
||||
}
|
||||
|
||||
void CUCode_AX::HandleCommandList()
|
||||
{
|
||||
// Temp variables for addresses computation
|
||||
@@ -200,11 +261,7 @@ void CUCode_AX::HandleCommandList()
|
||||
u16 idx = m_cmdlist[curr_idx++];
|
||||
addr_hi = m_cmdlist[curr_idx++];
|
||||
addr_lo = m_cmdlist[curr_idx++];
|
||||
|
||||
// TODO
|
||||
(void)samp_val;
|
||||
(void)idx;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -250,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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -405,11 +461,15 @@ 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);
|
||||
|
||||
Process1ms(pb, buffers, ConvertMixerControl(pb.mixer_control));
|
||||
ProcessVoice(pb, buffers, spms, ConvertMixerControl(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)
|
||||
@@ -599,6 +659,7 @@ void CUCode_AX::HandleMail(u32 mail)
|
||||
if (next_is_cmdlist)
|
||||
{
|
||||
CopyCmdList(mail, cmdlist_size);
|
||||
StartWorking();
|
||||
NotifyAXThread();
|
||||
}
|
||||
else if (m_UploadSetupInProgress)
|
||||
@@ -667,6 +728,12 @@ void CUCode_AX::Update(int cycles)
|
||||
m_rMailHandler.PushMail(DSP_RESUME);
|
||||
DSP::GenerateDSPInterruptFromDSPEmu(DSP::INT_DSP);
|
||||
}
|
||||
else if (m_work_available)
|
||||
{
|
||||
HandleCommandList();
|
||||
m_cmdlist_size = 0;
|
||||
SignalWorkEnd();
|
||||
}
|
||||
}
|
||||
|
||||
void CUCode_AX::DoAXState(PointerWrap& p)
|
||||
|
||||
@@ -27,7 +27,7 @@
|
||||
#define _UCODE_AX_H
|
||||
|
||||
#include "UCodes.h"
|
||||
#include "UCode_AX_Structs.h"
|
||||
#include "UCode_AXStructs.h"
|
||||
|
||||
// We can't directly use the mixer_control field from the PB because it does
|
||||
// not mean the same in all AX versions. The AX UCode converts the
|
||||
@@ -102,11 +102,16 @@ protected:
|
||||
int m_samples_auxB_right[32 * 5];
|
||||
int m_samples_auxB_surround[32 * 5];
|
||||
|
||||
// This flag is set if there is anything to process.
|
||||
bool m_work_available;
|
||||
|
||||
// Volatile because it's set by HandleMail and accessed in
|
||||
// HandleCommandList, which are running in two different threads.
|
||||
volatile u16 m_cmdlist[512];
|
||||
volatile u32 m_cmdlist_size;
|
||||
|
||||
bool m_run_on_thread;
|
||||
|
||||
// Sync objects
|
||||
std::mutex m_processing;
|
||||
std::condition_variable m_cmdlist_cv;
|
||||
@@ -114,6 +119,14 @@ protected:
|
||||
|
||||
std::thread m_axthread;
|
||||
|
||||
// Table of coefficients for polyphase sample rate conversion.
|
||||
// The coefficients aren't always available (they are part of the DSP DROM)
|
||||
// so we also need to know if they are valid or not.
|
||||
bool m_coeffs_available;
|
||||
s16 m_coeffs[0x800];
|
||||
|
||||
void LoadResamplingCoefficients();
|
||||
|
||||
// Copy a command list from memory to our temp buffer
|
||||
void CopyCmdList(u32 addr, u16 size);
|
||||
|
||||
@@ -122,13 +135,21 @@ protected:
|
||||
// versions of AX.
|
||||
AXMixControl ConvertMixerControl(u32 mixer_control);
|
||||
|
||||
// Send a notification to the AX thread to tell him a new cmdlist addr is
|
||||
// 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();
|
||||
|
||||
// Send a notification to the AX thread to tell it a new cmdlist addr is
|
||||
// available for processing.
|
||||
void NotifyAXThread();
|
||||
|
||||
void AXThread();
|
||||
|
||||
virtual void HandleCommandList();
|
||||
void SignalWorkEnd();
|
||||
|
||||
void SetupProcessing(u32 init_addr);
|
||||
void DownloadAndMixWithVolume(u32 addr, u16 vol_main, u16 vol_auxa, u16 vol_auxb);
|
||||
|
||||
+12
-58
@@ -161,15 +161,15 @@ struct PBDpopWii
|
||||
|
||||
struct PBDpopWM
|
||||
{
|
||||
s16 aMain0;
|
||||
s16 aMain1;
|
||||
s16 aMain2;
|
||||
s16 aMain3;
|
||||
s16 main0;
|
||||
s16 main1;
|
||||
s16 main2;
|
||||
s16 main3;
|
||||
|
||||
s16 aAux0;
|
||||
s16 aAux1;
|
||||
s16 aAux2;
|
||||
s16 aAux3;
|
||||
s16 aux0;
|
||||
s16 aux1;
|
||||
s16 aux2;
|
||||
s16 aux3;
|
||||
};
|
||||
|
||||
struct PBVolumeEnvelope
|
||||
@@ -211,13 +211,13 @@ struct PBSampleRateConverter
|
||||
u16 ratio_hi; // integer part of sampling ratio
|
||||
u16 ratio_lo; // fraction part of sampling ratio
|
||||
u16 cur_addr_frac;
|
||||
u16 last_samples[4];
|
||||
s16 last_samples[4];
|
||||
};
|
||||
|
||||
struct PBSampleRateConverterWM
|
||||
{
|
||||
u16 currentAddressFrac;
|
||||
u16 last_samples[4];
|
||||
u16 cur_addr_frac;
|
||||
s16 last_samples[4];
|
||||
};
|
||||
|
||||
struct PBADPCMLoopInfo
|
||||
@@ -230,7 +230,7 @@ struct PBADPCMLoopInfo
|
||||
struct PBLowPassFilter
|
||||
{
|
||||
u16 enabled;
|
||||
u16 yn1;
|
||||
s16 yn1;
|
||||
u16 a0;
|
||||
u16 b0;
|
||||
};
|
||||
@@ -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,
|
||||
File diff suppressed because it is too large
Load Diff
@@ -12,44 +12,116 @@
|
||||
// A copy of the GPL 2.0 should have been included with the program.
|
||||
// If not, see http://www.gnu.org/licenses/
|
||||
|
||||
// Official SVN repository and contact information can be found at
|
||||
// Official Git repository and contact information can be found at
|
||||
// http://code.google.com/p/dolphin-emu/
|
||||
|
||||
#ifndef _UCODE_AXWII
|
||||
#define _UCODE_AXWII
|
||||
#ifndef _UCODE_AXWII_H
|
||||
#define _UCODE_AXWII_H
|
||||
|
||||
#include "UCode_AXWii_Structs.h"
|
||||
#include "UCode_AX.h"
|
||||
|
||||
#define NUMBER_OF_PBS 128
|
||||
|
||||
class CUCode_AXWii : public IUCode
|
||||
class CUCode_AXWii : public CUCode_AX
|
||||
{
|
||||
public:
|
||||
CUCode_AXWii(DSPHLE *dsp_hle, u32 _CRC);
|
||||
virtual ~CUCode_AXWii();
|
||||
|
||||
void HandleMail(u32 _uMail);
|
||||
void MixAdd(short* _pBuffer, int _iSize);
|
||||
void Update(int cycles);
|
||||
void DoState(PointerWrap &p);
|
||||
virtual void DoState(PointerWrap &p);
|
||||
|
||||
protected:
|
||||
// Additional AUX buffers
|
||||
int m_samples_auxC_left[32 * 3];
|
||||
int m_samples_auxC_right[32 * 3];
|
||||
int m_samples_auxC_surround[32 * 3];
|
||||
|
||||
// Wiimote buffers
|
||||
int m_samples_wm0[6 * 3];
|
||||
int m_samples_aux0[6 * 3];
|
||||
int m_samples_wm1[6 * 3];
|
||||
int m_samples_aux1[6 * 3];
|
||||
int m_samples_wm2[6 * 3];
|
||||
int m_samples_aux2[6 * 3];
|
||||
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. We need to
|
||||
// reinject them afterwards so that the correct PB typs is written to RAM.
|
||||
bool ExtractUpdatesFields(AXPBWii& pb, u16* num_updates, u16* updates,
|
||||
u32* updates_addr);
|
||||
void ReinjectUpdatesFields(AXPBWii& pb, u16* num_updates, u32 updates_addr);
|
||||
|
||||
// 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.
|
||||
AXMixControl ConvertMixerControl(u32 mixer_control);
|
||||
|
||||
// Generate a volume ramp from vol1 to vol2, interpolating n volume values.
|
||||
// Uses floating point arithmetic, which isn't exactly what the UCode does,
|
||||
// but this gives better precision and nicer code.
|
||||
void GenerateVolumeRamp(u16* output, u16 vol1, u16 vol2, size_t nvals);
|
||||
|
||||
virtual void HandleCommandList();
|
||||
|
||||
void SetupProcessing(u32 init_addr);
|
||||
void AddToLR(u32 val_addr, bool neg);
|
||||
void AddSubToLR(u32 val_addr);
|
||||
void ProcessPBList(u32 pb_addr);
|
||||
void MixAUXSamples(int aux_id, u32 write_addr, u32 read_addr, u16 volume);
|
||||
void UploadAUXMixLRSC(int aux_id, u32* addresses, u16 volume);
|
||||
void OutputSamples(u32 lr_addr, u32 surround_addr, u16 volume,
|
||||
bool upload_auxc);
|
||||
void OutputWMSamples(u32* addresses); // 4 addresses
|
||||
|
||||
private:
|
||||
enum
|
||||
enum CmdType
|
||||
{
|
||||
MAIL_AX_ALIST = 0xBABE0000,
|
||||
CMD_SETUP = 0x00,
|
||||
CMD_ADD_TO_LR = 0x01,
|
||||
CMD_SUB_TO_LR = 0x02,
|
||||
CMD_ADD_SUB_TO_LR = 0x03,
|
||||
CMD_PROCESS = 0x04,
|
||||
CMD_MIX_AUXA = 0x05,
|
||||
CMD_MIX_AUXB = 0x06,
|
||||
CMD_MIX_AUXC = 0x07,
|
||||
CMD_UPL_AUXA_MIX_LRSC = 0x08,
|
||||
CMD_UPL_AUXB_MIX_LRSC = 0x09,
|
||||
CMD_UNK_0A = 0x0A,
|
||||
CMD_OUTPUT = 0x0B,
|
||||
CMD_OUTPUT_DPL2 = 0x0C,
|
||||
CMD_WM_OUTPUT = 0x0D,
|
||||
CMD_END = 0x0E,
|
||||
};
|
||||
|
||||
// PBs
|
||||
u32 m_addressPBs;
|
||||
|
||||
bool wiisportsHack;
|
||||
|
||||
int *templbuffer;
|
||||
int *temprbuffer;
|
||||
|
||||
// ax task message handler
|
||||
bool AXTask(u32& _uMail);
|
||||
void SendMail(u32 _uMail);
|
||||
// A lot of these are similar to the new version, but there is an offset in
|
||||
// the command ids due to the PB_ADDR command (which was removed from the
|
||||
// new AXWii).
|
||||
enum CmdTypeOld
|
||||
{
|
||||
CMD_SETUP_OLD = 0x00,
|
||||
CMD_ADD_TO_LR_OLD = 0x01,
|
||||
CMD_SUB_TO_LR_OLD = 0x02,
|
||||
CMD_ADD_SUB_TO_LR_OLD = 0x03,
|
||||
CMD_PB_ADDR_OLD = 0x04,
|
||||
CMD_PROCESS_OLD = 0x05,
|
||||
CMD_MIX_AUXA_OLD = 0x06,
|
||||
CMD_MIX_AUXB_OLD = 0x07,
|
||||
CMD_MIX_AUXC_OLD = 0x08,
|
||||
CMD_UPL_AUXA_MIX_LRSC_OLD = 0x09,
|
||||
CMD_UPL_AUXB_MIX_LRSC_OLD = 0x0a,
|
||||
CMD_UNK_0B_OLD = 0x0B,
|
||||
CMD_OUTPUT_OLD = 0x0C, // no volume!
|
||||
CMD_OUTPUT_DPL2_OLD = 0x0D,
|
||||
CMD_WM_OUTPUT_OLD = 0x0E,
|
||||
CMD_END_OLD = 0x0F
|
||||
};
|
||||
};
|
||||
|
||||
#endif // _UCODE_AXWII
|
||||
|
||||
@@ -1,92 +0,0 @@
|
||||
// Copyright (C) 2003 Dolphin Project.
|
||||
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, version 2.0.
|
||||
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License 2.0 for more details.
|
||||
|
||||
// A copy of the GPL 2.0 should have been included with the program.
|
||||
// If not, see http://www.gnu.org/licenses/
|
||||
|
||||
// Official SVN repository and contact information can be found at
|
||||
// http://code.google.com/p/dolphin-emu/
|
||||
|
||||
#ifndef _UCODE_AX_ADPCM_H
|
||||
#define _UCODE_AX_ADPCM_H
|
||||
|
||||
#include "../../DSP.h"
|
||||
|
||||
static inline s16 ADPCM_Step(PBADPCMInfo &adpcm, u32& samplePos, u32 newSamplePos, u16 frac)
|
||||
{
|
||||
while (samplePos < newSamplePos)
|
||||
{
|
||||
if ((samplePos & 15) == 0)
|
||||
{
|
||||
adpcm.pred_scale = DSP::ReadARAM((samplePos & ~15) >> 1);
|
||||
samplePos += 2;
|
||||
newSamplePos += 2;
|
||||
}
|
||||
|
||||
int scale = 1 << (adpcm.pred_scale & 0xF);
|
||||
int coef_idx = (adpcm.pred_scale >> 4) & 7;
|
||||
|
||||
s32 coef1 = adpcm.coefs[coef_idx * 2 + 0];
|
||||
s32 coef2 = adpcm.coefs[coef_idx * 2 + 1];
|
||||
|
||||
int temp = (samplePos & 1) ?
|
||||
(DSP::ReadARAM(samplePos >> 1) & 0xF) :
|
||||
(DSP::ReadARAM(samplePos >> 1) >> 4);
|
||||
|
||||
if (temp >= 8)
|
||||
temp -= 16;
|
||||
|
||||
// 0x400 = 0.5 in 11-bit fixed point
|
||||
int val = (scale * temp) + ((0x400 + coef1 * adpcm.yn1 + coef2 * adpcm.yn2) >> 11);
|
||||
|
||||
if (val > 0x7FFF)
|
||||
val = 0x7FFF;
|
||||
else if (val < -0x7FFF)
|
||||
val = -0x7FFF;
|
||||
|
||||
adpcm.yn2 = adpcm.yn1;
|
||||
adpcm.yn1 = val;
|
||||
|
||||
samplePos++;
|
||||
}
|
||||
|
||||
return adpcm.yn1;
|
||||
}
|
||||
|
||||
// TODO: WTF is going on here?!?
|
||||
// Volume control (ramping)
|
||||
static inline u16 ADPCM_Vol(u16 vol, u16 delta)
|
||||
{
|
||||
int x = vol;
|
||||
if (delta && delta < 0x5000)
|
||||
x += delta * 20 * 8; // unsure what the right step is
|
||||
//x += 1 * 20 * 8;
|
||||
else if (delta && delta > 0x5000)
|
||||
//x -= (0x10000 - delta); // this is to small, it's often 1
|
||||
x -= (0x10000 - delta) * 20 * 16; // if this was 20 * 8 the sounds in Fire Emblem and Paper Mario
|
||||
// did not have time to go to zero before the were closed
|
||||
//x -= 1 * 20 * 16;
|
||||
|
||||
// make lower limits
|
||||
if (x < 0) x = 0;
|
||||
//if (pb.mixer_control < 1000 && x < pb.mixer_control) x = pb.mixer_control; // does this make
|
||||
// any sense?
|
||||
|
||||
// make upper limits
|
||||
//if (mixer_control > 1000 && x > mixer_control) x = mixer_control; // maybe mixer_control also
|
||||
// has a volume target?
|
||||
//if (x >= 0x7fff) x = 0x7fff; // this seems a little high
|
||||
//if (x >= 0x4e20) x = 0x4e20; // add a definitive limit at 20 000
|
||||
if (x >= 0x8000) x = 0x8000; // clamp to 32768;
|
||||
return x; // update volume
|
||||
}
|
||||
|
||||
#endif // _UCODE_AX_ADPCM_H
|
||||
@@ -1,365 +0,0 @@
|
||||
// Copyright (C) 2003 Dolphin Project.
|
||||
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, version 2.0.
|
||||
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License 2.0 for more details.
|
||||
|
||||
// A copy of the GPL 2.0 should have been included with the program.
|
||||
// If not, see http://www.gnu.org/licenses/
|
||||
|
||||
// Official SVN repository and contact information can be found at
|
||||
// http://code.google.com/p/dolphin-emu/
|
||||
|
||||
#ifndef _UCODE_AX_STRUCTS_H
|
||||
#define _UCODE_AX_STRUCTS_H
|
||||
|
||||
struct PBMixer
|
||||
{
|
||||
u16 left;
|
||||
u16 left_delta;
|
||||
u16 right;
|
||||
u16 right_delta;
|
||||
|
||||
u16 unknown3[8];
|
||||
u16 unknown4[6];
|
||||
};
|
||||
|
||||
struct PBMixerWii
|
||||
{
|
||||
// volume mixing values in .15, 0x8000 = ca. 1.0
|
||||
u16 left;
|
||||
u16 left_delta;
|
||||
u16 right;
|
||||
u16 right_delta;
|
||||
|
||||
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;
|
||||
|
||||
// Note: the following elements usage changes a little in DPL2 mode
|
||||
// TODO: implement and comment it in the mixer
|
||||
u16 auxC_left;
|
||||
u16 auxC_left_delta;
|
||||
u16 auxC_right;
|
||||
u16 auxC_right_delta;
|
||||
|
||||
u16 surround;
|
||||
u16 surround_delta;
|
||||
u16 auxA_surround;
|
||||
u16 auxA_surround_delta;
|
||||
u16 auxB_surround;
|
||||
u16 auxB_surround_delta;
|
||||
u16 auxC_surround;
|
||||
u16 auxC_surround_delta;
|
||||
};
|
||||
|
||||
struct PBMixerWM
|
||||
{
|
||||
u16 main0;
|
||||
u16 main0_delta;
|
||||
u16 aux0;
|
||||
u16 aux0_delta;
|
||||
|
||||
u16 main1;
|
||||
u16 main1_delta;
|
||||
u16 aux1;
|
||||
u16 aux1_delta;
|
||||
|
||||
u16 main2;
|
||||
u16 main2_delta;
|
||||
u16 aux2;
|
||||
u16 aux2_delta;
|
||||
|
||||
u16 main3;
|
||||
u16 main3_delta;
|
||||
u16 aux3;
|
||||
u16 aux3_delta;
|
||||
};
|
||||
|
||||
struct PBInitialTimeDelay
|
||||
{
|
||||
u16 on;
|
||||
u16 addrMemHigh;
|
||||
u16 addrMemLow;
|
||||
u16 offsetLeft;
|
||||
u16 offsetRight;
|
||||
u16 targetLeft;
|
||||
u16 targetRight;
|
||||
};
|
||||
|
||||
// Update data - read these each 1ms subframe and use them!
|
||||
// It seems that to provide higher time precisions for MIDI events, some games
|
||||
// use this thing to update the parameter blocks per 1ms sub-block (a block is 5ms).
|
||||
// Using this data should fix games that are missing MIDI notes.
|
||||
struct PBUpdates
|
||||
{
|
||||
u16 num_updates[5];
|
||||
u16 data_hi; // These point to main RAM. Not sure about the structure of the data.
|
||||
u16 data_lo;
|
||||
};
|
||||
|
||||
// The DSP stores the final sample values for each voice after every frame of processing.
|
||||
// The values are then accumulated for all dropped voices, added to the next frame of audio,
|
||||
// and ramped down on a per-sample basis to provide a gentle "roll off."
|
||||
struct PBDpop
|
||||
{
|
||||
s16 unknown[9];
|
||||
};
|
||||
|
||||
struct PBDpopWii
|
||||
{
|
||||
s16 left;
|
||||
s16 auxA_left;
|
||||
s16 auxB_left;
|
||||
s16 auxC_left;
|
||||
|
||||
s16 right;
|
||||
s16 auxA_right;
|
||||
s16 auxB_right;
|
||||
s16 auxC_right;
|
||||
|
||||
s16 surround;
|
||||
s16 auxA_surround;
|
||||
s16 auxB_surround;
|
||||
s16 auxC_surround;
|
||||
};
|
||||
|
||||
struct PBDpopWM
|
||||
{
|
||||
s16 aMain0;
|
||||
s16 aMain1;
|
||||
s16 aMain2;
|
||||
s16 aMain3;
|
||||
|
||||
s16 aAux0;
|
||||
s16 aAux1;
|
||||
s16 aAux2;
|
||||
s16 aAux3;
|
||||
};
|
||||
|
||||
struct PBVolumeEnvelope
|
||||
{
|
||||
u16 cur_volume; // volume at start of frame
|
||||
s16 cur_volume_delta; // signed per sample delta (96 samples per frame)
|
||||
};
|
||||
|
||||
struct PBUnknown2
|
||||
{
|
||||
u16 unknown_reserved[3];
|
||||
};
|
||||
|
||||
struct PBAudioAddr
|
||||
{
|
||||
u16 looping;
|
||||
u16 sample_format;
|
||||
u16 loop_addr_hi; // Start of loop (this will point to a shared "zero" buffer if one-shot mode is active)
|
||||
u16 loop_addr_lo;
|
||||
u16 end_addr_hi; // End of sample (and loop), inclusive
|
||||
u16 end_addr_lo;
|
||||
u16 cur_addr_hi;
|
||||
u16 cur_addr_lo;
|
||||
};
|
||||
|
||||
struct PBADPCMInfo
|
||||
{
|
||||
s16 coefs[16];
|
||||
u16 gain;
|
||||
u16 pred_scale;
|
||||
s16 yn1;
|
||||
s16 yn2;
|
||||
};
|
||||
|
||||
struct PBSampleRateConverter
|
||||
{
|
||||
// ratio = (f32)ratio * 0x10000;
|
||||
// valid range is 1/512 to 4.0000
|
||||
u16 ratio_hi; // integer part of sampling ratio
|
||||
u16 ratio_lo; // fraction part of sampling ratio
|
||||
u16 cur_addr_frac;
|
||||
u16 last_samples[4];
|
||||
};
|
||||
|
||||
struct PBSampleRateConverterWM
|
||||
{
|
||||
u16 currentAddressFrac;
|
||||
u16 last_samples[4];
|
||||
};
|
||||
|
||||
struct PBADPCMLoopInfo
|
||||
{
|
||||
u16 pred_scale;
|
||||
u16 yn1;
|
||||
u16 yn2;
|
||||
};
|
||||
|
||||
struct AXPB
|
||||
{
|
||||
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, ?, linear)
|
||||
u16 coef_select;
|
||||
u16 mixer_control;
|
||||
|
||||
u16 running; // 1=RUN 0=STOP
|
||||
u16 is_stream; // 1 = stream, 0 = one shot
|
||||
|
||||
PBMixer mixer;
|
||||
PBInitialTimeDelay initial_time_delay;
|
||||
PBUpdates updates;
|
||||
PBDpop dpop;
|
||||
PBVolumeEnvelope vol_env;
|
||||
PBUnknown2 unknown3;
|
||||
PBAudioAddr audio_addr;
|
||||
PBADPCMInfo adpcm;
|
||||
PBSampleRateConverter src;
|
||||
PBADPCMLoopInfo adpcm_loop_info;
|
||||
u16 unknown_maybe_padding[3];
|
||||
};
|
||||
|
||||
struct PBLowPassFilter
|
||||
{
|
||||
u16 enabled;
|
||||
u16 yn1;
|
||||
u16 a0;
|
||||
u16 b0;
|
||||
};
|
||||
|
||||
struct PBBiquadFilter
|
||||
{
|
||||
|
||||
u16 on; // on = 2, off = 0
|
||||
u16 xn1; // History data
|
||||
u16 xn2;
|
||||
u16 yn1;
|
||||
u16 yn2;
|
||||
u16 b0; // Filter coefficients
|
||||
u16 b1;
|
||||
u16 b2;
|
||||
u16 a1;
|
||||
u16 a2;
|
||||
|
||||
};
|
||||
|
||||
union PBInfImpulseResponseWM
|
||||
{
|
||||
PBLowPassFilter lpf;
|
||||
PBBiquadFilter biquad;
|
||||
};
|
||||
|
||||
struct AXPBWii
|
||||
{
|
||||
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;
|
||||
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[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,
|
||||
AUDIOFORMAT_PCM8 = 0x19,
|
||||
AUDIOFORMAT_PCM16 = 0xA,
|
||||
};
|
||||
|
||||
enum {
|
||||
SRCTYPE_LINEAR = 1,
|
||||
SRCTYPE_NEAREST = 2,
|
||||
MIXCONTROL_RAMPING = 8,
|
||||
};
|
||||
|
||||
// Both may be used at once
|
||||
enum {
|
||||
FILTER_LOWPASS = 1,
|
||||
FILTER_BIQUAD = 2,
|
||||
};
|
||||
|
||||
#endif // _UCODE_AX_STRUCTS_H
|
||||
@@ -1,271 +0,0 @@
|
||||
// Copyright (C) 2003 Dolphin Project.
|
||||
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, version 2.0.
|
||||
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License 2.0 for more details.
|
||||
|
||||
// A copy of the GPL 2.0 should have been included with the program.
|
||||
// If not, see http://www.gnu.org/licenses/
|
||||
|
||||
// Official SVN repository and contact information can be found at
|
||||
// http://code.google.com/p/dolphin-emu/
|
||||
|
||||
#ifndef _UCODE_AXWII_VOICE_H
|
||||
#define _UCODE_AXWII_VOICE_H
|
||||
|
||||
#include "UCodes.h"
|
||||
#include "UCode_AXWii_ADPCM.h"
|
||||
#include "UCode_AX.h"
|
||||
#include "Mixer.h"
|
||||
#include "../../AudioInterface.h"
|
||||
|
||||
// MRAM -> ARAM for GC
|
||||
inline bool ReadPB(u32 addr, AXPB &PB)
|
||||
{
|
||||
const u16* PB_in_mram = (const u16*)Memory::GetPointer(addr);
|
||||
if (PB_in_mram == NULL)
|
||||
return false;
|
||||
u16* PB_in_aram = (u16*)&PB;
|
||||
|
||||
for (size_t p = 0; p < (sizeof(AXPB) >> 1); p++)
|
||||
{
|
||||
PB_in_aram[p] = Common::swap16(PB_in_mram[p]);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// MRAM -> ARAM for Wii
|
||||
inline bool ReadPB(u32 addr, AXPBWii &PB)
|
||||
{
|
||||
const u16* PB_in_mram = (const u16*)Memory::GetPointer(addr);
|
||||
if (PB_in_mram == NULL)
|
||||
return false;
|
||||
u16* PB_in_aram = (u16*)&PB;
|
||||
|
||||
// preswap the mixer_control
|
||||
PB.mixer_control = ((u32)PB_in_mram[7] << 16) | ((u32)PB_in_mram[6] >> 16);
|
||||
|
||||
for (size_t p = 0; p < (sizeof(AXPBWii) >> 1); p++)
|
||||
{
|
||||
PB_in_aram[p] = Common::swap16(PB_in_mram[p]);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// ARAM -> MRAM for GC
|
||||
inline bool WritePB(u32 addr, AXPB &PB)
|
||||
{
|
||||
const u16* PB_in_aram = (const u16*)&PB;
|
||||
u16* PB_in_mram = (u16*)Memory::GetPointer(addr);
|
||||
if (PB_in_mram == NULL)
|
||||
return false;
|
||||
|
||||
for (size_t p = 0; p < (sizeof(AXPB) >> 1); p++)
|
||||
{
|
||||
PB_in_mram[p] = Common::swap16(PB_in_aram[p]);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// ARAM -> MRAM for Wii
|
||||
inline bool WritePB(u32 addr, AXPBWii &PB)
|
||||
{
|
||||
const u16* PB_in_aram = (const u16*)&PB;
|
||||
u16* PB_in_mram = (u16*)Memory::GetPointer(addr);
|
||||
if (PB_in_mram == NULL)
|
||||
return false;
|
||||
|
||||
// preswap the mixer_control
|
||||
*(u32*)&PB_in_mram[6] = (PB.mixer_control << 16) | (PB.mixer_control >> 16);
|
||||
|
||||
for (size_t p = 0; p < (sizeof(AXPBWii) >> 1); p++)
|
||||
{
|
||||
PB_in_mram[p] = Common::swap16(PB_in_aram[p]);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// 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,
|
||||
int _iSize)
|
||||
{
|
||||
if (pb.running)
|
||||
{
|
||||
const u32 ratio = (u32)(((pb.src.ratio_hi << 16) + pb.src.ratio_lo)
|
||||
* /*ratioFactor:*/((float)AudioInterface::GetAIDSampleRate() / (float)soundStream->GetMixer()->GetSampleRate()));
|
||||
u32 sampleEnd = (pb.audio_addr.end_addr_hi << 16) | pb.audio_addr.end_addr_lo;
|
||||
u32 loopPos = (pb.audio_addr.loop_addr_hi << 16) | pb.audio_addr.loop_addr_lo;
|
||||
|
||||
u32 samplePos = (pb.audio_addr.cur_addr_hi << 16) | pb.audio_addr.cur_addr_lo;
|
||||
u32 frac = pb.src.cur_addr_frac;
|
||||
|
||||
// =======================================================================================
|
||||
// Handle No-SRC streams - No src streams have pb.src_type == 2 and have pb.src.ratio_hi = 0
|
||||
// and pb.src.ratio_lo = 0. We handle that by setting the sampling ratio integer to 1. This
|
||||
// makes samplePos update in the correct way. I'm unsure how we are actually supposed to
|
||||
// detect that this setting. Updates did not fix this automatically.
|
||||
// ---------------------------------------------------------------------------------------
|
||||
// Stream settings
|
||||
// src_type = 2 (most other games have src_type = 0)
|
||||
// Affected games:
|
||||
// Baten Kaitos - Eternal Wings (2003)
|
||||
// Baten Kaitos - Origins (2006)?
|
||||
// Soul Calibur 2: The movie music use src_type 2 but it needs no adjustment, perhaps
|
||||
// the sound format plays in to, Baten use ADPCM, SC2 use PCM16
|
||||
//if (pb.src_type == 2 && (pb.src.ratio_hi == 0 && pb.src.ratio_lo == 0))
|
||||
if (pb.running && (pb.src.ratio_hi == 0 && pb.src.ratio_lo == 0))
|
||||
{
|
||||
pb.src.ratio_hi = 1;
|
||||
}
|
||||
|
||||
// =======================================================================================
|
||||
// Games that use looping to play non-looping music streams - SSBM has info in all
|
||||
// pb.adpcm_loop_info parameters but has pb.audio_addr.looping = 0. If we treat these streams
|
||||
// like any other looping streams the music works. I'm unsure how we are actually supposed to
|
||||
// detect that these kinds of blocks should be looping. It seems like pb.mixer_control == 0 may
|
||||
// identify these types of blocks. Updates did not write any looping values.
|
||||
if (
|
||||
(pb.adpcm_loop_info.pred_scale || pb.adpcm_loop_info.yn1 || pb.adpcm_loop_info.yn2)
|
||||
&& pb.mixer_control == 0 && pb.adpcm_loop_info.pred_scale <= 0x7F
|
||||
)
|
||||
{
|
||||
pb.audio_addr.looping = 1;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Top Spin 3 Wii
|
||||
if (pb.audio_addr.sample_format > 25)
|
||||
pb.audio_addr.sample_format = 0;
|
||||
|
||||
// =======================================================================================
|
||||
// Walk through _iSize. _iSize = numSamples. If the game goes slow _iSize will be higher to
|
||||
// compensate for that. _iSize can be as low as 100 or as high as 2000 some cases.
|
||||
for (int s = 0; s < _iSize; s++)
|
||||
{
|
||||
int sample = 0;
|
||||
u32 oldFrac = frac;
|
||||
frac += ratio;
|
||||
u32 newSamplePos = samplePos + (frac >> 16); //whole number of frac
|
||||
|
||||
// =======================================================================================
|
||||
// Process sample format
|
||||
switch (pb.audio_addr.sample_format)
|
||||
{
|
||||
case AUDIOFORMAT_PCM8:
|
||||
pb.adpcm.yn2 = ((s8)DSP::ReadARAM(samplePos)) << 8; //current sample
|
||||
pb.adpcm.yn1 = ((s8)DSP::ReadARAM(samplePos + 1)) << 8; //next sample
|
||||
|
||||
if (pb.src_type == SRCTYPE_NEAREST)
|
||||
sample = pb.adpcm.yn2;
|
||||
else // linear interpolation
|
||||
sample = (pb.adpcm.yn1 * (u16)oldFrac + pb.adpcm.yn2 * (u16)(0xFFFF - oldFrac) + pb.adpcm.yn2) >> 16;
|
||||
|
||||
samplePos = newSamplePos;
|
||||
break;
|
||||
|
||||
case AUDIOFORMAT_PCM16:
|
||||
pb.adpcm.yn2 = (s16)(u16)((DSP::ReadARAM(samplePos * 2) << 8) | (DSP::ReadARAM((samplePos * 2 + 1)))); //current sample
|
||||
pb.adpcm.yn1 = (s16)(u16)((DSP::ReadARAM((samplePos + 1) * 2) << 8) | (DSP::ReadARAM(((samplePos + 1) * 2 + 1)))); //next sample
|
||||
|
||||
if (pb.src_type == SRCTYPE_NEAREST)
|
||||
sample = pb.adpcm.yn2;
|
||||
else // linear interpolation
|
||||
sample = (pb.adpcm.yn1 * (u16)oldFrac + pb.adpcm.yn2 * (u16)(0xFFFF - oldFrac) + pb.adpcm.yn2) >> 16;
|
||||
|
||||
samplePos = newSamplePos;
|
||||
break;
|
||||
|
||||
case AUDIOFORMAT_ADPCM:
|
||||
ADPCM_Step(pb.adpcm, samplePos, newSamplePos, frac);
|
||||
|
||||
if (pb.src_type == SRCTYPE_NEAREST)
|
||||
sample = pb.adpcm.yn2;
|
||||
else // linear interpolation
|
||||
sample = (pb.adpcm.yn1 * (u16)frac + pb.adpcm.yn2 * (u16)(0xFFFF - frac) + pb.adpcm.yn2) >> 16; //adpcm moves on frac
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
// ===================================================================
|
||||
// Overall volume control. In addition to this there is also separate volume settings to
|
||||
// different channels (left, right etc).
|
||||
frac &= 0xffff;
|
||||
|
||||
int vol = pb.vol_env.cur_volume >> 9;
|
||||
sample = sample * vol >> 8;
|
||||
|
||||
if (pb.mixer_control & MIXCONTROL_RAMPING)
|
||||
{
|
||||
int x = pb.vol_env.cur_volume;
|
||||
x += pb.vol_env.cur_volume_delta; // I'm not sure about this, can anybody find a game
|
||||
// that use this? Or how does it work?
|
||||
if (x < 0)
|
||||
x = 0;
|
||||
if (x >= 0x7fff)
|
||||
x = 0x7fff;
|
||||
pb.vol_env.cur_volume = x; // maybe not per sample?? :P
|
||||
}
|
||||
|
||||
int leftmix = pb.mixer.left >> 5;
|
||||
int rightmix = pb.mixer.right >> 5;
|
||||
int left = sample * leftmix >> 8;
|
||||
int right = sample * rightmix >> 8;
|
||||
// adpcm has to walk from oldSamplePos to samplePos here
|
||||
templbuffer[s] += left;
|
||||
temprbuffer[s] += right;
|
||||
|
||||
// Control the behavior when we reach the end of the sample
|
||||
if (samplePos >= sampleEnd)
|
||||
{
|
||||
if (pb.audio_addr.looping == 1)
|
||||
{
|
||||
if ((samplePos & ~0x1f) == (sampleEnd & ~0x1f) || (pb.audio_addr.sample_format != AUDIOFORMAT_ADPCM))
|
||||
samplePos = loopPos;
|
||||
if ((!pb.is_stream) && (pb.audio_addr.sample_format == AUDIOFORMAT_ADPCM))
|
||||
{
|
||||
pb.adpcm.yn1 = pb.adpcm_loop_info.yn1;
|
||||
pb.adpcm.yn2 = pb.adpcm_loop_info.yn2;
|
||||
pb.adpcm.pred_scale = pb.adpcm_loop_info.pred_scale;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
pb.running = 0;
|
||||
samplePos = loopPos;
|
||||
//samplePos = samplePos - sampleEnd + loopPos;
|
||||
memset(&pb.dpop, 0, sizeof(pb.dpop));
|
||||
memset(pb.src.last_samples, 0, 8);
|
||||
break;
|
||||
}
|
||||
}
|
||||
} // end of the _iSize loop
|
||||
|
||||
// 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.src.cur_addr_frac = (u16)frac;
|
||||
pb.audio_addr.cur_addr_hi = samplePos >> 16;
|
||||
pb.audio_addr.cur_addr_lo = (u16)samplePos;
|
||||
|
||||
} // if (pb.running)
|
||||
}
|
||||
|
||||
#endif
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,383 +0,0 @@
|
||||
// Copyright (C) 2003 Dolphin Project.
|
||||
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, version 2.0.
|
||||
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License 2.0 for more details.
|
||||
|
||||
// A copy of the GPL 2.0 should have been included with the program.
|
||||
// If not, see http://www.gnu.org/licenses/
|
||||
|
||||
// Official SVN repository and contact information can be found at
|
||||
// http://code.google.com/p/dolphin-emu/
|
||||
|
||||
#include "StringUtil.h"
|
||||
|
||||
#include "../MailHandler.h"
|
||||
#include "Mixer.h"
|
||||
|
||||
#include "UCodes.h"
|
||||
#include "UCode_AX_Structs.h"
|
||||
#include "UCode_NewAXWii.h"
|
||||
|
||||
#define AX_WII
|
||||
#include "UCode_AX_Voice.h"
|
||||
|
||||
|
||||
CUCode_NewAXWii::CUCode_NewAXWii(DSPHLE *dsp_hle, u32 l_CRC)
|
||||
: CUCode_AX(dsp_hle, l_CRC)
|
||||
{
|
||||
WARN_LOG(DSPHLE, "Instantiating CUCode_NewAXWii");
|
||||
}
|
||||
|
||||
CUCode_NewAXWii::~CUCode_NewAXWii()
|
||||
{
|
||||
}
|
||||
|
||||
void CUCode_NewAXWii::HandleCommandList()
|
||||
{
|
||||
// Temp variables for addresses computation
|
||||
u16 addr_hi, addr_lo;
|
||||
u16 addr2_hi, addr2_lo;
|
||||
u16 volume;
|
||||
|
||||
// WARN_LOG(DSPHLE, "Command list:");
|
||||
// for (u32 i = 0; m_cmdlist[i] != CMD_END; ++i)
|
||||
// WARN_LOG(DSPHLE, "%04x", m_cmdlist[i]);
|
||||
// WARN_LOG(DSPHLE, "-------------");
|
||||
|
||||
u32 curr_idx = 0;
|
||||
bool end = false;
|
||||
while (!end)
|
||||
{
|
||||
u16 cmd = m_cmdlist[curr_idx++];
|
||||
|
||||
switch (cmd)
|
||||
{
|
||||
// Some of these commands are unknown, or unused in this AX HLE.
|
||||
// We still need to skip their arguments using "curr_idx += N".
|
||||
|
||||
case CMD_SETUP:
|
||||
addr_hi = m_cmdlist[curr_idx++];
|
||||
addr_lo = m_cmdlist[curr_idx++];
|
||||
SetupProcessing(HILO_TO_32(addr));
|
||||
break;
|
||||
|
||||
case CMD_UNK_01: curr_idx += 2; break;
|
||||
case CMD_UNK_02: curr_idx += 2; break;
|
||||
case CMD_UNK_03: curr_idx += 2; break;
|
||||
|
||||
case CMD_PROCESS:
|
||||
addr_hi = m_cmdlist[curr_idx++];
|
||||
addr_lo = m_cmdlist[curr_idx++];
|
||||
ProcessPBList(HILO_TO_32(addr));
|
||||
break;
|
||||
|
||||
case CMD_MIX_AUXA:
|
||||
case CMD_MIX_AUXB:
|
||||
case CMD_MIX_AUXC:
|
||||
volume = m_cmdlist[curr_idx++];
|
||||
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), volume);
|
||||
break;
|
||||
|
||||
// These two go together and manipulate some AUX buffers.
|
||||
case CMD_UNK_08: curr_idx += 13; break;
|
||||
case CMD_UNK_09: curr_idx += 13; break;
|
||||
|
||||
case CMD_UNK_0A: curr_idx += 4; break;
|
||||
|
||||
case CMD_OUTPUT:
|
||||
volume = m_cmdlist[curr_idx++];
|
||||
addr_hi = m_cmdlist[curr_idx++];
|
||||
addr_lo = m_cmdlist[curr_idx++];
|
||||
addr2_hi = m_cmdlist[curr_idx++];
|
||||
addr2_lo = m_cmdlist[curr_idx++];
|
||||
OutputSamples(HILO_TO_32(addr2), HILO_TO_32(addr), volume);
|
||||
break;
|
||||
|
||||
case CMD_UNK_0C: curr_idx += 5; break;
|
||||
|
||||
case CMD_WM_OUTPUT:
|
||||
{
|
||||
u32 addresses[4] = {
|
||||
(u32)(m_cmdlist[curr_idx + 0] << 16) | m_cmdlist[curr_idx + 1],
|
||||
(u32)(m_cmdlist[curr_idx + 2] << 16) | m_cmdlist[curr_idx + 3],
|
||||
(u32)(m_cmdlist[curr_idx + 4] << 16) | m_cmdlist[curr_idx + 5],
|
||||
(u32)(m_cmdlist[curr_idx + 6] << 16) | m_cmdlist[curr_idx + 7],
|
||||
};
|
||||
curr_idx += 8;
|
||||
OutputWMSamples(addresses);
|
||||
break;
|
||||
}
|
||||
|
||||
case CMD_END:
|
||||
end = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CUCode_NewAXWii::SetupProcessing(u32 init_addr)
|
||||
{
|
||||
// TODO: should be easily factorizable with AX
|
||||
s16 init_data[60];
|
||||
|
||||
for (u32 i = 0; i < 60; ++i)
|
||||
init_data[i] = HLEMemory_Read_U16(init_addr + 2 * i);
|
||||
|
||||
// List of all buffers we have to initialize
|
||||
struct {
|
||||
int* ptr;
|
||||
u32 samples;
|
||||
} buffers[] = {
|
||||
{ m_samples_left, 32 },
|
||||
{ m_samples_right, 32 },
|
||||
{ m_samples_surround, 32 },
|
||||
{ m_samples_auxA_left, 32 },
|
||||
{ m_samples_auxA_right, 32 },
|
||||
{ m_samples_auxA_surround, 32 },
|
||||
{ m_samples_auxB_left, 32 },
|
||||
{ m_samples_auxB_right, 32 },
|
||||
{ m_samples_auxB_surround, 32 },
|
||||
{ m_samples_auxC_left, 32 },
|
||||
{ m_samples_auxC_right, 32 },
|
||||
{ m_samples_auxC_surround, 32 },
|
||||
|
||||
{ m_samples_wm0, 6 },
|
||||
{ m_samples_aux0, 6 },
|
||||
{ m_samples_wm1, 6 },
|
||||
{ m_samples_aux1, 6 },
|
||||
{ m_samples_wm2, 6 },
|
||||
{ m_samples_aux2, 6 },
|
||||
{ m_samples_wm3, 6 },
|
||||
{ m_samples_aux3, 6 }
|
||||
};
|
||||
|
||||
u32 init_idx = 0;
|
||||
for (u32 i = 0; i < sizeof (buffers) / sizeof (buffers[0]); ++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].ptr, 0, 3 * buffers[i].samples * sizeof (int));
|
||||
else
|
||||
{
|
||||
for (u32 j = 0; j < 3 * buffers[i].samples; ++j)
|
||||
{
|
||||
buffers[i].ptr[j] = init_val;
|
||||
init_val += delta;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
AXMixControl CUCode_NewAXWii::ConvertMixerControl(u32 mixer_control)
|
||||
{
|
||||
u32 ret = 0;
|
||||
|
||||
if (mixer_control & 0x00000001) ret |= MIX_L;
|
||||
if (mixer_control & 0x00000002) ret |= MIX_R;
|
||||
if (mixer_control & 0x00000004) ret |= MIX_L_RAMP | MIX_R_RAMP;
|
||||
if (mixer_control & 0x00000008) ret |= MIX_S;
|
||||
if (mixer_control & 0x00000010) ret |= MIX_S_RAMP;
|
||||
if (mixer_control & 0x00010000) ret |= MIX_AUXA_L;
|
||||
if (mixer_control & 0x00020000) ret |= MIX_AUXA_R;
|
||||
if (mixer_control & 0x00040000) ret |= MIX_AUXA_L_RAMP | MIX_AUXA_R_RAMP;
|
||||
if (mixer_control & 0x00080000) ret |= MIX_AUXA_S;
|
||||
if (mixer_control & 0x00100000) ret |= MIX_AUXA_S_RAMP;
|
||||
if (mixer_control & 0x00200000) ret |= MIX_AUXB_L;
|
||||
if (mixer_control & 0x00400000) ret |= MIX_AUXB_R;
|
||||
if (mixer_control & 0x00800000) ret |= MIX_AUXB_L_RAMP | MIX_AUXB_R_RAMP;
|
||||
if (mixer_control & 0x01000000) ret |= MIX_AUXB_S;
|
||||
if (mixer_control & 0x02000000) ret |= MIX_AUXB_S_RAMP;
|
||||
if (mixer_control & 0x04000000) ret |= MIX_AUXC_L;
|
||||
if (mixer_control & 0x08000000) ret |= MIX_AUXC_R;
|
||||
if (mixer_control & 0x10000000) ret |= MIX_AUXC_L_RAMP | MIX_AUXC_R_RAMP;
|
||||
if (mixer_control & 0x20000000) ret |= MIX_AUXC_S;
|
||||
if (mixer_control & 0x40000000) ret |= MIX_AUXC_S_RAMP;
|
||||
|
||||
return (AXMixControl)ret;
|
||||
}
|
||||
|
||||
void CUCode_NewAXWii::ProcessPBList(u32 pb_addr)
|
||||
{
|
||||
const u32 spms = 32;
|
||||
|
||||
AXPBWii pb;
|
||||
|
||||
while (pb_addr)
|
||||
{
|
||||
AXBuffers 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,
|
||||
m_samples_auxC_left,
|
||||
m_samples_auxC_right,
|
||||
m_samples_auxC_surround
|
||||
}};
|
||||
|
||||
if (!ReadPB(pb_addr, pb))
|
||||
break;
|
||||
|
||||
for (int curr_ms = 0; curr_ms < 3; ++curr_ms)
|
||||
{
|
||||
Process1ms(pb, buffers, ConvertMixerControl(HILO_TO_32(pb.mixer_control)));
|
||||
|
||||
// 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);
|
||||
}
|
||||
}
|
||||
|
||||
void CUCode_NewAXWii::MixAUXSamples(int aux_id, u32 write_addr, u32 read_addr, u16 volume)
|
||||
{
|
||||
int* buffers[3] = { 0 };
|
||||
int* main_buffers[3] = {
|
||||
m_samples_left,
|
||||
m_samples_right,
|
||||
m_samples_surround
|
||||
};
|
||||
|
||||
switch (aux_id)
|
||||
{
|
||||
case 0:
|
||||
buffers[0] = m_samples_auxA_left;
|
||||
buffers[1] = m_samples_auxA_right;
|
||||
buffers[2] = m_samples_auxA_surround;
|
||||
break;
|
||||
|
||||
case 1:
|
||||
buffers[0] = m_samples_auxB_left;
|
||||
buffers[1] = m_samples_auxB_right;
|
||||
buffers[2] = m_samples_auxB_surround;
|
||||
break;
|
||||
|
||||
case 2:
|
||||
buffers[0] = m_samples_auxC_left;
|
||||
buffers[1] = m_samples_auxC_right;
|
||||
buffers[2] = m_samples_auxC_surround;
|
||||
break;
|
||||
}
|
||||
|
||||
// Send the content of AUX buffers to the CPU
|
||||
if (write_addr)
|
||||
{
|
||||
int* ptr = (int*)HLEMemory_Get_Pointer(write_addr);
|
||||
for (u32 i = 0; i < 3; ++i)
|
||||
for (u32 j = 0; j < 3 * 32; ++j)
|
||||
*ptr++ = Common::swap32(buffers[i][j]);
|
||||
}
|
||||
|
||||
// Then read the buffers from the CPU and add to our main buffers.
|
||||
int* ptr = (int*)HLEMemory_Get_Pointer(read_addr);
|
||||
for (u32 i = 0; i < 3; ++i)
|
||||
for (u32 j = 0; j < 3 * 32; ++j)
|
||||
{
|
||||
s64 new_val = main_buffers[i][j] + Common::swap32(*ptr++);
|
||||
main_buffers[i][j] = (new_val * volume) >> 15;
|
||||
}
|
||||
}
|
||||
|
||||
void CUCode_NewAXWii::OutputSamples(u32 lr_addr, u32 surround_addr, u16 volume)
|
||||
{
|
||||
int surround_buffer[3 * 32] = { 0 };
|
||||
|
||||
for (u32 i = 0; i < 3 * 32; ++i)
|
||||
surround_buffer[i] = Common::swap32(m_samples_surround[i]);
|
||||
memcpy(HLEMemory_Get_Pointer(surround_addr), surround_buffer, sizeof (surround_buffer));
|
||||
|
||||
short buffer[3 * 32 * 2];
|
||||
|
||||
// Clamp internal buffers to 16 bits.
|
||||
for (u32 i = 0; i < 3 * 32; ++i)
|
||||
{
|
||||
int left = m_samples_left[i];
|
||||
int right = m_samples_right[i];
|
||||
|
||||
// Apply global volume. Cast to s64 to avoid overflow.
|
||||
left = ((s64)left * volume) >> 15;
|
||||
right = ((s64)right * volume) >> 15;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
for (u32 i = 0; i < 3 * 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(lr_addr), buffer, sizeof (buffer));
|
||||
}
|
||||
|
||||
void CUCode_NewAXWii::OutputWMSamples(u32* addresses)
|
||||
{
|
||||
int* buffers[] = {
|
||||
m_samples_wm0,
|
||||
m_samples_wm1,
|
||||
m_samples_wm2,
|
||||
m_samples_wm3
|
||||
};
|
||||
|
||||
for (u32 i = 0; i < 4; ++i)
|
||||
{
|
||||
int* in = buffers[i];
|
||||
u16* out = (u16*)HLEMemory_Get_Pointer(addresses[i]);
|
||||
for (u32 j = 0; j < 3 * 6; ++j)
|
||||
{
|
||||
int sample = in[j];
|
||||
if (sample < -32767) sample = -32767;
|
||||
if (sample > 32767) sample = 32767;
|
||||
out[j] = Common::swap16((u16)sample);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CUCode_NewAXWii::DoState(PointerWrap &p)
|
||||
{
|
||||
std::lock_guard<std::mutex> lk(m_processing);
|
||||
|
||||
DoStateShared(p);
|
||||
DoAXState(p);
|
||||
|
||||
p.Do(m_samples_auxC_left);
|
||||
p.Do(m_samples_auxC_right);
|
||||
p.Do(m_samples_auxC_surround);
|
||||
|
||||
p.Do(m_samples_wm0);
|
||||
p.Do(m_samples_wm1);
|
||||
p.Do(m_samples_wm2);
|
||||
p.Do(m_samples_wm3);
|
||||
|
||||
p.Do(m_samples_aux0);
|
||||
p.Do(m_samples_aux1);
|
||||
p.Do(m_samples_aux2);
|
||||
p.Do(m_samples_aux3);
|
||||
}
|
||||
@@ -1,80 +0,0 @@
|
||||
// Copyright (C) 2003 Dolphin Project.
|
||||
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, version 2.0.
|
||||
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License 2.0 for more details.
|
||||
|
||||
// A copy of the GPL 2.0 should have been included with the program.
|
||||
// If not, see http://www.gnu.org/licenses/
|
||||
|
||||
// Official Git repository and contact information can be found at
|
||||
// http://code.google.com/p/dolphin-emu/
|
||||
|
||||
#ifndef _UCODE_NEWAXWII_H
|
||||
#define _UCODE_NEWAXWII_H
|
||||
|
||||
#include "UCode_AX.h"
|
||||
|
||||
class CUCode_NewAXWii : public CUCode_AX
|
||||
{
|
||||
public:
|
||||
CUCode_NewAXWii(DSPHLE *dsp_hle, u32 _CRC);
|
||||
virtual ~CUCode_NewAXWii();
|
||||
|
||||
virtual void DoState(PointerWrap &p);
|
||||
|
||||
protected:
|
||||
int m_samples_auxC_left[32 * 3];
|
||||
int m_samples_auxC_right[32 * 3];
|
||||
int m_samples_auxC_surround[32 * 3];
|
||||
|
||||
// Wiimote buffers
|
||||
int m_samples_wm0[6 * 3];
|
||||
int m_samples_aux0[6 * 3];
|
||||
int m_samples_wm1[6 * 3];
|
||||
int m_samples_aux1[6 * 3];
|
||||
int m_samples_wm2[6 * 3];
|
||||
int m_samples_aux2[6 * 3];
|
||||
int m_samples_wm3[6 * 3];
|
||||
int m_samples_aux3[6 * 3];
|
||||
|
||||
// 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.
|
||||
AXMixControl ConvertMixerControl(u32 mixer_control);
|
||||
|
||||
virtual void HandleCommandList();
|
||||
|
||||
void SetupProcessing(u32 init_addr);
|
||||
void ProcessPBList(u32 pb_addr);
|
||||
void MixAUXSamples(int aux_id, u32 write_addr, u32 read_addr, u16 volume);
|
||||
void OutputSamples(u32 lr_addr, u32 surround_addr, u16 volume);
|
||||
void OutputWMSamples(u32* addresses); // 4 addresses
|
||||
|
||||
private:
|
||||
enum CmdType
|
||||
{
|
||||
CMD_SETUP = 0x00,
|
||||
CMD_UNK_01 = 0x01,
|
||||
CMD_UNK_02 = 0x02,
|
||||
CMD_UNK_03 = 0x03,
|
||||
CMD_PROCESS = 0x04,
|
||||
CMD_MIX_AUXA = 0x05,
|
||||
CMD_MIX_AUXB = 0x06,
|
||||
CMD_MIX_AUXC = 0x07,
|
||||
CMD_UNK_08 = 0x08,
|
||||
CMD_UNK_09 = 0x09,
|
||||
CMD_UNK_0A = 0x0A,
|
||||
CMD_OUTPUT = 0x0B,
|
||||
CMD_UNK_0C = 0x0C,
|
||||
CMD_WM_OUTPUT = 0x0D,
|
||||
CMD_END = 0x0E
|
||||
};
|
||||
};
|
||||
|
||||
#endif // _UCODE_AXWII
|
||||
@@ -19,7 +19,6 @@
|
||||
|
||||
#include "UCode_AX.h"
|
||||
#include "UCode_AXWii.h"
|
||||
#include "UCode_NewAXWii.h"
|
||||
#include "UCode_Zelda.h"
|
||||
#include "UCode_ROM.h"
|
||||
#include "UCode_CARD.h"
|
||||
@@ -27,12 +26,6 @@
|
||||
#include "UCode_GBA.h"
|
||||
#include "Hash.h"
|
||||
|
||||
#if 0
|
||||
# define AXWII CUCode_NewAXWii
|
||||
#else
|
||||
# define AXWII CUCode_AXWii
|
||||
#endif
|
||||
|
||||
IUCode* UCodeFactory(u32 _CRC, DSPHLE *dsp_hle, bool bWii)
|
||||
{
|
||||
switch (_CRC)
|
||||
@@ -97,13 +90,13 @@ IUCode* UCodeFactory(u32 _CRC, DSPHLE *dsp_hle, bool bWii)
|
||||
case 0x4cc52064: // Bleach: Versus Crusade
|
||||
case 0xd9c4bf34: // WiiMenu
|
||||
INFO_LOG(DSPHLE, "CRC %08x: Wii - AXWii chosen", _CRC);
|
||||
return new AXWII(dsp_hle, _CRC);
|
||||
return new CUCode_AXWii(dsp_hle, _CRC);
|
||||
|
||||
default:
|
||||
if (bWii)
|
||||
{
|
||||
PanicAlert("DSPHLE: Unknown ucode (CRC = %08x) - forcing AXWii.\n\nTry LLE emulator if this is homebrew.", _CRC);
|
||||
return new AXWII(dsp_hle, _CRC);
|
||||
return new CUCode_AXWii(dsp_hle, _CRC);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
@@ -508,7 +508,7 @@ void CConfigMain::InitializeGUITooltips()
|
||||
InterfaceLang->SetToolTip(_("Change the language of the user interface.\nRequires restart."));
|
||||
|
||||
// Audio tooltips
|
||||
DSPThread->SetToolTip(_("Run DSP LLE on a dedicated thread (not recommended)."));
|
||||
DSPThread->SetToolTip(_("Run DSP HLE and LLE on a dedicated thread (not recommended: might cause audio glitches with HLE and freezes with LLE)."));
|
||||
BackendSelection->SetToolTip(_("Changing this will have no effect while the emulator is running!"));
|
||||
|
||||
// Gamecube - Devices
|
||||
@@ -651,7 +651,7 @@ void CConfigMain::CreateGUIControls()
|
||||
// Audio page
|
||||
DSPEngine = new wxRadioBox(AudioPage, ID_DSPENGINE, _("DSP Emulator Engine"),
|
||||
wxDefaultPosition, wxDefaultSize, arrayStringFor_DSPEngine, 0, wxRA_SPECIFY_ROWS);
|
||||
DSPThread = new wxCheckBox(AudioPage, ID_DSPTHREAD, _("DSP LLE on Thread"));
|
||||
DSPThread = new wxCheckBox(AudioPage, ID_DSPTHREAD, _("DSP on Dedicated Thread"));
|
||||
DumpAudio = new wxCheckBox(AudioPage, ID_DUMP_AUDIO, _("Dump Audio"),
|
||||
wxDefaultPosition, wxDefaultSize, 0, wxDefaultValidator);
|
||||
DPL2Decoder = new wxCheckBox(AudioPage, ID_DPL2DECODER, _("Dolby Pro Logic II decoder"));
|
||||
|
||||
Reference in New Issue
Block a user