diff --git a/SConstruct b/SConstruct index d42a40b7..cc902a59 100644 --- a/SConstruct +++ b/SConstruct @@ -30,7 +30,7 @@ print "platform: ", env['PLATFORM'] if env['PLATFORM'] == 'cygwin': env.Append(CCFLAGS = " -mno-cygwin") env.Append(LINKFLAGS = " -mno-cygwin") - env['LIBS'] = ['ddraw','dinput','dsound','gdi32','dxguid','winmm','shell32','wsock32','comdlg32','ole32']; + env['LIBS'] = ['wsock32']; conf = Configure(env) if not conf.CheckLib('SDL'): @@ -60,6 +60,8 @@ if env['FRAMESKIP']: # parse SDL cflags/libs env.ParseConfig('sdl-config --cflags --libs') +print "LIBS:",env['CCFLAGS'] +#env['IBS'] = ['wsock32', 'SDL', 'z', 'mingw32', 'SDL'] Export('env') SConscript(['src/SConscript']) diff --git a/TODO-PROJECT b/TODO-PROJECT index 394918b3..f2d53513 100644 --- a/TODO-PROJECT +++ b/TODO-PROJECT @@ -2,6 +2,8 @@ Items to be completed before 2.0 release FASTAPASS / FP_FASTAPASS / Are these archaic? They suck - ?? +Separate frameskip/pause from EmulationPaused - zeromus + Make ALL Debugging code conditional - zeromus Doxygen integration - DONE diff --git a/src/drivers/common/Makefile.am.inc b/src/drivers/common/Makefile.am.inc deleted file mode 100644 index 626a617d..00000000 --- a/src/drivers/common/Makefile.am.inc +++ /dev/null @@ -1 +0,0 @@ -fceu_SOURCES += drivers/common/args.cpp drivers/common/cheat.cpp drivers/common/config.cpp drivers/common/vidblit.cpp drivers/common/hq2x.cpp drivers/common/hq3x.cpp drivers/common/scale2x.cpp drivers/common/scale3x.cpp drivers/common/scalebit.cpp diff --git a/src/drivers/win/OutputDS.cpp b/src/drivers/win/OutputDS.cpp new file mode 100644 index 00000000..b0139518 --- /dev/null +++ b/src/drivers/win/OutputDS.cpp @@ -0,0 +1,288 @@ +#include "oakra.h" + +#include "dsound.h" +#include + +#define LATENCY_MS (50) + + +int voltbl[] = { +-10000,-8750,-8018,-7499,-7097,-6768,-6490,-6250,-6037,-5847,-5675,-5518,-5374,-5240,-5116,-5000, +-4890,-4787,-4690,-4597,-4509,-4425,-4345,-4268,-4195,-4124,-4056,-3990,-3927,-3866,-3807,-3749, +-3694,-3640,-3588,-3537,-3488,-3440,-3393,-3347,-3303,-3259,-3217,-3175,-3135,-3095,-3056,-3018, +-2981,-2945,-2909,-2874,-2840,-2806,-2773,-2740,-2708,-2677,-2646,-2616,-2586,-2557,-2528,-2500, +-2472,-2444,-2417,-2390,-2364,-2338,-2312,-2287,-2262,-2238,-2213,-2190,-2166,-2143,-2120,-2097, +-2075,-2053,-2031,-2009,-1988,-1967,-1946,-1925,-1905,-1885,-1865,-1845,-1826,-1806,-1787,-1768, +-1750,-1731,-1713,-1695,-1677,-1659,-1641,-1624,-1607,-1590,-1573,-1556,-1539,-1523,-1506,-1490, +-1474,-1458,-1443,-1427,-1412,-1396,-1381,-1366,-1351,-1336,-1321,-1307,-1292,-1278,-1264,-1250, +-1235,-1222,-1208,-1194,-1180,-1167,-1153,-1140,-1127,-1114,-1101,-1088,-1075,-1062,-1050,-1037, +-1025,-1012,-1000,-988,-976,-963,-951,-940,-928,-916,-904,-893,-881,-870,-858,-847, +-836,-825,-814,-803,-792,-781,-770,-759,-748,-738,-727,-717,-706,-696,-685,-675, +-665,-655,-645,-635,-625,-615,-605,-595,-585,-576,-566,-556,-547,-537,-528,-518, +-509,-500,-490,-481,-472,-463,-454,-445,-436,-427,-418,-409,-400,-391,-383,-374, +-365,-357,-348,-340,-331,-323,-314,-306,-298,-289,-281,-273,-265,-256,-248,-240, +-232,-224,-216,-208,-200,-193,-185,-177,-169,-162,-154,-146,-139,-131,-123,-116, +-108,-101,-93,-86,-79,-71,-64,-57,-49,-42,-35,-28,-21,-14,-7,0}; + +class DSVoice : public OAKRA_Voice { +public: + OAKRA_Module_OutputDS *driver; + OAKRA_Format format; + int formatShift; + IDirectSoundBuffer *ds_buf; + int buflen; + unsigned int cPlay; + int vol,pan; + + virtual void setPan(int pan) { + this->pan = pan; + if(pan==0) ds_buf->SetPan(0); + else if(pan>0) ds_buf->SetPan(-voltbl[255-pan]); + else ds_buf->SetPan(voltbl[255+pan]); + } + virtual void setVol(int vol) { + this->vol = vol; + ds_buf->SetVolume(voltbl[vol]); + } + virtual int getVol() { return vol; } + virtual int getPan() { return pan; } + void setSource(OAKRA_Module *source) { + this->source = source; + } + virtual ~DSVoice() { + driver->freeVoiceInternal(this,true); + ds_buf->Release(); + } + DSVoice(OAKRA_Module_OutputDS *driver, OAKRA_Format &format, IDirectSound *ds_dev, bool global) { + this->driver = driver; + vol = 255; + pan = 0; + source = 0; + this->format = format; + formatShift = getFormatShift(format); + buflen = (format.rate * LATENCY_MS / 1000); + + WAVEFORMATEX wfx; + memset(&wfx, 0, sizeof(wfx)); + wfx.wFormatTag = WAVE_FORMAT_PCM; + wfx.nChannels = format.channels; + wfx.nSamplesPerSec = format.rate; + wfx.nAvgBytesPerSec = format.rate * format.size; + wfx.nBlockAlign = format.size; + wfx.wBitsPerSample = (format.format==OAKRA_S16?16:8); + wfx.cbSize = sizeof(wfx); + + DSBUFFERDESC dsbd; + memset(&dsbd, 0, sizeof(dsbd)); + dsbd.dwSize = sizeof(dsbd); + dsbd.dwFlags = DSBCAPS_GETCURRENTPOSITION2 | DSBCAPS_LOCSOFTWARE | DSBCAPS_CTRLVOLUME ; + if(global) dsbd.dwFlags |= DSBCAPS_GLOBALFOCUS ; + dsbd.dwBufferBytes = buflen * format.size; + dsbd.lpwfxFormat = &wfx; + + HRESULT hr = ds_dev->CreateSoundBuffer(&dsbd,&ds_buf,0); + cPlay = 0; + + hr = ds_buf->Play(0,0,DSBPLAY_LOOPING); + int xxx=9; + } + + //not supported + virtual void volFade(int start, int end, int ms) {} + + void update() { + DWORD play, write; + HRESULT hr = ds_buf->GetCurrentPosition(&play, &write); + play >>= formatShift; + write >>= formatShift; + + int todo; + if(playLock( + cPlay<>= formatShift; + buffer2_length >>= formatShift; + int done = 0; + if(source) { + done = source->generate(buffer1_length,buffer1); + if(done != buffer1_length) { + generateSilence(buffer1_length - done,(char *)buffer1 + (done<generate(buffer2_length,buffer2); + if(done != buffer2_length) { + generateSilence(buffer2_length - done,(char *)buffer2 + (done<Unlock( + buffer1, buffer1_length, + buffer2, buffer2_length); + + cPlay = play; + } +}; + +class Data { +public: + bool global; + IDirectSound* ds_dev; + std::vector voices; + CRITICAL_SECTION criticalSection; +}; + + +class ThreadData { +public: + ThreadData() { kill = dead = false; } + OAKRA_Module_OutputDS *ds; + bool kill,dead; +}; + +OAKRA_Module_OutputDS::OAKRA_Module_OutputDS() { + data = new Data(); + ((Data *)data)->global = false; + InitializeCriticalSection(&((Data *)data)->criticalSection); +} + +OAKRA_Module_OutputDS::~OAKRA_Module_OutputDS() { + //ask the driver to shutdown, and wait for it to do so + ((ThreadData *)threadData)->kill = true; + while(!((ThreadData *)threadData)->dead) Sleep(1); + + ////kill all the voices + std::vector voicesCopy = ((Data *)data)->voices; + int voices = (int)voicesCopy.size(); + for(int i=0;icriticalSection); + ((Data *)data)->ds_dev->Release(); + delete (Data *)data; + delete (ThreadData *)threadData; +} + +OAKRA_Voice *OAKRA_Module_OutputDS::getVoice(OAKRA_Format &format, OAKRA_Module *source) { + DSVoice *dsv = (DSVoice *)getVoice(format); + dsv->setSource(source); + return dsv; +} + +OAKRA_Voice *OAKRA_Module_OutputDS::getVoice(OAKRA_Format &format) { + DSVoice *voice = new DSVoice(this,format,((Data *)data)->ds_dev,((Data *)data)->global); + ((Data *)data)->voices.push_back(voice); + return voice; +} +void OAKRA_Module_OutputDS::freeVoice(OAKRA_Voice *voice) { + freeVoiceInternal(voice,false); +} + +void OAKRA_Module_OutputDS::freeVoiceInternal(OAKRA_Voice *voice, bool internal) { + lock(); + Data *data = (Data *)this->data; + int j = -1; + for(int i=0;i<(int)data->voices.size();i++) + if(data->voices[i] == voice) j = i; + if(j!=-1) + data->voices.erase(data->voices.begin()+j); + if(!internal) + delete voice; + unlock(); +} + + + +void OAKRA_Module_OutputDS::start(void *hwnd) { + HRESULT hr = CoInitialize(NULL); + IDirectSound* ds_dev; + hr = CoCreateInstance(CLSID_DirectSound,0,CLSCTX_INPROC_SERVER,IID_IDirectSound,(void**)&ds_dev); + + if(!hwnd) { + hwnd = GetDesktopWindow(); + ((Data *)data)->global = true; + } + + //use default device + hr = ds_dev->Initialize(0); + hr = ds_dev->SetCooperativeLevel((HWND)hwnd, DSSCL_NORMAL); + + ((Data *)data)->ds_dev = ds_dev; +} + + +DWORD WINAPI updateProc(LPVOID lpParameter) { + ThreadData *data = (ThreadData *)lpParameter; + for(;;) { + if(data->kill) break; + data->ds->update(); + Sleep(1); + } + data->dead = true; + return 0; +} + +void OAKRA_Module_OutputDS::beginThread() { + DWORD updateThreadId; + threadData = new ThreadData(); + ((ThreadData *)threadData)->ds = this; + HANDLE updateThread = CreateThread(0,0,updateProc,threadData,0,&updateThreadId); + SetThreadPriority(updateThread,THREAD_PRIORITY_TIME_CRITICAL); + //SetThreadPriority(updateThread,THREAD_PRIORITY_HIGHEST); +} + +void OAKRA_Module_OutputDS::endThread() { + ((ThreadData *)threadData)->kill = true; +} + +void OAKRA_Module_OutputDS::update() { + lock(); + int voices = (int)((Data *)data)->voices.size(); + + //render all the voices + for(int i=0;ivoices[i]->update(); + + //look for voices that are dead + std::vector deaders; + for(int i=0;ivoices[i]->dead) + deaders.push_back(((Data *)data)->voices[i]); + + //unlock the driver before killing voices! + //that way, the voice's death callback won't occur within the driver lock + unlock(); + + //kill those voices + for(int i=0;i<(int)deaders.size();i++) { + deaders[i]->callbackDied(); + freeVoice(deaders[i]); + } +} + +void OAKRA_Module_OutputDS::lock() { + EnterCriticalSection( &((Data *)this->data)->criticalSection ); +} + +void OAKRA_Module_OutputDS::unlock() { + LeaveCriticalSection( &((Data *)this->data)->criticalSection ); +} + diff --git a/src/drivers/win/input.cpp b/src/drivers/win/input.cpp index 154e55e1..dcc3a407 100644 --- a/src/drivers/win/input.cpp +++ b/src/drivers/win/input.cpp @@ -125,6 +125,7 @@ static void UpdateTopRider(void); static uint32 JSreturn=0; int NoWaiting=0; +bool turbo = false; #include "keyscan.h" static unsigned char *keys=0; @@ -1226,8 +1227,8 @@ static struct { EMUCMD_FRAME_ADVANCE, SCAN_TAB, }, { EMUCMD_SCREENSHOT, SCAN_F9 }, { EMUCMD_HIDE_MENU_TOGGLE, SCAN_ESCAPE }, - //{ EMUCMD_SPEED_SLOWER, SCAN_MINUS, }, // think about these - //{ EMUCMD_SPEED_FASTER, SCAN_EQUAL, }, // think about these + { EMUCMD_SPEED_SLOWER, SCAN_MINUS, }, // think about these + { EMUCMD_SPEED_FASTER, SCAN_EQUAL, }, // think about these { EMUCMD_SPEED_TURBO, SCAN_GRAVE, }, //tilde { EMUCMD_SAVE_SLOT_0, SCAN_0, }, { EMUCMD_SAVE_SLOT_1, SCAN_1, }, @@ -1826,11 +1827,13 @@ void MapInput(void) void FCEUD_TurboOn(void) { - NoWaiting|=1; + //NoWaiting|=1; + turbo = 1; } void FCEUD_TurboOff(void) { - NoWaiting&=~1; + //NoWaiting&=~1; + turbo = false; } diff --git a/src/drivers/win/main.cpp b/src/drivers/win/main.cpp index d1c7603b..b4fa6465 100644 --- a/src/drivers/win/main.cpp +++ b/src/drivers/win/main.cpp @@ -490,18 +490,17 @@ doloopy: { while(GameInfo) { - uint8 *gfx=0; - int32 *sound=0; - int32 ssize=0; + uint8 *gfx=0; ///contains framebuffer + int32 *sound=0; ///contains sound data buffer + int32 ssize=0; ///contains sound samples count #ifdef _USE_SHARED_MEMORY_ UpdateBasicBot(); #endif FCEU_UpdateBot(); - FCEUI_Emulate(&gfx, &sound, &ssize, 0); - xbsave = gfx; - FCEUD_Update(gfx, sound, ssize); + FCEUI_Emulate(&gfx, &sound, &ssize, 0); //emulate a single frame + FCEUD_Update(gfx, sound, ssize); //update displays and debug tools //mbg 6/30/06 - close game if we were commanded to by calls nested in FCEUI_Emulate() if(closeGame) @@ -510,7 +509,21 @@ doloopy: GameInfo = 0; } - + } + //xbsave = NULL; + RedrawWindow(hAppWnd,0,0,RDW_ERASE|RDW_INVALIDATE); + StopSound(); + } + Sleep(50); + if(!exiting) + goto doloopy; + + doexito: + DriverKill(); + timeEndPeriod(1); + FCEUI_Kill(); + return(0); +} //mbg merge 7/19/06 //--------this code was added by tasbuild @@ -549,21 +562,8 @@ doloopy: stopCount=0; }*/ //----------------------------------- - } - xbsave = NULL; - RedrawWindow(hAppWnd,0,0,RDW_ERASE|RDW_INVALIDATE); - StopSound(); - } - Sleep(50); - if(!exiting) - goto doloopy; - doexito: - DriverKill(); - timeEndPeriod(1); - FCEUI_Kill(); - return(0); -} + //mbg merge 7/19/06 - the function that contains the code that used to just be UpdateFCEUWindow() and FCEUD_UpdateInput() void _updateWindow() { @@ -780,15 +780,10 @@ void _updateWindow() { void FCEUD_Update(uint8 *XBuf, int32 *Buffer, int Count) { - static int skipcount = 0; - int temp_fps_scale=(NoWaiting&1)?(256*16):fps_scale; - int maxskip = (temp_fps_scale<=256) ? 0 : temp_fps_scale>>8; - - int ocount = Count; + //mbg merge 7/19/06 - leaving this untouched but untested + /*int ocount = Count; // apply frame scaling to Count Count = (Count<<8)/temp_fps_scale; - - //mbg merge 7/19/06 - leaving this untouched but untested //Disable sound and throttling for BotMode--we want max speed! if(FCEU_BotMode()) { @@ -804,19 +799,41 @@ void FCEUD_Update(uint8 *XBuf, int32 *Buffer, int Count) UpdateFCEUWindow(); FCEUD_UpdateInput(); return; - } + }*/ - //mbg naive code + extern bool turbo; //hack + + //write all the sound we generated. if(soundo && Buffer && Count) { - int32 writeSize = GetWriteSound(); - int32 writeCount = Count; - FCEUD_WriteSoundData(Buffer,temp_fps_scale,MAX(writeSize,writeCount)); + void FCEUD_WriteSoundData_new(int32 *Buffer, int scale, int Count, bool turbo); + FCEUD_WriteSoundData_new(Buffer,fps_scale,Count,turbo); + //FCEUD_WriteSoundData(Buffer,fps_scale,Count); } + //blit the framebuffer if(XBuf) FCEUD_BlitScreen(XBuf); + + //update debugging displays _updateWindow(); + + //throttle + bool skip = false; + for(;;) { + if(!(eoptions&EO_NOTHROTTLE)) //if throttling is enabled.. + if(!turbo) //and turbo is disabled.. + if(!FCEUI_EmulationPaused()) //and we're not paused.. + //if(SpeedThrottle()) {//...then check whether we need to throttle + // //idle.. + // Sleep(0); + // continue; + //} + {} + break; + } + + //delay until we unpause. we will only stick here if we're paused by a breakpoint or debug command while(FCEUI_EmulationPaused() && inDebugger) { diff --git a/src/drivers/win/oakra.h b/src/drivers/win/oakra.h new file mode 100644 index 00000000..5e815bf4 --- /dev/null +++ b/src/drivers/win/oakra.h @@ -0,0 +1,209 @@ +//this code is sloppily ripped from an unfinished sound system written for internal use by m.gambrell +//it is released into the public domain and its stability is not warranted + + +#define _USE_MATH_DEFINES +#include +#include +#include +#include +#include +#include + + +const int OAKRA_U8 = 0; +const int OAKRA_S16 = 1; +const int OAKRA_S32 = 2; + +const int OAKRA_STATUS_STOPPED = 0; +const int OAKRA_STATUS_PLAYING = 1; + +struct OAKRA_Format { + int channels, format, rate, size; +}; + +//implements an easy to use, bland callback +template struct OAKRA_Callback { + OAKRA_Callback() { func = 0; param = 0; } + T operator()() { if(func) return func(param); } + T (*func)(void *param); + void *param; +}; +template struct OAKRA_ArgumentCallback { + OAKRA_ArgumentCallback() { func = 0; param = 0; } + T operator()(TARG arg) { if(func) return func(param,arg); } + T (*func)(void *param, TARG arg); + void *param; +}; + +class OAKRA_Module { +public: + virtual int generate(int samples, void *buf) { return 0; } + + int adapt_to_2S16(int samples, void *buf, OAKRA_Format &sourceFormat) { + short *sbuf = (short *)buf; + unsigned char *bbuf = (unsigned char *)buf; + if(sourceFormat.format == OAKRA_S16) { + if(sourceFormat.channels == 2) return samples; + for(int i=samples-1,j=samples*2-2;i>=0;i--,j-=2) + sbuf[j] = sbuf[j+1] = sbuf[i]; + } else { + if(sourceFormat.channels == 1) + for(int i=samples-1,j=samples*2-2;i>=0;i--,j-=2) + sbuf[j] = sbuf[j+1] = ((int)bbuf[i]-128)<<8; + else + for(int j=samples*2-2;j>=0;j--) + sbuf[j] = sbuf[j+1] = ((int)bbuf[j]-128)<<8; + } + return samples; + } + + //trashes some samples using the buffer provided + void trash(int samples, void *buf, int bufsamples) { + while(samples) { + int todo = std::min(samples,bufsamples); + generate(todo,buf); + samples -= todo; + } + } + + static int calcSize(OAKRA_Format &format) { + int size = format.channels; + if(format.format == OAKRA_S16) size *= 2; + return size; + } + + static int getFormatShift(OAKRA_Format &format) { + if(format.size==1) return 0; + else if(format.size==2) return 1; + else if(format.size==4) return 2; + return -1; //try and crash! + } + + static void *malloc(int len) { + void *ptr = ::malloc(len); + return ptr; + } + void *malloc(int sampsize, int len) { return malloc(sampsize*len); } + void *malloc(int sampsize, int channels, int len) { return malloc(sampsize*len*channels); } + void *realloc(void *ptr, int len) { + ptr = ::realloc(ptr,len); + return ptr; + } + void free(void *ptr) { + ::free(ptr); + } + + + static int generateNoise(int samples, void *buf, int sampleSize) { + for(int i=0;i + T *malloc() { + return (T *)malloc(sizeof(T)); + } + template + T *malloc(int amt) { + return (T *)malloc(sizeof(T)*amt); + } + template + T **nmalloc(int number) { + return (T **)malloc(sizeof(T)*number); + } +}; + +class OAKRA_IQueryFormat { +public: + virtual OAKRA_Format &queryFormat() = 0; +}; + +//this is basically a filter class +//one source, one sink +class OAKRA_BasicModule : public OAKRA_Module { +public: + OAKRA_Module *source, *sink; + OAKRA_BasicModule() { source = sink = 0; } +}; + +class OAKRA_Voice : public OAKRA_BasicModule { +public: + OAKRA_Voice() { dead = false; } + virtual ~OAKRA_Voice() { } + virtual void setPan(int pan) = 0; + virtual int getPan() =0; + virtual void setVol(int vol) = 0; + virtual int getVol()=0; + virtual void setSource(OAKRA_Module *source) = 0; + + virtual void volFade(int start, int end, int ms)=0; + + //call this when youre in the middle of rendering the voice, but have decided to quit + //the driver will then trash the voice as soon as it gets the chance. + //you dont have to have the driver locked to call it. that would be illogical, + //as the driver is currently locked while it is rendering! + virtual void die() { + dead = true; + } + + //indicates whether a voice is dead + bool dead; + + //callback fired when a voice dies + OAKRA_Callback callbackDied; +}; + + +class OAKRA_OutputDriver : public OAKRA_BasicModule { +public: + virtual ~OAKRA_OutputDriver() {} ; + virtual OAKRA_Voice *getVoice(OAKRA_Format &format) = 0; + virtual OAKRA_Voice *getVoice(OAKRA_Format &format, OAKRA_Module *source) = 0; + + //this should be safe to call from within a driver callback. in general, nothing else will be. + //even if you dont delete the voice (if youre recycling it) you should clear out the source asap + virtual void freeVoice(OAKRA_Voice *voice) = 0; + + virtual void lock() = 0; + virtual void unlock() = 0; +}; + + +class OAKRA_Module_OutputDS : public OAKRA_OutputDriver { + + void *threadData; + void *data; + +public: + + OAKRA_Module_OutputDS(); + ~OAKRA_Module_OutputDS(); + OAKRA_Voice *getVoice(OAKRA_Format &format); + OAKRA_Voice *getVoice(OAKRA_Format &format, OAKRA_Module *source); + void freeVoice(OAKRA_Voice *voice); + void freeVoiceInternal(OAKRA_Voice *voice, bool internal); + void start(void *hwnd); + void update(); + void beginThread(); + void endThread(); + void lock(); + void unlock(); +}; diff --git a/src/drivers/win/sound.cpp b/src/drivers/win/sound.cpp index 50ce44a0..85c4ea85 100644 --- a/src/drivers/win/sound.cpp +++ b/src/drivers/win/sound.cpp @@ -18,6 +18,7 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +#include LPDIRECTSOUND ppDS=0; /* DirectSound object. */ LPDIRECTSOUNDBUFFER ppbuf=0; /* Primary buffer object. */ @@ -131,6 +132,10 @@ int32 GetMaxSound(void) return( BufHowMuch >> bittage); } +///enqueues the given samples for playback +static void EnqueueSamples(void *data, uint32 len) { +} + static int RawWrite(void *data, uint32 len) { //uint32 cw; //mbg merge 7/17/06 removed @@ -156,11 +161,12 @@ static int RawWrite(void *data, uint32 len) // THIS LIMITS THE EMULATION SPEED - if((!NoWaiting) || (soundoptions&SO_OLDUP)) - while(!(curlen=RawCanWrite())) - { - Sleep(1); - } + //if((!NoWaiting) || (soundoptions&SO_OLDUP)) + //printf("RawCanWrite(): %d\n",RawCanWrite()); + //while(!(curlen=RawCanWrite())) + //{ + // Sleep(1); + //} if(curlen>len) curlen=len; @@ -211,8 +217,8 @@ static int RawWrite(void *data, uint32 len) len-=curlen; data=(uint8 *)data+curlen; //mbg merge 7/17/06 reworked to be type proper - if(len && !NoWaiting && (fps_scale <= 256 || (soundoptions&SO_OLDUP))) - Sleep(1); // do some extra sleeping if we think there's time and we're not scaling up the FPS or in turbo mode + //if(len && !NoWaiting && (fps_scale <= 256 || (soundoptions&SO_OLDUP))) + // Sleep(1); // do some extra sleeping if we think there's time and we're not scaling up the FPS or in turbo mode } // end while(len) loop @@ -222,6 +228,212 @@ static int RawWrite(void *data, uint32 len) int silencer=0; +#undef min +#undef max +#include "oakra.h" + +OAKRA_Module_OutputDS *dsout; + +class BufferSet { +public: + + static const int BufferSize = 1024; + static const int BufferSizeBits = 10; + static const int BufferSizeBitmask = 1023; + + class Buffer { + public: + int decay, size, length; + Buffer(int size) { length = 0; this->size = size; data = OAKRA_Module::malloc(size); } + int getRemaining() { return size-length; } + void *data; + ~Buffer() { delete data; } + }; + + std::vector liveBuffers; + std::vector freeBuffers; + int length; + int offset; //offset of beginning of addressing into current buffer + int bufferCount; + + BufferSet() { + offset = length = bufferCount = 0; + } + + //causes the oldest free buffer to decay one unit. kills it if it gets too old + void decay(int threshold) { + if(freeBuffers.empty()) return; + if(freeBuffers[0]->decay++>=threshold) { + delete freeBuffers[0]; + freeBuffers.erase(freeBuffers.begin()); + } + } + + Buffer *getBuffer() { + //try to get a buffer from the free pool first + //if theres nothing in the free pool, get a new buffer + if(!freeBuffers.size()) return getNewBuffer(); + //otherwise, return the last thing in the free pool (most recently freed) + Buffer *ret = *--freeBuffers.end(); + freeBuffers.erase(--freeBuffers.end()); + return ret; + } + + //adds the buffer to the free list + void freeBuffer(Buffer *buf) { + freeBuffers.push_back(buf); + buf->decay = 0; + buf->length = 0; + } + + Buffer *getNewBuffer() { + bufferCount++; + return new Buffer(BufferSize); + } + + short getShortAtIndex(int addr) { + addr <<= 1; //shorts are 2bytes + int buffer = (addr+offset)>>BufferSizeBits; + int ofs = (addr+offset) & BufferSizeBitmask; + return *(short*)((char*)liveBuffers[buffer]->data+ofs); + } + + //dequeues the specified number of bytes + void dequeue(int length) { + offset += length; + while(offset >= BufferSize) { + Buffer *front = liveBuffers[0]; + freeBuffer(front); + liveBuffers.erase(liveBuffers.begin()); + offset -= BufferSize; + } + this->length -= length; + } + + + void enqueue(int length, void *data) { + int todo = length; + int done = 0; + + //if there are no buffers in the queue, then we definitely need one before we start + if(liveBuffers.empty()) liveBuffers.push_back(getBuffer()); + + while(todo) { + //check the frontmost buffer + Buffer *end = *--liveBuffers.end(); + int available = std::min(todo,end->getRemaining()); + memcpy((char*)end->data + end->length,(char*)data + done,available); + end->length += available; + todo -= available; + done += available; + + //we're going to need another buffer + if(todo != 0) + liveBuffers.push_back(getBuffer()); + } + + this->length += length; + } +}; + + +class Player : public OAKRA_Module { +public: + + BufferSet buffers; + int cursor; + + bool turbo; + int scale; + + //not interpolating! someday it will! + int generate(int samples, void *buf) { + + int incr = 256; + int bufferSamples = buffers.length>>1; + + //if we're we're too far behind, playback faster + if(bufferSamples > 44100*3/60) { + int behind = bufferSamples - 44100/60; + incr = behind*256*60/44100/2; + //we multiply our playback rate by 1/2 the number of frames we're behind + } + if(incr<256) printf("OHNO -- %d -- shouldnt be less than 256!\n",incr); //sanity check: should never be less than 256 + + incr = (incr*scale)>>8; //apply scaling factor + + //figure out how many dest samples we can generate at this rate without running out of source samples + int destSamplesCanGenerate = (bufferSamples<<8) / incr; + + int todo = std::min(destSamplesCanGenerate,samples); + short *sbuf = (short*)buf; + for(int i=0;i>8); + cursor += incr; + } + buffers.dequeue((cursor>>8)<<1); + cursor &= 255; + memset(sbuf+todo,0,(samples-todo)<<1); + return samples; + } + + Player() { + scale = 256; + cursor = 0; + turbo = false; + } + + ///receives and enqueues s16 stereo samples + void receive(int samples, short *buf, bool turbo, int scale) { + + dsout->lock(); + buffers.enqueue(samples*2,buf); + buffers.decay(60); + this->scale = scale; + dsout->unlock(); + + //throttle +// //wait for the buffer to be satisfactorily low before continuing + if(!turbo) { + for(;;) { + dsout->lock(); + int remain = buffers.length>>1; + dsout->unlock(); + if(remain<44100/60) break; + Sleep(1); + } + } + } + +}; +Player *player; + + +void sound_init_new() { + dsout = new OAKRA_Module_OutputDS(); + dsout->start(0); + dsout->beginThread(); + OAKRA_Format fmt; + fmt.format = OAKRA_S16; + fmt.channels = 1; + fmt.rate = 44100; + fmt.size = 2; + OAKRA_Voice *voice = dsout->getVoice(fmt); + player = new Player(); + dsout->lock(); + voice->setSource(player); + dsout->unlock(); +} + +void FCEUD_WriteSoundData_new(int32 *Buffer, int scale, int Count, bool turbo) { + #define WSD_BUFSIZE (2 * 96000 / 50) + static int16 MBuffer[WSD_BUFSIZE*2]; + + for(int i=0;ireceive(Count,MBuffer,turbo,scale); +} + int FCEUD_WriteSoundData(int32 *Buffer, int scale, int Count) { #define WSD_BUFSIZE (2 * 96000 / 50) @@ -230,21 +442,25 @@ int FCEUD_WriteSoundData(int32 *Buffer, int scale, int Count) int iCount=0; static int16 MBuffer[WSD_BUFSIZE*2]; - if(!(soundoptions&SO_OLDUP)) - { - if(FCEUI_EmulationPaused()) - memset(MBuffer, 0, WSD_BUFSIZE); // slow and/or unnecessary + //if(!(soundoptions&SO_OLDUP)) + //{ + // if(FCEUI_EmulationPaused()) + // memset(MBuffer, 0, WSD_BUFSIZE); // slow and/or unnecessary - if(FCEUI_EmulationPaused()) scale >>= 1; + // if(FCEUI_EmulationPaused()) scale >>= 1; - // limit frequency change to between 50% and 200% - if(scale > 512) scale = 512; - if(scale < 128) scale = 128; - } + // // limit frequency change to between 50% and 200% + // if(scale > 512) scale = 512; + // if(scale < 128) scale = 128; + //} // for(;Count>0;Count-=WSD_BUFSIZE) { - int amt = (soundoptions&SO_OLDUP) ? Count : (Count > WSD_BUFSIZE ? WSD_BUFSIZE : Count); + //int amt = (soundoptions&SO_OLDUP) ? Count : (Count > WSD_BUFSIZE ? WSD_BUFSIZE : Count); + int amt = Count; + + int cando = RawCanWrite(); + printf("Count/cando %d/%d\n",amt,cando); if(!bittage) { @@ -258,7 +474,7 @@ int FCEUD_WriteSoundData(int32 *Buffer, int scale, int Count) for(P=0;P>8))^128; - RawWrite(MBuffer,amt); + RawWrite(MBuffer,cando); } else // force 8-bit sound is off: { @@ -272,7 +488,7 @@ int FCEUD_WriteSoundData(int32 *Buffer, int scale, int Count) for(P=0;P>8; desiredfps=(desiredfps*fps_scale)>>8; + } static uint64 GetCurTime(void) @@ -47,56 +52,102 @@ static uint64 GetCurTime(void) } +static uint64 ttime,ltime; + static void InitSpeedThrottle(void) { - tmethod=0; - if(QueryPerformanceFrequency((LARGE_INTEGER*)&tfreq)) - { - tmethod=1; - } - else - tfreq=1000; - tfreq<<=16; /* Adjustment for fps returned from FCEUI_GetDesiredFPS(). */ + timeBeginPeriod(1); + SetThreadAffinityMask(GetCurrentThread(),1); + + + tmethod=0; + if(QueryPerformanceFrequency((LARGE_INTEGER*)&tfreq)) { + tmethod=1; + } + else tfreq=1000; + + tfreq<<=16; /* Adjustment for fps returned from FCEUI_GetDesiredFPS(). */ + ltime = 0; //mbg +} + +///Resets the throttle timing. use this when the player releases the turbo +void ResetSpeedThrottle() { + ltime = 0; } static int SpeedThrottle(void) { - static uint64 ttime,ltime; + //the desired running time for this frame + uint64 desiredRunningTime = tfreq/desiredfps; - waiter: + ttime = GetCurTime(); - ttime=GetCurTime(); + //if this is our first time, save ltime and bail out + if(ltime == 0) { + ltime = ttime; + return 0; + } - if( (ttime-ltime) < (tfreq/desiredfps) ) - { - uint64 sleepy; - sleepy=(tfreq/desiredfps)-(ttime-ltime); - sleepy*=1000; - if(tfreq>=65536) - sleepy/=tfreq>>16; - else - sleepy=0; - if(sleepy>100) - { - // block for a max of 100ms to - // keep the gui responsive - Sleep(100); - return 1; - } - Sleep(sleepy); - goto waiter; - } - if( (ttime-ltime) >= (tfreq*4/desiredfps)) - ltime=ttime; - else - { - ltime+=tfreq/desiredfps; + //otherwise calculate our delta + uint64 delta = ttime-ltime; + + /*printf("%20I64d %20I64d\n",delta,desiredRunningTime); + fflush(stdout);*/ + if( delta < desiredRunningTime ) { + //if the elapsed time is less than the desired running time - if( (ttime-ltime) >= (tfreq/desiredfps) ) // Oops, we're behind! - return(1); - } - return(0); + //sleepy gets the time that needs to be slept. + //it is then converted to ms<<16 + uint64 sleepy = desiredRunningTime-delta; + //sleepy *= 1000; + //if we have more than 1 ms to sleep, sleep 1 ms and return + if(sleepy>=65536) { + Sleep(1); + return 1; + } else { + //otherwise, we can't throttle for less than 1ms. assume we're close enough and return + ltime += desiredRunningTime; + return 0; + } + + //sleepy*=1000; + //if(tfreq>=65536) + // sleepy/=tfreq>>16; + //else + // sleepy=0; + //if(sleepy>100) + //{ + // // block for a max of 100ms to + // // keep the gui responsive + // Sleep(100); + // return 1; + //} + //Sleep(sleepy); + //goto waiter; + } else { + //we're behind... + if(delta > 2*desiredRunningTime) { + //if we're behind by 2 frames, then reset the throttling + ResetSpeedThrottle(); + } else { + //we're only behind by part of a frame + //just don't throttle! + ltime += desiredRunningTime; + } + return 0; + } + //} + //if( (ttime-ltime) >= (tfreq*4/desiredfps)) + // ltime=ttime; + //else + //{ + // ltime+=tfreq/desiredfps; + + // if( (ttime-ltime) >= (tfreq/desiredfps) ) // Oops, we're behind! + // return(1); + //} + //return(0); } // Quick code for internal FPS display. diff --git a/src/fceu.cpp b/src/fceu.cpp index 682949a0..bdee8934 100644 --- a/src/fceu.cpp +++ b/src/fceu.cpp @@ -426,6 +426,9 @@ void AutoFire(void) void UpdateRewind(void); +///Emulates a single frame. + +///Skip may be passed in, if FRAMESKIP is #defined, to cause this to emulate more than one frame void FCEUI_Emulate(uint8 **pXBuf, int32 **SoundBuf, int32 *SoundBufSize, int skip) { int r,ssize; @@ -452,7 +455,7 @@ void FCEUI_Emulate(uint8 **pXBuf, int32 **SoundBuf, int32 *SoundBufSize, int ski FCEU_UpdateInput(); if(geniestage!=1) FCEU_ApplyPeriodicCheats(); r=FCEUPPU_Loop(skip); - + ssize=FlushEmulateSound(); //#ifdef WIN32 diff --git a/src/types.h b/src/types.h index 7fc01e4a..f2b14624 100644 --- a/src/types.h +++ b/src/types.h @@ -53,6 +53,7 @@ typedef signed int int32; #define fstat _fstat #define mkdir _mkdir #define alloca _alloca +#define snprintf _snprintf #define W_OK 2 #define R_OK 2 #define X_OK 1 diff --git a/vc7/fceu.vcproj b/vc7/fceu.vcproj index 063aabf6..33c22a20 100644 --- a/vc7/fceu.vcproj +++ b/vc7/fceu.vcproj @@ -594,6 +594,9 @@ + + diff --git a/vc8/fceux.vcproj b/vc8/fceux.vcproj index 7aa53111..a1dd3fc8 100644 --- a/vc8/fceux.vcproj +++ b/vc8/fceux.vcproj @@ -70,7 +70,7 @@ AdditionalDependencies="comctl32.lib vfw32.lib dxguid.lib winmm.lib dinput.lib ws2_32.lib ddraw.lib dsound.lib" LinkIncremental="2" GenerateDebugInformation="true" - SubSystem="2" + SubSystem="1" EntryPointSymbol="mainCRTStartup" TargetMachine="1" /> @@ -211,12 +211,12 @@ EnableIntrinsicFunctions="true" FavorSizeOrSpeed="1" OmitFramePointers="true" - AdditionalIncludeDirectories="../zlib" + AdditionalIncludeDirectories="../src/drivers/win/zlib" PreprocessorDefinitions="WIN32;NDEBUG;MSVC;_CRT_SECURE_NO_DEPRECATE;_WIN32_WINDOWS=0x0410;WINVER=0x0410;NETWORK;LSB_FIRST;_USE_32BIT_TIME_T;FCEUDEF_DEBUGGER;_USE_SHARED_MEMORY_" RuntimeLibrary="0" UsePrecompiledHeader="0" WarningLevel="3" - Detect64BitPortabilityProblems="true" + Detect64BitPortabilityProblems="false" DebugInformationFormat="3" /> + + + + @@ -686,10 +694,6 @@ - - @@ -698,6 +702,10 @@ RelativePath="..\src\drivers\win\basicbot.cpp" > + + @@ -914,6 +922,14 @@ RelativePath="..\src\drivers\win\ntview.h" > + + + +