added very limited experimental improved sound/throttling code to windows build. windows users, please use 44100 16bit sound for now and test the stability.

This commit is contained in:
zeromus 2006-08-22 06:27:18 +00:00
parent d81578e52f
commit e991e6f0ca
13 changed files with 920 additions and 105 deletions

View File

@ -30,7 +30,7 @@ print "platform: ", env['PLATFORM']
if env['PLATFORM'] == 'cygwin': if env['PLATFORM'] == 'cygwin':
env.Append(CCFLAGS = " -mno-cygwin") env.Append(CCFLAGS = " -mno-cygwin")
env.Append(LINKFLAGS = " -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) conf = Configure(env)
if not conf.CheckLib('SDL'): if not conf.CheckLib('SDL'):
@ -60,6 +60,8 @@ if env['FRAMESKIP']:
# parse SDL cflags/libs # parse SDL cflags/libs
env.ParseConfig('sdl-config --cflags --libs') env.ParseConfig('sdl-config --cflags --libs')
print "LIBS:",env['CCFLAGS']
#env['IBS'] = ['wsock32', 'SDL', 'z', 'mingw32', 'SDL']
Export('env') Export('env')
SConscript(['src/SConscript']) SConscript(['src/SConscript'])

View File

@ -2,6 +2,8 @@ Items to be completed before 2.0 release
FASTAPASS / FP_FASTAPASS / Are these archaic? They suck - ?? FASTAPASS / FP_FASTAPASS / Are these archaic? They suck - ??
Separate frameskip/pause from EmulationPaused - zeromus
Make ALL Debugging code conditional - zeromus Make ALL Debugging code conditional - zeromus
Doxygen integration - DONE Doxygen integration - DONE

View File

@ -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

View File

@ -0,0 +1,288 @@
#include "oakra.h"
#include "dsound.h"
#include <vector>
#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(play<cPlay) todo = play + buflen - cPlay;
else todo = play - cPlay;
if(!todo) return;
void* buffer1;
void* buffer2;
DWORD buffer1_length;
DWORD buffer2_length;
hr = ds_buf->Lock(
cPlay<<formatShift,todo<<formatShift,
&buffer1, &buffer1_length,
&buffer2, &buffer2_length,0
);
buffer1_length >>= 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<<formatShift),format.size);
generateSilence(buffer2_length,buffer2,format.size);
die();
} else {
if(buffer2_length) {
done = source->generate(buffer2_length,buffer2);
if(done != buffer2_length) {
generateSilence(buffer2_length - done,(char *)buffer2 + (done<<formatShift),format.size);
die();
}
}
}
}
ds_buf->Unlock(
buffer1, buffer1_length,
buffer2, buffer2_length);
cPlay = play;
}
};
class Data {
public:
bool global;
IDirectSound* ds_dev;
std::vector<DSVoice *> 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<DSVoice *> voicesCopy = ((Data *)data)->voices;
int voices = (int)voicesCopy.size();
for(int i=0;i<voices;i++)
delete voicesCopy[i];
////free other resources
DeleteCriticalSection(&((Data *)data)->criticalSection);
((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;i<voices;i++)
((Data *)data)->voices[i]->update();
//look for voices that are dead
std::vector<DSVoice *> deaders;
for(int i=0;i<voices;i++)
if(((Data *)data)->voices[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 );
}

View File

@ -125,6 +125,7 @@ static void UpdateTopRider(void);
static uint32 JSreturn=0; static uint32 JSreturn=0;
int NoWaiting=0; int NoWaiting=0;
bool turbo = false;
#include "keyscan.h" #include "keyscan.h"
static unsigned char *keys=0; static unsigned char *keys=0;
@ -1226,8 +1227,8 @@ static struct
{ EMUCMD_FRAME_ADVANCE, SCAN_TAB, }, { EMUCMD_FRAME_ADVANCE, SCAN_TAB, },
{ EMUCMD_SCREENSHOT, SCAN_F9 }, { EMUCMD_SCREENSHOT, SCAN_F9 },
{ EMUCMD_HIDE_MENU_TOGGLE, SCAN_ESCAPE }, { EMUCMD_HIDE_MENU_TOGGLE, SCAN_ESCAPE },
//{ EMUCMD_SPEED_SLOWER, SCAN_MINUS, }, // think about these { EMUCMD_SPEED_SLOWER, SCAN_MINUS, }, // think about these
//{ EMUCMD_SPEED_FASTER, SCAN_EQUAL, }, // think about these { EMUCMD_SPEED_FASTER, SCAN_EQUAL, }, // think about these
{ EMUCMD_SPEED_TURBO, SCAN_GRAVE, }, //tilde { EMUCMD_SPEED_TURBO, SCAN_GRAVE, }, //tilde
{ EMUCMD_SAVE_SLOT_0, SCAN_0, }, { EMUCMD_SAVE_SLOT_0, SCAN_0, },
{ EMUCMD_SAVE_SLOT_1, SCAN_1, }, { EMUCMD_SAVE_SLOT_1, SCAN_1, },
@ -1826,11 +1827,13 @@ void MapInput(void)
void FCEUD_TurboOn(void) void FCEUD_TurboOn(void)
{ {
NoWaiting|=1; //NoWaiting|=1;
turbo = 1;
} }
void FCEUD_TurboOff(void) void FCEUD_TurboOff(void)
{ {
NoWaiting&=~1; //NoWaiting&=~1;
turbo = false;
} }

View File

@ -490,18 +490,17 @@ doloopy:
{ {
while(GameInfo) while(GameInfo)
{ {
uint8 *gfx=0; uint8 *gfx=0; ///contains framebuffer
int32 *sound=0; int32 *sound=0; ///contains sound data buffer
int32 ssize=0; int32 ssize=0; ///contains sound samples count
#ifdef _USE_SHARED_MEMORY_ #ifdef _USE_SHARED_MEMORY_
UpdateBasicBot(); UpdateBasicBot();
#endif #endif
FCEU_UpdateBot(); FCEU_UpdateBot();
FCEUI_Emulate(&gfx, &sound, &ssize, 0); FCEUI_Emulate(&gfx, &sound, &ssize, 0); //emulate a single frame
xbsave = gfx; FCEUD_Update(gfx, sound, ssize); //update displays and debug tools
FCEUD_Update(gfx, sound, ssize);
//mbg 6/30/06 - close game if we were commanded to by calls nested in FCEUI_Emulate() //mbg 6/30/06 - close game if we were commanded to by calls nested in FCEUI_Emulate()
if(closeGame) if(closeGame)
@ -510,7 +509,21 @@ doloopy:
GameInfo = 0; 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 //mbg merge 7/19/06
//--------this code was added by tasbuild //--------this code was added by tasbuild
@ -549,21 +562,8 @@ doloopy:
stopCount=0; 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() //mbg merge 7/19/06 - the function that contains the code that used to just be UpdateFCEUWindow() and FCEUD_UpdateInput()
void _updateWindow() { void _updateWindow() {
@ -780,15 +780,10 @@ void _updateWindow() {
void FCEUD_Update(uint8 *XBuf, int32 *Buffer, int Count) void FCEUD_Update(uint8 *XBuf, int32 *Buffer, int Count)
{ {
static int skipcount = 0; //mbg merge 7/19/06 - leaving this untouched but untested
int temp_fps_scale=(NoWaiting&1)?(256*16):fps_scale; /*int ocount = Count;
int maxskip = (temp_fps_scale<=256) ? 0 : temp_fps_scale>>8;
int ocount = Count;
// apply frame scaling to Count // apply frame scaling to Count
Count = (Count<<8)/temp_fps_scale; 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! //Disable sound and throttling for BotMode--we want max speed!
if(FCEU_BotMode()) if(FCEU_BotMode())
{ {
@ -804,19 +799,41 @@ void FCEUD_Update(uint8 *XBuf, int32 *Buffer, int Count)
UpdateFCEUWindow(); UpdateFCEUWindow();
FCEUD_UpdateInput(); FCEUD_UpdateInput();
return; return;
} }*/
//mbg naive code extern bool turbo; //hack
//write all the sound we generated.
if(soundo && Buffer && Count) { if(soundo && Buffer && Count) {
int32 writeSize = GetWriteSound(); void FCEUD_WriteSoundData_new(int32 *Buffer, int scale, int Count, bool turbo);
int32 writeCount = Count; FCEUD_WriteSoundData_new(Buffer,fps_scale,Count,turbo);
FCEUD_WriteSoundData(Buffer,temp_fps_scale,MAX(writeSize,writeCount)); //FCEUD_WriteSoundData(Buffer,fps_scale,Count);
} }
//blit the framebuffer
if(XBuf) if(XBuf)
FCEUD_BlitScreen(XBuf); FCEUD_BlitScreen(XBuf);
//update debugging displays
_updateWindow(); _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 //delay until we unpause. we will only stick here if we're paused by a breakpoint or debug command
while(FCEUI_EmulationPaused() && inDebugger) while(FCEUI_EmulationPaused() && inDebugger)
{ {

209
src/drivers/win/oakra.h Normal file
View File

@ -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 <math.h>
#include <stdlib.h>
#include <map>
#include <stack>
#include <ctype.h>
#include <queue>
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 <typename T> struct OAKRA_Callback {
OAKRA_Callback() { func = 0; param = 0; }
T operator()() { if(func) return func(param); }
T (*func)(void *param);
void *param;
};
template <typename T, typename TARG> 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<samples;i++)
for(int j=0;j<sampleSize;j++)
((char *)buf)[i*sampleSize+j] = rand();
return samples;
}
static int generateSilence(int samples, void *buf, int sampleSize) {
char *cbuf = (char *)buf;
int n = sampleSize*samples;
memset(buf,0,n);
return samples;
}
static int generateSilence(int samples, void *buf, OAKRA_Format &format) {
char *cbuf = (char *)buf;
int n = format.size*samples;
memset(buf,0,n);
return samples;
}
template<typename T>
T *malloc() {
return (T *)malloc(sizeof(T));
}
template<typename T>
T *malloc(int amt) {
return (T *)malloc(sizeof(T)*amt);
}
template<typename T>
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<void> 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();
};

View File

@ -18,6 +18,7 @@
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/ */
#include <list>
LPDIRECTSOUND ppDS=0; /* DirectSound object. */ LPDIRECTSOUND ppDS=0; /* DirectSound object. */
LPDIRECTSOUNDBUFFER ppbuf=0; /* Primary buffer object. */ LPDIRECTSOUNDBUFFER ppbuf=0; /* Primary buffer object. */
@ -131,6 +132,10 @@ int32 GetMaxSound(void)
return( BufHowMuch >> bittage); return( BufHowMuch >> bittage);
} }
///enqueues the given samples for playback
static void EnqueueSamples(void *data, uint32 len) {
}
static int RawWrite(void *data, uint32 len) static int RawWrite(void *data, uint32 len)
{ {
//uint32 cw; //mbg merge 7/17/06 removed //uint32 cw; //mbg merge 7/17/06 removed
@ -156,11 +161,12 @@ static int RawWrite(void *data, uint32 len)
// THIS LIMITS THE EMULATION SPEED // THIS LIMITS THE EMULATION SPEED
if((!NoWaiting) || (soundoptions&SO_OLDUP)) //if((!NoWaiting) || (soundoptions&SO_OLDUP))
while(!(curlen=RawCanWrite())) //printf("RawCanWrite(): %d\n",RawCanWrite());
{ //while(!(curlen=RawCanWrite()))
Sleep(1); //{
} // Sleep(1);
//}
if(curlen>len) curlen=len; if(curlen>len) curlen=len;
@ -211,8 +217,8 @@ static int RawWrite(void *data, uint32 len)
len-=curlen; len-=curlen;
data=(uint8 *)data+curlen; //mbg merge 7/17/06 reworked to be type proper data=(uint8 *)data+curlen; //mbg merge 7/17/06 reworked to be type proper
if(len && !NoWaiting && (fps_scale <= 256 || (soundoptions&SO_OLDUP))) //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 // 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 } // end while(len) loop
@ -222,6 +228,212 @@ static int RawWrite(void *data, uint32 len)
int silencer=0; 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<Buffer*> liveBuffers;
std::vector<Buffer*> 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<todo;i++) {
sbuf[i] = buffers.getShortAtIndex(cursor>>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;i<Count;i++)
MBuffer[i] = Buffer[i];
player->receive(Count,MBuffer,turbo,scale);
}
int FCEUD_WriteSoundData(int32 *Buffer, int scale, int Count) int FCEUD_WriteSoundData(int32 *Buffer, int scale, int Count)
{ {
#define WSD_BUFSIZE (2 * 96000 / 50) #define WSD_BUFSIZE (2 * 96000 / 50)
@ -230,21 +442,25 @@ int FCEUD_WriteSoundData(int32 *Buffer, int scale, int Count)
int iCount=0; int iCount=0;
static int16 MBuffer[WSD_BUFSIZE*2]; static int16 MBuffer[WSD_BUFSIZE*2];
if(!(soundoptions&SO_OLDUP)) //if(!(soundoptions&SO_OLDUP))
{ //{
if(FCEUI_EmulationPaused()) // if(FCEUI_EmulationPaused())
memset(MBuffer, 0, WSD_BUFSIZE); // slow and/or unnecessary // 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% // // limit frequency change to between 50% and 200%
if(scale > 512) scale = 512; // if(scale > 512) scale = 512;
if(scale < 128) scale = 128; // if(scale < 128) scale = 128;
} //}
// for(;Count>0;Count-=WSD_BUFSIZE) // 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) if(!bittage)
{ {
@ -258,7 +474,7 @@ int FCEUD_WriteSoundData(int32 *Buffer, int scale, int Count)
for(P=0;P<amt;P++) for(P=0;P<amt;P++)
*(((uint8*)MBuffer)+P)=((int8)(Buffer[P*scale/256]>>8))^128; *(((uint8*)MBuffer)+P)=((int8)(Buffer[P*scale/256]>>8))^128;
RawWrite(MBuffer,amt); RawWrite(MBuffer,cando);
} }
else // force 8-bit sound is off: else // force 8-bit sound is off:
{ {
@ -272,7 +488,7 @@ int FCEUD_WriteSoundData(int32 *Buffer, int scale, int Count)
for(P=0;P<amt;P++) for(P=0;P<amt;P++)
MBuffer[P]=Buffer[P*scale/256]; MBuffer[P]=Buffer[P*scale/256];
RawWrite(MBuffer,amt * 2); RawWrite(MBuffer,cando * 2);
} }
iCount+=amt; iCount+=amt;
@ -364,6 +580,8 @@ int InitSound()
DSCAPS dscaps; DSCAPS dscaps;
DSBCAPS dsbcaps; DSBCAPS dsbcaps;
sound_init_new();
memset(&wf,0x00,sizeof(wf)); memset(&wf,0x00,sizeof(wf));
wf.wFormatTag = WAVE_FORMAT_PCM; wf.wFormatTag = WAVE_FORMAT_PCM;
wf.nChannels = 1; wf.nChannels = 1;
@ -744,4 +962,7 @@ void FCEUD_SoundVolumeAdjust(int n)
FCEU_DispMessage("Sound volume %d.", soundvolume); FCEU_DispMessage("Sound volume %d.", soundvolume);
} }
//-----------
#include "wave.cpp" #include "wave.cpp"

View File

@ -18,6 +18,8 @@
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/ */
//http://www.geisswerks.com/ryan/FAQS/timing.html
static uint64 tmethod,tfreq; static uint64 tmethod,tfreq;
static uint64 desiredfps; static uint64 desiredfps;
@ -27,8 +29,11 @@ int32 fps_scale = 256;
static void RefreshThrottleFPS(void) static void RefreshThrottleFPS(void)
{ {
printf("WTF\n");
fflush(stdout);
desiredfps=FCEUI_GetDesiredFPS()>>8; desiredfps=FCEUI_GetDesiredFPS()>>8;
desiredfps=(desiredfps*fps_scale)>>8; desiredfps=(desiredfps*fps_scale)>>8;
} }
static uint64 GetCurTime(void) static uint64 GetCurTime(void)
@ -47,56 +52,102 @@ static uint64 GetCurTime(void)
} }
static uint64 ttime,ltime;
static void InitSpeedThrottle(void) static void InitSpeedThrottle(void)
{ {
tmethod=0; timeBeginPeriod(1);
if(QueryPerformanceFrequency((LARGE_INTEGER*)&tfreq)) SetThreadAffinityMask(GetCurrentThread(),1);
{
tmethod=1;
} tmethod=0;
else if(QueryPerformanceFrequency((LARGE_INTEGER*)&tfreq)) {
tfreq=1000; tmethod=1;
tfreq<<=16; /* Adjustment for fps returned from FCEUI_GetDesiredFPS(). */ }
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 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) ) //otherwise calculate our delta
{ uint64 delta = ttime-ltime;
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;
if( (ttime-ltime) >= (tfreq/desiredfps) ) // Oops, we're behind! /*printf("%20I64d %20I64d\n",delta,desiredRunningTime);
return(1); fflush(stdout);*/
} if( delta < desiredRunningTime ) {
return(0); //if the elapsed time is less than the desired running time
//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. // Quick code for internal FPS display.

View File

@ -426,6 +426,9 @@ void AutoFire(void)
void UpdateRewind(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) void FCEUI_Emulate(uint8 **pXBuf, int32 **SoundBuf, int32 *SoundBufSize, int skip)
{ {
int r,ssize; int r,ssize;

View File

@ -53,6 +53,7 @@ typedef signed int int32;
#define fstat _fstat #define fstat _fstat
#define mkdir _mkdir #define mkdir _mkdir
#define alloca _alloca #define alloca _alloca
#define snprintf _snprintf
#define W_OK 2 #define W_OK 2
#define R_OK 2 #define R_OK 2
#define X_OK 1 #define X_OK 1

View File

@ -594,6 +594,9 @@
<File <File
RelativePath="..\src\drivers\win\ntview.h"> RelativePath="..\src\drivers\win\ntview.h">
</File> </File>
<File
RelativePath="..\src\drivers\win\OutputDS.cpp">
</File>
<File <File
RelativePath="..\src\drivers\win\ppuview.cpp"> RelativePath="..\src\drivers\win\ppuview.cpp">
</File> </File>

View File

@ -70,7 +70,7 @@
AdditionalDependencies="comctl32.lib vfw32.lib dxguid.lib winmm.lib dinput.lib ws2_32.lib ddraw.lib dsound.lib" AdditionalDependencies="comctl32.lib vfw32.lib dxguid.lib winmm.lib dinput.lib ws2_32.lib ddraw.lib dsound.lib"
LinkIncremental="2" LinkIncremental="2"
GenerateDebugInformation="true" GenerateDebugInformation="true"
SubSystem="2" SubSystem="1"
EntryPointSymbol="mainCRTStartup" EntryPointSymbol="mainCRTStartup"
TargetMachine="1" TargetMachine="1"
/> />
@ -211,12 +211,12 @@
EnableIntrinsicFunctions="true" EnableIntrinsicFunctions="true"
FavorSizeOrSpeed="1" FavorSizeOrSpeed="1"
OmitFramePointers="true" 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_" 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" RuntimeLibrary="0"
UsePrecompiledHeader="0" UsePrecompiledHeader="0"
WarningLevel="3" WarningLevel="3"
Detect64BitPortabilityProblems="true" Detect64BitPortabilityProblems="false"
DebugInformationFormat="3" DebugInformationFormat="3"
/> />
<Tool <Tool
@ -634,6 +634,14 @@
RelativePath="..\src\drivers\common\config.h" RelativePath="..\src\drivers\common\config.h"
> >
</File> </File>
<File
RelativePath="..\src\drivers\common\configSys.cpp"
>
</File>
<File
RelativePath="..\src\drivers\common\configSys.h"
>
</File>
<File <File
RelativePath="..\src\drivers\common\hq2x.cpp" RelativePath="..\src\drivers\common\hq2x.cpp"
> >
@ -686,10 +694,6 @@
<Filter <Filter
Name="win" Name="win"
> >
<File
RelativePath="..\src\asm.h"
>
</File>
<File <File
RelativePath="..\src\drivers\win\aviout.cpp" RelativePath="..\src\drivers\win\aviout.cpp"
> >
@ -698,6 +702,10 @@
RelativePath="..\src\drivers\win\basicbot.cpp" RelativePath="..\src\drivers\win\basicbot.cpp"
> >
</File> </File>
<File
RelativePath="..\src\drivers\win\basicbot.h"
>
</File>
<File <File
RelativePath="..\src\drivers\win\cdlogger.cpp" RelativePath="..\src\drivers\win\cdlogger.cpp"
> >
@ -914,6 +922,14 @@
RelativePath="..\src\drivers\win\ntview.h" RelativePath="..\src\drivers\win\ntview.h"
> >
</File> </File>
<File
RelativePath="..\src\drivers\win\oakra.h"
>
</File>
<File
RelativePath="..\src\drivers\win\OutputDS.cpp"
>
</File>
<File <File
RelativePath="..\src\drivers\win\ppuview.cpp" RelativePath="..\src\drivers\win\ppuview.cpp"
> >