X-Git-Url: https://notaz.gp2x.de/cgi-bin/gitweb.cgi?a=blobdiff_plain;f=plugins%2Fdfsound%2Fspu.c;h=ebebd2a2c11cc6789c5c2dac9a6bed87adf40ab9;hb=16809ab9f432e902b4b049b4f1f01a0f42e92914;hp=c6a1bf5e53a9becf802e6feb555b56011001ec20;hpb=1e0eac2348343c8a046f7a75d460f087556cf7f6;p=pcsx_rearmed.git diff --git a/plugins/dfsound/spu.c b/plugins/dfsound/spu.c index c6a1bf5e..ebebd2a2 100644 --- a/plugins/dfsound/spu.c +++ b/plugins/dfsound/spu.c @@ -18,8 +18,7 @@ * * ***************************************************************************/ -#if !defined(_WIN32) && !defined(NO_OS) -#include // gettimeofday in xa.c +#if !defined(THREAD_ENABLED) && !defined(_WIN32) && !defined(NO_OS) #define THREAD_ENABLED 1 #endif #include "stdafx.h" @@ -35,7 +34,7 @@ #include "arm_features.h" #endif -#ifdef __ARM_ARCH_7A__ +#ifdef HAVE_ARMV7 #define ssat32_to_16(v) \ asm("ssat %0,#16,%1" : "=r" (v) : "r" (v)) #else @@ -212,7 +211,7 @@ static void do_irq(void) static int check_irq(int ch, unsigned char *pos) { - if((spu.spuCtrl & CTRL_IRQ) && pos == spu.pSpuIrq) + 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(); @@ -221,6 +220,17 @@ static int check_irq(int ch, unsigned char *pos) return 0; } +void check_irq_io(unsigned int addr) +{ + unsigned int irq_addr = regAreaGet(H_SPUirqAddr) << 3; + //addr &= ~7; // ? + if((spu.spuCtrl & (CTRL_ON|CTRL_IRQ)) == (CTRL_ON|CTRL_IRQ) && addr == irq_addr) + { + //printf("io irq %04x\n", irq_addr); + do_irq(); + } +} + //////////////////////////////////////////////////////////////////////// // START SOUND... called by main thread to setup a new sound on a channel //////////////////////////////////////////////////////////////////////// @@ -247,9 +257,11 @@ static void StartSoundMain(int ch) s_chan->iSBPos=27; s_chan->spos=0; + s_chan->pCurr = spu.spuMemC + ((regAreaGetCh(ch, 6) & ~1) << 3); + spu.dwNewChannel&=~(1<> 6) & ~3; gpos = SB[28]; - 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; - fa = vr>>11; + 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 @@ -423,8 +434,8 @@ static int decode_block(void *unused, int ch, int *SB) start = s_chan->pLoop; } - else - check_irq(ch, start); // hack, see check_irq below.. + + check_irq(ch, start); predict_nr = start[0]; shift_factor = predict_nr & 0xf; @@ -433,19 +444,11 @@ static int decode_block(void *unused, int ch, int *SB) decode_block_data(SB, start + 2, predict_nr, shift_factor); flags = start[1]; - if (flags & 4) + if (flags & 4 && !s_chan->bIgnoreLoop) s_chan->pLoop = start; // loop adress start += 16; - if (flags & 1) { // 1: stop/loop - start = s_chan->pLoop; - check_irq(ch, start); // hack.. :( - } - - if (start - spu.spuMemC >= 0x80000) - start = spu.spuMemC; - s_chan->pCurr = start; // store values for next cycle s_chan->prevflags = flags; @@ -466,20 +469,15 @@ static int skip_block(int ch) start = s_chan->pLoop; } - else - check_irq(ch, start); + + check_irq(ch, start); flags = start[1]; - if (flags & 4) + if (flags & 4 && !s_chan->bIgnoreLoop) s_chan->pLoop = start; start += 16; - if (flags & 1) { - start = s_chan->pLoop; - check_irq(ch, start); - } - s_chan->pCurr = start; s_chan->prevflags = flags; @@ -508,8 +506,6 @@ static void scan_for_irq(int ch, unsigned int *upd_samples) block += 16; if (flags & 1) { // 1: stop/loop block = s_chan->pLoop; - if (block == spu.pSpuIrq) // hack.. (see decode_block) - break; } pos += 28 << 16; } @@ -774,7 +770,7 @@ static void do_channels(int ns_to) StartSound(ch); } - mask = spu.dwChannelOn & 0xffffff; + mask = spu.dwChannelsAudible & 0xffffff; for (ch = 0; mask != 0; ch++, mask >>= 1) // loop em all... { if (!(mask & 1)) continue; // channel not playing? next @@ -782,6 +778,9 @@ static void do_channels(int ns_to) 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); @@ -798,7 +797,8 @@ static void do_channels(int ns_to) d = MixADSR(&s_chan->ADSRX, d); if (d < ns_to) { - spu.dwChannelOn &= ~(1 << ch); + spu.dwChannelsAudible &= ~(1 << ch); + s_chan->ADSRX.State = ADSR_RELEASE; s_chan->ADSRX.EnvelopeVol = 0; memset(&ChanBuf[d], 0, (ns_to - d) * sizeof(ChanBuf[0])); } @@ -869,11 +869,14 @@ static struct spu_worker { int sinc; int start; int loop; - int ns_to; short vol_l; short vol_r; + unsigned short ns_to; + unsigned short bNoise:1; + unsigned short bFMod:2; + unsigned short bRVBActive:1; + unsigned short bNewPitch:1; ADSRInfoEx adsr; - // might also want to add fmod flags.. } ch[24]; int SSumLR[NSSIZE * 2]; } i[4]; @@ -935,7 +938,7 @@ static void queue_channel_work(int ns_to, unsigned int silentch) StartSoundMain(ch); } - mask = work->channels_on = spu.dwChannelOn & 0xffffff; + mask = work->channels_on = spu.dwChannelsAudible & 0xffffff; spu.decode_dirty_ch |= mask & 0x0a; for (ch = 0; mask != 0; ch++, mask >>= 1) @@ -951,6 +954,10 @@ static void queue_channel_work(int ns_to, unsigned int silentch) work->ch[ch].vol_r = s_chan->iRightVolume; work->ch[ch].start = s_chan->pCurr - spu.spuMemC; work->ch[ch].loop = s_chan->pLoop - spu.spuMemC; + 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; @@ -960,9 +967,11 @@ static void queue_channel_work(int ns_to, unsigned int silentch) // note: d is not accurate on skip d = SkipADSR(&s_chan->ADSRX, d); if (d < ns_to) { - spu.dwChannelOn &= ~(1 << ch); + spu.dwChannelsAudible &= ~(1 << ch); + s_chan->ADSRX.State = ADSR_RELEASE; s_chan->ADSRX.EnvelopeVol = 0; } + s_chan->bNewPitch = 0; } work->rvb_addr = 0; @@ -982,8 +991,6 @@ static void queue_channel_work(int ns_to, unsigned int silentch) static void do_channel_work(struct work_item *work) { unsigned int mask; - unsigned int decode_dirty_ch = 0; - const SPUCHAN *s_chan; int *SB, sinc, spos, sbpos; int d, ch, ns_to; @@ -1008,15 +1015,16 @@ static void do_channel_work(struct work_item *work) sbpos = work->ch[ch].sbpos; sinc = work->ch[ch].sinc; - s_chan = &spu.s_chan[ch]; SB = spu.SB + ch * SB_SIZE; + if (work->ch[ch].bNewPitch) + SB[32] = 1; // reset interpolation - if (s_chan->bNoise) + if (work->ch[ch].bNoise) do_lsfr_samples(d, work->ctrl, &spu.dwNoiseCount, &spu.dwNoiseVal); - else if (s_chan->bFMod == 2 - || (s_chan->bFMod == 0 && spu_config.iUseInterpolation == 0)) + 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 (s_chan->bFMod == 0 && spu_config.iUseInterpolation == 1) + 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); @@ -1028,14 +1036,11 @@ static void do_channel_work(struct work_item *work) } if (ch == 1 || ch == 3) - { - do_decode_bufs(spu.spuMem, ch/2, ns_to, work->decode_pos); - decode_dirty_ch |= 1 << ch; - } + do_decode_bufs(spu.spuMem, ch/2, ns_to, work->decode_pos); - if (s_chan->bFMod == 2) // fmod freq channel + if (work->ch[ch].bFMod == 2) // fmod freq channel memcpy(iFMod, &ChanBuf, ns_to * sizeof(iFMod[0])); - if (s_chan->bRVBActive && work->rvb_addr) + if (work->ch[ch].bRVBActive && work->rvb_addr) mix_chan_rvb(work->SSumLR, ns_to, work->ch[ch].vol_l, work->ch[ch].vol_r, RVB); else @@ -1103,7 +1108,7 @@ void do_samples(unsigned int cycles_to, int do_direct) return; } - silentch = ~(spu.dwChannelOn | spu.dwNewChannel) & 0xffffff; + silentch = ~(spu.dwChannelsAudible | spu.dwNewChannel) & 0xffffff; do_direct |= (silentch == 0xffffff); if (worker != NULL) @@ -1147,6 +1152,7 @@ void do_samples(unsigned int cycles_to, int do_direct) do_irq(); } } + check_irq_io(spu.spuAddr); if (unlikely(spu.rvb->dirty)) REVERBPrep(); @@ -1157,6 +1163,7 @@ void do_samples(unsigned int cycles_to, int do_direct) } else { queue_channel_work(ns_to, silentch); + //sync_worker_thread(1); // uncomment for debug } // advance "stopped" channels that can cause irqs @@ -1171,7 +1178,8 @@ void do_samples(unsigned int cycles_to, int do_direct) static void do_samples_finish(int *SSumLR, int ns_to, int silentch, int decode_pos) { - int volmult = spu_config.iVolume; + int vol_l = ((int)regAreaGet(H_SPUmvolL) << 17) >> 17; + int vol_r = ((int)regAreaGet(H_SPUmvolR) << 17) >> 17; int ns; int d; @@ -1188,23 +1196,28 @@ static void do_samples_finish(int *SSumLR, int ns_to, } MixXA(SSumLR, ns_to, decode_pos); - - if((spu.spuCtrl&0x4000)==0) // muted? (rare, don't optimize for this) + + vol_l = vol_l * spu_config.iVolume >> 10; + vol_r = vol_r * spu_config.iVolume >> 10; + + if (!(spu.spuCtrl & 0x4000) || !(vol_l | vol_r)) { + // muted? (rare) memset(spu.pS, 0, ns_to * 2 * sizeof(spu.pS[0])); + memset(SSumLR, 0, ns_to * 2 * sizeof(SSumLR[0])); spu.pS += ns_to * 2; } else for (ns = 0; ns < ns_to * 2; ) { d = SSumLR[ns]; SSumLR[ns] = 0; - d = d * volmult >> 10; + d = d * vol_l >> 14; ssat32_to_16(d); *spu.pS++ = d; ns++; d = SSumLR[ns]; SSumLR[ns] = 0; - d = d * volmult >> 10; + d = d * vol_r >> 14; ssat32_to_16(d); *spu.pS++ = d; ns++; @@ -1228,6 +1241,8 @@ void schedule_next_irq(void) if ((unsigned long)(spu.pSpuIrq - spu.s_chan[ch].pCurr) > IRQ_NEAR_BLOCKS * 16 && (unsigned long)(spu.pSpuIrq - spu.s_chan[ch].pLoop) > IRQ_NEAR_BLOCKS * 16) continue; + if (spu.s_chan[ch].sinc == 0) + continue; scan_for_irq(ch, &upd_samples); } @@ -1286,20 +1301,26 @@ void CALLBACK SPUupdate(void) // XA AUDIO -void CALLBACK SPUplayADPCMchannel(xa_decode_t *xap) +void CALLBACK SPUplayADPCMchannel(xa_decode_t *xap, unsigned int cycle, int is_start) { if(!xap) return; - if(!xap->freq) return; // no xa freq ? bye + if(!xap->freq) return; // no xa freq ? bye - FeedXA(xap); // call main XA feeder + if (is_start) + do_samples(cycle, 1); // catch up to prevent source underflows later + + FeedXA(xap); // call main XA feeder } // CDDA AUDIO -int CALLBACK SPUplayCDDAchannel(short *pcm, int nbytes) +int CALLBACK SPUplayCDDAchannel(short *pcm, int nbytes, unsigned int cycle, int is_start) { if (!pcm) return -1; if (nbytes<=0) return -1; + if (is_start) + do_samples(cycle, 1); // catch up to prevent source underflows later + return FeedCDDA((unsigned char *)pcm, nbytes); } @@ -1316,14 +1337,12 @@ static void SetupStreams(void) spu.pSpuBuffer = (unsigned char *)malloc(32768); // alloc mixing buffer spu.SSumLR = calloc(NSSIZE * 2, sizeof(spu.SSumLR[0])); - spu.XAStart = // alloc xa buffer - (uint32_t *)malloc(44100 * sizeof(uint32_t)); + spu.XAStart = malloc(44100 * sizeof(uint32_t)); // alloc xa buffer spu.XAEnd = spu.XAStart + 44100; spu.XAPlay = spu.XAStart; spu.XAFeed = spu.XAStart; - spu.CDDAStart = // alloc cdda buffer - (uint32_t *)malloc(CDDA_BUFFER_SIZE); + spu.CDDAStart = malloc(CDDA_BUFFER_SIZE); // alloc cdda buffer spu.CDDAEnd = spu.CDDAStart + 16384; spu.CDDAPlay = spu.CDDAStart; spu.CDDAFeed = spu.CDDAStart; @@ -1489,6 +1508,7 @@ long CALLBACK SPUinit(void) spu.s_chan[i].ADSRX.SustainIncrease = 1; spu.s_chan[i].pLoop = spu.spuMemC; spu.s_chan[i].pCurr = spu.spuMemC; + spu.s_chan[i].bIgnoreLoop = 0; } spu.bSpuInit=1; // flag: we are inited @@ -1577,7 +1597,7 @@ void CALLBACK SPUregisterCallback(void (CALLBACK *callback)(void)) spu.irqCallback = callback; } -void CALLBACK SPUregisterCDDAVolume(void (CALLBACK *CDDAVcallback)(unsigned short,unsigned short)) +void CALLBACK SPUregisterCDDAVolume(void (CALLBACK *CDDAVcallback)(short, short)) { spu.cddavCallback = CDDAVcallback; } @@ -1620,7 +1640,7 @@ void spu_get_debug_info(int *chans_out, int *run_chans, int *fmod_chans_out, int for(;ch