mirror of https://github.com/PCSX2/pcsx2.git
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:
parent
742bae8d94
commit
7f46f80015
|
@ -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
|
||||
|
|
|
@ -20,7 +20,8 @@
|
|||
|
||||
#include <gtk/gtk.h>
|
||||
|
||||
extern "C" {
|
||||
extern "C"
|
||||
{
|
||||
#include "interface.h"
|
||||
#include "support.h"
|
||||
#include "callbacks.h"
|
||||
|
|
|
@ -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 />
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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()
|
||||
{
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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];
|
||||
|
|
|
@ -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);
|
||||
}
|
|
@ -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
|
Loading…
Reference in New Issue