s_buffer(MIX_DEST_B0, mix_dest_b0);\r
    s_buffer(MIX_DEST_B1, mix_dest_b1);\r
 \r
-   l = (mix_dest_a0 + mix_dest_b0) / 3;\r
-   r = (mix_dest_a1 + mix_dest_b1) / 3;\r
+   l = (mix_dest_a0 + mix_dest_b0) / 2;\r
+   r = (mix_dest_a1 + mix_dest_b1) / 2;\r
 \r
-   l = (l * rvb.VolLeft)  >> 14;\r
-   r = (r * rvb.VolRight) >> 14;\r
+   l = (l * rvb.VolLeft)  >> 15; // 15?\r
+   r = (r * rvb.VolRight) >> 15;\r
+\r
+   SSumLR[ns++] += (l + l_old) / 2;\r
+   SSumLR[ns++] += (r + r_old) / 2;\r
+   SSumLR[ns++] += l;\r
+   SSumLR[ns++] += r;\r
+\r
+   l_old = l;\r
+   r_old = r;\r
+\r
+   curr_addr++;\r
+   if (curr_addr >= 0x40000) curr_addr = rvb.StartAddr;\r
+  }\r
+\r
+ rvb.iRVBLeft = l;\r
+ rvb.iRVBRight = r;\r
+ rvb.CurrAddr = curr_addr;\r
+}\r
+\r
+static void MixREVERB_off(void)\r
+{\r
+ int l_old = rvb.iRVBLeft;\r
+ int r_old = rvb.iRVBRight;\r
+ int curr_addr = rvb.CurrAddr;\r
+ int space = 0x40000 - rvb.StartAddr;\r
+ int l, r, ns;\r
+\r
+ for (ns = 0; ns < NSSIZE*2; )\r
+  {\r
+   l = (g_buffer(MIX_DEST_A0) + g_buffer(MIX_DEST_B0)) / 2;\r
+   r = (g_buffer(MIX_DEST_A1) + g_buffer(MIX_DEST_B1)) / 2;\r
+\r
+   l = (l * rvb.VolLeft)  >> 15;\r
+   r = (r * rvb.VolRight) >> 15;\r
 \r
    SSumLR[ns++] += (l + l_old) / 2;\r
    SSumLR[ns++] += (r + r_old) / 2;\r
 \r
  if (spuCtrl & 0x80)                                   // -> reverb on? oki\r
  {\r
-  if (rvb.dirty)\r
+  if (unlikely(rvb.dirty))\r
    prepare_offsets();\r
 \r
   MixREVERB();\r
  }\r
+ else if (rvb.VolLeft || rvb.VolRight)\r
+ {\r
+  if (unlikely(rvb.dirty))\r
+   prepare_offsets();\r
+\r
+  MixREVERB_off();\r
+ }\r
  else                                                  // -> reverb off\r
  {\r
-  // supposedly runs anyway?\r
+  // reverb runs anyway\r
   rvb.CurrAddr += NSSIZE/2;\r
   while (rvb.CurrAddr >= 0x40000)\r
    rvb.CurrAddr -= 0x40000 - rvb.StartAddr;\r
 
 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
 }
 #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...
 
        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 && (spuCtrl&CTRL_IRQ) && pSpuIrq && pSpuIrq<spuMemC+0x1000)                 
+  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)))
      {
-        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();
 
    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;