mirror of https://github.com/PCSX2/pcsx2.git
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:
parent
7a456fe608
commit
98e9c9635d
|
@ -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;
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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__ */
|
||||
|
|
Loading…
Reference in New Issue