diff --git a/desmume/src/SPU.cpp b/desmume/src/SPU.cpp index ef3d93656..46b92758e 100644 --- a/desmume/src/SPU.cpp +++ b/desmume/src/SPU.cpp @@ -126,6 +126,14 @@ static FORCEINLINE T MinMax(T val, T min, T max) return val; } +// T must be unsigned type +template +static FORCEINLINE T AddAndReturnCarry(T *a, T b) { + T c = (*a >= (-b)); + *a += b; + return c; +} + //--------------external spu interface--------------- int SPU_ChangeSoundCore(int coreid, int newBufferSizeBytes) @@ -380,7 +388,8 @@ static FORCEINLINE void adjust_channel_timer(channel_struct *chan) // ARM7_CLOCK / (DESMUME_SAMPLE_RATE*2) / (2^16 - Timer) // = ARM7_CLOCK / (DESMUME_SAMPLE_RATE*2 * (2^16 - Timer)) // ... and then round up for good measure - chan->sampinc = ((u32)ARM7_CLOCK*(1ull<<32)-1) / (DESMUME_SAMPLE_RATE*2ull * (0x10000 - chan->timer)) + 1; + u64 sampinc = ((u32)ARM7_CLOCK*(1ull << 32) - 1) / (DESMUME_SAMPLE_RATE * 2ull * (0x10000 - chan->timer)) + 1; + chan->sampincInt = (u32)(sampinc >> 32), chan->sampincFrac = (u32)sampinc; } void SPU_struct::KeyProbe(int chan_num) @@ -431,23 +440,23 @@ void SPU_struct::KeyOn(int channel) case 0: // 8-bit // thischan.loopstart = thischan.loopstart << 2; // thischan.length = (thischan.length << 2) + thischan.loopstart; - thischan.sampcnt = -3 * (1ll<<32); + thischan.sampcntFrac = 0, thischan.sampcntInt = -3; break; case 1: // 16-bit // thischan.loopstart = thischan.loopstart << 1; // thischan.length = (thischan.length << 1) + thischan.loopstart; - thischan.sampcnt = -3 * (1ll<<32); + thischan.sampcntFrac = 0, thischan.sampcntInt = -3; break; case 2: // ADPCM thischan.pcm16b[0] = (s16)read16(thischan.addr); thischan.index = read08(thischan.addr + 2) & 0x7F; - thischan.sampcnt = -3 * (1ll<<32); + thischan.sampcntFrac = 0, thischan.sampcntInt = -3; thischan.loop_index = K_ADPCM_LOOPING_RECOVERY_INDEX; // thischan.loopstart = thischan.loopstart << 3; // thischan.length = (thischan.length << 3) + thischan.loopstart; break; case 3: // PSG - thischan.sampcnt = -1 * (1ll<<32); + thischan.sampcntFrac = 0, thischan.sampcntInt = -1; thischan.x = 0x7FFF; break; default: break; @@ -756,7 +765,7 @@ void SPU_struct::ProbeCapture(int which) u32 len = cap.len; if(len==0) len=1; cap.runtime.maxdad = cap.dad + len*4; - cap.runtime.sampcnt = 0; + cap.runtime.sampcntFrac = cap.runtime.sampcntInt = 0; cap.runtime.fifo.reset(); } @@ -1163,7 +1172,7 @@ static FORCEINLINE void MixLR(SPU_struct* SPU, channel_struct *chan, s32 data) template static FORCEINLINE void TestForLoop(SPU_struct *SPU, channel_struct *chan) { // Do nothing if we haven't reached the end - if((chan->sampcnt >> 32) < chan->totlength_shifted) return; + if(chan->sampcntInt < chan->totlength_shifted) return; // Kill the channel if we don't repeat if(chan->repeat != 1) @@ -1196,8 +1205,8 @@ template static FORCEINLINE void TestForLoop(SPU_struct *SPU, channe } // Wrap sampcnt - s64 step = chan->totlength_shifted - (chan->loopstart << format_shift[FORMAT]); - while ((chan->sampcnt >> 32) >= chan->totlength_shifted) chan->sampcnt -= step * (1ll << 32); + u32 step = chan->totlength_shifted - (chan->loopstart << format_shift[FORMAT]); + while (chan->sampcntInt >= chan->totlength_shifted) chan->sampcntInt -= step; } template FORCEINLINE static void SPU_Mix(SPU_struct* SPU, channel_struct *chan, s32 data) @@ -1220,14 +1229,11 @@ template { // Advance sampcnt one sample at a time. This is // needed to keep pcm16b[] filled for interpolation. - // We need to do some janky things here to keep the - // fractional bits in place when we loop :/ - s64 newsampcnt = chan->sampcnt + chan->sampinc; - u32 nSamplesToSkip = (u32)((newsampcnt >> 32) - (chan->sampcnt >> 32)); + u32 nSamplesToSkip = chan->sampincInt + AddAndReturnCarry(&chan->sampcntFrac, chan->sampincFrac); while(nSamplesToSkip--) { s16 data = 0; - s32 pos = chan->sampcnt >> 32; + s32 pos = chan->sampcntInt; switch(FORMAT) { case 0: data = Fetch8BitData (chan, pos); break; @@ -1239,14 +1245,13 @@ template chan->pcm16bOffs++; chan->pcm16b[SPUCHAN_PCM16B_AT(chan->pcm16bOffs)] = data; - chan->sampcnt += 1ll << 32; + chan->sampcntInt++; if (FORMAT != 3) TestForLoop(SPU, chan); } - chan->sampcnt = ((chan->sampcnt >> 32) << 32) | (u32)newsampcnt; if(CHANNELS != -1) { - s32 data = Interpolate(chan->pcm16b, chan->pcm16bOffs, (u32)chan->sampcnt); + s32 data = Interpolate(chan->pcm16b, chan->pcm16bOffs, chan->sampcntFrac); SPU_Mix(SPU, chan, data); } } @@ -1438,13 +1443,13 @@ static void SPU_MixAudio_Advanced(bool actuallyMix, SPU_struct *SPU, int length) for (int capchan = 0; capchan < 2; capchan++) { + SPU_struct::REGS::CAP& cap = SPU->regs.cap[capchan]; + channel_struct& srcChan = SPU->channels[1 + 2 * capchan]; if (SPU->regs.cap[capchan].runtime.running) { - SPU_struct::REGS::CAP& cap = SPU->regs.cap[capchan]; - u32 last = cap.runtime.sampcnt >> 32; - cap.runtime.sampcnt += SPU->channels[1+2*capchan].sampinc; - u32 curr = cap.runtime.sampcnt >> 32; - for (u32 j = last; j < curr; j++) + u32 nSamplesToProcess = srcChan.sampincInt + AddAndReturnCarry(&cap.runtime.sampcntFrac, srcChan.sampincFrac); + cap.runtime.sampcntInt += nSamplesToProcess; + while(nSamplesToProcess--) { //so, this is a little strange. why go through a fifo? //it seems that some games will set up a reverb effect by capturing @@ -1495,7 +1500,7 @@ static void SPU_MixAudio_Advanced(bool actuallyMix, SPU_struct *SPU, int length) if (cap.runtime.curdad >= cap.runtime.maxdad) { cap.runtime.curdad = cap.dad; - cap.runtime.sampcnt -= cap.len*multiplier * (1ull<<32); + cap.runtime.sampcntInt -= cap.len*multiplier; } } //sampinc loop } //if capchan running @@ -1555,14 +1560,14 @@ static void SPU_MixAudio(bool actuallyMix, SPU_struct *SPU, int length) for (int capchan = 0; capchan < 2; capchan++) { SPU_struct::REGS::CAP& cap = SPU->regs.cap[capchan]; + channel_struct& srcChan = SPU->channels[1 + 2 * capchan]; if (cap.runtime.running) { for (int samp = 0; samp < length; samp++) { - u32 last = cap.runtime.sampcnt >> 32; - cap.runtime.sampcnt += SPU->channels[1+2*capchan].sampinc; - u32 curr = cap.runtime.sampcnt >> 32; - for (u32 j = last; j < curr; j++) + u32 nSamplesToProcess = srcChan.sampincInt + AddAndReturnCarry(&cap.runtime.sampcntFrac, srcChan.sampincFrac); + cap.runtime.sampcntInt += nSamplesToProcess; + while (nSamplesToProcess--) { if (cap.bits8) { @@ -1578,7 +1583,7 @@ static void SPU_MixAudio(bool actuallyMix, SPU_struct *SPU, int length) if (cap.runtime.curdad >= cap.runtime.maxdad) { cap.runtime.curdad = cap.dad; - cap.runtime.sampcnt -= cap.len*(cap.bits8?4:2) * (1ull<<32); + cap.runtime.sampcntInt -= cap.len*(cap.bits8?4:2); } } } @@ -1929,8 +1934,10 @@ void spu_savestate(EMUFILE &os) os.write_16LE(chan.timer); os.write_16LE(chan.loopstart); os.write_32LE(chan.length); - os.write_64LE(chan.sampcnt); - os.write_64LE(chan.sampinc); + os.write_32LE(chan.sampcntFrac); + os.write_32LE(chan.sampcntInt); + os.write_32LE(chan.sampincFrac); + os.write_32LE(chan.sampincInt); for (int i = 0; i < SPUINTERPOLATION_TAPS; i++) os.write_16LE(chan.pcm16b[i]); os.write_32LE(chan.index); os.write_16LE(chan.x); @@ -1959,7 +1966,8 @@ void spu_savestate(EMUFILE &os) os.write_u8(spu->regs.cap[i].runtime.running); os.write_32LE(spu->regs.cap[i].runtime.curdad); os.write_32LE(spu->regs.cap[i].runtime.maxdad); - os.write_64LE(spu->regs.cap[i].runtime.sampcnt); + os.write_32LE(spu->regs.cap[i].runtime.sampcntFrac); + os.write_32LE(spu->regs.cap[i].runtime.sampcntInt); } for (int i = 0; i < 2; i++) @@ -2001,14 +2009,21 @@ bool spu_loadstate(EMUFILE &is, int size) chan.totlength = chan.length + chan.loopstart; chan.totlength_shifted = chan.totlength << format_shift[chan.format]; if(version >= 7) { - is.read_64LE(chan.sampcnt); - is.read_64LE(chan.sampinc); + is.read_32LE(chan.sampcntFrac); + is.read_32LE(chan.sampcntInt); + is.read_32LE(chan.sampincFrac); + is.read_32LE(chan.sampincInt); } else if (version >= 2) { double temp; - is.read_doubleLE(temp); chan.sampcnt = (s64)(temp * (1ll<<32)); - is.read_doubleLE(temp); chan.sampinc = (s64)(temp * (1ll<<32)); + s64 temp2; + is.read_doubleLE(temp); temp2 = (s64)(temp * (1ll << 32)); + chan.sampcntFrac = (u32)temp2; + chan.sampcntInt = (s32)(temp2 >> 32); + is.read_doubleLE(temp); temp2 = (u64)(temp * (1ull << 32)); // Intentionally unsigned + chan.sampincFrac = (u32)temp2; + chan.sampincInt = (u32)(temp2 >> 32); } else { @@ -2016,8 +2031,10 @@ bool spu_loadstate(EMUFILE &is, int size) // What even is supposed to be happening here? // sampcnt and sampinc were double type before // I even made any changes, so this is broken. - is.read_32LE(*(u32 *)&chan.sampcnt); - is.read_32LE(*(u32 *)&chan.sampinc); + chan.sampcntFrac = 0; + is.read_32LE(chan.sampcntInt); + chan.sampincFrac = 0; + is.read_32LE(chan.sampincInt); } if (version >= 7) { for (int i = 0; i < SPUINTERPOLATION_TAPS; i++) is.read_16LE(chan.pcm16b[i]); @@ -2070,12 +2087,16 @@ bool spu_loadstate(EMUFILE &is, int size) is.read_32LE(spu->regs.cap[i].runtime.curdad); is.read_32LE(spu->regs.cap[i].runtime.maxdad); if (version >= 7) { - is.read_64LE(spu->regs.cap[i].runtime.sampcnt); + is.read_32LE(spu->regs.cap[i].runtime.sampcntFrac); + is.read_32LE(spu->regs.cap[i].runtime.sampcntInt); } else { double temp; - is.read_doubleLE(temp); spu->regs.cap[i].runtime.sampcnt = temp * (1ull << 32); + u64 temp2; + is.read_doubleLE(temp); temp2 = (u64)(temp * (1ull << 32)); + spu->regs.cap[i].runtime.sampcntFrac = (u32)temp2; + spu->regs.cap[i].runtime.sampcntInt = (u32)(temp2 >> 32); } } } diff --git a/desmume/src/SPU.h b/desmume/src/SPU.h index 4951654a4..5ba6d1c96 100644 --- a/desmume/src/SPU.h +++ b/desmume/src/SPU.h @@ -91,8 +91,10 @@ struct channel_struct length(0), totlength(0), totlength_shifted(0), - sampcnt(0), - sampinc(0), + sampcntFrac(0), + sampcntInt(0), + sampincFrac(0), + sampincInt(0), loop_pcm16b(0), index(0), loop_index(0), @@ -114,9 +116,11 @@ struct channel_struct u16 loopstart; u32 length; u32 totlength; - s64 totlength_shifted; - s64 sampcnt; // .32fxp - s64 sampinc; // .32fxp + s32 totlength_shifted; + u32 sampcntFrac; + s32 sampcntInt; + u32 sampincFrac; + u32 sampincInt; s16 pcm16b[SPUINTERPOLATION_TAPS]; // ADPCM specific s16 loop_pcm16b; @@ -193,7 +197,8 @@ public: u8 running; u32 curdad; u32 maxdad; - s64 sampcnt; + u32 sampcntFrac; + u32 sampcntInt; SPUFifo fifo; } runtime; } cap[2]; diff --git a/desmume/src/frontend/windows/soundView.cpp b/desmume/src/frontend/windows/soundView.cpp index 085a44707..786b789a1 100644 --- a/desmume/src/frontend/windows/soundView.cpp +++ b/desmume/src/frontend/windows/soundView.cpp @@ -195,7 +195,7 @@ void SoundView_Refresh(bool forceRedraw) sprintf(buf, "$%04X (%.1f Hz)", thischan.timer, (ARM7_CLOCK/2) / (double)(0x10000 - thischan.timer)); SetDlgItemText(hDlg, IDC_SOUND0TMR+chanId, buf); - sprintf(buf, "samp #%d / #%d", (s32)(thischan.sampcnt >> 32), thischan.totlength << format_shift[thischan.format]); + sprintf(buf, "samp #%d / #%d", thischan.sampcntInt, thischan.totlength_shifted); SetDlgItemText(hDlg, IDC_SOUND0POSLEN+chanId, buf); } else {