X-Git-Url: https://notaz.gp2x.de/cgi-bin/gitweb.cgi?a=blobdiff_plain;f=plugins%2Fdfsound%2Fspu.c;h=0e4b154e66b331d53b7753e5c78c2255ff732f1d;hb=42dde520a83b9c391b41b0eceecb1ce3eaed7e72;hp=9e1e83f20983091873638fa354ad548469b42383;hpb=38e4048faeaccf7fdc6084f64866f2ea52bb97f1;p=pcsx_rearmed.git diff --git a/plugins/dfsound/spu.c b/plugins/dfsound/spu.c index 9e1e83f2..0e4b154e 100644 --- a/plugins/dfsound/spu.c +++ b/plugins/dfsound/spu.c @@ -18,6 +18,7 @@ * * ***************************************************************************/ +#include #include "stdafx.h" #define _IN_SPU @@ -26,6 +27,7 @@ #include "registers.h" #include "out.h" #include "spu_config.h" +#include "spu.h" #ifdef __arm__ #include "arm_features.h" @@ -197,12 +199,15 @@ static void InterpolateDown(sample_buf *sb, int sinc) #include "gauss_i.h" #include "xa.c" -static void do_irq(void) +static void do_irq(int cycles_after) { - //if(!(spu.spuStat & STAT_IRQ)) + if (spu.spuStat & STAT_IRQ) + log_unhandled("spu: missed irq?\n"); + else { spu.spuStat |= STAT_IRQ; // asserted status? - if(spu.irqCallback) spu.irqCallback(); + if (spu.irqCallback) + spu.irqCallback(cycles_after); } } @@ -210,8 +215,8 @@ static int check_irq(int ch, unsigned char *pos) { if((spu.spuCtrl & (CTRL_ON|CTRL_IRQ)) == (CTRL_ON|CTRL_IRQ) && pos == spu.pSpuIrq) { - //printf("ch%d irq %04x\n", ch, pos - spu.spuMemC); - do_irq(); + //printf("ch%d irq %04zx\n", ch, pos - spu.spuMemC); + do_irq(0); return 1; } return 0; @@ -224,7 +229,15 @@ void check_irq_io(unsigned int addr) if((spu.spuCtrl & (CTRL_ON|CTRL_IRQ)) == (CTRL_ON|CTRL_IRQ) && addr == irq_addr) { //printf("io irq %04x\n", irq_addr); - do_irq(); + do_irq(0); + } +} + +void do_irq_io(int cycles_after) +{ + if ((spu.spuCtrl & (CTRL_ON|CTRL_IRQ)) == (CTRL_ON|CTRL_IRQ)) + { + do_irq(cycles_after); } } @@ -274,15 +287,15 @@ static void StartSound(int ch) // ALL KIND OF HELPERS //////////////////////////////////////////////////////////////////////// -INLINE int FModChangeFrequency(int pitch, int ns) +INLINE int FModChangeFrequency(int pitch, int ns, int *fmod_buf) { pitch = (signed short)pitch; - pitch = ((32768 + iFMod[ns]) * pitch) >> 15; + pitch = ((32768 + fmod_buf[ns]) * pitch) >> 15; pitch &= 0xffff; if (pitch > 0x3fff) pitch = 0x3fff; - iFMod[ns] = 0; + fmod_buf[ns] = 0; return pitch << 4; } @@ -479,7 +492,7 @@ static void scan_for_irq(int ch, unsigned int *upd_samples) } #define make_do_samples(name, fmod_code, interp_start, interp_store, interp_get, interp_end) \ -static noinline int name( \ +static noinline int name(int *dst, \ int (*decode_f)(void *context, int ch, int *SB), void *ctx, \ int ch, int ns_to, sample_buf *sb, int sinc, int *spos, int *sbpos) \ { \ @@ -528,41 +541,41 @@ static noinline int name( \ if(sinc<0x10000) /* -> upsampling? */ \ InterpolateUp(sb, sinc); /* --> interpolate up */ \ else InterpolateDown(sb, sinc); /* --> else down */ \ - ChanBuf[ns] = sb->SB[29] + dst[ns] = sb->SB[29] make_do_samples(do_samples_nointerp, , fa = sb->SB[29], - , ChanBuf[ns] = fa, sb->SB[29] = fa) + , dst[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), ) + dst[ns] = GetInterpolationGauss(sb, *spos), ) make_do_samples(do_samples_cubic, , , StoreInterpolationGaussCubic(sb, fa), - ChanBuf[ns] = GetInterpolationCubic(sb, *spos), ) + dst[ns] = GetInterpolationCubic(sb, *spos), ) make_do_samples(do_samples_fmod, - sinc = FModChangeFrequency(spu.s_chan[ch].iRawPitch, ns), , + sinc = FModChangeFrequency(spu.s_chan[ch].iRawPitch, ns, iFMod), , StoreInterpolationGaussCubic(sb, fa), - ChanBuf[ns] = GetInterpolationGauss(sb, *spos), ) + dst[ns] = GetInterpolationGauss(sb, *spos), ) -INLINE int do_samples_adpcm( +INLINE int do_samples_adpcm(int *dst, 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); + return do_samples_fmod(dst, 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); + return do_samples_nointerp(dst, 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); + return do_samples_simple (dst, 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); + return do_samples_gauss (dst, 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); + return do_samples_cubic (dst, decode_f, ctx, ch, ns_to, sb, sinc, spos, sbpos); } } @@ -593,7 +606,33 @@ static int do_samples_skip(int ch, int ns_to) return ret; } -static void do_lsfr_samples(int ns_to, int ctrl, +static int do_samples_skip_fmod(int ch, int ns_to, int *fmod_buf) +{ + SPUCHAN *s_chan = &spu.s_chan[ch]; + int spos = s_chan->spos; + int ret = ns_to, ns, d; + + spos += s_chan->iSBPos << 16; + + for (ns = 0; ns < ns_to; ns++) + { + spos += FModChangeFrequency(s_chan->iRawPitch, ns, fmod_buf); + while (spos >= 28*0x10000) + { + d = skip_block(ch); + if (d && ns < ret) + ret = ns; + spos -= 28*0x10000; + } + } + + s_chan->iSBPos = spos >> 16; + s_chan->spos = spos & 0xffff; + + return ret; +} + +static void do_lsfr_samples(int *dst, int ns_to, int ctrl, unsigned int *dwNoiseCount, unsigned int *dwNoiseVal) { unsigned int counter = *dwNoiseCount; @@ -617,20 +656,20 @@ static void do_lsfr_samples(int ns_to, int ctrl, val = (val << 1) | bit; } - ChanBuf[ns] = (signed short)val; + dst[ns] = (signed short)val; } *dwNoiseCount = counter; *dwNoiseVal = val; } -static int do_samples_noise(int ch, int ns_to) +static int do_samples_noise(int *dst, int ch, int ns_to) { int ret; ret = do_samples_skip(ch, ns_to); - do_lsfr_samples(ns_to, spu.spuCtrl, &spu.dwNoiseCount, &spu.dwNoiseVal); + do_lsfr_samples(dst, ns_to, spu.spuCtrl, &spu.dwNoiseCount, &spu.dwNoiseVal); return ret; } @@ -766,13 +805,13 @@ static void do_channels(int ns_to) s_chan = &spu.s_chan[ch]; if (s_chan->bNoise) - d = do_samples_noise(ch, ns_to); + d = do_samples_noise(ChanBuf, ch, ns_to); else - d = do_samples_adpcm(decode_block, NULL, ch, ns_to, s_chan->bFMod, + d = do_samples_adpcm(ChanBuf, 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); + d = MixADSR(ChanBuf, &s_chan->ADSRX, d); if (d < ns_to) { spu.dwChannelsAudible &= ~(1 << ch); s_chan->ADSRX.State = ADSR_RELEASE; @@ -789,13 +828,15 @@ static void do_channels(int ns_to) if (s_chan->bFMod == 2) // fmod freq channel memcpy(iFMod, &ChanBuf, ns_to * sizeof(iFMod[0])); - if (s_chan->bRVBActive && do_rvb) + if (!(spu.spuCtrl & CTRL_MUTE)) + ; + else if (s_chan->bRVBActive && do_rvb) mix_chan_rvb(spu.SSumLR, ns_to, s_chan->iLeftVolume, s_chan->iRightVolume, RVB); else mix_chan(spu.SSumLR, ns_to, s_chan->iLeftVolume, s_chan->iRightVolume); } - MixXA(spu.SSumLR, RVB, ns_to, spu.decode_pos); + MixCD(spu.SSumLR, RVB, ns_to, spu.decode_pos); if (spu.rvb->StartAddr) { if (do_rvb) @@ -855,6 +896,7 @@ static struct spu_worker { unsigned short bNoise:1; unsigned short bFMod:2; unsigned short bRVBActive:1; + unsigned short bStarting:1; ADSRInfoEx adsr; } ch[24]; int SSumLR[NSSIZE * 2]; @@ -900,6 +942,7 @@ static int decode_block_work(void *context, int ch, int *SB) static void queue_channel_work(int ns_to, unsigned int silentch) { + int tmpFMod[NSSIZE]; struct work_item *work; SPUCHAN *s_chan; unsigned int mask; @@ -914,7 +957,7 @@ static void queue_channel_work(int ns_to, unsigned int silentch) mask = work->channels_new = spu.dwNewChannel & 0xffffff; for (ch = 0; mask != 0; ch++, mask >>= 1) { if (mask & 1) - StartSoundMain(ch); + StartSound(ch); } mask = work->channels_on = spu.dwChannelsAudible & 0xffffff; @@ -936,10 +979,32 @@ 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].bStarting = s_chan->bStarting; if (s_chan->prevflags & 1) work->ch[ch].start = work->ch[ch].loop; - d = do_samples_skip(ch, ns_to); + if (unlikely(s_chan->bFMod == 2)) + { + // sucks, have to do double work + assert(!s_chan->bNoise); + d = do_samples_gauss(tmpFMod, decode_block, NULL, ch, ns_to, + &spu.sb[ch], s_chan->sinc, &s_chan->spos, &s_chan->iSBPos); + if (!s_chan->bStarting) { + d = MixADSR(tmpFMod, &s_chan->ADSRX, d); + if (d < ns_to) { + spu.dwChannelsAudible &= ~(1 << ch); + s_chan->ADSRX.State = ADSR_RELEASE; + s_chan->ADSRX.EnvelopeVol = 0; + } + } + memset(&tmpFMod[d], 0, (ns_to - d) * sizeof(tmpFMod[d])); + work->ch[ch].ns_to = d; + continue; + } + if (unlikely(s_chan->bFMod)) + d = do_samples_skip_fmod(ch, ns_to, tmpFMod); + else + d = do_samples_skip(ch, ns_to); work->ch[ch].ns_to = d; if (!s_chan->bStarting) { @@ -951,7 +1016,7 @@ static void queue_channel_work(int ns_to, unsigned int silentch) s_chan->ADSRX.EnvelopeVol = 0; } } - } + } // for (ch;;) work->rvb_addr = 0; if (spu.rvb->StartAddr) { @@ -978,10 +1043,10 @@ static void do_channel_work(struct work_item *work) if (unlikely(spu.interpolation != spu_config.iUseInterpolation)) { spu.interpolation = spu_config.iUseInterpolation; - mask = spu.dwChannelsAudible & 0xffffff; + mask = work->channels_on; for (ch = 0; mask != 0; ch++, mask >>= 1) if (mask & 1) - ResetInterpolation(&spu.sb[ch]); + ResetInterpolation(&spu.sb_thread[ch]); } if (work->rvb_addr) @@ -990,7 +1055,7 @@ static void do_channel_work(struct work_item *work) mask = work->channels_new; for (ch = 0; mask != 0; ch++, mask >>= 1) { if (mask & 1) - StartSoundSB(&spu.sb[ch]); + StartSoundSB(&spu.sb_thread[ch]); } mask = work->channels_on; @@ -1003,12 +1068,12 @@ static void do_channel_work(struct work_item *work) sbpos = work->ch[ch].sbpos; if (work->ch[ch].bNoise) - do_lsfr_samples(d, work->ctrl, &spu.dwNoiseCount, &spu.dwNoiseVal); + do_lsfr_samples(ChanBuf, d, work->ctrl, &spu.dwNoiseCount, &spu.dwNoiseVal); else - do_samples_adpcm(decode_block_work, work, ch, d, work->ch[ch].bFMod, - &spu.sb[ch], work->ch[ch].sinc, &spos, &sbpos); + do_samples_adpcm(ChanBuf, decode_block_work, work, ch, d, work->ch[ch].bFMod, + &spu.sb_thread[ch], work->ch[ch].sinc, &spos, &sbpos); - d = MixADSR(&work->ch[ch].adsr, d); + d = MixADSR(ChanBuf, &work->ch[ch].adsr, d); if (d < ns_to) { work->ch[ch].adsr.EnvelopeVol = 0; memset(&ChanBuf[d], 0, (ns_to - d) * sizeof(ChanBuf[0])); @@ -1048,7 +1113,7 @@ static void sync_worker_thread(int force) work = &worker->i[worker->i_reaped & WORK_I_MASK]; thread_work_wait_sync(work, force); - MixXA(work->SSumLR, RVB, work->ns_to, work->decode_pos); + MixCD(work->SSumLR, RVB, work->ns_to, work->decode_pos); do_samples_finish(work->SSumLR, work->ns_to, work->channels_silent, work->decode_pos); @@ -1083,7 +1148,7 @@ void do_samples(unsigned int cycles_to, int do_direct) 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); + log_unhandled("desync %u %d\n", cycles_to, cycle_diff); spu.cycles_played = cycles_to; return; } @@ -1100,7 +1165,7 @@ void do_samples(unsigned int cycles_to, int do_direct) ns_to = (cycle_diff / 768 + 1) & ~1; if (ns_to > NSSIZE) { // should never happen - //xprintf("ns_to oflow %d %d\n", ns_to, NSSIZE); + log_unhandled("ns_to oflow %d %d\n", ns_to, NSSIZE); ns_to = NSSIZE; } @@ -1129,7 +1194,7 @@ void do_samples(unsigned int cycles_to, int do_direct) if (0 < left && left <= ns_to) { //xprintf("decoder irq %x\n", spu.decode_pos); - do_irq(); + do_irq(0); } } if (!spu.cycles_dma_end || (int)(spu.cycles_dma_end - cycles_to) < 0) { @@ -1186,7 +1251,7 @@ static void do_samples_finish(int *SSumLR, int ns_to, vol_l = vol_l * spu_config.iVolume >> 10; vol_r = vol_r * spu_config.iVolume >> 10; - if (!(spu.spuCtrl & CTRL_MUTE) || !(vol_l | vol_r)) + if (!(vol_l | vol_r)) { // muted? (rare) memset(spu.pS, 0, ns_to * 2 * sizeof(spu.pS[0])); @@ -1293,6 +1358,8 @@ void CALLBACK SPUplayADPCMchannel(xa_decode_t *xap, unsigned int cycle, int is_s if(!xap->freq) return; // no xa freq ? bye if (is_start) + spu.XAPlay = spu.XAFeed = spu.XAStart; + if (spu.XAPlay == spu.XAFeed) do_samples(cycle, 1); // catch up to prevent source underflows later FeedXA(xap); // call main XA feeder @@ -1300,18 +1367,29 @@ void CALLBACK SPUplayADPCMchannel(xa_decode_t *xap, unsigned int cycle, int is_s } // CDDA AUDIO -int CALLBACK SPUplayCDDAchannel(short *pcm, int nbytes, unsigned int cycle, int is_start) +int CALLBACK SPUplayCDDAchannel(short *pcm, int nbytes, unsigned int cycle, int unused) { if (!pcm) return -1; if (nbytes<=0) return -1; - if (is_start) + if (spu.CDDAPlay == spu.CDDAFeed) do_samples(cycle, 1); // catch up to prevent source underflows later FeedCDDA((unsigned char *)pcm, nbytes); return 0; } +void CALLBACK SPUsetCDvol(unsigned char ll, unsigned char lr, + unsigned char rl, unsigned char rr, unsigned int cycle) +{ + if (spu.XAPlay != spu.XAFeed || spu.CDDAPlay != spu.CDDAFeed) + do_samples(cycle, 1); + spu.cdv.ll = ll; + spu.cdv.lr = lr; + spu.cdv.rl = rl; + spu.cdv.rr = rr; +} + // to be called after state load void ClearWorkingState(void) { @@ -1412,6 +1490,8 @@ static void init_spu_thread(void) { int ret; + spu.sb_thread = spu.sb_thread_; + if (sysconf(_SC_NPROCESSORS_ONLN) <= 1) return; @@ -1473,7 +1553,10 @@ long CALLBACK SPUinit(void) int i; memset(&spu, 0, sizeof(spu)); - spu.spuMemC = calloc(1, 512 * 1024); + spu.spuMemC = calloc(1, 512 * 1024 + 16); + // a guard for runaway channels - End+Mute + spu.spuMemC[512 * 1024 + 1] = 1; + InitADSR(); spu.s_chan = calloc(MAXCHAN+1, sizeof(spu.s_chan[0])); // channel + 1 infos (1 is security for fmod handling) @@ -1551,14 +1634,14 @@ long CALLBACK SPUshutdown(void) // SETUP CALLBACKS // this functions will be called once, // passes a callback that should be called on SPU-IRQ/cdda volume change -void CALLBACK SPUregisterCallback(void (CALLBACK *callback)(void)) +void CALLBACK SPUregisterCallback(void (CALLBACK *callback)(int)) { spu.irqCallback = callback; } void CALLBACK SPUregisterCDDAVolume(void (CALLBACK *CDDAVcallback)(short, short)) { - spu.cddavCallback = CDDAVcallback; + //spu.cddavCallback = CDDAVcallback; } void CALLBACK SPUregisterScheduleCb(void (CALLBACK *callback)(unsigned int))