mirror of https://github.com/bsnes-emu/bsnes.git
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:
parent
d09e54149b
commit
e2cc164f70
|
@ -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`))
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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
|
|
@ -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
|
|
@ -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() {
|
||||
|
|
|
@ -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; }
|
||||
|
|
|
@ -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() {
|
||||
|
|
|
@ -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(); }
|
||||
|
|
|
@ -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() {
|
||||
|
|
|
@ -2,6 +2,8 @@ class Input {
|
|||
public:
|
||||
enum Setting {
|
||||
Handle,
|
||||
KeyboardSupport,
|
||||
JoypadSupport,
|
||||
AnalogAxisResistance,
|
||||
};
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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(); }
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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() {
|
||||
|
|
|
@ -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();
|
||||
|
||||
|
|
|
@ -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();
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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();
|
||||
}
|
|
@ -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;
|
|
@ -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);
|
||||
}
|
|
@ -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;
|
|
@ -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();
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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"
|
||||
|
|
Loading…
Reference in New Issue