diff --git a/higan/GNUmakefile b/higan/GNUmakefile index b0ad6d29..5eac8241 100644 --- a/higan/GNUmakefile +++ b/higan/GNUmakefile @@ -8,7 +8,7 @@ objects := libco emulator audio video resource flags += -I. -I.. ifeq ($(platform),windows) - link += -mwindows + link += $(if $(call streq,$(console),true),-mconsole,-mwindows) ifeq ($(binary),application) link += -mthreads -lpthread -luuid -lkernel32 -luser32 -lgdi32 -lcomctl32 -lcomdlg32 -lshell32 link += -Wl,-enable-auto-import diff --git a/higan/emulator/emulator.hpp b/higan/emulator/emulator.hpp index 9d1d4833..4ff509f4 100644 --- a/higan/emulator/emulator.hpp +++ b/higan/emulator/emulator.hpp @@ -12,7 +12,7 @@ using namespace nall; namespace Emulator { static const string Name = "higan"; - static const string Version = "104.17"; + static const string Version = "105"; static const string Author = "byuu"; static const string License = "GPLv3"; static const string Website = "https://byuu.org/"; diff --git a/higan/target-tomoko/input/hotkeys.cpp b/higan/target-tomoko/input/hotkeys.cpp index d33fe898..76304fc9 100644 --- a/higan/target-tomoko/input/hotkeys.cpp +++ b/higan/target-tomoko/input/hotkeys.cpp @@ -96,7 +96,7 @@ auto InputManager::appendHotkeys() -> void { } auto InputManager::pollHotkeys() -> void { - if(!program->focused()) return; + if(!program->focused() && !settings["Input/FocusLoss/AllowInput"].boolean()) return; for(auto& hotkey : hotkeys) { int16 state = hotkey->poll(); diff --git a/hiro/cocoa/application.cpp b/hiro/cocoa/application.cpp index 7cc054fd..1155fd22 100644 --- a/hiro/cocoa/application.cpp +++ b/hiro/cocoa/application.cpp @@ -33,17 +33,18 @@ @end CocoaDelegate* cocoaDelegate = nullptr; +NSTimer* applicationTimer = nullptr; namespace hiro { auto pApplication::run() -> void { -//NSTimer* timer = [NSTimer scheduledTimerWithTimeInterval:0.1667 target:cocoaDelegate selector:@selector(updateInDock:) userInfo:nil repeats:YES]; +//applicationTimer = [NSTimer scheduledTimerWithTimeInterval:0.1667 target:cocoaDelegate selector:@selector(updateInDock:) userInfo:nil repeats:YES]; if(Application::state.onMain) { - NSTimer* timer = [NSTimer scheduledTimerWithTimeInterval:0.0 target:cocoaDelegate selector:@selector(run:) userInfo:nil repeats:YES]; + applicationTimer = [NSTimer scheduledTimerWithTimeInterval:0.0 target:cocoaDelegate selector:@selector(run:) userInfo:nil repeats:YES]; //below line is needed to run application during window resize; however it has a large performance penalty on the resize smoothness - //[[NSRunLoop currentRunLoop] addTimer:timer forMode:NSEventTrackingRunLoopMode]; + //[[NSRunLoop currentRunLoop] addTimer:applicationTimer forMode:NSEventTrackingRunLoopMode]; } @autoreleasepool { @@ -74,6 +75,7 @@ auto pApplication::processEvents() -> void { auto pApplication::quit() -> void { @autoreleasepool { + [applicationTimer invalidate]; [NSApp stop:nil]; NSEvent* event = [NSEvent otherEventWithType:NSApplicationDefined location:NSMakePoint(0, 0) modifierFlags:0 timestamp:0.0 windowNumber:0 context:nil subtype:0 data1:0 data2:0]; [NSApp postEvent:event atStart:true]; diff --git a/ruby/audio/alsa.cpp b/ruby/audio/alsa.cpp index d8abf843..72e1a44d 100644 --- a/ruby/audio/alsa.cpp +++ b/ruby/audio/alsa.cpp @@ -54,8 +54,9 @@ struct AudioALSA : Audio { auto output(const double samples[]) -> void { if(!ready()) return; - _buffer[_offset++] = uint16_t(samples[0] * 32768.0) << 0 | uint16_t(samples[1] * 32768.0) << 16; - if(_offset < _periodSize) return; + _buffer[_offset] = (uint16_t)sclamp<16>(samples[0] * 32767.0) << 0; + _buffer[_offset] |= (uint16_t)sclamp<16>(samples[1] * 32767.0) << 16; + if(++_offset < _periodSize) return; snd_pcm_sframes_t available; do { diff --git a/ruby/audio/ao.cpp b/ruby/audio/ao.cpp index a2f8e37f..839fa4b3 100644 --- a/ruby/audio/ao.cpp +++ b/ruby/audio/ao.cpp @@ -33,7 +33,9 @@ struct AudioAO : Audio { } auto output(const double samples[]) -> void { - uint32_t sample = uint16_t(samples[0] * 32768.0) << 0 | uint16_t(samples[1] * 32768.0) << 16; + uint32_t sample = 0; + sample |= (uint16_t)sclamp<16>(samples[0] * 32767.0) << 0; + sample |= (uint16_t)sclamp<16>(samples[1] * 32767.0) << 16; ao_play(_interface, (char*)&sample, 4); } diff --git a/ruby/audio/asio.cpp b/ruby/audio/asio.cpp index 02bc013e..8fc822e2 100644 --- a/ruby/audio/asio.cpp +++ b/ruby/audio/asio.cpp @@ -208,30 +208,30 @@ private: switch(_sampleFormat) { case ASIOSTInt16LSB: { - *(int16_t*)buffer = samples[n] * double(1 << 15); + *(uint16_t*)buffer = (uint16_t)sclamp<16>(samples[n] * (32768.0 - 1.0)); break; } case ASIOSTInt24LSB: { - int value = samples[n] * double(1 << 23); - buffer[0] = value >> 0; - buffer[1] = value >> 8; + auto value = (uint32_t)sclamp<24>(samples[n] * (256.0 * 32768.0 - 1.0)); + buffer[0] = value >> 0; + buffer[1] = value >> 8; buffer[2] = value >> 16; break; } case ASIOSTInt32LSB: { - *(int32_t*)buffer = samples[n] * double(1 << 31); + *(uint32_t*)buffer = (uint32_t)sclamp<32>(samples[n] * (65536.0 * 32768.0 - 1.0)); break; } case ASIOSTFloat32LSB: { - *(float*)buffer = samples[n]; + *(float*)buffer = max(-1.0, min(+1.0, samples[n])); break; } case ASIOSTFloat64LSB: { - *(double*)buffer = samples[n]; + *(double*)buffer = max(-1.0, min(+1.0, samples[n])); break; } } diff --git a/ruby/audio/directsound.cpp b/ruby/audio/directsound.cpp index af760e5e..5a678927 100644 --- a/ruby/audio/directsound.cpp +++ b/ruby/audio/directsound.cpp @@ -70,8 +70,9 @@ struct AudioDirectSound : Audio { auto output(const double samples[]) -> void { if(!ready()) return; - _buffer[_offset++] = uint16_t(samples[0] * 32768.0) << 0 | uint16_t(samples[1] * 32768.0) << 16; - if(_offset < _period) return; + _buffer[_offset] = (uint16_t)sclamp<16>(samples[0] * 32767.0) << 0; + _buffer[_offset] |= (uint16_t)sclamp<16>(samples[1] * 32767.0) << 16; + if(++_offset < _period) return; _offset = 0; if(_blocking) { diff --git a/ruby/audio/openal.cpp b/ruby/audio/openal.cpp index 75dd9578..c606eaba 100644 --- a/ruby/audio/openal.cpp +++ b/ruby/audio/openal.cpp @@ -61,8 +61,8 @@ struct AudioOpenAL : Audio { } auto output(const double samples[]) -> void { - _buffer[_bufferLength] = int16_t(samples[0] * 32768.0) << 0; - _buffer[_bufferLength] |= int16_t(samples[1] * 32768.0) << 16; + _buffer[_bufferLength] = (uint16_t)sclamp<16>(samples[0] * 32767.0) << 0; + _buffer[_bufferLength] |= (uint16_t)sclamp<16>(samples[1] * 32767.0) << 16; if(++_bufferLength < _bufferSize) return; ALuint alBuffer = 0; diff --git a/ruby/audio/oss.cpp b/ruby/audio/oss.cpp index 705c7a48..4527123f 100644 --- a/ruby/audio/oss.cpp +++ b/ruby/audio/oss.cpp @@ -77,7 +77,7 @@ struct AudioOSS : Audio { auto output(const double samples[]) -> void { if(!ready()) return; for(auto n : range(_channels)) { - int16_t sample = samples[n] * 32768.0; + auto sample = (uint16_t)sclamp<16>(samples[n] * 32767.0); auto unused = write(_fd, &sample, 2); } } diff --git a/ruby/audio/pulseaudio.cpp b/ruby/audio/pulseaudio.cpp index 1e41e591..1f7866e3 100644 --- a/ruby/audio/pulseaudio.cpp +++ b/ruby/audio/pulseaudio.cpp @@ -46,8 +46,9 @@ struct AudioPulseAudio : Audio { auto output(const double samples[]) -> void { pa_stream_begin_write(_stream, (void**)&_buffer, &_period); - _buffer[_offset++] = uint16_t(samples[0] * 32768.0) << 0 | uint16_t(samples[1] * 32768.0) << 16; - if((_offset + 1) * pa_frame_size(&_specification) <= _period) return; + _buffer[_offset] = (uint16_t)sclamp<16>(samples[0] * 32767.0) << 0; + _buffer[_offset] |= (uint16_t)sclamp<16>(samples[1] * 32767.0) << 16; + if((++_offset + 1) * pa_frame_size(&_specification) <= _period) return; while(true) { if(_first) { diff --git a/ruby/audio/pulseaudiosimple.cpp b/ruby/audio/pulseaudiosimple.cpp index 275b1cd8..b0f0a498 100644 --- a/ruby/audio/pulseaudiosimple.cpp +++ b/ruby/audio/pulseaudiosimple.cpp @@ -36,8 +36,9 @@ struct AudioPulseAudioSimple : Audio { auto output(const double samples[]) -> void { if(!ready()) return; - _buffer[_offset++] = uint16_t(samples[0] * 32768.0) << 0 | uint16_t(samples[1] * 32768.0) << 16; - if(_offset >= 64) { + _buffer[_offset] = (uint16_t)sclamp<16>(samples[0] * 32767.0) << 0; + _buffer[_offset] |= (uint16_t)sclamp<16>(samples[1] * 32767.0) << 16; + if(++_offset >= 64) { int error; pa_simple_write(_interface, (const void*)_buffer, _offset * sizeof(uint32_t), &error); _offset = 0; diff --git a/ruby/audio/wasapi.cpp b/ruby/audio/wasapi.cpp index feb0f781..d62b58f5 100644 --- a/ruby/audio/wasapi.cpp +++ b/ruby/audio/wasapi.cpp @@ -205,16 +205,16 @@ private: } if(_mode == 1 && _precision == 16) { - auto output = (int16_t*)buffer; - for(uint n : range(_channels)) *output++ = int16_t(samples[n] * 32768.0); + auto output = (uint16_t*)buffer; + for(uint n : range(_channels)) *output++ = (uint16_t)sclamp<16>(samples[n] * (32768.0 - 1.0)); buffer = (uint8_t*)output; } else if(_mode == 1 && _precision == 32) { - auto output = (int32_t*)buffer; - for(uint n : range(_channels)) *output++ = int32_t(samples[n] * 65536.0 * 32768.0); + auto output = (uint32_t*)buffer; + for(uint n : range(_channels)) *output++ = (uint32_t)sclamp<32>(samples[n] * (65536.0 * 32768.0 - 1.0)); buffer = (uint8_t*)output; } else if(_mode == 3 && _precision == 32) { auto output = (float*)buffer; - for(uint n : range(_channels)) *output++ = float(samples[n]); + for(uint n : range(_channels)) *output++ = float(max(-1.0, min(+1.0, samples[n]))); buffer = (uint8_t*)output; } else { //output silence for unsupported sample formats diff --git a/ruby/audio/xaudio2.cpp b/ruby/audio/xaudio2.cpp index 7cf36bd6..c0ac6ac2 100644 --- a/ruby/audio/xaudio2.cpp +++ b/ruby/audio/xaudio2.cpp @@ -59,8 +59,9 @@ struct AudioXAudio2 : Audio, public IXAudio2VoiceCallback { } auto output(const double samples[]) -> void { - _buffer[_bufferIndex * _period + _bufferOffset++] = uint16_t(samples[0] * 32768.0) << 0 | uint16_t(samples[1] * 32768.0) << 16; - if(_bufferOffset < _period) return; + _buffer[_bufferIndex * _period + _bufferOffset] = (uint16_t)sclamp<16>(samples[0] * 32767.0) << 0; + _buffer[_bufferIndex * _period + _bufferOffset] |= (uint16_t)sclamp<16>(samples[1] * 32767.0) << 16; + if(++_bufferOffset < _period) return; _bufferOffset = 0; if(_bufferQueue == _bufferCount - 1) { diff --git a/ruby/input/joypad/directinput.cpp b/ruby/input/joypad/directinput.cpp index 375b1dd4..c5d72007 100644 --- a/ruby/input/joypad/directinput.cpp +++ b/ruby/input/joypad/directinput.cpp @@ -55,10 +55,10 @@ struct InputJoypadDirectInput { int16_t yaxis = 0; if(pov < 36000) { - if(pov >= 31500 || pov <= 4500) yaxis = -32768; + if(pov >= 31500 || pov <= 4500) yaxis = -32767; if(pov >= 4500 && pov <= 13500) xaxis = +32767; if(pov >= 13500 && pov <= 22500) yaxis = +32767; - if(pov >= 22500 && pov <= 31500) xaxis = -32768; + if(pov >= 22500 && pov <= 31500) xaxis = -32767; } assign(jp.hid, HID::Joypad::GroupID::Hat, n * 2 + 0, xaxis); diff --git a/ruby/input/joypad/sdl.cpp b/ruby/input/joypad/sdl.cpp index ed018344..a9c38848 100644 --- a/ruby/input/joypad/sdl.cpp +++ b/ruby/input/joypad/sdl.cpp @@ -30,8 +30,8 @@ struct InputJoypadSDL { for(signed n = 0; n < (signed)jp.hid->hats().size() - 1; n += 2) { uint8_t state = SDL_JoystickGetHat(jp.handle, n >> 1); - assign(jp.hid, HID::Joypad::GroupID::Hat, n + 0, state & SDL_HAT_LEFT ? -32768 : state & SDL_HAT_RIGHT ? +32767 : 0); - assign(jp.hid, HID::Joypad::GroupID::Hat, n + 1, state & SDL_HAT_UP ? -32768 : state & SDL_HAT_DOWN ? +32767 : 0); + assign(jp.hid, HID::Joypad::GroupID::Hat, n + 0, state & SDL_HAT_LEFT ? -32767 : state & SDL_HAT_RIGHT ? +32767 : 0); + assign(jp.hid, HID::Joypad::GroupID::Hat, n + 1, state & SDL_HAT_UP ? -32767 : state & SDL_HAT_DOWN ? +32767 : 0); } for(auto n : range(jp.hid->buttons())) {