+ if (!worker->pending)
+ return;
+
+ thread_work_wait_sync();
+ worker->pending = 0;
+
+ mask = worker->chmask;
+ for (ch = 0; mask != 0; ch++, mask >>= 1) {
+ if (!(mask & 1)) continue;
+
+ // be sure there was no keyoff while thread was working
+ if (spu.s_chan[ch].ADSRX.State != ADSR_RELEASE)
+ spu.s_chan[ch].ADSRX.State = worker->ch[ch].adsr.State;
+ spu.s_chan[ch].ADSRX.EnvelopeVol = worker->ch[ch].adsr.EnvelopeVol;
+ }
+
+ spu.dwChannelOn &= ~worker->r_chan_end;
+ spu.decode_dirty_ch |= worker->r_decode_dirty;
+
+ do_samples_finish(worker->ns_to, worker->silentch,
+ worker->decode_pos);
+}
+
+#else
+
+static void queue_channel_work(int ns_to, int silentch) {}
+static void sync_worker_thread(void) {}
+
+#endif // THREAD_ENABLED
+
+////////////////////////////////////////////////////////////////////////
+// MAIN SPU FUNCTION
+// here is the main job handler...
+////////////////////////////////////////////////////////////////////////
+
+void do_samples(unsigned int cycles_to, int do_sync)
+{
+ unsigned int mask;
+ int ch, ns_to;
+ int silentch;
+ int cycle_diff;
+
+ cycle_diff = cycles_to - spu.cycles_played;
+ if (cycle_diff < -2*1048576 || cycle_diff > 2*1048576)
+ {
+ //xprintf("desync %u %d\n", cycles_to, cycle_diff);
+ spu.cycles_played = cycles_to;
+ return;
+ }
+
+ if (cycle_diff < 2 * 768)
+ return;
+
+ ns_to = (cycle_diff / 768 + 1) & ~1;
+ if (ns_to > NSSIZE) {
+ // should never happen
+ //xprintf("ns_to oflow %d %d\n", ns_to, NSSIZE);
+ ns_to = NSSIZE;
+ }
+
+ //////////////////////////////////////////////////////
+ // special irq handling in the decode buffers (0x0000-0x1000)
+ // we know:
+ // the decode buffers are located in spu memory in the following way:
+ // 0x0000-0x03ff CD audio left
+ // 0x0400-0x07ff CD audio right
+ // 0x0800-0x0bff Voice 1
+ // 0x0c00-0x0fff Voice 3
+ // and decoded data is 16 bit for one sample
+ // we assume:
+ // even if voices 1/3 are off or no cd audio is playing, the internal
+ // play positions will move on and wrap after 0x400 bytes.
+ // Therefore: we just need a pointer from spumem+0 to spumem+3ff, and
+ // increase this pointer on each sample by 2 bytes. If this pointer
+ // (or 0x400 offsets of this pointer) hits the spuirq address, we generate
+ // an IRQ.
+
+ if (unlikely((spu.spuCtrl & CTRL_IRQ)
+ && spu.pSpuIrq < spu.spuMemC+0x1000))
+ {
+ int irq_pos = (spu.pSpuIrq - spu.spuMemC) / 2 & 0x1ff;
+ int left = (irq_pos - spu.decode_pos) & 0x1ff;
+ if (0 < left && left <= ns_to)
+ {
+ //xprintf("decoder irq %x\n", spu.decode_pos);
+ do_irq();
+ }
+ }
+
+ if (worker != NULL)
+ sync_worker_thread();
+
+ mask = spu.dwNewChannel & 0xffffff;
+ for (ch = 0; mask != 0; ch++, mask >>= 1) {
+ if (mask & 1)
+ StartSound(ch);
+ }
+
+ silentch = ~spu.dwChannelOn & 0xffffff;
+
+ if (spu.dwChannelOn == 0) {
+ InitREVERB(ns_to);
+ do_samples_finish(ns_to, silentch, spu.decode_pos);
+ }
+ else {
+ if (do_sync || worker == NULL || !spu_config.iUseThread) {
+ do_channels(ns_to);
+ do_samples_finish(ns_to, silentch, spu.decode_pos);
+ }
+ else {
+ queue_channel_work(ns_to, silentch);
+ }
+ }
+
+ // advance "stopped" channels that can cause irqs
+ // (all chans are always playing on the real thing..)
+ if (spu.spuCtrl & CTRL_IRQ)
+ do_silent_chans(ns_to, silentch);
+
+ spu.cycles_played += ns_to * 768;
+ spu.decode_pos = (spu.decode_pos + ns_to) & 0x1ff;
+}
+
+static void do_samples_finish(int ns_to, int silentch, int decode_pos)
+{
+ int volmult = spu_config.iVolume;
+ int ns;
+ int d;
+
+ if(unlikely(silentch & spu.decode_dirty_ch & (1<<1))) // must clear silent channel decode buffers
+ {
+ memset(&spu.spuMem[0x800/2], 0, 0x400);
+ spu.decode_dirty_ch &= ~(1<<1);
+ }
+ if(unlikely(silentch & spu.decode_dirty_ch & (1<<3)))
+ {
+ memset(&spu.spuMem[0xc00/2], 0, 0x400);
+ spu.decode_dirty_ch &= ~(1<<3);
+ }