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
***************************************************************************/
/***************************************************************************
#include "externals.h"
#include "registers.h"
-#include "dsoundoss.h"
+#include "out.h"
#ifdef ENABLE_NLS
#include <libintl.h>
unsigned char * spuMemC;
unsigned char * pSpuIrq=0;
unsigned char * pSpuBuffer;
-unsigned char * pMixIrq=0;
// user settings
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
// helpers for gauss interpolation
#define gval0 (((short*)(&s_chan[ch].SB[29]))[gpos])
-#define gval(x) (((short*)(&s_chan[ch].SB[29]))[(gpos+x)&3])
+#define gval(x) ((int)((short*)(&s_chan[ch].SB[29]))[(gpos+x)&3])
#include "gauss_i.h"
#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
////////////////////////////////////////////////////////////////////////
int vl, vr;int gpos;
vl = (spos >> 6) & ~3;
gpos = s_chan[ch].SB[28];
- vr=(gauss[vl]*gval0)&~2047;
+ vr=(gauss[vl]*(int)gval0)&~2047;
vr+=(gauss[vl+1]*gval(1))&~2047;
vr+=(gauss[vl+2]*gval(2))&~2047;
vr+=(gauss[vl+3]*gval(3))&~2047;
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;
}
+ else
+ ret = check_irq(ch, start); // hack, see check_irq below..
predict_nr=(int)start[0];
shift_factor=predict_nr&0xf;
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;
+ ret |= check_irq(ch, start); // hack.. :(
}
- if (start - spuMemC >= 0x80000) {
- // most likely wrong
+ if (start - spuMemC >= 0x80000)
start = spuMemC;
- printf("ch%d oflow\n", ch);
- }
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;
+ int ret = check_irq(ch, start);
- 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].pCurr = start;
+ s_chan[ch].prevflags = flags;
- s_chan[ch].bJump = flags & 1;
return ret;
}
sbpos = 0; \
d = decode_block(ch); \
if(d) \
- { \
- ret = ns; \
- goto out; \
- } \
+ ret = ns_to = ns + 1; \
} \
\
fa = SB[sbpos++]; \
spos += sinc; \
} \
\
-out: \
s_chan[ch].sinc = sinc; \
s_chan[ch].spos = spos; \
s_chan[ch].iSBPos = sbpos; \
static int do_samples_noise(int ch, int ns, int ns_to)
{
int level, shift, bit;
+ int ret = -1, d;
s_chan[ch].spos += s_chan[ch].sinc * (ns_to - ns);
while (s_chan[ch].spos >= 28*0x10000)
{
- skip_block(ch);
+ d = skip_block(ch);
+ if (d)
+ ret = ns_to;
s_chan[ch].spos -= 28*0x10000;
}
ChanBuf[ns] = (signed short)dwNoiseVal;
}
- return -1;
+ return ret;
}
#ifdef __arm__
}
#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...
// until enuff free place is available/a new channel gets
// started
- if(!forced_updates && SoundGetBytesBuffered()) // still enuff data in sound buffer?
+ if(!forced_updates && out_current->busy()) // still enuff data in sound buffer?
{
return 0;
}
bIRQReturn=1;
lastch=ch;
lastns=ns_to=d;
- if(d==0)
- break;
}
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)
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();
// wanna have around 1/60 sec (16.666 ms) updates
if (iCycle++ > 16/FRAG_MSECS)
{
- SoundFeedStreamData((unsigned char *)pSpuBuffer,
+ out_current->feed(pSpuBuffer,
((unsigned char *)pS) - ((unsigned char *)pSpuBuffer));
pS = (short *)pSpuBuffer;
iCycle = 0;
void CALLBACK SPUasync(unsigned long cycle)
{
+ static int old_ctrl;
int forced_updates = 0;
int do_update = 0;
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);
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;
lastch = -1;
bSPUIsOpen = 0; // no more open
- RemoveSound(); // no more sound handling
+ out_current->finish(); // no more sound handling
return 0;
}