ZeroSPU2: Some defines become functions, the register list got updated and commented, some code got moved into new files, and some preliminary code for later refactoring was added.

git-svn-id: http://pcsx2-playground.googlecode.com/svn/trunk@629 a6443dda-0b58-4228-96e9-037be469359c
This commit is contained in:
arcum42 2009-01-25 12:08:47 +00:00 committed by Gregory Hainaut
parent 7a456fe608
commit 98e9c9635d
7 changed files with 260 additions and 715 deletions

View File

@ -34,7 +34,7 @@ int AlsaSetupSound()
snd_pcm_hw_params_t *hwparams;
snd_pcm_sw_params_t *swparams;
snd_pcm_status_t *status;
unsigned int pspeed = 48000;
unsigned int pspeed = SAMPLE_RATE;
int pchannels = 2;
snd_pcm_format_t format = SND_PCM_FORMAT_S16_LE;
unsigned int buffer_time = SOUNDSIZE;

View File

@ -29,6 +29,8 @@
#include <sys/soundcard.h>
#include <unistd.h>
#include <zerospu2.h>
// Make it easier to check and set checkmarks in the gui
#define is_checked(main_widget, widget_name) (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(lookup_widget(main_widget, widget_name))))
#define set_checked(main_widget,widget_name, state) gtk_toggle_button_set_state(GTK_TOGGLE_BUTTON(lookup_widget(main_widget, widget_name)), state)

View File

@ -28,7 +28,7 @@ extern int errno;
// use OSS for sound
int OSSSetupSound()
{
int pspeed=48000;
int pspeed = SAMPLE_RATE;
int pstereo;
int format;
int fragsize = 0;

View File

@ -27,7 +27,8 @@ libZeroSPU2_LDFLAGS= @SHARED_LDFLAGS@
libZeroSPU2_LDFLAGS+=-Wl,-soname,@ZEROSPU2_SONAME@
libZeroSPU2_LDADD=$(libZeroSPU2_a_OBJECTS) SoundTouch/libSoundTouch.a
libZeroSPU2_a_SOURCES = zerospu2.cpp
libZeroSPU2_a_SOURCES = zerospu2.cpp voices.cpp zerodma.cpp
libZeroSPU2_a_SOURCES += zerospu2.h
libZeroSPU2_a_SOURCES += Linux/interface.c Linux/Linux.cpp Linux/Alsa.cpp Linux/OSS.cpp Linux/support.c
SUBDIRS = SoundTouch

View File

@ -60,7 +60,7 @@ int SetupSound()
pcmwf.nChannels = 2;
pcmwf.nBlockAlign = 4;
pcmwf.nSamplesPerSec = 48000;
pcmwf.nSamplesPerSec = SAMPLE_RATE;
pcmwf.nAvgBytesPerSec = pcmwf.nSamplesPerSec * pcmwf.nBlockAlign;
pcmwf.wBitsPerSample = 16;

View File

@ -28,19 +28,6 @@
#include "SoundTouch/SoundTouch.h"
#include "SoundTouch/WavFile.h"
// ADSR constants
#define ATTACK_MS 494L
#define DECAYHALF_MS 286L
#define DECAY_MS 572L
#define SUSTAIN_MS 441L
#define RELEASE_MS 437L
#define AUDIO_BUFFER 2048
#define NSSIZE 48 // ~ 1 ms of data
#define NSFRAMES 16 // gather at least NSFRAMES of NSSIZE before submitting
#define NSPACKETS 24
char libraryName[256];
FILE *spu2Log;
@ -59,7 +46,7 @@ u32 dwNewChannel2[2] = {0}; // keeps track of what channels that have been turne
u32 dwEndChannel2[2] = {0}; // keeps track of what channels have ended
unsigned long dwNoiseVal=1; // global noise generator
bool g_bPlaySound = true; // if true, will output sound, otherwise no
static int iFMod[NSSIZE];
int iFMod[NSSIZE];
int s_buffers[NSSIZE][2]; // left and right buffers
// mixer thread variables
@ -279,7 +266,7 @@ s32 CALLBACK SPU2open(void *pDsp)
if ( conf.options & OPTION_TIMESTRETCH )
{
pSoundTouch = new soundtouch::SoundTouch();
pSoundTouch->setSampleRate(48000);
pSoundTouch->setSampleRate(SAMPLE_RATE);
pSoundTouch->setChannels(2);
pSoundTouch->setTempoChange(0);
@ -705,19 +692,21 @@ ENDX:
// mix all channels
if ((spu2Ru16(REG_C0_MMIX) & 0xF0) && (spu2Ru16(REG_C0_ADMAS) & 0x1) /*&& !(spu2Ru16(REG_C0_CTRL) & 0x30)*/)
{
ADMA *Adma = &Adma4;
for (int ns=0;ns<NSSIZE;ns++)
{
if ((spu2Ru16(REG_C0_MMIX) & 0x80))
s_buffers[ns][0] += (((short*)spu2mem)[0x2000+Adma4.Index]*(int)spu2Ru16(REG_C0_BVOLL))>>16;
s_buffers[ns][0] += (((short*)spu2mem)[0x2000+Adma->Index]*(int)spu2Ru16(REG_C0_BVOLL))>>16;
if ((spu2Ru16(REG_C0_MMIX) & 0x40))
s_buffers[ns][1] += (((short*)spu2mem)[0x2200+Adma4.Index]*(int)spu2Ru16(REG_C0_BVOLR))>>16;
s_buffers[ns][1] += (((short*)spu2mem)[0x2200+Adma->Index]*(int)spu2Ru16(REG_C0_BVOLR))>>16;
Adma4.Index +=1;
Adma->Index +=1;
// just add after every sample, it is better than adding 1024 all at once (games like Genji don't like it)
MemAddr[0] += 4;
if (Adma4.Index == 128 || Adma4.Index == 384)
if ((Adma->Index == 128) || (Adma->Index == 384))
{
if (ADMAS4Write())
{
@ -731,31 +720,35 @@ ENDX:
}
}
if (Adma4.Index == 512)
if (Adma->Index == 512)
{
if ( Adma4.Enabled == 2 )
if ( Adma->Enabled == 2 )
{
Adma4.Enabled = 0;
Adma->Enabled = 0;
}
Adma4.Index = 0;
Adma->Index = 0;
}
}
}
// Let's do the same bloody mixing code again, only for C1.
// fixme - There is way too much duplication of code between C0 & C1, and Adma4 & Adma7.
// arcum42
if ((spu2Ru16(REG_C1_MMIX) & 0xF0) && (spu2Ru16(REG_C1_ADMAS) & 0x2))
{
ADMA *Adma = &Adma7;
for (int ns=0;ns<NSSIZE;ns++)
{
if ((spu2Ru16(REG_C1_MMIX) & 0x80))
s_buffers[ns][0] += (((short*)spu2mem)[0x2400+Adma7.Index]*(int)spu2Ru16(REG_C1_BVOLL))>>16;
s_buffers[ns][0] += (((short*)spu2mem)[0x2400+Adma->Index]*(int)spu2Ru16(REG_C1_BVOLL))>>16;
if ((spu2Ru16(REG_C1_MMIX) & 0x40))
s_buffers[ns][1] += (((short*)spu2mem)[0x2600+Adma7.Index]*(int)spu2Ru16(REG_C1_BVOLR))>>16;
s_buffers[ns][1] += (((short*)spu2mem)[0x2600+Adma->Index]*(int)spu2Ru16(REG_C1_BVOLR))>>16;
Adma7.Index +=1;
Adma->Index +=1;
MemAddr[1] += 4;
if (Adma7.Index == 128 || Adma7.Index == 384)
if (Adma->Index == 128 || Adma->Index == 384)
{
if (ADMAS7Write())
{
@ -767,13 +760,13 @@ ENDX:
irqCallbackDMA7();
}
Adma7.Enabled = 2;
Adma->Enabled = 2;
}
if (Adma7.Index == 512)
if (Adma->Index == 512)
{
if ( Adma7.Enabled == 2 ) Adma7.Enabled = 0;
Adma7.Index = 0;
if ( Adma->Enabled == 2 ) Adma->Enabled = 0;
Adma->Index = 0;
}
}
}
@ -982,299 +975,6 @@ void* SPU2ThreadProc(void* lpParam)
return NULL;
}
void CALLBACK SPU2readDMA4Mem(u16 *pMem, int size)
{
u32 spuaddr = C0_SPUADDR;
int i;
SPU2_LOG("SPU2 readDMA4Mem size %x, addr: %x\n", size, pMem);
for (i=0;i<size;i++)
{
*pMem++ = *(u16*)(spu2mem+spuaddr);
if ((spu2Rs16(REG_C0_CTRL)&0x40) && C0_IRQA == spuaddr)
{
C0_SPUADDR_SET(spuaddr);
IRQINFO |= 4;
SPU2_LOG("SPU2readDMA4Mem:interrupt\n");
irqCallbackSPU2();
}
spuaddr++; // inc spu addr
if (spuaddr>0x0fffff) spuaddr=0; // wrap at 2Mb
}
spuaddr+=19; //Transfer Local To Host TSAH/L + Data Size + 20 (already +1'd)
C0_SPUADDR_SET(spuaddr);
// got from J.F. and Kanodin... is it needed?
spu2Ru16(REG_C0_SPUSTAT) &=~0x80; // DMA complete
SPUStartCycle[0] = SPUCycles;
SPUTargetCycle[0] = size;
interrupt |= (1<<1);
}
void CALLBACK SPU2readDMA7Mem(u16* pMem, int size)
{
u32 spuaddr = C1_SPUADDR;
int i;
SPU2_LOG("SPU2 readDMA7Mem size %x, addr: %x\n", size, pMem);
for (i=0;i<size;i++)
{
*pMem++ = *(u16*)(spu2mem+spuaddr);
if ((spu2Rs16(REG_C1_CTRL)&0x40) && (C1_IRQA == spuaddr))
{
C1_SPUADDR_SET(spuaddr);
IRQINFO |= 8;
SPU2_LOG("SPU2readDMA7Mem:interrupt\n");
irqCallbackSPU2();
}
spuaddr++; // inc spu addr
if (spuaddr>0x0fffff) // wrap at 2Mb
spuaddr=0; // wrap
}
spuaddr+=19; //Transfer Local To Host TSAH/L + Data Size + 20 (already +1'd)
C1_SPUADDR_SET(spuaddr);
// got from J.F. and Kanodin... is it needed?
spu2Ru16(REG_C1_SPUSTAT)&=~0x80; // DMA complete
SPUStartCycle[1] = SPUCycles;
SPUTargetCycle[1] = size;
interrupt |= (1<<2);
}
// WRITE
// AutoDMA's are used to transfer to the DIRECT INPUT area of the spu2 memory
// Left and Right channels are always interleaved together in the transfer so
// the AutoDMA's deinterleaves them and transfers them. An interrupt is
// generated when half of the buffer (256 short-words for left and 256
// short-words for right ) has been transferred. Another interrupt occurs at
// the end of the transfer.
int ADMAS4Write()
{
u32 spuaddr;
if (interrupt & 0x2)
{
printf("4 returning for interrupt\n");
return 0;
}
if (Adma4.AmountLeft <= 0)
{
printf("4 amount left is 0\n");
return 1;
}
assert( Adma4.AmountLeft >= 512 );
spuaddr = C0_SPUADDR;
// SPU2 Deinterleaves the Left and Right Channels
memcpy((short*)(spu2mem + spuaddr + 0x2000),(short*)Adma4.MemAddr,512);
Adma4.MemAddr += 256;
memcpy((short*)(spu2mem + spuaddr + 0x2200),(short*)Adma4.MemAddr,512);
Adma4.MemAddr += 256;
if ((spu2Ru16(REG_C0_CTRL)&0x40) && ((spuaddr + 0x2400) <= C0_IRQA && (spuaddr + 0x2400 + 256) >= C0_IRQA))
{
IRQINFO |= 4;
printf("ADMA 4 Mem access:interrupt\n");
irqCallbackSPU2();
}
if ((spu2Ru16(REG_C0_CTRL)&0x40) && ((spuaddr + 0x2600) <= C0_IRQA && (spuaddr + 0x2600 + 256) >= C0_IRQA))
{
IRQINFO |= 4;
printf("ADMA 4 Mem access:interrupt\n");
irqCallbackSPU2();
}
spuaddr = (spuaddr + 256) & 511;
C0_SPUADDR_SET(spuaddr);
Adma4.AmountLeft-=512;
if (Adma4.AmountLeft > 0)
return 0;
else
return 1;
}
int ADMAS7Write()
{
u32 spuaddr;
if (interrupt & 0x4)
{
printf("7 returning for interrupt\n");
return 0;
}
if (Adma7.AmountLeft <= 0)
{
printf("7 amount left is 0\n");
return 1;
}
assert( Adma7.AmountLeft >= 512 );
spuaddr = C1_SPUADDR;
// SPU2 Deinterleaves the Left and Right Channels
memcpy((short*)(spu2mem + spuaddr + 0x2400),(short*)Adma7.MemAddr,512);
Adma7.MemAddr += 256;
memcpy((short*)(spu2mem + spuaddr + 0x2600),(short*)Adma7.MemAddr,512);
Adma7.MemAddr += 256;
if ((spu2Ru16(REG_C1_CTRL)&0x40) && ((spuaddr + 0x2400) <= C1_IRQA && (spuaddr + 0x2400 + 256) >= C1_IRQA))
{
IRQINFO |= 8;
printf("ADMA 7 Mem access:interrupt\n");
irqCallbackSPU2();
}
if ((spu2Ru16(REG_C1_CTRL)&0x40) && ((spuaddr + 0x2600) <= C1_IRQA && (spuaddr + 0x2600 + 256) >= C1_IRQA))
{
IRQINFO |= 8;
printf("ADMA 7 Mem access:interrupt\n");
irqCallbackSPU2();
}
spuaddr = (spuaddr + 256) & 511;
C1_SPUADDR_SET(spuaddr);
Adma7.AmountLeft-=512;
assert( Adma7.AmountLeft >= 0 );
if (Adma7.AmountLeft > 0)
return 0;
else
return 1;
}
void CALLBACK SPU2writeDMA4Mem(u16* pMem, int size)
{
u32 spuaddr;
SPU2_LOG("SPU2 writeDMA4Mem size %x, addr: %x(spu2:%x), ctrl: %x, adma: %x\n", size, pMem, C0_SPUADDR, spu2Ru16(REG_C0_CTRL), spu2Ru16(REG_C0_ADMAS));
if ((spu2Ru16(REG_C0_ADMAS) & 0x1) && (spu2Ru16(REG_C0_CTRL) & 0x30) == 0 && size)
{
// if still active, don't destroy adma4
if ( !Adma4.Enabled )
Adma4.Index = 0;
Adma4.MemAddr = pMem;
Adma4.AmountLeft = size;
SPUTargetCycle[0] = size;
spu2Ru16(REG_C0_SPUSTAT)&=~0x80;
if (!Adma4.Enabled || Adma4.Index > 384)
{
C0_SPUADDR_SET(0);
if (ADMAS4Write())
{
SPUStartCycle[0] = SPUCycles;
interrupt |= (1<<1);
}
}
Adma4.Enabled = 1;
return;
}
spuaddr = C0_SPUADDR;
memcpy((unsigned char*)(spu2mem + spuaddr),(unsigned char*)pMem,size<<1);
spuaddr += size;
C0_SPUADDR_SET(spuaddr);
if ((spu2Ru16(REG_C0_CTRL)&0x40) && (spuaddr < C0_IRQA && C0_IRQA <= spuaddr+0x20))
{
IRQINFO |= 4;
SPU2_LOG("SPU2writeDMA4Mem:interrupt\n");
irqCallbackSPU2();
}
if (spuaddr>0xFFFFE)
spuaddr = 0x2800;
C0_SPUADDR_SET(spuaddr);
MemAddr[0] += size<<1;
spu2Ru16(REG_C0_SPUSTAT)&=~0x80;
SPUStartCycle[0] = SPUCycles;
SPUTargetCycle[0] = size;
interrupt |= (1<<1);
}
void CALLBACK SPU2writeDMA7Mem(u16* pMem, int size)
{
u32 spuaddr;
SPU2_LOG("SPU2 writeDMA7Mem size %x, addr: %x(spu2:%x), ctrl: %x, adma: %x\n", size, pMem, C1_SPUADDR, spu2Ru16(REG_C1_CTRL), spu2Ru16(REG_C1_ADMAS));
if ((spu2Ru16(REG_C1_ADMAS) & 0x2) && (spu2Ru16(REG_C1_CTRL) & 0x30) == 0 && size)
{
if (!Adma7.Enabled ) Adma7.Index = 0;
Adma7.MemAddr = pMem;
Adma7.AmountLeft = size;
SPUTargetCycle[1] = size;
spu2Ru16(REG_C1_SPUSTAT)&=~0x80;
if (!Adma7.Enabled || Adma7.Index > 384)
{
C1_SPUADDR_SET(0);
if (ADMAS7Write())
{
SPUStartCycle[1] = SPUCycles;
interrupt |= (1<<2);
}
}
Adma7.Enabled = 1;
return;
}
#ifdef _DEBUG
if (conf.Log && conf.options & OPTION_RECORDING)
LogPacketSound(pMem, 0x8000);
#endif
spuaddr = C1_SPUADDR;
memcpy((unsigned char*)(spu2mem + spuaddr),(unsigned char*)pMem,size<<1);
spuaddr += size;
C1_SPUADDR_SET(spuaddr);
if ((spu2Ru16(REG_C1_CTRL)&0x40) && (spuaddr < C1_IRQA && C1_IRQA <= spuaddr+0x20))
{
IRQINFO |= 8;
SPU2_LOG("SPU2writeDMA7Mem:interrupt\n");
irqCallbackSPU2();
}
if (spuaddr>0xFFFFE) spuaddr = 0x2800;
C1_SPUADDR_SET(spuaddr);
MemAddr[1] += size<<1;
spu2Ru16(REG_C1_SPUSTAT)&=~0x80;
SPUStartCycle[1] = SPUCycles;
SPUTargetCycle[1] = size;
interrupt |= (1<<2);
}
void CALLBACK SPU2interruptDMA4()
{
SPU2_LOG("SPU2 interruptDMA4\n");
spu2Rs16(REG_C0_CTRL)&=~0x30;
spu2Ru16(REG_C0_SPUSTAT)|=0x80;
}
void CALLBACK SPU2interruptDMA7()
{
SPU2_LOG("SPU2 interruptDMA7\n");
spu2Rs16(REG_C1_CTRL)&=~0x30;
spu2Ru16(REG_C1_SPUSTAT)|=0x80;
}
// turn channels on
void SoundOn(int start,int end,unsigned short val) // SOUND ON PSX COMAND
{
@ -1374,7 +1074,7 @@ void CALLBACK SPU2write(u32 mem, u16 value)
pvoice->pvoice->pitch = NP;
NP=(48000L*NP)/4096L; // calc frequency
NP = (SAMPLE_RATE * NP) / 4096L; // calc frequency
if (NP<1) NP=1; // some security
pvoice->iActFreq=NP; // store frequency
break;
@ -1451,11 +1151,11 @@ void CALLBACK SPU2write(u32 mem, u16 value)
switch(mem&0xffff)
{
case REG_C0_SPUDATA:
spuaddr = C0_SPUADDR;
spuaddr = C0_SPUADDR();
spu2mem[spuaddr] = value;
spuaddr++;
if ((spu2Ru16(REG_C0_CTRL)&0x40) && (C0_IRQA == spuaddr))
if ((spu2Ru16(REG_C0_CTRL)&0x40) && (C0_IRQA() == spuaddr))
{
IRQINFO |= 4;
SPU2_LOG("SPU2write:C0_CPUDATA interrupt\n");
@ -1470,11 +1170,11 @@ void CALLBACK SPU2write(u32 mem, u16 value)
break;
case REG_C1_SPUDATA:
spuaddr = C1_SPUADDR;
spuaddr = C1_SPUADDR();
spu2mem[spuaddr] = value;
spuaddr++;
if ((spu2Ru16(REG_C1_CTRL)&0x40) && (C1_IRQA == spuaddr))
if ((spu2Ru16(REG_C1_CTRL)&0x40) && (C1_IRQA() == spuaddr))
{
IRQINFO |= 8;
SPU2_LOG("SPU2write:C1_CPUDATA interrupt\n");
@ -1490,12 +1190,12 @@ void CALLBACK SPU2write(u32 mem, u16 value)
case REG_C0_IRQA_HI:
case REG_C0_IRQA_LO:
pSpuIrq[0]=spu2mem+C0_IRQA;
pSpuIrq[0] = spu2mem + C0_IRQA();
break;
case REG_C1_IRQA_HI:
case REG_C1_IRQA_LO:
pSpuIrq[1]=spu2mem+C1_IRQA;
pSpuIrq[1] = spu2mem + C1_IRQA();
break;
case REG_C0_SPUADDR_HI:
@ -1613,7 +1313,7 @@ u16 CALLBACK SPU2read(u32 mem)
switch(mem&0xffff)
{
case REG_C0_SPUDATA:
spuaddr = C0_SPUADDR;
spuaddr = C0_SPUADDR();
ret =spu2mem[spuaddr];
spuaddr++;
if (spuaddr>0xfffff)
@ -1621,7 +1321,7 @@ u16 CALLBACK SPU2read(u32 mem)
C0_SPUADDR_SET(spuaddr);
break;
case REG_C1_SPUDATA:
spuaddr = C1_SPUADDR;
spuaddr = C1_SPUADDR();
ret = spu2mem[spuaddr];
spuaddr++;
if (spuaddr>0xfffff)
@ -1669,236 +1369,6 @@ void CALLBACK SPU2irqCallback(void (*SPU2callback)(),void (*DMA4callback)(),void
irqCallbackDMA7 = DMA7callback;
}
// VOICE_PROCESSED definitions
SPU_CONTROL_* VOICE_PROCESSED::GetCtrl()
{
return ((SPU_CONTROL_*)(spu2regs+memoffset+REG_C0_CTRL));
}
void VOICE_PROCESSED::SetVolume(int iProcessRight)
{
u16 vol = iProcessRight ? pvoice->right.word : pvoice->left.word;
if (vol&0x8000) // sweep not working
{
short sInc=1; // -> sweep up?
if (vol&0x2000) sInc=-1; // -> or down?
if (vol&0x1000) vol^=0xffff; // -> mmm... phase inverted? have to investigate this
vol=((vol&0x7f)+1)/2; // -> sweep: 0..127 -> 0..64
vol+=vol/(2*sInc); // -> HACK: we don't sweep right now, so we just raise/lower the volume by the half!
vol*=128;
}
else // no sweep:
{
if (vol&0x4000) vol=0x3fff-(vol&0x3fff); // -> mmm... phase inverted? have to investigate this
}
if ( iProcessRight )
rightvol = vol&0x3fff;
else
leftvol = vol&0x3fff;
bVolChanged = true;
}
void VOICE_PROCESSED::StartSound()
{
ADSRX.lVolume=1; // and init some adsr vars
ADSRX.State=0;
ADSRX.EnvelopeVol=0;
if (bReverb && GetCtrl()->reverb)
{
// setup the reverb effects
}
pCurr=pStart; // set sample start
s_1=0; // init mixing vars
s_2=0;
iSBPos=28;
bNew=false; // init channel flags
bStop=false;
bOn=true;
SB[29]=0; // init our interpolation helpers
SB[30]=0;
spos=0x10000L;
SB[31]=0;
}
void VOICE_PROCESSED::VoiceChangeFrequency()
{
iUsedFreq=iActFreq; // -> take it and calc steps
sinc=(u32)pvoice->pitch<<4;
if (!sinc) sinc=1;
// -> freq change in simle imterpolation mode: set flag
SB[32]=1;
}
void VOICE_PROCESSED::InterpolateUp()
{
if (SB[32]==1) // flag == 1? calc step and set flag... and don't change the value in this pass
{
const int id1=SB[30]-SB[29]; // curr delta to next val
const int id2=SB[31]-SB[30]; // and next delta to next-next val :)
SB[32]=0;
if (id1>0) // curr delta positive
{
if (id2<id1)
{
SB[28]=id1;
SB[32]=2;
}
else if (id2<(id1<<1))
SB[28]=(id1*sinc)/0x10000L;
else
SB[28]=(id1*sinc)/0x20000L;
}
else // curr delta negative
{
if (id2>id1)
{
SB[28]=id1;
SB[32]=2;
}
else if (id2>(id1<<1))
SB[28]=(id1*sinc)/0x10000L;
else
SB[28]=(id1*sinc)/0x20000L;
}
}
else if (SB[32]==2) // flag 1: calc step and set flag... and don't change the value in this pass
{
SB[32]=0;
SB[28]=(SB[28]*sinc)/0x20000L;
if (sinc<=0x8000)
SB[29]=SB[30]-(SB[28]*((0x10000/sinc)-1));
else
SB[29]+=SB[28];
}
else // no flags? add bigger val (if possible), calc smaller step, set flag1
SB[29]+=SB[28];
}
//
// even easier interpolation on downsampling, also no special filter, again just "Pete's common sense" tm
//
void VOICE_PROCESSED::InterpolateDown()
{
if (sinc>=0x20000L) // we would skip at least one val?
{
SB[29]+=(SB[30]-SB[29])/2; // add easy weight
if (sinc>=0x30000L) // we would skip even more vals?
SB[29]+=(SB[31]-SB[30])/2; // add additional next weight
}
}
void VOICE_PROCESSED::FModChangeFrequency(int ns)
{
int NP=pvoice->pitch;
NP=((32768L+iFMod[ns])*NP)/32768L;
if (NP>0x3fff) NP=0x3fff;
if (NP<0x1) NP=0x1;
NP=(48000L*NP)/(4096L); // calc frequency
iActFreq=NP;
iUsedFreq=NP;
sinc=(((NP/10)<<16)/4800);
if (!sinc)
sinc=1;
// freq change in simple interpolation mode
SB[32]=1;
iFMod[ns]=0;
}
// fixme - noise handler... just produces some noise data
// surely wrong... and no noise frequency (spuCtrl&0x3f00) will be used...
// and sometimes the noise will be used as fmod modulation... pfff
int VOICE_PROCESSED::iGetNoiseVal()
{
int fa;
if ((dwNoiseVal<<=1)&0x80000000L)
{
dwNoiseVal^=0x0040001L;
fa = ((dwNoiseVal>>2)&0x7fff);
fa = -fa;
}
else
fa=(dwNoiseVal>>2)&0x7fff;
// mmm... depending on the noise freq we allow bigger/smaller changes to the previous val
fa=iOldNoise + ((fa - iOldNoise) / ((0x001f - (GetCtrl()->noiseFreq)) + 1));
if (fa > 32767L)
fa = 32767L;
if (fa < -32767L)
fa = -32767L;
iOldNoise=fa;
SB[29] = fa; // -> store noise val in "current sample" slot
return fa;
}
void VOICE_PROCESSED::StoreInterpolationVal(int fa)
{
if (bFMod==2) // fmod freq channel
SB[29]=fa;
else
{
if (!GetCtrl()->spuUnmute)
fa=0; // muted?
else // else adjust
{
if (fa >32767L)
fa = 32767L;
if (fa < -32767L)
fa = -32767L;
}
SB[28] = 0;
SB[29] = SB[30]; // -> helpers for simple linear interpolation: delay real val for two slots, and calc the two deltas, for a 'look at the future behaviour'
SB[30] = SB[31];
SB[31] = fa;
SB[32] = 1; // -> flag: calc new interolation
}
}
int VOICE_PROCESSED::iGetInterpolationVal()
{
int fa;
if (bFMod==2) return SB[29];
if (sinc<0x10000L) // -> upsampling?
InterpolateUp(); // --> interpolate up
else
InterpolateDown(); // --> else down
fa=SB[29];
return fa;
}
void VOICE_PROCESSED::Stop()
{
}
s32 CALLBACK SPU2test()
{
return 0;
@ -1954,7 +1424,7 @@ void LogPacketSound(void* packet, int memsize)
void LogRawSound(void* pleft, int leftstride, void* pright, int rightstride, int numsamples)
{
if (g_pWavRecord == NULL )
g_pWavRecord = new WavOutFile(RECORD_FILENAME, 48000, 16, 2);
g_pWavRecord = new WavOutFile(RECORD_FILENAME, SAMPLE_RATE, 16, 2);
u8* left = (u8*)pleft;
u8* right = (u8*)pright;

View File

@ -95,6 +95,37 @@ extern FILE *spu2Log;
#define OPTION_MUTE 4 // don't output anything
#define 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
#define AUDIO_BUFFER 2048
#define NSSIZE 48 // ~ 1 ms of data
#define NSFRAMES 16 // gather at least NSFRAMES of NSSIZE before submitting
#define NSPACKETS 24
#define SAMPLE_RATE 48000L
extern s8 *spu2regs;
extern u16* spu2mem;
extern int iFMod[NSSIZE];
extern u32 MemAddr[2];
extern unsigned long dwNoiseVal; // global noise generator
// functions of main emu, called on spu irq
extern void (*irqCallbackSPU2)();
extern void (*irqCallbackDMA4)();
extern void (*irqCallbackDMA7)();
extern int SPUCycles, SPUWorkerCycles;
extern int SPUStartCycle[2];
extern int SPUTargetCycle[2];
extern u16 interrupt;
typedef struct {
int Log;
int options;
@ -172,143 +203,156 @@ static __forceinline long InterlockedExchangeAdd(long volatile* Addend, long Val
////////////////////
// SPU2 Registers //
////////////////////
#define REG_VP_VOLL 0x0000
#define REG_VP_VOLR 0x0002
#define REG_VP_PITCH 0x0004
#define REG_VP_ADSR1 0x0006
#define REG_VP_ADSR2 0x0008
#define REG_VP_ENVX 0x000A
#define REG_VP_VOLXL 0x000C
#define REG_VP_VOLXR 0x000E
#define REG_C0_FMOD1 0x0180
#define REG_C0_FMOD2 0x0182
#define REG_C0_VMIXL1 0x0188
#define REG_C0_VMIXL2 0x018A
#define REG_C0_VMIXR1 0x0190
#define REG_C0_VMIXR2 0x0192
#define REG_C0_MMIX 0x0198
#define REG_C0_CTRL 0x019A
#define REG_C0_IRQA_HI 0x019C
#define REG_C0_IRQA_LO 0x019E
#define REG_C0_SPUON1 0x1A0
#define REG_C0_SPUON2 0x1A2
#define REG_C0_SPUOFF1 0x1A4
#define REG_C0_SPUOFF2 0x1A6
#define REG_C0_SPUADDR_HI 0x01A8
#define REG_C0_SPUADDR_LO 0x01AA
#define REG_C0_SPUDATA 0x01AC
#define REG_C0_DMACTRL 0x01AE
#define REG_C0_ADMAS 0x01B0
#define REG_C0_END1 0x0340
#define REG_C0_END2 0x0342
#define REG_C0_SPUSTAT 0x0344
#define REG_C0_BVOLL 0x076C
#define REG_C0_BVOLR 0x076E
enum
{
// Volume Registers - currently not implemented in ZeroSPU2, like most volume registers.
REG_VP_VOLL = 0x0000, // Voice Volume Left
REG_VP_VOLR = 0x0002, // Voice Volume Right
REG_VP_PITCH = 0x0004, // Pitch
REG_VP_ADSR1 = 0x0006, // Envelope 1 (Attack-Decay-Sustain-Release)
REG_VP_ADSR2 = 0x0008, // Envelope 2 (Attack-Decay-Sustain-Release)
REG_VP_ENVX = 0x000A, // Current Envelope
REG_VP_VOLXL = 0x000C, // Current Voice Volume Left
REG_VP_VOLXR = 0x000E, // Current Voice Volume Right
// end unimplemented section
REG_C0_FMOD1 = 0x0180, // Pitch Modulation Spec.
REG_C0_FMOD2 = 0x0182,
REG_S_NON = 0x0184, // Alloc Noise Generator - unimplemented
REG_C0_VMIXL1 = 0x0188, // Voice Output Mix Left (Dry)
REG_C0_VMIXL2 = 0x018A,
REG_S_VMIXEL = 0x018C, // Voice Output Mix Left (Wet) - unimplemented
REG_C0_VMIXR1 = 0x0190, // Voice Output Mix Right (Dry)
REG_C0_VMIXR2 = 0x0192,
REG_S_VMIXER = 0x0194, // Voice Output Mix Right (Wet) - unimplemented
//#define SPU2_TRANS_BY_DMA (0x0<<3)
//#define SPU2_TRANS_BY_IO (0x1<<3)
//
//#define SPU2_BLOCK_ONESHOT (0<<4)
//#define SPU2_BLOCK_LOOP (1<<4)
//#define SPU2_BLOCK_HANDLER (1<<7)
//#define SPU2_BLOCK_C0_VOICE1 ( 0x0<<8 )
//#define SPU2_BLOCK_C0_VOICE3 ( 0x1<<8 )
//#define SPU2_BLOCK_C1_SINL ( 0x2<<8 )
//#define SPU2_BLOCK_C1_SINR ( 0x3<<8 )
//#define SPU2_BLOCK_C1_VOICE1 ( 0x4<<8 )
//#define SPU2_BLOCK_C1_VOICE3 ( 0x5<<8 )
//#define SPU2_BLOCK_C0_MEMOUTL ( 0x6<<8 )
//#define SPU2_BLOCK_C0_MEMOUTR ( 0x7<<8 )
//#define SPU2_BLOCK_C0_MEMOUTEL ( 0x8<<8 )
//#define SPU2_BLOCK_C0_MEMOUTER ( 0x9<<8 )
//#define SPU2_BLOCK_C1_MEMOUTL ( 0xa<<8 )
//#define SPU2_BLOCK_C1_MEMOUTR ( 0xb<<8 )
//#define SPU2_BLOCK_C1_MEMOUTEL ( 0xc<<8 )
//#define SPU2_BLOCK_C1_MEMOUTER ( 0xd<<8 )
REG_C0_MMIX = 0x0198, // Output Spec. After Voice Mix
REG_C0_CTRL = 0x019A, // Core X Attrib
REG_C0_IRQA_HI = 0x019C, // Interrupt Address Spec. - Hi
REG_C0_IRQA_LO = 0x019E, // Interrupt Address Spec. - Lo
//#define SPU2_BLOCK_COUNT(x) ( (x)<<12 )
//
//#define SPU2_TRANS_STATUS_WAIT 1
//#define SPU2_TRANS_STATUS_CHECK 0
//
//#define SPU2_REV_MODE_OFF 0
//#define SPU2_REV_MODE_ROOM 1
//#define SPU2_REV_MODE_STUDIO_A 2
//#define SPU2_REV_MODE_STUDIO_B 3
//#define SPU2_REV_MODE_STUDIO_C 4
//#define SPU2_REV_MODE_HALL 5
//#define SPU2_REV_MODE_SPACE 6
//#define SPU2_REV_MODE_ECHO 7
//#define SPU2_REV_MODE_DELAY 8
//#define SPU2_REV_MODE_PIPE 9
//#define SPU2_REV_MODE_MAX 10
//#define SPU2_REV_MODE_CLEAR_WA 0x100
//#define SPU2_REV_MODE_CLEAR_CH0 0x000
//#define SPU2_REV_MODE_CLEAR_CH1 0x200
//#define SPU2_REV_MODE_CLEAR_CH_MASK 0x200
//
//#define SPU2_REV_SIZE_OFF 0x80
//#define SPU2_REV_SIZE_ROOM 0x26c0
//#define SPU2_REV_SIZE_STUDIO_A 0x1f40
//#define SPU2_REV_SIZE_STUDIO_B 0x4840
//#define SPU2_REV_SIZE_STUDIO_C 0x6fe0
//#define SPU2_REV_SIZE_HALL 0xade0
//#define SPU2_REV_SIZE_SPACE 0xf6c0
//#define SPU2_REV_SIZE_ECHO 0x18040
//#define SPU2_REV_SIZE_DELAY 0x18040
//#define SPU2_REV_SIZE_PIPE 0x3c00
//
//#define SPU2_SOUNDOUT_TOPADDR 0x0
//#define SPU2_SOUNDIN_TOPADDR 0x4000
//#define SPU2_USERAREA_TOPADDR 0x5010
//
//#define SPU2_INIT_COLD 0
//#define SPU2_INIT_HOT 1
REG_C0_SPUON1 = 0x01A0, // Key On 0/1
REG_C0_SPUON2 = 0x01A2,
REG_C0_SPUOFF1 = 0x01A4, // Key Off 0/1
REG_C0_SPUOFF2 = 0x01A6,
#define REG_C1_FMOD1 0x0580
#define REG_C1_FMOD2 0x0582
#define REG_S_NON 0x0184
#define REG_C1_VMIXL1 0x0588
#define REG_C1_VMIXL2 0x058A
#define REG_S_VMIXEL 0x018C
#define REG_C1_VMIXR1 0x0590
#define REG_C1_VMIXR2 0x0592
#define REG_S_VMIXER 0x0194
#define REG_C1_MMIX 0x0598
#define REG_C1_IRQA_HI 0x059C
#define REG_C1_IRQA_LO 0x059E
#define REG_C1_SPUON1 0x5A0
#define REG_C1_SPUON2 0x5A2
#define REG_C1_SPUOFF1 0x5A4
#define REG_C1_SPUOFF2 0x5A6
#define REG_C1_SPUADDR_HI 0x05A8
#define REG_C1_SPUADDR_LO 0x05AA
#define REG_C1_SPUDATA 0x05AC
#define REG_C1_DMACTRL 0x05AE
#define REG_VA_SSA 0x01C0
#define REG_VA_LSAX 0x01C4
#define REG_VA_NAX 0x01C8
#define REG_A_ESA 0x02E0
#define REG_A_EEA 0x033C
#define REG_C1_END1 0x0740
#define REG_C1_END2 0x0742
#define REG_C1_CTRL 0x059A
#define REG_C1_ADMAS 0x05B0
#define REG_C1_SPUSTAT 0x0744
#define REG_P_MVOLL 0x0760
#define REG_P_MVOLR 0x0762
#define REG_P_EVOLL 0x0764
#define REG_P_EVOLR 0x0766
#define REG_P_AVOLL 0x0768
#define REG_P_AVOLR 0x076A
#define REG_C1_BVOLL 0x0794
#define REG_C1_BVOLR 0x0796
#define REG_P_MVOLXL 0x0770
#define REG_P_MVOLXR 0x0772
#define SPDIF_OUT 0x07C0
#define REG_IRQINFO 0x07C2
#define SPDIF_MODE 0x07C6
#define SPDIF_MEDIA 0x07C8
REG_C0_SPUADDR_HI = 0x01A8, // Transfer starting address - hi
REG_C0_SPUADDR_LO = 0x01AA, // Transfer starting address - lo
REG_C0_SPUDATA = 0x01AC, // Transfer data
REG_C0_DMACTRL = 0x01AE, // unimplemented
REG_C0_ADMAS = 0x01B0, // AutoDMA Status
// Section Unimplemented
REG_VA_SSA = 0x01C0, // Waveform data starting address
REG_VA_LSAX = 0x01C4, // Loop point address
REG_VA_NAX = 0x01C8, // Waveform data that should be read next
REG_A_ESA = 0x02E0, //Address: Top address of working area for effects processing
R_FB_SRC_A = 0x02E4, // Feedback Source A
R_FB_SRC_B = 0x02E8, // Feedback Source B
R_IIR_DEST_A0 = 0x02EC,
R_IIR_DEST_A1 = 0x02F0,
R_ACC_SRC_A0 = 0x02F4,
R_ACC_SRC_A1 = 0x02F8,
R_ACC_SRC_B0 = 0x02FC,
R_ACC_SRC_B1 = 0x0300,
R_IIR_SRC_A0 = 0x0304,
R_IIR_SRC_A1 = 0x0308,
R_IIR_DEST_B0 = 0x030C,
R_IIR_DEST_B1 = 0x0310,
R_ACC_SRC_C0 = 0x0314,
R_ACC_SRC_C1 = 0x0318,
R_ACC_SRC_D0 = 0x031C,
R_ACC_SRC_D1 = 0x0320,
R_IIR_SRC_B1 = 0x0324,
R_IIR_SRC_B0 = 0x0328,
R_MIX_DEST_A0 = 0x032C,
R_MIX_DEST_A1 = 0x0330,
R_MIX_DEST_B0 = 0x0334,
R_MIX_DEST_B1 = 0x0338,
REG_A_EEA = 0x033C, // Address: End address of working area for effects processing (upper part of address only!)
// end unimplemented section
REG_C0_END1 = 0x0340, // End Point passed flag
REG_C0_END2 = 0x0342,
REG_C0_SPUSTAT = 0x0344, // Status register?
// core 1 has the same registers with 0x400 added, and ends at 0x746.
REG_C1_FMOD1 = 0x0580,
REG_C1_FMOD2 = 0x0582,
REG_C1_VMIXL1 = 0x0588,
REG_C1_VMIXL2 = 0x058A,
REG_C1_VMIXR1 = 0x0590,
REG_C1_VMIXR2 = 0x0592,
REG_C1_MMIX = 0x0598,
REG_C1_CTRL = 0x059A,
REG_C1_IRQA_HI = 0x059C,
REG_C1_IRQA_LO = 0x059E,
REG_C1_SPUON1 = 0x05A0,
REG_C1_SPUON2 = 0x05A2,
REG_C1_SPUOFF1 = 0x05A4,
REG_C1_SPUOFF2 = 0x05A6,
REG_C1_SPUADDR_HI = 0x05A8,
REG_C1_SPUADDR_LO = 0x05AA,
REG_C1_SPUDATA = 0x05AC,
REG_C1_DMACTRL = 0x05AE, // unimplemented
REG_C1_ADMAS = 0x05B0,
REG_C1_END1 = 0x0740,
REG_C1_END2 = 0x0742,
REG_C1_SPUSTAT = 0x0744,
// Interesting to note that *most* of the volume controls aren't implemented in Zerospu2.
REG_P_MVOLL = 0x0760, // Master Volume Left - unimplemented
REG_P_MVOLR = 0x0762, // Master Volume Right - unimplemented
REG_P_EVOLL = 0x0764, // Effect Volume Left - unimplemented
REG_P_EVOLR = 0x0766, // Effect Volume Right - unimplemented
REG_P_AVOLL = 0x0768, // Core External Input Volume Left (Only Core 1) - unimplemented
REG_P_AVOLR = 0x076A, // Core External Input Volume Right (Only Core 1) - unimplemented
REG_C0_BVOLL = 0x076C, // Sound Data Volume Left
REG_C0_BVOLR = 0x076E, // Sound Data Volume Right
REG_P_MVOLXL = 0x0770, // Current Master Volume Left - unimplemented
REG_P_MVOLXR = 0x0772, // Current Master Volume Right - unimplemented
// Another unimplemented section
R_IIR_ALPHA = 0x0774, // IIR alpha (% used)
R_ACC_COEF_A = 0x0776,
R_ACC_COEF_B = 0x0778,
R_ACC_COEF_C = 0x077A,
R_ACC_COEF_D = 0x077C,
R_IIR_COEF = 0x077E,
R_FB_ALPHA = 0x0780, // feedback alpha (% used)
R_FB_X = 0x0782, // feedback
R_IN_COEF_L = 0x0784,
R_IN_COEF_R = 0x0786,
// end unimplemented section
REG_C1_BVOLL = 0x0794,
REG_C1_BVOLR = 0x0796,
SPDIF_OUT = 0x07C0, // SPDIF Out: OFF/'PCM'/Bitstream/Bypass - unimplemented
REG_IRQINFO = 0x07C2,
SPDIF_MODE = 0x07C6, // unimplemented
SPDIF_MEDIA = 0x07C8, // SPDIF Media: 'CD'/DVD - unimplemented
SPDIF_COPY_PROT = 0x07CC // SPDIF Copy Protection - unimplemented
// NOTE: SPDIF_COPY is defined in Linux kernel headers as 0x0004.
};
// 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
#define SPDIF_OUT_PCM 0x0020 //encode spdif from spu2 pcm output
#define SPDIF_OUT_BYPASS 0x0100 //bypass spu2 processing
#define SPDIF_MODE_BYPASS_BITSTREAM 0x0002 //bypass mode for digital bitstream data
#define SPDIF_MODE_BYPASS_PCM 0x0000 //bypass mode for pcm data (using analog output)
#define SPDIF_MODE_MEDIA_CD 0x0800 //source media is a CD
#define SPDIF_MODE_MEDIA_DVD 0x0000 //source media is a DVD
#define SPDIF_MEDIA_CDVD 0x0200
#define SPDIF_MEDIA_400 0x0000
#define SPDIF_COPY_NORMAL 0x0000 // spdif stream is not protected
#define SPDIF_COPY_PROHIBIT 0x8000 // spdif stream can't be copied
#define SPU_AUTODMA_ONESHOT 0 //spu2
#define SPU_AUTODMA_LOOP 1 //spu2
@ -316,25 +360,49 @@ static __forceinline long InterlockedExchangeAdd(long volatile* Addend, long Val
#define spu2Rs16(mem) (*(s16*)&spu2regs[(mem) & 0xffff])
#define spu2Ru16(mem) (*(u16*)&spu2regs[(mem) & 0xffff])
//#define spu2Rs32(mem) (*(s32*)&spu2regs[(mem) & 0xffff])
//#define spu2Ru32(mem) (*(u32*)&spu2regs[(mem) & 0xffff])
#define IRQINFO spu2Ru16(REG_IRQINFO)
#define SPU2_GET32BIT(lo,hi) (((u32)(spu2Ru16(hi)&0x3f)<<16)|(u32)spu2Ru16(lo))
#define SPU2_SET32BIT(value, lo, hi) { \
spu2Ru16(hi) = ((value)>>16)&0x3f; \
spu2Ru16(lo) = (value)&0xffff; \
} \
static __forceinline u32 SPU2_GET32BIT(u32 lo, u32 hi)
{
return (((u32)(spu2Ru16(hi) & 0x3f) << 16) | (u32)spu2Ru16(lo));
}
#define C0_IRQA SPU2_GET32BIT(REG_C0_IRQA_LO, REG_C0_IRQA_HI)
#define C1_IRQA SPU2_GET32BIT(REG_C1_IRQA_LO, REG_C1_IRQA_HI)
static __forceinline void SPU2_SET32BIT(u32 value, u32 lo, u32 hi)
{
spu2Ru16(hi) = ((value) >> 16) & 0x3f;
spu2Ru16(lo) = (value) & 0xffff;
}
#define C0_SPUADDR SPU2_GET32BIT(REG_C0_SPUADDR_LO, REG_C0_SPUADDR_HI)
#define C1_SPUADDR SPU2_GET32BIT(REG_C1_SPUADDR_LO, REG_C1_SPUADDR_HI)
static __forceinline u32 C0_IRQA()
{
return SPU2_GET32BIT(REG_C0_IRQA_LO, REG_C0_IRQA_HI);
}
#define C0_SPUADDR_SET(value) SPU2_SET32BIT(value, REG_C0_SPUADDR_LO, REG_C0_SPUADDR_HI)
#define C1_SPUADDR_SET(value) SPU2_SET32BIT(value, REG_C1_SPUADDR_LO, REG_C1_SPUADDR_HI)
static __forceinline u32 C1_IRQA()
{
return SPU2_GET32BIT(REG_C1_IRQA_LO, REG_C1_IRQA_HI);
}
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 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);
}
#define SPU_NUMBER_VOICES 48
@ -483,4 +551,8 @@ struct ADMA
int Enabled; // used to make sure that ADMA doesn't get interrupted with a writeDMA call
};
extern ADMA Adma4;
extern ADMA Adma7;
#endif /* __SPU2_H__ */