SPU2ghz: Significant overhaul of the volume system! Volumes for most games should be much more accurate now, and distortion greatly reduced. This is still a work-in-progress, and I intend to follow up with another commit soon that should improve performance in a few areas, improve overall audio quality and (hopefully!) fix up the Special Effects system as well.

Pcsx2: This time the Pg icon really has been replaced/reverted to the original (dunno why my last attempt didn't commit, oh well).  Also improved the syntax of the SourceLogs; fixing if/else statement scoping problems.

git-svn-id: http://pcsx2.googlecode.com/svn/trunk@470 96395faa-99c1-11dd-bbfe-3dabce05a288
This commit is contained in:
Jake.Stine 2009-02-11 07:20:14 +00:00
parent 9ee9d817c4
commit d5bc5099d1
13 changed files with 508 additions and 527 deletions

View File

@ -78,72 +78,72 @@ extern u32 varLog;
void SourceLog( u16 protocol, u8 source, u32 cpuPc, u32 cpuCycle, const char *fmt, ...); void SourceLog( u16 protocol, u8 source, u32 cpuPc, u32 cpuCycle, const char *fmt, ...);
void __Log( const char* fmt, ... ); void __Log( const char* fmt, ... );
extern void SrcLog_CPU( const char* fmt, ... ); extern bool SrcLog_CPU( const char* fmt, ... );
extern void SrcLog_COP0( const char* fmt, ... ); extern bool SrcLog_COP0( const char* fmt, ... );
extern void SrcLog_FPU( const char* fmt, ... ); extern bool SrcLog_FPU( const char* fmt, ... );
extern void SrcLog_MMI( const char* fmt, ... ); extern bool SrcLog_MMI( const char* fmt, ... );
extern void SrcLog_MEM( const char* fmt, ... ); extern bool SrcLog_MEM( const char* fmt, ... );
extern void SrcLog_HW( const char* fmt, ... ); extern bool SrcLog_HW( const char* fmt, ... );
extern void SrcLog_DMA( const char* fmt, ... ); extern bool SrcLog_DMA( const char* fmt, ... );
extern void SrcLog_BIOS( const char* fmt, ... ); extern bool SrcLog_BIOS( const char* fmt, ... );
extern void SrcLog_ELF( const char* fmt, ... ); extern bool SrcLog_ELF( const char* fmt, ... );
extern void SrcLog_VU0( const char* fmt, ... ); extern bool SrcLog_VU0( const char* fmt, ... );
extern void SrcLog_VIF( const char* fmt, ... ); extern bool SrcLog_VIF( const char* fmt, ... );
extern void SrcLog_SPR( const char* fmt, ... ); extern bool SrcLog_SPR( const char* fmt, ... );
extern void SrcLog_GIF( const char* fmt, ... ); extern bool SrcLog_GIF( const char* fmt, ... );
extern void SrcLog_SIF( const char* fmt, ... ); extern bool SrcLog_SIF( const char* fmt, ... );
extern void SrcLog_IPU( const char* fmt, ... ); extern bool SrcLog_IPU( const char* fmt, ... );
extern void SrcLog_VUM( const char* fmt, ... ); extern bool SrcLog_VUM( const char* fmt, ... );
extern void SrcLog_RPC( const char* fmt, ... ); extern bool SrcLog_RPC( const char* fmt, ... );
extern void SrcLog_EECNT( const char* fmt, ... ); extern bool SrcLog_EECNT( const char* fmt, ... );
extern void SrcLog_PSXCPU( const char* fmt, ... ); extern bool SrcLog_PSXCPU( const char* fmt, ... );
extern void SrcLog_PSXMEM( const char* fmt, ... ); extern bool SrcLog_PSXMEM( const char* fmt, ... );
extern void SrcLog_PSXHW( const char* fmt, ... ); extern bool SrcLog_PSXHW( const char* fmt, ... );
extern void SrcLog_PSXBIOS( const char* fmt, ... ); extern bool SrcLog_PSXBIOS( const char* fmt, ... );
extern void SrcLog_PSXDMA( const char* fmt, ... ); extern bool SrcLog_PSXDMA( const char* fmt, ... );
extern void SrcLog_PSXCNT( const char* fmt, ... ); extern bool SrcLog_PSXCNT( const char* fmt, ... );
extern void SrcLog_MEMCARDS( const char* fmt, ... ); extern bool SrcLog_MEMCARDS( const char* fmt, ... );
extern void SrcLog_PAD( const char* fmt, ... ); extern bool SrcLog_PAD( const char* fmt, ... );
extern void SrcLog_GTE( const char* fmt, ... ); extern bool SrcLog_GTE( const char* fmt, ... );
extern void SrcLog_CDR( const char* fmt, ... ); extern bool SrcLog_CDR( const char* fmt, ... );
extern void SrcLog_GPU( const char* fmt, ... ); extern bool SrcLog_GPU( const char* fmt, ... );
#define CPU_LOG if (varLog & 0x00000001) SrcLog_CPU #define CPU_LOG (varLog & 0x00000001) && SrcLog_CPU
#define MEM_LOG if (varLog & 0x00000002) SrcLog_MEM #define MEM_LOG (varLog & 0x00000002) && SrcLog_MEM
#define HW_LOG if (varLog & 0x00000004) SrcLog_HW #define HW_LOG (varLog & 0x00000004) && SrcLog_HW
#define DMA_LOG if (varLog & 0x00000008) SrcLog_DMA #define DMA_LOG (varLog & 0x00000008) && SrcLog_DMA
#define BIOS_LOG if (varLog & 0x00000010) SrcLog_BIOS #define BIOS_LOG (varLog & 0x00000010) && SrcLog_BIOS
#define ELF_LOG if (varLog & 0x00000020) SrcLog_ELF #define ELF_LOG (varLog & 0x00000020) && SrcLog_ELF
#define FPU_LOG if (varLog & 0x00000040) SrcLog_FPU #define FPU_LOG (varLog & 0x00000040) && SrcLog_FPU
#define MMI_LOG if (varLog & 0x00000080) SrcLog_MMI #define MMI_LOG (varLog & 0x00000080) && SrcLog_MMI
#define VU0_LOG if (varLog & 0x00000100) SrcLog_VU0 #define VU0_LOG (varLog & 0x00000100) && SrcLog_VU0
#define COP0_LOG if (varLog & 0x00000200) SrcLog_COP0 #define COP0_LOG (varLog & 0x00000200) && SrcLog_COP0
#define VIF_LOG if (varLog & 0x00000400) SrcLog_VIF #define VIF_LOG (varLog & 0x00000400) && SrcLog_VIF
#define SPR_LOG if (varLog & 0x00000800) SrcLog_SPR #define SPR_LOG (varLog & 0x00000800) && SrcLog_SPR
#define GIF_LOG if (varLog & 0x00001000) SrcLog_GIF #define GIF_LOG (varLog & 0x00001000) && SrcLog_GIF
#define SIF_LOG if (varLog & 0x00002000) SrcLog_SIF #define SIF_LOG (varLog & 0x00002000) && SrcLog_SIF
#define IPU_LOG if (varLog & 0x00004000) SrcLog_IPU #define IPU_LOG (varLog & 0x00004000) && SrcLog_IPU
#define VUM_LOG if (varLog & 0x00008000) SrcLog_VUM #define VUM_LOG (varLog & 0x00008000) && SrcLog_VUM
#define RPC_LOG if (varLog & 0x00010000) SrcLog_RPC #define RPC_LOG (varLog & 0x00010000) && SrcLog_RPC
#define EECNT_LOG if (varLog & 0x40000000) SrcLog_EECNT #define EECNT_LOG (varLog & 0x40000000) && SrcLog_EECNT
#define PSXCPU_LOG if (varLog & 0x00100000) SrcLog_PSXCPU #define PSXCPU_LOG (varLog & 0x00100000) && SrcLog_PSXCPU
#define PSXMEM_LOG if (varLog & 0x00200000) SrcLog_PSXMEM #define PSXMEM_LOG (varLog & 0x00200000) && SrcLog_PSXMEM
#define PSXHW_LOG if (varLog & 0x00400000) SrcLog_PSXHW #define PSXHW_LOG (varLog & 0x00400000) && SrcLog_PSXHW
#define PSXBIOS_LOG if (varLog & 0x00800000) SrcLog_PSXBIOS #define PSXBIOS_LOG (varLog & 0x00800000) && SrcLog_PSXBIOS
#define PSXDMA_LOG if (varLog & 0x01000000) SrcLog_PSXDMA #define PSXDMA_LOG (varLog & 0x01000000) && SrcLog_PSXDMA
#define PSXCNT_LOG if (varLog & 0x20000000) SrcLog_PSXCNT #define PSXCNT_LOG (varLog & 0x20000000) && SrcLog_PSXCNT
//memcard has the same number as PAD_LOG for now //memcard has the same number as PAD_LOG for now
#define MEMCARDS_LOG if (varLog & 0x02000000) SrcLog_MEMCARDS #define MEMCARDS_LOG (varLog & 0x02000000) && SrcLog_MEMCARDS
#define PAD_LOG if (varLog & 0x02000000) SrcLog_PAD #define PAD_LOG (varLog & 0x02000000) && SrcLog_PAD
#define GTE_LOG if (varLog & 0x04000000) SrcLog_GTE #define GTE_LOG (varLog & 0x04000000) && SrcLog_GTE
#define CDR_LOG if (varLog & 0x08000000) SrcLog_CDR #define CDR_LOG (varLog & 0x08000000) && SrcLog_CDR
#define GPU_LOG if (varLog & 0x10000000) SrcLog_GPU #define GPU_LOG (varLog & 0x10000000) && SrcLog_GPU
// fixme - currently we don't log cache // fixme - currently we don't log cache
#define CACHE_LOG 0&& #define CACHE_LOG 0&&

View File

@ -67,6 +67,7 @@ u8 sio_xor(u8 *buf, uint length){
void sioInit() void sioInit()
{ {
memzero_obj(sio); memzero_obj(sio);
memzero_obj(m_PostSavestateCards);
// Transfer(?) Ready and the Buffer is Empty // Transfer(?) Ready and the Buffer is Empty
sio.StatReg = TX_RDY | TX_EMPTY; sio.StatReg = TX_RDY | TX_EMPTY;

View File

@ -129,7 +129,7 @@ void SourceLog( u16 protocol, u8 source, u32 cpuPc, u32 cpuCycle, const char *fm
// Functions with variable argument lists can't be inlined. // Functions with variable argument lists can't be inlined.
#define IMPLEMENT_SOURCE_LOG( unit, source, protocol ) \ #define IMPLEMENT_SOURCE_LOG( unit, source, protocol ) \
void SrcLog_##unit( const char* fmt, ... ) \ bool SrcLog_##unit( const char* fmt, ... ) \
{ \ { \
va_list list; \ va_list list; \
va_start( list, fmt ); \ va_start( list, fmt ); \
@ -137,6 +137,7 @@ void SourceLog( u16 protocol, u8 source, u32 cpuPc, u32 cpuCycle, const char *fm
(source == 'E') ? cpuRegs.pc : psxRegs.pc, \ (source == 'E') ? cpuRegs.pc : psxRegs.pc, \
(source == 'E') ? cpuRegs.cycle : psxRegs.cycle, fmt, list ); \ (source == 'E') ? cpuRegs.cycle : psxRegs.cycle, fmt, list ); \
va_end( list ); \ va_end( list ); \
return false; \
} \ } \
IMPLEMENT_SOURCE_LOG( EECNT, 'E', 0 ) IMPLEMENT_SOURCE_LOG( EECNT, 'E', 0 )

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

After

Width:  |  Height:  |  Size: 13 KiB

View File

@ -26,11 +26,42 @@ void ConLog(const char *fmt, ...);
void DoFullDump(); void DoFullDump();
extern int wavedump_ok; namespace WaveDump
{
enum CoreSourceType
{
// Core's input stream, usually pulled from ADMA streams.
CoreSrc_Input = 0
int wavedump_open(); // Output of the actual 24 input voices which have dry output enabled.
void wavedump_close(); , CoreSrc_DryVoiceMix
void wavedump_write(s16 left,s16 right);
// Output of the actual 24 input voices that have wet output enabled.
, CoreSrc_WetVoiceMix
// Wet mix including inputs and externals, prior to the application of reverb.
, CoreSrc_PreReverb
// Wet mix after reverb has turned it into a pile of garbly gook.
, CoreSrc_PostReverb
// Final output of the core. For core 0, it's the feed into Core1.
// For Core1, it's the feed into SndOut.
, CoreSrc_External
, CoreSrc_Count
};
void Open();
void Close();
void WriteCore( uint coreidx, CoreSourceType src, s16 left, s16 right );
}
using WaveDump::CoreSrc_Input;
using WaveDump::CoreSrc_DryVoiceMix;
using WaveDump::CoreSrc_WetVoiceMix;
using WaveDump::CoreSrc_PreReverb;
using WaveDump::CoreSrc_PostReverb;
using WaveDump::CoreSrc_External;
#endif // DEBUG_H_INCLUDED // #endif // DEBUG_H_INCLUDED //

View File

@ -180,14 +180,6 @@ EXPORT_C_(s32) SPU2init()
DMALogOpen(); DMALogOpen();
if(WaveLog())
{
if(!wavedump_open())
{
SysMessage("Can't open '%s'.\nWave Log disabled.",WaveLogFileName);
}
}
for(v=0;v<16384;v++) for(v=0;v<16384;v++)
{ {
logvolume[v]=(s32)(s32)floor(log((double)(v+1))*3376.7); logvolume[v]=(s32)(s32)floor(log((double)(v+1))*3376.7);
@ -236,6 +228,8 @@ EXPORT_C_(s32) SPU2open(void *pDsp)
spdif_init(); spdif_init();
DspLoadLibrary(dspPlugin,dspPluginModule); DspLoadLibrary(dspPlugin,dspPluginModule);
WaveDump::Open();
return 0; return 0;
} }
@ -280,7 +274,7 @@ EXPORT_C_(void) SPU2shutdown()
fclose(el0); fclose(el0);
fclose(el1); fclose(el1);
#endif #endif
if(WaveLog() && wavedump_ok) wavedump_close(); WaveDump::Close();
DMALogClose(); DMALogClose();
@ -452,18 +446,9 @@ EXPORT_C_(int) SPU2setupRecording(int start, void* pData)
if( disableFreezes ) return 0; if( disableFreezes ) return 0;
if(start==0) if(start==0)
{
//stop recording
RecordStop(); RecordStop();
if(recording==0)
return 1;
}
else if(start==1) else if(start==1)
{
//start recording
RecordStart(); RecordStart();
if(recording!=0)
return 1;
}
return 0; return 0;
} }

View File

@ -25,11 +25,17 @@
// //
#include "spu2.h" #include "spu2.h"
#include <assert.h>
#include <math.h>
#include <float.h> #include <float.h>
#include "lowpass.h" #include "lowpass.h"
#undef min
#undef max
#include <algorithm>
using std::min;
using std::max;
extern void spdif_update(); extern void spdif_update();
void ADMAOutLogWrite(void *lpData, u32 ulSize); void ADMAOutLogWrite(void *lpData, u32 ulSize);
@ -39,19 +45,54 @@ extern void VoiceStop(int core,int vc);
double pow_2_31 = pow(2.0,31.0); double pow_2_31 = pow(2.0,31.0);
LPF_data L,R; LPF_data L,R;
extern u32 core; extern u32 core;
u32 core, voice; u32 core, voice;
extern u8 callirq; extern u8 callirq;
double srate_pv=1.0; double srate_pv=1.0;
extern u32 PsxRates[160];
static const s32 ADSR_MAX_VOL = 0x7fffffff; static const s32 ADSR_MAX_VOL = 0x7fffffff;
static const s32 f[5][2] =
{
{ 0, 0 },
{ 60, 0 },
{ 115, -52 },
{ 98, -55 },
{ 122, -60 }
};
static const int InvExpOffsets[] = { 0,4,6,8,9,10,11,12 };
static u32 PsxRates[160];
/*=
{
//for +Lin: PsxRates[value+8]
//for -Lin: PsxRates[value+7]
0xFFFFFFFF,0xFFFFFFFF,0xFFFFFFFF,0xFFFFFFFF,0xFFFFFFFF,0xFFFFFFFF,0xFFFFFFFF,0xFFFFFFFF,
0xD744FCCB,0xB504F334,0x9837F052,0x80000000,0x6BA27E65,0x5A82799A,0x4C1BF829,0x40000000,
0x35D13F33,0x2D413CCD,0x260DFC14,0x20000000,0x1AE89F99,0x16A09E66,0x1306FE0A,0x10000000,
0x0D744FCD,0x0B504F33,0x09837F05,0x08000000,0x06BA27E6,0x05A8279A,0x04C1BF83,0x04000000,
0x035D13F3,0x02D413CD,0x0260DFC1,0x02000000,0x01AE89FA,0x016A09E6,0x01306FE1,0x01000000,
0x00D744FD,0x00B504F3,0x009837F0,0x00800000,0x006BA27E,0x005A827A,0x004C1BF8,0x00400000,
0x0035D13F,0x002D413D,0x00260DFC,0x00200000,0x001AE8A0,0x0016A09E,0x001306FE,0x00100000,
0x000D7450,0x000B504F,0x0009837F,0x00080000,0x0006BA28,0x0005A828,0x0004C1C0,0x00040000,
0x00035D14,0x0002D414,0x000260E0,0x00020000,0x0001AE8A,0x00016A0A,0x00013070,0x00010000,
0x0000D745,0x0000B505,0x00009838,0x00008000,0x00006BA2,0x00005A82,0x00004C1C,0x00004000,
0x000035D1,0x00002D41,0x0000260E,0x00002000,0x00001AE9,0x000016A1,0x00001307,0x00001000,
0x00000D74,0x00000B50,0x00000983,0x00000800,0x000006BA,0x000005A8,0x000004C2,0x00000400,
0x0000035D,0x000002D4,0x00000261,0x00000200,0x000001AF,0x0000016A,0x00000130,0x00000100,
0x000000D7,0x000000B5,0x00000098,0x00000080,0x0000006C,0x0000005B,0x0000004C,0x00000040,
0x00000036,0x0000002D,0x00000026,0x00000020,0x0000001B,0x00000017,0x00000013,0x00000010,
0x0000000D,0x0000000B,0x0000000A,0x00000008,0x00000007,0x00000006,0x00000005,0x00000004,
0x00000003,0x00000003,0x00000002,0x00000002,0x00000002,0x00000001,0x00000001,0x00000000,
//128+8
0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,
};*/
// Performs a 64-bit multiplication between two values and returns the // Performs a 64-bit multiplication between two values and returns the
// high 32 bits as a result (discarding the fractional 32 bits). // high 32 bits as a result (discarding the fractional 32 bits).
// The combined fracional bits of both inputs must be 32 bits for this // The combined fracional bits of both inputs must be 32 bits for this
@ -89,26 +130,16 @@ void InitADSR() // INIT ADSR
int shift=(i-32)>>2; int shift=(i-32)>>2;
s64 rate=(i&3)+4; s64 rate=(i&3)+4;
if (shift<0) if (shift<0)
rate>>=-shift; rate >>= -shift;
else else
rate<<=shift; rate <<= shift;
PsxRates[i]=(int)min(rate,0x3fffffff); PsxRates[i] = (int)min( rate, 0x3fffffffLL );
} }
} }
#define VOL(x) (((s32)x)) //24.8 volume #define VOL(x) (((s32)x)) //24.8 volume
/////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////
// //
const s32 f[5][2] ={{ 0, 0 },
{ 60, 0 },
{ 115, -52 },
{ 98, -55 },
{ 122, -60 }};
static void __forceinline XA_decode_block(s16* buffer, const s16* block, s32& prev1, s32& prev2) static void __forceinline XA_decode_block(s16* buffer, const s16* block, s32& prev1, s32& prev2)
{ {
const s32 header = *block; const s32 header = *block;
@ -306,11 +337,16 @@ _skipIncrement:
Data = vc.SBuffer[vc.SCurrent++]; Data = vc.SBuffer[vc.SCurrent++];
} }
///////////////////////////////////////////////////////////////////////////////////////// // Returns the linear slide value for AR and SR inputs.
///////////////////////////////////////////////////////////////////////////////////////// static int GetLinearSrAr( uint SrAr )
// // {
// The Sr/Ar settings work in quarter steps, which means
// the bottom 2 bits go on the left side of the shift, and
// the right side of the shift gets divided by 4:
const int InvExpOffsets[] = { 0,4,6,8,9,10,11,12 }; const uint newSr = 0x127 - SrAr;
return (1+(newSr&3)) << (30UL-(newSr>>2));
}
static void __forceinline CalculateADSR( V_Voice& vc ) static void __forceinline CalculateADSR( V_Voice& vc )
{ {
@ -318,17 +354,8 @@ static void __forceinline CalculateADSR( V_Voice& vc )
jASSUME( env.Phase != 0 ); jASSUME( env.Phase != 0 );
s32 SLevel = ((s32)env.Sl)<<27; if(env.Releasing && (env.Phase < 5))
env.Phase = 5;
jASSUME( SLevel >= 0 );
if(env.Releasing)
{
if( env.Phase < 5)
{
env.Phase=5;
}
}
switch (env.Phase) switch (env.Phase)
{ {
@ -340,21 +367,14 @@ static void __forceinline CalculateADSR( V_Voice& vc )
break; break;
} }
if (env.Am) // pseudo exponential // Case 1 below is for pseudo exponential below 75%.
{ // Pseudo Exp > 75% and Linear are the same.
if (env.Value<0x60000000) // below 75%
{ if (env.Am && (env.Value>=0x60000000))
env.Value+=PsxRates[(env.Ar^0x7f)-0x10+32]; env.Value += PsxRates[(env.Ar^0x7f)-0x18+32];
} else if( env.Ar < 0x7f )
else // above 75% //env.Value+=PsxRates[(env.Ar^0x7f)-0x10+32];
{ env.Value += GetLinearSrAr( env.Ar );
env.Value+=PsxRates[(env.Ar^0x7f)-0x18+32];
}
}
else // linear
{
env.Value+=PsxRates[(env.Ar^0x7f)-0x10+32];
}
if( env.Value < 0 ) if( env.Value < 0 )
{ {
@ -362,29 +382,37 @@ static void __forceinline CalculateADSR( V_Voice& vc )
env.Phase++; env.Phase++;
env.Value = ADSR_MAX_VOL; env.Value = ADSR_MAX_VOL;
} }
break;
break;
case 2: // decay case 2: // decay
{ {
u32 off = InvExpOffsets[(env.Value>>28)&7]; u32 off = InvExpOffsets[(env.Value>>28)&7];
env.Value-=PsxRates[((env.Dr^0x1f)<<2)-0x18+off+32]; env.Value-=PsxRates[((env.Dr^0x1f)*4)-0x18+off+32];
if(env.Value <= SLevel) // Clamp decay to SLevel or Zero
if (env.Value < 0)
env.Value = 0;
else
{ {
// Clamp decay to SLevel or Zero // calculate sustain level by mirroring the bits
if (env.Value < 0) // of the sustain var into the lower bits as we shift up
env.Value = 0; // (total shift, 27 bits)
else
env.Value = SLevel; u32 suslev = (env.Sl << 4) | env.Sl;
suslev = (suslev << 8) | suslev; // brings us to 12 bits!
suslev = (suslev << 12) | suslev; // 24 bits!
env.Phase++; if( env.Value <= (suslev<<3) )
} env.Phase++;
}
break;
} }
break;
case 3: // sustain case 3: // sustain
{
// 0x7f disables sustain (infinite sustain)
if( env.Sr == 0x7f ) return;
if (env.Sm&2) // decreasing if (env.Sm&2) // decreasing
{ {
if (env.Sm&4) // exponential if (env.Sm&4) // exponential
@ -394,8 +422,10 @@ static void __forceinline CalculateADSR( V_Voice& vc )
} }
else // linear else // linear
{ {
env.Value-=PsxRates[(env.Sr^0x7f)-0xf+32]; //env.Value-=PsxRates[(env.Sr^0x7f)-0xf+32];
env.Value -= GetLinearSrAr( env.Sr );
} }
if( env.Value <= 0 ) if( env.Value <= 0 )
{ {
env.Value = 0; env.Value = 0;
@ -404,22 +434,15 @@ static void __forceinline CalculateADSR( V_Voice& vc )
} }
else // increasing else // increasing
{ {
if (env.Sm&4) // pseudo exponential if( (env.Sm&4) && (env.Value>=0x60000000) )
{ env.Value+=PsxRates[(env.Sr^0x7f)-0x18+32];
if (env.Value<0x60000000) // below 75%
{
env.Value+=PsxRates[(env.Sr^0x7f)-0x10+32];
}
else // above 75%
{
env.Value+=PsxRates[(env.Sr^0x7f)-0x18+32];
}
}
else else
{ {
// linear // linear / Pseudo below 75% (they're the same)
//env.Value+=PsxRates[(env.Sr^0x7f)-0x10+32];
env.Value+=PsxRates[(env.Sr^0x7f)-0x10+32]; int newSr = 0x7f-env.Sr;
env.Value += GetLinearSrAr( env.Sr );
} }
if( env.Value < 0 ) if( env.Value < 0 )
@ -428,8 +451,8 @@ static void __forceinline CalculateADSR( V_Voice& vc )
env.Phase++; env.Phase++;
} }
} }
}
break; break;
case 4: // sustain end case 4: // sustain end
env.Value = (env.Sm&2) ? 0 : ADSR_MAX_VOL; env.Value = (env.Sm&2) ? 0 : ADSR_MAX_VOL;
@ -442,11 +465,13 @@ static void __forceinline CalculateADSR( V_Voice& vc )
if (env.Rm) // exponential if (env.Rm) // exponential
{ {
u32 off=InvExpOffsets[(env.Value>>28)&7]; u32 off=InvExpOffsets[(env.Value>>28)&7];
env.Value-=PsxRates[((env.Rr^0x1f)<<2)-0x18+off+32]; env.Value-=PsxRates[((env.Rr^0x1f)*4)-0x18+off+32];
} }
else // linear else // linear
{ {
env.Value-=PsxRates[((env.Rr^0x1f)<<2)-0xc+32]; //env.Value-=PsxRates[((env.Rr^0x1f)*4)-0xc+32];
if( env.Rr != 0x1f )
env.Value -= 1 << (30UL-env.Rr);
} }
if( env.Value <= 0 ) if( env.Value <= 0 )
@ -454,12 +479,11 @@ static void __forceinline CalculateADSR( V_Voice& vc )
env.Value=0; env.Value=0;
env.Phase++; env.Phase++;
} }
break;
break;
case 6: // release end case 6: // release end
env.Value=0; env.Value=0;
break; break;
jNO_DEFAULT jNO_DEFAULT
} }
@ -482,7 +506,7 @@ static void __forceinline GetNoiseValues(s32& VD)
{ {
static s32 Seed = 0x41595321; static s32 Seed = 0x41595321;
if(Seed&0x100) VD =(s32)((Seed&0xff)<<8); if(Seed&0x100) VD = (s32)((Seed&0xff)<<8);
else if(!(Seed&0xffff)) VD = (s32)0x8000; else if(!(Seed&0xffff)) VD = (s32)0x8000;
else VD = (s32)0x7fff; else VD = (s32)0x7fff;
@ -515,12 +539,11 @@ void LowPass(s32& VL, s32& VR)
VR = (s32)(LPF(&R,(VR)/pow_2_31)*pow_2_31); VR = (s32)(LPF(&R,(VR)/pow_2_31)*pow_2_31);
} }
///////////////////////////////////////////////////////////////////////////////////////// // Data is expected to be 16 bit signed (typical stuff!).
///////////////////////////////////////////////////////////////////////////////////////// // Volume is the SPU2 register value, usually ranged from 0 to 0x7fff.
// //
static __forceinline s32 ApplyVolume(s32 data, s32 volume) static __forceinline s32 ApplyVolume(s32 data, s32 volume)
{ {
return (volume * data) >> 7; // >> 6 is more correct, but causes a few overflows return (volume * data) >> 15;
} }
static void __forceinline UpdatePitch( V_Voice& vc ) static void __forceinline UpdatePitch( V_Voice& vc )
@ -532,13 +555,14 @@ static void __forceinline UpdatePitch( V_Voice& vc )
// most of the time. Now it'll just check Modulated and short-circuit past the voice // most of the time. Now it'll just check Modulated and short-circuit past the voice
// check (not that it amounts to much, but eh every little bit helps). // check (not that it amounts to much, but eh every little bit helps).
if( (vc.Modulated==0) || (voice==0) ) if( (vc.Modulated==0) || (voice==0) )
pitch=vc.Pitch; pitch = vc.Pitch;
else else
pitch=(vc.Pitch*(32768 + abs(Cores[core].Voices[voice-1].OutX)))>>15; pitch = (vc.Pitch*(32768 + abs(Cores[core].Voices[voice-1].OutX)))>>15;
vc.SP+=pitch; vc.SP+=pitch;
} }
// Returns a 16 bit result in Value.
static void __forceinline GetVoiceValues_Linear(V_Core& thiscore, V_Voice& vc, s32& Value) static void __forceinline GetVoiceValues_Linear(V_Core& thiscore, V_Voice& vc, s32& Value)
{ {
while( vc.SP > 0 ) while( vc.SP > 0 )
@ -560,19 +584,22 @@ static void __forceinline GetVoiceValues_Linear(V_Core& thiscore, V_Voice& vc, s
jASSUME( vc.ADSR.Value >= 0 ); // ADSR should never be negative... jASSUME( vc.ADSR.Value >= 0 ); // ADSR should never be negative...
// Note! It's very important that ADSR stay as accurate as possible. By the way
// it is used, various sound effects can end prematurely if we truncate more than
// one or two bits.
if(Interpolation==0) if(Interpolation==0)
{ {
Value = MulShr32( vc.PV1, vc.ADSR.Value ); Value = MulShr32( vc.PV1<<1, vc.ADSR.Value );
} }
else //if(Interpolation==1) //must be linear else //if(Interpolation==1) //must be linear
{ {
s32 t0 = vc.PV2 - vc.PV1; s32 t0 = vc.PV2 - vc.PV1;
s32 t1 = vc.PV1; Value = MulShr32( (vc.PV1<<1) - ((t0*vc.SP)>>11), vc.ADSR.Value );
Value = MulShr32( t1 - ((t0*vc.SP)>>12), vc.ADSR.Value );
} }
} }
// Returns a 16 bit result in Value.
static void __forceinline GetVoiceValues_Cubic(V_Core& thiscore, V_Voice& vc, s32& Value) static void __forceinline GetVoiceValues_Cubic(V_Core& thiscore, V_Voice& vc, s32& Value)
{ {
while( vc.SP > 0 ) while( vc.SP > 0 )
@ -582,7 +609,7 @@ static void __forceinline GetVoiceValues_Cubic(V_Core& thiscore, V_Voice& vc, s3
vc.PV2=vc.PV1; vc.PV2=vc.PV1;
GetNextDataBuffered( thiscore, vc, vc.PV1 ); GetNextDataBuffered( thiscore, vc, vc.PV1 );
vc.PV1<<=3; vc.PV1<<=2;
vc.SPc = vc.SP&4095; // just the fractional part, please! vc.SPc = vc.SP&4095; // just the fractional part, please!
vc.SP-=4096; vc.SP-=4096;
} }
@ -608,11 +635,15 @@ static void __forceinline GetVoiceValues_Cubic(V_Core& thiscore, V_Voice& vc, s3
val = ((val + z2) * mu) >> 12; val = ((val + z2) * mu) >> 12;
val += vc.PV2; val += vc.PV2;
Value = MulShr32( val, vc.ADSR.Value>>3 ); // Note! It's very important that ADSR stay as accurate as possible. By the way
// it is used, various sound effects can end prematurely if we truncate more than
// one or two bits.
Value = MulShr32( val, vc.ADSR.Value>>1 );
} }
// [Air]: Noise values need to be mixed without going through interpolation, since it // Noise values need to be mixed without going through interpolation, since it
// can wreak havoc on the noise (causing muffling or popping). // can wreak havoc on the noise (causing muffling or popping). Not that this noise
// generator is accurate in its own right.. but eh, ah well :)
static void __forceinline __fastcall GetNoiseValues(V_Core& thiscore, V_Voice& vc, s32& Data) static void __forceinline __fastcall GetNoiseValues(V_Core& thiscore, V_Voice& vc, s32& Data)
{ {
while(vc.SP>=4096) while(vc.SP>=4096)
@ -622,10 +653,13 @@ static void __forceinline __fastcall GetNoiseValues(V_Core& thiscore, V_Voice& v
} }
// GetNoiseValues can't set the phase zero on us unexpectedly // GetNoiseValues can't set the phase zero on us unexpectedly
// like GetVoiceValues can. Better asster just in case though.. // like GetVoiceValues can. Better assert just in case though..
jASSUME( vc.ADSR.Phase != 0 ); jASSUME( vc.ADSR.Phase != 0 );
CalculateADSR( vc ); CalculateADSR( vc );
// Yup, ADSR applies even to noise sources...
Data = MulShr32( Data, vc.ADSR.Value );
} }
///////////////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////////////////
@ -642,7 +676,11 @@ void __fastcall ReadInput(V_Core& thiscore, s32& PDataL,s32& PDataR)
{ {
thiscore.InputPos&=~1; thiscore.InputPos&=~1;
//CDDA mode // CDDA mode
// Source audio data is 32 bits.
// We don't yet have the capability to handle this high res input data
// so we just downgrade it to 16 bits for now.
#ifdef PCM24_S1_INTERLEAVE #ifdef PCM24_S1_INTERLEAVE
*PDataL=*(((s32*)(thiscore.ADMATempBuffer+(thiscore.InputPos<<1)))); *PDataL=*(((s32*)(thiscore.ADMATempBuffer+(thiscore.InputPos<<1))));
*PDataR=*(((s32*)(thiscore.ADMATempBuffer+(thiscore.InputPos<<1)+2))); *PDataR=*(((s32*)(thiscore.ADMATempBuffer+(thiscore.InputPos<<1)+2)));
@ -653,8 +691,8 @@ void __fastcall ReadInput(V_Core& thiscore, s32& PDataL,s32& PDataR)
PDataR=*pr; PDataR=*pr;
#endif #endif
PDataL>>=4; //give 16.8 data PDataL>>=1; //give 31 bit data (SndOut downsamples the rest of the way)
PDataR>>=4; PDataR>>=1;
thiscore.InputPos+=2; thiscore.InputPos+=2;
if((thiscore.InputPos==0x100)||(thiscore.InputPos>=0x200)) { if((thiscore.InputPos==0x100)||(thiscore.InputPos>=0x200)) {
@ -821,11 +859,6 @@ static void __forceinline __fastcall ReadInputPV(V_Core& thiscore, s32& ValL,s32
// Apply volumes: // Apply volumes:
ValL = ApplyVolume( ValL, thiscore.InpL ); ValL = ApplyVolume( ValL, thiscore.InpL );
ValR = ApplyVolume( ValR, thiscore.InpR ); ValR = ApplyVolume( ValR, thiscore.InpR );
// These make XS2 intro too quiet.
//ValL >>= 1;
//ValR >>= 1;
} }
///////////////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////////////////
@ -839,72 +872,46 @@ static void __forceinline __fastcall ReadInputPV(V_Core& thiscore, s32& ValL,s32
static void __fastcall UpdateVolume(V_Volume& Vol) static void __fastcall UpdateVolume(V_Volume& Vol)
{ {
// TIMINGS ARE FAKE!!! Need to investigate. // TODO : Vol.Value should be a 32 bit internal value, same as ADSR.
// [Air]: Cleaned up this code... may have broken it. Can't really
// test it here since none of my games seem to use it. If anything's
// not sounding right, we should revert the code in this method first.
// [Air] Reverse phasing?
// Invert our value so that exponential mathematics are applied
// as if the volume were sliding the other direction. This makes
// a lot more sense than the old method's likeliness to chop off
// sound volumes to zero abruptly.
if(Vol.Mode & VOLFLAG_REVERSE_PHASE)
{
ConLog( " *** SPU2 > Reverse Phase in progress!\n" );
Vol.Value = 0x7fff - Vol.Value;
}
// Volume slides use the same basic logic as ADSR, but simplified (single-stage
// instead of multi-stage)
if (Vol.Mode & VOLFLAG_DECREMENT) if (Vol.Mode & VOLFLAG_DECREMENT)
{ {
// Decrement // Decrement
if(Vol.Mode & VOLFLAG_EXPONENTIAL) if(Vol.Mode & VOLFLAG_EXPONENTIAL)
{ {
ConLog( " *** SPU2 > Exponential Volume Slide Down!\n" ); u32 off = InvExpOffsets[(Vol.Value>>12)&7];
Vol.Value *= Vol.Increment >> 7; Vol.Value -= PsxRates[(Vol.Increment^0x7f)-0x1b+off+32] >> 16;
Vol.Value-=((32768*5)>>(Vol.Increment));
} }
else else
{ Vol.Value -= Vol.Increment;
Vol.Value-=Vol.Increment;
}
if (Vol.Value<0) if (Vol.Value < 0)
{ {
Vol.Value = 0; Vol.Value = 0;
Vol.Mode=0; // disable slide Vol.Mode = 0; // disable slide
} }
} }
else else
{ {
//ConLog( " *** SPU2 > Volflag > Increment!\n" );
// Increment // Increment
if(Vol.Mode & VOLFLAG_EXPONENTIAL) // Pseudo-exponential increments, as done by the SPU2 (really!)
{ // Above 75% slides slow, below 75% slides fast. It's exponential, pseudoly speaking.
ConLog( " *** SPU2 > Exponential Volume Slide Up!\n" );
int T = Vol.Increment>>(Vol.Value>>12);
Vol.Value+=T;
}
else
{
Vol.Value+=Vol.Increment;
}
if( Vol.Value > 0x7fff ) if( (Vol.Mode & VOLFLAG_EXPONENTIAL) && (Vol.Value>=0x6000))
Vol.Value += PsxRates[(Vol.Increment^0x7f)-0x18+32] >> 16;
else
Vol.Value += Vol.Increment;
if( Vol.Value < 0 ) // wrapped around the "top"?
{ {
Vol.Value = 0x7fff; Vol.Value = 0x7fff;
Vol.Mode=0; // disable slide Vol.Mode = 0; // disable slide
} }
} }
// Reverse phasing
// Invert the value back into output form:
if(Vol.Mode & VOLFLAG_REVERSE_PHASE) Vol.Value = 0x7fff-Vol.Value;
//Vol.Value=NVal;
} }
///////////////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////////////////
@ -1022,17 +1029,16 @@ static __forceinline void MixVoice( V_Core& thiscore, V_Voice& vc, s32& VValL, s
{ {
s32 Value=0; s32 Value=0;
VValL=0; VValL = 0;
VValR=0; VValR = 0;
// [Air] : Most games don't use much volume slide effects. So only // Most games don't use much volume slide effects. So only call the UpdateVolume
// call the UpdateVolume methods when needed by checking the flag // methods when needed by checking the flag outside the method here...
// outside the method here...
if( vc.VolumeL.Mode & VOLFLAG_SLIDE_ENABLE ) UpdateVolume( vc.VolumeL ); if( vc.VolumeL.Mode & VOLFLAG_SLIDE_ENABLE ) UpdateVolume( vc.VolumeL );
if( vc.VolumeR.Mode & VOLFLAG_SLIDE_ENABLE ) UpdateVolume( vc.VolumeR ); if( vc.VolumeR.Mode & VOLFLAG_SLIDE_ENABLE ) UpdateVolume( vc.VolumeR );
if (vc.ADSR.Phase>0) if( vc.ADSR.Phase > 0 )
{ {
UpdatePitch( vc ); UpdatePitch( vc );
@ -1053,12 +1059,17 @@ static __forceinline void MixVoice( V_Core& thiscore, V_Voice& vc, s32& VValL, s
DebugCores[core].Voices[voice].displayPeak = max(DebugCores[core].Voices[voice].displayPeak,abs(Value)); DebugCores[core].Voices[voice].displayPeak = max(DebugCores[core].Voices[voice].displayPeak,abs(Value));
#endif #endif
VValL=ApplyVolume(Value,(vc.VolumeL.Value)); // TODO : Implement this using high-def MulShr32.
VValR=ApplyVolume(Value,(vc.VolumeR.Value)); // vc.VolumeL/R are 15 bits. Value should be 32 bits (but is currently 16)
VValL = ApplyVolume(Value,vc.VolumeL.Value);
VValR = ApplyVolume(Value,vc.VolumeR.Value);
} }
if (voice==1) spu2M_WriteFast( 0x400 + (core<<12) + OutPos, (s16)Value ); // Write-back of raw voice data (post ADSR applied)
else if (voice==3) spu2M_WriteFast( 0x600 + (core<<12) + OutPos, (s16)Value );
if (voice==1) spu2M_WriteFast( 0x400 + (core<<12) + OutPos, (s16)Value>>3 );
else if (voice==3) spu2M_WriteFast( 0x600 + (core<<12) + OutPos, (s16)Value>>3 );
} }
@ -1071,29 +1082,42 @@ static void __fastcall MixCore(s32& OutL, s32& OutR, s32 ExtL, s32 ExtR)
V_Core& thiscore( Cores[core] ); V_Core& thiscore( Cores[core] );
for (voice=0;voice<24;voice++) for (voice=0;voice<24;voice++)
{ {
s32 VValL,VValR; s32 VValL,VValR;
V_Voice& vc( thiscore.Voices[voice] ); V_Voice& vc( thiscore.Voices[voice] );
MixVoice( thiscore, vc, VValL, VValR ); MixVoice( thiscore, vc, VValL, VValR );
// Note: Results from MixVoice are ranged at 16 bits.
// Following muls are toggles only (0 or 1)
SDL += VValL * vc.DryL; SDL += VValL * vc.DryL;
SDR += VValR * vc.DryR; SDR += VValR * vc.DryR;
SWL += VValL * vc.WetL; SWL += VValL * vc.WetL;
SWR += VValR * vc.WetR; SWR += VValR * vc.WetR;
} }
//Write To Output Area // Saturate final result to standard 16 bit range.
spu2M_WriteFast( 0x1000 + (core<<12) + OutPos, (s16)(SDL>>16) ); SDL = min( max( SDL, -0x8000 ), 0x7fff );
spu2M_WriteFast( 0x1200 + (core<<12) + OutPos, (s16)(SDR>>16) ); SDR = min( max( SDR, -0x8000 ), 0x7fff );
spu2M_WriteFast( 0x1400 + (core<<12) + OutPos, (s16)(SWL>>16) ); SWL = min( max( SWL, -0x8000 ), 0x7fff );
spu2M_WriteFast( 0x1600 + (core<<12) + OutPos, (s16)(SWR>>16) ); SWR = min( max( SWR, -0x8000 ), 0x7fff );
// Write Mixed results To Output Area
spu2M_WriteFast( 0x1000 + (core<<12) + OutPos, (s16)SDL );
spu2M_WriteFast( 0x1200 + (core<<12) + OutPos, (s16)SDR );
spu2M_WriteFast( 0x1400 + (core<<12) + OutPos, (s16)SWL );
spu2M_WriteFast( 0x1600 + (core<<12) + OutPos, (s16)SWR );
// Write mixed results to logfile (if enabled)
WaveDump::WriteCore( core, CoreSrc_DryVoiceMix, SDL, SDR );
WaveDump::WriteCore( core, CoreSrc_WetVoiceMix, SWL, SWR );
s32 TDL,TDR; s32 TDL,TDR;
// Mix in the Input data // Mix in the Input data
// divide by 3 fixes some volume problems.
TDL = OutL * thiscore.InpDryL; TDL = OutL * thiscore.InpDryL;
TDR = OutR * thiscore.InpDryR; TDR = OutR * thiscore.InpDryR;
@ -1107,7 +1131,7 @@ static void __fastcall MixCore(s32& OutL, s32& OutR, s32 ExtL, s32 ExtR)
if(EffectsEnabled) if(EffectsEnabled)
{ {
s32 TWL=0,TWR=0; s32 TWL,TWR;
// Mix Input, Voice, and External data: // Mix Input, Voice, and External data:
TWL = OutL * thiscore.InpWetL; TWL = OutL * thiscore.InpWetL;
@ -1117,15 +1141,19 @@ static void __fastcall MixCore(s32& OutL, s32& OutR, s32 ExtL, s32 ExtR)
TWL += ExtL * thiscore.ExtWetL; TWL += ExtL * thiscore.ExtWetL;
TWR += ExtR * thiscore.ExtWetR; TWR += ExtR * thiscore.ExtWetR;
//Apply Effects WaveDump::WriteCore( core, CoreSrc_PreReverb, TWL, TWR );
DoReverb( thiscore, RVL,RVR,TWL>>16,TWR>>16);
TWL=ApplyVolume(RVL,VOL(thiscore.FxL)); //Apply Effects
TWR=ApplyVolume(RVR,VOL(thiscore.FxR)); DoReverb( thiscore, RVL, RVR, TWL, TWR );
TWL = ApplyVolume(RVL,VOL(thiscore.FxL));
TWR = ApplyVolume(RVR,VOL(thiscore.FxR));
WaveDump::WriteCore( core, CoreSrc_PostReverb, TWL, TWR );
//Mix Wet,Dry //Mix Wet,Dry
OutL=(TDL + TWL); OutL = (TDL + TWL);
OutR=(TDR + TWR); OutR = (TDR + TWR);
} }
else else
{ {
@ -1139,8 +1167,11 @@ static void __fastcall MixCore(s32& OutL, s32& OutR, s32 ExtL, s32 ExtR)
if (thiscore.Mute==0) if (thiscore.Mute==0)
{ {
OutL = MulShr32( OutL, ((s32)thiscore.MasterL.Value)<<16 ); // Final output value -- We don't use ApplyVolume sot hat we can leave the 15 bits of
OutR = MulShr32( OutR, ((s32)thiscore.MasterR.Value)<<16 ); // fixed point accuracy in place for SoundTouch and other post processing.
OutL = OutL * thiscore.MasterL.Value;
OutR = OutR * thiscore.MasterR.Value;
} }
else else
{ {
@ -1159,10 +1190,11 @@ void __fastcall Mix()
// **** CORE ZERO **** // **** CORE ZERO ****
core=0; core=0;
if( (PlayMode&4) != 4 ) if( (PlayMode&4) == 0 )
{ {
// get input data from input buffers // get input data from input buffers
ReadInputPV(Cores[0], ExtL, ExtR); ReadInputPV(Cores[0], ExtL, ExtR);
WaveDump::WriteCore( 0, CoreSrc_Input, ExtL, ExtR );
} }
MixCore( ExtL, ExtR, 0, 0 ); MixCore( ExtL, ExtR, 0, 0 );
@ -1174,10 +1206,16 @@ void __fastcall Mix()
} }
// Commit Core 0 output to ram before mixing Core 1: // Commit Core 0 output to ram before mixing Core 1:
ExtL>>=14; ExtL>>=15;
ExtR>>=14; ExtR>>=15;
spu2M_WriteFast( 0x800 + OutPos, ExtL>>3 );
spu2M_WriteFast( 0xA00 + OutPos, ExtR>>3 ); ExtL = min( max( ExtL, -0x8000 ), 0x7fff );
ExtR = min( max( ExtR, -0x8000 ), 0x7fff );
spu2M_WriteFast( 0x800 + OutPos, ExtL );
spu2M_WriteFast( 0xA00 + OutPos, ExtR );
WaveDump::WriteCore( 0, CoreSrc_External, ExtL, ExtR );
// **** CORE ONE **** // **** CORE ONE ****
@ -1185,15 +1223,20 @@ void __fastcall Mix()
if( (PlayMode&8) != 8 ) if( (PlayMode&8) != 8 )
{ {
ReadInputPV(Cores[1], OutL, OutR); // get input data from input buffers ReadInputPV(Cores[1], OutL, OutR); // get input data from input buffers
WaveDump::WriteCore( 1, CoreSrc_Input, OutL, OutR );
} }
// Apply volume to the external (Core 0) input data. // Apply volume to the external (Core 0) input data.
MixCore( OutL, OutR, ExtL*Cores[1].ExtL, ExtR*Cores[1].ExtR ); MixCore( OutL, OutR, ApplyVolume( ExtL, Cores[1].ExtL), ApplyVolume( ExtR, Cores[1].ExtR) );
if( PlayMode & 8 ) if( PlayMode & 8 )
{ {
// Experimental CDDA support
// The CDDA overrides all other mixer output. It's a direct feed!
ReadInput(Cores[1], OutL, OutR); ReadInput(Cores[1], OutL, OutR);
//WaveLog::WriteCore( 1, "CDDA-32", OutL, OutR );
} }
#ifndef PUBLIC #ifndef PUBLIC
@ -1206,12 +1249,13 @@ void __fastcall Mix()
// Update spdif (called each sample) // Update spdif (called each sample)
if(PlayMode&4) if(PlayMode&4)
{
spdif_update(); spdif_update();
}
OutL >>= 6;
OutR >>= 6;
// AddToBuffer // AddToBuffer
SndWrite(OutL, OutR); //ExtL,ExtR); SndWrite(OutL, OutR);
OutPos++; OutPos++;
if (OutPos>=0x200) OutPos=0; if (OutPos>=0x200) OutPos=0;
@ -1233,36 +1277,6 @@ void __fastcall Mix()
#endif #endif
} }
/////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////
// //
u32 PsxRates[160]={
//for +Lin: PsxRates[value+8]
//for -Lin: PsxRates[value+7]
0xFFFFFFFF,0xFFFFFFFF,0xFFFFFFFF,0xFFFFFFFF,0xFFFFFFFF,0xFFFFFFFF,0xFFFFFFFF,0xFFFFFFFF,
0xD744FCCB,0xB504F334,0x9837F052,0x80000000,0x6BA27E65,0x5A82799A,0x4C1BF829,0x40000000,
0x35D13F33,0x2D413CCD,0x260DFC14,0x20000000,0x1AE89F99,0x16A09E66,0x1306FE0A,0x10000000,
0x0D744FCD,0x0B504F33,0x09837F05,0x08000000,0x06BA27E6,0x05A8279A,0x04C1BF83,0x04000000,
0x035D13F3,0x02D413CD,0x0260DFC1,0x02000000,0x01AE89FA,0x016A09E6,0x01306FE1,0x01000000,
0x00D744FD,0x00B504F3,0x009837F0,0x00800000,0x006BA27E,0x005A827A,0x004C1BF8,0x00400000,
0x0035D13F,0x002D413D,0x00260DFC,0x00200000,0x001AE8A0,0x0016A09E,0x001306FE,0x00100000,
0x000D7450,0x000B504F,0x0009837F,0x00080000,0x0006BA28,0x0005A828,0x0004C1C0,0x00040000,
0x00035D14,0x0002D414,0x000260E0,0x00020000,0x0001AE8A,0x00016A0A,0x00013070,0x00010000,
0x0000D745,0x0000B505,0x00009838,0x00008000,0x00006BA2,0x00005A82,0x00004C1C,0x00004000,
0x000035D1,0x00002D41,0x0000260E,0x00002000,0x00001AE9,0x000016A1,0x00001307,0x00001000,
0x00000D74,0x00000B50,0x00000983,0x00000800,0x000006BA,0x000005A8,0x000004C2,0x00000400,
0x0000035D,0x000002D4,0x00000261,0x00000200,0x000001AF,0x0000016A,0x00000130,0x00000100,
0x000000D7,0x000000B5,0x00000098,0x00000080,0x0000006C,0x0000005B,0x0000004C,0x00000040,
0x00000036,0x0000002D,0x00000026,0x00000020,0x0000001B,0x00000017,0x00000013,0x00000010,
0x0000000D,0x0000000B,0x0000000A,0x00000008,0x00000007,0x00000006,0x00000005,0x00000004,
0x00000003,0x00000003,0x00000002,0x00000002,0x00000002,0x00000001,0x00000001,0x00000000,
//128+8
0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,
};
///////////////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////////////////
// // // //

View File

@ -131,7 +131,7 @@ EXPORT_C_(s32) SPU2freeze(int mode, freezeData *data)
{ {
printf("\n*** SPU2Ghz Warning:\n"); printf("\n*** SPU2Ghz Warning:\n");
printf("\tSavestate version is from an older version of this plugin.\n"); printf("\tSavestate version is from an older version of this plugin.\n");
printf("\tAudio may not recover correctly."); printf("\tAudio may not recover correctly.\n\n");
const PcmCacheEntry* pcmSrc = &spud->cacheData; const PcmCacheEntry* pcmSrc = &spud->cacheData;
int blksLoaded=0; int blksLoaded=0;

View File

@ -693,15 +693,12 @@ void SndUpdateLimitMode()
s32 SndWrite(s32 ValL, s32 ValR) s32 SndWrite(s32 ValL, s32 ValR)
{ {
// Log final output to wavefile.
#ifndef PUBLIC #ifndef PUBLIC
if(WaveLog() && wavedump_ok) WaveDump::WriteCore( 1, CoreSrc_External, SndScaleVol(ValL), SndScaleVol(ValR) );
{
wavedump_write(SndScaleVol(ValL),SndScaleVol(ValR));
}
#endif #endif
if(recording!=0) RecordWrite(SndScaleVol(ValL),SndScaleVol(ValR));
RecordWrite(SndScaleVol(ValL),SndScaleVol(ValR));
if(mods[OutputModule] == &NullOut) // null output doesn't need buffering or stretching! :p if(mods[OutputModule] == &NullOut) // null output doesn't need buffering or stretching! :p
return 0; return 0;

View File

@ -23,7 +23,7 @@
// SndOut. // SndOut.
static const int SndOutPacketSize = 1024; static const int SndOutPacketSize = 1024;
static const int SndOutVolumeShiftBase = 8; static const int SndOutVolumeShiftBase = 10;
extern int SndOutVolumeShift; extern int SndOutVolumeShift;
#define pcmlog #define pcmlog

View File

@ -155,11 +155,8 @@ void AssignVolume(V_Volume& vol, s16 value)
else { else {
vol.Mode=0; vol.Mode=0;
vol.Increment=0; vol.Increment=0;
vol.Value=value<<1;
value<<=1; }
vol.Value=value;
}
} }
void CoreReset(int c) void CoreReset(int c)
@ -173,10 +170,10 @@ void CoreReset(int c)
Cores[c].Regs.STATX=0; Cores[c].Regs.STATX=0;
Cores[c].Regs.ATTR=0; Cores[c].Regs.ATTR=0;
Cores[c].ExtL=0x3FFF; Cores[c].ExtL=0x7FFF;
Cores[c].ExtR=0x3FFF; Cores[c].ExtR=0x7FFF;
Cores[c].InpL=0x3FFF; Cores[c].InpL=0x7FFF;
Cores[c].InpR=0x3FFF; Cores[c].InpR=0x7FFF;
Cores[c].FxL=0x7FFF; Cores[c].FxL=0x7FFF;
Cores[c].FxR=0x7FFF; Cores[c].FxR=0x7FFF;
Cores[c].MasterL.Reg_VOL=0x3FFF; Cores[c].MasterL.Reg_VOL=0x3FFF;
@ -519,7 +516,8 @@ void UpdateSpdifMode()
} }
if(OPM!=PlayMode) if(OPM!=PlayMode)
{ {
ConLog(" * SPU2: Play Mode Set to %s (%d).\n",(PlayMode==0)?"Normal":((PlayMode==1)?"PCM Clone":((PlayMode==2)?"PCM Bypass":"BitStream Bypass")),PlayMode); ConLog(" * SPU2: Play Mode Set to %s (%d).\n",
(PlayMode==0) ? "Normal" : ((PlayMode==1) ? "PCM Clone" : ((PlayMode==2) ? "PCM Bypass" : "BitStream Bypass")),PlayMode);
} }
} }
@ -748,19 +746,24 @@ void SPU2writeLog(u32 rmem, u16 value)
omem=mem=rmem & 0x7FF; //FFFF; omem=mem=rmem & 0x7FF; //FFFF;
if (mem & 0x400) { omem^=0x400; core=1; } if (mem & 0x400) { omem^=0x400; core=1; }
/* if( omem < 0x0180 ) // Voice Params (VP)
if ((omem >= 0x0000) && (omem < 0x0180)) { // Voice Params {
u32 voice=(omem & 0x1F0) >> 4; const u32 voice = (omem & 0x1F0) >> 4;
u32 param=(omem & 0xF)>>1; const u32 param = (omem & 0xF) >> 1;
FileLog("[%10d] SPU2 write mem %08x (Core %d Voice %d Param %s) value %x\n",Cycles,rmem,core,voice,ParamNames[param],value); char dest[192];
sprintf( dest, "Voice %d %s", voice,ParamNames[param] );
RegLog( 2, dest, rmem, core, value );
} }
else if ((omem >= 0x01C0) && (omem < 0x02DE)) { else if ((omem >= 0x01C0) && (omem < 0x02DE)) // Voice Addressing Params (VA)
u32 voice =((omem-0x01C0) / 12); {
u32 address =((omem-0x01C0) % 12)>>1; const u32 voice = ((omem-0x01C0) / 12);
FileLog("[%10d] SPU2 write mem %08x (Core %d Voice %d Address %s) value %x\n",Cycles,rmem,core,voice,AddressNames[address],value); const u32 address = ((omem-0x01C0) % 12)>>1;
char dest[192];
sprintf( dest, "Voice %d %s", voice, AddressNames[address] );
RegLog( 2, dest, rmem, core, value );
} }
*/ else if ((mem >= 0x0760) && (mem < 0x07b0))
if ((mem >= 0x0760) && (mem < 0x07b0))
{ {
omem=mem; core=0; omem=mem; core=0;
if (mem >= 0x0788) {omem-=0x28; core=1;} if (mem >= 0x0788) {omem-=0x28; core=1;}
@ -869,16 +872,16 @@ void SPU2writeLog(u32 rmem, u16 value)
RegLog(2,"IRQAL",rmem,core,value); RegLog(2,"IRQAL",rmem,core,value);
break; break;
case (REG_S_KON + 2): case (REG_S_KON + 2):
RegLog(2,"KON1",rmem,core,value); RegLog(1,"KON1",rmem,core,value);
break; break;
case REG_S_KON: case REG_S_KON:
RegLog(2,"KON0",rmem,core,value); RegLog(1,"KON0",rmem,core,value);
break; break;
case (REG_S_KOFF + 2): case (REG_S_KOFF + 2):
RegLog(2,"KOFF1",rmem,core,value); RegLog(1,"KOFF1",rmem,core,value);
break; break;
case REG_S_KOFF: case REG_S_KOFF:
RegLog(2,"KOFF0",rmem,core,value); RegLog(1,"KOFF0",rmem,core,value);
break; break;
case REG_A_TSA: case REG_A_TSA:
RegLog(2,"TSAH",rmem,core,value); RegLog(2,"TSAH",rmem,core,value);
@ -973,35 +976,31 @@ __forceinline void SPU2_FastWrite( u32 rmem, u16 value )
switch (param) switch (param)
{ {
case 0: //VOLL (Volume L) case 0: //VOLL (Volume L)
case 1: //VOLR (Volume R)
{
V_Volume& thisvol = (param==0) ? Cores[core].Voices[voice].VolumeL : Cores[core].Voices[voice].VolumeR;
if (value & 0x8000) // +Lin/-Lin/+Exp/-Exp if (value & 0x8000) // +Lin/-Lin/+Exp/-Exp
{ {
Cores[core].Voices[voice].VolumeL.Mode=(value & 0xF000)>>12; thisvol.Mode=(value & 0xF000)>>12;
Cores[core].Voices[voice].VolumeL.Increment=(value & 0x3F); thisvol.Increment=(value & 0x3F);
} }
else else
{ {
Cores[core].Voices[voice].VolumeL.Mode=0; // Constant Volume mode (no slides or envelopes)
Cores[core].Voices[voice].VolumeL.Increment=0; // Volumes range from 0x3fff to -0x4000. Values below zero invert the waveform (unimplemented)
if(value&0x4000)
value=0x3fff - (value&0x3fff); thisvol.Mode=0;
Cores[core].Voices[voice].VolumeL.Value=value<<1; thisvol.Increment=0;
s16 newval = value & 0x3fff;
if( value & 0x4000 )
newval = 0x3fff - newval;
thisvol.Value = newval<<1;
} }
Cores[core].Voices[voice].VolumeL.Reg_VOL = value; thisvol.Reg_VOL = value;
}
break; break;
case 1: //VOLR (Volume R)
if (value & 0x8000)
{
Cores[core].Voices[voice].VolumeR.Mode=(value & 0xF000)>>12;
Cores[core].Voices[voice].VolumeR.Increment=(value & 0x3F);
}
else
{
Cores[core].Voices[voice].VolumeR.Mode=0;
Cores[core].Voices[voice].VolumeR.Increment=0;
Cores[core].Voices[voice].VolumeR.Value=value<<1;
}
Cores[core].Voices[voice].VolumeR.Reg_VOL = value; break;
case 2: Cores[core].Voices[voice].Pitch=value; break; case 2: Cores[core].Voices[voice].Pitch=value; break;
case 3: // ADSR1 (Envelope) case 3: // ADSR1 (Envelope)
Cores[core].Voices[voice].ADSR.Am=(value & 0x8000)>>15; Cores[core].Voices[voice].ADSR.Am=(value & 0x8000)>>15;
@ -1021,6 +1020,7 @@ __forceinline void SPU2_FastWrite( u32 rmem, u16 value )
Cores[core].Voices[voice].ADSR.Value = value << 15; Cores[core].Voices[voice].ADSR.Value = value << 15;
ConLog( "* SPU2: Mysterious ADSR Volume Set to 0x%x", value ); ConLog( "* SPU2: Mysterious ADSR Volume Set to 0x%x", value );
break; break;
case 6: Cores[core].Voices[voice].VolumeL.Value=value; break; case 6: Cores[core].Voices[voice].VolumeL.Value=value; break;
case 7: Cores[core].Voices[voice].VolumeR.Value=value; break; case 7: Cores[core].Voices[voice].VolumeR.Value=value; break;
@ -1214,29 +1214,55 @@ __forceinline void SPU2_FastWrite( u32 rmem, u16 value )
return; return;
case REG_P_MVOLL: case REG_P_MVOLL:
if (value & 0x8000) { // +Lin/-Lin/+Exp/-Exp case REG_P_MVOLR:
Cores[core].MasterL.Mode=(value & 0xE000)/0x2000; {
Cores[core].MasterL.Increment=(value & 0x3F) | ((value & 0x800)/0x10); V_Volume& thisvol = (omem==REG_P_MVOLL) ? Cores[core].MasterL : Cores[core].MasterR;
if( value & 0x8000 ) // +Lin/-Lin/+Exp/-Exp
{
thisvol.Mode = (value & 0xE000) / 0x2000;
thisvol.Increment = (value & 0x7F); // | ((value & 0x800)/0x10);
} }
else { else
Cores[core].MasterL.Mode=0; {
Cores[core].MasterL.Increment=0; thisvol.Mode = 0;
Cores[core].MasterL.Value=value; thisvol.Increment = 0;
// Constant Volume mode (no slides or envelopes)
// Volumes range from 0x3fff to -0x4000. Values below zero invert the waveform (unimplemented)
s16 newval = value & 0x3fff;
if( value & 0x4000 )
newval = 0x3fff - newval;
thisvol.Value = newval<<1;
} }
Cores[core].MasterL.Reg_VOL=value; thisvol.Reg_VOL = value;
}
return; return;
case REG_P_MVOLR: case REG_P_EVOLL:
if (value & 0x8000) { // +Lin/-Lin/+Exp/-Exp Cores[core].FxL = ( value & 0x8000 ) ? -value : value;
Cores[core].MasterR.Mode=(value & 0xE000)/0x2000; return;
Cores[core].MasterR.Increment=(value & 0x3F) | ((value & 0x800)/0x10);
} case REG_P_EVOLR:
else { Cores[core].FxR = ( value & 0x8000 ) ? -value : value;
Cores[core].MasterR.Mode=0; return;
Cores[core].MasterR.Increment=0;
Cores[core].MasterR.Value=value; case REG_P_AVOLL:
} Cores[core].ExtL = ( value & 0x8000 ) ? -value : value;
Cores[core].MasterR.Reg_VOL=value; return;
case REG_P_AVOLR:
Cores[core].ExtR = ( value & 0x8000 ) ? -value : value;
return;
case REG_P_BVOLL:
Cores[core].InpL = ( value & 0x8000 ) ? -value : value;
return;
case REG_P_BVOLR:
Cores[core].InpR = ( value & 0x8000 ) ? -value : value;
return; return;
case REG_S_ADMAS: case REG_S_ADMAS:

View File

@ -135,7 +135,7 @@ default: \
#ifndef SAFE_DELETE_ARRAY #ifndef SAFE_DELETE_ARRAY
# define SAFE_DELETE_ARRAY(p) { if(p) { delete[] (p); (p)=NULL; } } # define SAFE_DELETE_ARRAY(p) { if(p) { delete[] (p); (p)=NULL; } }
#endif #endif
#ifndef SAFE_DELETE #ifndef SAFE_DELETE_OBJ
# define SAFE_DELETE_OBJ(p) { if(p) { delete (p); (p)=NULL; } } # define SAFE_DELETE_OBJ(p) { if(p) { delete (p); (p)=NULL; } }
#endif #endif
#ifndef SAFE_RELEASE #ifndef SAFE_RELEASE

View File

@ -16,181 +16,107 @@
//Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA //Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
// //
#include <stdexcept>
#include <new>
#include "spu2.h" #include "spu2.h"
#include <stdio.h>
#define WAVONLY #include "SoundTouch/WavFile.h"
typedef struct { static WavOutFile* _new_WavOutFile( const char* destfile )
//Main Header
char riffID[4];
long riffSize;
char riffTYPE[4];
//Format Tag
char chunkID[4];
long chunkSize;
short wFormatTag;
unsigned short wChannels;
unsigned long dwSamplesPerSec;
unsigned long dwAvgBytesPerSec;
unsigned short wBlockAlign;
unsigned short wBitsPerSample;
//Data Tag
char dataID[4];
long dataSize;
} WAVEHeader;
int wavedump_ok=0;
int datasize;
FILE *fdump;
#ifndef WAVONLY
int flacdump_open();
void flacdump_close();
void flacdump_write(s16 left,s16 right);
int oggvdump_open();
void oggvdump_close();
void oggvdump_write(s16 left,s16 right);
#endif
int wavedump_open()
{ {
#ifndef WAVONLY return new WavOutFile( destfile, 48000, 16, 2 );
if(WaveDumpFormat==1) return flacdump_open();
if(WaveDumpFormat==2) return oggvdump_open();
#endif
fdump=fopen(WaveLogFileName,"wb");
if(fdump==NULL) return 0;
fseek(fdump,sizeof(WAVEHeader),SEEK_SET);
datasize=0;
wavedump_ok=1;
return 1;
} }
void wavedump_flush() namespace WaveDump
{ {
WAVEHeader w; static WavOutFile* m_CoreWav[2][CoreSrc_Count] = { NULL };
memcpy(w.riffID,"RIFF",4); static const char* m_tbl_CoreOutputTypeNames[CoreSrc_Count] =
w.riffSize=datasize+36; {
memcpy(w.riffTYPE,"WAVE",4); "Input",
memcpy(w.chunkID,"fmt ",4); "DryVoiceMix",
w.chunkSize=0x10; "WetVoiceMix",
w.wFormatTag=1; "PreReverb",
w.wChannels=2; "PostReverb",
w.dwSamplesPerSec=48000; "External"
w.dwAvgBytesPerSec=48000*4; };
w.wBlockAlign=4;
w.wBitsPerSample=16;
memcpy(w.dataID,"data",4);
w.dataSize=datasize;
fseek(fdump,0,SEEK_SET); void Open()
fwrite(&w,sizeof(w),1,fdump); {
if( !WaveLog() ) return;
char wavfilename[256];
fseek(fdump,datasize+sizeof(w),SEEK_SET); for( uint cidx=0; cidx<2; cidx++ )
{
for( int srcidx=0; srcidx<CoreSrc_Count; srcidx++ )
{
SAFE_DELETE_OBJ( m_CoreWav[cidx][srcidx] );
sprintf( wavfilename, "logs\\spu2ghz-Core%d-%s.wav",
cidx, m_tbl_CoreOutputTypeNames[ srcidx ] );
try
{
m_CoreWav[cidx][srcidx] = _new_WavOutFile( wavfilename );
}
catch( std::runtime_error& ex )
{
printf( "SPU2ghz > %s.\n\tWave Log for this core source disabled.", ex.what() );
m_CoreWav[cidx][srcidx] = NULL;
}
}
}
}
void Close()
{
for( uint cidx=0; cidx<2; cidx++ )
{
for( int srcidx=0; srcidx<CoreSrc_Count; srcidx++ )
{
SAFE_DELETE_OBJ( m_CoreWav[cidx][srcidx] );
}
}
}
void WriteCore( uint coreidx, CoreSourceType src, s16 left, s16 right )
{
if( m_CoreWav[coreidx][src] != NULL )
{
s16 buffer[2] = { left, right };
m_CoreWav[coreidx][src]->write( buffer, 2 );
}
}
} }
void wavedump_close() WavOutFile* m_wavrecord = NULL;
{
if(!wavedump_ok) return;
wavedump_flush();
#ifndef WAVONLY
if(WaveDumpFormat==1) { flacdump_close(); return;}
if(WaveDumpFormat==2) { oggvdump_close(); return;}
#endif
fclose(fdump);
wavedump_ok=0;
}
void wavedump_write(s16 left,s16 right)
{
s16 buffer[2]={left,right};
if(!wavedump_ok) return;
#ifndef WAVONLY
if(WaveDumpFormat==1) return flacdump_write(left,right);
if(WaveDumpFormat==2) return oggvdump_write(left,right);
#endif
datasize+=4;
fwrite(buffer,4,1,fdump);
if((datasize&1023)==0)
wavedump_flush();
}
FILE *recordFile;
int recordSize;
int recording;
void RecordStart() void RecordStart()
{ {
if(recording&&recordFile) SAFE_DELETE_OBJ( m_wavrecord );
fclose(recordFile);
recordFile=fopen("recording.wav","wb"); try
if(recordFile==NULL) return; {
m_wavrecord = new WavOutFile( "recording.wav", 48000, 16, 2 );
fseek(recordFile,sizeof(WAVEHeader),SEEK_SET); }
catch( std::runtime_error& )
recordSize=0; {
recording=1; SysMessage("SPU2ghz couldn't open file for recording: %s.\nRecording to wavfile disabled.", "recording.wav");
} m_wavrecord = NULL;
}
void RecordFlush()
{
WAVEHeader w;
memcpy(w.riffID,"RIFF",4);
w.riffSize=recordSize+36;
memcpy(w.riffTYPE,"WAVE",4);
memcpy(w.chunkID,"fmt ",4);
w.chunkSize=0x10;
w.wFormatTag=1;
w.wChannels=2;
w.dwSamplesPerSec=48000;
w.dwAvgBytesPerSec=48000*4;
w.wBlockAlign=4;
w.wBitsPerSample=16;
memcpy(w.dataID,"data",4);
w.dataSize=recordSize;
fseek(recordFile,0,SEEK_SET);
fwrite(&w,sizeof(w),1,recordFile);
fseek(recordFile,recordSize+sizeof(w),SEEK_SET);
} }
void RecordStop() void RecordStop()
{ {
if(!recording) SAFE_DELETE_OBJ( m_wavrecord );
return;
recording=0;
RecordFlush();
fclose(recordFile);
} }
void RecordWrite(s16 left, s16 right) void RecordWrite(s16 left, s16 right)
{ {
if(!recording) if( m_wavrecord == NULL ) return;
return;
s16 buffer[2]={left,right};
recordSize+=4;
fwrite(buffer,4,1,recordFile);
if((recordSize&1023)==0)
RecordFlush();
s16 buffer[2] = { left, right };
m_wavrecord->write( buffer, 2 );
} }