From 38217c032fb83d45b0e0f23ddce08fa8d96fe3fe Mon Sep 17 00:00:00 2001 From: zeromus Date: Tue, 20 Oct 2009 06:52:07 +0000 Subject: [PATCH] add synch mode to spu and add necessary dialog configuration. (this is a spu option which fixes most streaming audio). it may become the default after we perform more analysis and refinement. also straighten out all the volume slider bugs. --- desmume/src/SPU.cpp | 577 +++++++++++++++++++++++++++---- desmume/src/SPU.h | 1 + desmume/src/windows/main.cpp | 81 ++++- desmume/src/windows/resource.h | 9 +- desmume/src/windows/resources.rc | Bin 443086 -> 440694 bytes desmume/src/windows/snddx.cpp | 74 ++-- 6 files changed, 622 insertions(+), 120 deletions(-) diff --git a/desmume/src/SPU.cpp b/desmume/src/SPU.cpp index 096981a18..c07a693d8 100644 --- a/desmume/src/SPU.cpp +++ b/desmume/src/SPU.cpp @@ -23,16 +23,16 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ -#include -#include - #define _USE_MATH_DEFINES #include #ifndef M_PI #define M_PI 3.1415926535897932386 #endif -#define K_ADPCM_LOOPING_RECOVERY_INDEX 99999 +#include +#include +#include +#include #include "debug.h" #include "MMU.h" @@ -43,12 +43,465 @@ #include "NDSSystem.h" #include "matrix.h" +#define K_ADPCM_LOOPING_RECOVERY_INDEX 99999 + //#undef FORCEINLINE //#define FORCEINLINE +class ISynchronizingAudioBuffer +{ +public: + virtual void enqueue_samples(s16* buf, int samples_provided) = 0; + + //returns the number of samples actually supplied, which may not match the number requested + virtual int output_samples(s16* buf, int samples_requested) = 0; +}; + +template inline T _abs(T val) +{ + if(val<0) return -val; + else return val; +} + +template inline T moveValueTowards(T val, T target, T incr) +{ + incr = _abs(incr); + T delta = _abs(target-val); + if(valtarget) val -= incr; + T newDelta = _abs(target-val); + if(newDelta >= delta) + val = target; + return val; +} + + +class ZeromusSynchronizer : public ISynchronizingAudioBuffer +{ +public: + ZeromusSynchronizer() + : mixqueue_go(false) + , + #ifdef NDEBUG + adjustobuf(200,1000) + #else + adjustobuf(22000,44000) + #endif + { + + } + + bool mixqueue_go; + + virtual void enqueue_samples(s16* buf, int samples_provided) + { + for(int i=0;i 200) + mixqueue_go = true; + } + else + { + for(int i=0;i buffer; + int size; + s16 curr[2]; + + std::queue statsHistory; + + void enqueue(s16 left, s16 right) + { + buffer.push(left); + buffer.push(right); + size++; + } + + s64 rollingTotalSize; + + u32 kAverageSize; + + void addStatistic() + { + statsHistory.push(size); + rollingTotalSize += size; + if(statsHistory.size()>kAverageSize) + { + rollingTotalSize -= statsHistory.front(); + statsHistory.pop(); + + float averageSize = (float)(rollingTotalSize / kAverageSize); + //static int ctr=0; ctr++; if((ctr&127)==0) printf("avg size: %f curr size: %d rate: %f\n",averageSize,size,rate); + { + float targetRate; + if(averageSize < targetLatency) + { + targetRate = 1.0f - (targetLatency-averageSize)/kAverageSize; + } + else if(averageSize > targetLatency) { + targetRate = 1.0f + (averageSize-targetLatency)/kAverageSize; + } else targetRate = 1.0f; + + //rate = moveValueTowards(rate,targetRate,0.001f); + rate = targetRate; + } + + } + + + } + + void dequeue(s16& left, s16& right) + { + left = right = 0; + addStatistic(); + if(size==0) { return; } + cursor += rate; + while(cursor>1.0f) { + cursor -= 1.0f; + if(size>0) { + curr[0] = buffer.front(); buffer.pop(); + curr[1] = buffer.front(); buffer.pop(); + size--; + } + } + left = curr[0]; + right = curr[1]; + } + } adjustobuf; +}; + +class NitsujaSynchronizer : public ISynchronizingAudioBuffer +{ +private: + template + struct ssampT + { + T l, r; + enum { TMAX = (1 << ((sizeof(T) * 8) - 1)) - 1 }; + ssampT() {} + ssampT(T ll, T rr) : l(ll), r(rr) {} + template + ssampT(ssampT s) : l(s.l), r(s.r) {} + ssampT operator+(const ssampT& rhs) + { + s32 l2 = l+rhs.l; + s32 r2 = r+rhs.r; + if(l2 > TMAX) l2 = TMAX; + if(l2 < -TMAX) l2 = -TMAX; + if(r2 > TMAX) r2 = TMAX; + if(r2 < -TMAX) r2 = -TMAX; + return ssampT(l2, r2); + } + ssampT operator/(int rhs) + { + return ssampT(l/rhs,r/rhs); + } + ssampT operator*(int rhs) + { + s32 l2 = l*rhs; + s32 r2 = r*rhs; + if(l2 > TMAX) l2 = TMAX; + if(l2 < -TMAX) l2 = -TMAX; + if(r2 > TMAX) r2 = TMAX; + if(r2 < -TMAX) r2 = -TMAX; + return ssampT(l2, r2); + } + ssampT muldiv (int num, int den) + { + num = std::max(0,num); + return ssampT(((s32)l * num) / den, ((s32)r * num) / den); + } + ssampT faded (ssampT rhs, int cur, int start, int end) + { + if(cur <= start) + return *this; + if(cur >= end) + return rhs; + + //float ang = 3.14159f * (float)(cur - start) / (float)(end - start); + //float amt = (1-cosf(ang))*0.5f; + //cur = start + (int)(amt * (end - start)); + + int inNum = cur - start; + int outNum = end - cur; + int denom = end - start; + + int lrv = ((int)l * outNum + (int)rhs.l * inNum) / denom; + int rrv = ((int)r * outNum + (int)rhs.r * inNum) / denom; + + return ssampT(lrv,rrv); + } + }; + + typedef ssampT ssamp; + + std::vector sampleQueue; + + // reflects x about y if x exceeds y. + FORCEINLINE int reflectAbout(int x, int y) + { + //return (x)+(x)/(y)*(2*((y)-(x))-1); + return (x 0x200 && audiosize > 0) // is there any work to do? + { + // are we going at normal speed? + // or more precisely, are the input and output queues/buffers of similar size? + if(queued > 900 || audiosize > queued * 2) + { + // not normal speed. we have to resample it somehow in this case. + static std::vector outsamples; + outsamples.clear(); + if(audiosize <= queued) + { + // fast forward speed + // this is the easy case, just crossfade it and it sounds ok + for(int i = 0; i < audiosize; i++) + { + int j = i + queued - audiosize; + ssamp outsamp = sampleQueue[i].faded(sampleQueue[j], i,0,audiosize); + outsamples.push_back(ssamp(outsamp)); + } + } + else + { + // slow motion speed + // here we take a very different approach, + // instead of crossfading it, we select a single sample from the queue + // and make sure that the index we use to select a sample is constantly moving + // and that it starts at the first sample in the queue and ends on the last one. + // + // hopefully the index doesn't move discontinuously or we'll get slight crackling + // (there might still be a minor bug here that causes this occasionally) + // + // here's a diagram of how the index we sample from moves: + // + // queued (this axis represents the index we sample from. the top means the end of the queue) + // ^ + // | --> audiosize (this axis represents the output index we write to, right meaning forward in output time/position) + // | A C C end + // A A B C C C + // A A A B C C C + // A A A B C C + // A A C + // start + // + // yes, this means we are spending some stretches of time playing the sound backwards, + // but the stretches are short enough that this doesn't sound weird. + // apparently this also sounds less "echoey" or "robotic" than only playing it forwards. + + int midpointX = audiosize >> 1; + int midpointY = queued >> 1; + + // all we need to do here is calculate the X position of the leftmost "B" in the above diagram. + // TODO: we should calculate it with a simple equation like + // midpointXOffset = min(something,somethingElse); + // but it's a little difficult to work it out exactly + // so here's a stupid search for the value for now: + + int prevA = 999999; + int midpointXOffset = queued/2; + while(true) + { + int a = abs(reflectAbout((midpointX - midpointXOffset) % (queued*2), queued) - midpointY) - midpointXOffset; + if(((a > 0) != (prevA > 0) || (a < 0) != (prevA < 0)) && prevA != 999999) + { + if((a + prevA)&1) // there's some sort of off-by-one problem with this search since we're moving diagonally... + midpointXOffset++; // but this fixes it most of the time... + break; // found it + } + prevA = a; + midpointXOffset--; + if(midpointXOffset < 0) + { + midpointXOffset = 0; + break; // failed somehow? let's just omit the "B" stretch in this case. + } + } + int leftMidpointX = midpointX - midpointXOffset; + int rightMidpointX = midpointX + midpointXOffset; + int leftMidpointY = reflectAbout((leftMidpointX) % (queued*2), queued); + int rightMidpointY = (queued-1) - reflectAbout((((int)audiosize-1 - rightMidpointX + queued*2) % (queued*2)), queued); + + // output the left almost-half of the sound (section "A") + for(int x = 0; x < leftMidpointX; x++) + { + int i = reflectAbout(x % (queued*2), queued); + outsamples.push_back(sampleQueue[i]); + } + + // output the middle stretch (section "B") + int y = leftMidpointY; + int dyMidLeft = (leftMidpointY < midpointY) ? 1 : -1; + int dyMidRight = (rightMidpointY > midpointY) ? 1 : -1; + for(int x = leftMidpointX; x < midpointX; x++, y+=dyMidLeft) + outsamples.push_back(sampleQueue[y]); + for(int x = midpointX; x < rightMidpointX; x++, y+=dyMidRight) + outsamples.push_back(sampleQueue[y]); + + // output the end of the queued sound (section "C") + for(int x = rightMidpointX; x < audiosize; x++) + { + int i = (queued-1) - reflectAbout((((int)audiosize-1 - x + queued*2) % (queued*2)), queued); + outsamples.push_back(sampleQueue[i]); + } + assert(outsamples.back().l == sampleQueue[queued-1].l); + } //end else + + // if the user SPU mixed some channels, mix them in with our output now +#ifdef HYBRID_SPU + SPU_MixAudio<2>(SPU_user,audiosize); + for(int i = 0; i < audiosize; i++) + outsamples[i] = outsamples[i] + *(ssamp*)(&SPU_user->outbuf[i*2]); +#endif + + emit_samples(buf,&outsamples[0],audiosize); + sampleQueue.erase(sampleQueue.begin(), sampleQueue.begin() + queued); + return audiosize; + } + else + { + // normal speed + // just output the samples straightforwardly. + // + // at almost-full speeds (like 50/60 FPS) + // what will happen is that we rapidly fluctuate between entering this branch + // and entering the "slow motion speed" branch above. + // but that's ok! because all of these branches sound similar enough that we can get away with it. + // so the two cases actually complement each other. + + if(audiosize >= queued) + { +#ifdef HYBRID_SPU + SPU_MixAudio<2>(SPU_user,queued); + for(int i = 0; i < queued; i++) + sampleQueue[i] = sampleQueue[i] + *(ssamp*)(&SPU_user->outbuf[i*2]); +#endif + emit_samples(buf,&sampleQueue[0],queued); + sampleQueue.erase(sampleQueue.begin(), sampleQueue.begin() + queued); + return queued; + } + else + { +#ifdef HYBRID_SPU + SPU_MixAudio<2>(SPU_user,audiosize); + for(int i = 0; i < audiosize; i++) + sampleQueue[i] = sampleQueue[i] + *(ssamp*)(&SPU_user->outbuf[i*2]); +#endif + emit_samples(buf,&sampleQueue[0],audiosize); + sampleQueue.erase(sampleQueue.begin(), sampleQueue.begin()+audiosize); + return audiosize; + } + + } //end normal speed + + } //end if there is any work to do + else + { + return 0; + } + + } //output_samples + +private: + +}; //NitsujaSynchronizer + +//static ISynchronizingAudioBuffer* synchronizer = new ZeromusSynchronizer(); +static ISynchronizingAudioBuffer* synchronizer = new NitsujaSynchronizer(); + SPU_struct *SPU_core = 0; SPU_struct *SPU_user = 0; int SPU_currentCoreNum = SNDCORE_DUMMY; +static int volume = 100; + +enum ESynchMode +{ + ESynchMode_DualSynchAsynch, + ESynchMode_Synchronous +}; +static ESynchMode synchmode = ESynchMode_DualSynchAsynch; + +enum ESynchMethod +{ + ESynchMethod_N, //nitsuja's + ESynchMethod_Z //zero's +}; +static ESynchMethod synchmethod = ESynchMethod_N; static SoundInterface_struct *SNDCore=NULL; extern SoundInterface_struct *SNDCoreList[]; @@ -93,8 +546,6 @@ static const double ARM7_CLOCK = 33513982; static double samples = 0; -////////////////////////////////////////////////////////////////////////////// - template static FORCEINLINE T MinMax(T val, T min, T max) { @@ -106,7 +557,7 @@ static FORCEINLINE T MinMax(T val, T min, T max) return val; } -////////////////////////////////////////////////////////////////////////////// +//--------------external spu interface--------------- int SPU_ChangeSoundCore(int coreid, int buffersize) { @@ -153,6 +604,8 @@ int SPU_ChangeSoundCore(int coreid, int buffersize) //enable the user spu SPU_user = new SPU_struct(buffersize); + SNDCore->SetVolume(volume); + return 0; } @@ -161,20 +614,19 @@ SoundInterface_struct *SPU_SoundCore() return SNDCore; } -////////////////////////////////////////////////////////////////////////////// - //static double cos_lut[256]; - int SPU_Init(int coreid, int buffersize) { int i, j; + //for some reason we dont use the cos lut anymore... did someone decide it was slow? //for(int i=0;i<256;i++) // cos_lut[i] = cos(i/256.0*M_PI); SPU_core = new SPU_struct(740); SPU_Reset(); + //create adpcm decode accelerator lookups for(i = 0; i < 16; i++) { for(j = 0; j < 89; j++) @@ -183,7 +635,6 @@ int SPU_Init(int coreid, int buffersize) if(i & 0x8) precalcdifftbl[j][i] = -precalcdifftbl[j][i]; } } - for(i = 0; i < 8; i++) { for(j = 0; j < 89; j++) @@ -195,8 +646,6 @@ int SPU_Init(int coreid, int buffersize) return SPU_ChangeSoundCore(coreid, buffersize); } -////////////////////////////////////////////////////////////////////////////// - void SPU_Pause(int pause) { if (SNDCore == NULL) return; @@ -207,16 +656,27 @@ void SPU_Pause(int pause) SNDCore->UnMuteAudio(); } -////////////////////////////////////////////////////////////////////////////// +void SPU_SetSynchMode(int mode, int method) +{ + synchmode = (ESynchMode)mode; + if(synchmethod != (ESynchMethod)method) + { + synchmethod = (ESynchMethod)method; + delete synchronizer; + //grr does this need to be locked? spu might need a lock method + if(synchmethod == ESynchMethod_N) + synchronizer = new NitsujaSynchronizer(); + else synchronizer = new ZeromusSynchronizer(); + } +} void SPU_SetVolume(int volume) { + ::volume = volume; if (SNDCore) SNDCore->SetVolume(volume); } -////////////////////////////////////////////////////////////////////////////// - void SPU_Reset(void) { @@ -228,6 +688,7 @@ void SPU_Reset(void) if(SNDCore && SPU_user) { SNDCore->DeInit(); SNDCore->Init(SPU_user->bufsize*2); + SNDCore->SetVolume(volume); //todo - check success? } @@ -238,6 +699,8 @@ void SPU_Reset(void) samples = 0; } +//------------------------------------------ + void SPU_struct::reset() { memset(sndbuf,0,bufsize*2*4); @@ -782,8 +1245,7 @@ FORCEINLINE static void _SPU_ChanUpdate(const bool actuallyMix, SPU_struct* cons } } -template -static void SPU_MixAudio(SPU_struct *SPU, int length) +static void SPU_MixAudio(bool actuallyMix, SPU_struct *SPU, int length) { u8 vol; @@ -849,11 +1311,13 @@ void SPU_Emulate_core() samples += samples_per_hline; spu_core_samples = (int)(samples); samples -= spu_core_samples; - - if(driver->AVI_IsRecording() || driver->WAV_IsRecording()) - SPU_MixAudio(SPU_core,spu_core_samples); - else - SPU_MixAudio(SPU_core,spu_core_samples); + + bool synchronize = (synchmode == ESynchMode_Synchronous); + bool mix = driver->AVI_IsRecording() || driver->WAV_IsRecording() || synchronize; + + SPU_MixAudio(mix,SPU_core,spu_core_samples); + if(synchronize) + synchronizer->enqueue_samples(SPU_core->outbuf, spu_core_samples); } void SPU_Emulate_user(bool mix) @@ -872,8 +1336,19 @@ void SPU_Emulate_user(bool mix) //printf("mix %i samples\n", audiosize); if (audiosize > SPU_user->bufsize) audiosize = SPU_user->bufsize; - if (mix) SPU_MixAudio(SPU_user,audiosize); - SNDCore->UpdateAudio(SPU_user->outbuf, audiosize); + + if(synchmode == ESynchMode_Synchronous) + { + int done = synchronizer->output_samples(SPU_user->outbuf, audiosize); + for(int j=0;jUpdateAudio(&SPU_user->outbuf[j*2],1); + } + else + { + SPU_MixAudio(mix,SPU_user,audiosize); + SNDCore->UpdateAudio(SPU_user->outbuf, audiosize); + } + } } @@ -901,51 +1376,15 @@ SoundInterface_struct SNDDummy = { SNDDummySetVolume }; -////////////////////////////////////////////////////////////////////////////// +int SNDDummyInit(int buffersize) { return 0; } +void SNDDummyDeInit() {} +void SNDDummyUpdateAudio(s16 *buffer, u32 num_samples) { } +u32 SNDDummyGetAudioSpace() { return 740; } +void SNDDummyMuteAudio() {} +void SNDDummyUnMuteAudio() {} +void SNDDummySetVolume(int volume) {} -int SNDDummyInit(int buffersize) -{ - return 0; -} - -////////////////////////////////////////////////////////////////////////////// - -void SNDDummyDeInit() -{ -} - -////////////////////////////////////////////////////////////////////////////// - -void SNDDummyUpdateAudio(s16 *buffer, u32 num_samples) -{ -} - -////////////////////////////////////////////////////////////////////////////// - -u32 SNDDummyGetAudioSpace() -{ - return 740; -} - -////////////////////////////////////////////////////////////////////////////// - -void SNDDummyMuteAudio() -{ -} - -////////////////////////////////////////////////////////////////////////////// - -void SNDDummyUnMuteAudio() -{ -} - -////////////////////////////////////////////////////////////////////////////// - -void SNDDummySetVolume(int volume) -{ -} - -////////////////////////////////////////////////////////////////////////////// +//---------wav writer------------ typedef struct { char id[4]; diff --git a/desmume/src/SPU.h b/desmume/src/SPU.h index cfecf678d..6d5803f70 100644 --- a/desmume/src/SPU.h +++ b/desmume/src/SPU.h @@ -131,6 +131,7 @@ SoundInterface_struct *SPU_SoundCore(); int SPU_Init(int coreid, int buffersize); void SPU_Pause(int pause); void SPU_SetVolume(int volume); +void SPU_SetSynchMode(int mode, int method); void SPU_Reset(void); void SPU_DeInit(void); void SPU_KeyOn(int channel); diff --git a/desmume/src/windows/main.cpp b/desmume/src/windows/main.cpp index 5fa6d5dc3..5577b56e3 100644 --- a/desmume/src/windows/main.cpp +++ b/desmume/src/windows/main.cpp @@ -194,6 +194,11 @@ inline bool IsDlgCheckboxChecked(HWND hDlg, int id) return IsDlgButtonChecked(hDlg,id) == BST_CHECKED; } +void CheckDlgItem(HWND hDlg, int id, bool checked) +{ + CheckDlgButton(hDlg, id, checked ? BST_CHECKED : BST_UNCHECKED); +} + LRESULT CALLBACK WindowProcedure (HWND, UINT, WPARAM, LPARAM); char SavName[MAX_PATH] = ""; @@ -261,11 +266,11 @@ extern bool userTouchesScreen; /*__declspec(thread)*/ bool inFrameBoundary = false; -//static char IniName[MAX_PATH]; -int sndcoretype=SNDCORE_DIRECTX; -int sndbuffersize=735*4; -int sndvolume=100; -HANDLE hSoundThreadWakeup = INVALID_HANDLE_VALUE; +static int sndcoretype=SNDCORE_DIRECTX; +static int sndbuffersize=735*4; +static int sndvolume=100; +static int snd_synchmode=0; +static int snd_synchmethod=0; SoundInterface_struct *SNDCoreList[] = { &SNDDummy, @@ -1198,8 +1203,8 @@ static void StepRunLoop_Core() { Lock lock; NDS_exec(); + SPU_Emulate_user(); win_sound_samplecounter = 735; - SetEvent(hSoundThreadWakeup); } inFrameBoundary = true; DRV_AviVideoUpdate((u16*)GPU_screen); @@ -1736,12 +1741,12 @@ class WinDriver : public BaseDriver virtual bool EMU_IsEmulationPaused() { - return emu_paused; + return emu_paused!=0; } virtual bool EMU_IsFastForwarding() { - return FastForward; + return FastForward!=0; } virtual bool EMU_HasEmulationStarted() @@ -1820,8 +1825,6 @@ int _main() display_invoke_done_event = CreateEvent(NULL, FALSE, FALSE, NULL); display_wakeup_event = CreateEvent(NULL, FALSE, FALSE, NULL); - hSoundThreadWakeup = CreateEvent(NULL, FALSE, FALSE, NULL); - #ifdef GDB_STUB gdbstub_handle_t arm9_gdb_stub; gdbstub_handle_t arm7_gdb_stub; @@ -2151,6 +2154,10 @@ int _main() sndvolume = GetPrivateProfileInt("Sound","Volume",100, IniName); SPU_SetVolume(sndvolume); + snd_synchmode = GetPrivateProfileInt("Sound","SynchMode",0,IniName); + snd_synchmethod = GetPrivateProfileInt("Sound","SynchMethod",0,IniName); + SPU_SetSynchMode(snd_synchmode,snd_synchmethod); + CommonSettings.DebugConsole = GetPrivateProfileBool("Emulation", "DebugConsole", FALSE, IniName); CommonSettings.UseExtBIOS = GetPrivateProfileBool("BIOS", "UseExtBIOS", FALSE, IniName); GetPrivateProfileString("BIOS", "ARM9BIOSFile", "bios9.bin", CommonSettings.ARM9BIOS, 256, IniName); @@ -2262,8 +2269,6 @@ int _main() UnregWndClass("DeSmuME"); - CloseHandle(hSoundThreadWakeup); - return 0; } @@ -2992,8 +2997,13 @@ LRESULT CALLBACK WindowProcedure (HWND hwnd, UINT message, WPARAM wParam, LPARAM static int tmp_execute; switch (message) // handle the messages { + case WM_EXITMENULOOP: + SPU_Pause(0); + break; case WM_ENTERMENULOOP: //Update menu items that needs to be updated dynamically { + SPU_Pause(1); + UpdateHotkeyAssignments(); //Add current hotkey mappings to menu item names MENUITEMINFO mii; @@ -4810,7 +4820,20 @@ LRESULT CALLBACK WifiSettingsDlgProc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM return FALSE; } -LRESULT CALLBACK SoundSettingsDlgProc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) +static void SoundSettings_updateVolumeReadout(HWND hDlg) +{ + SetDlgItemInt(hDlg,IDC_VOLUME,SendMessage(GetDlgItem(hDlg,IDC_SLVOLUME),TBM_GETPOS,0,0),FALSE); +} + +static void SoundSettings_updateSynchMode(HWND hDlg) +{ + BOOL en = IsDlgCheckboxChecked(hDlg,IDC_SYNCHMODE_SYNCH)?TRUE:FALSE; + EnableWindow(GetDlgItem(hDlg,IDC_GROUP_SYNCHMETHOD),en); + EnableWindow(GetDlgItem(hDlg,IDC_SYNCHMETHOD_N),en); + EnableWindow(GetDlgItem(hDlg,IDC_SYNCHMETHOD_Z),en); +} + +static LRESULT CALLBACK SoundSettingsDlgProc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) { static UINT_PTR timerid=0; switch (uMsg) @@ -4819,6 +4842,7 @@ LRESULT CALLBACK SoundSettingsDlgProc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARA { int i; char tempstr[MAX_PATH]; + // Setup Sound Core Combo box SendDlgItemMessage(hDlg, IDC_SOUNDCORECB, CB_RESETCONTENT, 0, 0); SendDlgItemMessage(hDlg, IDC_SOUNDCORECB, CB_ADDSTRING, 0, (LPARAM)"None"); @@ -4833,6 +4857,15 @@ LRESULT CALLBACK SoundSettingsDlgProc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARA SendDlgItemMessage(hDlg, IDC_SOUNDCORECB, CB_SETCURSEL, i, 0); } + //update the synch mode + CheckDlgItem(hDlg,IDC_SYNCHMODE_DUAL,snd_synchmode==0); + CheckDlgItem(hDlg,IDC_SYNCHMODE_SYNCH,snd_synchmode==1); + SoundSettings_updateSynchMode(hDlg); + + //update the synch method + CheckDlgItem(hDlg,IDC_SYNCHMETHOD_N,snd_synchmethod==0); + CheckDlgItem(hDlg,IDC_SYNCHMETHOD_Z,snd_synchmethod==1); + //setup interpolation combobox SendDlgItemMessage(hDlg, IDC_SPU_INTERPOLATION_CB, CB_RESETCONTENT, 0, 0); SendDlgItemMessage(hDlg, IDC_SPU_INTERPOLATION_CB, CB_ADDSTRING, 0, (LPARAM)"None (fastest, sounds bad)"); @@ -4849,10 +4882,15 @@ LRESULT CALLBACK SoundSettingsDlgProc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARA // Set Selected Volume SendDlgItemMessage(hDlg, IDC_SLVOLUME, TBM_SETPOS, TRUE, sndvolume); + SoundSettings_updateVolumeReadout(hDlg); timerid = SetTimer(hDlg, 1, 500, NULL); return TRUE; } + case WM_HSCROLL: + SoundSettings_updateVolumeReadout(hDlg); + break; + case WM_TIMER: { if (timerid == wParam) @@ -4868,6 +4906,11 @@ LRESULT CALLBACK SoundSettingsDlgProc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARA { switch (LOWORD(wParam)) { + case IDC_SYNCHMODE_DUAL: + case IDC_SYNCHMODE_SYNCH: + SoundSettings_updateSynchMode(hDlg); + break; + case IDOK: { char tempstr[MAX_PATH]; @@ -4897,6 +4940,18 @@ LRESULT CALLBACK SoundSettingsDlgProc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARA WritePrivateProfileString("Sound", "Volume", tempstr, IniName); SPU_SetVolume(sndvolume); + //save the synch mode + if(IsDlgCheckboxChecked(hDlg,IDC_SYNCHMODE_DUAL)) snd_synchmode = 0; + if(IsDlgCheckboxChecked(hDlg,IDC_SYNCHMODE_SYNCH)) snd_synchmode = 1; + WritePrivateProfileInt("Sound", "SynchMode", snd_synchmode, IniName); + + //save the synch method + if(IsDlgCheckboxChecked(hDlg,IDC_SYNCHMETHOD_N)) snd_synchmethod = 0; + if(IsDlgCheckboxChecked(hDlg,IDC_SYNCHMETHOD_Z)) snd_synchmethod = 1; + WritePrivateProfileInt("Sound", "SynchMethod", snd_synchmethod, IniName); + + SPU_SetSynchMode(snd_synchmode, snd_synchmethod); + //write interpolation type CommonSettings.spuInterpolationMode = (SPUInterpolationMode)SendDlgItemMessage(hDlg, IDC_SPU_INTERPOLATION_CB, CB_GETCURSEL, 0, 0); WritePrivateProfileInt("Sound","SPUInterpolation",(int)CommonSettings.spuInterpolationMode, IniName); diff --git a/desmume/src/windows/resource.h b/desmume/src/windows/resource.h index 2366ce500..4aa35839b 100644 --- a/desmume/src/windows/resource.h +++ b/desmume/src/windows/resource.h @@ -308,7 +308,6 @@ #define IDC_GGREEN 1005 #define IDC_EDIT12 1006 #define IDC_ROTATE270 1006 -#define IDC_SPU_CACHE 1006 #define IDC_GRED 1006 #define IDC_WIFIMODE0 1006 #define IDC_ARM7BIOSBROWSE 1007 @@ -336,14 +335,20 @@ #define IDC_RAWDUMP 1012 #define IDC_REFRESH 1012 #define IDC_WINDOW3X 1012 +#define IDC_SYNCHMODE_DUAL 1012 #define IDC_EDIT02 1013 #define IDC_WINDOW4X 1013 #define IDC_VIEWMODE 1013 +#define IDC_SYNCHMODE_SYNCH 1013 #define IDC_EDIT04 1014 #define IDC_FIRMWAREBROWSE 1014 +#define IDC_VOLUME 1014 +#define IDC_SYNCHMETHOD_N 1015 #define IDC_BGMAP_CHARBASE 1016 #define IDC_FIRMWAREBOOT 1016 +#define IDC_SYNCHMETHOD_Z 1016 #define IDC_BGMAP_SCRBASE 1017 +#define IDC_GROUP_SYNCHMETHOD 1017 #define IDC_AUTOUPDATE_ASM 1018 #define IDC_BGMAP_PRIO 1018 #define IDC_BGMAP_PAL 1019 @@ -793,7 +798,7 @@ #ifndef APSTUDIO_READONLY_SYMBOLS #define _APS_NEXT_RESOURCE_VALUE 105 #define _APS_NEXT_COMMAND_VALUE 40008 -#define _APS_NEXT_CONTROL_VALUE 1012 +#define _APS_NEXT_CONTROL_VALUE 1018 #define _APS_NEXT_SYMED_VALUE 101 #endif #endif diff --git a/desmume/src/windows/resources.rc b/desmume/src/windows/resources.rc index 61e6448edc8f64d5af492088f7b62f45b6ae1d2e..315c398c117e89c4f2553838926c907175005bfa 100644 GIT binary patch delta 1699 zcmbVNU2Icj7=BNCz7oK)R#%72g|mjpjAi!pcf~kpwz9F%!5nDB3#_Fbi)#mMaYBr8 zp)TGCdJ^6n6IcAXKuI=(X<}kR;*|+exip&KG%>p(gbU)8`n+G)b(r`Q)AW4jyyyL% z=lR~}{l4?f8{K#BY&&<=sNI|&TQM4AE3V5yUcBmAoZk{~<8L;$1k~9+UQix$bCN=2 zkxdDT^Nnwf3BI=5^Cp)E)wzo|j37V%wX0J_C`C3uHl(Ihl%kZB+g``|BFMfys*^w7 zqaNOvqBxLjia7NTdpJC-9^qTd#)F$9GC{uyv-fA?6ki($?84h_lLynP-;#~~Hr z3x;8Ch$1;gamW8vm$Q&JMcDWnY>Z;O>(`d1Z5egDO9(`6QVWu@$Wz4W&DlMZbcV{5 zrxR46S=>$2S;|ojBc3=rrgpOL2gB?bzziGnggXi4%UhNG;%TJLpwg3ch>qy71@<_l zsk#PjQ6C^H$UY9>Rd7A`Q3?WZKMm6m9Rtp@G>*Gb%F;o^GKdWWIZX$+e#w3C;$3sA zKZ3%FmZqX8*0KCb{I$QXW0p=sfDl))k`fg(t&9>aV=O?7{4FXXqk^%jMHV2eg!_M& z)|MOM4=%a0BCjK&y^O7jjB|a1E8zU}T`7I5M&7fp+aIyl=%6d} z6RMbf3V8+O<#CmM6tAnu$cx+Cy?V2*wmUZ7`$jz1hnyU?mng9QMn?kuP^6PU`rpjr zUR*ngLqjvO?;+JWG^fc0xQ3-#=#d=cAIDiBawImZ8@X8s3r|v@R=kMpD&IVBZud$@ zN?moEaJu3NIN?(8nP%g!KR;Rx6*~@x5qvZhL&)qRm#)<`8fv{|B?$bRnWi=3O#G_wd;ZpEjOeW(r&fi z#`%fpDCS)>(;R%6E9{kb14)uIKLQI@kEv{8e4;BSY%vsW`fh5m&Q#`cPepy`3j z+x7U2jPJZ+D#w{sAq~r_SWbEkxQwk%tf^uH7>fy3d8F-I??!C1(@1MAx(1Y{o^g8+9)$Vh$=QYsz(PTGA-w*zZE&WXy{FrGl)_|51cZ^HK)hw6llwcudyI{!}C? z0)Mfyow{k2gLU0!M)(dciVFPH!FJ%$ z9|hUWyRRDE*XT^^DQyR?RI=T8VwPo$pI#E?PA`cUt(fxwYtYr#W&LKSNvPKc-gjcW zKvoQ;;f<)c3(KyG4pd)~3MMDY@T^DJh2Qh(OXtKiRL11ITnbI$Nu%I4Xx?=T&dgH~ zq9fY}Cn16>Cdtg9Irk*Gyp{Iy*R^NFBQQ2rv528T)=Wi)aHUVYe}kH;T+?EL+yzej z`WacnFGql6+3IhpRS7KxG*7^q~uhI-hb^s6zF-FDh@yw z^gx)7ah|67gjn)R{ESk jKvOpF0Po9sClW414kTEFIXdnqkByTp&7GJvWflGlCkWIy diff --git a/desmume/src/windows/snddx.cpp b/desmume/src/windows/snddx.cpp index 3aced1660..99c638a45 100755 --- a/desmume/src/windows/snddx.cpp +++ b/desmume/src/windows/snddx.cpp @@ -69,37 +69,22 @@ static LONG soundvolume; static int issoundmuted; ////////////////////////////////////////////////////////////////////////////// +static volatile bool doterminate; +static volatile bool terminated; -//extern volatile int win_sound_samplecounter; -HANDLE hSNDDXThread = INVALID_HANDLE_VALUE; -extern HANDLE hSoundThreadWakeup; -bool bTerminateSoundThread = false; -bool bSilence = false; +extern volatile int win_sound_samplecounter; DWORD WINAPI SNDDXThread( LPVOID ) { - for(;;) - { - if(bTerminateSoundThread) break; - - if (bSilence) - { - if (WaitForSingleObject(hSoundThreadWakeup, 10) == WAIT_OBJECT_0) - bSilence = false; - } - else - { - // If the sound thread wakeup event is not signaled after a quarter second, output silence - if (WaitForSingleObject(hSoundThreadWakeup, 250) == WAIT_TIMEOUT) - bSilence = true; - } - + for(;;) { + if(doterminate) break; { Lock lock; - SPU_Emulate_user(!bSilence); + SPU_Emulate_user(); } + Sleep(10); } - + terminated = true; return 0; } @@ -199,9 +184,9 @@ int SNDDXInit(int buffersize) soundvolume = DSBVOLUME_MAX; issoundmuted = 0; - bSilence = false; - bTerminateSoundThread = false; - hSNDDXThread = CreateThread(0, 0, SNDDXThread, 0, 0, 0); + doterminate = false; + terminated = false; + CreateThread(0,0,SNDDXThread,0,0,0); return 0; } @@ -212,9 +197,10 @@ void SNDDXDeInit() { DWORD status=0; - bTerminateSoundThread = true; - SetEvent(hSoundThreadWakeup); - WaitForSingleObject(hSNDDXThread, INFINITE); + doterminate = true; + while(!terminated) { + Sleep(1); + } if (lpDSB2) { @@ -251,15 +237,22 @@ void SNDDXUpdateAudio(s16 *buffer, u32 num_samples) DWORD buffer1_size, buffer2_size; DWORD status; - lpDSB2->GetStatus(&status); + int samplecounter; + { + Lock lock; + samplecounter = win_sound_samplecounter -= num_samples; + } + + bool silence = (samplecounter<-44100*15/60); //behind by more than a quarter second -> silence + + IDirectSoundBuffer8_GetStatus(lpDSB2, &status); if (status & DSBSTATUS_BUFFERLOST) return; // fix me - lpDSB2->Lock(soundoffset, num_samples * sizeof(s16) * 2, &buffer1, &buffer1_size, &buffer2, &buffer2_size, 0); + IDirectSoundBuffer8_Lock(lpDSB2, soundoffset, num_samples * sizeof(s16) * 2, &buffer1, &buffer1_size, &buffer2, &buffer2_size, 0); - if(bSilence) - { + if(silence) { memset(buffer1, 0, buffer1_size); if(buffer2) memset(buffer2, 0, buffer2_size); @@ -274,9 +267,10 @@ void SNDDXUpdateAudio(s16 *buffer, u32 num_samples) soundoffset += buffer1_size + buffer2_size; soundoffset %= soundbufsize; - lpDSB2->Unlock(buffer1, buffer1_size, buffer2, buffer2_size); + IDirectSoundBuffer8_Unlock(lpDSB2, buffer1, buffer1_size, buffer2, buffer2_size); } + ////////////////////////////////////////////////////////////////////////////// u32 SNDDXGetAudioSpace() @@ -319,8 +313,16 @@ void SNDDXUnMuteAudio() void SNDDXSetVolume(int volume) { - if (!lpDSB2) return ; /* might happen when changing sounddevice on the fly, caused a gpf */ - soundvolume = (((LONG)volume) - 100) * 100; + if (!lpDSB2) return ; //might happen when changing sounddevice on the fly, caused a gpf + + if(volume==0) + soundvolume = DSBVOLUME_MIN; + else + { + float attenuate = 1000 * (float)log(100.0f/volume); + soundvolume = -(int)attenuate; + } + if (!issoundmuted) lpDSB2->SetVolume(soundvolume); }