diff --git a/plugins/zerospu2/CMakeLists.txt b/plugins/zerospu2/CMakeLists.txt index d905ef238b..b5336c4ad2 100644 --- a/plugins/zerospu2/CMakeLists.txt +++ b/plugins/zerospu2/CMakeLists.txt @@ -25,13 +25,16 @@ endif(CMAKE_BUILD_TYPE STREQUAL Release) set(zerospu2Sources voices.cpp zerodma.cpp - zerospu2.cpp) + zerospu2.cpp + zeroworker.cpp) # zerospu2 headers set(zerospu2Headers misc.h reg.h - zerospu2.h) + zerodma.h + zerospu2.h + zeroworker.h) # zerospu2 Linux sources set(zerospu2LinuxSources diff --git a/plugins/zerospu2/Linux/Linux.cpp b/plugins/zerospu2/Linux/Linux.cpp index 3918bbda80..7db94bf911 100644 --- a/plugins/zerospu2/Linux/Linux.cpp +++ b/plugins/zerospu2/Linux/Linux.cpp @@ -20,7 +20,8 @@ #include -extern "C" { +extern "C" +{ #include "interface.h" #include "support.h" #include "callbacks.h" diff --git a/plugins/zerospu2/Linux/ZeroSPU2.cbp b/plugins/zerospu2/Linux/ZeroSPU2.cbp index 46253f0ca7..be32485ab5 100644 --- a/plugins/zerospu2/Linux/ZeroSPU2.cbp +++ b/plugins/zerospu2/Linux/ZeroSPU2.cbp @@ -105,7 +105,7 @@ - + @@ -155,8 +155,11 @@ + + + diff --git a/plugins/zerospu2/Makefile.am b/plugins/zerospu2/Makefile.am index ee09c0ff6b..c1dab5cab7 100644 --- a/plugins/zerospu2/Makefile.am +++ b/plugins/zerospu2/Makefile.am @@ -22,8 +22,8 @@ libZeroSPU2_LDFLAGS= @SHARED_LDFLAGS@ libZeroSPU2_LDFLAGS+=-Wl,-soname,@ZEROSPU2_SONAME@ libZeroSPU2_LDADD=$(libZeroSPU2_a_OBJECTS) libSoundTouch.a -libZeroSPU2_a_SOURCES = zerospu2.cpp voices.cpp zerodma.cpp -libZeroSPU2_a_SOURCES += zerospu2.h reg.h misc.h +libZeroSPU2_a_SOURCES = zerospu2.cpp voices.cpp zerodma.cpp zeroworker.cpp +libZeroSPU2_a_SOURCES += zerospu2.h reg.h misc.h zerodma.h zeroworker.h libZeroSPU2_a_SOURCES += Linux/interface.c Linux/Linux.cpp Targets/Alsa.cpp Targets/OSS.cpp Targets/SoundTargets.cpp Linux/support.c #SUBDIRS = ../../3rdparty/SoundTouch diff --git a/plugins/zerospu2/Windows/ZeroSPU2_2008.vcproj b/plugins/zerospu2/Windows/ZeroSPU2_2008.vcproj index 8c6a84092c..6c02085f76 100644 --- a/plugins/zerospu2/Windows/ZeroSPU2_2008.vcproj +++ b/plugins/zerospu2/Windows/ZeroSPU2_2008.vcproj @@ -257,6 +257,10 @@ RelativePath="..\zerospu2.cpp" > + + + + + + #include @@ -24,34 +25,20 @@ #include "SoundTouch/SoundTouch.h" #include "SoundTouch/WavFile.h" -void CALLBACK SPU2readDMAMem(u16 *pMem, int size, int core) +void CALLBACK SPU2readDMAMem(u16 *pMem, int size, int channel) { - u32 spuaddr; - s32 i, dma, offset; + u32 spuaddr = C_SPUADDR(channel); - if ( core == 0) - { - dma = 4; - offset = 0; - } - else - { - dma = 7; - offset = 0x0400; - } + SPU2_LOG("SPU2 readDMAMem(%d) size %x, addr: %x\n", channel, size, pMem); - spuaddr = C_SPUADDR(core); - - SPU2_LOG("SPU2 readDMA%dMem size %x, addr: %x\n", dma, size, pMem); - - for (i = 0; i < size; i++) + for (u32 i = 0; i < size; i++) { *pMem++ = *(u16*)(spu2mem + spuaddr); - if ((spu2Rs16(REG_C0_CTRL + offset) & 0x40) && (C_IRQA(core) == spuaddr)) + if (spu2attr(channel).irq && (C_IRQA(channel) == spuaddr)) { - C_SPUADDR_SET(spuaddr, core); - IRQINFO |= (4 * (core + 1)); - SPU2_LOG("SPU2readDMA%dMem:interrupt\n", dma); + C_SPUADDR_SET(spuaddr, channel); + IRQINFO |= (4 * (channel + 1)); + SPU2_LOG("SPU2readDMAMem(%d):interrupt\n", channel); irqCallbackSPU2(); } @@ -60,13 +47,13 @@ void CALLBACK SPU2readDMAMem(u16 *pMem, int size, int core) } spuaddr += 19; //Transfer Local To Host TSAH/L + Data Size + 20 (already +1'd) - C_SPUADDR_SET(spuaddr, core); + C_SPUADDR_SET(spuaddr, channel); // DMA complete - spu2Ru16(REG_C0_SPUSTAT + offset) &= ~0x80; - SPUStartCycle[core] = SPUCycles; - SPUTargetCycle[core] = size; - interrupt |= (1 << (1 + core)); + spu2stat_clear_80(channel); + SPUStartCycle[channel] = SPUCycles; + SPUTargetCycle[channel] = size; + interrupt |= (1 << (1 + channel)); } void CALLBACK SPU2readDMA4Mem(u16 *pMem, int size) @@ -90,108 +77,73 @@ void CALLBACK SPU2readDMA7Mem(u16* pMem, int size) // short-words for right ) has been transferred. Another interrupt occurs at // the end of the transfer. -int ADMASWrite(int core) +int ADMASWrite(int channel) { u32 spuaddr; - ADMA *Adma; - s32 dma, offset; - - if (core == 0) - { - Adma = &Adma4; - dma = 4; - offset = 0; - } - else - { - Adma = &Adma7; - dma = 7; - offset = 0x0400; - } - + ADMA *Adma = &adma[channel]; if (interrupt & 0x2) { - WARN_LOG("%d returning for interrupt\n", dma); + WARN_LOG("ADMASWrite(%d) returning for interrupt\n", channel); return 0; } + if (Adma->AmountLeft <= 0) { - WARN_LOG("%d amount left is 0\n", dma); + WARN_LOG("ADMASWrite(%d) amount left is 0\n", channel); return 1; } assert( Adma->AmountLeft >= 512 ); - spuaddr = C_SPUADDR(core); + spuaddr = C_SPUADDR(channel); + u32 left_addr = spuaddr + 0x2000 + c_offset(channel); + u32 right_addr = left_addr + 0x200; // SPU2 Deinterleaves the Left and Right Channels - memcpy((s16*)(spu2mem + spuaddr + 0x2000 + offset),(s16*)Adma->MemAddr,512); + memcpy((s16*)(spu2mem + left_addr),(s16*)Adma->MemAddr,512); Adma->MemAddr += 256; - memcpy((s16*)(spu2mem + spuaddr + 0x2200 + offset),(s16*)Adma->MemAddr,512); + memcpy((s16*)(spu2mem + right_addr),(s16*)Adma->MemAddr,512); Adma->MemAddr += 256; - if ((spu2Ru16(REG_C0_CTRL + offset) & 0x40) && ((spuaddr + 0x2400) <= C_IRQA(core) && (spuaddr + 0x2400 + 256) >= C_IRQA(core))) + if (spu2attr(channel).irq && (irq_test1(channel, spuaddr) || irq_test2(channel, spuaddr))) { - IRQINFO |= (4 * (core + 1)); - WARN_LOG("ADMA %d Mem access:interrupt\n", dma); - irqCallbackSPU2(); - } - - if ((spu2Ru16(REG_C0_CTRL + offset) & 0x40) && ((spuaddr + 0x2600) <= C_IRQA(core) && (spuaddr + 0x2600 + 256) >= C_IRQA(core))) - { - IRQINFO |= (4 * (core + 1)); - WARN_LOG("ADMA %d Mem access:interrupt\n", dma); + IRQINFO |= (4 * (channel + 1)); + WARN_LOG("ADMAMem access(%d): interrupt\n", channel); irqCallbackSPU2(); } spuaddr = (spuaddr + 256) & 511; - C_SPUADDR_SET(spuaddr, core); + C_SPUADDR_SET(spuaddr, channel); Adma->AmountLeft -= 512; - if (Adma->AmountLeft > 0) - return 0; - else - return 1; + return (Adma->AmountLeft <= 0); } -void SPU2writeDMAMem(u16* pMem, int size, int core) +void SPU2writeDMAMem(u16* pMem, int size, int channel) { u32 spuaddr; - ADMA *Adma; - s32 dma, offset; - - if (core == 0) - { - Adma = &Adma4; - dma = 4; - offset = 0; - } - else - { - Adma = &Adma7; - dma = 7; - offset = 0x0400; - } + ADMA *Adma = &adma[channel]; + s32 offset = (channel == 0) ? 0 : 0x400; - SPU2_LOG("SPU2 writeDMA%dMem size %x, addr: %x(spu2:%x), ctrl: %x, adma: %x\n", \ - dma, size, pMem, C_SPUADDR(core), spu2Ru16(REG_C0_CTRL + offset), spu2Ru16(REG_C0_ADMAS + offset)); + SPU2_LOG("SPU2 writeDMAMem size %x, addr: %x(spu2:%x)"/*, ctrl: %x, adma: %x\n"*/, \ + size, pMem, C_SPUADDR(channel)/*, spu2Ru16(REG_C0_CTRL + offset), spu2Ru16(REG_C0_ADMAS + offset)*/); - if ((spu2Ru16(REG_C0_ADMAS + offset) & 0x1 * (core + 1)) && ((spu2Ru16(REG_C0_CTRL + offset) & 0x30) == 0) && size) + if (spu2admas(channel) && (spu2attr(channel).dma == 0) && size) { if (!Adma->Enabled ) Adma->Index = 0; Adma->MemAddr = pMem; Adma->AmountLeft = size; - SPUTargetCycle[core] = size; - spu2Ru16(REG_C0_SPUSTAT + offset) &= ~0x80; + SPUTargetCycle[channel] = size; + spu2stat_clear_80(channel); if (!Adma->Enabled || (Adma->Index > 384)) { - C_SPUADDR_SET(0, core); - if (ADMASWrite(core)) + C_SPUADDR_SET(0, channel); + if (ADMASWrite(channel)) { - SPUStartCycle[core] = SPUCycles; - interrupt |= (1 << (1 + core)); + SPUStartCycle[channel] = SPUCycles; + interrupt |= (1 << (1 + channel)); } } Adma->Enabled = 1; @@ -200,50 +152,37 @@ void SPU2writeDMAMem(u16* pMem, int size, int core) } #ifdef ZEROSPU2_DEVBUILD - if ((conf.Log && conf.options & OPTION_RECORDING) && (core == 1)) + if ((conf.Log && conf.options & OPTION_RECORDING) && (channel == 1)) LogPacketSound(pMem, 0x8000); #endif - spuaddr = C_SPUADDR(core); + spuaddr = C_SPUADDR(channel); memcpy((u8*)(spu2mem + spuaddr),(u8*)pMem,size << 1); spuaddr += size; - C_SPUADDR_SET(spuaddr, core); + C_SPUADDR_SET(spuaddr, channel); - if ((spu2Ru16(REG_C0_CTRL + offset)&0x40) && (spuaddr < C_IRQA(core) && (C_IRQA(core) <= (spuaddr+0x20)))) + if (spu2attr(channel).irq && (spuaddr < C_IRQA(channel) && (C_IRQA(channel) <= (spuaddr + 0x20)))) { - IRQINFO |= 4 * (core + 1); - SPU2_LOG("SPU2writeDMA%dMem:interrupt\n", dma); + IRQINFO |= 4 * (channel + 1); + SPU2_LOG("SPU2writeDMAMem:interrupt\n"); irqCallbackSPU2(); } if (spuaddr > 0xFFFFE) spuaddr = 0x2800; - C_SPUADDR_SET(spuaddr, core); + C_SPUADDR_SET(spuaddr, channel); - MemAddr[core] += size << 1; - spu2Ru16(REG_C0_SPUSTAT + offset) &= ~0x80; - SPUStartCycle[core] = SPUCycles; - SPUTargetCycle[core] = size; - interrupt |= (1 << (core + 1)); + MemAddr[channel] += size << 1; + spu2stat_clear_80(channel); + SPUStartCycle[channel] = SPUCycles; + SPUTargetCycle[channel] = size; + interrupt |= (1 << (channel + 1)); } -void CALLBACK SPU2interruptDMA(int dma) +void CALLBACK SPU2interruptDMA(int channel) { - s32 core, offset; - - if (dma == 4) - { - core = 0; - offset = 0; - } - else - { - core = 1; - offset = 0x0400; - } - - SPU2_LOG("SPU2 interruptDMA%d\n", dma); - spu2Rs16(REG_C0_CTRL + offset) &= ~0x30; - spu2Ru16(REG_C0_SPUSTAT + offset) |= 0x80; + SPU2_LOG("SPU2 interruptDMA(%d)\n", channel); + spu2attr(channel).dma = 0; + spu2stat_set_80(channel); } #ifndef ENABLE_NEW_IOPDMA_SPU2 diff --git a/plugins/zerospu2/zerodma.h b/plugins/zerospu2/zerodma.h new file mode 100644 index 0000000000..880b0ee7ca --- /dev/null +++ b/plugins/zerospu2/zerodma.h @@ -0,0 +1,23 @@ +#ifndef ZERODMA_H_INCLUDED +#define ZERODMA_H_INCLUDED + +#include + +#define SPU2defs +#include "PS2Edefs.h" + +#include "reg.h" +#include "misc.h" + +static __forceinline bool irq_test1(u32 ch, u32 addr) +{ + return ((addr + 0x2400) <= C_IRQA(ch) && (addr + 0x2500) >= C_IRQA(ch)); +} + +static __forceinline bool irq_test2(u32 ch, u32 addr) +{ + return ((addr + 0x2600) <= C_IRQA(ch) && (addr + 0x2700) >= C_IRQA(ch)); +} + + +#endif // ZERODMA_H_INCLUDED diff --git a/plugins/zerospu2/zerospu2.cpp b/plugins/zerospu2/zerospu2.cpp index 7443126aea..1118bb9ad5 100644 --- a/plugins/zerospu2/zerospu2.cpp +++ b/plugins/zerospu2/zerospu2.cpp @@ -35,8 +35,7 @@ char libraryName[256]; FILE *spu2Log; Config conf; -ADMA Adma4; -ADMA Adma7; +ADMA adma[2]; u32 MemAddr[2]; u32 g_nSpuInit = 0; @@ -52,8 +51,8 @@ s32 iFMod[NSSIZE]; s32 s_buffers[NSSIZE][2]; // left and right buffers // mixer thread variables -static bool s_bThreadExit = true; -static s32 s_nDropPacket = 0; +bool s_bThreadExit = true; +s32 s_nDropPacket = 0; string s_strIniPath( "inis/" ); #ifdef _WIN32 @@ -67,27 +66,25 @@ pthread_t s_threadSPU2; void* SPU2ThreadProc(void*); #endif -static AUDIOBUFFER s_pAudioBuffers[NSPACKETS]; -static s32 s_nCurBuffer = 0, s_nQueuedBuffers = 0; -static s16* s_pCurOutput = NULL; -static u32 g_startcount=0xffffffff; -static u32 g_packetcount=0; +AUDIOBUFFER s_pAudioBuffers[NSPACKETS]; +s32 s_nCurBuffer = 0, s_nQueuedBuffers = 0; +s16* s_pCurOutput = NULL; +u32 g_startcount=0xffffffff; +u32 g_packetcount=0; // time stretch variables soundtouch::SoundTouch* pSoundTouch=NULL; -WavOutFile* g_pWavRecord=NULL; // used for recording +extern WavOutFile* g_pWavRecord; // used for recording -static u64 s_GlobalTimeStamp = 0; -static s32 s_nDurations[64]={0}; -static s32 s_nCurDuration=0; -static s32 s_nTotalDuration=0; +u64 s_GlobalTimeStamp = 0; +s32 s_nDurations[64]={0}; +s32 s_nCurDuration=0; +s32 s_nTotalDuration=0; s32 SPUCycles = 0, SPUWorkerCycles = 0; s32 SPUStartCycle[2]; s32 SPUTargetCycle[2]; -s32 g_logsound=0; - int ADMASWrite(int c); void InitADSR(); @@ -100,14 +97,6 @@ void (*irqCallbackDMA7)()=0; #endif uptr g_pDMABaseAddr=0; - -const s32 f[5][2] = { - { 0, 0 }, - { 60, 0 }, - { 115, -52 }, - { 98, -55 }, - { 122, -60 } }; - u32 RateTable[160]; // channels and voices @@ -266,12 +255,9 @@ s32 CALLBACK SPU2init() voices[i+24].memoffset = 0x400; // init each channel - for (u32 i = 0; i < ArraySize(voices); ++i) { - voices[i].chanid = i; - voices[i].pLoop = voices[i].pStart = voices[i].pCurr = (u8*)spu2mem; - - voices[i].pvoice = (_SPU_VOICE*)((u8*)spu2regs+voices[i].memoffset)+(i%24); - voices[i].ADSRX.SustainLevel = 1024; // -> init sustain + for (u32 i = 0; i < ArraySize(voices); ++i) + { + voices[i].init(i); } return 0; @@ -319,7 +305,7 @@ s32 CALLBACK SPU2open(void *pDsp) // initialize the audio buffers for (u32 i = 0; i < ArraySize(s_pAudioBuffers); ++i) { - s_pAudioBuffers[i].pbuf = (u8*)_aligned_malloc(4*NSSIZE*NSFRAMES, 16); // 4 bytes for each sample + s_pAudioBuffers[i].pbuf = (u8*)_aligned_malloc(4 * NS_TOTAL_SIZE, 16); // 4 bytes for each sample s_pAudioBuffers[i].len = 0; } @@ -424,7 +410,7 @@ void CALLBACK SPU2async(u32 cycle) if ( g_nSpuInit ) { - while( SPUCycles-SPUWorkerCycles > 0 && CYCLES_PER_MS < SPUCycles-SPUWorkerCycles ) + while((SPUCycles-SPUWorkerCycles > 0) && (CYCLES_PER_MS < (SPUCycles - SPUWorkerCycles))) { SPU2Worker(); SPUWorkerCycles += CYCLES_PER_MS; @@ -563,58 +549,58 @@ s32 MixADSR(VOICE_PROCESSED* pvoice) // MIX ADSR return 0; } -void MixChannels(s32 core) +void MixChannels(s32 channel) { // mix all channels - s32 c_offset = 0x0400 * core; - s32 dma, left_vol, right_vol; + s32 left_vol, right_vol; ADMA *Adma; - if (core == 0) + if (channel == 0) { - Adma = &Adma4; - dma = 4; + Adma = &adma[0]; left_vol = REG_C0_BVOLL; right_vol = REG_C0_BVOLR; } else { - Adma = &Adma7; - dma = 7; + Adma = &adma[1]; left_vol = REG_C1_BVOLL; right_vol = REG_C1_BVOLR; } - if ((spu2Ru16(REG_C0_MMIX + c_offset) & 0xF0) && (spu2Ru16(REG_C0_ADMAS + c_offset) & (0x1 + core))) + if ((spu2mmix(channel) & 0xF0) && spu2admas(channel)) { - for (s32 ns=0;nsIndex]*(s32)spu2Ru16(left_vol))>>16; - if ((spu2Ru16(REG_C0_MMIX + c_offset) & 0x40)) - s_buffers[ns][1] += (((s16*)spu2mem)[0x2200 + c_offset +Adma->Index]*(s32)spu2Ru16(right_vol))>>16; + u16 left_reg = 0x2000 + c_offset(channel) + Adma->Index; + u16 right_reg = left_reg + 0x200; + + if ((spu2mmix(channel) & 0x80)) + s_buffers[ns][0] += (((s16*)spu2mem)[left_reg]*(s32)spu2Ru16(left_vol))>>16; + if ((spu2mmix(channel) & 0x40)) + s_buffers[ns][1] += (((s16*)spu2mem)[right_reg]*(s32)spu2Ru16(right_vol))>>16; Adma->Index +=1; - MemAddr[core] += 4; + MemAddr[channel] += 4; if (Adma->Index == 128 || Adma->Index == 384) { - if (ADMASWrite(core)) + if (ADMASWrite(channel)) { - if (interrupt & (0x2 * (core + 1))) + if (interrupt & (0x2 * (channel + 1))) { - interrupt &= ~(0x2 * (core + 1)); + interrupt &= ~(0x2 * (channel + 1)); WARN_LOG("Stopping double interrupt DMA7\n"); } #ifndef ENABLE_NEW_IOPDMA_SPU2 - if (core == 0) + if (channel == 0) irqCallbackDMA4(); else irqCallbackDMA7(); #endif } - if (core == 1) Adma->Enabled = 2; + if (channel == 1) Adma->Enabled = 2; } if (Adma->Index == 512) @@ -626,248 +612,6 @@ void MixChannels(s32 core) } } -// simulate SPU2 for 1ms -void SPU2Worker() -{ - u8* start; - u32 nSample; - s32 ch, predict_nr, shift_factor, flags; - - // assume s_buffers are zeroed out - if ( dwNewChannel2[0] || dwNewChannel2[1] ) - s_pAudioBuffers[s_nCurBuffer].newchannels++; - - VOICE_PROCESSED* pChannel=voices; - for (ch=0; ch < SPU_NUMBER_VOICES; ch++, pChannel++) // loop em all... we will collect 1 ms of sound of each playing channel - { - if (pChannel->bNew) - { - pChannel->StartSound(); // start new sound - dwEndChannel2[ch/24]&=~(1<<(ch%24)); // clear end channel bit - dwNewChannel2[ch/24]&=~(1<<(ch%24)); // clear channel bit - } - - if (!pChannel->bOn) continue; - - if (pChannel->iActFreq!=pChannel->iUsedFreq) // new psx frequency? - pChannel->VoiceChangeFrequency(); - - // loop until 1 ms of data is reached - s32 ns = 0; - - while(nsbFMod==1 && iFMod[ns]) pChannel->FModChangeFrequency(ns); - - while(pChannel->spos >= 0x10000 ) - { - if (pChannel->iSBPos == 28) // 28 reached? - { - start=pChannel->pCurr; // set up the current pos - - // special "stop" sign - fixme - an *unsigned* -1? - if (start == (u8*)-1) //!pChannel->bOn - { - pChannel->bOn=false; // -> turn everything off - pChannel->ADSRX.lVolume=0; - pChannel->ADSRX.EnvelopeVol=0; - goto ENDX; // -> and done for this channel - } - - predict_nr=(s32)start[0]; - shift_factor=predict_nr&0xf; - predict_nr >>= 4; - flags=(s32)start[1]; - start += 2; - - pChannel->iSBPos=0; - - // decode the 16byte packet - s_1=pChannel->s_1; - s_2=pChannel->s_2; - - for (nSample=0; nSample<28; ++start) - { - s32 s; - s32 d = (s32)*start; - - s = ((d & 0xf)<<12); - if (s & 0x8000) s |= 0xffff0000; - - fa = (s >> shift_factor); - fa += ((s_1 * f[predict_nr][0]) >> 6) + ((s_2 * f[predict_nr][1]) >> 6); - s_2 = s_1; - s_1 = fa; - pChannel->SB[nSample++]=fa; - - s = ((d & 0xf0) << 8); - if (s & 0x8000) s|=0xffff0000; - - fa = (s>>shift_factor); - fa += ((s_1 * f[predict_nr][0])>>6) + ((s_2 * f[predict_nr][1]) >> 6); - s_2 = s_1; - s_1 = fa; - pChannel->SB[nSample++]=fa; - } - - // irq occurs no matter what core access the address - for (s32 core = 0; core < 2; ++core) - { - if (((SPU_CONTROL_*)(spu2regs + (0x400 * core) + REG_C0_CTRL))->irq) // some callback and irq active? - { - // if irq address reached or irq on looping addr, when stop/loop flag is set - u8* pirq = (u8*)pSpuIrq[core]; - if ((pirq > (start - 16) && pirq <= start) || ((flags & 1) && (pirq > (pChannel->pLoop - 16) && pirq <= pChannel->pLoop))) - { - IRQINFO |= 4<bIgnoreLoop)) - pChannel->pLoop=start-16; // loop address - - if (flags&1) // 1: stop/loop - { - // We play this block out first... - dwEndChannel2[ch/24]|=(1<<(ch%24)); - - if (flags!=3 || pChannel->pLoop==NULL) - { // and checking if pLoop is set avoids crashes, yeah - start = (u8*)-1; - pChannel->bStop = true; - pChannel->bIgnoreLoop = false; - } - else - { - start = pChannel->pLoop; - } - } - - pChannel->pCurr=start; // store values for next cycle - pChannel->s_1=s_1; - pChannel->s_2=s_2; - } - - fa=pChannel->SB[pChannel->iSBPos++]; // get sample data - pChannel->StoreInterpolationVal(fa); - pChannel->spos -= 0x10000; - } - - if (pChannel->bNoise) - fa=pChannel->iGetNoiseVal(); // get noise val - else - fa=pChannel->iGetInterpolationVal(); // get sample val - - s32 sval = (MixADSR(pChannel) * fa) / 1023; // mix adsr - - if (pChannel->bFMod == 2) // fmod freq channel - { - iFMod[ns] = sval; // -> store 1T sample data, use that to do fmod on next channel - } - else - { - if (pChannel->bVolumeL) - s_buffers[ns][0]+=(sval * pChannel->leftvol)>>14; - - if (pChannel->bVolumeR) - s_buffers[ns][1]+=(sval * pChannel->rightvol)>>14; - } - - // go to the next packet - ns++; - pChannel->spos += pChannel->sinc; - } -ENDX: - ; - } - - // mix all channels - MixChannels(0); - MixChannels(1); - - if ( g_bPlaySound ) - { - assert( s_pCurOutput != NULL); - - for (s32 ns=0; ns= 4 * NSSIZE * NSFRAMES) - { - - if ( conf.options & OPTION_RECORDING ) - { - static s32 lastrectime = 0; - if (timeGetTime() - lastrectime > 5000) - { - WARN_LOG("ZeroSPU2: recording\n"); - lastrectime = timeGetTime(); - } - LogRawSound(s_pAudioBuffers[s_nCurBuffer].pbuf, 4, s_pAudioBuffers[s_nCurBuffer].pbuf+2, 4, NSSIZE*NSFRAMES); - } - - if ( s_nQueuedBuffers >= ArraySize(s_pAudioBuffers)-1 ) - { - //ZeroSPU2: dropping packets! game too fast - s_nDropPacket += NSFRAMES; - s_GlobalTimeStamp = GetMicroTime(); - } - else { - // submit to final mixer -#ifdef ZEROSPU2_DEVBUILD - if ( g_logsound ) - LogRawSound(s_pAudioBuffers[s_nCurBuffer].pbuf, 4, s_pAudioBuffers[s_nCurBuffer].pbuf+2, 4, NSSIZE*NSFRAMES); -#endif - if ( g_startcount == 0xffffffff ) - { - g_startcount = timeGetTime(); - g_packetcount = 0; - } - - if ( conf.options & OPTION_TIMESTRETCH ) - { - u64 newtime = GetMicroTime(); - if ( s_GlobalTimeStamp == 0 ) - s_GlobalTimeStamp = newtime-NSFRAMES*1000; - u32 newtotal = s_nTotalDuration-s_nDurations[s_nCurDuration]; - u32 duration = (u32)(newtime-s_GlobalTimeStamp); - s_nDurations[s_nCurDuration] = duration; - s_nTotalDuration = newtotal + duration; - s_nCurDuration = (s_nCurDuration+1)%ArraySize(s_nDurations); - s_GlobalTimeStamp = newtime; - s_pAudioBuffers[s_nCurBuffer].timestamp = timeGetTime(); - s_pAudioBuffers[s_nCurBuffer].avgtime = s_nTotalDuration/ArraySize(s_nDurations); - } - - s_pAudioBuffers[s_nCurBuffer].len = 4*NSSIZE*NSFRAMES; - InterlockedExchangeAdd((long*)&s_nQueuedBuffers, 1); - - s_nCurBuffer = (s_nCurBuffer+1)%ArraySize(s_pAudioBuffers); - s_pAudioBuffers[s_nCurBuffer].newchannels = 0; // reset - } - - // restart - s_pCurOutput = (s16*)s_pAudioBuffers[s_nCurBuffer].pbuf; - } - } -} - // resamples pStereoSamples void ResampleLinear(s16* pStereoSamples, s32 oldsamples, s16* pNewSamples, s32 newsamples) { @@ -885,10 +629,53 @@ void ResampleLinear(s16* pStereoSamples, s32 oldsamples, s16* pNewSamples, s32 n } } -static __aligned16 s16 s_ThreadBuffer[NSSIZE*NSFRAMES*2*5]; +static __aligned16 s16 s_ThreadBuffer[NS_TOTAL_SIZE * 2 * 5]; // SoundTouch's INTEGER system is broken these days, so we'll need this to do float conversions... -static __aligned16 float s_floatBuffer[NSSIZE*NSFRAMES*2*5]; +static __aligned16 float s_floatBuffer[NS_TOTAL_SIZE * 2 * 5]; + +static __forceinline u32 GetNewSamples(s32 nReadBuf) +{ + u32 NewSamples = s_pAudioBuffers[nReadBuf].avgtime / 1000; + s32 bytesbuf = SoundGetBytesBuffered() * 10; + + if (bytesbuf < MaxBuffer) + { + NewSamples += 1; + } + else if (bytesbuf > MaxBuffer * 5) + { + // check the current timestamp, if too far apart, speed up audio + //WARN_LOG("making faster %d\n", timeGetTime() - s_pAudioBuffers[nReadBuf].timestamp); + NewSamples = NewSamples - ((bytesbuf - 400000) / 100000); + } + + if (s_nDropPacket > 0) + { + s_nDropPacket--; + NewSamples -= 1; + } + + return min(NewSamples * NSSIZE, NS_TOTAL_SIZE * 3); +} + +static __forceinline void ExtractSamples() +{ + // extract 2*NSFRAMES ms at a time + s32 nOutSamples; + + do + { + nOutSamples = pSoundTouch->receiveSamples(s_floatBuffer, NS_TOTAL_SIZE * 5); + if ( nOutSamples > 0 ) + { + for( s32 sx=0; sx 72000 ) @@ -917,7 +703,7 @@ void* SPU2ThreadProc(void* lpParam) //Bytes buffered Sleep(1); - if ( s_bThreadExit ) return NULL; + if (s_bThreadExit) return NULL; } } else @@ -929,63 +715,28 @@ void* SPU2ThreadProc(void* lpParam) } } - - //s32 ps2delay = timeGetTime() - s_pAudioBuffers[nReadBuf].timestamp; - s32 NewSamples = s_pAudioBuffers[nReadBuf].avgtime; - - if ( (conf.options & OPTION_TIMESTRETCH) ) + if (conf.options & OPTION_TIMESTRETCH) { - - s32 bytesbuf = SoundGetBytesBuffered(); - if ( bytesbuf < MaxBuffer / 10 ) - NewSamples += 1000; - // check the current timestamp, if too far apart, speed up audio - else if ( bytesbuf > MaxBuffer / 2 ) - { - //WARN_LOG("making faster %d\n", timeGetTime() - s_pAudioBuffers[nReadBuf].timestamp); - NewSamples -= (bytesbuf-40000)/10;//*(ps2delay-NewSamples*8/1000); - } - - if ( s_nDropPacket > 0 ) - { - s_nDropPacket--; - NewSamples -= 1000; - } - - NewSamples *= NSSIZE; - NewSamples /= 1000; - - NewSamples = min(NewSamples, NSFRAMES * NSSIZE * 3); - - s32 oldsamples = s_pAudioBuffers[nReadBuf].len / 4; + u32 NewSamples = GetNewSamples(nReadBuf); + s32 OldSamples = s_pAudioBuffers[nReadBuf].len / 4; if ((nReadBuf & 3) == 0) // wow, this if statement makes the whole difference - pSoundTouch->setTempoChange(100.0f*(float)oldsamples/(float)NewSamples - 100.0f); + { + //SPU2_LOG("OldSamples = %d; NewSamples = %d/n", OldSamples, NewSamples); + float percent = ((float)OldSamples/(float)NewSamples - 1.0f) * 100.0f; + pSoundTouch->setTempoChange(percent); + } - for( s32 sx=0; sxputSamples(s_floatBuffer, oldsamples); - - // extract 2*NSFRAMES ms at a time - s32 nOutSamples; - - do - { - nOutSamples = pSoundTouch->receiveSamples(s_floatBuffer, NSSIZE * NSFRAMES * 5); - if ( nOutSamples > 0 ) - { - for( s32 sx=0; sxputSamples(s_floatBuffer, OldSamples); + ExtractSamples(); } else + { SoundFeedVoiceData(s_pAudioBuffers[nReadBuf].pbuf, s_pAudioBuffers[nReadBuf].len); + } // don't go to the next buffer unless there is more data buffered nReadBuf = (nReadBuf+1)%ArraySize(s_pAudioBuffers); @@ -1147,7 +898,7 @@ void CALLBACK SPU2write(u32 mem, u16 value) pvoice->iStartAddr=(((u32)value&0x3f)<<16)|(pvoice->iStartAddr&0xFFFF); pvoice->pStart=(u8*)(spu2mem+pvoice->iStartAddr); break; - case 0x1C2: + case REG_VA_SSA + 2: pvoice->iStartAddr=(pvoice->iStartAddr & 0x3f0000) | (value & 0xFFFF); pvoice->pStart=(u8*)(spu2mem+pvoice->iStartAddr); break; @@ -1156,7 +907,7 @@ void CALLBACK SPU2write(u32 mem, u16 value) pvoice->pLoop=(u8*)(spu2mem+pvoice->iLoopAddr); pvoice->bIgnoreLoop=pvoice->iLoopAddr>0; break; - case 0x1C6: + case REG_VA_LSAX + 2: pvoice->iLoopAddr=(pvoice->iLoopAddr& 0x3f0000) | (value & 0xFFFF); pvoice->pLoop=(u8*)(spu2mem+pvoice->iLoopAddr); pvoice->bIgnoreLoop=pvoice->iLoopAddr>0; @@ -1165,7 +916,7 @@ void CALLBACK SPU2write(u32 mem, u16 value) // unused... check if it gets written as well pvoice->iNextAddr=(((u32)value&0x3f)<<16)|(pvoice->iNextAddr&0xFFFF); break; - case 0x1CA: + case REG_VA_NAX + 2: // unused... check if it gets written as well pvoice->iNextAddr=(pvoice->iNextAddr & 0x3f0000) | (value & 0xFFFF); break; @@ -1182,7 +933,7 @@ void CALLBACK SPU2write(u32 mem, u16 value) spu2mem[spuaddr] = value; spuaddr++; - if ((spu2Ru16(REG_C0_CTRL)&0x40) && (C0_IRQA() == spuaddr)) + if (spu2attr0.irq && (C0_IRQA() == spuaddr)) { IRQINFO |= 4; SPU2_LOG("SPU2write:C0_CPUDATA interrupt\n"); @@ -1192,8 +943,8 @@ void CALLBACK SPU2write(u32 mem, u16 value) if (spuaddr>0xFFFFE) spuaddr = 0x2800; C0_SPUADDR_SET(spuaddr); - spu2Ru16(REG_C0_SPUSTAT)&=~0x80; - spu2Ru16(REG_C0_CTRL)&=~0x30; + spu2stat_clear_80(0); + spu2attr1.irq = 0; break; case REG_C1_SPUDATA: @@ -1201,7 +952,7 @@ void CALLBACK SPU2write(u32 mem, u16 value) spu2mem[spuaddr] = value; spuaddr++; - if ((spu2Ru16(REG_C1_CTRL)&0x40) && (C1_IRQA() == spuaddr)) + if (spu2attr1.irq && (C1_IRQA() == spuaddr)) { IRQINFO |= 8; SPU2_LOG("SPU2write:C1_CPUDATA interrupt\n"); @@ -1211,8 +962,8 @@ void CALLBACK SPU2write(u32 mem, u16 value) if (spuaddr>0xFFFFE) spuaddr = 0x2800; C1_SPUADDR_SET(spuaddr); - spu2Ru16(REG_C1_SPUSTAT)&=~0x80; - spu2Ru16(REG_C1_CTRL)&=~0x30; + spu2stat_clear_80(1); + spu2attr0.irq = 0; break; case REG_C0_IRQA_HI: @@ -1301,38 +1052,37 @@ u16 CALLBACK SPU2read(u32 mem) if ((r>=REG_VA_SSA && r=0x05c0 && r<0x06E0)) // some channel info? { - s32 ch=0; + s32 ch = 0; u32 rx = r; - if (rx >=0x400) + if (rx >= 0x400) { - ch=24; - rx-=0x400; + ch = 24; + rx -= 0x400; } - ch+=((rx-0x1c0)/12); - rx-=(ch%24)*12; + ch += ((rx - 0x1c0) / 12); + rx -= (ch % 24) * 12; VOICE_PROCESSED* pvoice = &voices[ch]; - // Note - can we generalize this? switch(rx) { case REG_VA_SSA: ret = ((((uptr)pvoice->pStart-(uptr)spu2mem)>>17)&0x3F); break; - case 0x1C2: + case REG_VA_SSA + 2: ret = ((((uptr)pvoice->pStart-(uptr)spu2mem)>>1)&0xFFFF); break; case REG_VA_LSAX: ret = ((((uptr)pvoice->pLoop-(uptr)spu2mem)>>17)&0x3F); break; - case 0x1C6: + case REG_VA_LSAX + 2: ret = ((((uptr)pvoice->pLoop-(uptr)spu2mem)>>1)&0xFFFF); break; case REG_VA_NAX: ret = ((((uptr)pvoice->pCurr-(uptr)spu2mem)>>17)&0x3F); break; - case 0x1CA: + case REG_VA_NAX + 2: ret = ((((uptr)pvoice->pCurr-(uptr)spu2mem)>>1)&0xFFFF); break; } @@ -1350,6 +1100,7 @@ u16 CALLBACK SPU2read(u32 mem) if (spuaddr > 0xfffff) spuaddr=0; C0_SPUADDR_SET(spuaddr); break; + case REG_C1_SPUDATA: spuaddr = C1_SPUADDR(); ret = spu2mem[spuaddr]; @@ -1416,69 +1167,6 @@ s32 CALLBACK SPU2test() return 0; } -#define SetPacket(s) \ -{ \ - if (s & 0x8000) s|=0xffff0000; \ - fa = (s >> shift_factor); \ - fa += ((s_1 * f[predict_nr][0]) >> 6) + ((s_2 * f[predict_nr][1]) >> 6); \ - s_2 = s_1; \ - s_1 = fa; \ - buf[nSample++] = fa; \ -} - -// size is in bytes -void LogPacketSound(void* packet, s32 memsize) -{ - u16 buf[28]; - - u8* pstart = (u8*)packet; - s32 s_1 = 0, s_2=0; - - for (s32 i = 0; i < memsize; i += 16) - { - s32 predict_nr=(s32)pstart[0]; - s32 shift_factor=predict_nr&0xf; - predict_nr >>= 4; - pstart += 2; - - for (s32 nSample=0;nSample<28; ++pstart) - { - s32 d=(s32)*pstart; - s32 s, fa; - - s =((d & 0xf) << 12); - SetPacket(s); - - s=((d & 0xf0) << 8); - SetPacket(s); - } - - LogRawSound(buf, 2, buf, 2, 28); - } -} - -void LogRawSound(void* pleft, s32 leftstride, void* pright, s32 rightstride, s32 numsamples) -{ - if (g_pWavRecord == NULL ) - g_pWavRecord = new WavOutFile(RECORD_FILENAME, SAMPLE_RATE, 16, 2); - - u8* left = (u8*)pleft; - u8* right = (u8*)pright; - static vector tempbuf; - - tempbuf.resize(2 * numsamples); - - for (s32 i = 0; i < numsamples; ++i) - { - tempbuf[2*i+0] = *(s16*)left; - tempbuf[2*i+1] = *(s16*)right; - left += leftstride; - right += rightstride; - } - - g_pWavRecord->write(&tempbuf[0], numsamples*2); -} - int CALLBACK SPU2setupRecording(int start, void* pData) { LOG_CALLBACK("SPU2setupRecording()\n"); @@ -1499,10 +1187,9 @@ int CALLBACK SPU2setupRecording(int start, void* pData) void save_data(freezeData *data) { SPU2freezeData *spud; - s32 i; spud = (SPU2freezeData*)data->data; - spud->version = 0x70000001; + spud->version = 0x70000002; memcpy(spud->spu2regs, spu2regs, 0x10000); memcpy(spud->spu2mem, spu2mem, 0x200000); @@ -1510,8 +1197,8 @@ void save_data(freezeData *data) spud->nSpuIrq[0] = (s32)(pSpuIrq[0] - spu2mem); spud->nSpuIrq[1] = (s32)(pSpuIrq[1] - spu2mem); - memcpy(spud->dwNewChannel2, dwNewChannel2, 4*2); - memcpy(spud->dwEndChannel2, dwEndChannel2, 4*2); + memcpy(spud->dwNewChannel2, dwNewChannel2, sizeof(dwNewChannel2)); + memcpy(spud->dwEndChannel2, dwEndChannel2, sizeof(dwEndChannel2)); spud->dwNoiseVal = dwNoiseVal; spud->interrupt = interrupt; @@ -1519,10 +1206,10 @@ void save_data(freezeData *data) memcpy(spud->iFMod, iFMod, sizeof(iFMod)); memcpy(spud->MemAddr, MemAddr, sizeof(MemAddr)); - spud->adma[0] = Adma4; - spud->adma[1] = Adma7; - spud->Adma4MemAddr = (u32)((uptr)Adma4.MemAddr - g_pDMABaseAddr); - spud->Adma7MemAddr = (u32)((uptr)Adma7.MemAddr - g_pDMABaseAddr); + spud->adma[0] = adma[0]; + spud->adma[1] = adma[1]; + spud->AdmaMemAddr[0] = (u32)((uptr)adma[0].MemAddr - g_pDMABaseAddr); + spud->AdmaMemAddr[1] = (u32)((uptr)adma[1].MemAddr - g_pDMABaseAddr); spud->SPUCycles = SPUCycles; spud->SPUWorkerCycles = SPUWorkerCycles; @@ -1530,7 +1217,7 @@ void save_data(freezeData *data) memcpy(spud->SPUStartCycle, SPUStartCycle, sizeof(SPUStartCycle)); memcpy(spud->SPUTargetCycle, SPUTargetCycle, sizeof(SPUTargetCycle)); - for (i = 0; i < ArraySize(s_nDurations); ++i) + for (u32 i = 0; i < ArraySize(s_nDurations); ++i) { s_nDurations[i] = NSFRAMES*1000; } @@ -1539,7 +1226,7 @@ void save_data(freezeData *data) s_nCurDuration = 0; spud->voicesize = SPU_VOICE_STATE_SIZE; - for (i = 0; i < ArraySize(voices); ++i) + for (u32 i = 0; i < ArraySize(voices); ++i) { memcpy(&spud->voices[i], &voices[i], SPU_VOICE_STATE_SIZE); spud->voices[i].pStart = (u8*)((uptr)voices[i].pStart-(uptr)spu2mem); @@ -1555,11 +1242,10 @@ void save_data(freezeData *data) void load_data(freezeData *data) { SPU2freezeData *spud; - s32 i; spud = (SPU2freezeData*)data->data; - if (spud->version != 0x70000001) + if (spud->version != 0x70000002) { ERROR_LOG("zerospu2: Sound data either corrupted or from another plugin. Ignoring.\n"); return; @@ -1571,8 +1257,8 @@ void load_data(freezeData *data) pSpuIrq[0] = spu2mem + spud->nSpuIrq[0]; pSpuIrq[1] = spu2mem + spud->nSpuIrq[1]; - memcpy(dwNewChannel2, spud->dwNewChannel2, 4*2); - memcpy(dwEndChannel2, spud->dwEndChannel2, 4*2); + memcpy(dwNewChannel2, spud->dwNewChannel2, sizeof(dwNewChannel2)); + memcpy(dwEndChannel2, spud->dwEndChannel2, sizeof(dwEndChannel2)); dwNoiseVal = spud->dwNoiseVal; interrupt = spud->interrupt; @@ -1580,10 +1266,10 @@ void load_data(freezeData *data) memcpy(iFMod, spud->iFMod, sizeof(iFMod)); memcpy(MemAddr, spud->MemAddr, sizeof(MemAddr)); - Adma4 = spud->adma[0]; - Adma7 = spud->adma[1]; - Adma4.MemAddr = (u16*)(g_pDMABaseAddr+spud->Adma4MemAddr); - Adma7.MemAddr = (u16*)(g_pDMABaseAddr+spud->Adma7MemAddr); + adma[0] = spud->adma[0]; + adma[1] = spud->adma[1]; + adma[0].MemAddr = (u16*)(g_pDMABaseAddr+spud->AdmaMemAddr[0]); + adma[1].MemAddr = (u16*)(g_pDMABaseAddr+spud->AdmaMemAddr[1]); SPUCycles = spud->SPUCycles; SPUWorkerCycles = spud->SPUWorkerCycles; @@ -1591,7 +1277,7 @@ void load_data(freezeData *data) memcpy(SPUStartCycle, spud->SPUStartCycle, sizeof(SPUStartCycle)); memcpy(SPUTargetCycle, spud->SPUTargetCycle, sizeof(SPUTargetCycle)); - for (i = 0; i < ArraySize(voices); ++i) + for (u32 i = 0; i < ArraySize(voices); ++i) { memcpy(&voices[i], &spud->voices[i], min((s32)SPU_VOICE_STATE_SIZE, spud->voicesize)); voices[i].pStart = (u8*)((uptr)spud->voices[i].pStart+(uptr)spu2mem); @@ -1602,7 +1288,7 @@ void load_data(freezeData *data) s_GlobalTimeStamp = 0; g_startcount = 0xffffffff; - for (s32 i = 0; i < ArraySize(s_nDurations); ++i) + for (u32 i = 0; i < ArraySize(s_nDurations); ++i) { s_nDurations[i] = NSFRAMES*1000; } diff --git a/plugins/zerospu2/zerospu2.h b/plugins/zerospu2/zerospu2.h index 1e81c9c161..361bbfb299 100644 --- a/plugins/zerospu2/zerospu2.h +++ b/plugins/zerospu2/zerospu2.h @@ -46,6 +46,7 @@ extern string s_strIniPath; #define LOG_CALLBACK 0&& #endif +#define ZEROSPU2_DEVBUILD #ifdef ZEROSPU2_DEVBUILD #define SPU2_LOG __Log //dev mode #else @@ -60,36 +61,43 @@ extern string s_strIniPath; #define SPU2_BUILD 4 // increase that with each version #define SPU2_MINOR 6 -#define OPTION_TIMESTRETCH 1 // stretches samples without changing pitch to reduce cracking -#define OPTION_REALTIME 2 // sync to real time instead of ps2 time -#define OPTION_MUTE 4 // don't output anything -#define OPTION_RECORDING 8 +enum zerospu2_options +{ + OPTION_TIMESTRETCH = 1, // stretches samples without changing pitch to reduce cracking + OPTION_REALTIME = 2, // sync to real time instead of ps2 time + OPTION_MUTE = 4, // don't output anything + OPTION_RECORDING = 8 +}; // ADSR constants -#define ATTACK_MS 494L -#define DECAYHALF_MS 286L -#define DECAY_MS 572L -#define SUSTAIN_MS 441L -#define RELEASE_MS 437L +enum adsr_ms +{ + ATTACK_MS = 494L, + DECAYHALF_MS = 286L, + DECAY_MS = 572L, + SUSTAIN_MS = 441L, + RELEASE_MS = 437L +}; -#define CYCLES_PER_MS (36864000/1000) +const s32 CYCLES_PER_MS = 36864000 / 1000; -#define AUDIO_BUFFER 2048 +const u32 AUDIO_BUFFER = 2048; -#define NSSIZE 48 // ~ 1 ms of data -#define NSFRAMES 16 // gather at least NSFRAMES of NSSIZE before submitting -#define NSPACKETS 24 +const u32 NSSIZE = 48; // ~ 1 ms of data +const u32 NSFRAMES = 16; // gather at least NSFRAMES of NSSIZE before submitting +const u32 NSPACKETS = 24; +const u32 NS_TOTAL_SIZE = NSSIZE * NSFRAMES; -#define SPU_NUMBER_VOICES 48 +const u32 SPU_NUMBER_VOICES = 48; -#define SAMPLE_RATE 48000L +const u32 SAMPLE_RATE = 48000L; #define RECORD_FILENAME "zerospu2.wav" extern s8 *spu2regs; extern u16* spu2mem; extern s32 iFMod[NSSIZE]; extern u32 MemAddr[2]; -extern u32 dwNoiseVal; // global noise generator +extern u32 dwNoiseVal; // global noise generator // functions of main emu, called on spu irq extern void (*irqCallbackSPU2)(); @@ -115,11 +123,11 @@ void SaveConfig(); void LoadConfig(); void SysMessage(char *fmt, ...); -void LogRawSound(void* pleft, int leftstride, void* pright, int rightstride, int numsamples); -void LogPacketSound(void* packet, int memsize); +extern void LogRawSound(void* pleft, int leftstride, void* pright, int rightstride, int numsamples); +extern void LogPacketSound(void* packet, int memsize); // simulate SPU2 for 1ms -void SPU2Worker(); +extern void SPU2Worker(); // hardware sound functions int SetupSound(); // if successful, returns 0 @@ -128,95 +136,25 @@ int SoundGetBytesBuffered(); // returns 0 is successful, else nonzero void SoundFeedVoiceData(unsigned char* pSound,long lBytes); -#define clamp16(dest) \ -{ \ - if ( dest < -32768L ) \ - dest = -32768L; \ - else if ( dest > 32767L ) \ - dest = 32767L; \ -} - -#define clampandwrite16(dest,value) \ -{ \ - if ( value < -32768 ) \ - dest = -32768; \ - else if ( value > 32767 ) \ - dest = 32767; \ - else \ - dest = (s16)value; \ -} - -#define spu2Rs16(mem) (*(s16*)&spu2regs[(mem) & 0xffff]) -#define spu2Ru16(mem) (*(u16*)&spu2regs[(mem) & 0xffff]) - -#define IRQINFO spu2Ru16(REG_IRQINFO) - -static __forceinline u32 SPU2_GET32BIT(u32 lo, u32 hi) +static __forceinline void clamp16(s32 &dest) { - return (((u32)(spu2Ru16(hi) & 0x3f) << 16) | (u32)spu2Ru16(lo)); + if (dest < -32768L) + dest = -32768L; + else if (dest > 32767L) + dest = 32767L; } -static __forceinline void SPU2_SET32BIT(u32 value, u32 lo, u32 hi) +static __forceinline void clampandwrite16(s16 &dest, s32 &value) { - spu2Ru16(hi) = ((value) >> 16) & 0x3f; - spu2Ru16(lo) = (value) & 0xffff; + if (value < -32768) + dest = -32768; + else if (value > 32767) + dest = 32767; + else + dest = (s16)value; } -static __forceinline u32 C0_IRQA() -{ - return SPU2_GET32BIT(REG_C0_IRQA_LO, REG_C0_IRQA_HI); -} - -static __forceinline u32 C1_IRQA() -{ - return SPU2_GET32BIT(REG_C1_IRQA_LO, REG_C1_IRQA_HI); -} - -static __forceinline u32 C_IRQA(s32 c) -{ - if (c == 0) - return C0_IRQA(); - else - return C1_IRQA(); -} - -static __forceinline u32 C0_SPUADDR() -{ - return SPU2_GET32BIT(REG_C0_SPUADDR_LO, REG_C0_SPUADDR_HI); -} - -static __forceinline u32 C1_SPUADDR() -{ - return SPU2_GET32BIT(REG_C1_SPUADDR_LO, REG_C1_SPUADDR_HI); -} - -static __forceinline u32 C_SPUADDR(s32 c) -{ - if (c == 0) - return C0_SPUADDR(); - else - return C1_SPUADDR(); -} - -static __forceinline void C0_SPUADDR_SET(u32 value) -{ - SPU2_SET32BIT(value, REG_C0_SPUADDR_LO, REG_C0_SPUADDR_HI); -} - -static __forceinline void C1_SPUADDR_SET(u32 value) -{ - SPU2_SET32BIT(value, REG_C1_SPUADDR_LO, REG_C1_SPUADDR_HI); -} - -static __forceinline void C_SPUADDR_SET(u32 value, s32 c) -{ - if (c == 0) - C0_SPUADDR_SET(value); - else - C1_SPUADDR_SET(value); -} - -struct SPU_CONTROL_ +struct tSPU_ATTR { u16 extCd : 1; u16 extAudio : 1; @@ -229,6 +167,99 @@ struct SPU_CONTROL_ u16 spuUnmute : 1; u16 spuon : 1; }; +#define channel_test(channel) ((channel == 4) || (channel == 0)) +#define spu2Rs16(mem) (*(s16*)&spu2regs[(mem) & 0xffff]) +#define spu2Ru16(mem) (*(u16*)&spu2regs[(mem) & 0xffff]) +#define spu2attr0 (*(tSPU_ATTR*)&spu2regs[REG_C0_CTRL]) +#define spu2attr1 (*(tSPU_ATTR*)&spu2regs[REG_C1_CTRL]) + +static __forceinline u16 c_offset(u32 ch) +{ + return channel_test(ch) ? 0x0 : 0x400; +} + +static __forceinline tSPU_ATTR spu2attr(u32 channel) +{ + return channel_test(channel) ? spu2attr0 : spu2attr1; +} + +static __forceinline bool spu2admas(u32 channel) +{ + return channel_test(channel) ? !!(spu2Ru16(REG_C0_ADMAS) & 0x1) : !!(spu2Ru16(REG_C1_ADMAS) & 0x2); +} + +static __forceinline u16 spu2mmix(u32 channel) +{ + return channel_test(channel) ? spu2Ru16(REG_C0_MMIX) : spu2Ru16(REG_C1_MMIX); +} + +static __forceinline u16 spu2stat(u32 channel) +{ + return channel_test(channel) ? spu2Ru16(REG_C0_SPUSTAT) : spu2Ru16(REG_C1_SPUSTAT); +} + +static __forceinline void spu2stat_clear_80(u32 channel) +{ + if channel_test(channel) + spu2Ru16(REG_C0_SPUSTAT) &= ~0x80; + else + spu2Ru16(REG_C1_SPUSTAT) &= ~0x80; +} + +static __forceinline void spu2stat_set_80(u32 channel) +{ + if channel_test(channel) + spu2Ru16(REG_C0_SPUSTAT) |= 0x80; + else + spu2Ru16(REG_C1_SPUSTAT) |= 0x80; +} + +#define IRQINFO spu2Ru16(REG_IRQINFO) + +#define spu2_core_regs_0 (*(core_registers*)&spu2regs[0x0]) +#define spu2_core_regs_1 (*(core_registers*)&spu2regs[0x400]) + +static __forceinline u32 SPU2_GET32BIT(u32 lo, u32 hi) +{ + return (((u32)(spu2Ru16(hi) & 0x3f) << 16) | (u32)spu2Ru16(lo)); +} + +static __forceinline void SPU2_SET32BIT(u32 value, u32 lo, u32 hi) +{ + spu2Ru16(hi) = ((value) >> 16) & 0x3f; + spu2Ru16(lo) = (value) & 0xffff; +} + +#define C0_IRQA() C_IRQA(0) +#define C1_IRQA() C_IRQA(1) +#define C0_SPUADDR() C_SPUADDR(0) +#define C1_SPUADDR() C_SPUADDR(1) +#define C0_SPUADDR_SET(val) C_SPUADDR_SET(val, 0) +#define C1_SPUADDR_SET(val) C_SPUADDR_SET(val, 1) + +static __forceinline u32 C_IRQA(s32 c) +{ + if (c == 0) + SPU2_GET32BIT(REG_C0_IRQA_LO, REG_C0_IRQA_HI); + else + SPU2_GET32BIT(REG_C1_IRQA_LO, REG_C1_IRQA_HI); +} + +static __forceinline u32 C_SPUADDR(s32 c) +{ + if (c == 0) + return SPU2_GET32BIT(REG_C0_SPUADDR_LO, REG_C0_SPUADDR_HI); + else + return SPU2_GET32BIT(REG_C1_SPUADDR_LO, REG_C1_SPUADDR_HI); +} + +static __forceinline void C_SPUADDR_SET(u32 value, s32 c) +{ + if (c == 0) + SPU2_SET32BIT(value, REG_C0_SPUADDR_LO, REG_C0_SPUADDR_HI); + else + SPU2_SET32BIT(value, REG_C1_SPUADDR_LO, REG_C1_SPUADDR_HI); +} #if defined(_MSC_VER) #pragma pack(1) @@ -313,9 +344,10 @@ struct VOICE_PROCESSED s32 iGetNoiseVal(); void StoreInterpolationVal(int fa); s32 iGetInterpolationVal(); + s32 iGetVal(); void Stop(); - SPU_CONTROL_* GetCtrl(); + tSPU_ATTR* GetCtrl(); // start save state s32 leftvol, rightvol; // left right volumes @@ -326,11 +358,9 @@ struct VOICE_PROCESSED s32 sinc; s32 iIrqDone; // debug irq done flag - s32 s_1; // last decoding infos - s32 s_2; + s32 s_1, s_2; // last decoding infos s32 iOldNoise; // old noise val for this channel - s32 iActFreq; // current psx pitch - s32 iUsedFreq; // current pc pitch + s32 iActFreq, iUsedFreq; // current psx pitch & pc pitch s32 iStartAddr, iLoopAddr, iNextAddr; s32 bFMod; @@ -351,6 +381,27 @@ struct VOICE_PROCESSED u8* pLoop, *pCurr; _SPU_VOICE* pvoice; + u32 memchannel; + void init(s32 i) + { + chanid = i; + + if (chanid > 23) + { + memoffset = 0x400; + memchannel = 1; + } + else + { + memoffset = 0x0; + memchannel = 0; + } + + pLoop = pStart = pCurr = (u8*)spu2mem; + + pvoice = (_SPU_VOICE*)((u8*)spu2regs + memoffset) + (i % 24); + ADSRX.SustainLevel = 1024; // -> init sustain + } }; struct AUDIOBUFFER @@ -374,8 +425,7 @@ struct ADMA // used to make sure that ADMA doesn't get interrupted with a writeDMA call }; -extern ADMA Adma4; -extern ADMA Adma7; +extern ADMA adma[2]; struct SPU2freezeData { @@ -389,7 +439,7 @@ struct SPU2freezeData s32 iFMod[NSSIZE]; u32 MemAddr[2]; ADMA adma[2]; - u32 Adma4MemAddr, Adma7MemAddr; + u32 AdmaMemAddr[2]; s32 SPUCycles, SPUWorkerCycles; s32 SPUStartCycle[2]; diff --git a/plugins/zerospu2/zeroworker.cpp b/plugins/zerospu2/zeroworker.cpp new file mode 100644 index 0000000000..55e79c073f --- /dev/null +++ b/plugins/zerospu2/zeroworker.cpp @@ -0,0 +1,323 @@ +/* ZeroSPU2 + * Copyright (C) 2006-2010 zerofrog + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "zerospu2.h" +#include "zeroworker.h" +#include "SoundTouch/SoundTouch.h" +#include "SoundTouch/WavFile.h" + +s32 g_logsound = 0; +WavOutFile* g_pWavRecord=NULL; // used for recording + +const s32 f[5][2] = { + { 0, 0 }, + { 60, 0 }, + { 115, -52 }, + { 98, -55 }, + { 122, -60 } }; + +s32 predict_nr, shift_factor, flags; +s32 s_1, s_2; + +static __forceinline s32 SetPacket(s32 val) +{ + s32 ret; + if (val & 0x8000) val |= 0xffff0000; + ret = (val >> shift_factor); + ret += ((s_1 * f[predict_nr][0]) >> 6) + ((s_2 * f[predict_nr][1]) >> 6); + s_2 = s_1; + s_1 = ret; + + return ret; +} + +void SPU2Loop(VOICE_PROCESSED* pChannel, u32 ch) +{ + u8* start; + u32 nSample; + + for (u32 ns = 0; ns < NSSIZE; ns++) + { + // fmod freq channel + if (pChannel->bFMod == 1 && iFMod[ns]) pChannel->FModChangeFrequency(ns); + + while(pChannel->spos >= 0x10000 ) + { + if (pChannel->iSBPos == 28) // 28 reached? + { + start=pChannel->pCurr; // set up the current pos + + // special "stop" sign + if (start == (u8*)-1) //!pChannel->bOn + { + pChannel->bOn = false; // -> turn everything off + pChannel->ADSRX.lVolume = 0; + pChannel->ADSRX.EnvelopeVol = 0; + return; // -> and done for this channel + } + + predict_nr = (s32)start[0]; + shift_factor = predict_nr&0xf; + predict_nr >>= 4; + flags=(s32)start[1]; + start += 2; + + pChannel->iSBPos = 0; + + // decode the 16byte packet + s_1 = pChannel->s_1; + s_2 = pChannel->s_2; + + for (nSample=0; nSample<28; ++start) + { + s32 d = (s32)*start; + s32 s; + + s = ((d & 0xf)<<12); + pChannel->SB[nSample++] = SetPacket(s); + + s = ((d & 0xf0) << 8); + pChannel->SB[nSample++] = SetPacket(s); + } + + // irq occurs no matter what core accesses the address + for (s32 core = 0; core < 2; ++core) + { + if (spu2attr(core).irq) // some callback and irq active? + { + // if irq address reached or irq on looping addr, when stop/loop flag is set + u8* pirq = (u8*)pSpuIrq[core]; + + if ((pirq > (start - 16) && pirq <= start) || + ((flags & 1) && (pirq > (pChannel->pLoop - 16) && pirq <= pChannel->pLoop))) + { + IRQINFO |= 4 << core; + SPU2_LOG("SPU2Worker:interrupt\n"); + irqCallbackSPU2(); + } + } + } + + // flag handler + if ((flags & 4) && (!pChannel->bIgnoreLoop)) + pChannel->pLoop = start - 16; // loop address + + if (flags & 1) // 1: stop/loop + { + // We play this block out first... + dwEndChannel2[ch / 24] |= (1 << (ch % 24)); + + if (flags != 3 || pChannel->pLoop == NULL) + { // and checking if pLoop is set avoids crashes, yeah + start = (u8*)-1; + pChannel->bStop = true; + pChannel->bIgnoreLoop = false; + } + else + { + start = pChannel->pLoop; + } + } + + pChannel->pCurr = start; // store values for next cycle + pChannel->s_1 = s_1; + pChannel->s_2 = s_2; + } + + pChannel->StoreInterpolationVal(pChannel->SB[pChannel->iSBPos++]); // get sample data + pChannel->spos -= 0x10000; + } + + s32 sval = (MixADSR(pChannel) * pChannel->iGetVal()) / 1023; // mix adsr with noise or sample val. + + if (pChannel->bFMod == 2) // fmod freq channel + { + // -> store 1T sample data, use that to do fmod on next channel + if (!pChannel->bNoise) iFMod[ns] = sval; + } + else + { + if (pChannel->bVolumeL) + s_buffers[ns][0] += (sval * pChannel->leftvol) >> 14; + + if (pChannel->bVolumeR) + s_buffers[ns][1] += (sval * pChannel->rightvol) >> 14; + } + + pChannel->spos += pChannel->sinc; + } +} +// simulate SPU2 for 1ms +void SPU2Worker() +{ + // assume s_buffers are zeroed out + if (dwNewChannel2[0] || dwNewChannel2[1]) s_pAudioBuffers[s_nCurBuffer].newchannels++; + + VOICE_PROCESSED* pChannel = voices; + for (u32 ch=0; ch < SPU_NUMBER_VOICES; ch++, pChannel++) // loop em all... we will collect 1 ms of sound of each playing channel + { + if (pChannel->bNew) + { + pChannel->StartSound(); // start new sound + dwEndChannel2[ch / 24] &= ~(1 << (ch % 24)); // clear end channel bit + dwNewChannel2[ch / 24] &= ~(1 << (ch % 24)); // clear channel bit + } + + if (!pChannel->bOn) continue; + + if (pChannel->iActFreq != pChannel->iUsedFreq) // new psx frequency? + pChannel->VoiceChangeFrequency(); + + // loop until 1 ms of data is reached + SPU2Loop(pChannel, ch); + } + + // mix all channels + MixChannels(0); + MixChannels(1); + + if ( g_bPlaySound ) + { + assert( s_pCurOutput != NULL); + + for (u32 ns = 0; ns < NSSIZE; ns++) + { + // clamp and write + clampandwrite16(s_pCurOutput[0],s_buffers[ns][0]); + clampandwrite16(s_pCurOutput[1],s_buffers[ns][1]); + + s_pCurOutput += 2; + s_buffers[ns][0] = 0; + s_buffers[ns][1] = 0; + } + // check if end reached + + if ((uptr)s_pCurOutput - (uptr)s_pAudioBuffers[s_nCurBuffer].pbuf >= 4 * NS_TOTAL_SIZE) + { + + if ( conf.options & OPTION_RECORDING ) + { + static s32 lastrectime = 0; + if (timeGetTime() - lastrectime > 5000) + { + WARN_LOG("ZeroSPU2: recording\n"); + lastrectime = timeGetTime(); + } + LogRawSound(s_pAudioBuffers[s_nCurBuffer].pbuf, 4, s_pAudioBuffers[s_nCurBuffer].pbuf+2, 4, NS_TOTAL_SIZE); + } + + if ( s_nQueuedBuffers >= ArraySize(s_pAudioBuffers)-1 ) + { + //ZeroSPU2: dropping packets! game too fast + s_nDropPacket += NSFRAMES; + s_GlobalTimeStamp = GetMicroTime(); + } + else { + // submit to final mixer +#ifdef ZEROSPU2_DEVBUILD + if ( g_logsound ) + LogRawSound(s_pAudioBuffers[s_nCurBuffer].pbuf, 4, s_pAudioBuffers[s_nCurBuffer].pbuf + 2, 4, NS_TOTAL_SIZE); +#endif + if ( g_startcount == 0xffffffff ) + { + g_startcount = timeGetTime(); + g_packetcount = 0; + } + + if ( conf.options & OPTION_TIMESTRETCH ) + { + u32 newtotal = s_nTotalDuration - s_nDurations[s_nCurDuration]; + u64 newtime = GetMicroTime(); + u32 duration; + + if (s_GlobalTimeStamp == 0) s_GlobalTimeStamp = newtime - NSFRAMES * 1000; + duration = (u32)(newtime-s_GlobalTimeStamp); + + s_nDurations[s_nCurDuration] = duration; + s_nTotalDuration = newtotal + duration; + s_nCurDuration = (s_nCurDuration+1)%ArraySize(s_nDurations); + s_GlobalTimeStamp = newtime; + s_pAudioBuffers[s_nCurBuffer].timestamp = timeGetTime(); + s_pAudioBuffers[s_nCurBuffer].avgtime = s_nTotalDuration/ArraySize(s_nDurations); + } + + s_pAudioBuffers[s_nCurBuffer].len = 4 * NS_TOTAL_SIZE; + InterlockedExchangeAdd((long*)&s_nQueuedBuffers, 1); + + s_nCurBuffer = (s_nCurBuffer+1)%ArraySize(s_pAudioBuffers); + s_pAudioBuffers[s_nCurBuffer].newchannels = 0; // reset + } + + // restart + s_pCurOutput = (s16*)s_pAudioBuffers[s_nCurBuffer].pbuf; + } + } +} + +// size is in bytes +void LogPacketSound(void* packet, s32 memsize) +{ + u16 buf[28]; + + u8* pstart = (u8*)packet; + s_1 = s_2 = 0; + + for (s32 i = 0; i < memsize; i += 16) + { + predict_nr = (s32)pstart[0]; + shift_factor = predict_nr&0xf; + predict_nr >>= 4; + pstart += 2; + + for (s32 nSample = 0;nSample < 28; ++pstart) + { + s32 d = (s32)*pstart; + + s32 temp; + + temp = ((d & 0xf) << 12); + buf[nSample++] = SetPacket(temp); + temp = ((d & 0xf0) << 8); + buf[nSample++] = SetPacket(temp); + } + + LogRawSound(buf, 2, buf, 2, 28); + } +} + +void LogRawSound(void* pleft, s32 leftstride, void* pright, s32 rightstride, s32 numsamples) +{ + if (g_pWavRecord == NULL ) + g_pWavRecord = new WavOutFile(RECORD_FILENAME, SAMPLE_RATE, 16, 2); + + u8* left = (u8*)pleft; + u8* right = (u8*)pright; + static vector tempbuf; + + tempbuf.resize(2 * numsamples); + + for (s32 i = 0; i < numsamples; ++i) + { + tempbuf[2*i+0] = *(s16*)left; + tempbuf[2*i+1] = *(s16*)right; + left += leftstride; + right += rightstride; + } + + g_pWavRecord->write(&tempbuf[0], numsamples*2); +} diff --git a/plugins/zerospu2/zeroworker.h b/plugins/zerospu2/zeroworker.h new file mode 100644 index 0000000000..7728a3d69f --- /dev/null +++ b/plugins/zerospu2/zeroworker.h @@ -0,0 +1,44 @@ +/* ZeroSPU2 + * Copyright (C) 2006-2010 zerofrog + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef ZEROWORKER_H_INCLUDED +#define ZEROWORKER_H_INCLUDED + +extern u32 dwNewChannel2[2]; // keeps track of what channels that have been turned on +extern u32 dwEndChannel2[2]; // keeps track of what channels have ended +extern AUDIOBUFFER s_pAudioBuffers[NSPACKETS]; +extern s32 s_nCurBuffer, s_nQueuedBuffers; +extern VOICE_PROCESSED voices[SPU_NUMBER_VOICES+1]; // +1 for modulation +extern u16* pSpuIrq[2]; +extern s32 s_buffers[NSSIZE][2]; // left and right buffers +extern bool g_bPlaySound; // if true, will output sound, otherwise no +extern s16* s_pCurOutput; +extern bool s_bThreadExit; +extern s32 s_nDropPacket; +extern u64 s_GlobalTimeStamp; +extern s32 s_nDurations[64]; +extern s32 s_nCurDuration, s_nTotalDuration; +extern u32 g_startcount, g_packetcount; + +extern void SPU2Worker(); +extern void LogPacketSound(void* packet, s32 memsize); +extern void LogRawSound(void* pleft, s32 leftstride, void* pright, s32 rightstride, s32 numsamples); + +extern s32 MixADSR(VOICE_PROCESSED* pvoice); +extern void MixChannels(s32 core); +#endif // ZEROWORKER_H_INCLUDED