Update to bsnes v034r06 release.

This will probably be the last public WIP, so get it now if you want
it.

    http://byuu.cinnamonpirate.com/temp/bsnes_v034_wip06.zip


I used the same "create a child window inside the output window" trick
for Xv that I used for OpenGL, so Xv will now work even with a
compositor enabled.

I also added Video::Synchronize support to OpenGL for Windows. My card
seems to force it on regardless of my driver settings, but maybe
you'll have better luck. That driver had the same issue with
allocating 16MB of memory instead of 4MB (that was due to copy and
pasting of code), so that's fixed too.

This version lowers the CPU<>SMP drifting by an order of magnitude.
You shouldn't notice the speed hit. I can't really get any lower
latency with that, though.

I also restricted the latency range to 25 - 175, with the default
being in the center, 100ms. Quite conservative, given the average we
see is 70-80ms. But you won't notice the difference, and this way we
ensure no popping even in exceptional circumstances by default. 25ms
is doable without video sync and with OSS4+cooked mode, but I
seriously doubt any Windows user will get lower without something
crazy going on with the sound card drivers.

Lastly, I've replaced the 2-tap linear resampler with a 4-tap hermite
resampler. You won't be able to tell the difference, but it's quite
pronounced if you use a waveform analyzer on much higher output
frequencies:

Linear:
Image

Hermite:
Image

Hermite is essentially better than cubic (for which cubic spline is an
optimized version of), as it is better at not going too far away from
the points, so you get a bit less clamping in the extreme cases. But
the difference isn't audible to humans anyway. It's still clearly
inferior to band-limited interpolation, as it will still have
noticeable aliasing of things like square waves and such, but it's
orders of magnitude less complex to implement.

Keep in mind that nobody could tell the difference even with linear
interpolation from the last few WIPs.

----------

Aside from that, I'm pretty much ready to release a new version. If
anyone has any show stoppers, _now_ is the time to say something.
Otherwise I'll probably post something tomorrow or Friday.
This commit is contained in:
byuu 2008-08-20 20:36:54 +00:00
parent d09e54149b
commit e2cc164f70
39 changed files with 1263 additions and 306 deletions

BIN
bsnes.exe

Binary file not shown.

View File

@ -133,7 +133,7 @@ obj/bsnesrc.$(obj): ui/bsnes.rc; windres ui/bsnes.rc obj/bsnesrc.$(obj)
### libraries ###
#################
obj/ruby.$(obj): lib/ruby/ruby.cpp lib/ruby/*
obj/ruby.$(obj): lib/ruby/ruby.cpp lib/ruby/* lib/ruby/video/* lib/ruby/audio/* lib/ruby/input/*
$(call compile,$(rubydef) $(rubyflags))
obj/hiro.$(obj): lib/hiro/hiro.cpp lib/hiro/* lib/hiro/gtk/* lib/hiro/win/*
$(call compile,$(if $(call streq,$(platform),x),`pkg-config --cflags gtk+-2.0`))

View File

@ -1,4 +1,4 @@
#define BSNES_VERSION "0.034.03"
#define BSNES_VERSION "0.034.06"
#define BSNES_TITLE "bsnes v" BSNES_VERSION
#define BUSCORE sBus

View File

@ -200,8 +200,6 @@ unsigned Cartridge::score_header(unsigned addr) {
if(rom[addr + RAM_SIZE] < 0x08) score++;
if(rom[addr + REGION] < 14) score++;
printf("* score @ %0.6x = %2d, resetop = %0.2x\n", addr, score, resetop);
if(score < 0) score = 0;
return score;
}
@ -212,8 +210,6 @@ void Cartridge::find_header() {
unsigned score_ex = score_header(0x40ffc0);
if(score_ex) score_ex += 4; //favor ExHiROM on images > 32mbits
printf("\n");
if(score_lo >= score_hi && score_lo >= score_ex) {
info.header_index = 0x007fc0;
} else if(score_hi >= score_ex) {

6
src/lib/hiro/cc.bat Normal file
View File

@ -0,0 +1,6 @@
mingw32-g++ -c test/test.cpp -I. -I../
mingw32-g++ -c hiro.cpp -I. -I../
mingw32-g++ -c ../nall/string.cpp -I. -I../
mingw32-g++ test.o hiro.o string.o -o test_app.exe -lkernel32 -luser32 -lgdi32 -ladvapi32 -lcomctl32 -lcomdlg32
@pause
@del *.o

6
src/lib/hiro/cc.sh Normal file
View File

@ -0,0 +1,6 @@
clear
g++ -c test/test.cpp -I. -I../
g++ -c hiro.cpp `pkg-config --cflags gtk+-2.0` -I. -I../
g++ -c ../nall/string.cpp -I. -I../
g++ test.o hiro.o string.o -o test_app `pkg-config --libs gtk+-2.0` -lXtst
rm *.o

View File

@ -65,7 +65,7 @@ void NTSCFilter::adjust(
NTSCFilter::NTSCFilter() {
ntsc = 0;
adjust(0, 0, 0, 0, 0, true);
adjust(0, 0, 0, 0, 0, false);
}
NTSCFilter::~NTSCFilter() {

View File

@ -1,9 +1,17 @@
class Audio {
public:
enum Setting {
//AudioInterface settings
Volume,
Resample,
ResampleOutputFrequency,
ResampleInputFrequency,
//Audio settings
Handle,
Synchronize,
Frequency,
Latency,
};
virtual bool cap(Setting) { return false; }

View File

@ -17,7 +17,6 @@ public:
snd_pcm_uframes_t period_size;
int channels;
const char *name;
unsigned latency;
} device;
struct {
@ -28,37 +27,57 @@ public:
struct {
bool synchronize;
unsigned frequency;
unsigned latency;
} settings;
bool cap(Audio::Setting setting) {
if(setting == Audio::Synchronize) return true;
if(setting == Audio::Frequency) return true;
if(setting == Audio::Latency) return true;
return false;
}
uintptr_t get(Audio::Setting setting) {
if(setting == Audio::Synchronize) return settings.synchronize;
if(setting == Audio::Frequency) return settings.frequency;
if(setting == Audio::Latency) return settings.latency;
return false;
}
bool set(Audio::Setting setting, uintptr_t param) {
if(setting == Audio::Synchronize) {
settings.synchronize = param;
if(device.handle) {
term();
init();
if(settings.synchronize != param) {
settings.synchronize = param;
if(device.handle) {
term();
init();
}
}
return true;
}
if(setting == Audio::Frequency) {
settings.frequency = param;
if(device.handle) {
term();
init();
if(settings.frequency != param) {
settings.frequency = param;
if(device.handle) {
term();
init();
}
}
return true;
}
if(setting == Audio::Latency) {
if(settings.latency != param) {
settings.latency = param;
if(device.handle) {
term();
init();
}
}
return true;
}
return false;
}
@ -94,13 +113,13 @@ public:
}
}
/*if(i < 0) {
if(i < 0) {
if(buffer.data == buffer_ptr) {
buffer.length--;
buffer_ptr++;
}
memmove(buffer.data, buffer_ptr, buffer.length * sizeof(uint32_t));
}*/
}
}
bool init() {
@ -109,23 +128,23 @@ public:
return false;
}
/* //below code will not work with 24khz mode (ALSA library bug)
/* //below code will not work with 24khz frequency rate (ALSA library bug)
if(snd_pcm_set_params(device.handle, device.format, SND_PCM_ACCESS_RW_INTERLEAVED,
device.channels, settings.frequency, 1, device.latency) < 0) {
device.channels, settings.frequency, 1, settings.latency * 100) < 0) {
//failed to set device parameters
term();
return false;
}
if(snd_pcm_get_params(device.handle, &device.buffer_size, &device.period_size) < 0) {
device.period_size = device.latency * 1e-6 * settings.frequency / 4;
device.period_size = settings.latency * 100 * 1e-6 * settings.frequency / 4;
}*/
snd_pcm_hw_params_t *hwparams;
snd_pcm_sw_params_t *swparams;
unsigned rate = settings.frequency;
unsigned buffer_time = device.latency;
unsigned period_time = device.latency / 4;
unsigned buffer_time = settings.latency * 100;
unsigned period_time = settings.latency * 100 / 4;
snd_pcm_hw_params_alloca(&hwparams);
if(snd_pcm_hw_params_any(device.handle, hwparams) < 0) {
@ -194,13 +213,13 @@ public:
device.format = SND_PCM_FORMAT_S16_LE;
device.channels = 2;
device.name = "default";
device.latency = 60000;
buffer.data = 0;
buffer.length = 0;
settings.synchronize = false;
settings.frequency = 22050;
settings.latency = 60;
}
~pAudioALSA() {

View File

@ -17,21 +17,29 @@ public:
WAVEFORMATEX wfx;
struct {
unsigned rings;
unsigned latency;
uint32_t *buffer;
unsigned buffer_pos, ring_pos;
unsigned buffer_size, ring_size;
} data;
unsigned bufferoffset;
unsigned readring;
unsigned writering;
int distance;
} device;
struct {
HWND handle;
bool synchronize;
unsigned frequency;
unsigned latency;
} settings;
bool cap(Audio::Setting setting) {
if(setting == Audio::Handle) return true;
if(setting == Audio::Synchronize) return true;
if(setting == Audio::Frequency) return true;
if(setting == Audio::Latency) return true;
return false;
}
@ -39,6 +47,7 @@ public:
if(setting == Audio::Handle) return (uintptr_t)settings.handle;
if(setting == Audio::Synchronize) return settings.synchronize;
if(setting == Audio::Frequency) return settings.frequency;
if(setting == Audio::Latency) return settings.latency;
return false;
}
@ -47,53 +56,83 @@ public:
settings.handle = (HWND)param;
return true;
}
if(setting == Audio::Synchronize) {
settings.synchronize = param;
if(ds) clear();
return true;
}
if(setting == Audio::Frequency) {
settings.frequency = param;
if(ds) init();
return true;
}
if(setting == Audio::Latency) {
settings.latency = param;
if(ds) init();
return true;
}
return false;
}
void sample(uint16_t l_sample, uint16_t r_sample) {
data.buffer[data.buffer_pos++] = (l_sample << 0) + (r_sample << 16);
if(data.buffer_pos < settings.frequency / 40) return;
void sample(uint16_t left, uint16_t right) {
device.buffer[device.bufferoffset++] = left + (right << 16);
if(device.bufferoffset < device.latency) return;
device.bufferoffset = 0;
DWORD ring_pos, pos, size;
for(;;) {
dsb_b->GetCurrentPosition(&pos, 0);
ring_pos = pos / data.ring_size;
if(settings.synchronize == false || ring_pos != data.ring_pos) break;
Sleep(1);
DWORD pos, size;
void *output;
if(settings.synchronize == true) {
//wait until playback buffer has an empty ring to write new audio data to
while(device.distance >= device.rings - 1) {
dsb_b->GetCurrentPosition(&pos, 0);
unsigned activering = pos / (device.latency * 4);
if(activering == device.readring) {
if(video.get(Video::Synchronize) == false) Sleep(1);
continue;
}
//subtract number of played rings from ring distance counter
device.distance -= (device.rings + activering - device.readring) % device.rings;
device.readring = activering;
if(device.distance < 2) {
//buffer underflow; set max distance to recover quickly
device.distance = device.rings - 1;
device.writering = (device.rings + device.readring - 1) % device.rings;
break;
}
}
}
data.ring_pos = ring_pos;
void *output;
if(dsb_b->Lock(((data.ring_pos + 2) % 3) * data.ring_size, data.ring_size,
&output, &size, 0, 0, 0) == DS_OK) {
memcpy(output, data.buffer, data.ring_size);
device.writering = (device.writering + 1) % device.rings;
device.distance = (device.distance + 1) % device.rings;
if(dsb_b->Lock(device.writering * device.latency * 4, device.latency * 4, &output, &size, 0, 0, 0) == DS_OK) {
memcpy(output, device.buffer, device.latency * 4);
dsb_b->Unlock(output, size, 0, 0);
}
data.buffer_pos = 0;
}
void clear() {
data.buffer_pos = 0;
data.ring_pos = 0;
if(data.buffer) memset(data.buffer, 0, data.buffer_size);
if(!dsb_b) return;
device.readring = 0;
device.writering = device.rings - 1;
device.distance = device.rings - 1;
device.bufferoffset = 0;
if(device.buffer) memset(device.buffer, 0, device.latency * device.rings * 4);
if(!dsb_b) return;
dsb_b->Stop();
dsb_b->SetCurrentPosition(0);
DWORD size;
void *output;
dsb_b->Lock(0, data.ring_size * 3, &output, &size, 0, 0, 0);
DWORD size;
void *output;
dsb_b->Lock(0, device.latency * device.rings * 4, &output, &size, 0, 0, 0);
memset(output, 0, size);
dsb_b->Unlock(output, size, 0, 0);
@ -101,13 +140,12 @@ public:
}
bool init() {
clear();
term();
data.ring_size = settings.frequency / 40 * sizeof(uint32_t);
data.buffer_size = data.ring_size * 16;
data.buffer = (uint32_t*)malloc(data.buffer_size);
data.buffer_pos = 0;
device.rings = 8;
device.latency = settings.frequency * settings.latency / device.rings / 1000.0 + 0.5;
device.buffer = new uint32_t[device.latency * device.rings];
device.bufferoffset = 0;
DirectSoundCreate(0, &ds, 0);
ds->SetCooperativeLevel((HWND)settings.handle, DSSCL_PRIORITY);
@ -129,10 +167,9 @@ public:
dsb_p->SetFormat(&wfx);
memset(&dsbd, 0, sizeof(dsbd));
dsbd.dwSize = sizeof(dsbd);
dsbd.dwFlags = DSBCAPS_GETCURRENTPOSITION2 | DSBCAPS_CTRLFREQUENCY |
DSBCAPS_GLOBALFOCUS | DSBCAPS_LOCSOFTWARE;
dsbd.dwBufferBytes = data.ring_size * 3;
dsbd.dwSize = sizeof(dsbd);
dsbd.dwFlags = DSBCAPS_GETCURRENTPOSITION2 | DSBCAPS_CTRLFREQUENCY | DSBCAPS_GLOBALFOCUS | DSBCAPS_LOCSOFTWARE;
dsbd.dwBufferBytes = device.latency * device.rings * sizeof(uint32_t);
dsbd.guid3DAlgorithm = GUID_NULL;
dsbd.lpwfxFormat = &wfx;
ds->CreateSoundBuffer(&dsbd, &dsb_b, 0);
@ -144,9 +181,9 @@ public:
}
void term() {
if(data.buffer) {
free(data.buffer);
data.buffer = 0;
if(device.buffer) {
delete[] device.buffer;
device.buffer = 0;
}
if(dsb_b) { dsb_b->Stop(); dsb_b->Release(); dsb_b = 0; }
@ -159,22 +196,23 @@ public:
dsb_p = 0;
dsb_b = 0;
data.buffer = 0;
data.buffer_pos = 0;
data.ring_pos = 0;
data.buffer_size = 0;
data.ring_size = 0;
device.buffer = 0;
device.bufferoffset = 0;
device.readring = 0;
device.writering = 0;
device.distance = 0;
settings.handle = GetDesktopWindow();
settings.synchronize = false;
settings.frequency = 22050;
settings.latency = 120;
}
};
bool AudioDS::cap(Setting setting) { return p.cap(setting); }
uintptr_t AudioDS::get(Setting setting) { return p.get(setting); }
bool AudioDS::set(Setting setting, uintptr_t param) { return p.set(setting, param); }
void AudioDS::sample(uint16_t l_sample, uint16_t r_sample) { p.sample(l_sample, r_sample); }
void AudioDS::sample(uint16_t left, uint16_t right) { p.sample(left, right); }
void AudioDS::clear() { p.clear(); }
bool AudioDS::init() { return p.init(); }
void AudioDS::term() { p.term(); }

View File

@ -29,17 +29,20 @@ public:
struct {
bool synchronize;
unsigned frequency;
unsigned latency;
} settings;
bool cap(Audio::Setting setting) {
if(setting == Audio::Synchronize) return true;
if(setting == Audio::Frequency) return true;
if(setting == Audio::Latency) return true;
return false;
}
uintptr_t get(Audio::Setting setting) {
if(setting == Audio::Synchronize) return settings.synchronize;
if(setting == Audio::Frequency) return settings.frequency;
if(setting == Audio::Latency) return settings.latency;
return false;
}
@ -48,10 +51,20 @@ public:
settings.synchronize = param;
return true;
}
if(setting == Audio::Frequency) {
settings.frequency = param;
return true;
}
if(setting == Audio::Latency) {
if(settings.latency != param) {
settings.latency = param;
update_latency();
}
return true;
}
return false;
}
@ -61,7 +74,7 @@ public:
ALuint albuffer = 0;
int processed = 0;
for(;;) {
while(true) {
alGetSourcei(device.source, AL_BUFFERS_PROCESSED, &processed);
while(processed--) {
alSourceUnqueueBuffers(device.source, 1, &albuffer);
@ -85,8 +98,14 @@ public:
buffer.length = 0;
}
bool init() {
void update_latency() {
if(buffer.data) delete[] buffer.data;
buffer.size = settings.frequency * settings.latency / 1000.0 + 0.5;
buffer.data = new uint32_t[buffer.size];
}
bool init() {
update_latency();
device.queue_length = 0;
bool success = false;
@ -95,18 +114,17 @@ public:
alcMakeContextCurrent(device.context);
alGenSources(1, &device.source);
//disable unused 3D features
alSourcef (device.source, AL_PITCH, 1.0);
alSourcef (device.source, AL_GAIN, 1.0);
alSource3f(device.source, AL_POSITION, 0.0, 0.0, 0.0);
alSource3f(device.source, AL_VELOCITY, 0.0, 0.0, 0.0);
alSource3f(device.source, AL_DIRECTION, 0.0, 0.0, 0.0);
alSourcef (device.source, AL_ROLLOFF_FACTOR, 0.0);
alSourcei (device.source, AL_SOURCE_RELATIVE, AL_TRUE);
//alSourcef (device.source, AL_PITCH, 1.0);
//alSourcef (device.source, AL_GAIN, 1.0);
//alSource3f(device.source, AL_POSITION, 0.0, 0.0, 0.0);
//alSource3f(device.source, AL_VELOCITY, 0.0, 0.0, 0.0);
//alSource3f(device.source, AL_DIRECTION, 0.0, 0.0, 0.0);
//alSourcef (device.source, AL_ROLLOFF_FACTOR, 0.0);
//alSourcei (device.source, AL_SOURCE_RELATIVE, AL_TRUE);
alListener3f(AL_POSITION, 0.0, 0.0, 0.0);
alListener3f(AL_VELOCITY, 0.0, 0.0, 0.0);
ALfloat listener_orientation[] = { 0.0, 0.0, -1.0, 0.0, 1.0, 0.0 };
ALfloat listener_orientation[] = { 0.0, 0.0, 0.0, 0.0, 0.0, 0.0 };
alListenerfv(AL_ORIENTATION, listener_orientation);
success = true;
@ -163,15 +181,15 @@ public:
device.handle = 0;
device.context = 0;
device.format = AL_FORMAT_STEREO16;
device.latency = 40;
device.queue_length = 0;
buffer.data = 0;
buffer.length = 0;
buffer.size = device.latency * 32;
buffer.size = 0;
settings.synchronize = true;
settings.frequency = 22050;
settings.latency = 40;
}
~pAudioOpenAL() {

View File

@ -2,6 +2,8 @@ class Input {
public:
enum Setting {
Handle,
KeyboardSupport,
JoypadSupport,
AnalogAxisResistance,
};

View File

@ -30,6 +30,8 @@ public:
bool cap(Input::Setting setting) {
if(setting == Input::Handle) return true;
if(setting == Input::KeyboardSupport) return true;
if(setting == Input::JoypadSupport) return true;
if(setting == Input::AnalogAxisResistance) return true;
return false;
}

View File

@ -59,6 +59,8 @@ public:
} settings;
bool cap(Input::Setting setting) {
if(setting == Input::KeyboardSupport) return true;
if(setting == Input::JoypadSupport) return true;
if(setting == Input::AnalogAxisResistance) return true;
return false;
}

View File

@ -16,6 +16,19 @@ public:
Display *display;
char keymap[32];
bool cap(Input::Setting setting) {
if(setting == Input::KeyboardSupport) return true;
return false;
}
uintptr_t get(Input::Setting setting) {
return false;
}
bool set(Input::Setting setting, uintptr_t param) {
return false;
}
bool key_down(uint16_t key) {
using namespace nall;
#define map(i) (keymap[i >> 3] & (1 << (i & 7)))
@ -161,6 +174,9 @@ public:
pInputX(InputX &self_) : self(self_) {}
};
bool InputX::cap(Setting setting) { return p.cap(setting); }
uintptr_t InputX::get(Setting setting) { return p.get(setting); }
bool InputX::set(Setting setting, uintptr_t param) { return p.set(setting, param); }
bool InputX::key_down(uint16_t key) { return p.key_down(key); }
void InputX::clear() { p.clear(); }
void InputX::poll() { p.poll(); }

View File

@ -2,6 +2,10 @@ class pInputX;
class InputX : public Input {
public:
bool cap(Setting);
uintptr_t get(Setting);
bool set(Setting, uintptr_t);
bool key_down(uint16_t key);
void clear();

View File

@ -12,55 +12,99 @@ InputInterface input;
void VideoInterface::driver(const char *driver) {
if(p) term();
if(!strcmp(driver, "none")) p = new Video();
if(driver == "") driver = default_driver();
if(0);
#ifdef VIDEO_DIRECT3D
else if(!strcmp(driver, "direct3d")) p = new VideoD3D();
else if(!strcmp(driver, "Direct3D")) p = new VideoD3D();
#endif
#ifdef VIDEO_DIRECTDRAW
else if(!strcmp(driver, "directdraw")) p = new VideoDD();
else if(!strcmp(driver, "DirectDraw")) p = new VideoDD();
#endif
#ifdef VIDEO_GDI
else if(!strcmp(driver, "gdi")) p = new VideoGDI();
else if(!strcmp(driver, "GDI")) p = new VideoGDI();
#endif
#ifdef VIDEO_GLX
else if(!strcmp(driver, "glx")) p = new VideoGLX();
else if(!strcmp(driver, "OpenGL")) p = new VideoGLX();
#endif
#ifdef VIDEO_SDL
else if(!strcmp(driver, "sdl")) p = new VideoSDL();
else if(!strcmp(driver, "SDL")) p = new VideoSDL();
#endif
#ifdef VIDEO_WGL
else if(!strcmp(driver, "wgl")) p = new VideoWGL();
else if(!strcmp(driver, "OpenGL")) p = new VideoWGL();
#endif
#ifdef VIDEO_XV
else if(!strcmp(driver, "xv")) p = new VideoXv();
else if(!strcmp(driver, "X-Video")) p = new VideoXv();
#endif
else //select the *safest* available driver, not the fastest
else p = new Video();
}
//select the *safest* available driver, not the fastest
const char* VideoInterface::default_driver() {
#if defined(VIDEO_DIRECT3D)
return "Direct3D";
#elif defined(VIDEO_WGL)
return "OpenGL";
#elif defined(VIDEO_DIRECTDRAW)
return "DirectDraw";
#elif defined(VIDEO_GDI)
return "GDI";
#elif defined(VIDEO_SDL)
return "SDL";
#elif defined(VIDEO_XV)
return "X-Video";
#elif defined(VIDEO_GLX)
return "OpenGL";
#else
return "None";
#endif
}
//returns list of available drivers, sorted from most to least optimal
const char* VideoInterface::driver_list() {
return
//Windows
#if defined(VIDEO_DIRECT3D)
p = new VideoD3D();
#elif defined(VIDEO_WGL)
p = new VideoWGL();
#elif defined(VIDEO_DIRECTDRAW)
p = new VideoDD();
#elif defined(VIDEO_GDI)
p = new VideoGDI();
#elif defined(VIDEO_SDL)
p = new VideoSDL();
#elif defined(VIDEO_XV)
p = new VideoXv();
#elif defined(VIDEO_GLX)
p = new VideoGLX();
#else
p = new Video();
"Direct3D;"
#endif
#if defined(VIDEO_WGL)
"OpenGL;"
#endif
#if defined(VIDEO_DIRECTDRAW)
"DirectDraw;"
#endif
#if defined(VIDEO_GDI)
"GDI;"
#endif
//Linux
#if defined(VIDEO_GLX)
"OpenGL;"
#endif
#if defined(VIDEO_XV)
"X-Video;"
#endif
#if defined(VIDEO_SDL)
"SDL;"
#endif
"None";
}
bool VideoInterface::init() {
@ -90,95 +134,140 @@ VideoInterface::~VideoInterface() { term(); }
void AudioInterface::driver(const char *driver) {
if(p) term();
if(!strcmp(driver, "none")) p = new Audio();
if(driver == "") driver = default_driver();
if(0);
#ifdef AUDIO_ALSA
else if(!strcmp(driver, "alsa")) p = new AudioALSA();
else if(!strcmp(driver, "ALSA")) p = new AudioALSA();
#endif
#ifdef AUDIO_AO
else if(!strcmp(driver, "ao")) p = new AudioAO();
else if(!strcmp(driver, "libao")) p = new AudioAO();
#endif
#ifdef AUDIO_DIRECTSOUND
else if(!strcmp(driver, "directsound")) p = new AudioDS();
else if(!strcmp(driver, "DirectSound")) p = new AudioDS();
#endif
#ifdef AUDIO_OPENAL
else if(!strcmp(driver, "openal")) p = new AudioOpenAL();
else if(!strcmp(driver, "OpenAL")) p = new AudioOpenAL();
#endif
#ifdef AUDIO_OSS
else if(!strcmp(driver, "oss")) p = new AudioOSS();
else if(!strcmp(driver, "OSS")) p = new AudioOSS();
#endif
else //select the *safest* available driver, not the fastest
else p = new Audio();
}
//select the *safest* available driver, not the fastest
const char* AudioInterface::default_driver() {
#if defined(AUDIO_DIRECTSOUND)
return "DirectSound";
#elif defined(AUDIO_AO)
return "libao";
#elif defined(AUDIO_ALSA)
return "ALSA";
#elif defined(AUDIO_OPENAL)
return "OpenAL";
#elif defined(AUDIO_OSS)
return "OSS";
#else
return "None";
#endif
}
//returns list of available drivers, sorted from most to least optimal
const char* AudioInterface::driver_list() {
return
//Windows
#if defined(AUDIO_DIRECTSOUND)
p = new AudioDS();
#elif defined(AUDIO_AO)
p = new AudioAO();
#elif defined(AUDIO_ALSA)
p = new AudioALSA();
#elif defined(AUDIO_OPENAL)
p = new AudioOpenAL();
#elif defined(AUDIO_OSS)
p = new AudioOSS();
#else
p = new Audio();
"DirectSound;"
#endif
//Linux
#if defined(AUDIO_ALSA)
"ALSA;"
#endif
#if defined(AUDIO_OPENAL)
"OpenAL;"
#endif
#if defined(AUDIO_OSS)
"OSS;"
#endif
#if defined(AUDIO_AO)
"libao;"
#endif
"None";
}
bool AudioInterface::init() {
if(!p) driver();
return p->init();
}
void AudioInterface::term() {
if(p) {
delete p;
p = 0;
}
}
bool AudioInterface::cap(Audio::Setting setting) { return p ? p->cap(setting) : false; }
uintptr_t AudioInterface::get(Audio::Setting setting) { return p ? p->get(setting) : false; }
bool AudioInterface::set(Audio::Setting setting, uintptr_t param) { return p ? p->set(setting, param) : false; }
void AudioInterface::sample(uint16_t left, uint16_t right) { if(p) p->sample(left, right); }
void AudioInterface::clear() { if(p) p->clear(); }
AudioInterface::AudioInterface() : p(0) {}
AudioInterface::~AudioInterface() { term(); }
#include "ruby_audio.cpp"
/* InputInterface */
void InputInterface::driver(const char *driver) {
if(p) term();
if(!strcmp(driver, "none")) p = new Input();
if(driver == "") driver = default_driver();
if(0);
#ifdef INPUT_DIRECTINPUT
else if(!strcmp(driver, "directinput")) p = new InputDI();
else if(!strcmp(driver, "DirectInput")) p = new InputDI();
#endif
#ifdef INPUT_SDL
else if(!strcmp(driver, "sdl")) p = new InputSDL();
else if(!strcmp(driver, "SDL")) p = new InputSDL();
#endif
#ifdef INPUT_X
else if(!strcmp(driver, "x")) p = new InputX();
else if(!strcmp(driver, "X-Windows")) p = new InputX();
#endif
else //select the *safest* available driver, not the fastest
else p = new Input();
}
//select the *safest* available driver, not the fastest
const char* InputInterface::default_driver() {
#if defined(INPUT_DIRECTINPUT)
return "DirectInput";
#elif defined(INPUT_SDL)
return "SDL";
#elif defined(INPUT_X)
return "X-Windows";
#else
return "none";
#endif
}
const char* InputInterface::driver_list() {
return
//Windows
#if defined(INPUT_DIRECTINPUT)
p = new InputDI();
#elif defined(INPUT_SDL)
p = new InputSDL();
#elif defined(INPUT_X)
p = new InputX();
#else
p = new Input();
"DirectInput;"
#endif
//Linux
#if defined(INPUT_SDL)
"SDL;"
#endif
#if defined(INPUT_X)
"X-Windows;"
#endif
"None";
}
bool InputInterface::init() {

View File

@ -1,6 +1,6 @@
/*
ruby
version: 0.03 (2008-05-04)
version: 0.04 (2008-08-20)
license: public domain
*/
@ -8,6 +8,7 @@
#define RUBY_H
#include <nall/algorithm.hpp>
#include <nall/bit.hpp>
#include <nall/input.hpp>
#include <nall/new.hpp>
#include <nall/stdint.hpp>
@ -24,6 +25,8 @@ namespace ruby {
class VideoInterface {
public:
void driver(const char *driver = "");
const char* default_driver();
const char* driver_list();
bool init();
void term();
@ -44,6 +47,8 @@ private:
class AudioInterface {
public:
void driver(const char *driver = "");
const char* default_driver();
const char* driver_list();
bool init();
void term();
@ -57,11 +62,22 @@ public:
private:
Audio *p;
unsigned volume;
//resample unit
double hermite(double mu, double a, double b, double c, double d);
bool resample_enabled;
double r_outfreq, r_infreq;
double r_step, r_frac;
int r_left[4], r_right[4];
};
class InputInterface {
public:
void driver(const char *driver = "");
const char* default_driver();
const char* driver_list();
bool init();
void term();

132
src/lib/ruby/ruby_audio.cpp Normal file
View File

@ -0,0 +1,132 @@
bool AudioInterface::init() {
if(!p) driver();
return p->init();
}
void AudioInterface::term() {
if(p) {
delete p;
p = 0;
}
}
bool AudioInterface::cap(Audio::Setting setting) {
if(setting == Audio::Volume) return true;
if(setting == Audio::Resample) return true;
if(setting == Audio::ResampleOutputFrequency) return true;
if(setting == Audio::ResampleInputFrequency) return true;
return p ? p->cap(setting) : false;
}
uintptr_t AudioInterface::get(Audio::Setting setting) {
if(setting == Audio::Volume) return volume;
if(setting == Audio::Resample) return resample_enabled;
if(setting == Audio::ResampleOutputFrequency) return r_outfreq;
if(setting == Audio::ResampleInputFrequency) return r_infreq;
return p ? p->get(setting) : false;
}
bool AudioInterface::set(Audio::Setting setting, uintptr_t param) {
if(setting == Audio::Volume) {
volume = param;
return true;
}
if(setting == Audio::Resample) {
resample_enabled = param;
return true;
}
if(setting == Audio::ResampleOutputFrequency) {
r_outfreq = max(1, param);
r_step = (double)r_infreq / (double)r_outfreq;
r_frac = 0;
return true;
}
if(setting == Audio::ResampleInputFrequency) {
r_infreq = max(1, param);
r_step = (double)r_infreq / (double)r_outfreq;
r_frac = 0;
return true;
}
return p ? p->set(setting, param) : false;
}
//4-tap hermite interpolation
double AudioInterface::hermite(double mu1, double a, double b, double c, double d) {
const double tension = 0.0; //-1 = low, 0 = normal, 1 = high
const double bias = 0.0; //-1 = left, 0 = even, 1 = right
double mu2, mu3, m0, m1, a0, a1, a2, a3;
mu2 = mu1 * mu1;
mu3 = mu2 * mu1;
m0 = (b - a) * (1 + bias) * (1 - tension) / 2;
m0 += (c - b) * (1 - bias) * (1 - tension) / 2;
m1 = (c - b) * (1 + bias) * (1 - tension) / 2;
m1 += (d - c) * (1 - bias) * (1 - tension) / 2;
a0 = +2 * mu3 - 3 * mu2 + 1;
a1 = mu3 - 2 * mu2 + mu1;
a2 = mu3 - mu2;
a3 = -2 * mu3 + 3 * mu2;
return (a0 * b) + (a1 * m0) + (a2 * m1) + (a3 * c);
}
void AudioInterface::sample(uint16_t left, uint16_t right) {
if(volume != 100) {
left = sclamp<16>((double)left * (double)volume / 100.0);
right = sclamp<16>((double)right * (double)volume / 100.0);
}
r_left [0] = r_left [1];
r_left [1] = r_left [2];
r_left [2] = r_left [3];
r_left [3] = (int16_t)left;
r_right[0] = r_right[1];
r_right[1] = r_right[2];
r_right[2] = r_right[3];
r_right[3] = (int16_t)right;
if(resample_enabled == false) {
if(p) p->sample(left, right);
return;
}
while(r_frac <= 1.0) {
int output_left = sclamp<16>(hermite(r_frac, r_left [0], r_left [1], r_left [2], r_left [3]));
int output_right = sclamp<16>(hermite(r_frac, r_right[0], r_right[1], r_right[2], r_right[3]));
r_frac += r_step;
if(p) p->sample(output_left, output_right);
}
r_frac -= 1.0;
}
void AudioInterface::clear() {
r_frac = 0;
r_left [0] = r_left [1] = r_left [2] = r_left [3] = 0;
r_right[0] = r_right[1] = r_right[2] = r_right[3] = 0;
if(p) p->clear();
}
AudioInterface::AudioInterface() {
p = 0;
volume = 100;
resample_enabled = false;
r_outfreq = r_infreq = 1;
r_step = r_frac = 0;
r_left [0] = r_left [1] = r_left [2] = r_left [3] = 0;
r_right[0] = r_right[1] = r_right[2] = r_right[3] = 0;
}
AudioInterface::~AudioInterface() {
term();
}

View File

@ -71,15 +71,18 @@ public:
settings.handle = (HWND)param;
return true;
}
if(setting == Video::Synchronize) {
settings.synchronize = param;
return true;
}
if(setting == Video::Filter) {
settings.filter = param;
update_filter();
if(lpd3d) update_filter();
return true;
}
return false;
}
@ -172,7 +175,7 @@ public:
void unlock() {
surface->UnlockRect();
if(caps.stretchrect == false)surface->Release();
if(caps.stretchrect == false) surface->Release();
}
void refresh(unsigned width, unsigned height) {
@ -204,11 +207,10 @@ public:
device->EndScene();
if(settings.synchronize) {
D3DRASTER_STATUS status;
for(;;) {
while(true) {
D3DRASTER_STATUS status;
device->GetRasterStatus(0, &status);
if(bool(status.InVBlank) == true) break;
//Sleep(1);
if(status.InVBlank == true) break;
}
}

View File

@ -67,11 +67,10 @@ public:
void refresh(unsigned r_width, unsigned r_height) {
if(settings.synchronize) {
for(;;) {
while(true) {
BOOL in_vblank;
lpdd7->GetVerticalBlankStatus(&in_vblank);
if(bool(in_vblank) == true) break;
//Sleep(1);
if(in_vblank == true) break;
}
}

View File

@ -2,7 +2,7 @@
video.glx
author: byuu
license: public domain
date: 2008-02-06
last updated: 2008-08-20
Design notes:
SGI's GLX is the X11/Xlib interface to OpenGL.
@ -33,7 +33,7 @@ namespace ruby {
#include "glx.h"
//returns true once window is mapped (created and displayed onscreen)
static Bool x_wait_for_map_notify(Display *d, XEvent *e, char *arg) {
static Bool glx_wait_for_map_notify(Display *d, XEvent *e, char *arg) {
return (e->type == MapNotify) && (e->xmap.window == (Window)arg);
}
@ -45,6 +45,7 @@ public:
Display *display;
int screen;
Window xwindow;
Colormap colormap;
GLXContext glxcontext;
GLXWindow glxwindow;
GLuint gltexture;
@ -57,17 +58,20 @@ public:
struct {
Window handle;
bool synchronize;
unsigned filter;
} settings;
bool cap(Video::Setting setting) {
if(setting == Video::Handle) return true;
if(setting == Video::Synchronize) return true;
if(setting == Video::Filter) return true;
return false;
}
uintptr_t get(Video::Setting setting) {
if(setting == Video::Handle) return settings.handle;
if(setting == Video::Synchronize) return settings.synchronize;
if(setting == Video::Filter) return settings.filter;
return false;
}
@ -77,10 +81,23 @@ public:
settings.handle = param;
return true;
}
if(setting == Video::Synchronize) {
if(settings.synchronize != param) {
settings.synchronize = param;
if(glxcontext) {
term();
init();
}
return true;
}
}
if(setting == Video::Filter) {
settings.filter = param;
return true;
}
return false;
}
@ -157,37 +174,37 @@ public:
//require GLX 1.2+ API
if(glx.version_major < 1 || (glx.version_major == 1 && glx.version_minor < 2)) return false;
buffer = new(zeromemory) uint32_t[1024 * 1024 * sizeof(uint32_t)];
buffer = new(zeromemory) uint32_t[1024 * 1024];
XWindowAttributes wa;
XGetWindowAttributes(display, settings.handle, &wa);
XWindowAttributes window_attributes;
XGetWindowAttributes(display, settings.handle, &window_attributes);
//let GLX determine the best Visual to use for GL output; provide a few hints
//note: some video drivers will override double buffering attribute
int elements = 0;
int attributelist[] = {
GLX_RGBA,
GLX_DOUBLEBUFFER,
None
};
XVisualInfo *vi = glXChooseVisual(display, screen, attributelist);
int attributelist[] = { GLX_RGBA, None };
int attributelist_sync[] = { GLX_RGBA, GLX_DOUBLEBUFFER, None };
XVisualInfo *vi = glXChooseVisual(display, screen,
settings.synchronize ? attributelist_sync : attributelist);
//Window settings.handle has already been realized, most likely with DefaultVisual.
//GLX requires that the GL output window has the same Visual as the GLX context.
//it is not possible to change the Visual of an already realized (created) window.
//therefore a new child window, using the same GLX Visual, must be created and binded to settings.handle.
Colormap colormap = XCreateColormap(display, RootWindow(display, vi->screen), vi->visual, AllocNone);
XSetWindowAttributes swa;
swa.colormap = colormap;
swa.border_pixel = 0;
swa.event_mask = StructureNotifyMask;
colormap = XCreateColormap(display, RootWindow(display, vi->screen), vi->visual, AllocNone);
XSetWindowAttributes attributes;
attributes.colormap = colormap;
attributes.border_pixel = 0;
attributes.event_mask = StructureNotifyMask;
xwindow = XCreateWindow(display, /* parent = */ settings.handle,
/* x = */ 0, /* y = */ 0, wa.width, wa.height,
/* x = */ 0, /* y = */ 0, window_attributes.width, window_attributes.height,
/* border_width = */ 0, vi->depth, InputOutput, vi->visual,
CWColormap | CWBorderPixel | CWEventMask, &swa);
CWColormap | CWBorderPixel | CWEventMask, &attributes);
XSetWindowBackground(display, xwindow, /* color = */ 0);
XMapWindow(display, xwindow);
XEvent event;
XIfEvent(display, &event, x_wait_for_map_notify, (char*)xwindow); //wait for window to appear onscreen
//window must be realized (appear onscreen) before we make the context current
XIfEvent(display, &event, glx_wait_for_map_notify, (char*)xwindow);
glxcontext = glXCreateContext(display, vi, /* sharelist = */ 0, /* direct = */ GL_TRUE);
glXMakeCurrent(display, glxwindow = xwindow, glxcontext);
@ -233,6 +250,16 @@ public:
glxcontext = 0;
}
if(xwindow) {
XUnmapWindow(display, xwindow);
xwindow = 0;
}
if(colormap) {
XFreeColormap(display, colormap);
colormap = 0;
}
if(buffer) {
delete[] buffer;
buffer = 0;
@ -241,7 +268,9 @@ public:
pVideoGLX(VideoGLX &self_) : self(self_) {
settings.handle = 0;
settings.synchronize = false;
xwindow = 0;
colormap = 0;
glxcontext = 0;
glxwindow = 0;
gltexture = 0;

View File

@ -53,9 +53,32 @@ public:
}
void clear() {
if(SDL_MUSTLOCK(buffer)) SDL_LockSurface(buffer);
uint32_t *data = (uint32_t*)buffer->pixels;
for(unsigned y = 0; y < 1024; y++) {
for(unsigned x = 0; x < 1024; x++) {
*data++ |= 0xff000000;
}
data += (buffer->pitch >> 2) - 1024;
}
if(SDL_MUSTLOCK(buffer)) SDL_UnlockSurface(buffer);
refresh(1024, 1024);
}
void refresh(unsigned width, unsigned height) {
//ruby input is X8R8G8B8, top 8-bits are ignored.
//as SDL forces us to use a 32-bit buffer, we must set alpha to 255 (full opacity)
//to prevent blending against the window beneath when X window visual is 32-bits.
if(SDL_MUSTLOCK(buffer)) SDL_LockSurface(buffer);
uint32_t *data = (uint32_t*)buffer->pixels;
for(unsigned y = 0; y < height; y++) {
for(unsigned x = 0; x < width; x++) {
*data++ |= 0xff000000;
}
data += (buffer->pitch >> 2) - width;
}
if(SDL_MUSTLOCK(buffer)) SDL_UnlockSurface(buffer);
XWindowAttributes attributes;
XGetWindowAttributes(display, settings.handle, &attributes);
@ -81,10 +104,11 @@ public:
sprintf(env, "SDL_WINDOWID=%ld", settings.handle);
putenv(env);
SDL_InitSubSystem(SDL_INIT_VIDEO);
//screen depth must be 32, as 24bpp with a 32-bit X window visual produces no output.
screen = SDL_SetVideoMode(2560, 1600, 32, SDL_HWSURFACE);
//buffer depth must be 32, as this is the input format used by all ruby drivers.
buffer = SDL_CreateRGBSurface(SDL_HWSURFACE,
1024, 1024,
32, 0x00ff0000, 0x0000ff00, 0x000000ff, 0x00000000
1024, 1024, 32, 0x00ff0000, 0x0000ff00, 0x000000ff, 0xff000000
);
return true;
}

View File

@ -2,7 +2,7 @@
video.wgl
authors: byuu, krom, mudlord
license: public domain
date: 2008-03-26
last updated: 2008-08-20
*/
#include <windows.h>
@ -28,17 +28,20 @@ public:
struct {
HWND handle;
bool synchronize;
unsigned filter;
} settings;
bool cap(Video::Setting setting) {
if(setting == Video::Handle) return true;
if(setting == Video::Synchronize) return true;
if(setting == Video::Filter) return true;
return false;
}
uintptr_t get(Video::Setting setting) {
if(setting == Video::Handle) return (uintptr_t)settings.handle;
if(setting == Video::Synchronize) return settings.synchronize;
if(setting == Video::Filter) return settings.filter;
return false;
}
@ -48,10 +51,22 @@ public:
settings.handle = (HWND)param;
return true;
}
if(setting == Video::Synchronize) {
if(settings.synchronize != param) {
settings.synchronize = param;
if(wglcontext) {
term();
init();
}
}
}
if(setting == Video::Filter) {
settings.filter = param;
return true;
}
return false;
}
@ -114,14 +129,14 @@ public:
}
bool init() {
buffer = new(zeromemory) uint32_t[1024 * 1024 * sizeof(uint32_t)];
buffer = new(zeromemory) uint32_t[1024 * 1024];
GLuint pixel_format;
PIXELFORMATDESCRIPTOR pfd;
memset(&pfd, 0, sizeof(PIXELFORMATDESCRIPTOR));
pfd.nSize = sizeof(PIXELFORMATDESCRIPTOR);
pfd.nVersion = 1;
pfd.dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER;
pfd.dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | (settings.synchronize ? PFD_DOUBLEBUFFER : 0);
pfd.iPixelType = PFD_TYPE_RGBA;
display = GetDC(settings.handle);
@ -174,6 +189,9 @@ public:
pVideoWGL(VideoWGL &self_) : self(self_) {
settings.handle = 0;
settings.synchronize = false;
settings.filter = 0;
window = 0;
wglcontext = 0;
glwindow = 0;

View File

@ -18,25 +18,35 @@ namespace ruby {
class pVideoXv {
public:
VideoXv &self;
uint32_t *buffer;
XvImage *xvimage;
GC gc;
Display *display;
int screen, xv_port;
GC gc;
int screen, xv_port, xv_depth, xv_visualid;
XvImage *xvimage;
XShmSegmentInfo shminfo;
uint32_t *buffer;
bool use_child_window;
Window xwindow;
Colormap colormap;
uint8_t *ytable, *utable, *vtable;
struct {
Window handle;
bool synchronize;
} settings;
bool cap(Video::Setting setting) {
if(setting == Video::Handle) return true;
if(setting == Video::Synchronize) {
return XInternAtom(XOpenDisplay(0), "XV_SYNC_TO_VBLANK", true) != None;
}
return false;
}
uintptr_t get(Video::Setting setting) {
if(setting == Video::Handle) return settings.handle;
if(setting == Video::Synchronize) return settings.synchronize;
return false;
}
@ -45,6 +55,18 @@ public:
settings.handle = param;
return true;
}
if(setting == Video::Synchronize) {
Display *display = XOpenDisplay(0);
Atom atom = XInternAtom(display, "XV_SYNC_TO_VBLANK", true);
if(atom != None) {
settings.synchronize = param;
XvSetPortAttribute(display, xv_port, atom, settings.synchronize);
return true;
}
return false;
}
return false;
}
@ -63,21 +85,36 @@ public:
refresh(1024, 1024);
}
void refresh(unsigned width, unsigned height) {
XWindowAttributes attributes;
XGetWindowAttributes(display, settings.handle, &attributes);
void refresh(unsigned width, unsigned height) {
XWindowAttributes target;
XGetWindowAttributes(display, xwindow, &target);
if(use_child_window) {
//we must ensure that the child window is the same size as the parent window.
//unfortunately, we cannot hook the parent window resize event notification,
//as we did not create the parent window, nor have any knowledge of the toolkit used.
//therefore, inelegant as it may be, we query each window size and resize as needed.
XWindowAttributes parent;
XGetWindowAttributes(display, settings.handle, &parent);
if(target.width != parent.width || target.height != parent.height) {
XResizeWindow(display, xwindow, parent.width, parent.height);
}
//update target width and height attributes
XGetWindowAttributes(display, xwindow, &target);
}
uint32_t *input = (uint32_t*)buffer;
uint16_t *output = (uint16_t*)xvimage->data;
for(int y = 0; y < height; y++) {
for(int x = 0; x < width >> 1; x++) {
for(unsigned y = 0; y < height; y++) {
for(unsigned x = 0; x < width >> 1; x++) {
uint32_t p0 = *input++;
uint32_t p1 = *input++;
p0 = ((p0 >> 8) & 0xf800) + ((p0 >> 5) & 0x07e0) + ((p0 >> 3) & 0x001f);
p1 = ((p1 >> 8) & 0xf800) + ((p1 >> 5) & 0x07e0) + ((p1 >> 3) & 0x001f);
uint8_t u = (utable[p0] + utable[p1]) >> 1;
uint8_t v = (vtable[p0] + vtable[p1]) >> 1;
uint8_t u = (utable[p0] + utable[p1]) >> 1;
uint8_t v = (vtable[p0] + vtable[p1]) >> 1;
*output++ = (u << 8) | ytable[p0];
*output++ = (v << 8) | ytable[p1];
@ -86,33 +123,37 @@ public:
output += 1024 - width;
}
XvShmPutImage(display, xv_port, settings.handle, gc, xvimage,
XvShmPutImage(display, xv_port, xwindow, gc, xvimage,
0, 0, width, height,
0, 0, attributes.width, attributes.height,
0, 0, target.width, target.height,
true);
}
bool init() {
buffer = (uint32_t*)malloc(1024 * 1024 * sizeof(uint32_t));
display = XOpenDisplay(0);
screen = DefaultScreen(display);
gc = XCreateGC(display, settings.handle, 0, 0);
//XShm is required for rendering
if(!XShmQueryExtension(display)) {
fprintf(stderr, "VideoXv: XShm extension not found.\n");
return false;
}
//find an appropriate port, if possible
xv_port = -1;
XvAdaptorInfo *adaptor_info;
unsigned adaptor_count;
XvQueryAdaptors(display, DefaultRootWindow(display), &adaptor_count, &adaptor_info);
for(unsigned i = 0; i < adaptor_count; i++) {
//find adaptor that supports both input (memory->drawable) and image (drawable->screen) masks
if(adaptor_info[i].type & XvInputMask && adaptor_info[i].type & XvImageMask) {
xv_port = adaptor_info[i].base_id;
break;
}
if(adaptor_info[i].num_formats < 1) continue;
if(!(adaptor_info[i].type & XvInputMask)) continue;
if(!(adaptor_info[i].type & XvImageMask)) continue;
xv_port = adaptor_info[i].base_id;
xv_depth = adaptor_info[i].formats->depth;
xv_visualid = adaptor_info[i].formats->visual_id;
break;
}
XvFreeAdaptorInfo(adaptor_info);
if(xv_port == -1) {
@ -120,8 +161,49 @@ public:
return false;
}
XWindowAttributes window_attributes;
XGetWindowAttributes(display, settings.handle, &window_attributes);
if(xv_depth == window_attributes.depth) {
//Xv port is depth-compatible with target output window
use_child_window = false;
xwindow = settings.handle;
} else {
//Xv port is not depth-compatible with target output window
//this is often the case when a 32bpp composited window is used with a 24bpp-only Xv adaptor
//the only way to render to target is to create a child window with the Xv ports' depth
use_child_window = true;
XVisualInfo visualtemplate;
visualtemplate.visualid = xv_visualid;
visualtemplate.screen = screen;
visualtemplate.depth = xv_depth;
visualtemplate.visual = 0;
int visualmatches = 0;
XVisualInfo *visualinfo = XGetVisualInfo(display, VisualIDMask | VisualScreenMask | VisualDepthMask, &visualtemplate, &visualmatches);
if(visualmatches < 1 || !visualinfo->visual) {
if(visualinfo) XFree(visualinfo);
fprintf(stderr, "VideoXv: unable to find Xv-compatible visual.\n");
return false;
}
colormap = XCreateColormap(display, settings.handle, visualinfo->visual, AllocNone);
XSetWindowAttributes attributes;
attributes.colormap = colormap;
attributes.border_pixel = 0;
attributes.event_mask = StructureNotifyMask;
xwindow = XCreateWindow(display, /* parent = */ settings.handle,
/* x = */ 0, /* y = */ 0, window_attributes.width, window_attributes.height,
/* border_width = */ 0, xv_depth, InputOutput, visualinfo->visual,
CWColormap | CWBorderPixel | CWEventMask, &attributes);
XFree(visualinfo);
XSetWindowBackground(display, xwindow, /* color = */ 0);
XMapWindow(display, xwindow);
}
gc = XCreateGC(display, xwindow, 0, 0);
//set colorkey to auto paint, so that Xv video output is always visible
const Atom atom = XInternAtom(display, "XV_AUTOPAINT_COLORKEY", true);
Atom atom = XInternAtom(display, "XV_AUTOPAINT_COLORKEY", true);
if(atom != None) XvSetPortAttribute(display, xv_port, atom, 1);
//0x32595559 = 16-bit Y8U8,Y8V8 (YUY2)
@ -130,6 +212,7 @@ public:
fprintf(stderr, "VideoXv: XShmCreateImage failed.\n");
return false;
}
shminfo.shmid = shmget(IPC_PRIVATE, xvimage->data_size, IPC_CREAT | 0777);
shminfo.shmaddr = xvimage->data = (char*)shmat(shminfo.shmid, 0, 0);
shminfo.readOnly = false;
@ -138,6 +221,7 @@ public:
return false;
}
buffer = new uint32_t[1024 * 1024];
init_yuv_tables();
clear();
return true;
@ -145,33 +229,47 @@ public:
void term() {
XShmDetach(display, &shminfo);
free(ytable);
free(utable);
free(vtable);
if(use_child_window) {
if(xwindow) {
XUnmapWindow(display, xwindow);
xwindow = 0;
}
if(colormap) {
XFreeColormap(display, colormap);
colormap = 0;
}
}
if(buffer) { delete[] buffer; buffer = 0; }
if(ytable) { delete[] ytable; ytable = 0; }
if(utable) { delete[] utable; utable = 0; }
if(vtable) { delete[] vtable; vtable = 0; }
}
void init_yuv_tables() {
ytable = (uint8_t*)malloc(65536);
utable = (uint8_t*)malloc(65536);
vtable = (uint8_t*)malloc(65536);
ytable = new uint8_t[65536];
utable = new uint8_t[65536];
vtable = new uint8_t[65536];
for(int i = 0; i < 65536; i++) {
//extract RGB565 color data from i
uint8_t r = (i >> 11) & 31, g = (i >> 5) & 63, b = (i) & 31;
for(unsigned i = 0; i < 65536; i++) {
//extract RGB565 color data from i
uint8_t r = (i >> 11) & 31, g = (i >> 5) & 63, b = (i) & 31;
r = (r << 3) | (r >> 2); //R5->R8
g = (g << 2) | (g >> 4); //G6->G8
b = (b << 3) | (b >> 2); //B5->B8
//RGB->YUV conversion
int y = int( +(double(r) * 0.257) + (double(g) * 0.504) + (double(b) * 0.098) + 16.0 );
int u = int( -(double(r) * 0.148) - (double(g) * 0.291) + (double(b) * 0.439) + 128.0 );
int v = int( +(double(r) * 0.439) - (double(g) * 0.368) - (double(b) * 0.071) + 128.0 );
//RGB->YUV conversion
int y = int( +(double(r) * 0.257) + (double(g) * 0.504) + (double(b) * 0.098) + 16.0 );
int u = int( -(double(r) * 0.148) - (double(g) * 0.291) + (double(b) * 0.439) + 128.0 );
int v = int( +(double(r) * 0.439) - (double(g) * 0.368) - (double(b) * 0.071) + 128.0 );
//RGB->YCbCr conversion
//double lr = 0.2126, lb = 0.0722, lg = (1.0 - lr - lb);
//int y = int( double(r) * lr + double(g) * lg + double(b) * lb );
//int u = int( (double(b) - y) / (2.0 - 2.0 * lb) + 128.0 );
//int v = int( (double(r) - y) / (2.0 - 2.0 * lr) + 128.0 );
//RGB->YCbCr conversion
//double lr = 0.2126, lb = 0.0722, lg = (1.0 - lr - lb);
//int y = int( double(r) * lr + double(g) * lg + double(b) * lb );
//int u = int( (double(b) - y) / (2.0 - 2.0 * lb) + 128.0 );
//int v = int( (double(r) - y) / (2.0 - 2.0 * lr) + 128.0 );
ytable[i] = y < 0 ? 0 : y > 255 ? 255 : y;
utable[i] = u < 0 ? 0 : u > 255 ? 255 : u;
@ -180,7 +278,16 @@ public:
}
pVideoXv(VideoXv &self_) : self(self_) {
use_child_window = false;
xwindow = 0;
colormap = 0;
ytable = 0;
utable = 0;
vtable = 0;
settings.handle = 0;
settings.synchronize = false;
}
};

View File

@ -58,12 +58,12 @@ public:
alwaysinline void addclocks_cpu(uint clocks) {
clock.cpusmp -= clocks * (uint64)clock.smp_freq;
if(clock.cpusmp < -(250000 * (int64)20000000)) sync_cpusmp();
if(clock.cpusmp < -(20000 * (int64)24000000)) sync_cpusmp();
}
alwaysinline void addclocks_smp(uint clocks) {
clock.cpusmp += clocks * (uint64)clock.cpu_freq;
if(clock.cpusmp > +(250000 * (int64)20000000)) sync_smpcpu();
if(clock.cpusmp > +(20000 * (int64)24000000)) sync_smpcpu();
clock.smpdsp -= clocks;
#if !defined(USE_STATE_MACHINE)
sync_smpdsp();

View File

@ -103,7 +103,21 @@ uintptr_t MainWindow::event(event_t e) {
if(e.widget == &menu_settings_emuspeed_normal) { event::update_emulation_speed(2); }
if(e.widget == &menu_settings_emuspeed_fast) { event::update_emulation_speed(3); }
if(e.widget == &menu_settings_emuspeed_fastest) { event::update_emulation_speed(4); }
if(e.widget == &menu_settings_emuspeed_disabled) { event::update_emulation_speed(5); }
if(e.widget == &menu_settings_emuspeed_videosync) {
if(config::video.mode == 0) {
config::video.windowed.synchronize = menu_settings_emuspeed_videosync.checked();
video.set(Video::Synchronize, config::video.windowed.synchronize);
} else {
config::video.fullscreen.synchronize = menu_settings_emuspeed_videosync.checked();
video.set(Video::Synchronize, config::video.fullscreen.synchronize);
}
}
if(e.widget == &menu_settings_emuspeed_audiosync) {
config::audio.synchronize = menu_settings_emuspeed_audiosync.checked();
audio.set(Audio::Synchronize, config::audio.synchronize);
}
if(e.widget == &menu_settings_config) { window_settings.show(); }
@ -111,10 +125,6 @@ uintptr_t MainWindow::event(event_t e) {
(menu_misc_logaudio.checked() == true) ? snes.audio.log_enable() : snes.audio.log_disable();
}
if(e.widget == &menu_misc_showstatus) {
status.show(config::misc.status_enable = menu_misc_showstatus.checked());
}
if(e.widget == &menu_misc_about) {
window_about.focus();
}
@ -193,10 +203,15 @@ void MainWindow::setup() {
menu_settings.attach(menu_settings_videofilter.create(translate["Video Filter"]));
group.add(&menu_settings_videofilter_hwpoint);
group.add(&menu_settings_videofilter_hwlinear);
menu_settings_videofilter.attach(menu_settings_videofilter_hwpoint.create(group, translate["Point"]));
menu_settings_videofilter.attach(menu_settings_videofilter_hwlinear.create(group, translate["Linear"]));
menu_settings_videofilter_hwpoint.create(group, translate["Point"]);
menu_settings_videofilter_hwlinear.create(group, translate["Linear"]);
group.reset();
menu_settings_videofilter.attach(menu_settings_videofilter_sep1.create());
menu_settings_videofilter_sep1.create();
if(video.cap(Video::Filter)) {
menu_settings_videofilter.attach(menu_settings_videofilter_hwpoint);
menu_settings_videofilter.attach(menu_settings_videofilter_hwlinear);
menu_settings_videofilter.attach(menu_settings_videofilter_sep1);
}
group.add(&menu_settings_videofilter_swnone);
group.add(&menu_settings_videofilter_swscanline);
group.add(&menu_settings_videofilter_swscale2x);
@ -236,25 +251,46 @@ void MainWindow::setup() {
menu_settings.attach(menu_settings_mute.create(translate["Mute Audio Output"]));
menu_settings.attach(menu_settings_sep2.create());
menu_settings.attach(menu_settings_emuspeed.create(translate["Emulation Speed"]));
menu_settings_emuspeed.create(translate["Emulation Speed"]);
group.add(&menu_settings_emuspeed_slowest);
group.add(&menu_settings_emuspeed_slow);
group.add(&menu_settings_emuspeed_normal);
group.add(&menu_settings_emuspeed_fast);
group.add(&menu_settings_emuspeed_fastest);
group.add(&menu_settings_emuspeed_disabled);
menu_settings_emuspeed.attach(menu_settings_emuspeed_slowest.create(group, translate["50%"]));
menu_settings_emuspeed.attach(menu_settings_emuspeed_slow.create(group, translate["75%"]));
menu_settings_emuspeed.attach(menu_settings_emuspeed_normal.create(group, translate["100%"]));
menu_settings_emuspeed.attach(menu_settings_emuspeed_fast.create(group, translate["150%"]));
menu_settings_emuspeed.attach(menu_settings_emuspeed_fastest.create(group, translate["200%"]));
menu_settings_emuspeed.attach(menu_settings_emuspeed_disabled.create(group, translate["Uncapped"]));
menu_settings_emuspeed_slowest.create(group, translate["50%"]);
menu_settings_emuspeed_slow.create(group, translate["75%"]);
menu_settings_emuspeed_normal.create(group, translate["100%"]);
menu_settings_emuspeed_fast.create(group, translate["150%"]);
menu_settings_emuspeed_fastest.create(group, translate["200%"]);
group.reset();
menu_settings_emuspeed_sep1.create();
menu_settings_emuspeed_videosync.create(translate["Sync to Video"]);
menu_settings_emuspeed_audiosync.create(translate["Sync to Audio"]);
if(audio.cap(Audio::Frequency)) {
//only audio can sync to specific frequency rates; video syncs only to monitor refresh rate
menu_settings_emuspeed.attach(menu_settings_emuspeed_slowest);
menu_settings_emuspeed.attach(menu_settings_emuspeed_slow);
menu_settings_emuspeed.attach(menu_settings_emuspeed_normal);
menu_settings_emuspeed.attach(menu_settings_emuspeed_fast);
menu_settings_emuspeed.attach(menu_settings_emuspeed_fastest);
menu_settings_emuspeed.attach(menu_settings_emuspeed_sep1);
}
if(video.cap(Video::Synchronize)) {
menu_settings_emuspeed.attach(menu_settings_emuspeed_videosync);
}
if(audio.cap(Audio::Synchronize)) {
menu_settings_emuspeed.attach(menu_settings_emuspeed_audiosync);
}
if(video.cap(Video::Synchronize) || audio.cap(Audio::Synchronize)) {
menu_settings.attach(menu_settings_emuspeed);
}
menu_settings.attach(menu_settings_config.create(string() << translate["Configuration"] << " ..."));
attach(menu_misc.create(translate["Misc"]));
menu_misc.attach(menu_misc_logaudio.create(translate["Log Audio Data"]));
menu_misc.attach(menu_misc_showstatus.create(translate["Show Statusbar"]));
menu_misc.attach(menu_misc_sep1.create());
menu_misc.attach(menu_misc_about.create(string() << translate["About"] << " ..."));
@ -320,16 +356,16 @@ void MainWindow::setup() {
menu_settings_emuspeed_normal.on_tick =
menu_settings_emuspeed_fast.on_tick =
menu_settings_emuspeed_fastest.on_tick =
menu_settings_emuspeed_disabled.on_tick =
menu_settings_emuspeed_videosync.on_tick =
menu_settings_emuspeed_audiosync.on_tick =
menu_settings_config.on_tick =
menu_misc_logaudio.on_tick =
menu_misc_showstatus.on_tick =
menu_misc_about.on_tick =
bind(&MainWindow::event, this);
if(config::misc.status_enable) status.show();
status.show();
}
void MainWindow::sync() {
@ -396,8 +432,12 @@ void MainWindow::sync() {
case 2: menu_settings_emuspeed_normal.check(); break;
case 3: menu_settings_emuspeed_fast.check(); break;
case 4: menu_settings_emuspeed_fastest.check(); break;
case 5: menu_settings_emuspeed_disabled.check(); break;
}
menu_misc_showstatus.check(config::misc.status_enable);
if(config::video.mode == 0) {
menu_settings_emuspeed_videosync.check(config::video.windowed.synchronize);
} else {
menu_settings_emuspeed_videosync.check(config::video.fullscreen.synchronize);
}
menu_settings_emuspeed_audiosync.check(config::audio.synchronize);
}

View File

@ -62,13 +62,14 @@ public:
MenuRadioItem menu_settings_emuspeed_slow;
MenuRadioItem menu_settings_emuspeed_normal;
MenuRadioItem menu_settings_emuspeed_fast;
MenuRadioItem menu_settings_emuspeed_fastest;
MenuRadioItem menu_settings_emuspeed_disabled;
MenuRadioItem menu_settings_emuspeed_fastest;
MenuSeparator menu_settings_emuspeed_sep1;
MenuCheckItem menu_settings_emuspeed_videosync;
MenuCheckItem menu_settings_emuspeed_audiosync;
MenuItem menu_settings_config;
MenuGroup menu_misc;
MenuCheckItem menu_misc_logaudio;
MenuCheckItem menu_misc_showstatus;
MenuSeparator menu_misc_sep1;
MenuItem menu_misc_about;

View File

@ -5,6 +5,7 @@ char locale_cfg[PATH_MAX] = "";
struct System {
static string_setting video, audio, input;
static integral_setting invoke_crash_handler;
static integral_setting emulation_speed;
static integral_setting gamma_ramp, sepia, grayscale, invert, contrast, brightness, gamma;
} system;
@ -13,14 +14,20 @@ string_setting System::video(config(), "system.video", "Video hardware interface
string_setting System::audio(config(), "system.audio", "Audio hardware interface", "");
string_setting System::input(config(), "system.input", "Input hardware interface", "");
integral_setting System::emulation_speed(config(), "system.emulation_speed",
"Relative speed of emulator compared to SNES hardware\n"
"0 = 50%\n"
"1 = 75%\n"
"2 = 100%\n"
"3 = 150%\n"
"4 = 200%\n"
"5 = Uncapped",
integral_setting System::invoke_crash_handler(config(), "system.invoke_crash_handler",
"Do not modify this setting!\n"
"Used to detect crashes caused by initialization of video / audio / input drivers.\n"
"When the emulator crashes during driver initialization, this value will be left as true.\n"
"When true, driver selection crash handler window will appear on next start-up.\n"
"", integral_setting::boolean, false);
integral_setting System::emulation_speed(config(), "system.emulation_speed",
"Relative speed of emulator compared to SNES hardware\n"
"0 = 50%\n"
"1 = 75%\n"
"2 = 100%\n"
"3 = 150%\n"
"4 = 200%\n",
integral_setting::decimal, 2);
integral_setting System::gamma_ramp(config(), "system.colorfilter.gamma_ramp",
@ -96,11 +103,17 @@ integral_setting Video::aspect_pal_y (config(), "video.aspect_pal_y", "", integ
integral_setting Video::frameskip("video.frameskip", "Video frameskip", integral_setting::decimal, 0);
struct Audio {
static integral_setting output_frequency, input_frequency;
static integral_setting latency;
static integral_setting volume, mute;
static integral_setting synchronize;
static integral_setting mute;
} audio;
integral_setting Audio::synchronize(config(), "audio.synchronize", "Synchronize to audio sample rate", integral_setting::boolean, true);
integral_setting Audio::output_frequency(config(), "audio.output_frequency", "Sound card audio output frequency", integral_setting::decimal, 48000);
integral_setting Audio::input_frequency(config(), "audio.input_frequency", "Emulator audio input frequency", integral_setting::decimal, 31950);
integral_setting Audio::latency(config(), "audio.latency", "Sound card latency (in ms)", integral_setting::decimal, 100);
integral_setting Audio::volume(config(), "audio.volume", "Audio volume (0 - 100)", integral_setting::decimal, 100);
integral_setting Audio::mute(config(), "audio.mute", "Mute audio playback", integral_setting::boolean, false);
integral_setting Audio::synchronize(config(), "audio.synchronize", "Synchronize to audio sample rate", integral_setting::boolean, true);
struct Input {
static integral_setting capture_mode;
@ -123,11 +136,11 @@ struct Input {
static string_setting load;
static string_setting pause;
static string_setting reset;
static string_setting power;
static string_setting quit;
static string_setting speed_decrease;
static string_setting speed_increase;
static string_setting frameskip_decrease;
static string_setting power;
static string_setting quit;
static string_setting speed_decrease;
static string_setting speed_increase;
static string_setting frameskip_decrease;
static string_setting frameskip_increase;
static string_setting toggle_fullscreen;
static string_setting toggle_menubar;
@ -212,11 +225,11 @@ DeclMultitap(Multitap2D, "2d")
string_setting Input::GUI::load (config(), "input.gui.load", "", "none");
string_setting Input::GUI::pause (config(), "input.gui.pause", "", "f12");
string_setting Input::GUI::reset (config(), "input.gui.reset", "", "none");
string_setting Input::GUI::power (config(), "input.gui.power", "", "none");
string_setting Input::GUI::quit (config(), "input.gui.quit", "", "none");
string_setting Input::GUI::speed_decrease (config(), "input.gui.speed_decrease", "", "divide");
string_setting Input::GUI::speed_increase (config(), "input.gui.speed_increase", "", "multiply");
string_setting Input::GUI::frameskip_decrease(config(), "input.gui.frameskip_decrease", "", "subtract");
string_setting Input::GUI::power (config(), "input.gui.power", "", "none");
string_setting Input::GUI::quit (config(), "input.gui.quit", "", "none");
string_setting Input::GUI::speed_decrease (config(), "input.gui.speed_decrease", "", "divide");
string_setting Input::GUI::speed_increase (config(), "input.gui.speed_increase", "", "multiply");
string_setting Input::GUI::frameskip_decrease(config(), "input.gui.frameskip_decrease", "", "subtract");
string_setting Input::GUI::frameskip_increase(config(), "input.gui.frameskip_increase", "", "add");
string_setting Input::GUI::toggle_fullscreen (config(), "input.gui.toggle_fullscreen", "", "f11");
string_setting Input::GUI::toggle_menubar (config(), "input.gui.toggle_menubar", "", "escape");
@ -224,11 +237,9 @@ string_setting Input::GUI::toggle_statusbar (config(), "input.gui.toggle_status
struct Misc {
static integral_setting opacity;
static integral_setting status_enable;
} misc;
integral_setting Misc::opacity(config(), "misc.opacity", "Opacity of user interface windows", integral_setting::decimal, 100);
integral_setting Misc::status_enable(config(), "misc.status_enable", "Display information statusbar", integral_setting::boolean, true);
struct Advanced {
static integral_setting enable;

View File

@ -118,22 +118,19 @@ void update_frameskip(int speed) {
}
void update_emulation_speed(int speed) {
config::system.emulation_speed = max(0, min(5, speed));
config::system.emulation_speed = max(0, min(4, speed));
//adjust audio frequency to match selected speed setting
if(audio.cap(Audio::Frequency)) {
switch(config::system.emulation_speed) {
case 0: audio.set(Audio::Frequency, 16000); break; //slowest (50%)
case 1: audio.set(Audio::Frequency, 24000); break; //slow (75%)
case 2: audio.set(Audio::Frequency, 32000); break; //normal (100%)
case 3: audio.set(Audio::Frequency, 48000); break; //fast (150%)
case 4: audio.set(Audio::Frequency, 64000); break; //fastest (200%)
case 5: audio.set(Audio::Frequency, 32000); break; //disabled
}
double scale;
switch(config::system.emulation_speed) {
case 0: scale = 0.50; break; //slowest
case 1: scale = 0.75; break; //slow
case 2: scale = 1.00; break; //normal
case 3: scale = 1.50; break; //fast
case 4: scale = 2.00; break; //fastest
}
//do not regulate speed when speed regulation is disabled
audio.set(Audio::Synchronize, config::system.emulation_speed != 5);
audio.set(Audio::ResampleOutputFrequency, config::audio.output_frequency);
audio.set(Audio::ResampleInputFrequency, config::audio.input_frequency * scale + 0.5);
window_main.sync();
}
@ -268,8 +265,7 @@ void toggle_menubar() {
}
void toggle_statusbar() {
config::misc.status_enable = !window_main.status.visible();
window_main.status.show(config::misc.status_enable);
window_main.status.show(!window_main.status.visible());
update_video_settings();
}

View File

@ -156,6 +156,7 @@ int main(int argc, char *argv[]) { */
hiro().init();
ui_init();
if(app.term == true) goto app_term;
snes.init();
if(argc >= 2 && fexists(argv[1])) {
@ -170,6 +171,7 @@ int main(int argc, char *argv[]) { */
event::unload_rom();
app_term:
config::config().save(config::bsnes_cfg);
snes.term();
ui_term();

View File

@ -55,14 +55,14 @@ void AdvancedWindow::load() {
string name = config::config().list[i]->name;
//blacklist (omit/hide options that can be configured through the standard UI)
if(strbegin(name, "system.")) continue;
if(strbegin(name, "path.")) continue;
if(strbegin(name, "snes.controller_port")) continue;
if(strpos(name, "colorfilter.") >= 0) continue;
if(name == "misc.status_enable") continue;
if(name == "system.emulation_speed") continue;
if(strbegin(name, "video.windowed.") && name != "video.windowed.synchronize") continue;
if(strbegin(name, "video.fullscreen.") && name != "video.fullscreen.synchronize") continue;
if(name == "audio.mute") continue;
if(strbegin(name, "video.windowed.")) continue;
if(strbegin(name, "video.fullscreen.")) continue;
if(strbegin(name, "audio.")) continue;
if(name == "input.capture_mode") continue;
if(strbegin(name, "input.joypad")) continue;
if(strbegin(name, "input.multitap")) continue;

View File

@ -0,0 +1,116 @@
uintptr_t AudioSettingsWindow::volume_change(event_t) {
config::audio.volume = 10 + volume.get_position();
audio.set(Audio::Volume, config::audio.volume);
sync_ui();
return true;
}
uintptr_t AudioSettingsWindow::latency_change(event_t) {
config::audio.latency = 25 + latency.get_position() * 5;
audio.set(Audio::Latency, config::audio.latency);
sync_ui();
return true;
}
uintptr_t AudioSettingsWindow::outputfrequency_change(event_t) {
switch(outputfrequency.get_position()) { default:
case 0: config::audio.output_frequency = 32000; break;
case 1: config::audio.output_frequency = 44100; break;
case 2: config::audio.output_frequency = 48000; break;
case 3: config::audio.output_frequency = 96000; break;
}
audio.set(Audio::Frequency, config::audio.output_frequency);
event::update_emulation_speed(config::system.emulation_speed);
sync_ui();
return true;
}
uintptr_t AudioSettingsWindow::inputfrequency_change(event_t) {
config::audio.input_frequency = inputfrequency.get_position() + 32000 - 200;
event::update_emulation_speed(config::system.emulation_speed);
sync_ui();
return true;
}
void AudioSettingsWindow::sync_ui() {
lvolume.set_text(string()
<< translate["Volume:"] << " "
<< (int)config::audio.volume << "%"
);
volume.set_position(config::audio.volume - 10);
llatency.set_text(string()
<< translate["Latency:"] << " "
<< (int)config::audio.latency << "ms"
);
latency.set_position((config::audio.latency - 25) / 5);
loutputfrequency.set_text(string()
<< translate["PC output frequency:"] << " "
<< (int)config::audio.output_frequency << "hz"
);
unsigned position;
if(config::audio.output_frequency <= 32000) position = 0;
else if(config::audio.output_frequency <= 44100) position = 1;
else if(config::audio.output_frequency <= 48000) position = 2;
else position = 3;
outputfrequency.set_position(position);
int input_freq = config::audio.input_frequency - 32000;
string adjust;
if(input_freq > 0) adjust << "+";
adjust << input_freq;
if(config::advanced.enable == true) {
adjust << " (" << (int)config::audio.input_frequency << "hz)";
}
linputfrequency.set_text(string()
<< translate["SNES input frequency adjust:"] << " "
<< adjust
);
inputfrequency.set_position(input_freq + 200);
}
void AudioSettingsWindow::setup() {
create(0, 475, 355);
lvolume.create(0, 475, 18);
//if advanced UI mode is disabled, volume range is 10% - 100%
//if enabled, volume range is 10% - 200%
//101%+ is hidden by default, as it can cause audio quality degradation due to clamping
volume.create(0, 475, 30, (config::advanced.enable == false ? 91 : 191));
llatency.create(0, 475, 18);
latency.create(0, 475, 30, 31);
loutputfrequency.create(0, 475, 18);
outputfrequency.create(0, 475, 30, 4);
linputfrequency.create(0, 475, 18);
inputfrequency.create(0, 475, 30, 401);
unsigned y = 0;
attach(lvolume, 0, y); y += 18;
attach(volume, 0, y); y += 30;
if(audio.cap(Audio::Latency) == true) {
attach(llatency, 0, y); y += 18;
attach(latency, 0, y); y += 30;
}
if(audio.cap(Audio::Frequency) == true) {
attach(loutputfrequency, 0, y); y += 18;
attach(outputfrequency, 0, y); y += 30;
}
attach(linputfrequency, 0, y); y += 18;
attach(inputfrequency, 0, y); y += 30;
volume.on_change = bind(&AudioSettingsWindow::volume_change, this);
latency.on_change = bind(&AudioSettingsWindow::latency_change, this);
outputfrequency.on_change = bind(&AudioSettingsWindow::outputfrequency_change, this);
inputfrequency.on_change = bind(&AudioSettingsWindow::inputfrequency_change, this);
sync_ui();
}

View File

@ -0,0 +1,22 @@
class AudioSettingsWindow : public Window {
public:
Label lvolume;
Slider volume;
Label llatency;
Slider latency;
Label loutputfrequency;
Slider outputfrequency;
Label linputfrequency;
Slider inputfrequency;
uintptr_t volume_change(event_t);
uintptr_t latency_change(event_t);
uintptr_t outputfrequency_change(event_t);
uintptr_t inputfrequency_change(event_t);
void sync_ui();
void setup();
} window_audio_settings;

View File

@ -0,0 +1,146 @@
uintptr_t DriverSelectWindow::video_change(event_t) {
lstring part;
split(part, ";", video.driver_list());
config::system.video = part[cvideo.get_selection()];
return true;
}
uintptr_t DriverSelectWindow::audio_change(event_t) {
lstring part;
split(part, ";", audio.driver_list());
config::system.audio = part[caudio.get_selection()];
return true;
}
uintptr_t DriverSelectWindow::input_change(event_t) {
lstring part;
split(part, ";", input.driver_list());
config::system.input = part[cinput.get_selection()];
return true;
}
void DriverSelectWindow::setup() {
create(0, 475, 355);
//this is only displayed if app.crashed_on_last_run == true
crash_message.create(0, 475, 36, string()
<< translate["WARNING: bsnes crashed on last startup due to incompatible driver settings."] << "\n"
<< translate["Please select a different driver configuration below."]
);
lstring part;
lvideo.create(0, 155, 18, translate["Video Driver:"]);
cvideo.create(0, 155, 25);
split(part, ";", video.driver_list());
for(unsigned i = 0; i < count(part); i++) {
cvideo.add_item(part[i]);
if(part[i] == config::system.video) cvideo.set_selection(i);
}
laudio.create(0, 155, 18, translate["Audio Driver:"]);
caudio.create(0, 155, 25);
split(part, ";", audio.driver_list());
for(unsigned i = 0; i < count(part); i++) {
caudio.add_item(part[i]);
if(part[i] == config::system.audio) caudio.set_selection(i);
}
linput.create(0, 155, 18, translate["Input Driver:"]);
cinput.create(0, 155, 25);
split(part, ";", input.driver_list());
for(unsigned i = 0; i < count(part); i++) {
cinput.add_item(part[i]);
if(part[i] == config::system.input) cinput.set_selection(i);
}
video_caps.create(0, 475, 18);
video_sync.create(0, 155, 18, translate["Synchronize"]);
video_sync.disable();
video_filter.create(0, 155, 18, translate["Hardware Filtering"]);
video_filter.disable();
audio_caps.create(0, 475, 18);
audio_sync.create(0, 155, 18, translate["Synchronize"]);
audio_sync.disable();
audio_freq.create(0, 155, 18, translate["Frequency Control"]);
audio_freq.disable();
audio_latency.create(0, 155, 18, translate["Latency Control"]);
audio_latency.disable();
input_caps.create(0, 475, 18);
input_keyboard.create(0, 155, 18, translate["Keyboard Support"]);
input_keyboard.disable();
input_joypad.create(0, 155, 18, translate["Joypad Support"]);
input_joypad.disable();
restart_message.create(0, 475, 36, string()
<< translate["Note: bsnes must be restarted for changes to take effect."] << "\n"
<< translate["If bsnes then crashes on startup, restart it again to adjust driver settings."]
);
bool crashed = config::system.invoke_crash_handler;
string t = translate["Capabilities of active video driver ($):"];
replace(t, "$", config::system.video);
video_caps.set_text(t);
video_sync.check(video.cap(Video::Synchronize));
video_filter.check(video.cap(Video::Filter));
t = translate["Capabilities of active audio driver ($):"];
replace(t, "$", config::system.audio);
audio_caps.set_text(t);
audio_sync.check(audio.cap(Audio::Synchronize));
audio_freq.check(audio.cap(Audio::Frequency));
audio_latency.check(audio.cap(Audio::Latency));
t = translate["Capabilities of active input driver ($):"];
replace(t, "$", config::system.input);
input_caps.set_text(t);
input_keyboard.check(input.cap(Input::KeyboardSupport));
input_joypad.check(input.cap(Input::JoypadSupport));
unsigned y = 5;
if(crashed == true) {
attach(crash_message, 0, y); y += 36 + 5;
}
attach(lvideo, 0, y);
attach(laudio, 160, y);
attach(linput, 320, y); y += 18;
attach(cvideo, 0, y);
attach(caudio, 160, y);
attach(cinput, 320, y); y += 25 + 5;
if(crashed == false) {
attach(video_caps, 0, y); y += 18;
attach(video_sync, 0, y);
attach(video_filter, 160, y); y += 18 + 5;
attach(audio_caps, 0, y); y += 18;
attach(audio_sync, 0, y);
attach(audio_freq, 160, y);
attach(audio_latency, 320, y); y += 18 + 5;
attach(input_caps, 0, y); y += 18;
attach(input_keyboard, 0, y);
attach(input_joypad, 160, y); y += 18 + 5;
attach(restart_message, 0, y); y += 36 + 5;
}
cvideo.on_change = bind(&DriverSelectWindow::video_change, this);
caudio.on_change = bind(&DriverSelectWindow::audio_change, this);
cinput.on_change = bind(&DriverSelectWindow::input_change, this);
}

View File

@ -0,0 +1,32 @@
class DriverSelectWindow : public Window {
public:
Label crash_message;
Label lvideo;
Combobox cvideo;
Label laudio;
Combobox caudio;
Label linput;
Combobox cinput;
Label video_caps;
Checkbox video_sync;
Checkbox video_filter;
Label audio_caps;
Checkbox audio_sync;
Checkbox audio_freq;
Checkbox audio_latency;
Label input_caps;
Checkbox input_keyboard;
Checkbox input_joypad;
Label restart_message;
uintptr_t video_change(event_t);
uintptr_t audio_change(event_t);
uintptr_t input_change(event_t);
void setup();
} window_driver_select;

View File

@ -3,14 +3,18 @@ void SettingsWindow::setup() {
set_icon(48, 48, (uint32_t*)resource::icon48);
panel_list.create(0, 135, 355);
panel_list.add_item(translate["Drivers"]);
panel_list.add_item(translate["Video"]);
panel_list.add_item(translate["Audio"]);
panel_list.add_item(translate["Input"]);
panel_list.add_item(translate["Paths"]);
panel_list.add_item(translate["Cheat Codes"]);
panel_list.add_item(translate["Advanced"]);
attach(panel_list, 5, 5);
attach(window_driver_select, 145, 5);
attach(window_video_settings, 145, 5);
attach(window_audio_settings, 145, 5);
attach(window_input_config, 145, 5);
attach(window_path_settings, 145, 5);
attach(window_cheat_editor, 145, 5);
@ -18,7 +22,12 @@ void SettingsWindow::setup() {
on_close = bind(&SettingsWindow::close, this);
panel_list.on_change = bind(&SettingsWindow::list_change, this);
panel_list.set_selection(1); //default to input configuration (most frequently used panel)
//if emulator crashed on last run, default to driver select window and disable list selection,
//otherwise default to input configuration (most frequently used panel)
panel_list.set_selection(config::system.invoke_crash_handler == true ? 0 : 3);
panel_list.enable(config::system.invoke_crash_handler == false);
list_change(event_t(event_t::Change));
}
uintptr_t SettingsWindow::close(event_t) {
@ -27,18 +36,22 @@ uintptr_t SettingsWindow::close(event_t) {
}
uintptr_t SettingsWindow::list_change(event_t) {
window_driver_select.hide();
window_video_settings.hide();
window_audio_settings.hide();
window_input_config.hide();
window_path_settings.hide();
window_cheat_editor.hide();
window_advanced.hide();
switch(panel_list.get_selection()) {
case 0: window_video_settings.show(); break;
case 1: window_input_config.show(); break;
case 2: window_path_settings.show(); break;
case 3: window_cheat_editor.show(); break;
case 4: window_advanced.show(); break;
case 0: window_driver_select.show(); break;
case 1: window_video_settings.show(); break;
case 2: window_audio_settings.show(); break;
case 3: window_input_config.show(); break;
case 4: window_path_settings.show(); break;
case 5: window_cheat_editor.show(); break;
case 6: window_advanced.show(); break;
}
panel_list.focus();

View File

@ -7,7 +7,9 @@
#include "loader/stloader.cpp"
#include "settings/settings.cpp"
#include "settings/driverselect.cpp"
#include "settings/videosettings.cpp"
#include "settings/audiosettings.cpp"
#include "settings/inputconfig.cpp"
#include "settings/pathsettings.cpp"
#include "settings/cheateditor.cpp"
@ -17,13 +19,26 @@ void ui_init() {
hiro().disable_screensaver();
resource::init();
//ruby can initialize with "", but driver selection window needs active driver name
if(config::system.video == "") config::system.video = video.default_driver();
if(config::system.audio == "") config::system.audio = audio.default_driver();
if(config::system.input == "") config::system.input = input.default_driver();
if(config::system.invoke_crash_handler == false) {
video.driver(config::system.video);
audio.driver(config::system.audio);
input.driver(config::system.input);
}
window_main.setup();
window_about.setup();
window_bsxloader.setup();
window_stloader.setup();
window_driver_select.setup();
window_video_settings.setup();
window_audio_settings.setup();
window_input_config.setup();
window_path_settings.setup();
window_cheat_editor.setup();
@ -32,26 +47,54 @@ void ui_init() {
event::update_opacity();
event::update_video_settings(); //call first time to resize main window and update menubar
if(config::system.invoke_crash_handler == true) {
//application crashed during driver setup on last run ...
//allow selection of new drivers, and then exit emulator.
window_settings.show();
while(window_settings.visible()) {
while(hiro().pending()) hiro().run();
usleep(20 * 1000);
}
config::system.invoke_crash_handler = false;
event::quit();
return;
}
window_main.show();
while(hiro().pending()) hiro().run();
video.driver(config::system.video);
audio.driver(config::system.audio);
input.driver(config::system.input);
//detect crashes during driver initialization
config::system.invoke_crash_handler = true;
config::config().save(config::bsnes_cfg);
video.set(Video::Handle, window_main.view.handle());
video.set(Video::Synchronize, false);
video.set(Video::Synchronize, config::video.windowed.synchronize);
audio.set(Audio::Resample, true);
audio.set(Audio::Handle, window_main.handle());
audio.set(Audio::Synchronize, config::audio.synchronize);
audio.set(Audio::Volume, config::audio.volume);
audio.set(Audio::Frequency, config::audio.output_frequency);
audio.set(Audio::Latency, config::audio.latency);
input.set(Input::Handle, window_main.handle());
input.set(Input::AnalogAxisResistance, config::input.analog_axis_resistance);
//sets Audio::Synchronize and Audio::Frequency
//sets Audio::ResampleOutputFrequency and Audio::ResampleInputFrequency
event::update_emulation_speed(config::system.emulation_speed);
video.init();
audio.init();
input.init();
event::update_video_settings(); //call second time to update uiVideo->settings
video.clear();
audio.clear();
input.clear();
//if code has reached this point, driver initialized successfully
config::system.invoke_crash_handler = false;
config::config().save(config::bsnes_cfg);
event::update_video_settings(); //call second time to update video class settings
//UI setup complete, hook keyboard callbacks
snesinterface.input_ready = bind(&MainWindow::input_ready, &window_main);

View File

@ -11,7 +11,9 @@ nall::dictionary translate;
#include "loader/stloader.h"
#include "settings/settings.h"
#include "settings/driverselect.h"
#include "settings/videosettings.h"
#include "settings/audiosettings.h"
#include "settings/inputconfig.h"
#include "settings/pathsettings.h"
#include "settings/cheateditor.h"