+ 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, int *SB)
+{
+ unsigned char *start;
+ int predict_nr, shift_factor, flags;
+ int ret = 0;
+
+ start = s_chan[ch].pCurr; // set up the current pos
+ if (start == spu.spuMemC) // ?
+ ret = 1;
+
+ if (s_chan[ch].prevflags & 1) // 1: stop/loop
+ {
+ if (!(s_chan[ch].prevflags & 2))
+ ret = 1;
+
+ start = s_chan[ch].pLoop;
+ }
+ else
+ check_irq(ch, start); // hack, see check_irq below..
+
+ predict_nr = (int)start[0];
+ shift_factor = predict_nr & 0xf;
+ predict_nr >>= 4;
+
+ decode_block_data(SB, start + 2, predict_nr, shift_factor);
+
+ flags = start[1];
+ if (flags & 4)
+ s_chan[ch].pLoop = start; // loop adress
+
+ start += 16;
+
+ if (flags & 1) { // 1: stop/loop
+ start = s_chan[ch].pLoop;
+ check_irq(ch, start); // hack.. :(
+ }
+
+ if (start - spu.spuMemC >= 0x80000)
+ start = spu.spuMemC;
+
+ s_chan[ch].pCurr = start; // store values for next cycle
+ s_chan[ch].prevflags = flags;
+
+ return ret;
+}
+
+// do block, but ignore sample data
+static int skip_block(int ch)
+{
+ unsigned char *start = s_chan[ch].pCurr;
+ int flags;
+ int ret = 0;
+
+ if (s_chan[ch].prevflags & 1) {
+ if (!(s_chan[ch].prevflags & 2))
+ ret = 1;
+
+ start = s_chan[ch].pLoop;
+ }
+ else
+ check_irq(ch, start);
+
+ flags = start[1];
+ if (flags & 4)
+ s_chan[ch].pLoop = start;
+
+ start += 16;
+
+ if (flags & 1) {
+ start = s_chan[ch].pLoop;
+ check_irq(ch, start);
+ }
+
+ s_chan[ch].pCurr = start;
+ s_chan[ch].prevflags = flags;
+
+ return ret;
+}
+
+#ifdef THREAD_ENABLED
+
+static int decode_block_work(int ch, int *SB)
+{
+ int predict_nr, shift_factor, flags;
+ const unsigned char *ram = worker->ram;
+ int start = worker->ch[ch].start;
+ int loop = worker->ch[ch].loop;
+
+ predict_nr = ram[start];
+ shift_factor = predict_nr & 0xf;
+ predict_nr >>= 4;
+
+ decode_block_data(SB, ram + start + 2, predict_nr, shift_factor);
+
+ flags = ram[start + 1];
+ if (flags & 4)
+ loop = start; // loop adress
+
+ start += 16;
+
+ if (flags & 1) // 1: stop/loop
+ start = loop;
+
+ worker->ch[ch].start = start & 0x7ffff;
+ worker->ch[ch].loop = loop;
+
+ return 0;
+}
+
+#endif
+
+// if irq is going to trigger sooner than in upd_samples, set upd_samples
+static void scan_for_irq(int ch, unsigned int *upd_samples)
+{
+ int pos, sinc, sinc_inv, end;
+ unsigned char *block;
+ int flags;
+
+ block = s_chan[ch].pCurr;
+ pos = s_chan[ch].spos;
+ sinc = s_chan[ch].sinc;
+ end = pos + *upd_samples * sinc;
+
+ pos += (28 - s_chan[ch].iSBPos) << 16;
+ while (pos < end)
+ {
+ if (block == spu.pSpuIrq)
+ break;
+ flags = block[1];
+ block += 16;
+ if (flags & 1) { // 1: stop/loop
+ block = s_chan[ch].pLoop;
+ if (block == spu.pSpuIrq) // hack.. (see decode_block)
+ break;
+ }
+ pos += 28 << 16;
+ }
+
+ if (pos < end)
+ {
+ sinc_inv = s_chan[ch].sinc_inv;
+ if (sinc_inv == 0)
+ sinc_inv = s_chan[ch].sinc_inv = (0x80000000u / (uint32_t)sinc) << 1;
+
+ pos -= s_chan[ch].spos;
+ *upd_samples = (((uint64_t)pos * sinc_inv) >> 32) + 1;
+ //xprintf("ch%02d: irq sched: %3d %03d\n",
+ // ch, *upd_samples, *upd_samples * 60 * 263 / 44100);
+ }
+}
+
+#define make_do_samples(name, fmod_code, interp_start, interp1_code, interp2_code, interp_end) \
+static noinline int do_samples_##name(int (*decode_f)(int ch, int *SB), int ch, \
+ int ns_to, int *SB, int sinc, int *spos, int *sbpos) \
+{ \
+ int ns, d, fa; \
+ int ret = ns_to; \
+ interp_start; \
+ \
+ for (ns = 0; ns < ns_to; ns++) \
+ { \
+ fmod_code; \
+ \
+ *spos += sinc; \
+ while (*spos >= 0x10000) \
+ { \
+ fa = SB[(*sbpos)++]; \
+ if (*sbpos >= 28) \
+ { \
+ *sbpos = 0; \
+ d = decode_f(ch, SB); \
+ if (d && ns < ret) \
+ ret = ns; \
+ } \
+ \
+ interp1_code; \
+ *spos -= 0x10000; \
+ } \
+ \
+ interp2_code; \
+ } \
+ \
+ interp_end; \
+ \
+ return ret; \
+}
+
+#define fmod_recv_check \
+ if(s_chan[ch].bFMod==1 && iFMod[ns]) \
+ sinc = FModChangeFrequency(SB, s_chan[ch].iRawPitch, ns)
+
+make_do_samples(default, fmod_recv_check, ,
+ StoreInterpolationVal(SB, sinc, fa, s_chan[ch].bFMod==2),
+ ChanBuf[ns] = iGetInterpolationVal(SB, sinc, *spos, s_chan[ch].bFMod==2), )
+make_do_samples(noint, , fa = SB[29], , ChanBuf[ns] = fa, SB[29] = fa)
+
+#define simple_interp_store \
+ SB[28] = 0; \
+ SB[29] = SB[30]; \
+ SB[30] = SB[31]; \
+ SB[31] = fa; \
+ SB[32] = 1
+
+#define simple_interp_get \
+ if(sinc<0x10000) /* -> upsampling? */ \
+ InterpolateUp(SB, sinc); /* --> interpolate up */ \
+ else InterpolateDown(SB, sinc); /* --> else down */ \
+ ChanBuf[ns] = SB[29]
+
+make_do_samples(simple, , ,
+ simple_interp_store, simple_interp_get, )
+
+static int do_samples_skip(int ch, int ns_to)
+{
+ int ret = ns_to, ns, d;
+
+ s_chan[ch].spos += s_chan[ch].iSBPos << 16;
+
+ for (ns = 0; ns < ns_to; ns++)
+ {
+ s_chan[ch].spos += s_chan[ch].sinc;
+ while (s_chan[ch].spos >= 28*0x10000)
+ {
+ d = skip_block(ch);
+ if (d && ns < ret)
+ ret = ns;
+ s_chan[ch].spos -= 28*0x10000;
+ }
+ }
+
+ s_chan[ch].iSBPos = s_chan[ch].spos >> 16;
+ s_chan[ch].spos &= 0xffff;
+
+ return ret;
+}