aboutsummaryrefslogtreecommitdiff
path: root/plugins/dfsound
diff options
context:
space:
mode:
authornotaz2012-07-07 21:45:00 +0300
committernotaz2012-07-15 20:29:48 +0300
commite4f075af4e4ba79332c72809d3bb4ba6e2895253 (patch)
treecb9851e519f749f3f3a998055824d6755d2d4f1c /plugins/dfsound
parentf2aec10aa8e3befe7e89070e8559d070835cc23e (diff)
downloadpcsx_rearmed-e4f075af4e4ba79332c72809d3bb4ba6e2895253.tar.gz
pcsx_rearmed-e4f075af4e4ba79332c72809d3bb4ba6e2895253.tar.bz2
pcsx_rearmed-e4f075af4e4ba79332c72809d3bb4ba6e2895253.zip
spu: rework irq handling and loop reloading
irq might be too early, but this seems to be better than was before. Special thanks to Ryphecha for some ideas again.
Diffstat (limited to 'plugins/dfsound')
-rw-r--r--plugins/dfsound/externals.h2
-rw-r--r--plugins/dfsound/freeze.c13
-rw-r--r--plugins/dfsound/registers.c6
-rw-r--r--plugins/dfsound/spu.c105
4 files changed, 62 insertions, 64 deletions
diff --git a/plugins/dfsound/externals.h b/plugins/dfsound/externals.h
index 0146553..3cb388c 100644
--- a/plugins/dfsound/externals.h
+++ b/plugins/dfsound/externals.h
@@ -109,7 +109,7 @@ typedef struct
unsigned int bRVBActive:1; // reverb active flag
unsigned int bNoise:1; // noise active flag
unsigned int bFMod:2; // freq mod (0=off, 1=sound channel, 2=freq channel)
- unsigned int bJump:1; // last decoded block jumped
+ unsigned int prevflags:3; // flags from previous block
int iLeftVolume; // left volume
int iRightVolume; // right volume
diff --git a/plugins/dfsound/freeze.c b/plugins/dfsound/freeze.c
index d7e45b1..ec097c5 100644
--- a/plugins/dfsound/freeze.c
+++ b/plugins/dfsound/freeze.c
@@ -134,11 +134,11 @@ static void save_channel(SPUCHAN_orig *d, const SPUCHAN *s, int ch)
d->bOn = !!(dwChannelOn & (1<<ch));
d->bStop = s->bStop;
d->bReverb = s->bReverb;
- d->bIgnoreLoop = s->bJump;
d->iActFreq = 1;
d->iUsedFreq = 2;
d->iLeftVolume = s->iLeftVolume;
- d->bIgnoreLoop = 0;
+ // this one is nasty but safe, save compat is important
+ d->bIgnoreLoop = (s->prevflags ^ 2) << 1;
d->iRightVolume = s->iRightVolume;
d->iRawPitch = s->iRawPitch;
d->s_1 = s->SB[27]; // yes it's reversed
@@ -179,7 +179,7 @@ static void load_channel(SPUCHAN *d, const SPUCHAN_orig *s, int ch)
d->bRVBActive = s->bRVBActive;
d->bNoise = s->bNoise;
d->bFMod = s->bFMod;
- d->bJump = s->bIgnoreLoop;
+ d->prevflags = (s->bIgnoreLoop >> 1) ^ 2;
d->ADSRX.State = s->ADSRX.State;
d->ADSRX.AttackModeExp = s->ADSRX.AttackModeExp;
d->ADSRX.AttackRate = s->ADSRX.AttackRate;
@@ -232,11 +232,11 @@ long CALLBACK SPUfreeze(uint32_t ulFreezeMode,SPUFreeze_t * pF)
pFO->spuAddr=spuAddr;
if(pFO->spuAddr==0) pFO->spuAddr=0xbaadf00d;
- dwChannelOn&=~dwPendingChanOff;
- dwPendingChanOff=0;
-
for(i=0;i<MAXCHAN;i++)
{
+ if(!(s_chan[i].prevflags&2))
+ dwChannelOn&=~(1<<i);
+
save_channel(&pFO->s_chan[i],&s_chan[i],i);
if(pFO->s_chan[i].pCurr)
pFO->s_chan[i].pCurr-=(unsigned long)spuMemC;
@@ -257,7 +257,6 @@ long CALLBACK SPUfreeze(uint32_t ulFreezeMode,SPUFreeze_t * pF)
SPUplayADPCMchannel(&pF->xaS);
xapGlobal=0;
- dwPendingChanOff=0;
if(!strcmp(pF->szSPUName,"PBOSS") && pF->ulFreezeVersion==5)
LoadStateV5(pF);
diff --git a/plugins/dfsound/registers.c b/plugins/dfsound/registers.c
index 1a51cd7..45df2eb 100644
--- a/plugins/dfsound/registers.c
+++ b/plugins/dfsound/registers.c
@@ -173,10 +173,6 @@ void CALLBACK SPUwriteRegister(unsigned long reg, unsigned short val)
//------------------------------------------------//
case 14: // loop?
s_chan[ch].pLoop=spuMemC+((val&~1)<<3);
- if(s_chan[ch].bJump)
- // real machine would be most likely still doing the last block and use new value for the jump;
- // but we decode ahead a bit and already did the jump part, so compensate for that now.
- s_chan[ch].pCurr=s_chan[ch].pLoop;
break;
//------------------------------------------------//
}
@@ -428,7 +424,7 @@ static void SoundOn(int start,int end,unsigned short val)
s_chan[ch].bStop=0;
s_chan[ch].pCurr=spuMemC+((regAreaGet(ch,6)&~1)<<3); // must be block aligned
s_chan[ch].pLoop=spuMemC+((regAreaGet(ch,14)&~1)<<3);
- s_chan[ch].bJump=0;
+ s_chan[ch].prevflags=2;
dwNewChannel|=(1<<ch); // bitfield for faster testing
dwChannelOn|=1<<ch;
diff --git a/plugins/dfsound/spu.c b/plugins/dfsound/spu.c
index 4759f3f..d5a8215 100644
--- a/plugins/dfsound/spu.c
+++ b/plugins/dfsound/spu.c
@@ -5,7 +5,7 @@
copyright : (C) 2002 by Pete Bernert
email : BlackDove@addcom.de
- Portions (C) Gražvydas "notaz" Ignotas, 2010-2011
+ Portions (C) Gražvydas "notaz" Ignotas, 2010-2012
***************************************************************************/
/***************************************************************************
@@ -250,6 +250,26 @@ INLINE void InterpolateDown(int ch)
#include "xa.c"
+static void do_irq(void)
+{
+ //if(!(spuStat & STAT_IRQ))
+ {
+ spuStat |= STAT_IRQ; // asserted status?
+ if(irqCallback) irqCallback();
+ }
+}
+
+static int check_irq(int ch, unsigned char *pos)
+{
+ if((spuCtrl & CTRL_IRQ) && pos == pSpuIrq)
+ {
+ //printf("ch%d irq %04x\n", ch, pos - spuMemC);
+ do_irq();
+ return 1;
+ }
+ return 0;
+}
+
////////////////////////////////////////////////////////////////////////
// START SOUND... called by main thread to setup a new sound on a channel
////////////////////////////////////////////////////////////////////////
@@ -275,6 +295,8 @@ INLINE void StartSound(int ch)
{s_chan[ch].spos=0x30000L;s_chan[ch].SB[28]=0;} // -> start with more decoding
else {s_chan[ch].spos=0x10000L;s_chan[ch].SB[31]=0;} // -> no/simple interpolation starts with one 44100 decoding
+ check_irq(ch, s_chan[ch].pCurr); // just in case
+
dwNewChannel&=~(1<<ch); // clear new channel bit
}
@@ -390,15 +412,6 @@ INLINE int iGetInterpolationVal(int ch, int spos)
return fa;
}
-static void do_irq(void)
-{
- //if(!(spuStat & STAT_IRQ))
- {
- spuStat |= STAT_IRQ; // asserted status?
- if(irqCallback) irqCallback();
- }
-}
-
static void decode_block_data(int *dest, const unsigned char *src, int predict_nr, int shift_factor)
{
int nSample;
@@ -434,23 +447,17 @@ static int decode_block(int ch)
int ret = 0;
start=s_chan[ch].pCurr; // set up the current pos
- if(dwPendingChanOff&(1<<ch))
- {
- dwChannelOn&=~(1<<ch); // -> turn everything off
- dwPendingChanOff&=~(1<<ch);
- s_chan[ch].bStop=1;
- s_chan[ch].ADSRX.EnvelopeVol=0;
- }
- //////////////////////////////////////////// irq check
-
- if(spuCtrl&CTRL_IRQ)
+ if(s_chan[ch].prevflags&1) // 1: stop/loop
{
- if(pSpuIrq == start) // irq address reached?
+ if(!(s_chan[ch].prevflags&2))
{
- do_irq(); // -> call main emu
- ret = 1;
+ dwChannelOn&=~(1<<ch); // -> turn everything off
+ s_chan[ch].bStop=1;
+ s_chan[ch].ADSRX.EnvelopeVol=0;
}
+
+ start = s_chan[ch].pLoop;
}
predict_nr=(int)start[0];
@@ -459,29 +466,22 @@ static int decode_block(int ch)
decode_block_data(s_chan[ch].SB, start + 2, predict_nr, shift_factor);
- //////////////////////////////////////////// flag handler
-
flags=(int)start[1];
if(flags&4)
s_chan[ch].pLoop=start; // loop adress
start+=16;
- if(flags&1) // 1: stop/loop
- {
- if(!(flags&2))
- dwPendingChanOff|=1<<ch;
+ if(flags&1) // 1: stop/loop
start = s_chan[ch].pLoop;
- }
- if (start - spuMemC >= 0x80000) {
- // most likely wrong
+ if (start - spuMemC >= 0x80000)
start = spuMemC;
- printf("ch%d oflow\n", ch);
- }
+
+ ret = check_irq(ch, start);
s_chan[ch].pCurr = start; // store values for next cycle
- s_chan[ch].bJump = flags & 1;
+ s_chan[ch].prevflags = flags;
return ret;
}
@@ -491,24 +491,22 @@ static int skip_block(int ch)
{
unsigned char *start = s_chan[ch].pCurr;
int flags = start[1];
- int ret = 0;
- if(start == pSpuIrq)
- {
- do_irq();
- ret = 1;
- }
+ if(s_chan[ch].prevflags & 1)
+ start = s_chan[ch].pLoop;
if(flags & 4)
s_chan[ch].pLoop = start;
- s_chan[ch].pCurr += 16;
+ start += 16;
if(flags & 1)
- s_chan[ch].pCurr = s_chan[ch].pLoop;
+ start = s_chan[ch].pLoop;
- s_chan[ch].bJump = flags & 1;
- return ret;
+ s_chan[ch].pCurr = start;
+ s_chan[ch].prevflags = flags;
+
+ return check_irq(ch, start);
}
#define make_do_samples(name, fmod_code, interp_start, interp1_code, interp2_code, interp_end) \
@@ -824,18 +822,15 @@ static int do_samples(int forced_updates)
// an IRQ. Only problem: the "wait for cpu" option is kinda hard to do here
// in some of Peops timer modes. So: we ignore this option here (for now).
- if(pMixIrq)
+ if(pMixIrq && (spuCtrl&CTRL_IRQ) && pSpuIrq && pSpuIrq<spuMemC+0x1000)
{
for(ns=0;ns<NSSIZE;ns++)
{
- if((spuCtrl&0x40) && pSpuIrq && pSpuIrq<spuMemC+0x1000)
- {
for(ch=0;ch<4;ch++)
{
if(pSpuIrq>=pMixIrq+(ch*0x400) && pSpuIrq<pMixIrq+(ch*0x400)+2)
do_irq();
}
- }
pMixIrq+=2;if(pMixIrq>spuMemC+0x3ff) pMixIrq=spuMemC;
}
}
@@ -863,6 +858,7 @@ static int do_samples(int forced_updates)
void CALLBACK SPUasync(unsigned long cycle)
{
+ static int old_ctrl;
int forced_updates = 0;
int do_update = 0;
@@ -877,9 +873,16 @@ void CALLBACK SPUasync(unsigned long cycle)
had_dma = 0;
}
- // once per frame should be fine (using a bit more because of BIAS)
- if(cycles_since_update > PSXCLK/60 * 5/4)
+ if((spuCtrl&CTRL_IRQ) && (((spuCtrl^old_ctrl)&CTRL_IRQ) // irq was enabled
+ || cycles_since_update > PSXCLK/60 / 4)) {
do_update = 1;
+ forced_updates = cycles_since_update / (PSXCLK/44100) / NSSIZE;
+ }
+ // with no irqs, once per frame should be fine (using a bit more because of BIAS)
+ else if(cycles_since_update > PSXCLK/60 * 5/4)
+ do_update = 1;
+
+ old_ctrl = spuCtrl;
if(do_update)
do_samples(forced_updates);