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
voices.cpp
zerodma.cpp
zerospu2.cpp)
zerospu2.cpp
zeroworker.cpp)
# zerospu2 headers
set(zerospu2Headers
misc.h
reg.h
zerospu2.h)
zerodma.h
zerospu2.h
zeroworker.h)
# zerospu2 Linux sources
set(zerospu2LinuxSources

View File

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

View File

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

View File

@ -22,8 +22,8 @@ libZeroSPU2_LDFLAGS= @SHARED_LDFLAGS@
libZeroSPU2_LDFLAGS+=-Wl,-soname,@ZEROSPU2_SONAME@
libZeroSPU2_LDADD=$(libZeroSPU2_a_OBJECTS) libSoundTouch.a
libZeroSPU2_a_SOURCES = zerospu2.cpp voices.cpp zerodma.cpp
libZeroSPU2_a_SOURCES += zerospu2.h reg.h misc.h
libZeroSPU2_a_SOURCES = zerospu2.cpp voices.cpp zerodma.cpp zeroworker.cpp
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
#SUBDIRS = ../../3rdparty/SoundTouch

View File

@ -257,6 +257,10 @@
RelativePath="..\zerospu2.cpp"
>
</File>
<File
RelativePath="..\zeroworker.cpp"
>
</File>
</Filter>
<Filter
Name="Header Files"
@ -283,10 +287,18 @@
RelativePath="..\Targets\SoundTargets.h"
>
</File>
<File
RelativePath="..\zerodma.h"
>
</File>
<File
RelativePath="..\zerospu2.h"
>
</File>
<File
RelativePath="..\zeroworker.h"
>
</File>
</Filter>
<Filter
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.
};
// 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.
// -- arcum42
#define SPDIF_OUT_OFF 0x0000 //no spdif output

View File

@ -26,9 +26,9 @@
// 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)
@ -216,7 +216,7 @@ static void __forceinline GetNoiseValues(s32& VD)
"XOR %%eax,%%ebx\n"
"ROR %%eax,3\n"
"MOV %0,%%eax\n"
".att_syntax\n" : "r="(Seed) :"r"(Seed));
".att_syntax\n" : "=r"(Seed) :"r"(Seed));
#endif
}
@ -283,6 +283,14 @@ s32 VOICE_PROCESSED::iGetInterpolationVal()
return fa;
}
s32 VOICE_PROCESSED::iGetVal()
{
if (bNoise)
return iGetNoiseVal();
else
return iGetInterpolationVal();
}
void VOICE_PROCESSED::Stop()
{
}

View File

@ -17,6 +17,7 @@
*/
#include "zerospu2.h"
#include "zerodma.h"
#include <assert.h>
#include <stdlib.h>
@ -24,34 +25,20 @@
#include "SoundTouch/SoundTouch.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;
s32 i, dma, offset;
u32 spuaddr = C_SPUADDR(channel);
if ( core == 0)
{
dma = 4;
offset = 0;
}
else
{
dma = 7;
offset = 0x0400;
}
SPU2_LOG("SPU2 readDMAMem(%d) size %x, addr: %x\n", channel, size, pMem);
spuaddr = C_SPUADDR(core);
SPU2_LOG("SPU2 readDMA%dMem size %x, addr: %x\n", dma, size, pMem);
for (i = 0; i < size; i++)
for (u32 i = 0; i < size; i++)
{
*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);
IRQINFO |= (4 * (core + 1));
SPU2_LOG("SPU2readDMA%dMem:interrupt\n", dma);
C_SPUADDR_SET(spuaddr, channel);
IRQINFO |= (4 * (channel + 1));
SPU2_LOG("SPU2readDMAMem(%d):interrupt\n", channel);
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)
C_SPUADDR_SET(spuaddr, core);
C_SPUADDR_SET(spuaddr, channel);
// DMA complete
spu2Ru16(REG_C0_SPUSTAT + offset) &= ~0x80;
SPUStartCycle[core] = SPUCycles;
SPUTargetCycle[core] = size;
interrupt |= (1 << (1 + core));
spu2stat_clear_80(channel);
SPUStartCycle[channel] = SPUCycles;
SPUTargetCycle[channel] = size;
interrupt |= (1 << (1 + channel));
}
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
// the end of the transfer.
int ADMASWrite(int core)
int ADMASWrite(int channel)
{
u32 spuaddr;
ADMA *Adma;
s32 dma, offset;
if (core == 0)
{
Adma = &Adma4;
dma = 4;
offset = 0;
}
else
{
Adma = &Adma7;
dma = 7;
offset = 0x0400;
}
ADMA *Adma = &adma[channel];
if (interrupt & 0x2)
{
WARN_LOG("%d returning for interrupt\n", dma);
WARN_LOG("ADMASWrite(%d) returning for interrupt\n", channel);
return 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;
}
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
memcpy((s16*)(spu2mem + spuaddr + 0x2000 + offset),(s16*)Adma->MemAddr,512);
memcpy((s16*)(spu2mem + left_addr),(s16*)Adma->MemAddr,512);
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;
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));
WARN_LOG("ADMA %d Mem access:interrupt\n", dma);
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);
IRQINFO |= (4 * (channel + 1));
WARN_LOG("ADMAMem access(%d): interrupt\n", channel);
irqCallbackSPU2();
}
spuaddr = (spuaddr + 256) & 511;
C_SPUADDR_SET(spuaddr, core);
C_SPUADDR_SET(spuaddr, channel);
Adma->AmountLeft -= 512;
if (Adma->AmountLeft > 0)
return 0;
else
return 1;
return (Adma->AmountLeft <= 0);
}
void SPU2writeDMAMem(u16* pMem, int size, int core)
void SPU2writeDMAMem(u16* pMem, int size, int channel)
{
u32 spuaddr;
ADMA *Adma;
s32 dma, offset;
if (core == 0)
{
Adma = &Adma4;
dma = 4;
offset = 0;
}
else
{
Adma = &Adma7;
dma = 7;
offset = 0x0400;
}
ADMA *Adma = &adma[channel];
s32 offset = (channel == 0) ? 0 : 0x400;
SPU2_LOG("SPU2 writeDMA%dMem 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));
SPU2_LOG("SPU2 writeDMAMem size %x, addr: %x(spu2:%x)"/*, ctrl: %x, adma: %x\n"*/, \
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;
Adma->MemAddr = pMem;
Adma->AmountLeft = size;
SPUTargetCycle[core] = size;
spu2Ru16(REG_C0_SPUSTAT + offset) &= ~0x80;
SPUTargetCycle[channel] = size;
spu2stat_clear_80(channel);
if (!Adma->Enabled || (Adma->Index > 384))
{
C_SPUADDR_SET(0, core);
if (ADMASWrite(core))
C_SPUADDR_SET(0, channel);
if (ADMASWrite(channel))
{
SPUStartCycle[core] = SPUCycles;
interrupt |= (1 << (1 + core));
SPUStartCycle[channel] = SPUCycles;
interrupt |= (1 << (1 + channel));
}
}
Adma->Enabled = 1;
@ -200,50 +152,37 @@ void SPU2writeDMAMem(u16* pMem, int size, int core)
}
#ifdef ZEROSPU2_DEVBUILD
if ((conf.Log && conf.options & OPTION_RECORDING) && (core == 1))
if ((conf.Log && conf.options & OPTION_RECORDING) && (channel == 1))
LogPacketSound(pMem, 0x8000);
#endif
spuaddr = C_SPUADDR(core);
spuaddr = C_SPUADDR(channel);
memcpy((u8*)(spu2mem + spuaddr),(u8*)pMem,size << 1);
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);
SPU2_LOG("SPU2writeDMA%dMem:interrupt\n", dma);
IRQINFO |= 4 * (channel + 1);
SPU2_LOG("SPU2writeDMAMem:interrupt\n");
irqCallbackSPU2();
}
if (spuaddr > 0xFFFFE) spuaddr = 0x2800;
C_SPUADDR_SET(spuaddr, core);
C_SPUADDR_SET(spuaddr, channel);
MemAddr[core] += size << 1;
spu2Ru16(REG_C0_SPUSTAT + offset) &= ~0x80;
SPUStartCycle[core] = SPUCycles;
SPUTargetCycle[core] = size;
interrupt |= (1 << (core + 1));
MemAddr[channel] += size << 1;
spu2stat_clear_80(channel);
SPUStartCycle[channel] = SPUCycles;
SPUTargetCycle[channel] = size;
interrupt |= (1 << (channel + 1));
}
void CALLBACK SPU2interruptDMA(int dma)
void CALLBACK SPU2interruptDMA(int channel)
{
s32 core, offset;
if (dma == 4)
{
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;
SPU2_LOG("SPU2 interruptDMA(%d)\n", channel);
spu2attr(channel).dma = 0;
spu2stat_set_80(channel);
}
#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;
Config conf;
ADMA Adma4;
ADMA Adma7;
ADMA adma[2];
u32 MemAddr[2];
u32 g_nSpuInit = 0;
@ -52,8 +51,8 @@ s32 iFMod[NSSIZE];
s32 s_buffers[NSSIZE][2]; // left and right buffers
// mixer thread variables
static bool s_bThreadExit = true;
static s32 s_nDropPacket = 0;
bool s_bThreadExit = true;
s32 s_nDropPacket = 0;
string s_strIniPath( "inis/" );
#ifdef _WIN32
@ -67,27 +66,25 @@ pthread_t s_threadSPU2;
void* SPU2ThreadProc(void*);
#endif
static AUDIOBUFFER s_pAudioBuffers[NSPACKETS];
static s32 s_nCurBuffer = 0, s_nQueuedBuffers = 0;
static s16* s_pCurOutput = NULL;
static u32 g_startcount=0xffffffff;
static u32 g_packetcount=0;
AUDIOBUFFER s_pAudioBuffers[NSPACKETS];
s32 s_nCurBuffer = 0, s_nQueuedBuffers = 0;
s16* s_pCurOutput = NULL;
u32 g_startcount=0xffffffff;
u32 g_packetcount=0;
// time stretch variables
soundtouch::SoundTouch* pSoundTouch=NULL;
WavOutFile* g_pWavRecord=NULL; // used for recording
extern WavOutFile* g_pWavRecord; // used for recording
static u64 s_GlobalTimeStamp = 0;
static s32 s_nDurations[64]={0};
static s32 s_nCurDuration=0;
static s32 s_nTotalDuration=0;
u64 s_GlobalTimeStamp = 0;
s32 s_nDurations[64]={0};
s32 s_nCurDuration=0;
s32 s_nTotalDuration=0;
s32 SPUCycles = 0, SPUWorkerCycles = 0;
s32 SPUStartCycle[2];
s32 SPUTargetCycle[2];
s32 g_logsound=0;
int ADMASWrite(int c);
void InitADSR();
@ -100,14 +97,6 @@ void (*irqCallbackDMA7)()=0;
#endif
uptr g_pDMABaseAddr=0;
const s32 f[5][2] = {
{ 0, 0 },
{ 60, 0 },
{ 115, -52 },
{ 98, -55 },
{ 122, -60 } };
u32 RateTable[160];
// channels and voices
@ -266,12 +255,9 @@ s32 CALLBACK SPU2init()
voices[i+24].memoffset = 0x400;
// init each channel
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].pvoice = (_SPU_VOICE*)((u8*)spu2regs+voices[i].memoffset)+(i%24);
voices[i].ADSRX.SustainLevel = 1024; // -> init sustain
for (u32 i = 0; i < ArraySize(voices); ++i)
{
voices[i].init(i);
}
return 0;
@ -319,7 +305,7 @@ s32 CALLBACK SPU2open(void *pDsp)
// initialize the audio buffers
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;
}
@ -424,7 +410,7 @@ void CALLBACK SPU2async(u32 cycle)
if ( g_nSpuInit )
{
while( SPUCycles-SPUWorkerCycles > 0 && CYCLES_PER_MS < SPUCycles-SPUWorkerCycles )
while((SPUCycles-SPUWorkerCycles > 0) && (CYCLES_PER_MS < (SPUCycles - SPUWorkerCycles)))
{
SPU2Worker();
SPUWorkerCycles += CYCLES_PER_MS;
@ -563,58 +549,58 @@ s32 MixADSR(VOICE_PROCESSED* pvoice) // MIX ADSR
return 0;
}
void MixChannels(s32 core)
void MixChannels(s32 channel)
{
// mix all channels
s32 c_offset = 0x0400 * core;
s32 dma, left_vol, right_vol;
s32 left_vol, right_vol;
ADMA *Adma;
if (core == 0)
if (channel == 0)
{
Adma = &Adma4;
dma = 4;
Adma = &adma[0];
left_vol = REG_C0_BVOLL;
right_vol = REG_C0_BVOLR;
}
else
{
Adma = &Adma7;
dma = 7;
Adma = &adma[1];
left_vol = REG_C1_BVOLL;
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))
s_buffers[ns][0] += (((s16*)spu2mem)[0x2000 + c_offset +Adma->Index]*(s32)spu2Ru16(left_vol))>>16;
if ((spu2Ru16(REG_C0_MMIX + c_offset) & 0x40))
s_buffers[ns][1] += (((s16*)spu2mem)[0x2200 + c_offset +Adma->Index]*(s32)spu2Ru16(right_vol))>>16;
u16 left_reg = 0x2000 + c_offset(channel) + Adma->Index;
u16 right_reg = left_reg + 0x200;
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;
MemAddr[core] += 4;
MemAddr[channel] += 4;
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");
}
#ifndef ENABLE_NEW_IOPDMA_SPU2
if (core == 0)
if (channel == 0)
irqCallbackDMA4();
else
irqCallbackDMA7();
#endif
}
if (core == 1) Adma->Enabled = 2;
if (channel == 1) Adma->Enabled = 2;
}
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
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...
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
#ifdef _WIN32
@ -908,8 +695,7 @@ void* SPU2ThreadProc(void* lpParam)
{
//Sleeping
Sleep(1);
if ( s_bThreadExit )
return NULL;
if (s_bThreadExit) return NULL;
}
while( SoundGetBytesBuffered() > 72000 )
@ -917,7 +703,7 @@ void* SPU2ThreadProc(void* lpParam)
//Bytes buffered
Sleep(1);
if ( s_bThreadExit ) return NULL;
if (s_bThreadExit) return NULL;
}
}
else
@ -929,63 +715,28 @@ void* SPU2ThreadProc(void* lpParam)
}
}
//s32 ps2delay = timeGetTime() - s_pAudioBuffers[nReadBuf].timestamp;
s32 NewSamples = s_pAudioBuffers[nReadBuf].avgtime;
if ( (conf.options & OPTION_TIMESTRETCH) )
if (conf.options & OPTION_TIMESTRETCH)
{
s32 bytesbuf = SoundGetBytesBuffered();
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;
u32 NewSamples = GetNewSamples(nReadBuf);
s32 OldSamples = s_pAudioBuffers[nReadBuf].len / 4;
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;
pSoundTouch->putSamples(s_floatBuffer, oldsamples);
// 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);
pSoundTouch->putSamples(s_floatBuffer, OldSamples);
ExtractSamples();
}
else
{
SoundFeedVoiceData(s_pAudioBuffers[nReadBuf].pbuf, s_pAudioBuffers[nReadBuf].len);
}
// don't go to the next buffer unless there is more data buffered
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->pStart=(u8*)(spu2mem+pvoice->iStartAddr);
break;
case 0x1C2:
case REG_VA_SSA + 2:
pvoice->iStartAddr=(pvoice->iStartAddr & 0x3f0000) | (value & 0xFFFF);
pvoice->pStart=(u8*)(spu2mem+pvoice->iStartAddr);
break;
@ -1156,7 +907,7 @@ void CALLBACK SPU2write(u32 mem, u16 value)
pvoice->pLoop=(u8*)(spu2mem+pvoice->iLoopAddr);
pvoice->bIgnoreLoop=pvoice->iLoopAddr>0;
break;
case 0x1C6:
case REG_VA_LSAX + 2:
pvoice->iLoopAddr=(pvoice->iLoopAddr& 0x3f0000) | (value & 0xFFFF);
pvoice->pLoop=(u8*)(spu2mem+pvoice->iLoopAddr);
pvoice->bIgnoreLoop=pvoice->iLoopAddr>0;
@ -1165,7 +916,7 @@ void CALLBACK SPU2write(u32 mem, u16 value)
// unused... check if it gets written as well
pvoice->iNextAddr=(((u32)value&0x3f)<<16)|(pvoice->iNextAddr&0xFFFF);
break;
case 0x1CA:
case REG_VA_NAX + 2:
// unused... check if it gets written as well
pvoice->iNextAddr=(pvoice->iNextAddr & 0x3f0000) | (value & 0xFFFF);
break;
@ -1182,7 +933,7 @@ void CALLBACK SPU2write(u32 mem, u16 value)
spu2mem[spuaddr] = value;
spuaddr++;
if ((spu2Ru16(REG_C0_CTRL)&0x40) && (C0_IRQA() == spuaddr))
if (spu2attr0.irq && (C0_IRQA() == spuaddr))
{
IRQINFO |= 4;
SPU2_LOG("SPU2write:C0_CPUDATA interrupt\n");
@ -1192,8 +943,8 @@ void CALLBACK SPU2write(u32 mem, u16 value)
if (spuaddr>0xFFFFE) spuaddr = 0x2800;
C0_SPUADDR_SET(spuaddr);
spu2Ru16(REG_C0_SPUSTAT)&=~0x80;
spu2Ru16(REG_C0_CTRL)&=~0x30;
spu2stat_clear_80(0);
spu2attr1.irq = 0;
break;
case REG_C1_SPUDATA:
@ -1201,7 +952,7 @@ void CALLBACK SPU2write(u32 mem, u16 value)
spu2mem[spuaddr] = value;
spuaddr++;
if ((spu2Ru16(REG_C1_CTRL)&0x40) && (C1_IRQA() == spuaddr))
if (spu2attr1.irq && (C1_IRQA() == spuaddr))
{
IRQINFO |= 8;
SPU2_LOG("SPU2write:C1_CPUDATA interrupt\n");
@ -1211,8 +962,8 @@ void CALLBACK SPU2write(u32 mem, u16 value)
if (spuaddr>0xFFFFE) spuaddr = 0x2800;
C1_SPUADDR_SET(spuaddr);
spu2Ru16(REG_C1_SPUSTAT)&=~0x80;
spu2Ru16(REG_C1_CTRL)&=~0x30;
spu2stat_clear_80(1);
spu2attr0.irq = 0;
break;
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?
{
s32 ch=0;
s32 ch = 0;
u32 rx = r;
if (rx >=0x400)
if (rx >= 0x400)
{
ch=24;
rx-=0x400;
ch = 24;
rx -= 0x400;
}
ch+=((rx-0x1c0)/12);
rx-=(ch%24)*12;
ch += ((rx - 0x1c0) / 12);
rx -= (ch % 24) * 12;
VOICE_PROCESSED* pvoice = &voices[ch];
// Note - can we generalize this?
switch(rx)
{
case REG_VA_SSA:
ret = ((((uptr)pvoice->pStart-(uptr)spu2mem)>>17)&0x3F);
break;
case 0x1C2:
case REG_VA_SSA + 2:
ret = ((((uptr)pvoice->pStart-(uptr)spu2mem)>>1)&0xFFFF);
break;
case REG_VA_LSAX:
ret = ((((uptr)pvoice->pLoop-(uptr)spu2mem)>>17)&0x3F);
break;
case 0x1C6:
case REG_VA_LSAX + 2:
ret = ((((uptr)pvoice->pLoop-(uptr)spu2mem)>>1)&0xFFFF);
break;
case REG_VA_NAX:
ret = ((((uptr)pvoice->pCurr-(uptr)spu2mem)>>17)&0x3F);
break;
case 0x1CA:
case REG_VA_NAX + 2:
ret = ((((uptr)pvoice->pCurr-(uptr)spu2mem)>>1)&0xFFFF);
break;
}
@ -1350,6 +1100,7 @@ u16 CALLBACK SPU2read(u32 mem)
if (spuaddr > 0xfffff) spuaddr=0;
C0_SPUADDR_SET(spuaddr);
break;
case REG_C1_SPUDATA:
spuaddr = C1_SPUADDR();
ret = spu2mem[spuaddr];
@ -1416,69 +1167,6 @@ s32 CALLBACK SPU2test()
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)
{
LOG_CALLBACK("SPU2setupRecording()\n");
@ -1499,10 +1187,9 @@ int CALLBACK SPU2setupRecording(int start, void* pData)
void save_data(freezeData *data)
{
SPU2freezeData *spud;
s32 i;
spud = (SPU2freezeData*)data->data;
spud->version = 0x70000001;
spud->version = 0x70000002;
memcpy(spud->spu2regs, spu2regs, 0x10000);
memcpy(spud->spu2mem, spu2mem, 0x200000);
@ -1510,8 +1197,8 @@ void save_data(freezeData *data)
spud->nSpuIrq[0] = (s32)(pSpuIrq[0] - spu2mem);
spud->nSpuIrq[1] = (s32)(pSpuIrq[1] - spu2mem);
memcpy(spud->dwNewChannel2, dwNewChannel2, 4*2);
memcpy(spud->dwEndChannel2, dwEndChannel2, 4*2);
memcpy(spud->dwNewChannel2, dwNewChannel2, sizeof(dwNewChannel2));
memcpy(spud->dwEndChannel2, dwEndChannel2, sizeof(dwEndChannel2));
spud->dwNoiseVal = dwNoiseVal;
spud->interrupt = interrupt;
@ -1519,10 +1206,10 @@ void save_data(freezeData *data)
memcpy(spud->iFMod, iFMod, sizeof(iFMod));
memcpy(spud->MemAddr, MemAddr, sizeof(MemAddr));
spud->adma[0] = Adma4;
spud->adma[1] = Adma7;
spud->Adma4MemAddr = (u32)((uptr)Adma4.MemAddr - g_pDMABaseAddr);
spud->Adma7MemAddr = (u32)((uptr)Adma7.MemAddr - g_pDMABaseAddr);
spud->adma[0] = adma[0];
spud->adma[1] = adma[1];
spud->AdmaMemAddr[0] = (u32)((uptr)adma[0].MemAddr - g_pDMABaseAddr);
spud->AdmaMemAddr[1] = (u32)((uptr)adma[1].MemAddr - g_pDMABaseAddr);
spud->SPUCycles = SPUCycles;
spud->SPUWorkerCycles = SPUWorkerCycles;
@ -1530,7 +1217,7 @@ void save_data(freezeData *data)
memcpy(spud->SPUStartCycle, SPUStartCycle, sizeof(SPUStartCycle));
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;
}
@ -1539,7 +1226,7 @@ void save_data(freezeData *data)
s_nCurDuration = 0;
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);
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)
{
SPU2freezeData *spud;
s32 i;
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");
return;
@ -1571,8 +1257,8 @@ void load_data(freezeData *data)
pSpuIrq[0] = spu2mem + spud->nSpuIrq[0];
pSpuIrq[1] = spu2mem + spud->nSpuIrq[1];
memcpy(dwNewChannel2, spud->dwNewChannel2, 4*2);
memcpy(dwEndChannel2, spud->dwEndChannel2, 4*2);
memcpy(dwNewChannel2, spud->dwNewChannel2, sizeof(dwNewChannel2));
memcpy(dwEndChannel2, spud->dwEndChannel2, sizeof(dwEndChannel2));
dwNoiseVal = spud->dwNoiseVal;
interrupt = spud->interrupt;
@ -1580,10 +1266,10 @@ void load_data(freezeData *data)
memcpy(iFMod, spud->iFMod, sizeof(iFMod));
memcpy(MemAddr, spud->MemAddr, sizeof(MemAddr));
Adma4 = spud->adma[0];
Adma7 = spud->adma[1];
Adma4.MemAddr = (u16*)(g_pDMABaseAddr+spud->Adma4MemAddr);
Adma7.MemAddr = (u16*)(g_pDMABaseAddr+spud->Adma7MemAddr);
adma[0] = spud->adma[0];
adma[1] = spud->adma[1];
adma[0].MemAddr = (u16*)(g_pDMABaseAddr+spud->AdmaMemAddr[0]);
adma[1].MemAddr = (u16*)(g_pDMABaseAddr+spud->AdmaMemAddr[1]);
SPUCycles = spud->SPUCycles;
SPUWorkerCycles = spud->SPUWorkerCycles;
@ -1591,7 +1277,7 @@ void load_data(freezeData *data)
memcpy(SPUStartCycle, spud->SPUStartCycle, sizeof(SPUStartCycle));
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));
voices[i].pStart = (u8*)((uptr)spud->voices[i].pStart+(uptr)spu2mem);
@ -1602,7 +1288,7 @@ void load_data(freezeData *data)
s_GlobalTimeStamp = 0;
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;
}

View File

@ -46,6 +46,7 @@ extern string s_strIniPath;
#define LOG_CALLBACK 0&&
#endif
#define ZEROSPU2_DEVBUILD
#ifdef ZEROSPU2_DEVBUILD
#define SPU2_LOG __Log //dev mode
#else
@ -60,36 +61,43 @@ extern string s_strIniPath;
#define SPU2_BUILD 4 // increase that with each version
#define SPU2_MINOR 6
#define OPTION_TIMESTRETCH 1 // stretches samples without changing pitch to reduce cracking
#define OPTION_REALTIME 2 // sync to real time instead of ps2 time
#define OPTION_MUTE 4 // don't output anything
#define OPTION_RECORDING 8
enum zerospu2_options
{
OPTION_TIMESTRETCH = 1, // stretches samples without changing pitch to reduce cracking
OPTION_REALTIME = 2, // sync to real time instead of ps2 time
OPTION_MUTE = 4, // don't output anything
OPTION_RECORDING = 8
};
// ADSR constants
#define ATTACK_MS 494L
#define DECAYHALF_MS 286L
#define DECAY_MS 572L
#define SUSTAIN_MS 441L
#define RELEASE_MS 437L
enum adsr_ms
{
ATTACK_MS = 494L,
DECAYHALF_MS = 286L,
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
#define NSFRAMES 16 // gather at least NSFRAMES of NSSIZE before submitting
#define NSPACKETS 24
const u32 NSSIZE = 48; // ~ 1 ms of data
const u32 NSFRAMES = 16; // gather at least NSFRAMES of NSSIZE before submitting
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"
extern s8 *spu2regs;
extern u16* spu2mem;
extern s32 iFMod[NSSIZE];
extern u32 MemAddr[2];
extern u32 dwNoiseVal; // global noise generator
extern u32 dwNoiseVal; // global noise generator
// functions of main emu, called on spu irq
extern void (*irqCallbackSPU2)();
@ -115,11 +123,11 @@ void SaveConfig();
void LoadConfig();
void SysMessage(char *fmt, ...);
void LogRawSound(void* pleft, int leftstride, void* pright, int rightstride, int numsamples);
void LogPacketSound(void* packet, int memsize);
extern void LogRawSound(void* pleft, int leftstride, void* pright, int rightstride, int numsamples);
extern void LogPacketSound(void* packet, int memsize);
// simulate SPU2 for 1ms
void SPU2Worker();
extern void SPU2Worker();
// hardware sound functions
int SetupSound(); // if successful, returns 0
@ -128,95 +136,25 @@ int SoundGetBytesBuffered();
// returns 0 is successful, else nonzero
void SoundFeedVoiceData(unsigned char* pSound,long lBytes);
#define clamp16(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)
static __forceinline void clamp16(s32 &dest)
{
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;
spu2Ru16(lo) = (value) & 0xffff;
if (value < -32768)
dest = -32768;
else if (value > 32767)
dest = 32767;
else
dest = (s16)value;
}
static __forceinline u32 C0_IRQA()
{
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_
struct tSPU_ATTR
{
u16 extCd : 1;
u16 extAudio : 1;
@ -229,6 +167,99 @@ struct SPU_CONTROL_
u16 spuUnmute : 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)
#pragma pack(1)
@ -313,9 +344,10 @@ struct VOICE_PROCESSED
s32 iGetNoiseVal();
void StoreInterpolationVal(int fa);
s32 iGetInterpolationVal();
s32 iGetVal();
void Stop();
SPU_CONTROL_* GetCtrl();
tSPU_ATTR* GetCtrl();
// start save state
s32 leftvol, rightvol; // left right volumes
@ -326,11 +358,9 @@ struct VOICE_PROCESSED
s32 sinc;
s32 iIrqDone; // debug irq done flag
s32 s_1; // last decoding infos
s32 s_2;
s32 s_1, s_2; // last decoding infos
s32 iOldNoise; // old noise val for this channel
s32 iActFreq; // current psx pitch
s32 iUsedFreq; // current pc pitch
s32 iActFreq, iUsedFreq; // current psx pitch & pc pitch
s32 iStartAddr, iLoopAddr, iNextAddr;
s32 bFMod;
@ -351,6 +381,27 @@ struct VOICE_PROCESSED
u8* pLoop, *pCurr;
_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
@ -374,8 +425,7 @@ struct ADMA
// used to make sure that ADMA doesn't get interrupted with a writeDMA call
};
extern ADMA Adma4;
extern ADMA Adma7;
extern ADMA adma[2];
struct SPU2freezeData
{
@ -389,7 +439,7 @@ struct SPU2freezeData
s32 iFMod[NSSIZE];
u32 MemAddr[2];
ADMA adma[2];
u32 Adma4MemAddr, Adma7MemAddr;
u32 AdmaMemAddr[2];
s32 SPUCycles, SPUWorkerCycles;
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