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.

This commit is contained in:
zeromus 2009-10-20 06:52:07 +00:00
parent 959d551b34
commit 38217c032f
6 changed files with 622 additions and 120 deletions

View File

@ -23,16 +23,16 @@
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/ */
#include <stdlib.h>
#include <string.h>
#define _USE_MATH_DEFINES #define _USE_MATH_DEFINES
#include <math.h> #include <math.h>
#ifndef M_PI #ifndef M_PI
#define M_PI 3.1415926535897932386 #define M_PI 3.1415926535897932386
#endif #endif
#define K_ADPCM_LOOPING_RECOVERY_INDEX 99999 #include <stdlib.h>
#include <string.h>
#include <queue>
#include <vector>
#include "debug.h" #include "debug.h"
#include "MMU.h" #include "MMU.h"
@ -43,12 +43,465 @@
#include "NDSSystem.h" #include "NDSSystem.h"
#include "matrix.h" #include "matrix.h"
#define K_ADPCM_LOOPING_RECOVERY_INDEX 99999
//#undef FORCEINLINE //#undef FORCEINLINE
//#define 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<typename T> inline T _abs(T val)
{
if(val<0) return -val;
else return val;
}
template<typename T> inline T moveValueTowards(T val, T target, T incr)
{
incr = _abs(incr);
T delta = _abs(target-val);
if(val<target) val += incr;
else if(val>target) 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<samples_provided;i++) {
s16 left = *buf++;
s16 right = *buf++;
adjustobuf.enqueue(left,right);
}
}
//returns the number of samples actually supplied, which may not match the number requested
virtual int output_samples(s16* buf, int samples_requested)
{
int done = 0;
if(!mixqueue_go) {
if(adjustobuf.size > 200)
mixqueue_go = true;
}
else
{
for(int i=0;i<samples_requested;i++) {
if(adjustobuf.size==0) {
mixqueue_go = false;
break;
}
done++;
s16 left, right;
adjustobuf.dequeue(left,right);
*buf++ = left;
*buf++ = right;
}
}
return done;
}
private:
class Adjustobuf
{
public:
Adjustobuf(int _minLatency, int _maxLatency)
: size(0)
, minLatency(_minLatency)
, maxLatency(_maxLatency)
{
rollingTotalSize = 0;
targetLatency = (maxLatency + minLatency)/2;
rate = 1.0f;
cursor = 0.0f;
curr[0] = curr[1] = 0;
kAverageSize = 80000;
}
float rate, cursor;
int minLatency, targetLatency, maxLatency;
std::queue<s16> buffer;
int size;
s16 curr[2];
std::queue<int> 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<typename T>
struct ssampT
{
T l, r;
enum { TMAX = (1 << ((sizeof(T) * 8) - 1)) - 1 };
ssampT() {}
ssampT(T ll, T rr) : l(ll), r(rr) {}
template<typename T2>
ssampT(ssampT<T2> 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<T>(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<T>(lrv,rrv);
}
};
typedef ssampT<s16> ssamp;
std::vector<ssamp> 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<y) ? x : (2*y-x-1);
}
void emit_samples(s16* outbuf, ssamp* samplebuf, int samples)
{
for(int i=0;i<samples;i++) {
*outbuf++ = samplebuf[i].l;
*outbuf++ = samplebuf[i].r;
}
}
public:
NitsujaSynchronizer()
{}
virtual void enqueue_samples(s16* buf, int samples_provided)
{
for(int i=0;i<samples_provided;i++)
{
sampleQueue.push_back(ssamp(buf[0],buf[1]));
buf += 2;
}
}
virtual int output_samples(s16* buf, int samples_requested)
{
int audiosize = samples_requested;
int queued = sampleQueue.size();
// truncate input and output sizes to 8 because I am too lazy to deal with odd numbers
audiosize &= ~7;
queued &= ~7;
if(queued > 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<ssamp> 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_core = 0;
SPU_struct *SPU_user = 0; SPU_struct *SPU_user = 0;
int SPU_currentCoreNum = SNDCORE_DUMMY; 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; static SoundInterface_struct *SNDCore=NULL;
extern SoundInterface_struct *SNDCoreList[]; extern SoundInterface_struct *SNDCoreList[];
@ -93,8 +546,6 @@ static const double ARM7_CLOCK = 33513982;
static double samples = 0; static double samples = 0;
//////////////////////////////////////////////////////////////////////////////
template<typename T> template<typename T>
static FORCEINLINE T MinMax(T val, T min, T max) 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; return val;
} }
////////////////////////////////////////////////////////////////////////////// //--------------external spu interface---------------
int SPU_ChangeSoundCore(int coreid, int buffersize) int SPU_ChangeSoundCore(int coreid, int buffersize)
{ {
@ -153,6 +604,8 @@ int SPU_ChangeSoundCore(int coreid, int buffersize)
//enable the user spu //enable the user spu
SPU_user = new SPU_struct(buffersize); SPU_user = new SPU_struct(buffersize);
SNDCore->SetVolume(volume);
return 0; return 0;
} }
@ -161,20 +614,19 @@ SoundInterface_struct *SPU_SoundCore()
return SNDCore; return SNDCore;
} }
//////////////////////////////////////////////////////////////////////////////
//static double cos_lut[256]; //static double cos_lut[256];
int SPU_Init(int coreid, int buffersize) int SPU_Init(int coreid, int buffersize)
{ {
int i, j; 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++) //for(int i=0;i<256;i++)
// cos_lut[i] = cos(i/256.0*M_PI); // cos_lut[i] = cos(i/256.0*M_PI);
SPU_core = new SPU_struct(740); SPU_core = new SPU_struct(740);
SPU_Reset(); SPU_Reset();
//create adpcm decode accelerator lookups
for(i = 0; i < 16; i++) for(i = 0; i < 16; i++)
{ {
for(j = 0; j < 89; j++) 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]; if(i & 0x8) precalcdifftbl[j][i] = -precalcdifftbl[j][i];
} }
} }
for(i = 0; i < 8; i++) for(i = 0; i < 8; i++)
{ {
for(j = 0; j < 89; j++) for(j = 0; j < 89; j++)
@ -195,8 +646,6 @@ int SPU_Init(int coreid, int buffersize)
return SPU_ChangeSoundCore(coreid, buffersize); return SPU_ChangeSoundCore(coreid, buffersize);
} }
//////////////////////////////////////////////////////////////////////////////
void SPU_Pause(int pause) void SPU_Pause(int pause)
{ {
if (SNDCore == NULL) return; if (SNDCore == NULL) return;
@ -207,16 +656,27 @@ void SPU_Pause(int pause)
SNDCore->UnMuteAudio(); 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) void SPU_SetVolume(int volume)
{ {
::volume = volume;
if (SNDCore) if (SNDCore)
SNDCore->SetVolume(volume); SNDCore->SetVolume(volume);
} }
//////////////////////////////////////////////////////////////////////////////
void SPU_Reset(void) void SPU_Reset(void)
{ {
@ -228,6 +688,7 @@ void SPU_Reset(void)
if(SNDCore && SPU_user) { if(SNDCore && SPU_user) {
SNDCore->DeInit(); SNDCore->DeInit();
SNDCore->Init(SPU_user->bufsize*2); SNDCore->Init(SPU_user->bufsize*2);
SNDCore->SetVolume(volume);
//todo - check success? //todo - check success?
} }
@ -238,6 +699,8 @@ void SPU_Reset(void)
samples = 0; samples = 0;
} }
//------------------------------------------
void SPU_struct::reset() void SPU_struct::reset()
{ {
memset(sndbuf,0,bufsize*2*4); memset(sndbuf,0,bufsize*2*4);
@ -782,8 +1245,7 @@ FORCEINLINE static void _SPU_ChanUpdate(const bool actuallyMix, SPU_struct* cons
} }
} }
template<bool actuallyMix> static void SPU_MixAudio(bool actuallyMix, SPU_struct *SPU, int length)
static void SPU_MixAudio(SPU_struct *SPU, int length)
{ {
u8 vol; u8 vol;
@ -850,10 +1312,12 @@ void SPU_Emulate_core()
spu_core_samples = (int)(samples); spu_core_samples = (int)(samples);
samples -= spu_core_samples; samples -= spu_core_samples;
if(driver->AVI_IsRecording() || driver->WAV_IsRecording()) bool synchronize = (synchmode == ESynchMode_Synchronous);
SPU_MixAudio<true>(SPU_core,spu_core_samples); bool mix = driver->AVI_IsRecording() || driver->WAV_IsRecording() || synchronize;
else
SPU_MixAudio<false>(SPU_core,spu_core_samples); 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) void SPU_Emulate_user(bool mix)
@ -872,9 +1336,20 @@ void SPU_Emulate_user(bool mix)
//printf("mix %i samples\n", audiosize); //printf("mix %i samples\n", audiosize);
if (audiosize > SPU_user->bufsize) if (audiosize > SPU_user->bufsize)
audiosize = SPU_user->bufsize; audiosize = SPU_user->bufsize;
if (mix) SPU_MixAudio<true>(SPU_user,audiosize);
if(synchmode == ESynchMode_Synchronous)
{
int done = synchronizer->output_samples(SPU_user->outbuf, audiosize);
for(int j=0;j<done;j++)
SNDCore->UpdateAudio(&SPU_user->outbuf[j*2],1);
}
else
{
SPU_MixAudio(mix,SPU_user,audiosize);
SNDCore->UpdateAudio(SPU_user->outbuf, audiosize); SNDCore->UpdateAudio(SPU_user->outbuf, audiosize);
} }
}
} }
////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////
@ -901,51 +1376,15 @@ SoundInterface_struct SNDDummy = {
SNDDummySetVolume 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) //---------wav writer------------
{
return 0;
}
//////////////////////////////////////////////////////////////////////////////
void SNDDummyDeInit()
{
}
//////////////////////////////////////////////////////////////////////////////
void SNDDummyUpdateAudio(s16 *buffer, u32 num_samples)
{
}
//////////////////////////////////////////////////////////////////////////////
u32 SNDDummyGetAudioSpace()
{
return 740;
}
//////////////////////////////////////////////////////////////////////////////
void SNDDummyMuteAudio()
{
}
//////////////////////////////////////////////////////////////////////////////
void SNDDummyUnMuteAudio()
{
}
//////////////////////////////////////////////////////////////////////////////
void SNDDummySetVolume(int volume)
{
}
//////////////////////////////////////////////////////////////////////////////
typedef struct { typedef struct {
char id[4]; char id[4];

View File

@ -131,6 +131,7 @@ SoundInterface_struct *SPU_SoundCore();
int SPU_Init(int coreid, int buffersize); int SPU_Init(int coreid, int buffersize);
void SPU_Pause(int pause); void SPU_Pause(int pause);
void SPU_SetVolume(int volume); void SPU_SetVolume(int volume);
void SPU_SetSynchMode(int mode, int method);
void SPU_Reset(void); void SPU_Reset(void);
void SPU_DeInit(void); void SPU_DeInit(void);
void SPU_KeyOn(int channel); void SPU_KeyOn(int channel);

View File

@ -194,6 +194,11 @@ inline bool IsDlgCheckboxChecked(HWND hDlg, int id)
return IsDlgButtonChecked(hDlg,id) == BST_CHECKED; 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); LRESULT CALLBACK WindowProcedure (HWND, UINT, WPARAM, LPARAM);
char SavName[MAX_PATH] = ""; char SavName[MAX_PATH] = "";
@ -261,11 +266,11 @@ extern bool userTouchesScreen;
/*__declspec(thread)*/ bool inFrameBoundary = false; /*__declspec(thread)*/ bool inFrameBoundary = false;
//static char IniName[MAX_PATH]; static int sndcoretype=SNDCORE_DIRECTX;
int sndcoretype=SNDCORE_DIRECTX; static int sndbuffersize=735*4;
int sndbuffersize=735*4; static int sndvolume=100;
int sndvolume=100; static int snd_synchmode=0;
HANDLE hSoundThreadWakeup = INVALID_HANDLE_VALUE; static int snd_synchmethod=0;
SoundInterface_struct *SNDCoreList[] = { SoundInterface_struct *SNDCoreList[] = {
&SNDDummy, &SNDDummy,
@ -1198,8 +1203,8 @@ static void StepRunLoop_Core()
{ {
Lock lock; Lock lock;
NDS_exec<false>(); NDS_exec<false>();
SPU_Emulate_user();
win_sound_samplecounter = 735; win_sound_samplecounter = 735;
SetEvent(hSoundThreadWakeup);
} }
inFrameBoundary = true; inFrameBoundary = true;
DRV_AviVideoUpdate((u16*)GPU_screen); DRV_AviVideoUpdate((u16*)GPU_screen);
@ -1736,12 +1741,12 @@ class WinDriver : public BaseDriver
virtual bool EMU_IsEmulationPaused() virtual bool EMU_IsEmulationPaused()
{ {
return emu_paused; return emu_paused!=0;
} }
virtual bool EMU_IsFastForwarding() virtual bool EMU_IsFastForwarding()
{ {
return FastForward; return FastForward!=0;
} }
virtual bool EMU_HasEmulationStarted() virtual bool EMU_HasEmulationStarted()
@ -1820,8 +1825,6 @@ int _main()
display_invoke_done_event = CreateEvent(NULL, FALSE, FALSE, NULL); display_invoke_done_event = CreateEvent(NULL, FALSE, FALSE, NULL);
display_wakeup_event = CreateEvent(NULL, FALSE, FALSE, NULL); display_wakeup_event = CreateEvent(NULL, FALSE, FALSE, NULL);
hSoundThreadWakeup = CreateEvent(NULL, FALSE, FALSE, NULL);
#ifdef GDB_STUB #ifdef GDB_STUB
gdbstub_handle_t arm9_gdb_stub; gdbstub_handle_t arm9_gdb_stub;
gdbstub_handle_t arm7_gdb_stub; gdbstub_handle_t arm7_gdb_stub;
@ -2151,6 +2154,10 @@ int _main()
sndvolume = GetPrivateProfileInt("Sound","Volume",100, IniName); sndvolume = GetPrivateProfileInt("Sound","Volume",100, IniName);
SPU_SetVolume(sndvolume); 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.DebugConsole = GetPrivateProfileBool("Emulation", "DebugConsole", FALSE, IniName);
CommonSettings.UseExtBIOS = GetPrivateProfileBool("BIOS", "UseExtBIOS", FALSE, IniName); CommonSettings.UseExtBIOS = GetPrivateProfileBool("BIOS", "UseExtBIOS", FALSE, IniName);
GetPrivateProfileString("BIOS", "ARM9BIOSFile", "bios9.bin", CommonSettings.ARM9BIOS, 256, IniName); GetPrivateProfileString("BIOS", "ARM9BIOSFile", "bios9.bin", CommonSettings.ARM9BIOS, 256, IniName);
@ -2262,8 +2269,6 @@ int _main()
UnregWndClass("DeSmuME"); UnregWndClass("DeSmuME");
CloseHandle(hSoundThreadWakeup);
return 0; return 0;
} }
@ -2992,8 +2997,13 @@ LRESULT CALLBACK WindowProcedure (HWND hwnd, UINT message, WPARAM wParam, LPARAM
static int tmp_execute; static int tmp_execute;
switch (message) // handle the messages switch (message) // handle the messages
{ {
case WM_EXITMENULOOP:
SPU_Pause(0);
break;
case WM_ENTERMENULOOP: //Update menu items that needs to be updated dynamically case WM_ENTERMENULOOP: //Update menu items that needs to be updated dynamically
{ {
SPU_Pause(1);
UpdateHotkeyAssignments(); //Add current hotkey mappings to menu item names UpdateHotkeyAssignments(); //Add current hotkey mappings to menu item names
MENUITEMINFO mii; MENUITEMINFO mii;
@ -4810,7 +4820,20 @@ LRESULT CALLBACK WifiSettingsDlgProc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM
return FALSE; 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; static UINT_PTR timerid=0;
switch (uMsg) switch (uMsg)
@ -4819,6 +4842,7 @@ LRESULT CALLBACK SoundSettingsDlgProc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARA
{ {
int i; int i;
char tempstr[MAX_PATH]; char tempstr[MAX_PATH];
// Setup Sound Core Combo box // Setup Sound Core Combo box
SendDlgItemMessage(hDlg, IDC_SOUNDCORECB, CB_RESETCONTENT, 0, 0); SendDlgItemMessage(hDlg, IDC_SOUNDCORECB, CB_RESETCONTENT, 0, 0);
SendDlgItemMessage(hDlg, IDC_SOUNDCORECB, CB_ADDSTRING, 0, (LPARAM)"None"); 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); 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 //setup interpolation combobox
SendDlgItemMessage(hDlg, IDC_SPU_INTERPOLATION_CB, CB_RESETCONTENT, 0, 0); SendDlgItemMessage(hDlg, IDC_SPU_INTERPOLATION_CB, CB_RESETCONTENT, 0, 0);
SendDlgItemMessage(hDlg, IDC_SPU_INTERPOLATION_CB, CB_ADDSTRING, 0, (LPARAM)"None (fastest, sounds bad)"); 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 // Set Selected Volume
SendDlgItemMessage(hDlg, IDC_SLVOLUME, TBM_SETPOS, TRUE, sndvolume); SendDlgItemMessage(hDlg, IDC_SLVOLUME, TBM_SETPOS, TRUE, sndvolume);
SoundSettings_updateVolumeReadout(hDlg);
timerid = SetTimer(hDlg, 1, 500, NULL); timerid = SetTimer(hDlg, 1, 500, NULL);
return TRUE; return TRUE;
} }
case WM_HSCROLL:
SoundSettings_updateVolumeReadout(hDlg);
break;
case WM_TIMER: case WM_TIMER:
{ {
if (timerid == wParam) if (timerid == wParam)
@ -4868,6 +4906,11 @@ LRESULT CALLBACK SoundSettingsDlgProc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARA
{ {
switch (LOWORD(wParam)) switch (LOWORD(wParam))
{ {
case IDC_SYNCHMODE_DUAL:
case IDC_SYNCHMODE_SYNCH:
SoundSettings_updateSynchMode(hDlg);
break;
case IDOK: case IDOK:
{ {
char tempstr[MAX_PATH]; char tempstr[MAX_PATH];
@ -4897,6 +4940,18 @@ LRESULT CALLBACK SoundSettingsDlgProc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARA
WritePrivateProfileString("Sound", "Volume", tempstr, IniName); WritePrivateProfileString("Sound", "Volume", tempstr, IniName);
SPU_SetVolume(sndvolume); 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 //write interpolation type
CommonSettings.spuInterpolationMode = (SPUInterpolationMode)SendDlgItemMessage(hDlg, IDC_SPU_INTERPOLATION_CB, CB_GETCURSEL, 0, 0); CommonSettings.spuInterpolationMode = (SPUInterpolationMode)SendDlgItemMessage(hDlg, IDC_SPU_INTERPOLATION_CB, CB_GETCURSEL, 0, 0);
WritePrivateProfileInt("Sound","SPUInterpolation",(int)CommonSettings.spuInterpolationMode, IniName); WritePrivateProfileInt("Sound","SPUInterpolation",(int)CommonSettings.spuInterpolationMode, IniName);

View File

@ -308,7 +308,6 @@
#define IDC_GGREEN 1005 #define IDC_GGREEN 1005
#define IDC_EDIT12 1006 #define IDC_EDIT12 1006
#define IDC_ROTATE270 1006 #define IDC_ROTATE270 1006
#define IDC_SPU_CACHE 1006
#define IDC_GRED 1006 #define IDC_GRED 1006
#define IDC_WIFIMODE0 1006 #define IDC_WIFIMODE0 1006
#define IDC_ARM7BIOSBROWSE 1007 #define IDC_ARM7BIOSBROWSE 1007
@ -336,14 +335,20 @@
#define IDC_RAWDUMP 1012 #define IDC_RAWDUMP 1012
#define IDC_REFRESH 1012 #define IDC_REFRESH 1012
#define IDC_WINDOW3X 1012 #define IDC_WINDOW3X 1012
#define IDC_SYNCHMODE_DUAL 1012
#define IDC_EDIT02 1013 #define IDC_EDIT02 1013
#define IDC_WINDOW4X 1013 #define IDC_WINDOW4X 1013
#define IDC_VIEWMODE 1013 #define IDC_VIEWMODE 1013
#define IDC_SYNCHMODE_SYNCH 1013
#define IDC_EDIT04 1014 #define IDC_EDIT04 1014
#define IDC_FIRMWAREBROWSE 1014 #define IDC_FIRMWAREBROWSE 1014
#define IDC_VOLUME 1014
#define IDC_SYNCHMETHOD_N 1015
#define IDC_BGMAP_CHARBASE 1016 #define IDC_BGMAP_CHARBASE 1016
#define IDC_FIRMWAREBOOT 1016 #define IDC_FIRMWAREBOOT 1016
#define IDC_SYNCHMETHOD_Z 1016
#define IDC_BGMAP_SCRBASE 1017 #define IDC_BGMAP_SCRBASE 1017
#define IDC_GROUP_SYNCHMETHOD 1017
#define IDC_AUTOUPDATE_ASM 1018 #define IDC_AUTOUPDATE_ASM 1018
#define IDC_BGMAP_PRIO 1018 #define IDC_BGMAP_PRIO 1018
#define IDC_BGMAP_PAL 1019 #define IDC_BGMAP_PAL 1019
@ -793,7 +798,7 @@
#ifndef APSTUDIO_READONLY_SYMBOLS #ifndef APSTUDIO_READONLY_SYMBOLS
#define _APS_NEXT_RESOURCE_VALUE 105 #define _APS_NEXT_RESOURCE_VALUE 105
#define _APS_NEXT_COMMAND_VALUE 40008 #define _APS_NEXT_COMMAND_VALUE 40008
#define _APS_NEXT_CONTROL_VALUE 1012 #define _APS_NEXT_CONTROL_VALUE 1018
#define _APS_NEXT_SYMED_VALUE 101 #define _APS_NEXT_SYMED_VALUE 101
#endif #endif
#endif #endif

Binary file not shown.

View File

@ -69,37 +69,22 @@ static LONG soundvolume;
static int issoundmuted; static int issoundmuted;
////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////
static volatile bool doterminate;
static volatile bool terminated;
//extern volatile int win_sound_samplecounter; extern volatile int win_sound_samplecounter;
HANDLE hSNDDXThread = INVALID_HANDLE_VALUE;
extern HANDLE hSoundThreadWakeup;
bool bTerminateSoundThread = false;
bool bSilence = false;
DWORD WINAPI SNDDXThread( LPVOID ) DWORD WINAPI SNDDXThread( LPVOID )
{ {
for(;;) for(;;) {
{ if(doterminate) break;
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;
}
{ {
Lock lock; Lock lock;
SPU_Emulate_user(!bSilence); SPU_Emulate_user();
} }
Sleep(10);
} }
terminated = true;
return 0; return 0;
} }
@ -199,9 +184,9 @@ int SNDDXInit(int buffersize)
soundvolume = DSBVOLUME_MAX; soundvolume = DSBVOLUME_MAX;
issoundmuted = 0; issoundmuted = 0;
bSilence = false; doterminate = false;
bTerminateSoundThread = false; terminated = false;
hSNDDXThread = CreateThread(0, 0, SNDDXThread, 0, 0, 0); CreateThread(0,0,SNDDXThread,0,0,0);
return 0; return 0;
} }
@ -212,9 +197,10 @@ void SNDDXDeInit()
{ {
DWORD status=0; DWORD status=0;
bTerminateSoundThread = true; doterminate = true;
SetEvent(hSoundThreadWakeup); while(!terminated) {
WaitForSingleObject(hSNDDXThread, INFINITE); Sleep(1);
}
if (lpDSB2) if (lpDSB2)
{ {
@ -251,15 +237,22 @@ void SNDDXUpdateAudio(s16 *buffer, u32 num_samples)
DWORD buffer1_size, buffer2_size; DWORD buffer1_size, buffer2_size;
DWORD status; 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) if (status & DSBSTATUS_BUFFERLOST)
return; // fix me 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); memset(buffer1, 0, buffer1_size);
if(buffer2) if(buffer2)
memset(buffer2, 0, buffer2_size); memset(buffer2, 0, buffer2_size);
@ -274,9 +267,10 @@ void SNDDXUpdateAudio(s16 *buffer, u32 num_samples)
soundoffset += buffer1_size + buffer2_size; soundoffset += buffer1_size + buffer2_size;
soundoffset %= soundbufsize; soundoffset %= soundbufsize;
lpDSB2->Unlock(buffer1, buffer1_size, buffer2, buffer2_size); IDirectSoundBuffer8_Unlock(lpDSB2, buffer1, buffer1_size, buffer2, buffer2_size);
} }
////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////
u32 SNDDXGetAudioSpace() u32 SNDDXGetAudioSpace()
@ -319,8 +313,16 @@ void SNDDXUnMuteAudio()
void SNDDXSetVolume(int volume) void SNDDXSetVolume(int volume)
{ {
if (!lpDSB2) return ; /* might happen when changing sounddevice on the fly, caused a gpf */ if (!lpDSB2) return ; //might happen when changing sounddevice on the fly, caused a gpf
soundvolume = (((LONG)volume) - 100) * 100;
if(volume==0)
soundvolume = DSBVOLUME_MIN;
else
{
float attenuate = 1000 * (float)log(100.0f/volume);
soundvolume = -(int)attenuate;
}
if (!issoundmuted) if (!issoundmuted)
lpDSB2->SetVolume(soundvolume); lpDSB2->SetVolume(soundvolume);
} }