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
***************************************************************************/
/***************************************************************************
} while (0)
#endif
+#define PSXCLK 33868800 /* 33.8688 MHz */
+
/*
#if defined (USEMACOSX)
static char * libraryName = N_("Mac OS X Sound");
unsigned char * spuMemC;
unsigned char * pSpuIrq=0;
unsigned char * pSpuBuffer;
-unsigned char * pMixIrq=0;
// user settings
int iVolume=768; // 1024 is 1.0
int iXAPitch=1;
-int iSPUIRQWait=1;
-int iDebugMode=0;
-int iRecordMode=0;
int iUseReverb=2;
int iUseInterpolation=2;
unsigned int dwNoiseVal; // global noise generator
unsigned int dwNoiseCount;
-int iSpuAsyncWait=0;
unsigned short spuCtrl=0; // some vars to store psx reg infos
unsigned short spuStat=0;
int iCycle = 0;
short * pS;
+static int decode_dirty_ch;
+int decode_pos;
+int had_dma;
int lastch=-1; // last channel processed on spu irq in timer mode
static int lastns=0; // last ns pos
+static int cycles_since_update;
#define CDDA_BUFFER_SIZE (16384 * sizeof(uint32_t)) // must be power of 2
#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
////////////////////////////////////////////////////////////////////////
{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
}
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;
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];
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;
}
{
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) \
{ \
sbpos = 0; \
d = decode_block(ch); \
- if(d && iSPUIRQWait) \
+ if(d) \
{ \
ret = ns; \
goto out; \
}
#endif
+// 0x0800-0x0bff Voice 1
+// 0x0c00-0x0fff Voice 3
+static void noinline do_decode_bufs(int which, int start, int count)
+{
+ const int *src = ChanBuf + start;
+ unsigned short *dst = &spuMem[0x800/2 + which*0x400/2];
+ int cursor = decode_pos;
+
+ while (count-- > 0)
+ {
+ dst[cursor] = *src++;
+ cursor = (cursor + 1) & 0x1ff;
+ }
+
+ // decode_pos is updated and irqs are checked later, after voice loop
+}
+
////////////////////////////////////////////////////////////////////////
// MAIN SPU FUNCTION
// here is the main job handler...
// basically the whole sound processing is done in this fat func!
////////////////////////////////////////////////////////////////////////
-static int do_samples(void)
+static int do_samples(int forced_updates)
{
int volmult = iVolume;
int ns,ns_from,ns_to;
// until enuff free place is available/a new channel gets
// started
- if(!dwNewChannel && SoundGetBytesBuffered()) // still enuff data in sound buffer?
+ if(!forced_updates && SoundGetBytesBuffered()) // still enuff data in sound buffer?
{
return 0;
}
+ cycles_since_update = 0;
+ if(forced_updates > 0)
+ forced_updates--;
+
//--------------------------------------------------// continue from irq handling in timer mode?
ns_from=0;
MixADSR(ch, ns_from, ns_to);
+ if(ch==1 || ch==3)
+ {
+ do_decode_bufs(ch/2, ns_from, ns_to-ns_from);
+ decode_dirty_ch |= 1<<ch;
+ }
+
if(s_chan[ch].bFMod==2) // fmod freq channel
memcpy(iFMod, ChanBuf, sizeof(iFMod));
else if(s_chan[ch].bRVBActive)
// advance "stopped" channels that can cause irqs
// (all chans are always playing on the real thing..)
- if(!bIRQReturn && (spuCtrl&CTRL_IRQ))
+ if(spuCtrl&CTRL_IRQ)
for(ch=0;ch<MAXCHAN;ch++)
{
if(!(silentch&(1<<ch))) continue; // already handled
if(s_chan[ch].pCurr > pSpuIrq && s_chan[ch].pLoop > pSpuIrq)
continue;
- s_chan[ch].spos += s_chan[ch].sinc * NSSIZE;
+ s_chan[ch].spos += s_chan[ch].sinc * (ns_to - ns_from);
while(s_chan[ch].spos >= 28 * 0x10000)
{
unsigned char *start = s_chan[ch].pCurr;
// no need for bIRQReturn since the channel is silent
- iSpuAsyncWait |= skip_block(ch);
+ skip_block(ch);
if(start == s_chan[ch].pCurr)
{
// looping on self
}
}
- if(bIRQReturn && iSPUIRQWait) // special return for "spu irq - wait for cpu action"
- {
- iSpuAsyncWait=1;
- bIRQReturn=0;
+ if(bIRQReturn) // special return for "spu irq - wait for cpu action"
return 0;
- }
+ if(unlikely(silentch & decode_dirty_ch & (1<<1))) // must clear silent channel decode buffers
+ {
+ memset(&spuMem[0x800/2], 0, 0x400);
+ decode_dirty_ch &= ~(1<<1);
+ }
+ if(unlikely(silentch & decode_dirty_ch & (1<<3)))
+ {
+ memset(&spuMem[0xc00/2], 0, 0x400);
+ decode_dirty_ch &= ~(1<<3);
+ }
//---------------------------------------------------//
//- here we have another 1 ms of sound data
// 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(unlikely((spuCtrl&CTRL_IRQ) && pSpuIrq && pSpuIrq<spuMemC+0x1000))
{
- for(ns=0;ns<NSSIZE;ns++)
+ int irq_pos=(pSpuIrq-spuMemC)/2 & 0x1ff;
+ if((decode_pos <= irq_pos && irq_pos < decode_pos+NSSIZE)
+ || (decode_pos+NSSIZE > 0x200 && irq_pos < ((decode_pos+NSSIZE) & 0x1ff)))
{
- 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;
+ //printf("decoder irq %x\n", decode_pos);
+ do_irq();
}
}
+ decode_pos = (decode_pos + NSSIZE) & 0x1ff;
InitREVERB();
void CALLBACK SPUasync(unsigned long cycle)
{
+ static int old_ctrl;
+ int forced_updates = 0;
+ int do_update = 0;
+
if(!bSpuInit) return; // -> no init, no call
- if(iSpuAsyncWait)
+ cycles_since_update += cycle;
+
+ if(dwNewChannel || had_dma)
{
- iSpuAsyncWait++;
- if(iSpuAsyncWait<=16/FRAG_MSECS) return;
- iSpuAsyncWait=0;
+ forced_updates = 1;
+ do_update = 1;
+ had_dma = 0;
}
- do_samples();
+ 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);
}
// SPU UPDATE... new epsxe func
s_chan[i].pCurr=spuMemC;
}
- pMixIrq=spuMemC; // enable decoded buffer irqs by setting the address
-
ClearWorkingState();
bSpuInit=1; // flag: we are inited
spuIrq = 0;
spuAddr = 0xffffffff;
spuMemC = (unsigned char *)spuMem;
- pMixIrq = 0;
+ decode_pos = 0;
memset((void *)s_chan, 0, (MAXCHAN + 1) * sizeof(SPUCHAN));
pSpuIrq = 0;
- //iSPUIRQWait = 0;
lastch = -1;
SetupStreams(); // prepare streaming