ZeroSPU2: Various cleanups, refactors, and general mucking about with code.

git-svn-id: http://pcsx2.googlecode.com/svn/trunk@2588 96395faa-99c1-11dd-bbfe-3dabce05a288
This commit is contained in:
arcum42 2010-02-12 11:42:29 +00:00
parent 742bae8d94
commit 7f46f80015
13 changed files with 840 additions and 687 deletions

View File

@ -25,13 +25,16 @@ endif(CMAKE_BUILD_TYPE STREQUAL Release)
set(zerospu2Sources set(zerospu2Sources
voices.cpp voices.cpp
zerodma.cpp zerodma.cpp
zerospu2.cpp) zerospu2.cpp
zeroworker.cpp)
# zerospu2 headers # zerospu2 headers
set(zerospu2Headers set(zerospu2Headers
misc.h misc.h
reg.h reg.h
zerospu2.h) zerodma.h
zerospu2.h
zeroworker.h)
# zerospu2 Linux sources # zerospu2 Linux sources
set(zerospu2LinuxSources set(zerospu2LinuxSources

View File

@ -20,7 +20,8 @@
#include <gtk/gtk.h> #include <gtk/gtk.h>
extern "C" { extern "C"
{
#include "interface.h" #include "interface.h"
#include "support.h" #include "support.h"
#include "callbacks.h" #include "callbacks.h"

View File

@ -105,7 +105,7 @@
</Target> </Target>
</Build> </Build>
<Compiler> <Compiler>
<Add option="-Wall" /> <Add option="-W" />
<Add option="`pkg-config gtk+-2.0 --cflags`" /> <Add option="`pkg-config gtk+-2.0 --cflags`" />
<Add option="-fPIC" /> <Add option="-fPIC" />
<Add option="-m32" /> <Add option="-m32" />
@ -155,8 +155,11 @@
<Unit filename="../reg.h" /> <Unit filename="../reg.h" />
<Unit filename="../voices.cpp" /> <Unit filename="../voices.cpp" />
<Unit filename="../zerodma.cpp" /> <Unit filename="../zerodma.cpp" />
<Unit filename="../zerodma.h" />
<Unit filename="../zerospu2.cpp" /> <Unit filename="../zerospu2.cpp" />
<Unit filename="../zerospu2.h" /> <Unit filename="../zerospu2.h" />
<Unit filename="../zeroworker.cpp" />
<Unit filename="../zeroworker.h" />
<Extensions> <Extensions>
<code_completion /> <code_completion />
<debugger /> <debugger />

View File

@ -22,8 +22,8 @@ libZeroSPU2_LDFLAGS= @SHARED_LDFLAGS@
libZeroSPU2_LDFLAGS+=-Wl,-soname,@ZEROSPU2_SONAME@ libZeroSPU2_LDFLAGS+=-Wl,-soname,@ZEROSPU2_SONAME@
libZeroSPU2_LDADD=$(libZeroSPU2_a_OBJECTS) libSoundTouch.a libZeroSPU2_LDADD=$(libZeroSPU2_a_OBJECTS) libSoundTouch.a
libZeroSPU2_a_SOURCES = zerospu2.cpp voices.cpp zerodma.cpp libZeroSPU2_a_SOURCES = zerospu2.cpp voices.cpp zerodma.cpp zeroworker.cpp
libZeroSPU2_a_SOURCES += zerospu2.h reg.h misc.h libZeroSPU2_a_SOURCES += zerospu2.h reg.h misc.h zerodma.h zeroworker.h
libZeroSPU2_a_SOURCES += Linux/interface.c Linux/Linux.cpp Targets/Alsa.cpp Targets/OSS.cpp Targets/SoundTargets.cpp Linux/support.c libZeroSPU2_a_SOURCES += Linux/interface.c Linux/Linux.cpp Targets/Alsa.cpp Targets/OSS.cpp Targets/SoundTargets.cpp Linux/support.c
#SUBDIRS = ../../3rdparty/SoundTouch #SUBDIRS = ../../3rdparty/SoundTouch

View File

@ -257,6 +257,10 @@
RelativePath="..\zerospu2.cpp" RelativePath="..\zerospu2.cpp"
> >
</File> </File>
<File
RelativePath="..\zeroworker.cpp"
>
</File>
</Filter> </Filter>
<Filter <Filter
Name="Header Files" Name="Header Files"
@ -283,10 +287,18 @@
RelativePath="..\Targets\SoundTargets.h" RelativePath="..\Targets\SoundTargets.h"
> >
</File> </File>
<File
RelativePath="..\zerodma.h"
>
</File>
<File <File
RelativePath="..\zerospu2.h" RelativePath="..\zerospu2.h"
> >
</File> </File>
<File
RelativePath="..\zeroworker.h"
>
</File>
</Filter> </Filter>
<Filter <Filter
Name="Resource Files" Name="Resource Files"

View File

@ -157,6 +157,67 @@ R_MIX_DEST_B1 = 0x0338,
// NOTE: SPDIF_COPY is defined in Linux kernel headers as 0x0004. // NOTE: SPDIF_COPY is defined in Linux kernel headers as 0x0004.
}; };
// Not sure if this is a good idea, but I don't feel like retyping it, so I'll leave it in for now.
/*struct core_registers
{
u16 pad[179];
u16 fmod1; u16 pad1;
u16 fmod2; u16 pad2;
u16 s_non; u16 pad3[3];
u16 vmix_l1; u16 pad4;
u16 vmix_l2; u16 pad5;
u16 vmixel; u16 pad6[3];
u16 vmixr1; u16 pad7;
u16 vmixr2; u16 pad8;
u16 vmixer; u16 pad9[3];
u16 mmix; u16 pad10;
u16 attr; u16 pad11;
u16 irqa_hi; u16 pad12;
u16 irqa_lo; u16 pad13;
u16 spu_on_1; u16 pad14;
u16 spu_on_2; u16 pad15;
u16 spu_off_1; u16 pad16;
u16 spu_off_2; u16 pad17;
u16 spu_addr_hi; u16 pad18;
u16 spu_addr_lo; u16 pad19;
u16 spu_data; u16 pad20;
u16 dma_ctrl; u16 pad21;
u16 admas; u16 pad22[16];
// Not complete past this point (not padded properly)
u16 va_ssa; u16 pad23[3]; //0x01C0, // Waveform data starting address
u16 va_lsax; u16 pad24[3]; //0x01C4, // Loop point address
u16 va_nax; u16 pad25; //0x01C8, // Waveform data that should be read next
u16 a_esa; u16 pad26; //0x02E0, //Address: Top address of working area for effects processing
u16 fb_src_a; u16 pad27; //0x02E4, // Feedback Source A
u16 fb_sra_b; u16 pad28; //0x02E8, // Feedback Source B
u16 iir_dest_a0; u16 pad29; //0x02EC,
u16 iir_dest_a1; u16 pad30; //0x02F0,
u16 acc_src_a0; u16 pad31; //0x02F4,
u16 acc_src_a1; u16 pad32; //0x02F8,
u16 acc_src_b0; u16 pad33; //0x02FC,
u16 acc_src_b1; u16 pad34; //0x0300,
u16 iir_src_a0; u16 pad35; //0x0304,
u16 iir_src_a1; u16 pad36; //0x0308,
u16 iir_dest_b0; u16 pad37; //0x030C,
u16 iir_dest_b1; u16 pad38; //0x0310,
u16 acc_src_c0; u16 pad39; //0x0314,
u16 acc_src_c1; u16 pad40; //0x0318,
u16 acc_src_d0; u16 pad41; //0x031C,
u16 acc_src_d1; u16 pad42; // 0x0320,
u16 iir_src_b1; u16 pad43; //0x0324,
u16 iir_src_b0; u16 pad44; //0x0328,
u16 mix_dest_a0; u16 pad45; //0x032C,
u16 mix_dest_a1; u16 pad46; //0x0330,
u16 mix_dest_b0; u16 pad47; //0x0334,
u16 mix_dest_b1; u16 pad48; // 0x0338,
u16 a_eea; u16 pad49; //0x033C,
u16 end1; u16 pad 50; //0x0340, // End Point passed flag
u16 end2; u16 pad 51; //0x0342,
u16 statx; //0x0344, // Status register?
};*/
// These SPDIF defines aren't used yet - swiped from spu2ghz, like a number of the registers I added in. // These SPDIF defines aren't used yet - swiped from spu2ghz, like a number of the registers I added in.
// -- arcum42 // -- arcum42
#define SPDIF_OUT_OFF 0x0000 //no spdif output #define SPDIF_OUT_OFF 0x0000 //no spdif output

View File

@ -26,9 +26,9 @@
// VOICE_PROCESSED definitions // VOICE_PROCESSED definitions
SPU_CONTROL_* VOICE_PROCESSED::GetCtrl() tSPU_ATTR* VOICE_PROCESSED::GetCtrl()
{ {
return ((SPU_CONTROL_*)(spu2regs+memoffset+REG_C0_CTRL)); return &spu2attr(memchannel);
} }
void VOICE_PROCESSED::SetVolume(s32 iProcessRight) void VOICE_PROCESSED::SetVolume(s32 iProcessRight)
@ -216,7 +216,7 @@ static void __forceinline GetNoiseValues(s32& VD)
"XOR %%eax,%%ebx\n" "XOR %%eax,%%ebx\n"
"ROR %%eax,3\n" "ROR %%eax,3\n"
"MOV %0,%%eax\n" "MOV %0,%%eax\n"
".att_syntax\n" : "r="(Seed) :"r"(Seed)); ".att_syntax\n" : "=r"(Seed) :"r"(Seed));
#endif #endif
} }
@ -283,6 +283,14 @@ s32 VOICE_PROCESSED::iGetInterpolationVal()
return fa; return fa;
} }
s32 VOICE_PROCESSED::iGetVal()
{
if (bNoise)
return iGetNoiseVal();
else
return iGetInterpolationVal();
}
void VOICE_PROCESSED::Stop() void VOICE_PROCESSED::Stop()
{ {
} }

View File

@ -17,6 +17,7 @@
*/ */
#include "zerospu2.h" #include "zerospu2.h"
#include "zerodma.h"
#include <assert.h> #include <assert.h>
#include <stdlib.h> #include <stdlib.h>
@ -24,34 +25,20 @@
#include "SoundTouch/SoundTouch.h" #include "SoundTouch/SoundTouch.h"
#include "SoundTouch/WavFile.h" #include "SoundTouch/WavFile.h"
void CALLBACK SPU2readDMAMem(u16 *pMem, int size, int core) void CALLBACK SPU2readDMAMem(u16 *pMem, int size, int channel)
{ {
u32 spuaddr; u32 spuaddr = C_SPUADDR(channel);
s32 i, dma, offset;
if ( core == 0) SPU2_LOG("SPU2 readDMAMem(%d) size %x, addr: %x\n", channel, size, pMem);
{
dma = 4;
offset = 0;
}
else
{
dma = 7;
offset = 0x0400;
}
spuaddr = C_SPUADDR(core); for (u32 i = 0; i < size; i++)
SPU2_LOG("SPU2 readDMA%dMem size %x, addr: %x\n", dma, size, pMem);
for (i = 0; i < size; i++)
{ {
*pMem++ = *(u16*)(spu2mem + spuaddr); *pMem++ = *(u16*)(spu2mem + spuaddr);
if ((spu2Rs16(REG_C0_CTRL + offset) & 0x40) && (C_IRQA(core) == spuaddr)) if (spu2attr(channel).irq && (C_IRQA(channel) == spuaddr))
{ {
C_SPUADDR_SET(spuaddr, core); C_SPUADDR_SET(spuaddr, channel);
IRQINFO |= (4 * (core + 1)); IRQINFO |= (4 * (channel + 1));
SPU2_LOG("SPU2readDMA%dMem:interrupt\n", dma); SPU2_LOG("SPU2readDMAMem(%d):interrupt\n", channel);
irqCallbackSPU2(); irqCallbackSPU2();
} }
@ -60,13 +47,13 @@ void CALLBACK SPU2readDMAMem(u16 *pMem, int size, int core)
} }
spuaddr += 19; //Transfer Local To Host TSAH/L + Data Size + 20 (already +1'd) spuaddr += 19; //Transfer Local To Host TSAH/L + Data Size + 20 (already +1'd)
C_SPUADDR_SET(spuaddr, core); C_SPUADDR_SET(spuaddr, channel);
// DMA complete // DMA complete
spu2Ru16(REG_C0_SPUSTAT + offset) &= ~0x80; spu2stat_clear_80(channel);
SPUStartCycle[core] = SPUCycles; SPUStartCycle[channel] = SPUCycles;
SPUTargetCycle[core] = size; SPUTargetCycle[channel] = size;
interrupt |= (1 << (1 + core)); interrupt |= (1 << (1 + channel));
} }
void CALLBACK SPU2readDMA4Mem(u16 *pMem, int size) void CALLBACK SPU2readDMA4Mem(u16 *pMem, int size)
@ -90,108 +77,73 @@ void CALLBACK SPU2readDMA7Mem(u16* pMem, int size)
// short-words for right ) has been transferred. Another interrupt occurs at // short-words for right ) has been transferred. Another interrupt occurs at
// the end of the transfer. // the end of the transfer.
int ADMASWrite(int core) int ADMASWrite(int channel)
{ {
u32 spuaddr; u32 spuaddr;
ADMA *Adma; ADMA *Adma = &adma[channel];
s32 dma, offset;
if (core == 0)
{
Adma = &Adma4;
dma = 4;
offset = 0;
}
else
{
Adma = &Adma7;
dma = 7;
offset = 0x0400;
}
if (interrupt & 0x2) if (interrupt & 0x2)
{ {
WARN_LOG("%d returning for interrupt\n", dma); WARN_LOG("ADMASWrite(%d) returning for interrupt\n", channel);
return 0; return 0;
} }
if (Adma->AmountLeft <= 0) if (Adma->AmountLeft <= 0)
{ {
WARN_LOG("%d amount left is 0\n", dma); WARN_LOG("ADMASWrite(%d) amount left is 0\n", channel);
return 1; return 1;
} }
assert( Adma->AmountLeft >= 512 ); assert( Adma->AmountLeft >= 512 );
spuaddr = C_SPUADDR(core); spuaddr = C_SPUADDR(channel);
u32 left_addr = spuaddr + 0x2000 + c_offset(channel);
u32 right_addr = left_addr + 0x200;
// SPU2 Deinterleaves the Left and Right Channels // SPU2 Deinterleaves the Left and Right Channels
memcpy((s16*)(spu2mem + spuaddr + 0x2000 + offset),(s16*)Adma->MemAddr,512); memcpy((s16*)(spu2mem + left_addr),(s16*)Adma->MemAddr,512);
Adma->MemAddr += 256; Adma->MemAddr += 256;
memcpy((s16*)(spu2mem + spuaddr + 0x2200 + offset),(s16*)Adma->MemAddr,512); memcpy((s16*)(spu2mem + right_addr),(s16*)Adma->MemAddr,512);
Adma->MemAddr += 256; Adma->MemAddr += 256;
if ((spu2Ru16(REG_C0_CTRL + offset) & 0x40) && ((spuaddr + 0x2400) <= C_IRQA(core) && (spuaddr + 0x2400 + 256) >= C_IRQA(core))) if (spu2attr(channel).irq && (irq_test1(channel, spuaddr) || irq_test2(channel, spuaddr)))
{ {
IRQINFO |= (4 * (core + 1)); IRQINFO |= (4 * (channel + 1));
WARN_LOG("ADMA %d Mem access:interrupt\n", dma); WARN_LOG("ADMAMem access(%d): interrupt\n", channel);
irqCallbackSPU2();
}
if ((spu2Ru16(REG_C0_CTRL + offset) & 0x40) && ((spuaddr + 0x2600) <= C_IRQA(core) && (spuaddr + 0x2600 + 256) >= C_IRQA(core)))
{
IRQINFO |= (4 * (core + 1));
WARN_LOG("ADMA %d Mem access:interrupt\n", dma);
irqCallbackSPU2(); irqCallbackSPU2();
} }
spuaddr = (spuaddr + 256) & 511; spuaddr = (spuaddr + 256) & 511;
C_SPUADDR_SET(spuaddr, core); C_SPUADDR_SET(spuaddr, channel);
Adma->AmountLeft -= 512; Adma->AmountLeft -= 512;
if (Adma->AmountLeft > 0) return (Adma->AmountLeft <= 0);
return 0;
else
return 1;
} }
void SPU2writeDMAMem(u16* pMem, int size, int core) void SPU2writeDMAMem(u16* pMem, int size, int channel)
{ {
u32 spuaddr; u32 spuaddr;
ADMA *Adma; ADMA *Adma = &adma[channel];
s32 dma, offset; s32 offset = (channel == 0) ? 0 : 0x400;
if (core == 0)
{
Adma = &Adma4;
dma = 4;
offset = 0;
}
else
{
Adma = &Adma7;
dma = 7;
offset = 0x0400;
}
SPU2_LOG("SPU2 writeDMA%dMem size %x, addr: %x(spu2:%x), ctrl: %x, adma: %x\n", \ SPU2_LOG("SPU2 writeDMAMem size %x, addr: %x(spu2:%x)"/*, ctrl: %x, adma: %x\n"*/, \
dma, size, pMem, C_SPUADDR(core), spu2Ru16(REG_C0_CTRL + offset), spu2Ru16(REG_C0_ADMAS + offset)); size, pMem, C_SPUADDR(channel)/*, spu2Ru16(REG_C0_CTRL + offset), spu2Ru16(REG_C0_ADMAS + offset)*/);
if ((spu2Ru16(REG_C0_ADMAS + offset) & 0x1 * (core + 1)) && ((spu2Ru16(REG_C0_CTRL + offset) & 0x30) == 0) && size) if (spu2admas(channel) && (spu2attr(channel).dma == 0) && size)
{ {
if (!Adma->Enabled ) Adma->Index = 0; if (!Adma->Enabled ) Adma->Index = 0;
Adma->MemAddr = pMem; Adma->MemAddr = pMem;
Adma->AmountLeft = size; Adma->AmountLeft = size;
SPUTargetCycle[core] = size; SPUTargetCycle[channel] = size;
spu2Ru16(REG_C0_SPUSTAT + offset) &= ~0x80; spu2stat_clear_80(channel);
if (!Adma->Enabled || (Adma->Index > 384)) if (!Adma->Enabled || (Adma->Index > 384))
{ {
C_SPUADDR_SET(0, core); C_SPUADDR_SET(0, channel);
if (ADMASWrite(core)) if (ADMASWrite(channel))
{ {
SPUStartCycle[core] = SPUCycles; SPUStartCycle[channel] = SPUCycles;
interrupt |= (1 << (1 + core)); interrupt |= (1 << (1 + channel));
} }
} }
Adma->Enabled = 1; Adma->Enabled = 1;
@ -200,50 +152,37 @@ void SPU2writeDMAMem(u16* pMem, int size, int core)
} }
#ifdef ZEROSPU2_DEVBUILD #ifdef ZEROSPU2_DEVBUILD
if ((conf.Log && conf.options & OPTION_RECORDING) && (core == 1)) if ((conf.Log && conf.options & OPTION_RECORDING) && (channel == 1))
LogPacketSound(pMem, 0x8000); LogPacketSound(pMem, 0x8000);
#endif #endif
spuaddr = C_SPUADDR(core); spuaddr = C_SPUADDR(channel);
memcpy((u8*)(spu2mem + spuaddr),(u8*)pMem,size << 1); memcpy((u8*)(spu2mem + spuaddr),(u8*)pMem,size << 1);
spuaddr += size; spuaddr += size;
C_SPUADDR_SET(spuaddr, core); C_SPUADDR_SET(spuaddr, channel);
if ((spu2Ru16(REG_C0_CTRL + offset)&0x40) && (spuaddr < C_IRQA(core) && (C_IRQA(core) <= (spuaddr+0x20)))) if (spu2attr(channel).irq && (spuaddr < C_IRQA(channel) && (C_IRQA(channel) <= (spuaddr + 0x20))))
{ {
IRQINFO |= 4 * (core + 1); IRQINFO |= 4 * (channel + 1);
SPU2_LOG("SPU2writeDMA%dMem:interrupt\n", dma); SPU2_LOG("SPU2writeDMAMem:interrupt\n");
irqCallbackSPU2(); irqCallbackSPU2();
} }
if (spuaddr > 0xFFFFE) spuaddr = 0x2800; if (spuaddr > 0xFFFFE) spuaddr = 0x2800;
C_SPUADDR_SET(spuaddr, core); C_SPUADDR_SET(spuaddr, channel);
MemAddr[core] += size << 1; MemAddr[channel] += size << 1;
spu2Ru16(REG_C0_SPUSTAT + offset) &= ~0x80; spu2stat_clear_80(channel);
SPUStartCycle[core] = SPUCycles; SPUStartCycle[channel] = SPUCycles;
SPUTargetCycle[core] = size; SPUTargetCycle[channel] = size;
interrupt |= (1 << (core + 1)); interrupt |= (1 << (channel + 1));
} }
void CALLBACK SPU2interruptDMA(int dma) void CALLBACK SPU2interruptDMA(int channel)
{ {
s32 core, offset; SPU2_LOG("SPU2 interruptDMA(%d)\n", channel);
spu2attr(channel).dma = 0;
if (dma == 4) spu2stat_set_80(channel);
{
core = 0;
offset = 0;
}
else
{
core = 1;
offset = 0x0400;
}
SPU2_LOG("SPU2 interruptDMA%d\n", dma);
spu2Rs16(REG_C0_CTRL + offset) &= ~0x30;
spu2Ru16(REG_C0_SPUSTAT + offset) |= 0x80;
} }
#ifndef ENABLE_NEW_IOPDMA_SPU2 #ifndef ENABLE_NEW_IOPDMA_SPU2

View File

@ -0,0 +1,23 @@
#ifndef ZERODMA_H_INCLUDED
#define ZERODMA_H_INCLUDED
#include <stdio.h>
#define SPU2defs
#include "PS2Edefs.h"
#include "reg.h"
#include "misc.h"
static __forceinline bool irq_test1(u32 ch, u32 addr)
{
return ((addr + 0x2400) <= C_IRQA(ch) && (addr + 0x2500) >= C_IRQA(ch));
}
static __forceinline bool irq_test2(u32 ch, u32 addr)
{
return ((addr + 0x2600) <= C_IRQA(ch) && (addr + 0x2700) >= C_IRQA(ch));
}
#endif // ZERODMA_H_INCLUDED

View File

@ -35,8 +35,7 @@ char libraryName[256];
FILE *spu2Log; FILE *spu2Log;
Config conf; Config conf;
ADMA Adma4; ADMA adma[2];
ADMA Adma7;
u32 MemAddr[2]; u32 MemAddr[2];
u32 g_nSpuInit = 0; u32 g_nSpuInit = 0;
@ -52,8 +51,8 @@ s32 iFMod[NSSIZE];
s32 s_buffers[NSSIZE][2]; // left and right buffers s32 s_buffers[NSSIZE][2]; // left and right buffers
// mixer thread variables // mixer thread variables
static bool s_bThreadExit = true; bool s_bThreadExit = true;
static s32 s_nDropPacket = 0; s32 s_nDropPacket = 0;
string s_strIniPath( "inis/" ); string s_strIniPath( "inis/" );
#ifdef _WIN32 #ifdef _WIN32
@ -67,27 +66,25 @@ pthread_t s_threadSPU2;
void* SPU2ThreadProc(void*); void* SPU2ThreadProc(void*);
#endif #endif
static AUDIOBUFFER s_pAudioBuffers[NSPACKETS]; AUDIOBUFFER s_pAudioBuffers[NSPACKETS];
static s32 s_nCurBuffer = 0, s_nQueuedBuffers = 0; s32 s_nCurBuffer = 0, s_nQueuedBuffers = 0;
static s16* s_pCurOutput = NULL; s16* s_pCurOutput = NULL;
static u32 g_startcount=0xffffffff; u32 g_startcount=0xffffffff;
static u32 g_packetcount=0; u32 g_packetcount=0;
// time stretch variables // time stretch variables
soundtouch::SoundTouch* pSoundTouch=NULL; soundtouch::SoundTouch* pSoundTouch=NULL;
WavOutFile* g_pWavRecord=NULL; // used for recording extern WavOutFile* g_pWavRecord; // used for recording
static u64 s_GlobalTimeStamp = 0; u64 s_GlobalTimeStamp = 0;
static s32 s_nDurations[64]={0}; s32 s_nDurations[64]={0};
static s32 s_nCurDuration=0; s32 s_nCurDuration=0;
static s32 s_nTotalDuration=0; s32 s_nTotalDuration=0;
s32 SPUCycles = 0, SPUWorkerCycles = 0; s32 SPUCycles = 0, SPUWorkerCycles = 0;
s32 SPUStartCycle[2]; s32 SPUStartCycle[2];
s32 SPUTargetCycle[2]; s32 SPUTargetCycle[2];
s32 g_logsound=0;
int ADMASWrite(int c); int ADMASWrite(int c);
void InitADSR(); void InitADSR();
@ -100,14 +97,6 @@ void (*irqCallbackDMA7)()=0;
#endif #endif
uptr g_pDMABaseAddr=0; uptr g_pDMABaseAddr=0;
const s32 f[5][2] = {
{ 0, 0 },
{ 60, 0 },
{ 115, -52 },
{ 98, -55 },
{ 122, -60 } };
u32 RateTable[160]; u32 RateTable[160];
// channels and voices // channels and voices
@ -266,12 +255,9 @@ s32 CALLBACK SPU2init()
voices[i+24].memoffset = 0x400; voices[i+24].memoffset = 0x400;
// init each channel // init each channel
for (u32 i = 0; i < ArraySize(voices); ++i) { for (u32 i = 0; i < ArraySize(voices); ++i)
voices[i].chanid = i; {
voices[i].pLoop = voices[i].pStart = voices[i].pCurr = (u8*)spu2mem; voices[i].init(i);
voices[i].pvoice = (_SPU_VOICE*)((u8*)spu2regs+voices[i].memoffset)+(i%24);
voices[i].ADSRX.SustainLevel = 1024; // -> init sustain
} }
return 0; return 0;
@ -319,7 +305,7 @@ s32 CALLBACK SPU2open(void *pDsp)
// initialize the audio buffers // initialize the audio buffers
for (u32 i = 0; i < ArraySize(s_pAudioBuffers); ++i) for (u32 i = 0; i < ArraySize(s_pAudioBuffers); ++i)
{ {
s_pAudioBuffers[i].pbuf = (u8*)_aligned_malloc(4*NSSIZE*NSFRAMES, 16); // 4 bytes for each sample s_pAudioBuffers[i].pbuf = (u8*)_aligned_malloc(4 * NS_TOTAL_SIZE, 16); // 4 bytes for each sample
s_pAudioBuffers[i].len = 0; s_pAudioBuffers[i].len = 0;
} }
@ -424,7 +410,7 @@ void CALLBACK SPU2async(u32 cycle)
if ( g_nSpuInit ) if ( g_nSpuInit )
{ {
while( SPUCycles-SPUWorkerCycles > 0 && CYCLES_PER_MS < SPUCycles-SPUWorkerCycles ) while((SPUCycles-SPUWorkerCycles > 0) && (CYCLES_PER_MS < (SPUCycles - SPUWorkerCycles)))
{ {
SPU2Worker(); SPU2Worker();
SPUWorkerCycles += CYCLES_PER_MS; SPUWorkerCycles += CYCLES_PER_MS;
@ -563,58 +549,58 @@ s32 MixADSR(VOICE_PROCESSED* pvoice) // MIX ADSR
return 0; return 0;
} }
void MixChannels(s32 core) void MixChannels(s32 channel)
{ {
// mix all channels // mix all channels
s32 c_offset = 0x0400 * core; s32 left_vol, right_vol;
s32 dma, left_vol, right_vol;
ADMA *Adma; ADMA *Adma;
if (core == 0) if (channel == 0)
{ {
Adma = &Adma4; Adma = &adma[0];
dma = 4;
left_vol = REG_C0_BVOLL; left_vol = REG_C0_BVOLL;
right_vol = REG_C0_BVOLR; right_vol = REG_C0_BVOLR;
} }
else else
{ {
Adma = &Adma7; Adma = &adma[1];
dma = 7;
left_vol = REG_C1_BVOLL; left_vol = REG_C1_BVOLL;
right_vol = REG_C1_BVOLR; right_vol = REG_C1_BVOLR;
} }
if ((spu2Ru16(REG_C0_MMIX + c_offset) & 0xF0) && (spu2Ru16(REG_C0_ADMAS + c_offset) & (0x1 + core))) if ((spu2mmix(channel) & 0xF0) && spu2admas(channel))
{ {
for (s32 ns=0;ns<NSSIZE;ns++) for (u32 ns = 0; ns < NSSIZE; ns++)
{ {
if ((spu2Ru16(REG_C0_MMIX + c_offset) & 0x80)) u16 left_reg = 0x2000 + c_offset(channel) + Adma->Index;
s_buffers[ns][0] += (((s16*)spu2mem)[0x2000 + c_offset +Adma->Index]*(s32)spu2Ru16(left_vol))>>16; u16 right_reg = left_reg + 0x200;
if ((spu2Ru16(REG_C0_MMIX + c_offset) & 0x40))
s_buffers[ns][1] += (((s16*)spu2mem)[0x2200 + c_offset +Adma->Index]*(s32)spu2Ru16(right_vol))>>16; if ((spu2mmix(channel) & 0x80))
s_buffers[ns][0] += (((s16*)spu2mem)[left_reg]*(s32)spu2Ru16(left_vol))>>16;
if ((spu2mmix(channel) & 0x40))
s_buffers[ns][1] += (((s16*)spu2mem)[right_reg]*(s32)spu2Ru16(right_vol))>>16;
Adma->Index +=1; Adma->Index +=1;
MemAddr[core] += 4; MemAddr[channel] += 4;
if (Adma->Index == 128 || Adma->Index == 384) if (Adma->Index == 128 || Adma->Index == 384)
{ {
if (ADMASWrite(core)) if (ADMASWrite(channel))
{ {
if (interrupt & (0x2 * (core + 1))) if (interrupt & (0x2 * (channel + 1)))
{ {
interrupt &= ~(0x2 * (core + 1)); interrupt &= ~(0x2 * (channel + 1));
WARN_LOG("Stopping double interrupt DMA7\n"); WARN_LOG("Stopping double interrupt DMA7\n");
} }
#ifndef ENABLE_NEW_IOPDMA_SPU2 #ifndef ENABLE_NEW_IOPDMA_SPU2
if (core == 0) if (channel == 0)
irqCallbackDMA4(); irqCallbackDMA4();
else else
irqCallbackDMA7(); irqCallbackDMA7();
#endif #endif
} }
if (core == 1) Adma->Enabled = 2; if (channel == 1) Adma->Enabled = 2;
} }
if (Adma->Index == 512) if (Adma->Index == 512)
@ -626,248 +612,6 @@ void MixChannels(s32 core)
} }
} }
// simulate SPU2 for 1ms
void SPU2Worker()
{
u8* start;
u32 nSample;
s32 ch, predict_nr, shift_factor, flags;
// assume s_buffers are zeroed out
if ( dwNewChannel2[0] || dwNewChannel2[1] )
s_pAudioBuffers[s_nCurBuffer].newchannels++;
VOICE_PROCESSED* pChannel=voices;
for (ch=0; ch < SPU_NUMBER_VOICES; ch++, pChannel++) // loop em all... we will collect 1 ms of sound of each playing channel
{
if (pChannel->bNew)
{
pChannel->StartSound(); // start new sound
dwEndChannel2[ch/24]&=~(1<<(ch%24)); // clear end channel bit
dwNewChannel2[ch/24]&=~(1<<(ch%24)); // clear channel bit
}
if (!pChannel->bOn) continue;
if (pChannel->iActFreq!=pChannel->iUsedFreq) // new psx frequency?
pChannel->VoiceChangeFrequency();
// loop until 1 ms of data is reached
s32 ns = 0;
while(ns<NSSIZE)
{
s32 s_1, s_2, fa;
// fmod freq channel
if (pChannel->bFMod==1 && iFMod[ns]) pChannel->FModChangeFrequency(ns);
while(pChannel->spos >= 0x10000 )
{
if (pChannel->iSBPos == 28) // 28 reached?
{
start=pChannel->pCurr; // set up the current pos
// special "stop" sign - fixme - an *unsigned* -1?
if (start == (u8*)-1) //!pChannel->bOn
{
pChannel->bOn=false; // -> turn everything off
pChannel->ADSRX.lVolume=0;
pChannel->ADSRX.EnvelopeVol=0;
goto ENDX; // -> and done for this channel
}
predict_nr=(s32)start[0];
shift_factor=predict_nr&0xf;
predict_nr >>= 4;
flags=(s32)start[1];
start += 2;
pChannel->iSBPos=0;
// decode the 16byte packet
s_1=pChannel->s_1;
s_2=pChannel->s_2;
for (nSample=0; nSample<28; ++start)
{
s32 s;
s32 d = (s32)*start;
s = ((d & 0xf)<<12);
if (s & 0x8000) s |= 0xffff0000;
fa = (s >> shift_factor);
fa += ((s_1 * f[predict_nr][0]) >> 6) + ((s_2 * f[predict_nr][1]) >> 6);
s_2 = s_1;
s_1 = fa;
pChannel->SB[nSample++]=fa;
s = ((d & 0xf0) << 8);
if (s & 0x8000) s|=0xffff0000;
fa = (s>>shift_factor);
fa += ((s_1 * f[predict_nr][0])>>6) + ((s_2 * f[predict_nr][1]) >> 6);
s_2 = s_1;
s_1 = fa;
pChannel->SB[nSample++]=fa;
}
// irq occurs no matter what core access the address
for (s32 core = 0; core < 2; ++core)
{
if (((SPU_CONTROL_*)(spu2regs + (0x400 * core) + REG_C0_CTRL))->irq) // some callback and irq active?
{
// if irq address reached or irq on looping addr, when stop/loop flag is set
u8* pirq = (u8*)pSpuIrq[core];
if ((pirq > (start - 16) && pirq <= start) || ((flags & 1) && (pirq > (pChannel->pLoop - 16) && pirq <= pChannel->pLoop)))
{
IRQINFO |= 4<<core;
SPU2_LOG("SPU2Worker:interrupt\n");
irqCallbackSPU2();
}
}
}
// flag handler
if ((flags&4) && (!pChannel->bIgnoreLoop))
pChannel->pLoop=start-16; // loop address
if (flags&1) // 1: stop/loop
{
// We play this block out first...
dwEndChannel2[ch/24]|=(1<<(ch%24));
if (flags!=3 || pChannel->pLoop==NULL)
{ // and checking if pLoop is set avoids crashes, yeah
start = (u8*)-1;
pChannel->bStop = true;
pChannel->bIgnoreLoop = false;
}
else
{
start = pChannel->pLoop;
}
}
pChannel->pCurr=start; // store values for next cycle
pChannel->s_1=s_1;
pChannel->s_2=s_2;
}
fa=pChannel->SB[pChannel->iSBPos++]; // get sample data
pChannel->StoreInterpolationVal(fa);
pChannel->spos -= 0x10000;
}
if (pChannel->bNoise)
fa=pChannel->iGetNoiseVal(); // get noise val
else
fa=pChannel->iGetInterpolationVal(); // get sample val
s32 sval = (MixADSR(pChannel) * fa) / 1023; // mix adsr
if (pChannel->bFMod == 2) // fmod freq channel
{
iFMod[ns] = sval; // -> store 1T sample data, use that to do fmod on next channel
}
else
{
if (pChannel->bVolumeL)
s_buffers[ns][0]+=(sval * pChannel->leftvol)>>14;
if (pChannel->bVolumeR)
s_buffers[ns][1]+=(sval * pChannel->rightvol)>>14;
}
// go to the next packet
ns++;
pChannel->spos += pChannel->sinc;
}
ENDX:
;
}
// mix all channels
MixChannels(0);
MixChannels(1);
if ( g_bPlaySound )
{
assert( s_pCurOutput != NULL);
for (s32 ns=0; ns<NSSIZE; ns++)
{
// clamp and write
clampandwrite16(s_pCurOutput[0],s_buffers[ns][0]);
clampandwrite16(s_pCurOutput[1],s_buffers[ns][1]);
s_pCurOutput += 2;
s_buffers[ns][0] = 0;
s_buffers[ns][1] = 0;
}
// check if end reached
if ((uptr)s_pCurOutput - (uptr)s_pAudioBuffers[s_nCurBuffer].pbuf >= 4 * NSSIZE * NSFRAMES)
{
if ( conf.options & OPTION_RECORDING )
{
static s32 lastrectime = 0;
if (timeGetTime() - lastrectime > 5000)
{
WARN_LOG("ZeroSPU2: recording\n");
lastrectime = timeGetTime();
}
LogRawSound(s_pAudioBuffers[s_nCurBuffer].pbuf, 4, s_pAudioBuffers[s_nCurBuffer].pbuf+2, 4, NSSIZE*NSFRAMES);
}
if ( s_nQueuedBuffers >= ArraySize(s_pAudioBuffers)-1 )
{
//ZeroSPU2: dropping packets! game too fast
s_nDropPacket += NSFRAMES;
s_GlobalTimeStamp = GetMicroTime();
}
else {
// submit to final mixer
#ifdef ZEROSPU2_DEVBUILD
if ( g_logsound )
LogRawSound(s_pAudioBuffers[s_nCurBuffer].pbuf, 4, s_pAudioBuffers[s_nCurBuffer].pbuf+2, 4, NSSIZE*NSFRAMES);
#endif
if ( g_startcount == 0xffffffff )
{
g_startcount = timeGetTime();
g_packetcount = 0;
}
if ( conf.options & OPTION_TIMESTRETCH )
{
u64 newtime = GetMicroTime();
if ( s_GlobalTimeStamp == 0 )
s_GlobalTimeStamp = newtime-NSFRAMES*1000;
u32 newtotal = s_nTotalDuration-s_nDurations[s_nCurDuration];
u32 duration = (u32)(newtime-s_GlobalTimeStamp);
s_nDurations[s_nCurDuration] = duration;
s_nTotalDuration = newtotal + duration;
s_nCurDuration = (s_nCurDuration+1)%ArraySize(s_nDurations);
s_GlobalTimeStamp = newtime;
s_pAudioBuffers[s_nCurBuffer].timestamp = timeGetTime();
s_pAudioBuffers[s_nCurBuffer].avgtime = s_nTotalDuration/ArraySize(s_nDurations);
}
s_pAudioBuffers[s_nCurBuffer].len = 4*NSSIZE*NSFRAMES;
InterlockedExchangeAdd((long*)&s_nQueuedBuffers, 1);
s_nCurBuffer = (s_nCurBuffer+1)%ArraySize(s_pAudioBuffers);
s_pAudioBuffers[s_nCurBuffer].newchannels = 0; // reset
}
// restart
s_pCurOutput = (s16*)s_pAudioBuffers[s_nCurBuffer].pbuf;
}
}
}
// resamples pStereoSamples // resamples pStereoSamples
void ResampleLinear(s16* pStereoSamples, s32 oldsamples, s16* pNewSamples, s32 newsamples) void ResampleLinear(s16* pStereoSamples, s32 oldsamples, s16* pNewSamples, s32 newsamples)
{ {
@ -885,10 +629,53 @@ void ResampleLinear(s16* pStereoSamples, s32 oldsamples, s16* pNewSamples, s32 n
} }
} }
static __aligned16 s16 s_ThreadBuffer[NSSIZE*NSFRAMES*2*5]; static __aligned16 s16 s_ThreadBuffer[NS_TOTAL_SIZE * 2 * 5];
// SoundTouch's INTEGER system is broken these days, so we'll need this to do float conversions... // SoundTouch's INTEGER system is broken these days, so we'll need this to do float conversions...
static __aligned16 float s_floatBuffer[NSSIZE*NSFRAMES*2*5]; static __aligned16 float s_floatBuffer[NS_TOTAL_SIZE * 2 * 5];
static __forceinline u32 GetNewSamples(s32 nReadBuf)
{
u32 NewSamples = s_pAudioBuffers[nReadBuf].avgtime / 1000;
s32 bytesbuf = SoundGetBytesBuffered() * 10;
if (bytesbuf < MaxBuffer)
{
NewSamples += 1;
}
else if (bytesbuf > MaxBuffer * 5)
{
// check the current timestamp, if too far apart, speed up audio
//WARN_LOG("making faster %d\n", timeGetTime() - s_pAudioBuffers[nReadBuf].timestamp);
NewSamples = NewSamples - ((bytesbuf - 400000) / 100000);
}
if (s_nDropPacket > 0)
{
s_nDropPacket--;
NewSamples -= 1;
}
return min(NewSamples * NSSIZE, NS_TOTAL_SIZE * 3);
}
static __forceinline void ExtractSamples()
{
// extract 2*NSFRAMES ms at a time
s32 nOutSamples;
do
{
nOutSamples = pSoundTouch->receiveSamples(s_floatBuffer, NS_TOTAL_SIZE * 5);
if ( nOutSamples > 0 )
{
for( s32 sx=0; sx<nOutSamples*2; sx++ )
s_ThreadBuffer[sx] = (s16)(s_floatBuffer[sx]*65536.0f);
SoundFeedVoiceData((u8*)s_ThreadBuffer, nOutSamples * 4);
}
} while (nOutSamples != 0);
}
// communicates with the audio hardware // communicates with the audio hardware
#ifdef _WIN32 #ifdef _WIN32
@ -908,8 +695,7 @@ void* SPU2ThreadProc(void* lpParam)
{ {
//Sleeping //Sleeping
Sleep(1); Sleep(1);
if ( s_bThreadExit ) if (s_bThreadExit) return NULL;
return NULL;
} }
while( SoundGetBytesBuffered() > 72000 ) while( SoundGetBytesBuffered() > 72000 )
@ -917,7 +703,7 @@ void* SPU2ThreadProc(void* lpParam)
//Bytes buffered //Bytes buffered
Sleep(1); Sleep(1);
if ( s_bThreadExit ) return NULL; if (s_bThreadExit) return NULL;
} }
} }
else else
@ -929,63 +715,28 @@ void* SPU2ThreadProc(void* lpParam)
} }
} }
if (conf.options & OPTION_TIMESTRETCH)
//s32 ps2delay = timeGetTime() - s_pAudioBuffers[nReadBuf].timestamp;
s32 NewSamples = s_pAudioBuffers[nReadBuf].avgtime;
if ( (conf.options & OPTION_TIMESTRETCH) )
{ {
u32 NewSamples = GetNewSamples(nReadBuf);
s32 bytesbuf = SoundGetBytesBuffered(); s32 OldSamples = s_pAudioBuffers[nReadBuf].len / 4;
if ( bytesbuf < MaxBuffer / 10 )
NewSamples += 1000;
// check the current timestamp, if too far apart, speed up audio
else if ( bytesbuf > MaxBuffer / 2 )
{
//WARN_LOG("making faster %d\n", timeGetTime() - s_pAudioBuffers[nReadBuf].timestamp);
NewSamples -= (bytesbuf-40000)/10;//*(ps2delay-NewSamples*8/1000);
}
if ( s_nDropPacket > 0 )
{
s_nDropPacket--;
NewSamples -= 1000;
}
NewSamples *= NSSIZE;
NewSamples /= 1000;
NewSamples = min(NewSamples, NSFRAMES * NSSIZE * 3);
s32 oldsamples = s_pAudioBuffers[nReadBuf].len / 4;
if ((nReadBuf & 3) == 0) // wow, this if statement makes the whole difference if ((nReadBuf & 3) == 0) // wow, this if statement makes the whole difference
pSoundTouch->setTempoChange(100.0f*(float)oldsamples/(float)NewSamples - 100.0f); {
//SPU2_LOG("OldSamples = %d; NewSamples = %d/n", OldSamples, NewSamples);
float percent = ((float)OldSamples/(float)NewSamples - 1.0f) * 100.0f;
pSoundTouch->setTempoChange(percent);
}
for( s32 sx=0; sx<oldsamples*2; sx++ ) for( s32 sx = 0; sx < OldSamples * 2; sx++ )
s_floatBuffer[sx] = ((s16*)s_pAudioBuffers[nReadBuf].pbuf)[sx]/65536.0f; s_floatBuffer[sx] = ((s16*)s_pAudioBuffers[nReadBuf].pbuf)[sx]/65536.0f;
pSoundTouch->putSamples(s_floatBuffer, oldsamples); pSoundTouch->putSamples(s_floatBuffer, OldSamples);
ExtractSamples();
// extract 2*NSFRAMES ms at a time
s32 nOutSamples;
do
{
nOutSamples = pSoundTouch->receiveSamples(s_floatBuffer, NSSIZE * NSFRAMES * 5);
if ( nOutSamples > 0 )
{
for( s32 sx=0; sx<nOutSamples*2; sx++ )
s_ThreadBuffer[sx] = (s16)(s_floatBuffer[sx]*65536.0f);
SoundFeedVoiceData((u8*)s_ThreadBuffer, nOutSamples * 4);
}
} while (nOutSamples != 0);
} }
else else
{
SoundFeedVoiceData(s_pAudioBuffers[nReadBuf].pbuf, s_pAudioBuffers[nReadBuf].len); SoundFeedVoiceData(s_pAudioBuffers[nReadBuf].pbuf, s_pAudioBuffers[nReadBuf].len);
}
// don't go to the next buffer unless there is more data buffered // don't go to the next buffer unless there is more data buffered
nReadBuf = (nReadBuf+1)%ArraySize(s_pAudioBuffers); nReadBuf = (nReadBuf+1)%ArraySize(s_pAudioBuffers);
@ -1147,7 +898,7 @@ void CALLBACK SPU2write(u32 mem, u16 value)
pvoice->iStartAddr=(((u32)value&0x3f)<<16)|(pvoice->iStartAddr&0xFFFF); pvoice->iStartAddr=(((u32)value&0x3f)<<16)|(pvoice->iStartAddr&0xFFFF);
pvoice->pStart=(u8*)(spu2mem+pvoice->iStartAddr); pvoice->pStart=(u8*)(spu2mem+pvoice->iStartAddr);
break; break;
case 0x1C2: case REG_VA_SSA + 2:
pvoice->iStartAddr=(pvoice->iStartAddr & 0x3f0000) | (value & 0xFFFF); pvoice->iStartAddr=(pvoice->iStartAddr & 0x3f0000) | (value & 0xFFFF);
pvoice->pStart=(u8*)(spu2mem+pvoice->iStartAddr); pvoice->pStart=(u8*)(spu2mem+pvoice->iStartAddr);
break; break;
@ -1156,7 +907,7 @@ void CALLBACK SPU2write(u32 mem, u16 value)
pvoice->pLoop=(u8*)(spu2mem+pvoice->iLoopAddr); pvoice->pLoop=(u8*)(spu2mem+pvoice->iLoopAddr);
pvoice->bIgnoreLoop=pvoice->iLoopAddr>0; pvoice->bIgnoreLoop=pvoice->iLoopAddr>0;
break; break;
case 0x1C6: case REG_VA_LSAX + 2:
pvoice->iLoopAddr=(pvoice->iLoopAddr& 0x3f0000) | (value & 0xFFFF); pvoice->iLoopAddr=(pvoice->iLoopAddr& 0x3f0000) | (value & 0xFFFF);
pvoice->pLoop=(u8*)(spu2mem+pvoice->iLoopAddr); pvoice->pLoop=(u8*)(spu2mem+pvoice->iLoopAddr);
pvoice->bIgnoreLoop=pvoice->iLoopAddr>0; pvoice->bIgnoreLoop=pvoice->iLoopAddr>0;
@ -1165,7 +916,7 @@ void CALLBACK SPU2write(u32 mem, u16 value)
// unused... check if it gets written as well // unused... check if it gets written as well
pvoice->iNextAddr=(((u32)value&0x3f)<<16)|(pvoice->iNextAddr&0xFFFF); pvoice->iNextAddr=(((u32)value&0x3f)<<16)|(pvoice->iNextAddr&0xFFFF);
break; break;
case 0x1CA: case REG_VA_NAX + 2:
// unused... check if it gets written as well // unused... check if it gets written as well
pvoice->iNextAddr=(pvoice->iNextAddr & 0x3f0000) | (value & 0xFFFF); pvoice->iNextAddr=(pvoice->iNextAddr & 0x3f0000) | (value & 0xFFFF);
break; break;
@ -1182,7 +933,7 @@ void CALLBACK SPU2write(u32 mem, u16 value)
spu2mem[spuaddr] = value; spu2mem[spuaddr] = value;
spuaddr++; spuaddr++;
if ((spu2Ru16(REG_C0_CTRL)&0x40) && (C0_IRQA() == spuaddr)) if (spu2attr0.irq && (C0_IRQA() == spuaddr))
{ {
IRQINFO |= 4; IRQINFO |= 4;
SPU2_LOG("SPU2write:C0_CPUDATA interrupt\n"); SPU2_LOG("SPU2write:C0_CPUDATA interrupt\n");
@ -1192,8 +943,8 @@ void CALLBACK SPU2write(u32 mem, u16 value)
if (spuaddr>0xFFFFE) spuaddr = 0x2800; if (spuaddr>0xFFFFE) spuaddr = 0x2800;
C0_SPUADDR_SET(spuaddr); C0_SPUADDR_SET(spuaddr);
spu2Ru16(REG_C0_SPUSTAT)&=~0x80; spu2stat_clear_80(0);
spu2Ru16(REG_C0_CTRL)&=~0x30; spu2attr1.irq = 0;
break; break;
case REG_C1_SPUDATA: case REG_C1_SPUDATA:
@ -1201,7 +952,7 @@ void CALLBACK SPU2write(u32 mem, u16 value)
spu2mem[spuaddr] = value; spu2mem[spuaddr] = value;
spuaddr++; spuaddr++;
if ((spu2Ru16(REG_C1_CTRL)&0x40) && (C1_IRQA() == spuaddr)) if (spu2attr1.irq && (C1_IRQA() == spuaddr))
{ {
IRQINFO |= 8; IRQINFO |= 8;
SPU2_LOG("SPU2write:C1_CPUDATA interrupt\n"); SPU2_LOG("SPU2write:C1_CPUDATA interrupt\n");
@ -1211,8 +962,8 @@ void CALLBACK SPU2write(u32 mem, u16 value)
if (spuaddr>0xFFFFE) spuaddr = 0x2800; if (spuaddr>0xFFFFE) spuaddr = 0x2800;
C1_SPUADDR_SET(spuaddr); C1_SPUADDR_SET(spuaddr);
spu2Ru16(REG_C1_SPUSTAT)&=~0x80; spu2stat_clear_80(1);
spu2Ru16(REG_C1_CTRL)&=~0x30; spu2attr0.irq = 0;
break; break;
case REG_C0_IRQA_HI: case REG_C0_IRQA_HI:
@ -1301,38 +1052,37 @@ u16 CALLBACK SPU2read(u32 mem)
if ((r>=REG_VA_SSA && r<REG_A_ESA) || (r>=0x05c0 && r<0x06E0)) // some channel info? if ((r>=REG_VA_SSA && r<REG_A_ESA) || (r>=0x05c0 && r<0x06E0)) // some channel info?
{ {
s32 ch=0; s32 ch = 0;
u32 rx = r; u32 rx = r;
if (rx >=0x400) if (rx >= 0x400)
{ {
ch=24; ch = 24;
rx-=0x400; rx -= 0x400;
} }
ch+=((rx-0x1c0)/12); ch += ((rx - 0x1c0) / 12);
rx-=(ch%24)*12; rx -= (ch % 24) * 12;
VOICE_PROCESSED* pvoice = &voices[ch]; VOICE_PROCESSED* pvoice = &voices[ch];
// Note - can we generalize this?
switch(rx) switch(rx)
{ {
case REG_VA_SSA: case REG_VA_SSA:
ret = ((((uptr)pvoice->pStart-(uptr)spu2mem)>>17)&0x3F); ret = ((((uptr)pvoice->pStart-(uptr)spu2mem)>>17)&0x3F);
break; break;
case 0x1C2: case REG_VA_SSA + 2:
ret = ((((uptr)pvoice->pStart-(uptr)spu2mem)>>1)&0xFFFF); ret = ((((uptr)pvoice->pStart-(uptr)spu2mem)>>1)&0xFFFF);
break; break;
case REG_VA_LSAX: case REG_VA_LSAX:
ret = ((((uptr)pvoice->pLoop-(uptr)spu2mem)>>17)&0x3F); ret = ((((uptr)pvoice->pLoop-(uptr)spu2mem)>>17)&0x3F);
break; break;
case 0x1C6: case REG_VA_LSAX + 2:
ret = ((((uptr)pvoice->pLoop-(uptr)spu2mem)>>1)&0xFFFF); ret = ((((uptr)pvoice->pLoop-(uptr)spu2mem)>>1)&0xFFFF);
break; break;
case REG_VA_NAX: case REG_VA_NAX:
ret = ((((uptr)pvoice->pCurr-(uptr)spu2mem)>>17)&0x3F); ret = ((((uptr)pvoice->pCurr-(uptr)spu2mem)>>17)&0x3F);
break; break;
case 0x1CA: case REG_VA_NAX + 2:
ret = ((((uptr)pvoice->pCurr-(uptr)spu2mem)>>1)&0xFFFF); ret = ((((uptr)pvoice->pCurr-(uptr)spu2mem)>>1)&0xFFFF);
break; break;
} }
@ -1350,6 +1100,7 @@ u16 CALLBACK SPU2read(u32 mem)
if (spuaddr > 0xfffff) spuaddr=0; if (spuaddr > 0xfffff) spuaddr=0;
C0_SPUADDR_SET(spuaddr); C0_SPUADDR_SET(spuaddr);
break; break;
case REG_C1_SPUDATA: case REG_C1_SPUDATA:
spuaddr = C1_SPUADDR(); spuaddr = C1_SPUADDR();
ret = spu2mem[spuaddr]; ret = spu2mem[spuaddr];
@ -1416,69 +1167,6 @@ s32 CALLBACK SPU2test()
return 0; return 0;
} }
#define SetPacket(s) \
{ \
if (s & 0x8000) s|=0xffff0000; \
fa = (s >> shift_factor); \
fa += ((s_1 * f[predict_nr][0]) >> 6) + ((s_2 * f[predict_nr][1]) >> 6); \
s_2 = s_1; \
s_1 = fa; \
buf[nSample++] = fa; \
}
// size is in bytes
void LogPacketSound(void* packet, s32 memsize)
{
u16 buf[28];
u8* pstart = (u8*)packet;
s32 s_1 = 0, s_2=0;
for (s32 i = 0; i < memsize; i += 16)
{
s32 predict_nr=(s32)pstart[0];
s32 shift_factor=predict_nr&0xf;
predict_nr >>= 4;
pstart += 2;
for (s32 nSample=0;nSample<28; ++pstart)
{
s32 d=(s32)*pstart;
s32 s, fa;
s =((d & 0xf) << 12);
SetPacket(s);
s=((d & 0xf0) << 8);
SetPacket(s);
}
LogRawSound(buf, 2, buf, 2, 28);
}
}
void LogRawSound(void* pleft, s32 leftstride, void* pright, s32 rightstride, s32 numsamples)
{
if (g_pWavRecord == NULL )
g_pWavRecord = new WavOutFile(RECORD_FILENAME, SAMPLE_RATE, 16, 2);
u8* left = (u8*)pleft;
u8* right = (u8*)pright;
static vector<s16> tempbuf;
tempbuf.resize(2 * numsamples);
for (s32 i = 0; i < numsamples; ++i)
{
tempbuf[2*i+0] = *(s16*)left;
tempbuf[2*i+1] = *(s16*)right;
left += leftstride;
right += rightstride;
}
g_pWavRecord->write(&tempbuf[0], numsamples*2);
}
int CALLBACK SPU2setupRecording(int start, void* pData) int CALLBACK SPU2setupRecording(int start, void* pData)
{ {
LOG_CALLBACK("SPU2setupRecording()\n"); LOG_CALLBACK("SPU2setupRecording()\n");
@ -1499,10 +1187,9 @@ int CALLBACK SPU2setupRecording(int start, void* pData)
void save_data(freezeData *data) void save_data(freezeData *data)
{ {
SPU2freezeData *spud; SPU2freezeData *spud;
s32 i;
spud = (SPU2freezeData*)data->data; spud = (SPU2freezeData*)data->data;
spud->version = 0x70000001; spud->version = 0x70000002;
memcpy(spud->spu2regs, spu2regs, 0x10000); memcpy(spud->spu2regs, spu2regs, 0x10000);
memcpy(spud->spu2mem, spu2mem, 0x200000); memcpy(spud->spu2mem, spu2mem, 0x200000);
@ -1510,8 +1197,8 @@ void save_data(freezeData *data)
spud->nSpuIrq[0] = (s32)(pSpuIrq[0] - spu2mem); spud->nSpuIrq[0] = (s32)(pSpuIrq[0] - spu2mem);
spud->nSpuIrq[1] = (s32)(pSpuIrq[1] - spu2mem); spud->nSpuIrq[1] = (s32)(pSpuIrq[1] - spu2mem);
memcpy(spud->dwNewChannel2, dwNewChannel2, 4*2); memcpy(spud->dwNewChannel2, dwNewChannel2, sizeof(dwNewChannel2));
memcpy(spud->dwEndChannel2, dwEndChannel2, 4*2); memcpy(spud->dwEndChannel2, dwEndChannel2, sizeof(dwEndChannel2));
spud->dwNoiseVal = dwNoiseVal; spud->dwNoiseVal = dwNoiseVal;
spud->interrupt = interrupt; spud->interrupt = interrupt;
@ -1519,10 +1206,10 @@ void save_data(freezeData *data)
memcpy(spud->iFMod, iFMod, sizeof(iFMod)); memcpy(spud->iFMod, iFMod, sizeof(iFMod));
memcpy(spud->MemAddr, MemAddr, sizeof(MemAddr)); memcpy(spud->MemAddr, MemAddr, sizeof(MemAddr));
spud->adma[0] = Adma4; spud->adma[0] = adma[0];
spud->adma[1] = Adma7; spud->adma[1] = adma[1];
spud->Adma4MemAddr = (u32)((uptr)Adma4.MemAddr - g_pDMABaseAddr); spud->AdmaMemAddr[0] = (u32)((uptr)adma[0].MemAddr - g_pDMABaseAddr);
spud->Adma7MemAddr = (u32)((uptr)Adma7.MemAddr - g_pDMABaseAddr); spud->AdmaMemAddr[1] = (u32)((uptr)adma[1].MemAddr - g_pDMABaseAddr);
spud->SPUCycles = SPUCycles; spud->SPUCycles = SPUCycles;
spud->SPUWorkerCycles = SPUWorkerCycles; spud->SPUWorkerCycles = SPUWorkerCycles;
@ -1530,7 +1217,7 @@ void save_data(freezeData *data)
memcpy(spud->SPUStartCycle, SPUStartCycle, sizeof(SPUStartCycle)); memcpy(spud->SPUStartCycle, SPUStartCycle, sizeof(SPUStartCycle));
memcpy(spud->SPUTargetCycle, SPUTargetCycle, sizeof(SPUTargetCycle)); memcpy(spud->SPUTargetCycle, SPUTargetCycle, sizeof(SPUTargetCycle));
for (i = 0; i < ArraySize(s_nDurations); ++i) for (u32 i = 0; i < ArraySize(s_nDurations); ++i)
{ {
s_nDurations[i] = NSFRAMES*1000; s_nDurations[i] = NSFRAMES*1000;
} }
@ -1539,7 +1226,7 @@ void save_data(freezeData *data)
s_nCurDuration = 0; s_nCurDuration = 0;
spud->voicesize = SPU_VOICE_STATE_SIZE; spud->voicesize = SPU_VOICE_STATE_SIZE;
for (i = 0; i < ArraySize(voices); ++i) for (u32 i = 0; i < ArraySize(voices); ++i)
{ {
memcpy(&spud->voices[i], &voices[i], SPU_VOICE_STATE_SIZE); memcpy(&spud->voices[i], &voices[i], SPU_VOICE_STATE_SIZE);
spud->voices[i].pStart = (u8*)((uptr)voices[i].pStart-(uptr)spu2mem); spud->voices[i].pStart = (u8*)((uptr)voices[i].pStart-(uptr)spu2mem);
@ -1555,11 +1242,10 @@ void save_data(freezeData *data)
void load_data(freezeData *data) void load_data(freezeData *data)
{ {
SPU2freezeData *spud; SPU2freezeData *spud;
s32 i;
spud = (SPU2freezeData*)data->data; spud = (SPU2freezeData*)data->data;
if (spud->version != 0x70000001) if (spud->version != 0x70000002)
{ {
ERROR_LOG("zerospu2: Sound data either corrupted or from another plugin. Ignoring.\n"); ERROR_LOG("zerospu2: Sound data either corrupted or from another plugin. Ignoring.\n");
return; return;
@ -1571,8 +1257,8 @@ void load_data(freezeData *data)
pSpuIrq[0] = spu2mem + spud->nSpuIrq[0]; pSpuIrq[0] = spu2mem + spud->nSpuIrq[0];
pSpuIrq[1] = spu2mem + spud->nSpuIrq[1]; pSpuIrq[1] = spu2mem + spud->nSpuIrq[1];
memcpy(dwNewChannel2, spud->dwNewChannel2, 4*2); memcpy(dwNewChannel2, spud->dwNewChannel2, sizeof(dwNewChannel2));
memcpy(dwEndChannel2, spud->dwEndChannel2, 4*2); memcpy(dwEndChannel2, spud->dwEndChannel2, sizeof(dwEndChannel2));
dwNoiseVal = spud->dwNoiseVal; dwNoiseVal = spud->dwNoiseVal;
interrupt = spud->interrupt; interrupt = spud->interrupt;
@ -1580,10 +1266,10 @@ void load_data(freezeData *data)
memcpy(iFMod, spud->iFMod, sizeof(iFMod)); memcpy(iFMod, spud->iFMod, sizeof(iFMod));
memcpy(MemAddr, spud->MemAddr, sizeof(MemAddr)); memcpy(MemAddr, spud->MemAddr, sizeof(MemAddr));
Adma4 = spud->adma[0]; adma[0] = spud->adma[0];
Adma7 = spud->adma[1]; adma[1] = spud->adma[1];
Adma4.MemAddr = (u16*)(g_pDMABaseAddr+spud->Adma4MemAddr); adma[0].MemAddr = (u16*)(g_pDMABaseAddr+spud->AdmaMemAddr[0]);
Adma7.MemAddr = (u16*)(g_pDMABaseAddr+spud->Adma7MemAddr); adma[1].MemAddr = (u16*)(g_pDMABaseAddr+spud->AdmaMemAddr[1]);
SPUCycles = spud->SPUCycles; SPUCycles = spud->SPUCycles;
SPUWorkerCycles = spud->SPUWorkerCycles; SPUWorkerCycles = spud->SPUWorkerCycles;
@ -1591,7 +1277,7 @@ void load_data(freezeData *data)
memcpy(SPUStartCycle, spud->SPUStartCycle, sizeof(SPUStartCycle)); memcpy(SPUStartCycle, spud->SPUStartCycle, sizeof(SPUStartCycle));
memcpy(SPUTargetCycle, spud->SPUTargetCycle, sizeof(SPUTargetCycle)); memcpy(SPUTargetCycle, spud->SPUTargetCycle, sizeof(SPUTargetCycle));
for (i = 0; i < ArraySize(voices); ++i) for (u32 i = 0; i < ArraySize(voices); ++i)
{ {
memcpy(&voices[i], &spud->voices[i], min((s32)SPU_VOICE_STATE_SIZE, spud->voicesize)); memcpy(&voices[i], &spud->voices[i], min((s32)SPU_VOICE_STATE_SIZE, spud->voicesize));
voices[i].pStart = (u8*)((uptr)spud->voices[i].pStart+(uptr)spu2mem); voices[i].pStart = (u8*)((uptr)spud->voices[i].pStart+(uptr)spu2mem);
@ -1602,7 +1288,7 @@ void load_data(freezeData *data)
s_GlobalTimeStamp = 0; s_GlobalTimeStamp = 0;
g_startcount = 0xffffffff; g_startcount = 0xffffffff;
for (s32 i = 0; i < ArraySize(s_nDurations); ++i) for (u32 i = 0; i < ArraySize(s_nDurations); ++i)
{ {
s_nDurations[i] = NSFRAMES*1000; s_nDurations[i] = NSFRAMES*1000;
} }

View File

@ -46,6 +46,7 @@ extern string s_strIniPath;
#define LOG_CALLBACK 0&& #define LOG_CALLBACK 0&&
#endif #endif
#define ZEROSPU2_DEVBUILD
#ifdef ZEROSPU2_DEVBUILD #ifdef ZEROSPU2_DEVBUILD
#define SPU2_LOG __Log //dev mode #define SPU2_LOG __Log //dev mode
#else #else
@ -60,36 +61,43 @@ extern string s_strIniPath;
#define SPU2_BUILD 4 // increase that with each version #define SPU2_BUILD 4 // increase that with each version
#define SPU2_MINOR 6 #define SPU2_MINOR 6
#define OPTION_TIMESTRETCH 1 // stretches samples without changing pitch to reduce cracking enum zerospu2_options
#define OPTION_REALTIME 2 // sync to real time instead of ps2 time {
#define OPTION_MUTE 4 // don't output anything OPTION_TIMESTRETCH = 1, // stretches samples without changing pitch to reduce cracking
#define OPTION_RECORDING 8 OPTION_REALTIME = 2, // sync to real time instead of ps2 time
OPTION_MUTE = 4, // don't output anything
OPTION_RECORDING = 8
};
// ADSR constants // ADSR constants
#define ATTACK_MS 494L enum adsr_ms
#define DECAYHALF_MS 286L {
#define DECAY_MS 572L ATTACK_MS = 494L,
#define SUSTAIN_MS 441L DECAYHALF_MS = 286L,
#define RELEASE_MS 437L DECAY_MS = 572L,
SUSTAIN_MS = 441L,
RELEASE_MS = 437L
};
#define CYCLES_PER_MS (36864000/1000) const s32 CYCLES_PER_MS = 36864000 / 1000;
#define AUDIO_BUFFER 2048 const u32 AUDIO_BUFFER = 2048;
#define NSSIZE 48 // ~ 1 ms of data const u32 NSSIZE = 48; // ~ 1 ms of data
#define NSFRAMES 16 // gather at least NSFRAMES of NSSIZE before submitting const u32 NSFRAMES = 16; // gather at least NSFRAMES of NSSIZE before submitting
#define NSPACKETS 24 const u32 NSPACKETS = 24;
const u32 NS_TOTAL_SIZE = NSSIZE * NSFRAMES;
#define SPU_NUMBER_VOICES 48 const u32 SPU_NUMBER_VOICES = 48;
#define SAMPLE_RATE 48000L const u32 SAMPLE_RATE = 48000L;
#define RECORD_FILENAME "zerospu2.wav" #define RECORD_FILENAME "zerospu2.wav"
extern s8 *spu2regs; extern s8 *spu2regs;
extern u16* spu2mem; extern u16* spu2mem;
extern s32 iFMod[NSSIZE]; extern s32 iFMod[NSSIZE];
extern u32 MemAddr[2]; extern u32 MemAddr[2];
extern u32 dwNoiseVal; // global noise generator extern u32 dwNoiseVal; // global noise generator
// functions of main emu, called on spu irq // functions of main emu, called on spu irq
extern void (*irqCallbackSPU2)(); extern void (*irqCallbackSPU2)();
@ -115,11 +123,11 @@ void SaveConfig();
void LoadConfig(); void LoadConfig();
void SysMessage(char *fmt, ...); void SysMessage(char *fmt, ...);
void LogRawSound(void* pleft, int leftstride, void* pright, int rightstride, int numsamples); extern void LogRawSound(void* pleft, int leftstride, void* pright, int rightstride, int numsamples);
void LogPacketSound(void* packet, int memsize); extern void LogPacketSound(void* packet, int memsize);
// simulate SPU2 for 1ms // simulate SPU2 for 1ms
void SPU2Worker(); extern void SPU2Worker();
// hardware sound functions // hardware sound functions
int SetupSound(); // if successful, returns 0 int SetupSound(); // if successful, returns 0
@ -128,95 +136,25 @@ int SoundGetBytesBuffered();
// returns 0 is successful, else nonzero // returns 0 is successful, else nonzero
void SoundFeedVoiceData(unsigned char* pSound,long lBytes); void SoundFeedVoiceData(unsigned char* pSound,long lBytes);
#define clamp16(dest) \ static __forceinline void clamp16(s32 &dest)
{ \
if ( dest < -32768L ) \
dest = -32768L; \
else if ( dest > 32767L ) \
dest = 32767L; \
}
#define clampandwrite16(dest,value) \
{ \
if ( value < -32768 ) \
dest = -32768; \
else if ( value > 32767 ) \
dest = 32767; \
else \
dest = (s16)value; \
}
#define spu2Rs16(mem) (*(s16*)&spu2regs[(mem) & 0xffff])
#define spu2Ru16(mem) (*(u16*)&spu2regs[(mem) & 0xffff])
#define IRQINFO spu2Ru16(REG_IRQINFO)
static __forceinline u32 SPU2_GET32BIT(u32 lo, u32 hi)
{ {
return (((u32)(spu2Ru16(hi) & 0x3f) << 16) | (u32)spu2Ru16(lo)); if (dest < -32768L)
dest = -32768L;
else if (dest > 32767L)
dest = 32767L;
} }
static __forceinline void SPU2_SET32BIT(u32 value, u32 lo, u32 hi) static __forceinline void clampandwrite16(s16 &dest, s32 &value)
{ {
spu2Ru16(hi) = ((value) >> 16) & 0x3f; if (value < -32768)
spu2Ru16(lo) = (value) & 0xffff; dest = -32768;
else if (value > 32767)
dest = 32767;
else
dest = (s16)value;
} }
static __forceinline u32 C0_IRQA() struct tSPU_ATTR
{
return SPU2_GET32BIT(REG_C0_IRQA_LO, REG_C0_IRQA_HI);
}
static __forceinline u32 C1_IRQA()
{
return SPU2_GET32BIT(REG_C1_IRQA_LO, REG_C1_IRQA_HI);
}
static __forceinline u32 C_IRQA(s32 c)
{
if (c == 0)
return C0_IRQA();
else
return C1_IRQA();
}
static __forceinline u32 C0_SPUADDR()
{
return SPU2_GET32BIT(REG_C0_SPUADDR_LO, REG_C0_SPUADDR_HI);
}
static __forceinline u32 C1_SPUADDR()
{
return SPU2_GET32BIT(REG_C1_SPUADDR_LO, REG_C1_SPUADDR_HI);
}
static __forceinline u32 C_SPUADDR(s32 c)
{
if (c == 0)
return C0_SPUADDR();
else
return C1_SPUADDR();
}
static __forceinline void C0_SPUADDR_SET(u32 value)
{
SPU2_SET32BIT(value, REG_C0_SPUADDR_LO, REG_C0_SPUADDR_HI);
}
static __forceinline void C1_SPUADDR_SET(u32 value)
{
SPU2_SET32BIT(value, REG_C1_SPUADDR_LO, REG_C1_SPUADDR_HI);
}
static __forceinline void C_SPUADDR_SET(u32 value, s32 c)
{
if (c == 0)
C0_SPUADDR_SET(value);
else
C1_SPUADDR_SET(value);
}
struct SPU_CONTROL_
{ {
u16 extCd : 1; u16 extCd : 1;
u16 extAudio : 1; u16 extAudio : 1;
@ -229,6 +167,99 @@ struct SPU_CONTROL_
u16 spuUnmute : 1; u16 spuUnmute : 1;
u16 spuon : 1; u16 spuon : 1;
}; };
#define channel_test(channel) ((channel == 4) || (channel == 0))
#define spu2Rs16(mem) (*(s16*)&spu2regs[(mem) & 0xffff])
#define spu2Ru16(mem) (*(u16*)&spu2regs[(mem) & 0xffff])
#define spu2attr0 (*(tSPU_ATTR*)&spu2regs[REG_C0_CTRL])
#define spu2attr1 (*(tSPU_ATTR*)&spu2regs[REG_C1_CTRL])
static __forceinline u16 c_offset(u32 ch)
{
return channel_test(ch) ? 0x0 : 0x400;
}
static __forceinline tSPU_ATTR spu2attr(u32 channel)
{
return channel_test(channel) ? spu2attr0 : spu2attr1;
}
static __forceinline bool spu2admas(u32 channel)
{
return channel_test(channel) ? !!(spu2Ru16(REG_C0_ADMAS) & 0x1) : !!(spu2Ru16(REG_C1_ADMAS) & 0x2);
}
static __forceinline u16 spu2mmix(u32 channel)
{
return channel_test(channel) ? spu2Ru16(REG_C0_MMIX) : spu2Ru16(REG_C1_MMIX);
}
static __forceinline u16 spu2stat(u32 channel)
{
return channel_test(channel) ? spu2Ru16(REG_C0_SPUSTAT) : spu2Ru16(REG_C1_SPUSTAT);
}
static __forceinline void spu2stat_clear_80(u32 channel)
{
if channel_test(channel)
spu2Ru16(REG_C0_SPUSTAT) &= ~0x80;
else
spu2Ru16(REG_C1_SPUSTAT) &= ~0x80;
}
static __forceinline void spu2stat_set_80(u32 channel)
{
if channel_test(channel)
spu2Ru16(REG_C0_SPUSTAT) |= 0x80;
else
spu2Ru16(REG_C1_SPUSTAT) |= 0x80;
}
#define IRQINFO spu2Ru16(REG_IRQINFO)
#define spu2_core_regs_0 (*(core_registers*)&spu2regs[0x0])
#define spu2_core_regs_1 (*(core_registers*)&spu2regs[0x400])
static __forceinline u32 SPU2_GET32BIT(u32 lo, u32 hi)
{
return (((u32)(spu2Ru16(hi) & 0x3f) << 16) | (u32)spu2Ru16(lo));
}
static __forceinline void SPU2_SET32BIT(u32 value, u32 lo, u32 hi)
{
spu2Ru16(hi) = ((value) >> 16) & 0x3f;
spu2Ru16(lo) = (value) & 0xffff;
}
#define C0_IRQA() C_IRQA(0)
#define C1_IRQA() C_IRQA(1)
#define C0_SPUADDR() C_SPUADDR(0)
#define C1_SPUADDR() C_SPUADDR(1)
#define C0_SPUADDR_SET(val) C_SPUADDR_SET(val, 0)
#define C1_SPUADDR_SET(val) C_SPUADDR_SET(val, 1)
static __forceinline u32 C_IRQA(s32 c)
{
if (c == 0)
SPU2_GET32BIT(REG_C0_IRQA_LO, REG_C0_IRQA_HI);
else
SPU2_GET32BIT(REG_C1_IRQA_LO, REG_C1_IRQA_HI);
}
static __forceinline u32 C_SPUADDR(s32 c)
{
if (c == 0)
return SPU2_GET32BIT(REG_C0_SPUADDR_LO, REG_C0_SPUADDR_HI);
else
return SPU2_GET32BIT(REG_C1_SPUADDR_LO, REG_C1_SPUADDR_HI);
}
static __forceinline void C_SPUADDR_SET(u32 value, s32 c)
{
if (c == 0)
SPU2_SET32BIT(value, REG_C0_SPUADDR_LO, REG_C0_SPUADDR_HI);
else
SPU2_SET32BIT(value, REG_C1_SPUADDR_LO, REG_C1_SPUADDR_HI);
}
#if defined(_MSC_VER) #if defined(_MSC_VER)
#pragma pack(1) #pragma pack(1)
@ -313,9 +344,10 @@ struct VOICE_PROCESSED
s32 iGetNoiseVal(); s32 iGetNoiseVal();
void StoreInterpolationVal(int fa); void StoreInterpolationVal(int fa);
s32 iGetInterpolationVal(); s32 iGetInterpolationVal();
s32 iGetVal();
void Stop(); void Stop();
SPU_CONTROL_* GetCtrl(); tSPU_ATTR* GetCtrl();
// start save state // start save state
s32 leftvol, rightvol; // left right volumes s32 leftvol, rightvol; // left right volumes
@ -326,11 +358,9 @@ struct VOICE_PROCESSED
s32 sinc; s32 sinc;
s32 iIrqDone; // debug irq done flag s32 iIrqDone; // debug irq done flag
s32 s_1; // last decoding infos s32 s_1, s_2; // last decoding infos
s32 s_2;
s32 iOldNoise; // old noise val for this channel s32 iOldNoise; // old noise val for this channel
s32 iActFreq; // current psx pitch s32 iActFreq, iUsedFreq; // current psx pitch & pc pitch
s32 iUsedFreq; // current pc pitch
s32 iStartAddr, iLoopAddr, iNextAddr; s32 iStartAddr, iLoopAddr, iNextAddr;
s32 bFMod; s32 bFMod;
@ -351,6 +381,27 @@ struct VOICE_PROCESSED
u8* pLoop, *pCurr; u8* pLoop, *pCurr;
_SPU_VOICE* pvoice; _SPU_VOICE* pvoice;
u32 memchannel;
void init(s32 i)
{
chanid = i;
if (chanid > 23)
{
memoffset = 0x400;
memchannel = 1;
}
else
{
memoffset = 0x0;
memchannel = 0;
}
pLoop = pStart = pCurr = (u8*)spu2mem;
pvoice = (_SPU_VOICE*)((u8*)spu2regs + memoffset) + (i % 24);
ADSRX.SustainLevel = 1024; // -> init sustain
}
}; };
struct AUDIOBUFFER struct AUDIOBUFFER
@ -374,8 +425,7 @@ struct ADMA
// used to make sure that ADMA doesn't get interrupted with a writeDMA call // used to make sure that ADMA doesn't get interrupted with a writeDMA call
}; };
extern ADMA Adma4; extern ADMA adma[2];
extern ADMA Adma7;
struct SPU2freezeData struct SPU2freezeData
{ {
@ -389,7 +439,7 @@ struct SPU2freezeData
s32 iFMod[NSSIZE]; s32 iFMod[NSSIZE];
u32 MemAddr[2]; u32 MemAddr[2];
ADMA adma[2]; ADMA adma[2];
u32 Adma4MemAddr, Adma7MemAddr; u32 AdmaMemAddr[2];
s32 SPUCycles, SPUWorkerCycles; s32 SPUCycles, SPUWorkerCycles;
s32 SPUStartCycle[2]; s32 SPUStartCycle[2];

View File

@ -0,0 +1,323 @@
/* ZeroSPU2
* Copyright (C) 2006-2010 zerofrog
*
* 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; either version 2 of the License, or
* (at your option) any later version.
*
* 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 for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "zerospu2.h"
#include "zeroworker.h"
#include "SoundTouch/SoundTouch.h"
#include "SoundTouch/WavFile.h"
s32 g_logsound = 0;
WavOutFile* g_pWavRecord=NULL; // used for recording
const s32 f[5][2] = {
{ 0, 0 },
{ 60, 0 },
{ 115, -52 },
{ 98, -55 },
{ 122, -60 } };
s32 predict_nr, shift_factor, flags;
s32 s_1, s_2;
static __forceinline s32 SetPacket(s32 val)
{
s32 ret;
if (val & 0x8000) val |= 0xffff0000;
ret = (val >> shift_factor);
ret += ((s_1 * f[predict_nr][0]) >> 6) + ((s_2 * f[predict_nr][1]) >> 6);
s_2 = s_1;
s_1 = ret;
return ret;
}
void SPU2Loop(VOICE_PROCESSED* pChannel, u32 ch)
{
u8* start;
u32 nSample;
for (u32 ns = 0; ns < NSSIZE; ns++)
{
// fmod freq channel
if (pChannel->bFMod == 1 && iFMod[ns]) pChannel->FModChangeFrequency(ns);
while(pChannel->spos >= 0x10000 )
{
if (pChannel->iSBPos == 28) // 28 reached?
{
start=pChannel->pCurr; // set up the current pos
// special "stop" sign
if (start == (u8*)-1) //!pChannel->bOn
{
pChannel->bOn = false; // -> turn everything off
pChannel->ADSRX.lVolume = 0;
pChannel->ADSRX.EnvelopeVol = 0;
return; // -> and done for this channel
}
predict_nr = (s32)start[0];
shift_factor = predict_nr&0xf;
predict_nr >>= 4;
flags=(s32)start[1];
start += 2;
pChannel->iSBPos = 0;
// decode the 16byte packet
s_1 = pChannel->s_1;
s_2 = pChannel->s_2;
for (nSample=0; nSample<28; ++start)
{
s32 d = (s32)*start;
s32 s;
s = ((d & 0xf)<<12);
pChannel->SB[nSample++] = SetPacket(s);
s = ((d & 0xf0) << 8);
pChannel->SB[nSample++] = SetPacket(s);
}
// irq occurs no matter what core accesses the address
for (s32 core = 0; core < 2; ++core)
{
if (spu2attr(core).irq) // some callback and irq active?
{
// if irq address reached or irq on looping addr, when stop/loop flag is set
u8* pirq = (u8*)pSpuIrq[core];
if ((pirq > (start - 16) && pirq <= start) ||
((flags & 1) && (pirq > (pChannel->pLoop - 16) && pirq <= pChannel->pLoop)))
{
IRQINFO |= 4 << core;
SPU2_LOG("SPU2Worker:interrupt\n");
irqCallbackSPU2();
}
}
}
// flag handler
if ((flags & 4) && (!pChannel->bIgnoreLoop))
pChannel->pLoop = start - 16; // loop address
if (flags & 1) // 1: stop/loop
{
// We play this block out first...
dwEndChannel2[ch / 24] |= (1 << (ch % 24));
if (flags != 3 || pChannel->pLoop == NULL)
{ // and checking if pLoop is set avoids crashes, yeah
start = (u8*)-1;
pChannel->bStop = true;
pChannel->bIgnoreLoop = false;
}
else
{
start = pChannel->pLoop;
}
}
pChannel->pCurr = start; // store values for next cycle
pChannel->s_1 = s_1;
pChannel->s_2 = s_2;
}
pChannel->StoreInterpolationVal(pChannel->SB[pChannel->iSBPos++]); // get sample data
pChannel->spos -= 0x10000;
}
s32 sval = (MixADSR(pChannel) * pChannel->iGetVal()) / 1023; // mix adsr with noise or sample val.
if (pChannel->bFMod == 2) // fmod freq channel
{
// -> store 1T sample data, use that to do fmod on next channel
if (!pChannel->bNoise) iFMod[ns] = sval;
}
else
{
if (pChannel->bVolumeL)
s_buffers[ns][0] += (sval * pChannel->leftvol) >> 14;
if (pChannel->bVolumeR)
s_buffers[ns][1] += (sval * pChannel->rightvol) >> 14;
}
pChannel->spos += pChannel->sinc;
}
}
// simulate SPU2 for 1ms
void SPU2Worker()
{
// assume s_buffers are zeroed out
if (dwNewChannel2[0] || dwNewChannel2[1]) s_pAudioBuffers[s_nCurBuffer].newchannels++;
VOICE_PROCESSED* pChannel = voices;
for (u32 ch=0; ch < SPU_NUMBER_VOICES; ch++, pChannel++) // loop em all... we will collect 1 ms of sound of each playing channel
{
if (pChannel->bNew)
{
pChannel->StartSound(); // start new sound
dwEndChannel2[ch / 24] &= ~(1 << (ch % 24)); // clear end channel bit
dwNewChannel2[ch / 24] &= ~(1 << (ch % 24)); // clear channel bit
}
if (!pChannel->bOn) continue;
if (pChannel->iActFreq != pChannel->iUsedFreq) // new psx frequency?
pChannel->VoiceChangeFrequency();
// loop until 1 ms of data is reached
SPU2Loop(pChannel, ch);
}
// mix all channels
MixChannels(0);
MixChannels(1);
if ( g_bPlaySound )
{
assert( s_pCurOutput != NULL);
for (u32 ns = 0; ns < NSSIZE; ns++)
{
// clamp and write
clampandwrite16(s_pCurOutput[0],s_buffers[ns][0]);
clampandwrite16(s_pCurOutput[1],s_buffers[ns][1]);
s_pCurOutput += 2;
s_buffers[ns][0] = 0;
s_buffers[ns][1] = 0;
}
// check if end reached
if ((uptr)s_pCurOutput - (uptr)s_pAudioBuffers[s_nCurBuffer].pbuf >= 4 * NS_TOTAL_SIZE)
{
if ( conf.options & OPTION_RECORDING )
{
static s32 lastrectime = 0;
if (timeGetTime() - lastrectime > 5000)
{
WARN_LOG("ZeroSPU2: recording\n");
lastrectime = timeGetTime();
}
LogRawSound(s_pAudioBuffers[s_nCurBuffer].pbuf, 4, s_pAudioBuffers[s_nCurBuffer].pbuf+2, 4, NS_TOTAL_SIZE);
}
if ( s_nQueuedBuffers >= ArraySize(s_pAudioBuffers)-1 )
{
//ZeroSPU2: dropping packets! game too fast
s_nDropPacket += NSFRAMES;
s_GlobalTimeStamp = GetMicroTime();
}
else {
// submit to final mixer
#ifdef ZEROSPU2_DEVBUILD
if ( g_logsound )
LogRawSound(s_pAudioBuffers[s_nCurBuffer].pbuf, 4, s_pAudioBuffers[s_nCurBuffer].pbuf + 2, 4, NS_TOTAL_SIZE);
#endif
if ( g_startcount == 0xffffffff )
{
g_startcount = timeGetTime();
g_packetcount = 0;
}
if ( conf.options & OPTION_TIMESTRETCH )
{
u32 newtotal = s_nTotalDuration - s_nDurations[s_nCurDuration];
u64 newtime = GetMicroTime();
u32 duration;
if (s_GlobalTimeStamp == 0) s_GlobalTimeStamp = newtime - NSFRAMES * 1000;
duration = (u32)(newtime-s_GlobalTimeStamp);
s_nDurations[s_nCurDuration] = duration;
s_nTotalDuration = newtotal + duration;
s_nCurDuration = (s_nCurDuration+1)%ArraySize(s_nDurations);
s_GlobalTimeStamp = newtime;
s_pAudioBuffers[s_nCurBuffer].timestamp = timeGetTime();
s_pAudioBuffers[s_nCurBuffer].avgtime = s_nTotalDuration/ArraySize(s_nDurations);
}
s_pAudioBuffers[s_nCurBuffer].len = 4 * NS_TOTAL_SIZE;
InterlockedExchangeAdd((long*)&s_nQueuedBuffers, 1);
s_nCurBuffer = (s_nCurBuffer+1)%ArraySize(s_pAudioBuffers);
s_pAudioBuffers[s_nCurBuffer].newchannels = 0; // reset
}
// restart
s_pCurOutput = (s16*)s_pAudioBuffers[s_nCurBuffer].pbuf;
}
}
}
// size is in bytes
void LogPacketSound(void* packet, s32 memsize)
{
u16 buf[28];
u8* pstart = (u8*)packet;
s_1 = s_2 = 0;
for (s32 i = 0; i < memsize; i += 16)
{
predict_nr = (s32)pstart[0];
shift_factor = predict_nr&0xf;
predict_nr >>= 4;
pstart += 2;
for (s32 nSample = 0;nSample < 28; ++pstart)
{
s32 d = (s32)*pstart;
s32 temp;
temp = ((d & 0xf) << 12);
buf[nSample++] = SetPacket(temp);
temp = ((d & 0xf0) << 8);
buf[nSample++] = SetPacket(temp);
}
LogRawSound(buf, 2, buf, 2, 28);
}
}
void LogRawSound(void* pleft, s32 leftstride, void* pright, s32 rightstride, s32 numsamples)
{
if (g_pWavRecord == NULL )
g_pWavRecord = new WavOutFile(RECORD_FILENAME, SAMPLE_RATE, 16, 2);
u8* left = (u8*)pleft;
u8* right = (u8*)pright;
static vector<s16> tempbuf;
tempbuf.resize(2 * numsamples);
for (s32 i = 0; i < numsamples; ++i)
{
tempbuf[2*i+0] = *(s16*)left;
tempbuf[2*i+1] = *(s16*)right;
left += leftstride;
right += rightstride;
}
g_pWavRecord->write(&tempbuf[0], numsamples*2);
}

View File

@ -0,0 +1,44 @@
/* ZeroSPU2
* Copyright (C) 2006-2010 zerofrog
*
* 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; either version 2 of the License, or
* (at your option) any later version.
*
* 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 for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#ifndef ZEROWORKER_H_INCLUDED
#define ZEROWORKER_H_INCLUDED
extern u32 dwNewChannel2[2]; // keeps track of what channels that have been turned on
extern u32 dwEndChannel2[2]; // keeps track of what channels have ended
extern AUDIOBUFFER s_pAudioBuffers[NSPACKETS];
extern s32 s_nCurBuffer, s_nQueuedBuffers;
extern VOICE_PROCESSED voices[SPU_NUMBER_VOICES+1]; // +1 for modulation
extern u16* pSpuIrq[2];
extern s32 s_buffers[NSSIZE][2]; // left and right buffers
extern bool g_bPlaySound; // if true, will output sound, otherwise no
extern s16* s_pCurOutput;
extern bool s_bThreadExit;
extern s32 s_nDropPacket;
extern u64 s_GlobalTimeStamp;
extern s32 s_nDurations[64];
extern s32 s_nCurDuration, s_nTotalDuration;
extern u32 g_startcount, g_packetcount;
extern void SPU2Worker();
extern void LogPacketSound(void* packet, s32 memsize);
extern void LogRawSound(void* pleft, s32 leftstride, void* pright, s32 rightstride, s32 numsamples);
extern s32 MixADSR(VOICE_PROCESSED* pvoice);
extern void MixChannels(s32 core);
#endif // ZEROWORKER_H_INCLUDED