ZeroSPU2: Add two files I forgot in my last commit.

git-svn-id: http://pcsx2-playground.googlecode.com/svn/trunk@633 a6443dda-0b58-4228-96e9-037be469359c
This commit is contained in:
arcum42 2009-01-25 19:20:34 +00:00 committed by Gregory Hainaut
parent be57d5384a
commit a165a01495
2 changed files with 578 additions and 0 deletions

255
plugins/zerospu2/voices.cpp Normal file
View File

@ -0,0 +1,255 @@
/* ZeroSPU2
* Copyright (C) 2006-2007 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 <assert.h>
#include <stdlib.h>
#include "SoundTouch/SoundTouch.h"
#include "SoundTouch/WavFile.h"
// 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 = (SAMPLE_RATE * 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()
{
}

View File

@ -0,0 +1,323 @@
/* ZeroSPU2
* Copyright (C) 2006-2007 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 <assert.h>
#include <stdlib.h>
#include "SoundTouch/SoundTouch.h"
#include "SoundTouch/WavFile.h"
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;
ADMA *Adma = &Adma4;
if (interrupt & 0x2)
{
printf("4 returning for interrupt\n");
return 0;
}
if (Adma->AmountLeft <= 0)
{
printf("4 amount left is 0\n");
return 1;
}
assert( Adma->AmountLeft >= 512 );
spuaddr = C0_SPUADDR();
// SPU2 Deinterleaves the Left and Right Channels
memcpy((short*)(spu2mem + spuaddr + 0x2000),(short*)Adma->MemAddr,512);
Adma->MemAddr += 256;
memcpy((short*)(spu2mem + spuaddr + 0x2200),(short*)Adma->MemAddr,512);
Adma->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);
Adma->AmountLeft-=512;
if (Adma->AmountLeft > 0)
return 0;
else
return 1;
}
int ADMAS7Write()
{
u32 spuaddr;
ADMA *Adma = &Adma7;
if (interrupt & 0x4)
{
printf("7 returning for interrupt\n");
return 0;
}
if (Adma->AmountLeft <= 0)
{
printf("7 amount left is 0\n");
return 1;
}
assert( Adma->AmountLeft >= 512 );
spuaddr = C1_SPUADDR();
// SPU2 Deinterleaves the Left and Right Channels
memcpy((short*)(spu2mem + spuaddr + 0x2400),(short*)Adma->MemAddr,512);
Adma->MemAddr += 256;
memcpy((short*)(spu2mem + spuaddr + 0x2600),(short*)Adma->MemAddr,512);
Adma->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);
Adma->AmountLeft-=512;
assert( Adma->AmountLeft >= 0 );
if (Adma->AmountLeft > 0)
return 0;
else
return 1;
}
void CALLBACK SPU2writeDMA4Mem(u16* pMem, int size)
{
u32 spuaddr;
ADMA *Adma = &Adma4;
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 ( !Adma->Enabled )
Adma->Index = 0;
Adma->MemAddr = pMem;
Adma->AmountLeft = size;
SPUTargetCycle[0] = size;
spu2Ru16(REG_C0_SPUSTAT)&=~0x80;
if (!Adma->Enabled || Adma->Index > 384)
{
C0_SPUADDR_SET(0);
if (ADMAS4Write())
{
SPUStartCycle[0] = SPUCycles;
interrupt |= (1<<1);
}
}
Adma->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;
ADMA *Adma = &Adma7;
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 (!Adma->Enabled ) Adma->Index = 0;
Adma->MemAddr = pMem;
Adma->AmountLeft = size;
SPUTargetCycle[1] = size;
spu2Ru16(REG_C1_SPUSTAT)&=~0x80;
if (!Adma->Enabled || Adma->Index > 384)
{
C1_SPUADDR_SET(0);
if (ADMAS7Write())
{
SPUStartCycle[1] = SPUCycles;
interrupt |= (1<<2);
}
}
Adma->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;
}