+static void do_irq(void)
+{
+ if(!(spuStat & STAT_IRQ))
+ {
+ spuStat |= STAT_IRQ;
+ if(irqCallback) irqCallback();
+ }
+}
+
+static void decode_block_data(int *dest, const unsigned char *src, int predict_nr, int shift_factor)
+{
+ int nSample;
+ int fa, s_1, s_2, d, s;
+
+ s_1 = dest[27];
+ s_2 = dest[26];
+
+ for (nSample = 0; nSample < 28; src++)
+ {
+ d = (int)*src;
+ s = (int)(signed short)((d & 0x0f) << 12);
+
+ fa = s >> shift_factor;
+ fa += ((s_1 * f[predict_nr][0])>>6) + ((s_2 * f[predict_nr][1])>>6);
+ s_2=s_1;s_1=fa;
+
+ dest[nSample++] = fa;
+
+ s = (int)(signed short)((d & 0xf0) << 8);
+ fa = s >> shift_factor;
+ fa += ((s_1 * f[predict_nr][0])>>6) + ((s_2 * f[predict_nr][1])>>6);
+ s_2=s_1;s_1=fa;
+
+ dest[nSample++] = fa;
+ }
+}
+
+static int decode_block(int ch)
+{
+ unsigned char *start;
+ int predict_nr,shift_factor,flags;
+ 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(pSpuIrq == start) // irq address reached?
+ {
+ do_irq(); // -> call main emu
+ ret = 1;
+ }
+ }
+
+ predict_nr=(int)start[0];
+ shift_factor=predict_nr&0xf;
+ predict_nr >>= 4;
+
+ 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;
+
+ start = s_chan[ch].pLoop;
+ }
+
+ if (start - spuMemC >= 0x80000) {
+ // most likely wrong
+ start = spuMemC;
+ printf("ch%d oflow\n", ch);
+ }
+
+ s_chan[ch].pCurr = start; // store values for next cycle
+ s_chan[ch].bJump = flags & 1;
+
+ return ret;
+}
+
+// do block, but ignore sample data
+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(flags & 4)
+ s_chan[ch].pLoop = start;
+
+ s_chan[ch].pCurr += 16;
+
+ if(flags & 1)
+ s_chan[ch].pCurr = s_chan[ch].pLoop;
+
+ s_chan[ch].bJump = flags & 1;
+ return ret;
+}
+
+#define make_do_samples(name, fmod_code, interp_start, interp1_code, interp2_code, interp_end) \
+static int do_samples_##name(int ch, int ns, int ns_to) \
+{ \
+ int sinc = s_chan[ch].sinc; \
+ int spos = s_chan[ch].spos; \
+ int sbpos = s_chan[ch].iSBPos; \
+ int *SB = s_chan[ch].SB; \
+ int ret = -1; \
+ int d, fa; \
+ interp_start; \
+ \
+ for (; ns < ns_to; ns++) \
+ { \
+ fmod_code; \
+ \
+ while (spos >= 0x10000) \
+ { \
+ if(sbpos == 28) \
+ { \
+ sbpos = 0; \
+ d = decode_block(ch); \
+ if(d && iSPUIRQWait) \
+ { \
+ ret = ns; \
+ goto out; \
+ } \
+ } \
+ \
+ fa = SB[sbpos++]; \
+ interp1_code; \
+ spos -= 0x10000; \
+ } \
+ \
+ interp2_code; \
+ spos += sinc; \
+ } \
+ \
+out: \
+ s_chan[ch].sinc = sinc; \
+ s_chan[ch].spos = spos; \
+ s_chan[ch].iSBPos = sbpos; \
+ interp_end; \
+ \
+ return ret; \
+}
+
+#define fmod_recv_check \
+ if(s_chan[ch].bFMod==1 && iFMod[ns]) \
+ sinc = FModChangeFrequency(ch,ns)
+
+make_do_samples(default, fmod_recv_check, ,
+ StoreInterpolationVal(ch, fa),
+ ChanBuf[ns] = iGetInterpolationVal(ch, spos), )
+make_do_samples(noint, , fa = s_chan[ch].SB[29], , ChanBuf[ns] = fa, s_chan[ch].SB[29] = fa)
+
+#define simple_interp_store \
+ s_chan[ch].SB[28] = 0; \
+ s_chan[ch].SB[29] = s_chan[ch].SB[30]; \
+ s_chan[ch].SB[30] = s_chan[ch].SB[31]; \
+ s_chan[ch].SB[31] = fa; \
+ s_chan[ch].SB[32] = 1
+
+#define simple_interp_get \
+ if(sinc<0x10000) /* -> upsampling? */ \
+ InterpolateUp(ch); /* --> interpolate up */ \
+ else InterpolateDown(ch); /* --> else down */ \
+ ChanBuf[ns] = s_chan[ch].SB[29]
+
+make_do_samples(simple, , ,
+ simple_interp_store, simple_interp_get, )
+
+static int do_samples_noise(int ch, int ns, int ns_to)
+{
+ int level, shift, bit;
+
+ s_chan[ch].spos += s_chan[ch].sinc * (ns_to - ns);
+ while (s_chan[ch].spos >= 28*0x10000)
+ {
+ skip_block(ch);
+ s_chan[ch].spos -= 28*0x10000;
+ }
+
+ // modified from DrHell/shalma, no fraction
+ level = (spuCtrl >> 10) & 0x0f;
+ level = 0x8000 >> level;
+
+ for (; ns < ns_to; ns++)
+ {
+ dwNoiseCount += 2;
+ if (dwNoiseCount >= level)
+ {
+ dwNoiseCount -= level;
+ shift = (dwNoiseVal >> 10) & 0x1f;
+ bit = (0x69696969 >> shift) & 1;
+ if (dwNoiseVal & 0x8000)
+ bit ^= 1;
+ dwNoiseVal = (dwNoiseVal << 1) | bit;
+ }
+
+ ChanBuf[ns] = (signed short)dwNoiseVal;
+ }
+
+ return -1;
+}
+
+#ifdef __arm__
+// asm code; lv and rv must be 0-3fff
+extern void mix_chan(int start, int count, int lv, int rv);
+extern void mix_chan_rvb(int start, int count, int lv, int rv);
+#else
+static void mix_chan(int start, int count, int lv, int rv)
+{
+ int *dst = SSumLR + start * 2;
+ const int *src = ChanBuf + start;
+ int l, r;
+
+ while (count--)
+ {
+ int sval = *src++;
+
+ l = (sval * lv) >> 14;
+ r = (sval * rv) >> 14;
+ *dst++ += l;
+ *dst++ += r;
+ }
+}
+
+static void mix_chan_rvb(int start, int count, int lv, int rv)
+{
+ int *dst = SSumLR + start * 2;
+ int *drvb = sRVBStart + start * 2;
+ const int *src = ChanBuf + start;
+ int l, r;
+
+ while (count--)
+ {
+ int sval = *src++;
+
+ l = (sval * lv) >> 14;
+ r = (sval * rv) >> 14;
+ *dst++ += l;
+ *dst++ += r;
+ *drvb++ += l;
+ *drvb++ += r;
+ }
+}
+#endif
+