updated dsp null some clean up as well.

Please test on windows


git-svn-id: https://dolphin-emu.googlecode.com/svn/trunk@2018 8ced0084-cf51-0410-be5f-012b33b47a6e
This commit is contained in:
nakeee 2009-01-26 16:58:01 +00:00
parent 356db07cbf
commit 52deb12599
6 changed files with 919 additions and 923 deletions

View File

@ -11,6 +11,7 @@ files = [
"main.cpp",
"Globals.cpp",
"UCodes/UCode_AX.cpp",
"UCodes/UCode_AXWii.cpp",
"UCodes/UCode_CARD.cpp",
"UCodes/UCode_InitAudioSystem.cpp",
"UCodes/UCode_Jac.cpp",

View File

@ -17,7 +17,6 @@
#include "FileUtil.h" // For IsDirectory()
#include "StringUtil.h" // For StringFromFormat()
#include <sstream>
#include "../MailHandler.h"
@ -27,7 +26,6 @@
#include "UCode_AX.h"
#include "UCode_AX_Voice.h"
// ------------------------------------------------------------------
// Externals
// -----------
@ -95,8 +93,9 @@ void CUCode_AX::SaveLog_(bool Wii, const char* _fmt, va_list ap)
char Msg[512];
vsprintf(Msg, _fmt, ap);
TmpMailLog += Msg;
TmpMailLog += "\n";
TmpMailLog += Msg;
TmpMailLog += "\n";
}
// ----------------
@ -125,6 +124,7 @@ int ReadOutPBs(u32 pbs_address, AXParamBlock* _pPBs, int _num)
for (size_t p = 0; p < sizeof(AXParamBlock) / 2; p++)
{
pDest[p] = Common::swap16(pSrc[p]);
}
blockAddr = (_pPBs[i].next_pb_hi << 16) | _pPBs[i].next_pb_lo;
count++;
@ -236,6 +236,7 @@ void CUCode_AX::MixAdd(short* _pBuffer, int _iSize)
*_pBuffer++ = left;
*_pBuffer++ = right;
}
}
@ -343,7 +344,6 @@ bool CUCode_AX::AXTask(u32& _uMail)
m_addressPBs = Memory_Read_U32(uAddress);
uAddress += 4;
SaveLog("%08x : AXLIST PB address: %08x", uAddress, m_addressPBs);
}
break;

View File

@ -1,372 +1,366 @@
// Copyright (C) 2003-2008 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 "UCodes.h"
#include "UCode_AXStructs.h"
#include "UCode_AX.h" // for some functions in CUCode_AX
#include "UCode_AXWii.h"
#include "UCode_AX_Voice.h"
// ------------------------------------------------------------------
// Declarations
// -----------
extern bool gSequenced;
// -----------
CUCode_AXWii::CUCode_AXWii(CMailHandler& _rMailHandler, u32 l_CRC)
: IUCode(_rMailHandler)
, m_addressPBs(0xFFFFFFFF)
, _CRC(l_CRC)
{
// we got loaded
m_rMailHandler.PushMail(0xDCD10000);
m_rMailHandler.PushMail(0x80000000); // handshake ??? only (crc == 0xe2136399) needs it ...
templbuffer = new int[1024 * 1024];
temprbuffer = new int[1024 * 1024];
lCUCode_AX = new CUCode_AX(_rMailHandler);
lCUCode_AX->_CRC = l_CRC;
}
CUCode_AXWii::~CUCode_AXWii()
{
m_rMailHandler.Clear();
delete [] templbuffer;
delete [] temprbuffer;
}
void CUCode_AXWii::HandleMail(u32 _uMail)
{
if ((_uMail & 0xFFFF0000) == MAIL_AX_ALIST)
{
// a new List
}
else
{
AXTask(_uMail);
}
}
void CUCode_AXWii::MixAdd(short* _pBuffer, int _iSize)
{
if(_CRC == 0xfa450138)
{
AXParamBlockWii PBs[NUMBER_OF_PBS];
MixAdd_( _pBuffer, _iSize, PBs);
}
else
{
AXParamBlockWii_ PBs[NUMBER_OF_PBS];
MixAdd_(_pBuffer, _iSize, PBs);
}
}
template<class ParamBlockType>
void CUCode_AXWii::MixAdd_(short* _pBuffer, int _iSize, ParamBlockType &PBs)
{
//AXParamBlockWii PBs[NUMBER_OF_PBS];
// read out pbs
int numberOfPBs = ReadOutPBsWii(m_addressPBs, PBs, NUMBER_OF_PBS);
if (_iSize > 1024 * 1024)
_iSize = 1024 * 1024;
// write zeroes to the beginning of templbuffer
memset(templbuffer, 0, _iSize * sizeof(int));
memset(temprbuffer, 0, _iSize * sizeof(int));
// -------------------------------------------
// write logging data to debugger
// ---------------------------------------------------------------------------------------
/* Make the updates we are told to do. See comments to the GC version in UCode_AX.cpp */
// ------------
for (int i = 0; i < numberOfPBs; i++)
{
u16 *pDest = (u16 *)&PBs[i];
u16 upd0 = pDest[41]; u16 upd1 = pDest[42]; u16 upd2 = pDest[43]; // num_updates
u16 upd_hi = pDest[44]; // update addr
u16 upd_lo = pDest[45];
int numupd = upd0 + upd1 + upd2;
if(numupd > 64) numupd = 64; // prevent to high values
const u32 updaddr = (u32)(upd_hi << 16) | upd_lo;
int on = false, off = false;
for (int j = 0; j < numupd; j++) // make alll updates
{
const u16 updpar = Memory_Read_U16(updaddr);
const u16 upddata = Memory_Read_U16(updaddr + 2);
// some safety checks, I hope it's enough
if( ( (updaddr > 0x80000000 && updaddr < 0x817fffff)
|| (updaddr > 0x90000000 && updaddr < 0x93ffffff) )
&& updpar < 127 && updpar > 3 && upddata >= 0 // updpar > 3 because we don't want to change
// 0-3, those are important
//&& (upd0 || upd1 || upd2) // We should use these in some way to I think
// but I don't know how or when
&& gSequenced) // on and off option
{
//PanicAlert("Update %i: %i = %04x", i, updpar, upddata);
//DebugLog("Update: %i = %04x", updpar, upddata);
pDest[updpar] = upddata;
}
if (updpar == 7 && upddata == 1) on++;
if (updpar == 7 && upddata == 1) off++;
}
// hack: if we get both an on and an off select on rather than off
if (on > 0 && off > 0) pDest[7] = 1;
}
//PrintFile(1, "%08x %04x %04x\n", updaddr, updpar, upddata);
// ------------
for (int i = 0; i < numberOfPBs; i++)
{
MixAddVoice(PBs[i], templbuffer, temprbuffer, _iSize, true);
}
WriteBackPBsWii(m_addressPBs, PBs, numberOfPBs);
// We write the sound to _pBuffer
for (int i = 0; i < _iSize; i++)
{
// Clamp into 16-bit. Maybe we should add a volume compressor here.
int left = templbuffer[i] + _pBuffer[0];
int right = temprbuffer[i] + _pBuffer[1];
if (left < -32767) left = -32767;
if (left > 32767) left = 32767;
if (right < -32767) right = -32767;
if (right > 32767) right = 32767;
*_pBuffer++ = left;
*_pBuffer++ = right;
}
}
void CUCode_AXWii::Update()
{
// check if we have to sent something
if (!m_rMailHandler.IsEmpty())
{
g_dspInitialize.pGenerateDSPInterrupt();
}
}
// Shortcut
void CUCode_AXWii::SaveLog(const char* _fmt, ...)
{
}
// AX seems to bootup one task only and waits for resume-callbacks
// everytime the DSP has "spare time" it sends a resume-mail to the CPU
// and the __DSPHandler calls a AX-Callback which generates a new AXFrame
bool CUCode_AXWii::AXTask(u32& _uMail)
{
u32 uAddress = _uMail;
SaveLog("Begin");
SaveLog("=====================================================================");
SaveLog("%08x: AXTask - AXCommandList-Addr", uAddress);
u32 Addr__AXStudio;
u32 Addr__AXOutSBuffer;
// u32 Addr__AXOutSBuffer_1;
// u32 Addr__AXOutSBuffer_2;
u32 Addr__A;
// u32 Addr__12;
u32 Addr__5_1;
u32 Addr__5_2;
u32 Addr__6;
// u32 Addr__9;
bool bExecuteList = true;
if (false)
{
// PanicAlert("%i", sizeof(AXParamBlockWii)); // 252 ??
FILE *f = fopen("D:\\axdump.txt", "a");
if (!f)
f = fopen("D:\\axdump.txt", "w");
u32 addr = uAddress;
for (int i = 0; i < 100; i++) {
fprintf(f, "%02x\n", Memory_Read_U16(addr + i * 2));
}
fprintf(f, "===========------------------------------------------------=\n");
fclose(f);
}
else
{
// PanicAlert("%i", sizeof(AXParamBlock)); // 192
}
while (bExecuteList)
{
static int last_valid_command = 0;
u16 iCommand = Memory_Read_U16(uAddress);
uAddress += 2;
switch (iCommand)
{
case 0x0000: //00
Addr__AXStudio = Memory_Read_U32(uAddress);
uAddress += 4;
SaveLog("%08x : AXLIST studio address: %08x", uAddress, Addr__AXStudio);
break;
case 0x0001:
{
u32 address = Memory_Read_U32(uAddress);
uAddress += 4;
SaveLog("%08x : AXLIST 1: %08x", uAddress, address);
}
break;
case 0x0003:
{
u32 address = Memory_Read_U32(uAddress);
uAddress += 4;
SaveLog("%08x : AXLIST 3: %08x", uAddress, address);
}
break;
case 0x0004: // PBs are here now
m_addressPBs = Memory_Read_U32(uAddress);
lCUCode_AX->m_addressPBs = m_addressPBs; // for the sake of logging
SaveLog("%08x : AXLIST PB address: %08x", uAddress, m_addressPBs);
uAddress += 4;
break;
case 0x0005:
if(Memory_Read_U16(uAddress) > 25) // this occured in Wii Sports
{
Addr__5_1 = Memory_Read_U32(uAddress);
uAddress += 4;
Addr__5_2 = Memory_Read_U32(uAddress);
uAddress += 4;
uAddress += 2;
SaveLog("%08x : AXLIST 5_1 5_2 addresses: %08x %08x", uAddress, Addr__5_1, Addr__5_2);
}
else
{
uAddress += 2;
SaveLog("%08x : AXLIST Empty 0x0005", uAddress);
}
break;
case 0x0006:
Addr__6 = Memory_Read_U32(uAddress);
uAddress += 10;
SaveLog("%08x : AXLIST 6 address: %08x", uAddress, Addr__6);
break;
/**/ case 0x0007: // AXLIST_SBUFFER
Addr__AXOutSBuffer = Memory_Read_U32(uAddress);
uAddress += 10;
// uAddress += 12;
SaveLog("%08x : AXLIST OutSBuffer (0x0007) address: %08x", uAddress, Addr__AXOutSBuffer);
break;
/* case 0x0009:
Addr__9 = Memory_Read_U32(uAddress);
uAddress += 4;
DebugLog("AXLIST 6 address: %08x", Addr__9);
break;*/
case 0x000a: // AXLIST_COMPRESSORTABLE
Addr__A = Memory_Read_U32(uAddress);
uAddress += 4;
//Addr__A = Memory_Read_U32(uAddress);
uAddress += 4;
SaveLog("%08x : AXLIST CompressorTable address: %08x", uAddress, Addr__A);
break;
case 0x000b:
uAddress += 2; // one 0x8000 in rabbids
uAddress += 4 * 2; // then two RAM addressses
break;
case 0x000c:
uAddress += 2; // one 0x8000 in rabbids
uAddress += 4 * 2; // then two RAM addressses
break;
case 0x000d:
uAddress += 4 * 4;
break;
case 0x000e:
// This is the end.
bExecuteList = false;
SaveLog("%08x : AXLIST end, wii stylee.", uAddress);
break;
default:
{
static bool bFirst = true;
if (bFirst == true)
{
// A little more descreet way to say that there is a problem, that also let you
// take a look at the mail (and possible previous mails) in the debugger
SaveLog("%08x : Unknown AX-Command 0x%04x", uAddress, iCommand);
bExecuteList = false;
break;
char szTemp[2048];
sprintf(szTemp, "Unknown AX-Command 0x%04x (address: 0x%08x). Last valid: %02x\n",
iCommand, uAddress - 2, last_valid_command);
int num = -32;
while (num < 64+32)
{
char szTemp2[128] = "";
sprintf(szTemp2, "%s0x%04x\n", num == 0 ? ">>" : " ", Memory_Read_U16(uAddress + num));
strcat(szTemp, szTemp2);
num += 2;
}
// Wii AX will always show this
PanicAlert(szTemp);
// bFirst = false;
}
// unknown command so stop the execution of this TaskList
bExecuteList = false;
}
break;
}
if (bExecuteList)
last_valid_command = iCommand;
}
SaveLog("AXTask - done, send resume");
SaveLog("=====================================================================");
SaveLog("End");
// i hope resume is okay AX
m_rMailHandler.PushMail(0xDCD10001);
return true;
}
// Copyright (C) 2003-2008 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 "UCodes.h"
#include "UCode_AXStructs.h"
#include "UCode_AX.h" // for some functions in CUCode_AX
#include "UCode_AXWii.h"
#include "UCode_AX_Voice.h"
// ------------------------------------------------------------------
// Declarations
// -----------
extern bool gSequenced;
// -----------
CUCode_AXWii::CUCode_AXWii(CMailHandler& _rMailHandler, u32 l_CRC)
: IUCode(_rMailHandler)
, m_addressPBs(0xFFFFFFFF)
, _CRC(l_CRC)
{
// we got loaded
m_rMailHandler.PushMail(0xDCD10000);
m_rMailHandler.PushMail(0x80000000); // handshake ??? only (crc == 0xe2136399) needs it ...
templbuffer = new int[1024 * 1024];
temprbuffer = new int[1024 * 1024];
lCUCode_AX = new CUCode_AX(_rMailHandler);
lCUCode_AX->_CRC = l_CRC;
}
CUCode_AXWii::~CUCode_AXWii()
{
m_rMailHandler.Clear();
delete [] templbuffer;
delete [] temprbuffer;
}
void CUCode_AXWii::HandleMail(u32 _uMail)
{
if ((_uMail & 0xFFFF0000) == MAIL_AX_ALIST)
{
// a new List
}
else
{
AXTask(_uMail);
}
}
void CUCode_AXWii::MixAdd(short* _pBuffer, int _iSize)
{
if(_CRC == 0xfa450138)
{
AXParamBlockWii PBs[NUMBER_OF_PBS];
MixAdd_( _pBuffer, _iSize, PBs);
}
else
{
AXParamBlockWii_ PBs[NUMBER_OF_PBS];
MixAdd_(_pBuffer, _iSize, PBs);
}
}
template<class ParamBlockType>
void CUCode_AXWii::MixAdd_(short* _pBuffer, int _iSize, ParamBlockType &PBs)
{
//AXParamBlockWii PBs[NUMBER_OF_PBS];
// read out pbs
int numberOfPBs = ReadOutPBsWii(m_addressPBs, PBs, NUMBER_OF_PBS);
if (_iSize > 1024 * 1024)
_iSize = 1024 * 1024;
// write zeroes to the beginning of templbuffer
memset(templbuffer, 0, _iSize * sizeof(int));
memset(temprbuffer, 0, _iSize * sizeof(int));
// -------------------------------------------
// write logging data to debugger
/* Make the updates we are told to do. See comments to the GC version in UCode_AX.cpp */
// ------------
for (int i = 0; i < numberOfPBs; i++)
{
u16 *pDest = (u16 *)&PBs[i];
u16 upd0 = pDest[41]; u16 upd1 = pDest[42]; u16 upd2 = pDest[43]; // num_updates
u16 upd_hi = pDest[44]; // update addr
u16 upd_lo = pDest[45];
int numupd = upd0 + upd1 + upd2;
if(numupd > 64) numupd = 64; // prevent to high values
const u32 updaddr = (u32)(upd_hi << 16) | upd_lo;
int on = false, off = false;
for (int j = 0; j < numupd; j++) // make alll updates
{
const u16 updpar = Memory_Read_U16(updaddr);
const u16 upddata = Memory_Read_U16(updaddr + 2);
// some safety checks, I hope it's enough
if( ( (updaddr > 0x80000000 && updaddr < 0x817fffff)
|| (updaddr > 0x90000000 && updaddr < 0x93ffffff) )
&& updpar < 127 && updpar > 3 && upddata >= 0 // updpar > 3 because we don't want to change
// 0-3, those are important
//&& (upd0 || upd1 || upd2) // We should use these in some way to I think
// but I don't know how or when
&& gSequenced) // on and off option
{
//PanicAlert("Update %i: %i = %04x", i, updpar, upddata);
//DebugLog("Update: %i = %04x", updpar, upddata);
pDest[updpar] = upddata;
}
if (updpar == 7 && upddata == 1) on++;
if (updpar == 7 && upddata == 1) off++;
}
// hack: if we get both an on and an off select on rather than off
if (on > 0 && off > 0) pDest[7] = 1;
}
//PrintFile(1, "%08x %04x %04x\n", updaddr, updpar, upddata);
// ------------
for (int i = 0; i < numberOfPBs; i++)
{
MixAddVoice(PBs[i], templbuffer, temprbuffer, _iSize, true);
}
WriteBackPBsWii(m_addressPBs, PBs, numberOfPBs);
// We write the sound to _pBuffer
for (int i = 0; i < _iSize; i++)
{
// Clamp into 16-bit. Maybe we should add a volume compressor here.
int left = templbuffer[i] + _pBuffer[0];
int right = temprbuffer[i] + _pBuffer[1];
if (left < -32767) left = -32767;
if (left > 32767) left = 32767;
if (right < -32767) right = -32767;
if (right > 32767) right = 32767;
*_pBuffer++ = left;
*_pBuffer++ = right;
}
}
void CUCode_AXWii::Update()
{
// check if we have to sent something
if (!m_rMailHandler.IsEmpty())
{
g_dspInitialize.pGenerateDSPInterrupt();
}
}
// Shortcut
void CUCode_AXWii::SaveLog(const char* _fmt, ...)
{
}
// AX seems to bootup one task only and waits for resume-callbacks
// everytime the DSP has "spare time" it sends a resume-mail to the CPU
// and the __DSPHandler calls a AX-Callback which generates a new AXFrame
bool CUCode_AXWii::AXTask(u32& _uMail)
{
u32 uAddress = _uMail;
SaveLog("Begin");
SaveLog("=====================================================================");
SaveLog("%08x: AXTask - AXCommandList-Addr", uAddress);
u32 Addr__AXStudio;
u32 Addr__AXOutSBuffer;
// u32 Addr__AXOutSBuffer_1;
// u32 Addr__AXOutSBuffer_2;
u32 Addr__A;
// u32 Addr__12;
u32 Addr__5_1;
u32 Addr__5_2;
u32 Addr__6;
// u32 Addr__9;
bool bExecuteList = true;
if (false)
{
// PanicAlert("%i", sizeof(AXParamBlockWii)); // 252 ??
FILE *f = fopen("D:\\axdump.txt", "a");
if (!f)
f = fopen("D:\\axdump.txt", "w");
u32 addr = uAddress;
for (int i = 0; i < 100; i++) {
fprintf(f, "%02x\n", Memory_Read_U16(addr + i * 2));
}
fprintf(f, "===========------------------------------------------------=\n");
fclose(f);
}
else
{
// PanicAlert("%i", sizeof(AXParamBlock)); // 192
}
while (bExecuteList)
{
static int last_valid_command = 0;
u16 iCommand = Memory_Read_U16(uAddress);
uAddress += 2;
switch (iCommand)
{
case 0x0000: //00
Addr__AXStudio = Memory_Read_U32(uAddress);
uAddress += 4;
SaveLog("%08x : AXLIST studio address: %08x", uAddress, Addr__AXStudio);
break;
case 0x0001:
{
u32 address = Memory_Read_U32(uAddress);
uAddress += 4;
}
break;
case 0x0003:
{
u32 address = Memory_Read_U32(uAddress);
uAddress += 4;
}
break;
case 0x0004: // PBs are here now
m_addressPBs = Memory_Read_U32(uAddress);
lCUCode_AX->m_addressPBs = m_addressPBs; // for the sake of logging
uAddress += 4;
break;
case 0x0005:
if(Memory_Read_U16(uAddress) > 25) // this occured in Wii Sports
{
Addr__5_1 = Memory_Read_U32(uAddress);
uAddress += 4;
Addr__5_2 = Memory_Read_U32(uAddress);
uAddress += 4;
uAddress += 2;
}
else
{
uAddress += 2;
SaveLog("%08x : AXLIST Empty 0x0005", uAddress);
}
break;
case 0x0006:
Addr__6 = Memory_Read_U32(uAddress);
uAddress += 10;
SaveLog("%08x : AXLIST 6 address: %08x", uAddress, Addr__6);
break;
/**/ case 0x0007: // AXLIST_SBUFFER
Addr__AXOutSBuffer = Memory_Read_U32(uAddress);
uAddress += 10;
// uAddress += 12;
SaveLog("%08x : AXLIST OutSBuffer (0x0007) address: %08x", uAddress, Addr__AXOutSBuffer);
break;
/* case 0x0009:
Addr__9 = Memory_Read_U32(uAddress);
uAddress += 4;
DebugLog("AXLIST 6 address: %08x", Addr__9);
break;*/
case 0x000a: // AXLIST_COMPRESSORTABLE
Addr__A = Memory_Read_U32(uAddress);
uAddress += 4;
//Addr__A = Memory_Read_U32(uAddress);
uAddress += 4;
SaveLog("%08x : AXLIST CompressorTable address: %08x", uAddress, Addr__A);
break;
case 0x000b:
uAddress += 2; // one 0x8000 in rabbids
uAddress += 4 * 2; // then two RAM addressses
break;
case 0x000c:
uAddress += 2; // one 0x8000 in rabbids
uAddress += 4 * 2; // then two RAM addressses
break;
case 0x000d:
uAddress += 4 * 4;
break;
case 0x000e:
// This is the end.
bExecuteList = false;
SaveLog("%08x : AXLIST end, wii stylee.", uAddress);
break;
default:
{
static bool bFirst = true;
if (bFirst == true)
{
// A little more descreet way to say that there is a problem, that also let you
// take a look at the mail (and possible previous mails) in the debugger
SaveLog("%08x : Unknown AX-Command 0x%04x", uAddress, iCommand);
bExecuteList = false;
break;
char szTemp[2048];
sprintf(szTemp, "Unknown AX-Command 0x%04x (address: 0x%08x). Last valid: %02x\n",
iCommand, uAddress - 2, last_valid_command);
int num = -32;
while (num < 64+32)
{
char szTemp2[128] = "";
sprintf(szTemp2, "%s0x%04x\n", num == 0 ? ">>" : " ", Memory_Read_U16(uAddress + num));
strcat(szTemp, szTemp2);
num += 2;
}
// Wii AX will always show this
PanicAlert(szTemp);
// bFirst = false;
}
// unknown command so stop the execution of this TaskList
bExecuteList = false;
}
break;
}
if (bExecuteList)
last_valid_command = iCommand;
}
SaveLog("AXTask - done, send resume");
SaveLog("=====================================================================");
SaveLog("End");
// i hope resume is okay AX
m_rMailHandler.PushMail(0xDCD10001);
return true;
}

View File

@ -1,64 +1,64 @@
// Copyright (C) 2003-2008 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
#define _UCODE_AXWII
#include "UCode_AXStructs.h"
#define NUMBER_OF_PBS 128
class CUCode_AXWii : public IUCode
{
public:
CUCode_AXWii(CMailHandler& _rMailHandler, u32 _CRC);
virtual ~CUCode_AXWii();
void HandleMail(u32 _uMail);
void MixAdd(short* _pBuffer, int _iSize);
template<class ParamBlockType>
//void Logging(short* _pBuffer, int _iSize, int a, bool Wii, ParamBlockType &PBs, int numberOfPBs);
void MixAdd_(short* _pBuffer, int _iSize, ParamBlockType &PBs);
void Update();
// The logging function for the debugger
void Logging(short* _pBuffer, int _iSize, int a);
CUCode_AX * lCUCode_AX; // we need the logging functions in there
private:
enum
{
MAIL_AX_ALIST = 0xBABE0000,
};
// PBs
u32 m_addressPBs;
u32 _CRC;
int *templbuffer;
int *temprbuffer;
// ax task message handler
bool AXTask(u32& _uMail);
void SaveLog(const char* _fmt, ...);
void SendMail(u32 _uMail);
};
//int ReadOutPBsWii(u32 pbs_address, AXParamBlockWii* _pPBs, int _num);
//void WriteBackPBsWii(u32 pbs_address, AXParamBlockWii* _pPBs, int _num);
#endif // _UCODE_AXWII
// Copyright (C) 2003-2008 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
#define _UCODE_AXWII
#include "UCode_AXStructs.h"
#define NUMBER_OF_PBS 128
class CUCode_AXWii : public IUCode
{
public:
CUCode_AXWii(CMailHandler& _rMailHandler, u32 _CRC);
virtual ~CUCode_AXWii();
void HandleMail(u32 _uMail);
void MixAdd(short* _pBuffer, int _iSize);
template<class ParamBlockType>
//void Logging(short* _pBuffer, int _iSize, int a, bool Wii, ParamBlockType &PBs, int numberOfPBs);
void MixAdd_(short* _pBuffer, int _iSize, ParamBlockType &PBs);
void Update();
// The logging function for the debugger
void Logging(short* _pBuffer, int _iSize, int a);
CUCode_AX * lCUCode_AX; // we need the logging functions in there
private:
enum
{
MAIL_AX_ALIST = 0xBABE0000,
};
// PBs
u32 m_addressPBs;
u32 _CRC;
int *templbuffer;
int *temprbuffer;
// ax task message handler
bool AXTask(u32& _uMail);
void SaveLog(const char* _fmt, ...);
void SendMail(u32 _uMail);
};
//int ReadOutPBsWii(u32 pbs_address, AXParamBlockWii* _pPBs, int _num);
//void WriteBackPBsWii(u32 pbs_address, AXParamBlockWii* _pPBs, int _num);
#endif // _UCODE_AXWII

View File

@ -1,92 +1,92 @@
// Copyright (C) 2003-2008 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 "../Globals.h"
inline s16 ADPCM_Step(PBADPCMInfo &adpcm, u32& samplePos, u32 newSamplePos, u16 frac)
{
while (samplePos < newSamplePos)
{
if ((samplePos & 15) == 0)
{
adpcm.pred_scale = g_dspInitialize.pARAM_Read_U8((samplePos & ~15) >> 1);
samplePos += 2;
newSamplePos += 2;
}
int scale = 1 << (adpcm.pred_scale & 0xF);
int coef_idx = adpcm.pred_scale >> 4;
s32 coef1 = adpcm.coefs[coef_idx * 2 + 0];
s32 coef2 = adpcm.coefs[coef_idx * 2 + 1];
int temp = (samplePos & 1) ?
(g_dspInitialize.pARAM_Read_U8(samplePos >> 1) & 0xF) :
(g_dspInitialize.pARAM_Read_U8(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;
}
// =======================================================================================
// Volume control (ramping)
// --------------
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
return x; // update volume
}
// ==============
#endif // _UCODE_AX_ADPCM_H
// Copyright (C) 2003-2008 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 "../Globals.h"
inline s16 ADPCM_Step(PBADPCMInfo &adpcm, u32& samplePos, u32 newSamplePos, u16 frac)
{
while (samplePos < newSamplePos)
{
if ((samplePos & 15) == 0)
{
adpcm.pred_scale = g_dspInitialize.pARAM_Read_U8((samplePos & ~15) >> 1);
samplePos += 2;
newSamplePos += 2;
}
int scale = 1 << (adpcm.pred_scale & 0xF);
int coef_idx = adpcm.pred_scale >> 4;
s32 coef1 = adpcm.coefs[coef_idx * 2 + 0];
s32 coef2 = adpcm.coefs[coef_idx * 2 + 1];
int temp = (samplePos & 1) ?
(g_dspInitialize.pARAM_Read_U8(samplePos >> 1) & 0xF) :
(g_dspInitialize.pARAM_Read_U8(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;
}
// =======================================================================================
// Volume control (ramping)
// --------------
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
return x; // update volume
}
// ==============
#endif // _UCODE_AX_ADPCM_H

View File

@ -1,390 +1,391 @@
// Copyright (C) 2003-2008 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_VOICE_H
#define _UCODE_AX_VOICE_H
#include "UCode_AX_ADPCM.h"
#include "UCode_AX.h"
// ----------------------------------------------------
// Externals
// -----------
extern bool gSSBM;
extern bool gSSBMremedy1;
extern bool gSSBMremedy2;
extern bool gSequenced;
extern bool gVolume;
extern bool gReset;
extern bool gSequenced;
extern float ratioFactor;
template<class ParamBlockType>
inline int ReadOutPBsWii(u32 pbs_address, ParamBlockType& _pPBs, int _num)
{
int count = 0;
u32 blockAddr = pbs_address;
u32 pAddr = 0;
// reading and 'halfword' swap
for (int i = 0; i < _num; i++)
{
const short *pSrc = (const short *)g_dspInitialize.pGetMemoryPointer(blockAddr);
pAddr = blockAddr;
if (pSrc != NULL)
{
short *pDest = (short *)&_pPBs[i];
for (u32 p = 0; p < sizeof(AXParamBlockWii) / 2; p++)
{
if(p == 6 || p == 7) pDest[p] = pSrc[p]; // control for the u32
else pDest[p] = Common::swap16(pSrc[p]);
}
_pPBs[i].mixer_control = Common::swap32(_pPBs[i].mixer_control);
blockAddr = (_pPBs[i].next_pb_hi << 16) | _pPBs[i].next_pb_lo;
count++;
// Detect the last mail by checking when next_pb = 0
u32 next_pb = (Common::swap16(pSrc[0]) << 16) | Common::swap16(pSrc[1]);
if(next_pb == 0) break;
}
else
break;
}
// return the number of read PBs
return count;
}
template<class ParamBlockType>
inline void WriteBackPBsWii(u32 pbs_address, ParamBlockType& _pPBs, int _num)
//void WriteBackPBsWii(u32 pbs_address, AXParamBlockWii* _pPBs, int _num)
{
u32 blockAddr = pbs_address;
// write back and 'halfword'swap
for (int i = 0; i < _num; i++)
{
short* pSrc = (short*)&_pPBs[i];
short* pDest = (short*)g_dspInitialize.pGetMemoryPointer(blockAddr);
_pPBs[i].mixer_control = Common::swap32(_pPBs[i].mixer_control);
for (size_t p = 0; p < sizeof(AXParamBlockWii) / 2; p++)
{
if(p == 6 || p == 7) pDest[p] = pSrc[p]; // control for the u32
else pDest[p] = Common::swap16(pSrc[p]);
}
// next block
blockAddr = (_pPBs[i].next_pb_hi << 16) | _pPBs[i].next_pb_lo;
}
}
template<class ParamBlockType>
inline void MixAddVoice(ParamBlockType &pb, int *templbuffer, int *temprbuffer, int _iSize, bool Wii)
{
DoVoiceHacks(pb, Wii);
// =============
if (pb.running)
{
// =======================================================================================
// Read initial parameters
// ------------
//constants
const u32 ratio = (u32)(((pb.src.ratio_hi << 16) + pb.src.ratio_lo) * ratioFactor);
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;
//variables
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.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;
frac += ratio;
u32 newSamplePos = samplePos + (frac >> 16); //whole number of frac
// =======================================================================================
// Process sample format
// --------------
switch (pb.audio_addr.sample_format)
{
case AUDIOFORMAT_PCM8:
// TODO - the linear interpolation code below is somewhat suspicious
pb.adpcm.yn2 = pb.adpcm.yn1; //save last sample
pb.adpcm.yn1 = ((s8)g_dspInitialize.pARAM_Read_U8(samplePos)) << 8;
if (pb.src_type == SRCTYPE_NEAREST)
{
sample = pb.adpcm.yn1;
}
else //linear interpolation
{
sample = (pb.adpcm.yn1 * (u16)frac + pb.adpcm.yn2 * (u16)(0xFFFF - frac)) >> 16;
}
samplePos = newSamplePos;
break;
case AUDIOFORMAT_PCM16:
// TODO - the linear interpolation code below is somewhat suspicious
pb.adpcm.yn2 = pb.adpcm.yn1; //save last sample
pb.adpcm.yn1 = (s16)(u16)((g_dspInitialize.pARAM_Read_U8(samplePos * 2) << 8) | (g_dspInitialize.pARAM_Read_U8((samplePos * 2 + 1))));
if (pb.src_type == SRCTYPE_NEAREST)
sample = pb.adpcm.yn1;
else //linear interpolation
sample = (pb.adpcm.yn1 * (u16)frac + pb.adpcm.yn2 * (u16)(0xFFFF - frac)) >> 16;
samplePos = newSamplePos;
break;
case AUDIOFORMAT_ADPCM:
sample = ADPCM_Step(pb.adpcm, samplePos, newSamplePos, 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.volume_left >> 5;
int rightmix = pb.mixer.volume_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)
{
samplePos = loopPos;
if (pb.audio_addr.sample_format == AUDIOFORMAT_ADPCM)
{
if (!pb.is_stream)
{
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;
break;
}
}
// ===============
} // end of the _iSize loop
// Update volume
//if (sizeof(ParamBlockType) == sizeof(AXParamBlock)) // this is not needed anymore I think
if (gVolume) // allow us to turn this off in the debugger
{
pb.mixer.volume_left = ADPCM_Vol(pb.mixer.volume_left, pb.mixer.unknown);
pb.mixer.volume_right = ADPCM_Vol(pb.mixer.volume_right, pb.mixer.unknown2);
}
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)
}
// ================================================
// Voice hacks
// --------------
template<class ParamBlockType>
inline void DoVoiceHacks(ParamBlockType &pb, bool Wii)
{
// get necessary values
const u32 sampleEnd = (pb.audio_addr.end_addr_hi << 16) | pb.audio_addr.end_addr_lo;
const u32 loopPos = (pb.audio_addr.loop_addr_hi << 16) | pb.audio_addr.loop_addr_lo;
const u32 updaddr = (u32)(pb.updates.data_hi << 16) | pb.updates.data_lo;
const u16 updpar = Memory_Read_U16(updaddr);
const u16 upddata = Memory_Read_U16(updaddr + 2);
// =======================================================================================
/* Fix problems introduced with the SSBM fix. Sometimes when a music stream ended sampleEnd
would end up outside of bounds while the block was still playing resulting in noise
a strange noise. This should take care of that.
*/
// ------------
if (
(sampleEnd > (0x017fffff * 2) || loopPos > (0x017fffff * 2)) // ARAM bounds in nibbles
&& gSSBMremedy1
&& !Wii
)
{
pb.running = 0;
// also reset all values if it makes any difference
pb.audio_addr.cur_addr_hi = 0; pb.audio_addr.cur_addr_lo = 0;
pb.audio_addr.end_addr_hi = 0; pb.audio_addr.end_addr_lo = 0;
pb.audio_addr.loop_addr_hi = 0; pb.audio_addr.loop_addr_lo = 0;
pb.src.cur_addr_frac = 0; pb.src.ratio_hi = 0; pb.src.ratio_lo = 0;
pb.adpcm.pred_scale = 0; pb.adpcm.yn1 = 0; pb.adpcm.yn2 = 0;
pb.audio_addr.looping = 0;
pb.adpcm_loop_info.pred_scale = 0;
pb.adpcm_loop_info.yn1 = 0; pb.adpcm_loop_info.yn2 = 0;
}
/*
// the fact that no settings are reset (except running) after a SSBM type music stream or another
looping block (for example in Battle Stadium DON) has ended could cause loud garbled sound to be
played from one or more blocks. Perhaps it was in conjunction with the old sequenced music fix below,
I'm not sure. This was an attempt to prevent that anyway by resetting all. But I'm not sure if this
is needed anymore. Please try to play SSBM without it and see if it works anyway.
*/
if (
// detect blocks that have recently been running that we should reset
pb.running == 0 && pb.audio_addr.looping == 1
//pb.running == 0 && pb.adpcm_loop_info.pred_scale
// this prevents us from ruining sequenced music blocks, may not be needed
/*
&& !(pb.updates.num_updates[0] || pb.updates.num_updates[1] || pb.updates.num_updates[2]
|| pb.updates.num_updates[3] || pb.updates.num_updates[4])
*/
&& !(updpar || upddata)
&& pb.mixer_control == 0 // only use this in SSBM
&& gSSBMremedy2 // let us turn this fix on and off
&& !Wii
)
{
// reset the detection values
pb.audio_addr.looping = 0;
pb.adpcm_loop_info.pred_scale = 0;
pb.adpcm_loop_info.yn1 = 0; pb.adpcm_loop_info.yn2 = 0;
//pb.audio_addr.cur_addr_hi = 0; pb.audio_addr.cur_addr_lo = 0;
//pb.audio_addr.end_addr_hi = 0; pb.audio_addr.end_addr_lo = 0;
//pb.audio_addr.loop_addr_hi = 0; pb.audio_addr.loop_addr_lo = 0;
//pb.src.cur_addr_frac = 0; PBs[i].src.ratio_hi = 0; PBs[i].src.ratio_lo = 0;
//pb.adpcm.pred_scale = 0; pb.adpcm.yn1 = 0; pb.adpcm.yn2 = 0;
}
// =============
// =======================================================================================
// Reset all values
// ------------
if (gReset
&& (pb.running || pb.audio_addr.looping || pb.adpcm_loop_info.pred_scale)
)
{
pb.running = 0;
pb.audio_addr.cur_addr_hi = 0; pb.audio_addr.cur_addr_lo = 0;
pb.audio_addr.end_addr_hi = 0; pb.audio_addr.end_addr_lo = 0;
pb.audio_addr.loop_addr_hi = 0; pb.audio_addr.loop_addr_lo = 0;
pb.src.cur_addr_frac = 0; pb.src.ratio_hi = 0; pb.src.ratio_lo = 0;
pb.adpcm.pred_scale = 0; pb.adpcm.yn1 = 0; pb.adpcm.yn2 = 0;
pb.audio_addr.looping = 0;
pb.adpcm_loop_info.pred_scale = 0;
pb.adpcm_loop_info.yn1 = 0; pb.adpcm_loop_info.yn2 = 0;
}
}
#endif // _UCODE_AX_VOICE_H
// Copyright (C) 2003-2008 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_VOICE_H
#define _UCODE_AX_VOICE_H
#include "UCode_AX_ADPCM.h"
#include "UCode_AX.h"
// ----------------------------------------------------
// Externals
// -----------
extern bool gSSBM;
extern bool gSSBMremedy1;
extern bool gSSBMremedy2;
extern bool gSequenced;
extern bool gVolume;
extern bool gReset;
extern bool gSequenced;
extern float ratioFactor;
template<class ParamBlockType>
inline int ReadOutPBsWii(u32 pbs_address, ParamBlockType& _pPBs, int _num)
{
int count = 0;
u32 blockAddr = pbs_address;
u32 pAddr = 0;
// reading and 'halfword' swap
for (int i = 0; i < _num; i++)
{
const short *pSrc = (const short *)g_dspInitialize.pGetMemoryPointer(blockAddr);
pAddr = blockAddr;
if (pSrc != NULL)
{
short *pDest = (short *)&_pPBs[i];
for (u32 p = 0; p < sizeof(AXParamBlockWii) / 2; p++)
{
if(p == 6 || p == 7) pDest[p] = pSrc[p]; // control for the u32
else pDest[p] = Common::swap16(pSrc[p]);
}
_pPBs[i].mixer_control = Common::swap32(_pPBs[i].mixer_control);
blockAddr = (_pPBs[i].next_pb_hi << 16) | _pPBs[i].next_pb_lo;
count++;
// Detect the last mail by checking when next_pb = 0
u32 next_pb = (Common::swap16(pSrc[0]) << 16) | Common::swap16(pSrc[1]);
if(next_pb == 0) break;
}
else
break;
}
// return the number of read PBs
return count;
}
template<class ParamBlockType>
inline void WriteBackPBsWii(u32 pbs_address, ParamBlockType& _pPBs, int _num)
//void WriteBackPBsWii(u32 pbs_address, AXParamBlockWii* _pPBs, int _num)
{
u32 blockAddr = pbs_address;
// write back and 'halfword'swap
for (int i = 0; i < _num; i++)
{
short* pSrc = (short*)&_pPBs[i];
short* pDest = (short*)g_dspInitialize.pGetMemoryPointer(blockAddr);
_pPBs[i].mixer_control = Common::swap32(_pPBs[i].mixer_control);
for (size_t p = 0; p < sizeof(AXParamBlockWii) / 2; p++)
{
if(p == 6 || p == 7) pDest[p] = pSrc[p]; // control for the u32
else pDest[p] = Common::swap16(pSrc[p]);
}
// next block
blockAddr = (_pPBs[i].next_pb_hi << 16) | _pPBs[i].next_pb_lo;
}
}
template<class ParamBlockType>
inline void MixAddVoice(ParamBlockType &pb, int *templbuffer, int *temprbuffer, int _iSize, bool Wii)
{
DoVoiceHacks(pb, Wii);
// =============
if (pb.running)
{
// =======================================================================================
// Read initial parameters
// ------------
//constants
const u32 ratio = (u32)(((pb.src.ratio_hi << 16) + pb.src.ratio_lo) * ratioFactor);
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;
//variables
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.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;
frac += ratio;
u32 newSamplePos = samplePos + (frac >> 16); //whole number of frac
// =======================================================================================
// Process sample format
// --------------
switch (pb.audio_addr.sample_format)
{
case AUDIOFORMAT_PCM8:
// TODO - the linear interpolation code below is somewhat suspicious
pb.adpcm.yn2 = pb.adpcm.yn1; //save last sample
pb.adpcm.yn1 = ((s8)g_dspInitialize.pARAM_Read_U8(samplePos)) << 8;
if (pb.src_type == SRCTYPE_NEAREST)
{
sample = pb.adpcm.yn1;
}
else //linear interpolation
{
sample = (pb.adpcm.yn1 * (u16)frac + pb.adpcm.yn2 * (u16)(0xFFFF - frac)) >> 16;
}
samplePos = newSamplePos;
break;
case AUDIOFORMAT_PCM16:
// TODO - the linear interpolation code below is somewhat suspicious
pb.adpcm.yn2 = pb.adpcm.yn1; //save last sample
pb.adpcm.yn1 = (s16)(u16)((g_dspInitialize.pARAM_Read_U8(samplePos * 2) << 8) | (g_dspInitialize.pARAM_Read_U8((samplePos * 2 + 1))));
if (pb.src_type == SRCTYPE_NEAREST)
sample = pb.adpcm.yn1;
else //linear interpolation
sample = (pb.adpcm.yn1 * (u16)frac + pb.adpcm.yn2 * (u16)(0xFFFF - frac)) >> 16;
samplePos = newSamplePos;
break;
case AUDIOFORMAT_ADPCM:
sample = ADPCM_Step(pb.adpcm, samplePos, newSamplePos, 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.volume_left >> 5;
int rightmix = pb.mixer.volume_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)
{
samplePos = loopPos;
if (pb.audio_addr.sample_format == AUDIOFORMAT_ADPCM)
{
if (!pb.is_stream)
{
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;
break;
}
}
// ===============
} // end of the _iSize loop
// Update volume
//if (sizeof(ParamBlockType) == sizeof(AXParamBlock)) // this is not needed anymore I think
if (gVolume) // allow us to turn this off in the debugger
{
pb.mixer.volume_left = ADPCM_Vol(pb.mixer.volume_left, pb.mixer.unknown);
pb.mixer.volume_right = ADPCM_Vol(pb.mixer.volume_right, pb.mixer.unknown2);
}
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)
}
// ================================================
// Voice hacks
// --------------
template<class ParamBlockType>
inline void DoVoiceHacks(ParamBlockType &pb, bool Wii)
{
// get necessary values
const u32 sampleEnd = (pb.audio_addr.end_addr_hi << 16) | pb.audio_addr.end_addr_lo;
const u32 loopPos = (pb.audio_addr.loop_addr_hi << 16) | pb.audio_addr.loop_addr_lo;
const u32 updaddr = (u32)(pb.updates.data_hi << 16) | pb.updates.data_lo;
const u16 updpar = Memory_Read_U16(updaddr);
const u16 upddata = Memory_Read_U16(updaddr + 2);
// =======================================================================================
/* Fix problems introduced with the SSBM fix. Sometimes when a music stream ended sampleEnd
would end up outside of bounds while the block was still playing resulting in noise
a strange noise. This should take care of that.
*/
// ------------
if (
(sampleEnd > (0x017fffff * 2) || loopPos > (0x017fffff * 2)) // ARAM bounds in nibbles
&& gSSBMremedy1
&& !Wii
)
{
pb.running = 0;
// also reset all values if it makes any difference
pb.audio_addr.cur_addr_hi = 0; pb.audio_addr.cur_addr_lo = 0;
pb.audio_addr.end_addr_hi = 0; pb.audio_addr.end_addr_lo = 0;
pb.audio_addr.loop_addr_hi = 0; pb.audio_addr.loop_addr_lo = 0;
pb.src.cur_addr_frac = 0; pb.src.ratio_hi = 0; pb.src.ratio_lo = 0;
pb.adpcm.pred_scale = 0; pb.adpcm.yn1 = 0; pb.adpcm.yn2 = 0;
pb.audio_addr.looping = 0;
pb.adpcm_loop_info.pred_scale = 0;
pb.adpcm_loop_info.yn1 = 0; pb.adpcm_loop_info.yn2 = 0;
}
/*
// the fact that no settings are reset (except running) after a SSBM type music stream or another
looping block (for example in Battle Stadium DON) has ended could cause loud garbled sound to be
played from one or more blocks. Perhaps it was in conjunction with the old sequenced music fix below,
I'm not sure. This was an attempt to prevent that anyway by resetting all. But I'm not sure if this
is needed anymore. Please try to play SSBM without it and see if it works anyway.
*/
if (
// detect blocks that have recently been running that we should reset
pb.running == 0 && pb.audio_addr.looping == 1
//pb.running == 0 && pb.adpcm_loop_info.pred_scale
// this prevents us from ruining sequenced music blocks, may not be needed
/*
&& !(pb.updates.num_updates[0] || pb.updates.num_updates[1] || pb.updates.num_updates[2]
|| pb.updates.num_updates[3] || pb.updates.num_updates[4])
*/
&& !(updpar || upddata)
&& pb.mixer_control == 0 // only use this in SSBM
&& gSSBMremedy2 // let us turn this fix on and off
&& !Wii
)
{
// reset the detection values
pb.audio_addr.looping = 0;
pb.adpcm_loop_info.pred_scale = 0;
pb.adpcm_loop_info.yn1 = 0; pb.adpcm_loop_info.yn2 = 0;
//pb.audio_addr.cur_addr_hi = 0; pb.audio_addr.cur_addr_lo = 0;
//pb.audio_addr.end_addr_hi = 0; pb.audio_addr.end_addr_lo = 0;
//pb.audio_addr.loop_addr_hi = 0; pb.audio_addr.loop_addr_lo = 0;
//pb.src.cur_addr_frac = 0; PBs[i].src.ratio_hi = 0; PBs[i].src.ratio_lo = 0;
//pb.adpcm.pred_scale = 0; pb.adpcm.yn1 = 0; pb.adpcm.yn2 = 0;
}
// =============
// =======================================================================================
// Reset all values
// ------------
if (gReset
&& (pb.running || pb.audio_addr.looping || pb.adpcm_loop_info.pred_scale)
)
{
pb.running = 0;
pb.audio_addr.cur_addr_hi = 0; pb.audio_addr.cur_addr_lo = 0;
pb.audio_addr.end_addr_hi = 0; pb.audio_addr.end_addr_lo = 0;
pb.audio_addr.loop_addr_hi = 0; pb.audio_addr.loop_addr_lo = 0;
pb.src.cur_addr_frac = 0; pb.src.ratio_hi = 0; pb.src.ratio_lo = 0;
pb.adpcm.pred_scale = 0; pb.adpcm.yn1 = 0; pb.adpcm.yn2 = 0;
pb.audio_addr.looping = 0;
pb.adpcm_loop_info.pred_scale = 0;
pb.adpcm_loop_info.yn1 = 0; pb.adpcm_loop_info.yn2 = 0;
}
}
#endif // _UCODE_AX_VOICE_H