move blit to core, allow filtering while blitting
[pcsx_rearmed.git] / plugins / dfsound / spu.c
index d5a8215..45a7886 100644 (file)
@@ -24,7 +24,7 @@
 
 #include "externals.h"
 #include "registers.h"
-#include "dsoundoss.h"
+#include "out.h"
 
 #ifdef ENABLE_NLS
 #include <libintl.h>
@@ -75,7 +75,6 @@ unsigned short  spuMem[256*1024];
 unsigned char * spuMemC;
 unsigned char * pSpuIrq=0;
 unsigned char * pSpuBuffer;
-unsigned char * pMixIrq=0;
 
 // user settings
 
@@ -120,6 +119,8 @@ int iFMod[NSSIZE];
 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
@@ -242,7 +243,7 @@ INLINE void InterpolateDown(int ch)
 // 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"
 
@@ -295,8 +296,6 @@ INLINE void StartSound(int ch)
       {s_chan[ch].spos=0x30000L;s_chan[ch].SB[28]=0;}  // -> start with more decoding
  else {s_chan[ch].spos=0x10000L;s_chan[ch].SB[31]=0;}  // -> no/simple interpolation starts with one 44100 decoding
 
- check_irq(ch, s_chan[ch].pCurr);                      // just in case
-
  dwNewChannel&=~(1<<ch);                               // clear new channel bit
 }
 
@@ -387,7 +386,7 @@ INLINE int iGetInterpolationVal(int ch, int spos)
      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;
@@ -459,6 +458,8 @@ static int decode_block(int ch)
 
   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;
@@ -472,14 +473,14 @@ static int decode_block(int ch)
 
  start+=16;
 
- if(flags&1)                               // 1: stop/loop
+ if(flags&1) {                             // 1: stop/loop
   start = s_chan[ch].pLoop;
+  ret |= check_irq(ch, start);             // hack.. :(
+ }
 
  if (start - spuMemC >= 0x80000)
   start = spuMemC;
 
- ret = check_irq(ch, start);
-
  s_chan[ch].pCurr = start;                 // store values for next cycle
  s_chan[ch].prevflags = flags;
 
@@ -491,6 +492,7 @@ static int skip_block(int ch)
 {
  unsigned char *start = s_chan[ch].pCurr;
  int flags = start[1];
+ int ret = check_irq(ch, start);
 
  if(s_chan[ch].prevflags & 1)
   start = s_chan[ch].pLoop;
@@ -506,7 +508,7 @@ static int skip_block(int ch)
  s_chan[ch].pCurr = start;
  s_chan[ch].prevflags = flags;
 
- return check_irq(ch, start);
+ return ret;
 }
 
 #define make_do_samples(name, fmod_code, interp_start, interp1_code, interp2_code, interp_end) \
@@ -531,10 +533,7 @@ static int do_samples_##name(int ch, int ns, int ns_to) \
     sbpos = 0;                               \
     d = decode_block(ch);                    \
     if(d)                                    \
-    {                                        \
-     ret = ns;                               \
-     goto out;                               \
-    }                                        \
+     ret = ns_to = ns + 1;                   \
    }                                         \
                                              \
    fa = SB[sbpos++];                         \
@@ -546,7 +545,6 @@ static int do_samples_##name(int ch, int ns, int ns_to) \
   spos += sinc;                              \
  }                                           \
                                              \
-out:                                         \
  s_chan[ch].sinc = sinc;                     \
  s_chan[ch].spos = spos;                     \
  s_chan[ch].iSBPos = sbpos;                  \
@@ -583,11 +581,14 @@ make_do_samples(simple, , ,
 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;
  }
 
@@ -611,7 +612,7 @@ static int do_samples_noise(int ch, int ns, int ns_to)
   ChanBuf[ns] = (signed short)dwNoiseVal;
  }
 
- return -1;
+ return ret;
 }
 
 #ifdef __arm__
@@ -657,6 +658,23 @@ static void mix_chan_rvb(int start, int count, int lv, int rv)
 }
 #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...
@@ -679,7 +697,7 @@ static int do_samples(int forced_updates)
    // 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;
     }
@@ -722,12 +740,16 @@ static int do_samples(int forced_updates)
          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)
@@ -769,6 +791,16 @@ static int do_samples(int forced_updates)
     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
@@ -822,18 +854,17 @@ static int do_samples(int forced_updates)
   // 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();
 
@@ -841,7 +872,7 @@ static int do_samples(int forced_updates)
   // 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;
@@ -966,8 +997,6 @@ void SetupStreams(void)
    s_chan[i].pCurr=spuMemC;
   }
 
- pMixIrq=spuMemC;                                      // enable decoded buffer irqs by setting the address
-
  ClearWorkingState();
 
  bSpuInit=1;                                           // flag: we are inited
@@ -998,7 +1027,7 @@ long CALLBACK SPUinit(void)
  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;
@@ -1027,7 +1056,7 @@ long CALLBACK SPUclose(void)
 
  bSPUIsOpen = 0;                                       // no more open
 
RemoveSound();                                        // no more sound handling
out_current->finish();                                // no more sound handling
 
  return 0;
 }