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':
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'])

View File

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

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;
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;
}

View File

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

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
*/
#include <list>
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<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)
{
#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<amt;P++)
*(((uint8*)MBuffer)+P)=((int8)(Buffer[P*scale/256]>>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<amt;P++)
MBuffer[P]=Buffer[P*scale/256];
RawWrite(MBuffer,amt * 2);
RawWrite(MBuffer,cando * 2);
}
iCount+=amt;
@ -364,6 +580,8 @@ int InitSound()
DSCAPS dscaps;
DSBCAPS dsbcaps;
sound_init_new();
memset(&wf,0x00,sizeof(wf));
wf.wFormatTag = WAVE_FORMAT_PCM;
wf.nChannels = 1;
@ -744,4 +962,7 @@ void FCEUD_SoundVolumeAdjust(int n)
FCEU_DispMessage("Sound volume %d.", soundvolume);
}
//-----------
#include "wave.cpp"

View File

@ -18,6 +18,8 @@
* 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 desiredfps;
@ -27,8 +29,11 @@ int32 fps_scale = 256;
static void RefreshThrottleFPS(void)
{
printf("WTF\n");
fflush(stdout);
desiredfps=FCEUI_GetDesiredFPS()>>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.

View File

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

View File

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

View File

@ -594,6 +594,9 @@
<File
RelativePath="..\src\drivers\win\ntview.h">
</File>
<File
RelativePath="..\src\drivers\win\OutputDS.cpp">
</File>
<File
RelativePath="..\src\drivers\win\ppuview.cpp">
</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"
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"
/>
<Tool
@ -634,6 +634,14 @@
RelativePath="..\src\drivers\common\config.h"
>
</File>
<File
RelativePath="..\src\drivers\common\configSys.cpp"
>
</File>
<File
RelativePath="..\src\drivers\common\configSys.h"
>
</File>
<File
RelativePath="..\src\drivers\common\hq2x.cpp"
>
@ -686,10 +694,6 @@
<Filter
Name="win"
>
<File
RelativePath="..\src\asm.h"
>
</File>
<File
RelativePath="..\src\drivers\win\aviout.cpp"
>
@ -698,6 +702,10 @@
RelativePath="..\src\drivers\win\basicbot.cpp"
>
</File>
<File
RelativePath="..\src\drivers\win\basicbot.h"
>
</File>
<File
RelativePath="..\src\drivers\win\cdlogger.cpp"
>
@ -914,6 +922,14 @@
RelativePath="..\src\drivers\win\ntview.h"
>
</File>
<File
RelativePath="..\src\drivers\win\oakra.h"
>
</File>
<File
RelativePath="..\src\drivers\win\OutputDS.cpp"
>
</File>
<File
RelativePath="..\src\drivers\win\ppuview.cpp"
>