diff --git a/AUTHORS.md b/AUTHORS.md index d2de85747..41313b8aa 100644 --- a/AUTHORS.md +++ b/AUTHORS.md @@ -184,6 +184,7 @@ Programmers sergoz ShadowRadiance Siimacore + Simon Meulenbeek (simonmb) sir_herrbatka smbas Sophie Kirschner (pineapplemachine) diff --git a/CHANGELOG.md b/CHANGELOG.md index f8cb55d36..69a467b8e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -134,6 +134,7 @@ Feature #5456: Basic collada animation support Feature #5457: Realistic diagonal movement Feature #5486: Fixes trainers to choose their training skills based on their base skill points + Feature #5511: Add in game option to toggle HRTF support in OpenMW Feature #5519: Code Patch tab in launcher Feature #5524: Resume failed script execution after reload Feature #5545: Option to allow stealing from an unconscious NPC during combat diff --git a/apps/launcher/CMakeLists.txt b/apps/launcher/CMakeLists.txt index 329d06a57..301823770 100644 --- a/apps/launcher/CMakeLists.txt +++ b/apps/launcher/CMakeLists.txt @@ -13,6 +13,7 @@ set(LAUNCHER utils/profilescombobox.cpp utils/textinputdialog.cpp utils/lineedit.cpp + utils/openalutil.cpp ${CMAKE_SOURCE_DIR}/files/windows/launcher.rc ) @@ -31,6 +32,7 @@ set(LAUNCHER_HEADER utils/profilescombobox.hpp utils/textinputdialog.hpp utils/lineedit.hpp + utils/openalutil.hpp ) # Headers that must be pre-processed @@ -47,6 +49,7 @@ set(LAUNCHER_HEADER_MOC utils/textinputdialog.hpp utils/profilescombobox.hpp utils/lineedit.hpp + utils/openalutil.hpp ) @@ -95,6 +98,7 @@ endif (WIN32) target_link_libraries(openmw-launcher ${SDL2_LIBRARY_ONLY} + ${OPENAL_LIBRARY} components ) diff --git a/apps/launcher/advancedpage.cpp b/apps/launcher/advancedpage.cpp index d82dd1be2..b7bf1a249 100644 --- a/apps/launcher/advancedpage.cpp +++ b/apps/launcher/advancedpage.cpp @@ -5,11 +5,14 @@ #include #include #include +#include #include #include #include +#include "utils/openalutil.hpp" + Launcher::AdvancedPage::AdvancedPage(Config::GameSettings &gameSettings, Settings::Manager &engineSettings, QWidget *parent) : QWidget(parent) @@ -19,7 +22,17 @@ Launcher::AdvancedPage::AdvancedPage(Config::GameSettings &gameSettings, setObjectName ("AdvancedPage"); setupUi(this); + for(const char * name : Launcher::enumerateOpenALDevices()) + { + audioDeviceSelectorComboBox->addItem(QString::fromUtf8(name), QString::fromUtf8(name)); + } + for(const char * name : Launcher::enumerateOpenALDevicesHrtf()) + { + hrtfProfileSelectorComboBox->addItem(QString::fromUtf8(name), QString::fromUtf8(name)); + } + loadSettings(); + mCellNameCompleter.setModel(&mCellNameCompleterModel); startDefaultCharacterAtField->setCompleter(&mCellNameCompleter); } @@ -126,6 +139,34 @@ bool Launcher::AdvancedPage::loadSettings() viewingDistanceComboBox->setValue(convertToCells(mEngineSettings.getInt("viewing distance", "Camera"))); } + // Audio + { + std::string selectedAudioDevice = mEngineSettings.getString("device", "Sound"); + if (selectedAudioDevice.empty() == false) + { + int audioDeviceIndex = audioDeviceSelectorComboBox->findData(QString::fromStdString(selectedAudioDevice)); + if (audioDeviceIndex != -1) + { + audioDeviceSelectorComboBox->setCurrentIndex(audioDeviceIndex); + } + } + int hrtfEnabledIndex = mEngineSettings.getInt("hrtf enable", "Sound"); + if (hrtfEnabledIndex >= -1 && hrtfEnabledIndex <= 1) + { + enableHRTFComboBox->setCurrentIndex(hrtfEnabledIndex + 1); + } + std::string selectedHRTFProfile = mEngineSettings.getString("hrtf", "Sound"); + if (selectedHRTFProfile.empty() == false) + { + int hrtfProfileIndex = hrtfProfileSelectorComboBox->findData(QString::fromStdString(selectedHRTFProfile)); + if (hrtfProfileIndex != -1) + { + hrtfProfileSelectorComboBox->setCurrentIndex(hrtfProfileIndex); + } + } + } + + // Camera { loadSettingBool(viewOverShoulderCheckBox, "view over shoulder", "Camera"); @@ -247,6 +288,33 @@ void Launcher::AdvancedPage::saveSettings() mEngineSettings.setInt("viewing distance", "Camera", convertToUnits(viewingDistance)); } } + + // Audio + { + int audioDeviceIndex = audioDeviceSelectorComboBox->currentIndex(); + if (audioDeviceIndex != 0) + { + mEngineSettings.setString("device", "Sound", audioDeviceSelectorComboBox->currentText().toUtf8().constData()); + } + else + { + mEngineSettings.setString("device", "Sound", ""); + } + int hrtfEnabledIndex = enableHRTFComboBox->currentIndex() - 1; + if (hrtfEnabledIndex != mEngineSettings.getInt("hrtf enable", "Sound")) + { + mEngineSettings.setInt("hrtf enable", "Sound", hrtfEnabledIndex); + } + int selectedHRTFProfileIndex = hrtfProfileSelectorComboBox->currentIndex(); + if (selectedHRTFProfileIndex != 0) + { + mEngineSettings.setString("hrtf", "Sound", hrtfProfileSelectorComboBox->currentText().toUtf8().constData()); + } + else + { + mEngineSettings.setString("hrtf", "Sound", ""); + } + } // Camera { diff --git a/apps/launcher/utils/openalutil.cpp b/apps/launcher/utils/openalutil.cpp new file mode 100644 index 000000000..6f76b7130 --- /dev/null +++ b/apps/launcher/utils/openalutil.cpp @@ -0,0 +1,55 @@ +#include +#include +#include + +#include + +#include "openalutil.hpp" + +#ifndef ALC_ALL_DEVICES_SPECIFIER +#define ALC_ALL_DEVICES_SPECIFIER 0x1013 +#endif + +std::vector Launcher::enumerateOpenALDevices() +{ + std::vector devlist; + const ALCchar *devnames; + + if(alcIsExtensionPresent(nullptr, "ALC_ENUMERATE_ALL_EXT")) + { + devnames = alcGetString(nullptr, ALC_ALL_DEVICES_SPECIFIER); + } + else + { + devnames = alcGetString(nullptr, ALC_DEVICE_SPECIFIER); + } + + while(devnames && *devnames) + { + devlist.emplace_back(devnames); + devnames += strlen(devnames)+1; + } + return devlist; +} + +std::vector Launcher::enumerateOpenALDevicesHrtf() +{ + std::vector ret; + + ALCdevice *device = alcOpenDevice(nullptr); + if(device && alcIsExtensionPresent(device, "ALC_SOFT_HRTF")) + { + LPALCGETSTRINGISOFT alcGetStringiSOFT = nullptr; + void* funcPtr = alcGetProcAddress(device, "alcGetStringiSOFT"); + memcpy(&alcGetStringiSOFT, &funcPtr, sizeof(funcPtr)); + ALCint num_hrtf; + alcGetIntegerv(device, ALC_NUM_HRTF_SPECIFIERS_SOFT, 1, &num_hrtf); + ret.reserve(num_hrtf); + for(ALCint i = 0;i < num_hrtf && i < 20;++i) + { + const ALCchar *entry = alcGetStringiSOFT(device, ALC_HRTF_SPECIFIER_SOFT, i); + ret.emplace_back(entry); + } + } + return ret; +} \ No newline at end of file diff --git a/apps/launcher/utils/openalutil.hpp b/apps/launcher/utils/openalutil.hpp new file mode 100644 index 000000000..4a84fbae7 --- /dev/null +++ b/apps/launcher/utils/openalutil.hpp @@ -0,0 +1,7 @@ +#include + +namespace Launcher +{ + std::vector enumerateOpenALDevices(); + std::vector enumerateOpenALDevicesHrtf(); +} \ No newline at end of file diff --git a/docs/source/reference/modding/settings/sound.rst b/docs/source/reference/modding/settings/sound.rst index 895b919fb..4cc665582 100644 --- a/docs/source/reference/modding/settings/sound.rst +++ b/docs/source/reference/modding/settings/sound.rst @@ -5,7 +5,7 @@ device ------ :Type: string -:Range: +:Range: :Default: "" This setting determines which audio device to use. A blank or missing setting means to use the default device, @@ -13,7 +13,7 @@ which should usually be sufficient, but if you need to explicitly specify a devi The names of detected devices can be found in the openmw.log file in your configuration directory. -This setting can only be configured by editing the settings configuration file. +This setting can be configured by editing the settings configuration file, or in the Audio tab of the OpenMW Launcher. master volume ------------- @@ -111,13 +111,13 @@ Enabling HRTF may also require an OpenAL Soft version greater than 1.17.0, and possibly some operating system configuration. A value of 0 disables HRTF processing, while a value of 1 explicitly enables HRTF processing. The default value is -1, which should enable the feature automatically for most users when possible. -This setting can only be configured by editing the settings configuration file. +This setting can be configured by editing the settings configuration file, or in the Audio tab of the OpenMW Launcher. hrtf ---- :Type: string -:Range: +:Range: :Default: "" This setting specifies which HRTF profile to use when HRTF is enabled. Blank means use the default. @@ -125,4 +125,4 @@ This setting has no effect if HRTF is not enabled based on the hrtf enable setti Allowed values for this field are enumerated in openmw.log file is an HRTF enabled audio system is installed. The default value is empty, which uses the default profile. -This setting can only be configured by editing the settings configuration file. +This setting can be configured by editing the settings configuration file, or in the Audio tab of the OpenMW Launcher. diff --git a/files/ui/advancedpage.ui b/files/ui/advancedpage.ui index a3601ce94..a990e9172 100644 --- a/files/ui/advancedpage.ui +++ b/files/ui/advancedpage.ui @@ -467,6 +467,150 @@ This setting makes the fog use the actual eye point distance (or so called Eucli + + + Audio + + + + + + + + Audio Device + + + Select your preferred audio device. + + + + + + + + 0 + 0 + + + + + 283 + 0 + + + + 0 + + + + Default + + + + + + + + + + + + HRTF + + + This setting controls HRTF, which simulates 3D sound on stereo systems. + + + + + + + + 0 + 0 + + + + + 283 + 0 + + + + 0 + + + + Automatic + + + + + Off + + + + + On + + + + + + + + + + + + HRTF Profile + + + Select your preferred HRTF profile. + + + + + + + + 0 + 0 + + + + + 283 + 0 + + + + 0 + + + + Default + + + + + + + + + + Qt::Vertical + + + + 0 + 0 + + + + + + Camera