From c4c8592f7f905f420ce5f540783aea72d7897479 Mon Sep 17 00:00:00 2001 From: zeromus Date: Mon, 5 Apr 2010 06:24:12 +0000 Subject: [PATCH] spu: fix regression in some sound capture cases and support reverb better --- desmume/src/SPU.cpp | 193 ++++++++++++++++++++++++++++++-------------- desmume/src/SPU.h | 16 +++- 2 files changed, 147 insertions(+), 62 deletions(-) diff --git a/desmume/src/SPU.cpp b/desmume/src/SPU.cpp index 286d336b6..b0fc26eda 100644 --- a/desmume/src/SPU.cpp +++ b/desmume/src/SPU.cpp @@ -461,7 +461,8 @@ u8 SPU_struct::ReadByte(u32 addr) | (regs.cap[which].source<<1) | (regs.cap[which].oneshot<<2) | (regs.cap[which].bits8<<3) - | (regs.cap[which].active<<7); + //| (regs.cap[which].active<<7); //? which is right? need test + | (regs.cap[which].runtime.running<<7); } //SNDCAP0DAD @@ -523,6 +524,58 @@ u8 SPU_struct::ReadByte(u32 addr) } //switch on address } +SPUFifo::SPUFifo() +{ + reset(); +} + +void SPUFifo::reset() +{ + head = tail = size = 0; +} + +void SPUFifo::enqueue(s16 val) +{ + if(size==16) return; + buffer[tail] = val; + tail++; + tail &= 15; + size++; +} + +s16 SPUFifo::dequeue() +{ + if(size==0) return 0; + head++; + head &= 15; + s16 ret = buffer[head]; + size--; + return ret; +} + +void SPUFifo::save(EMUFILE* fp) +{ + u32 version = 1; + write32le(version,fp); + write32le(head,fp); + write32le(tail,fp); + write32le(size,fp); + for(int i=0;i<16;i++) + write16le(buffer[i],fp); +} + +bool SPUFifo::load(EMUFILE* fp) +{ + u32 version; + if(read32le(&version,fp) != 1) return false; + read32le(&head,fp); + read32le(&tail,fp); + read32le(&size,fp); + for(int i=0;i<16;i++) + read16le(&buffer[i],fp); + return true; +} + void SPU_struct::ProbeCapture(int which) { //VERY UNTESTED -- HOW MUCH OF THIS RESETS, AND WHEN? @@ -533,12 +586,14 @@ void SPU_struct::ProbeCapture(int which) return; } - regs.cap[which].runtime.running = 1; - regs.cap[which].runtime.curdad = regs.cap[which].dad; - u32 len = regs.cap[which].len; + REGS::CAP &cap = regs.cap[which]; + cap.runtime.running = 1; + cap.runtime.curdad = cap.dad; + u32 len = cap.len; if(len==0) len=1; - regs.cap[which].runtime.maxdad = regs.cap[which].dad + len*4; - regs.cap[which].runtime.sampcnt = 0; + cap.runtime.maxdad = cap.dad + len*4; + cap.runtime.sampcnt = 0; + cap.runtime.fifo.reset(); } void SPU_struct::WriteByte(u32 addr, u8 val) @@ -1037,6 +1092,7 @@ static void SPU_MixAudio_Advanced(bool actuallyMix, SPU_struct *SPU, int length) SPU->buflength = 1; s32 capmix[2] = {0,0}; + s32 mix[2] = {0,0}; s32 chanout[16]; s32 submix[32]; @@ -1053,24 +1109,20 @@ static void SPU_MixAudio_Advanced(bool actuallyMix, SPU_struct *SPU, int length) if(i==1 && SPU->regs.ctl_ch1bypass) bypass=true; if(i==3 && SPU->regs.ctl_ch3bypass) bypass=true; - bool domix = !CommonSettings.spu_muteChannels[i]; - bool docapmix = true; - if(!domix) { - bypass = true; - if(CommonSettings.spu_captureMuted) - { - domix = true; - docapmix = true; - } - } - if(!actuallyMix) { - domix = false; - bypass = true; - } - - //save our old mix accumulators so we can generate a clean panned sample - s32 save[] = {SPU->sndbuf[0],SPU->sndbuf[1]}; + //output to mixer unless we are bypassed. + //dont output to mixer if the user muted us + bool outputToMix = true; + if(CommonSettings.spu_muteChannels[i]) outputToMix = false; + if(bypass) outputToMix = false; + bool outputToCap = outputToMix; + if(CommonSettings.spu_captureMuted && !bypass) outputToCap = true; + + //channels 1 and 3 should probably always generate their audio + //internally at least, just in case they get used by the spu output + bool domix = outputToCap || outputToMix || i==1 || i==3; + + //clear the output buffer since this is where _SPU_ChanUpdate wants to accumulate things SPU->sndbuf[0] = SPU->sndbuf[1] = 0; //get channel's next output sample. @@ -1081,30 +1133,19 @@ static void SPU_MixAudio_Advanced(bool actuallyMix, SPU_struct *SPU, int length) submix[i*2] = SPU->sndbuf[0]; submix[i*2+1] = SPU->sndbuf[1]; - //restore the old mix accumulator - SPU->sndbuf[0] = save[0]; - SPU->sndbuf[1] = save[1]; - - //mix sample into our capture mix - if(docapmix) + //send sample to our capture mix + if(outputToCap) { capmix[0] += submix[i*2]; capmix[1] += submix[i*2+1]; } - //mix in this sample, unless we were bypassing it - if(bypass) + //send sample to our main mixer + if(outputToMix) { - submix[i*2]=0; - submix[i*2+1]=0; + mix[0] += submix[i*2]; + mix[1] += submix[i*2+1]; } - else - { - SPU->sndbuf[0] += submix[i*2]; - SPU->sndbuf[1] += submix[i*2+1]; - } - - } else { @@ -1114,7 +1155,7 @@ static void SPU_MixAudio_Advanced(bool actuallyMix, SPU_struct *SPU, int length) } } //foreach channel - s32 mixout[2] = {SPU->sndbuf[0],SPU->sndbuf[1]}; + s32 mixout[2] = {mix[0],mix[1]}; s32 capmixout[2] = {capmix[0],capmix[1]}; s32 sndout[2]; s32 capout[2]; @@ -1149,12 +1190,19 @@ static void SPU_MixAudio_Advanced(bool actuallyMix, SPU_struct *SPU, int length) capout[1] = chanout[2] + chanout[3]; //cap1 = ch2+ch3 else capout[1] = chanout[2]; //cap1 = ch2 + capout[0] = MinMax(capout[0],-0x8000,0x7FFF); + capout[1] = MinMax(capout[1],-0x8000,0x7FFF); + //write the output sample where it is supposed to go - SPU->sndbuf[samp*2+0] = sndout[0]; - SPU->sndbuf[samp*2+1] = sndout[1]; - if(samp==0) { - samp0[0] = SPU->sndbuf[samp*2+0]; - samp0[1] = SPU->sndbuf[samp*2+1]; + if(samp==0) + { + samp0[0] = sndout[0]; + samp0[1] = sndout[1]; + } + else + { + SPU->sndbuf[samp*2+0] = sndout[0]; + SPU->sndbuf[samp*2+1] = sndout[1]; } for(int capchan=0;capchan<2;capchan++) @@ -1162,21 +1210,43 @@ static void SPU_MixAudio_Advanced(bool actuallyMix, SPU_struct *SPU, int length) if(SPU->regs.cap[capchan].runtime.running) { SPU_struct::REGS::CAP& cap = SPU->regs.cap[capchan]; - s32 sample = capout[capchan]; - //s32 sample = 0; u32 last = sputrunc(cap.runtime.sampcnt); cap.runtime.sampcnt += SPU->channels[1+2*capchan].sampinc; u32 curr = sputrunc(cap.runtime.sampcnt); for(u32 j=last;j>8; - //static FILE* fCapOut = NULL; - //if(!fCapOut) fCapOut = fopen("d:\\capout.raw","wb"); - //fwrite(&sample8,1,1,fCapOut); if(skipcap) _MMU_write08<1,MMU_AT_DMA>(cap.runtime.curdad,0); else _MMU_write08<1,MMU_AT_DMA>(cap.runtime.curdad,sample8); cap.runtime.curdad++; @@ -1260,15 +1330,7 @@ static void SPU_MixAudio(bool actuallyMix, SPU_struct *SPU, int length) { // Apply Master Volume SPU->sndbuf[i] = spumuldiv7(SPU->sndbuf[i], vol); - - u16 outsample; - if (SPU->sndbuf[i] > 0x7FFF) - outsample = 0x7FFF; - else if (SPU->sndbuf[i] < -0x8000) - outsample = -0x8000; - else - outsample = (s16)SPU->sndbuf[i]; - + s16 outsample = MinMax(SPU->sndbuf[i],-0x8000,0x7FFF); SPU->outbuf[i] = outsample; } @@ -1491,7 +1553,7 @@ void WAV_WavSoundUpdate(void* soundData, int numSamples, WAVMode mode) void spu_savestate(EMUFILE* os) { //version - write32le(5,os); + write32le(6,os); SPU_struct *spu = SPU_core; @@ -1545,6 +1607,9 @@ void spu_savestate(EMUFILE* os) write32le(spu->regs.cap[i].runtime.maxdad,os); write_double_le(spu->regs.cap[i].runtime.sampcnt,os); } + + for(int i=0;i<2;i++) + spu->regs.cap[i].runtime.fifo.save(os); } bool spu_loadstate(EMUFILE* is, int size) @@ -1556,6 +1621,7 @@ bool spu_loadstate(EMUFILE* is, int size) if(read32le(&version,is) != 1) return false; SPU_struct *spu = SPU_core; + reconstruct(&SPU_core->regs); for(int j=0;j<16;j++) { channel_struct &chan = spu->channels[j]; @@ -1635,6 +1701,11 @@ bool spu_loadstate(EMUFILE* is, int size) } } + if(version>=6) + for(int i=0;i<2;i++) spu->regs.cap[i].runtime.fifo.load(is); + else + for(int i=0;i<2;i++) spu->regs.cap[i].runtime.fifo.reset(); + //older versions didnt store a mastervol; //we must reload this or else games will start silent if(version<4) diff --git a/desmume/src/SPU.h b/desmume/src/SPU.h index 052b25f84..f5520153b 100644 --- a/desmume/src/SPU.h +++ b/desmume/src/SPU.h @@ -103,7 +103,20 @@ struct channel_struct int loop_index; u16 x; s16 psgnoise_last; -} ; +}; + +class SPUFifo +{ +public: + SPUFifo(); + void enqueue(s16 val); + s16 dequeue(); + s16 buffer[16]; + s32 head,tail,size; + void save(EMUFILE* fp); + bool load(EMUFILE* fp); + void reset(); +}; class SPU_struct { @@ -160,6 +173,7 @@ public: u32 curdad; u32 maxdad; double sampcnt; + SPUFifo fifo; } runtime; } cap[2]; } regs;