From 17697317d4a372aa2cf40dfe957eefe49a3335cf Mon Sep 17 00:00:00 2001 From: Tim Allen Date: Sat, 15 Jul 2017 22:00:20 +1000 Subject: [PATCH] Update to v103r14 release. byuu says: Changelog: - tomoko: by popular choice, default to adaptive mode on new installs - hiro/windows: fix bug that was preventing the escape key from closing some dialog windows - nall/registry: use "\\\\" as separator instead of "/" ... because some registry keys contain "/" in them >_> - ruby: add ASIO driver stub (so far it can only initialize and grab the driver name/version information) --- higan/emulator/emulator.hpp | 2 +- higan/target-tomoko/GNUmakefile | 2 +- .../configuration/configuration.cpp | 2 +- hiro/windows/application.cpp | 7 +- hiro/windows/browser-window.cpp | 4 +- nall/windows/registry.hpp | 12 +- ruby/audio/asio.cpp | 90 +++++++++ ruby/audio/asio.hpp | 171 ++++++++++++++++++ ruby/audio/directsound.cpp | 4 +- ruby/ruby.cpp | 20 +- 10 files changed, 296 insertions(+), 18 deletions(-) create mode 100644 ruby/audio/asio.cpp create mode 100644 ruby/audio/asio.hpp diff --git a/higan/emulator/emulator.hpp b/higan/emulator/emulator.hpp index cb23ec68..56dd3d06 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 = "103.13"; + static const string Version = "103.14"; static const string Author = "byuu"; static const string License = "GPLv3"; static const string Website = "http://byuu.org/"; diff --git a/higan/target-tomoko/GNUmakefile b/higan/target-tomoko/GNUmakefile index 59fd2940..2b0c2d1e 100644 --- a/higan/target-tomoko/GNUmakefile +++ b/higan/target-tomoko/GNUmakefile @@ -19,7 +19,7 @@ ui_objects += $(if $(call streq,$(platform),windows),ui-resource) # platform ifeq ($(platform),windows) ruby += video.direct3d video.wgl video.directdraw video.gdi - ruby += audio.wasapi audio.xaudio2 audio.directsound + ruby += audio.asio audio.wasapi audio.xaudio2 audio.directsound ruby += input.windows else ifeq ($(platform),macosx) ruby += video.cgl diff --git a/higan/target-tomoko/configuration/configuration.cpp b/higan/target-tomoko/configuration/configuration.cpp index f31eadd8..3b93d83a 100644 --- a/higan/target-tomoko/configuration/configuration.cpp +++ b/higan/target-tomoko/configuration/configuration.cpp @@ -30,7 +30,7 @@ Settings::Settings() { set("Video/Windowed/AspectCorrection", true); set("Video/Windowed/IntegralScaling", true); - set("Video/Windowed/Adaptive", false); + set("Video/Windowed/Adaptive", true); set("Video/Windowed/Scale", "Small"); set("Video/Windowed/Scale/Small", "640x480"); set("Video/Windowed/Scale/Medium", "960x720"); diff --git a/hiro/windows/application.cpp b/hiro/windows/application.cpp index f4d12970..0af48b14 100644 --- a/hiro/windows/application.cpp +++ b/hiro/windows/application.cpp @@ -142,7 +142,10 @@ static auto Application_keyboardProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM auto object = (mObject*)GetWindowLongPtr(info.hwndFocus, GWLP_USERDATA); if(!object) return false; - if(auto window = dynamic_cast(object)) { + auto objectWindow = (mObject*)GetWindowLongPtr(GetAncestor(info.hwndFocus, GA_ROOT), GWLP_USERDATA); + if(!objectWindow) return false; + + if(auto window = dynamic_cast(objectWindow)) { if(auto self = window->self()) { if(!self->_modalityDisabled()) { if(auto code = pKeyboard::_translate(wparam, lparam)) { @@ -158,7 +161,6 @@ static auto Application_keyboardProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM } } } - return false; } if(auto window = object->parentWindow(true)) { @@ -218,7 +220,6 @@ static auto Application_keyboardProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM } } CloseClipboard(); - return false; } } #endif diff --git a/hiro/windows/browser-window.cpp b/hiro/windows/browser-window.cpp index 8b82f16d..22e29cf4 100644 --- a/hiro/windows/browser-window.cpp +++ b/hiro/windows/browser-window.cpp @@ -41,8 +41,8 @@ static auto BrowserWindow_fileDialog(bool save, BrowserWindow::State& state) -> if(path) { //clear COMDLG32 MRU (most recently used) file list //this is required in order for lpstrInitialDir to be honored in Windows 7 and above - registry::remove("HKCU/Software/Microsoft/Windows/CurrentVersion/Explorer/ComDlg32/LastVisitedPidlMRU/"); - registry::remove("HKCU/Software/Microsoft/Windows/CurrentVersion/Explorer/ComDlg32/OpenSavePidlMRU/"); + registry::remove("HKCU\\Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\ComDlg32\\LastVisitedPidlMRU\\"); + registry::remove("HKCU\\Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\ComDlg32\\OpenSavePidlMRU\\"); } OPENFILENAME ofn; diff --git a/nall/windows/registry.hpp b/nall/windows/registry.hpp index d58ff186..aad2a32c 100644 --- a/nall/windows/registry.hpp +++ b/nall/windows/registry.hpp @@ -24,7 +24,7 @@ namespace nall { struct registry { static auto exists(const string& name) -> bool { - auto part = name.split("/"); + auto part = name.split("\\"); HKEY handle, rootKey = root(part.takeLeft()); string node = part.takeRight(); string path = part.merge("\\"); @@ -39,7 +39,7 @@ struct registry { } static auto read(const string& name) -> string { - auto part = name.split("/"); + auto part = name.split("\\"); HKEY handle, rootKey = root(part.takeLeft()); string node = part.takeRight(); string path = part.merge("\\"); @@ -54,7 +54,7 @@ struct registry { } static auto write(const string& name, const string& data = "") -> void { - auto part = name.split("/"); + auto part = name.split("\\"); HKEY handle, rootKey = root(part.takeLeft()); string node = part.takeRight(), path; DWORD disposition; @@ -71,7 +71,7 @@ struct registry { } static auto remove(const string& name) -> bool { - auto part = name.split("/"); + auto part = name.split("\\"); HKEY rootKey = root(part.takeLeft()); string node = part.takeRight(); string path = part.merge("\\"); @@ -81,7 +81,7 @@ struct registry { static auto contents(const string& name) -> string_vector { string_vector result; - auto part = name.split("/"); + auto part = name.split("\\"); HKEY handle, rootKey = root(part.takeLeft()); part.removeRight(); string path = part.merge("\\"); @@ -92,7 +92,7 @@ struct registry { wchar_t name[NWR_SIZE] = L""; DWORD size = NWR_SIZE * sizeof(wchar_t); RegEnumKeyEx(handle, n, (wchar_t*)&name, &size, nullptr, nullptr, nullptr, nullptr); - result.append(string{(const char*)utf8_t(name), "/"}); + result.append(string{(const char*)utf8_t(name), "\\"}); } for(uint n = 0; n < nodes; n++) { wchar_t name[NWR_SIZE] = L""; diff --git a/ruby/audio/asio.cpp b/ruby/audio/asio.cpp new file mode 100644 index 00000000..aa456dcf --- /dev/null +++ b/ruby/audio/asio.cpp @@ -0,0 +1,90 @@ +#include "asio.hpp" + +struct AudioASIO : Audio { + ~AudioASIO() { term(); } + + struct Settings { + HWND handle = nullptr; + bool synchronize = true; + uint frequency = 48000; + } settings; + + struct Driver { + string name; + string classID; + }; + vector drivers; + + Driver driver; + IASIO* device = nullptr; + + auto cap(const string& name) -> bool { + if(name == Audio::Handle) return true; + if(name == Audio::Synchronize) return true; + if(name == Audio::Frequency) return true; + return false; + } + + auto get(const string& name) -> any { + if(name == Audio::Handle) return (uintptr)settings.handle; + if(name == Audio::Synchronize) return settings.synchronize; + if(name == Audio::Frequency) return settings.frequency; + return {}; + } + + auto set(const string& name, const any& value) -> bool { + if(name == Audio::Handle && value.is()) { + settings.handle = (HWND)value.get(); + return true; + } + + if(name == Audio::Synchronize && value.is()) { + settings.synchronize = value.get(); + return true; + } + + if(name == Audio::Frequency && value.is()) { + settings.frequency = value.get(); + return true; + } + + return false; + } + + auto sample(int16_t left, int16_t right) -> void { + } + + auto clear() -> void { + } + + auto init() -> bool { + //enumerate available ASIO drivers from the registry + for(auto candidate : registry::contents("HKLM\\SOFTWARE\\ASIO\\")) { + if(auto classID = registry::read({"HKLM\\SOFTWARE\\ASIO\\", candidate, "CLSID"})) { + drivers.append({candidate.trimRight("\\", 1L), classID}); + } + } + if(!drivers) return false; + + //default to first driver for now + driver = drivers[0]; + + CLSID classID; + if(CLSIDFromString((LPOLESTR)utf16_t(driver.classID), (LPCLSID)&classID) != S_OK) return false; + if(CoCreateInstance(classID, 0, CLSCTX_INPROC_SERVER, classID, (void**)&device) != S_OK) return false; + + if(!device->init((void*)settings.handle)) return false; + + //temporary debugging information + char driverName[4096] = {0}; + device->getDriverName(driverName); + print("Driver: ", driverName, "\n"); + print("Version: ", device->getDriverVersion(), "\n"); + print("---\n"); + + return true; + } + + auto term() -> void { + } +}; diff --git a/ruby/audio/asio.hpp b/ruby/audio/asio.hpp new file mode 100644 index 00000000..dfda9c3f --- /dev/null +++ b/ruby/audio/asio.hpp @@ -0,0 +1,171 @@ +using ASIOError = long; +enum : long { + ASE_OK = 0, + ASE_SUCCESS = 0x3f4847a0, + ASE_NotPresent = -1000, + ASE_HWMalfunction, + ASE_InvalidParameter, + ASE_InvalidMode, + ASE_SPNotAdvancing, + ASE_NoClock, + ASE_NoMemory, +}; + +using ASIOBool = long; +enum : long { + ASIOFalse = 0, + ASIOTrue = 1, +}; + +using ASIOSampleRate = double; +using ASIOSamples = long long int; +using ASIOTimeStamp = long long int; + +using ASIOSampleType = long; +enum : long { + ASIOSTInt16MSB = 0, + ASIOSTInt24MSB = 1, + ASIOSTInt32MSB = 2, + ASIOSTFloat32MSB = 3, + ASIOSTFloat64MSB = 4, + + ASIOSTInt32MSB16 = 8, + ASIOSTInt32MSB18 = 9, + ASIOSTInt32MSB20 = 10, + ASIOSTInt32MSB24 = 11, + + ASIOSTInt16LSB = 16, + ASIOSTInt24LSB = 17, + ASIOSTInt32LSB = 18, + ASIOSTFloat32LSB = 19, + ASIOSTFloat64LSB = 20, + + ASIOSTInt32LSB16 = 24, + ASIOSTInt32LSB18 = 25, + ASIOSTInt32LSB20 = 26, + ASIOSTInt32LSB24 = 27, + + ASIOSTDSDInt8LSB1 = 32, + ASIOSTDSDInt8MSB1 = 33, + ASIOSTDSDInt8NER8 = 40, + + ASIOSTLastEntry, +}; + +struct ASIODriverInfo { + long asioVersion; + long driverVersion; + char name[32]; + char errorMessage[124]; + void* sysRef; +}; + +struct ASIOBufferInfo { + ASIOBool isInput; + long channelNum; + void* buffers[2]; +}; + +struct ASIOChannelInfo { + long channel; + ASIOBool isInput; + ASIOBool isActive; + long channelGroup; + ASIOSampleType type; + char name[32]; +}; + +struct ASIOClockSource { + long index; + long associatedChannel; + long associatedGroup; + ASIOBool isCurrentSource; + char name[32]; +}; + +struct ASIOTimeInfo { + double speed; + ASIOTimeStamp systemTime; + ASIOSamples samplePosition; + ASIOSampleRate sampleRate; + unsigned long flags; + char reserved[12]; +}; +enum : unsigned long { + kSystemTimeValid = 1 << 0, + kSamplePositionValid = 1 << 1, + kSampleRateValid = 1 << 2, + kSpeedValid = 1 << 3, + kSampleRateChanged = 1 << 4, + kClockSourceChanged = 1 << 5, +}; + +struct ASIOTimeCode { + double speed; + ASIOSamples timeCodeSamples; + unsigned long flags; + char future[64]; +}; +enum : unsigned long { + kTcValid = 1 << 0, + kTcRunning = 1 << 1, + kTcReverse = 1 << 2, + kTcOnspeed = 1 << 3, + kTcStill = 1 << 4, + kTcSpeedValid = 1 << 8, +}; + +struct ASIOTime { + long reserved[4]; + ASIOTimeInfo timeInfo; + ASIOTimeCode timeCode; +}; + +struct ASIOCallbacks { + auto (*bufferSwitch)(long doubleBufferIndex, ASIOBool directProcess) -> void; + auto (*sampleRateDidChange)(ASIOSampleRate sRate) -> void; + auto (*asioMessage)(long selector, long value, void* message, double* opt) -> long; + auto (*bufferSwitchTimeInfo)(ASIOTime* params, long doubleBufferIndex, ASIOBool directProcess) -> ASIOTime*; +}; +enum : long { + kAsioSelectorSupported = 1, + kAsioEngineVersion, + kAsioResetRequest, + kAsioBufferSizeChange, + kAsioResyncRequest, + kAsioLatenciesChanged, + kAsioSupportsTimeInfo, + kAsioSupportsTimeCode, + kAsioMMCCommand, + kAsioSupportsInputMonitor, + kAsioSupportsInputGain, + kAsioSupportsInputMeter, + kAsioSupportsOutputGain, + kAsioSupportsOutputMeter, + kAsioOverload, + kAsioNumMessageSelectors, +}; + +struct IASIO : public IUnknown { + virtual auto init(void* sysHandle) -> ASIOBool; + virtual auto getDriverName(char* name) -> void; + virtual auto getDriverVersion() -> long; + virtual auto getErrorMessage(char* error) -> void; + virtual auto start() -> ASIOError; + virtual auto stop() -> ASIOError; + virtual auto getChannels(long* numInputChannels, long* numOutputChannels) -> ASIOError = 0; + virtual auto getLatencies(long* inputLatency, long* outputLatency) -> ASIOError = 0; + virtual auto getBufferSize(long* minSize, long* maxSize, long* preferredSize, long* granularity) -> ASIOError = 0; + virtual auto canSampleRate(ASIOSampleRate sampleRate) -> ASIOError = 0; + virtual auto getSampleRate(ASIOSampleRate* sampleRate) -> ASIOError = 0; + virtual auto setSampleRate(ASIOSampleRate sampleRate) -> ASIOError = 0; + virtual auto getClockSources(ASIOClockSource* clocks, long* numSources) -> ASIOError = 0; + virtual auto setClockSource(long reference) -> ASIOError = 0; + virtual auto getSamplePosition(ASIOSamples* sPos, ASIOTimeStamp* tStamp) -> ASIOError = 0; + virtual auto getChannelInfo(ASIOChannelInfo* info) -> ASIOError = 0; + virtual auto createBuffers(ASIOBufferInfo* bufferInfos, long numChannels, long bufferSize, ASIOCallbacks* callbacks) -> ASIOError = 0; + virtual auto disposeBuffers() -> ASIOError = 0; + virtual auto controlPanel() -> ASIOError = 0; + virtual auto future(long selector, void* opt) -> ASIOError = 0; + virtual auto outputReady() -> ASIOError = 0; +}; diff --git a/ruby/audio/directsound.cpp b/ruby/audio/directsound.cpp index 6f119607..ba96b119 100644 --- a/ruby/audio/directsound.cpp +++ b/ruby/audio/directsound.cpp @@ -1,7 +1,7 @@ #include -struct AudioDS : Audio { - ~AudioDS() { term(); } +struct AudioDirectSound : Audio { + ~AudioDirectSound() { term(); } LPDIRECTSOUND ds = nullptr; LPDIRECTSOUNDBUFFER dsb_p = nullptr; diff --git a/ruby/ruby.cpp b/ruby/ruby.cpp index 9cfd3d4b..ddb9e81a 100644 --- a/ruby/ruby.cpp +++ b/ruby/ruby.cpp @@ -238,6 +238,10 @@ auto Video::availableDrivers() -> string_vector { #include #endif +#if defined(AUDIO_ASIO) + #include +#endif + #if defined(AUDIO_DIRECTSOUND) #include #endif @@ -286,8 +290,12 @@ auto Audio::create(const string& driver) -> Audio* { if(driver == "libao") return new AudioAO; #endif + #if defined(AUDIO_ASIO) + if(driver == "ASIO") return new AudioASIO; + #endif + #if defined(AUDIO_DIRECTSOUND) - if(driver == "DirectSound") return new AudioDS; + if(driver == "DirectSound") return new AudioDirectSound; #endif #if defined(AUDIO_OPENAL) @@ -318,7 +326,9 @@ auto Audio::create(const string& driver) -> Audio* { } auto Audio::optimalDriver() -> string { - #if defined(AUDIO_WASAPI) + #if defined(AUDIO_ASIO) + return "ASIO"; + #elif defined(AUDIO_WASAPI) return "WASAPI"; #elif defined(AUDIO_XAUDIO2) return "XAudio2"; @@ -360,6 +370,8 @@ auto Audio::safestDriver() -> string { return "PulseAudioSimple"; #elif defined(AUDIO_AO) return "libao"; + #elif defined(AUDIO_ASIO) + return "ASIO"; #else return "None"; #endif @@ -368,6 +380,10 @@ auto Audio::safestDriver() -> string { auto Audio::availableDrivers() -> string_vector { return { + #if defined(AUDIO_ASIO) + "ASIO", + #endif + #if defined(AUDIO_WASAPI) "WASAPI", #endif