From: notaz Date: Sat, 7 Oct 2023 19:56:09 +0000 (+0300) Subject: spu: try to clean up the interpolation mess X-Git-Tag: r24~128 X-Git-Url: https://notaz.gp2x.de/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=38e4048faeaccf7fdc6084f64866f2ea52bb97f1;p=pcsx_rearmed.git spu: try to clean up the interpolation mess It's still full of magic offsets and constants, but maybe not as much as before. --- diff --git a/libpcsxcore/plugins.h b/libpcsxcore/plugins.h index fbbd44f1..cb9b88a2 100644 --- a/libpcsxcore/plugins.h +++ b/libpcsxcore/plugins.h @@ -184,7 +184,7 @@ typedef struct { unsigned char SPUPorts[0x200]; unsigned char SPURam[0x80000]; xa_decode_t xa; - unsigned char *SPUInfo; + unsigned char *unused; } SPUFreeze_t; typedef long (CALLBACK* SPUfreeze)(uint32_t, SPUFreeze_t *, uint32_t); typedef void (CALLBACK* SPUasync)(uint32_t, uint32_t); diff --git a/plugins/dfsound/externals.h b/plugins/dfsound/externals.h index 4407177a..9fb58ad2 100644 --- a/plugins/dfsound/externals.h +++ b/plugins/dfsound/externals.h @@ -117,7 +117,6 @@ typedef struct unsigned int bFMod:2; // freq mod (0=off, 1=sound channel, 2=freq channel) unsigned int prevflags:3; // flags from previous block unsigned int bIgnoreLoop:1; // Ignore loop - unsigned int bNewPitch:1; // pitch changed unsigned int bStarting:1; // starting after keyon union { struct { @@ -183,7 +182,21 @@ typedef struct // psx buffers / addresses -#define SB_SIZE (32 + 4) +typedef union +{ + int SB[28 + 4 + 4]; + struct { + int sample[28]; + union { + struct { + int pos; + signed short val[4]; + } gauss; + int simple[5]; // 28-32 + } interp; + int sinc_old; + }; +} sample_buf; typedef struct { @@ -239,10 +252,13 @@ typedef struct REVERBInfo * rvb; // buffers - int * SB; + void * unused; int * SSumLR; unsigned short regArea[0x400]; + + sample_buf sb[MAXCHAN]; + int interpolation; } SPUInfo; #define regAreaGet(offset) \ diff --git a/plugins/dfsound/freeze.c b/plugins/dfsound/freeze.c index d4898b46..c4afad83 100644 --- a/plugins/dfsound/freeze.c +++ b/plugins/dfsound/freeze.c @@ -15,6 +15,7 @@ * * ***************************************************************************/ +#include #include "stdafx.h" #define _IN_FREEZE @@ -145,7 +146,8 @@ static void save_channel(SPUCHAN_orig *d, const SPUCHAN *s, int ch) d->iSBPos = s->iSBPos; d->spos = s->spos; d->sinc = s->sinc; - memcpy(d->SB, spu.SB + ch * SB_SIZE, sizeof(d->SB[0]) * SB_SIZE); + assert(sizeof(d->SB) >= sizeof(spu.sb[ch])); + memcpy(d->SB, &spu.sb[ch], sizeof(spu.sb[ch])); d->iStart = (regAreaGetCh(ch, 6) & ~1) << 3; d->iCurr = 0; // set by the caller d->iLoop = 0; // set by the caller @@ -159,8 +161,8 @@ static void save_channel(SPUCHAN_orig *d, const SPUCHAN *s, int ch) d->bIgnoreLoop = (s->prevflags ^ 2) << 1; d->iRightVolume = s->iRightVolume; d->iRawPitch = s->iRawPitch; - d->s_1 = spu.SB[ch * SB_SIZE + 27]; // yes it's reversed - d->s_2 = spu.SB[ch * SB_SIZE + 26]; + d->s_1 = spu.sb[ch].SB[27]; // yes it's reversed + d->s_2 = spu.sb[ch].SB[26]; d->bRVBActive = s->bRVBActive; d->bNoise = s->bNoise; d->bFMod = s->bFMod; @@ -187,7 +189,7 @@ static void load_channel(SPUCHAN *d, const SPUCHAN_orig *s, int ch) d->spos = s->spos; d->sinc = s->sinc; d->sinc_inv = 0; - memcpy(spu.SB + ch * SB_SIZE, s->SB, sizeof(spu.SB[0]) * SB_SIZE); + memcpy(&spu.sb[ch], s->SB, sizeof(spu.sb[ch])); d->pCurr = (void *)((uintptr_t)s->iCurr & 0x7fff0); d->pLoop = (void *)((uintptr_t)s->iLoop & 0x7fff0); d->bReverb = s->bReverb; @@ -340,7 +342,7 @@ long CALLBACK SPUfreeze(uint32_t ulFreezeMode, SPUFreeze_t * pF, load_register(H_CDRight, cycles); // fix to prevent new interpolations from crashing - for(i=0;iSB; + 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);