spu: try to clean up the interpolation mess
[pcsx_rearmed.git] / plugins / dfsound / spu.c
index 04997ba..9e1e83f 100644 (file)
@@ -126,8 +126,14 @@ int ChanBuf[NSSIZE];
 //          /
 //
 
-static void InterpolateUp(int *SB, int sinc)
+static void InterpolateUp(sample_buf *sb, int sinc)
 {
+ int *SB = sb->SB;
+ if (sb->sinc_old != sinc)
+ {
+  sb->sinc_old = sinc;
+  SB[32] = 1;
+ }
  if(SB[32]==1)                                         // flag == 1? calc step and set flag... and don't change the value in this pass
   {
    const int id1=SB[30]-SB[29];                        // curr delta to next val
@@ -175,8 +181,9 @@ static void InterpolateUp(int *SB, int sinc)
 // even easier interpolation on downsampling, also no special filter, again just "Pete's common sense" tm
 //
 
-static void InterpolateDown(int *SB, int sinc)
+static void InterpolateDown(sample_buf *sb, int sinc)
 {
+ int *SB = sb->SB;
  if(sinc>=0x20000L)                                 // we would skip at least one val?
   {
    SB[29]+=(SB[30]-SB[29])/2;                                  // add easy weight
@@ -186,15 +193,8 @@ static void InterpolateDown(int *SB, int sinc)
 }
 
 ////////////////////////////////////////////////////////////////////////
-// helpers for gauss interpolation
-
-#define gval0 (((short*)(&SB[29]))[gpos&3])
-#define gval(x) ((int)((short*)(&SB[29]))[(gpos+x)&3])
 
 #include "gauss_i.h"
-
-////////////////////////////////////////////////////////////////////////
-
 #include "xa.c"
 
 static void do_irq(void)
@@ -232,15 +232,17 @@ void check_irq_io(unsigned int addr)
 // START SOUND... called by main thread to setup a new sound on a channel
 ////////////////////////////////////////////////////////////////////////
 
-static void StartSoundSB(int *SB)
+static void ResetInterpolation(sample_buf *sb)
 {
- SB[26]=0;                                             // init mixing vars
- SB[27]=0;
+ memset(&sb->interp, 0, sizeof(sb->interp));
+ sb->sinc_old = -1;
+}
 
- SB[28]=0;
- SB[29]=0;                                             // init our interpolation helpers
- SB[30]=0;
- SB[31]=0;
+static void StartSoundSB(sample_buf *sb)
+{
+ sb->SB[26] = 0;                                       // init mixing vars
+ sb->SB[27] = 0;
+ ResetInterpolation(sb);
 }
 
 static void StartSoundMain(int ch)
@@ -265,14 +267,14 @@ static void StartSoundMain(int ch)
 static void StartSound(int ch)
 {
  StartSoundMain(ch);
- StartSoundSB(spu.SB + ch * SB_SIZE);
+ StartSoundSB(&spu.sb[ch]);
 }
 
 ////////////////////////////////////////////////////////////////////////
 // ALL KIND OF HELPERS
 ////////////////////////////////////////////////////////////////////////
 
-INLINE int FModChangeFrequency(int *SB, int pitch, int ns)
+INLINE int FModChangeFrequency(int pitch, int ns)
 {
  pitch = (signed short)pitch;
  pitch = ((32768 + iFMod[ns]) * pitch) >> 15;
@@ -281,101 +283,50 @@ INLINE int FModChangeFrequency(int *SB, int pitch, int ns)
   pitch = 0x3fff;
 
  iFMod[ns] = 0;
- SB[32] = 1;                                           // reset interpolation
 
  return pitch << 4;
 }                    
 
-////////////////////////////////////////////////////////////////////////
-
-INLINE void StoreInterpolationVal(int *SB, int sinc, int fa, int fmod_freq)
+INLINE void StoreInterpolationGaussCubic(sample_buf *sb, int fa)
 {
- if(fmod_freq)                                         // fmod freq channel
-  SB[29]=fa;
- else
-  {
-   ssat32_to_16(fa);
-
-   if(spu_config.iUseInterpolation>=2)                 // gauss/cubic interpolation
-    {
-     int gpos = SB[28];
-     gval0 = fa;
-     gpos = (gpos+1) & 3;
-     SB[28] = gpos;
-    }
-   else
-   if(spu_config.iUseInterpolation==1)                 // simple interpolation
-    {
-     SB[28] = 0;
-     SB[29] = SB[30];                                  // -> helpers for simple linear interpolation: delay real val for two slots, and calc the two deltas, for a 'look at the future behaviour'
-     SB[30] = SB[31];
-     SB[31] = fa;
-     SB[32] = 1;                                       // -> flag: calc new interolation
-    }
-   else SB[29]=fa;                                     // no interpolation
-  }
+ int gpos = sb->interp.gauss.pos & 3;
+ sb->interp.gauss.val[gpos++] = fa;
+ sb->interp.gauss.pos = gpos & 3;
 }
 
-////////////////////////////////////////////////////////////////////////
+#define gval(x) (int)sb->interp.gauss.val[(gpos + x) & 3]
 
-INLINE int iGetInterpolationVal(int *SB, int sinc, int spos, int fmod_freq)
+INLINE int GetInterpolationCubic(const sample_buf *sb, int spos)
 {
+ int gpos = sb->interp.gauss.pos;
+ int xd = (spos >> 1) + 1;
  int fa;
 
- if(fmod_freq) return SB[29];
-
- switch(spu_config.iUseInterpolation)
-  {
-   //--------------------------------------------------//
-   case 3:                                             // cubic interpolation
-    {
-     long xd;int gpos;
-     xd = (spos >> 1)+1;
-     gpos = SB[28];
-
-     fa  = gval(3) - 3*gval(2) + 3*gval(1) - gval0;
-     fa *= (xd - (2<<15)) / 6;
-     fa >>= 15;
-     fa += gval(2) - gval(1) - gval(1) + gval0;
-     fa *= (xd - (1<<15)) >> 1;
-     fa >>= 15;
-     fa += gval(1) - gval0;
-     fa *= xd;
-     fa >>= 15;
-     fa = fa + gval0;
-
-    } break;
-   //--------------------------------------------------//
-   case 2:                                             // gauss interpolation
-    {
-     int vl, vr;int gpos;
-     vl = (spos >> 6) & ~3;
-     gpos = SB[28];
-     vr=(gauss[vl]*(int)gval0) >> 15;
-     vr+=(gauss[vl+1]*gval(1)) >> 15;
-     vr+=(gauss[vl+2]*gval(2)) >> 15;
-     vr+=(gauss[vl+3]*gval(3)) >> 15;
-     fa = vr;
-    } break;
-   //--------------------------------------------------//
-   case 1:                                             // simple interpolation
-    {
-     if(sinc<0x10000L)                                 // -> upsampling?
-          InterpolateUp(SB, sinc);                     // --> interpolate up
-     else InterpolateDown(SB, sinc);                   // --> else down
-     fa=SB[29];
-    } break;
-   //--------------------------------------------------//
-   default:                                            // no interpolation
-    {
-     fa=SB[29];
-    } break;
-   //--------------------------------------------------//
-  }
-
+ fa  = gval(3) - 3*gval(2) + 3*gval(1) - gval(0);
+ fa *= (xd - (2<<15)) / 6;
+ fa >>= 15;
+ fa += gval(2) - gval(1) - gval(1) + gval(0);
+ fa *= (xd - (1<<15)) >> 1;
+ fa >>= 15;
+ fa += gval(1) - gval(0);
+ fa *= xd;
+ fa >>= 15;
+ fa = fa + gval(0);
  return fa;
 }
 
+INLINE int GetInterpolationGauss(const sample_buf *sb, int spos)
+{
+ int gpos = sb->interp.gauss.pos;
+ int vl = (spos >> 6) & ~3;
+ int vr;
+ vr  = (gauss[vl+0] * gval(0)) >> 15;
+ vr += (gauss[vl+1] * gval(1)) >> 15;
+ vr += (gauss[vl+2] * gval(2)) >> 15;
+ vr += (gauss[vl+3] * gval(3)) >> 15;
+ return vr;
+}
+
 static void decode_block_data(int *dest, const unsigned char *src, int predict_nr, int shift_factor)
 {
  static const int f[16][2] = {
@@ -527,10 +478,10 @@ static void scan_for_irq(int ch, unsigned int *upd_samples)
  }
 }
 
-#define make_do_samples(name, fmod_code, interp_start, interp1_code, interp2_code, interp_end) \
-static noinline int do_samples_##name( \
+#define make_do_samples(name, fmod_code, interp_start, interp_store, interp_get, interp_end) \
+static noinline int name( \
  int (*decode_f)(void *context, int ch, int *SB), void *ctx, \
- int ch, int ns_to, int *SB, int sinc, int *spos, int *sbpos) \
+ int ch, int ns_to, sample_buf *sb, int sinc, int *spos, int *sbpos) \
 {                                            \
  int ns, d, fa;                              \
  int ret = ns_to;                            \
@@ -543,20 +494,20 @@ static noinline int do_samples_##name( \
   *spos += sinc;                             \
   while (*spos >= 0x10000)                   \
   {                                          \
-   fa = SB[(*sbpos)++];                      \
+   fa = sb->SB[(*sbpos)++];                  \
    if (*sbpos >= 28)                         \
    {                                         \
     *sbpos = 0;                              \
-    d = decode_f(ctx, ch, SB);               \
+    d = decode_f(ctx, ch, sb->SB);           \
     if (d && ns < ret)                       \
      ret = ns;                               \
    }                                         \
                                              \
-   interp1_code;                             \
+   interp_store;                             \
    *spos -= 0x10000;                         \
   }                                          \
                                              \
-  interp2_code;                              \
+  interp_get;                                \
  }                                           \
                                              \
  interp_end;                                 \
@@ -564,30 +515,56 @@ static noinline int do_samples_##name( \
  return ret;                                 \
 }
 
-#define fmod_recv_check \
-  if(spu.s_chan[ch].bFMod==1 && iFMod[ns]) \
-    sinc = FModChangeFrequency(SB, spu.s_chan[ch].iRawPitch, ns)
-
-make_do_samples(default, fmod_recv_check, ,
-  StoreInterpolationVal(SB, sinc, fa, spu.s_chan[ch].bFMod==2),
-  ChanBuf[ns] = iGetInterpolationVal(SB, sinc, *spos, spu.s_chan[ch].bFMod==2), )
-make_do_samples(noint, , fa = SB[29], , ChanBuf[ns] = fa, SB[29] = fa)
-
+// helpers for simple linear interpolation: delay real val for two slots,
+// and calc the two deltas, for a 'look at the future behaviour'
 #define simple_interp_store \
-  SB[28] = 0; \
-  SB[29] = SB[30]; \
-  SB[30] = SB[31]; \
-  SB[31] = fa; \
-  SB[32] = 1
+  sb->SB[28] = 0; \
+  sb->SB[29] = sb->SB[30]; \
+  sb->SB[30] = sb->SB[31]; \
+  sb->SB[31] = fa; \
+  sb->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]
+       InterpolateUp(sb, sinc);   /* --> interpolate up */ \
+  else InterpolateDown(sb, sinc); /* --> else down */ \
+  ChanBuf[ns] = sb->SB[29]
 
-make_do_samples(simple, , ,
+make_do_samples(do_samples_nointerp, , fa = sb->SB[29],
+   , ChanBuf[ns] = fa, sb->SB[29] = fa)
+make_do_samples(do_samples_simple, , ,
   simple_interp_store, simple_interp_get, )
+make_do_samples(do_samples_gauss, , ,
+  StoreInterpolationGaussCubic(sb, fa),
+  ChanBuf[ns] = GetInterpolationGauss(sb, *spos), )
+make_do_samples(do_samples_cubic, , ,
+  StoreInterpolationGaussCubic(sb, fa),
+  ChanBuf[ns] = GetInterpolationCubic(sb, *spos), )
+make_do_samples(do_samples_fmod,
+  sinc = FModChangeFrequency(spu.s_chan[ch].iRawPitch, ns), ,
+  StoreInterpolationGaussCubic(sb, fa),
+  ChanBuf[ns] = GetInterpolationGauss(sb, *spos), )
+
+INLINE int do_samples_adpcm(
+ int (*decode_f)(void *context, int ch, int *SB), void *ctx,
+ int ch, int ns_to, int fmod, sample_buf *sb, int sinc, int *spos, int *sbpos)
+{
+ int interp = spu.interpolation;
+ if (fmod == 1)
+  return do_samples_fmod(decode_f, ctx, ch, ns_to, sb, sinc, spos, sbpos);
+ if (fmod)
+  interp = 2;
+ switch (interp) {
+  case 0:
+   return do_samples_nointerp(decode_f, ctx, ch, ns_to, sb, sinc, spos, sbpos);
+  case 1:
+   return do_samples_simple  (decode_f, ctx, ch, ns_to, sb, sinc, spos, sbpos);
+  default:
+   return do_samples_gauss   (decode_f, ctx, ch, ns_to, sb, sinc, spos, sbpos);
+  case 3:
+   return do_samples_cubic   (decode_f, ctx, ch, ns_to, sb, sinc, spos, sbpos);
+ }
+}
 
 static int do_samples_skip(int ch, int ns_to)
 {
@@ -762,7 +739,15 @@ static void do_channels(int ns_to)
  unsigned int mask;
  int do_rvb, ch, d;
  SPUCHAN *s_chan;
- int *SB, sinc;
+
+ if (unlikely(spu.interpolation != spu_config.iUseInterpolation))
+ {
+  spu.interpolation = spu_config.iUseInterpolation;
+  mask = spu.dwChannelsAudible & 0xffffff;
+  for (ch = 0; mask != 0; ch++, mask >>= 1)
+   if (mask & 1)
+    ResetInterpolation(&spu.sb[ch]);
+ }
 
  do_rvb = spu.rvb->StartAddr && spu_config.iUseReverb;
  if (do_rvb)
@@ -780,24 +765,11 @@ static void do_channels(int ns_to)
    if (!(mask & 1)) continue;                      // channel not playing? next
 
    s_chan = &spu.s_chan[ch];
-   SB = spu.SB + ch * SB_SIZE;
-   sinc = s_chan->sinc;
-   if (spu.s_chan[ch].bNewPitch)
-    SB[32] = 1;                                    // reset interpolation
-   spu.s_chan[ch].bNewPitch = 0;
-
    if (s_chan->bNoise)
     d = do_samples_noise(ch, ns_to);
-   else if (s_chan->bFMod == 2
-         || (s_chan->bFMod == 0 && spu_config.iUseInterpolation == 0))
-    d = do_samples_noint(decode_block, NULL, ch, ns_to,
-          SB, sinc, &s_chan->spos, &s_chan->iSBPos);
-   else if (s_chan->bFMod == 0 && spu_config.iUseInterpolation == 1)
-    d = do_samples_simple(decode_block, NULL, ch, ns_to,
-          SB, sinc, &s_chan->spos, &s_chan->iSBPos);
    else
-    d = do_samples_default(decode_block, NULL, ch, ns_to,
-          SB, sinc, &s_chan->spos, &s_chan->iSBPos);
+    d = do_samples_adpcm(decode_block, NULL, ch, ns_to, s_chan->bFMod,
+          &spu.sb[ch], s_chan->sinc, &s_chan->spos, &s_chan->iSBPos);
 
    if (!s_chan->bStarting) {
     d = MixADSR(&s_chan->ADSRX, d);
@@ -883,7 +855,6 @@ static struct spu_worker {
    unsigned short bNoise:1;
    unsigned short bFMod:2;
    unsigned short bRVBActive:1;
-   unsigned short bNewPitch:1;
    ADSRInfoEx adsr;
   } ch[24];
   int SSumLR[NSSIZE * 2];
@@ -965,7 +936,6 @@ static void queue_channel_work(int ns_to, unsigned int silentch)
    work->ch[ch].bNoise = s_chan->bNoise;
    work->ch[ch].bFMod = s_chan->bFMod;
    work->ch[ch].bRVBActive = s_chan->bRVBActive;
-   work->ch[ch].bNewPitch = s_chan->bNewPitch;
    if (s_chan->prevflags & 1)
     work->ch[ch].start = work->ch[ch].loop;
 
@@ -981,7 +951,6 @@ static void queue_channel_work(int ns_to, unsigned int silentch)
      s_chan->ADSRX.EnvelopeVol = 0;
     }
    }
-   s_chan->bNewPitch = 0;
   }
 
  work->rvb_addr = 0;
@@ -1001,18 +970,27 @@ static void queue_channel_work(int ns_to, unsigned int silentch)
 static void do_channel_work(struct work_item *work)
 {
  unsigned int mask;
- int *SB, sinc, spos, sbpos;
+ int spos, sbpos;
  int d, ch, ns_to;
 
  ns_to = work->ns_to;
 
+ if (unlikely(spu.interpolation != spu_config.iUseInterpolation))
+ {
+  spu.interpolation = spu_config.iUseInterpolation;
+  mask = spu.dwChannelsAudible & 0xffffff;
+  for (ch = 0; mask != 0; ch++, mask >>= 1)
+   if (mask & 1)
+    ResetInterpolation(&spu.sb[ch]);
+ }
+
  if (work->rvb_addr)
   memset(RVB, 0, ns_to * sizeof(RVB[0]) * 2);
 
  mask = work->channels_new;
  for (ch = 0; mask != 0; ch++, mask >>= 1) {
   if (mask & 1)
-   StartSoundSB(spu.SB + ch * SB_SIZE);
+   StartSoundSB(&spu.sb[ch]);
  }
 
  mask = work->channels_on;
@@ -1023,21 +1001,12 @@ static void do_channel_work(struct work_item *work)
    d = work->ch[ch].ns_to;
    spos = work->ch[ch].spos;
    sbpos = work->ch[ch].sbpos;
-   sinc = work->ch[ch].sinc;
-
-   SB = spu.SB + ch * SB_SIZE;
-   if (work->ch[ch].bNewPitch)
-    SB[32] = 1; // reset interpolation
 
    if (work->ch[ch].bNoise)
     do_lsfr_samples(d, work->ctrl, &spu.dwNoiseCount, &spu.dwNoiseVal);
-   else if (work->ch[ch].bFMod == 2
-         || (work->ch[ch].bFMod == 0 && spu_config.iUseInterpolation == 0))
-    do_samples_noint(decode_block_work, work, ch, d, SB, sinc, &spos, &sbpos);
-   else if (work->ch[ch].bFMod == 0 && spu_config.iUseInterpolation == 1)
-    do_samples_simple(decode_block_work, work, ch, d, SB, sinc, &spos, &sbpos);
    else
-    do_samples_default(decode_block_work, work, ch, d, SB, sinc, &spos, &sbpos);
+    do_samples_adpcm(decode_block_work, work, ch, d, work->ch[ch].bFMod,
+          &spu.sb[ch], work->ch[ch].sinc, &spos, &sbpos);
 
    d = MixADSR(&work->ch[ch].adsr, d);
    if (d < ns_to) {
@@ -1509,7 +1478,6 @@ long CALLBACK SPUinit(void)
 
  spu.s_chan = calloc(MAXCHAN+1, sizeof(spu.s_chan[0])); // channel + 1 infos (1 is security for fmod handling)
  spu.rvb = calloc(1, sizeof(REVERBInfo));
- spu.SB = calloc(MAXCHAN, sizeof(spu.SB[0]) * SB_SIZE);
 
  spu.spuAddr = 0;
  spu.decode_pos = 0;
@@ -1569,8 +1537,6 @@ long CALLBACK SPUshutdown(void)
 
  free(spu.spuMemC);
  spu.spuMemC = NULL;
- free(spu.SB);
- spu.SB = NULL;
  free(spu.s_chan);
  spu.s_chan = NULL;
  free(spu.rvb);