X-Git-Url: https://notaz.gp2x.de/cgi-bin/gitweb.cgi?a=blobdiff_plain;f=plugins%2Fdfsound%2Fspu.c;h=1127cd7113d0b10a919717e9a2d2d6d7cf5f71ee;hb=1d5d35bc9d0d3d84873cd1d31870d09248ddc05b;hp=ec31b0ca3db94f3f0c00061dc6676e03386042d1;hpb=8f5f2dd5a70f47322614eda6f97304808447199c;p=pcsx_rearmed.git diff --git a/plugins/dfsound/spu.c b/plugins/dfsound/spu.c index ec31b0ca..1127cd71 100644 --- a/plugins/dfsound/spu.c +++ b/plugins/dfsound/spu.c @@ -35,7 +35,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 @@ -73,11 +73,8 @@ static char * libraryInfo = N_("P.E.Op.S. Sound Driver V1.7\nCoded by Pete B SPUInfo spu; SPUConfig spu_config; -// MAIN infos struct for each channel - -REVERBInfo rvb; - static int iFMod[NSSIZE]; +static int RVB[NSSIZE * 2]; int ChanBuf[NSSIZE]; #define CDDA_BUFFER_SIZE (16384 * sizeof(uint32_t)) // must be power of 2 @@ -250,9 +247,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 @@ -426,8 +425,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; @@ -436,19 +435,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; @@ -469,20 +460,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; @@ -511,8 +497,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; } @@ -763,11 +747,13 @@ static void do_silent_chans(int ns_to, int silentch) static void do_channels(int ns_to) { unsigned int mask; + int do_rvb, ch, d; SPUCHAN *s_chan; int *SB, sinc; - int ch, d; - memset(spu.RVB, 0, ns_to * sizeof(spu.RVB[0]) * 2); + do_rvb = spu.rvb->StartAddr && spu_config.iUseReverb; + if (do_rvb) + memset(RVB, 0, ns_to * sizeof(RVB[0]) * 2); mask = spu.dwNewChannel & 0xffffff; for (ch = 0; mask != 0; ch++, mask >>= 1) { @@ -775,7 +761,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 @@ -799,7 +785,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])); } @@ -812,14 +799,23 @@ 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) - mix_chan_rvb(spu.SSumLR, ns_to, s_chan->iLeftVolume, s_chan->iRightVolume, spu.RVB); + 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); } + + if (spu.rvb->StartAddr) { + if (do_rvb) + REVERBDo(spu.SSumLR, RVB, ns_to, spu.rvb->CurrAddr); + + spu.rvb->CurrAddr += ns_to / 2; + while (spu.rvb->CurrAddr >= 0x40000) + spu.rvb->CurrAddr -= 0x40000 - spu.rvb->StartAddr; + } } -static void do_samples_finish(int *SSumLR, int *RVB, int ns_to, +static void do_samples_finish(int *SSumLR, int ns_to, int silentch, int decode_pos); // optional worker thread handling @@ -833,8 +829,8 @@ static struct spu_worker { unsigned int exit_thread; unsigned int i_ready; unsigned int i_reaped; - unsigned int req_sent; // dsp - unsigned int last_boot_cnt; + unsigned int last_boot_cnt; // dsp + unsigned int ram_dirty; }; // aligning for C64X_DSP unsigned int _pad0[128/4]; @@ -851,6 +847,7 @@ static struct spu_worker { int ns_to; int ctrl; int decode_pos; + int rvb_addr; unsigned int channels_new; unsigned int channels_on; unsigned int channels_silent; @@ -861,10 +858,11 @@ static struct spu_worker { int start; int loop; int ns_to; + short vol_l; + short vol_r; ADSRInfoEx adsr; - // might want to add vol and fmod flags.. + // might also want to add fmod flags.. } ch[24]; - int RVB[NSSIZE * 2]; int SSumLR[NSSIZE * 2]; } i[4]; } *worker; @@ -874,6 +872,7 @@ static struct spu_worker { static void thread_work_start(void); static void thread_work_wait_sync(struct work_item *work, int force); +static void thread_sync_caches(void); static int thread_get_i_done(void); static int decode_block_work(void *context, int ch, int *SB) @@ -924,7 +923,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) @@ -936,6 +935,8 @@ static void queue_channel_work(int ns_to, unsigned int silentch) work->ch[ch].sbpos = s_chan->iSBPos; work->ch[ch].sinc = s_chan->sinc; work->ch[ch].adsr = s_chan->ADSRX; + work->ch[ch].vol_l = s_chan->iLeftVolume; + 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; if (s_chan->prevflags & 1) @@ -947,11 +948,22 @@ 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; } } + work->rvb_addr = 0; + if (spu.rvb->StartAddr) { + if (spu_config.iUseReverb) + work->rvb_addr = spu.rvb->CurrAddr; + + spu.rvb->CurrAddr += ns_to / 2; + while (spu.rvb->CurrAddr >= 0x40000) + spu.rvb->CurrAddr -= 0x40000 - spu.rvb->StartAddr; + } + worker->i_ready++; thread_work_start(); } @@ -960,12 +972,14 @@ 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; - SPUCHAN *s_chan; ns_to = work->ns_to; - memset(work->RVB, 0, ns_to * sizeof(work->RVB[0]) * 2); + + 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) { @@ -1010,12 +1024,15 @@ static void do_channel_work(struct work_item *work) if (s_chan->bFMod == 2) // fmod freq channel memcpy(iFMod, &ChanBuf, ns_to * sizeof(iFMod[0])); - if (s_chan->bRVBActive) + if (s_chan->bRVBActive && work->rvb_addr) mix_chan_rvb(work->SSumLR, ns_to, - s_chan->iLeftVolume, s_chan->iRightVolume, work->RVB); + work->ch[ch].vol_l, work->ch[ch].vol_r, RVB); else - mix_chan(work->SSumLR, ns_to, s_chan->iLeftVolume, s_chan->iRightVolume); + mix_chan(work->SSumLR, ns_to, work->ch[ch].vol_l, work->ch[ch].vol_r); } + + if (work->rvb_addr) + REVERBDo(work->SSumLR, RVB, ns_to, work->rvb_addr); } static void sync_worker_thread(int force) @@ -1023,8 +1040,12 @@ static void sync_worker_thread(int force) struct work_item *work; int done, used_space; + // rvb offsets will change, thread may be using them + force |= spu.rvb->dirty && spu.rvb->StartAddr; + done = thread_get_i_done() - worker->i_reaped; used_space = worker->i_ready - worker->i_reaped; + //printf("done: %d use: %d dsp: %u/%u\n", done, used_space, // worker->boot_cnt, worker->i_done); @@ -1032,13 +1053,15 @@ static void sync_worker_thread(int force) work = &worker->i[worker->i_reaped & WORK_I_MASK]; thread_work_wait_sync(work, force); - do_samples_finish(work->SSumLR, work->RVB, work->ns_to, + do_samples_finish(work->SSumLR, work->ns_to, work->channels_silent, work->decode_pos); worker->i_reaped++; done = thread_get_i_done() - worker->i_reaped; used_space = worker->i_ready - worker->i_reaped; } + if (force) + thread_sync_caches(); } #else @@ -1069,7 +1092,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) @@ -1114,9 +1137,12 @@ void do_samples(unsigned int cycles_to, int do_direct) } } + if (unlikely(spu.rvb->dirty)) + REVERBPrep(); + if (do_direct || worker == NULL || !spu_config.iUseThread) { do_channels(ns_to); - do_samples_finish(spu.SSumLR, spu.RVB, ns_to, silentch, spu.decode_pos); + do_samples_finish(spu.SSumLR, ns_to, silentch, spu.decode_pos); } else { queue_channel_work(ns_to, silentch); @@ -1131,10 +1157,11 @@ void do_samples(unsigned int cycles_to, int do_direct) spu.decode_pos = (spu.decode_pos + ns_to) & 0x1ff; } -static void do_samples_finish(int *SSumLR, int *RVB, int ns_to, +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; @@ -1150,33 +1177,29 @@ static void do_samples_finish(int *SSumLR, int *RVB, int ns_to, spu.decode_dirty_ch &= ~(1<<3); } - //---------------------------------------------------// - // mix XA infos (if any) - MixXA(SSumLR, ns_to, decode_pos); - - /////////////////////////////////////////////////////// - // mix all channels (including reverb) into one buffer - if(spu_config.iUseReverb) - REVERBDo(SSumLR, RVB, ns_to); + vol_l = vol_l * spu_config.iVolume >> 10; + vol_r = vol_r * spu_config.iVolume >> 10; - if((spu.spuCtrl&0x4000)==0) // muted? (rare, don't optimize for this) + 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++; @@ -1200,6 +1223,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); } @@ -1225,7 +1250,7 @@ void schedule_next_irq(void) void CALLBACK SPUasync(unsigned int cycle, unsigned int flags) { - do_samples(cycle, 0); + do_samples(cycle, spu_config.iUseFixedUpdates); if (spu.spuCtrl & CTRL_IRQ) schedule_next_irq(); @@ -1258,20 +1283,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); } @@ -1283,46 +1314,29 @@ void ClearWorkingState(void) } // SETUPSTREAMS: init most of the spu buffers -void SetupStreams(void) +static void SetupStreams(void) { - int i; - spu.pSpuBuffer = (unsigned char *)malloc(32768); // alloc mixing buffer - spu.RVB = calloc(NSSIZE * 2, sizeof(spu.RVB[0])); 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; - for(i=0;i init sustain - spu.s_chan[i].ADSRX.SustainIncrease = 1; - spu.s_chan[i].pLoop=spu.spuMemC; - spu.s_chan[i].pCurr=spu.spuMemC; - } - ClearWorkingState(); - - spu.bSpuInit=1; // flag: we are inited } // REMOVESTREAMS: free most buffer -void RemoveStreams(void) +static void RemoveStreams(void) { free(spu.pSpuBuffer); // free mixing buffer spu.pSpuBuffer = NULL; - free(spu.RVB); // free reverb buffer - spu.RVB = NULL; free(spu.SSumLR); spu.SSumLR = NULL; free(spu.XAStart); // free XA buffer @@ -1365,6 +1379,10 @@ static int thread_get_i_done(void) return worker->i_done; } +static void thread_sync_caches(void) +{ +} + static void *spu_worker_thread(void *unused) { struct work_item *work; @@ -1446,11 +1464,13 @@ static void exit_spu_thread(void) // SPUINIT: this func will be called first by the main emu long CALLBACK SPUinit(void) { + int i; + spu.spuMemC = calloc(1, 512 * 1024); - memset((void *)&rvb, 0, sizeof(REVERBInfo)); InitADSR(); 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; @@ -1464,6 +1484,17 @@ long CALLBACK SPUinit(void) init_spu_thread(); + for (i = 0; i < MAXCHAN; i++) // loop sound channels + { + spu.s_chan[i].ADSRX.SustainLevel = 0xf; // -> init sustain + 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 + return 0; } @@ -1504,6 +1535,8 @@ long CALLBACK SPUshutdown(void) spu.SB = NULL; free(spu.s_chan); spu.s_chan = NULL; + free(spu.rvb); + spu.rvb = NULL; RemoveStreams(); // no more streaming spu.bSpuInit=0; @@ -1546,7 +1579,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; } @@ -1589,7 +1622,7 @@ void spu_get_debug_info(int *chans_out, int *run_chans, int *fmod_chans_out, int for(;ch