From 178e60258922b670a96559ed26042d75918f40d8 Mon Sep 17 00:00:00 2001 From: Steveice10 <1269164+Steveice10@users.noreply.github.com> Date: Fri, 22 Dec 2023 11:38:06 -0800 Subject: [PATCH] misc: Improve defaults for macOS and handling of missing audio backends. (#7273) * misc: Improve backend defaults for macOS. * audio_core: Improve handling of missing audio backends. --- CMakeLists.txt | 3 +- src/CMakeLists.txt | 4 ++ src/audio_core/dsp_interface.cpp | 2 +- src/audio_core/input_details.cpp | 43 +++---------- src/audio_core/input_details.h | 27 ++++++--- src/audio_core/sink_details.cpp | 35 ++--------- src/audio_core/sink_details.h | 25 +++++--- src/citra_qt/CMakeLists.txt | 4 -- .../configuration/configure_audio.cpp | 60 +++++++++++++------ src/common/settings.h | 9 ++- src/core/hle/service/mic/mic_u.cpp | 4 +- 11 files changed, 109 insertions(+), 107 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 1344d06e5..d2bf7ae8a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -74,7 +74,8 @@ CMAKE_DEPENDENT_OPTION(ENABLE_DEDICATED_ROOM "Enable generating dedicated room e option(ENABLE_WEB_SERVICE "Enable web services (telemetry, etc.)" ON) option(ENABLE_SCRIPTING "Enable RPC server for scripting" ON) -CMAKE_DEPENDENT_OPTION(ENABLE_CUBEB "Enables the cubeb audio backend" ON "NOT IOS" OFF) +# TODO: cubeb currently causes issues on macOS, see: https://github.com/mozilla/cubeb/issues/771 +CMAKE_DEPENDENT_OPTION(ENABLE_CUBEB "Enables the cubeb audio backend" ON "NOT APPLE" OFF) option(ENABLE_OPENAL "Enables the OpenAL audio backend" ON) CMAKE_DEPENDENT_OPTION(ENABLE_LIBUSB "Enable libusb for GameCube Adapter support" ON "NOT IOS" OFF) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index e67b76828..37030b88a 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -155,6 +155,10 @@ else() endif() endif() +if (NOT APPLE) + add_compile_definitions(HAS_OPENGL) +endif() + add_subdirectory(common) add_subdirectory(core) add_subdirectory(video_core) diff --git a/src/audio_core/dsp_interface.cpp b/src/audio_core/dsp_interface.cpp index d633a89a1..5f85b67e4 100644 --- a/src/audio_core/dsp_interface.cpp +++ b/src/audio_core/dsp_interface.cpp @@ -21,7 +21,7 @@ void DspInterface::SetSink(AudioCore::SinkType sink_type, std::string_view audio // Dispose of the current sink first to avoid contention. sink.reset(); - sink = CreateSinkFromID(sink_type, audio_device); + sink = AudioCore::GetSinkDetails(sink_type).create_sink(audio_device); sink->SetCallback( [this](s16* buffer, std::size_t num_frames) { OutputCallback(buffer, num_frames); }); time_stretcher.SetOutputSampleRate(sink->GetNativeSampleRate()); diff --git a/src/audio_core/input_details.cpp b/src/audio_core/input_details.cpp index b3367b7cc..d6b2dc5c9 100644 --- a/src/audio_core/input_details.cpp +++ b/src/audio_core/input_details.cpp @@ -20,24 +20,10 @@ namespace AudioCore { namespace { -struct InputDetails { - using FactoryFn = std::unique_ptr (*)(std::string_view); - using ListDevicesFn = std::vector (*)(); - - /// Type of this input. - InputType type; - /// Name for this input. - std::string_view name; - /// A method to call to construct an instance of this type of input. - FactoryFn factory; - /// A method to call to list available devices. - ListDevicesFn list_devices; -}; - // input_details is ordered in terms of desirability, with the best choice at the top. constexpr std::array input_details = { #ifdef HAVE_CUBEB - InputDetails{InputType::Cubeb, "Real Device (Cubeb)", + InputDetails{InputType::Cubeb, "Real Device (Cubeb)", true, [](std::string_view device_id) -> std::unique_ptr { if (!Core::System::GetInstance().HasMicPermission()) { LOG_WARNING(Audio, @@ -49,7 +35,7 @@ constexpr std::array input_details = { &ListCubebInputDevices}, #endif #ifdef HAVE_OPENAL - InputDetails{InputType::OpenAL, "Real Device (OpenAL)", + InputDetails{InputType::OpenAL, "Real Device (OpenAL)", true, [](std::string_view device_id) -> std::unique_ptr { if (!Core::System::GetInstance().HasMicPermission()) { LOG_WARNING(Audio, @@ -60,17 +46,22 @@ constexpr std::array input_details = { }, &ListOpenALInputDevices}, #endif - InputDetails{InputType::Static, "Static Noise", + InputDetails{InputType::Static, "Static Noise", false, [](std::string_view device_id) -> std::unique_ptr { return std::make_unique(); }, [] { return std::vector{"Static Noise"}; }}, - InputDetails{InputType::Null, "None", + InputDetails{InputType::Null, "None", false, [](std::string_view device_id) -> std::unique_ptr { return std::make_unique(); }, [] { return std::vector{"None"}; }}, }; +} // Anonymous namespace + +std::vector ListInputs() { + return {input_details.begin(), input_details.end()}; +} const InputDetails& GetInputDetails(InputType input_type) { auto iter = std::find_if( @@ -88,21 +79,5 @@ const InputDetails& GetInputDetails(InputType input_type) { return *iter; } -} // Anonymous namespace - -std::string_view GetInputName(InputType input_type) { - if (input_type == InputType::Auto) { - return "Auto"; - } - return GetInputDetails(input_type).name; -} - -std::vector GetDeviceListForInput(InputType input_type) { - return GetInputDetails(input_type).list_devices(); -} - -std::unique_ptr CreateInputFromID(InputType input_type, std::string_view device_id) { - return GetInputDetails(input_type).factory(device_id); -} } // namespace AudioCore diff --git a/src/audio_core/input_details.h b/src/audio_core/input_details.h index e5b7b9820..f1be87572 100644 --- a/src/audio_core/input_details.h +++ b/src/audio_core/input_details.h @@ -20,17 +20,28 @@ enum class InputType : u32 { Static = 2, Cubeb = 3, OpenAL = 4, - - NumInputTypes, }; -/// Gets the name of a input type. -std::string_view GetInputName(InputType input_type); +struct InputDetails { + using FactoryFn = std::unique_ptr (*)(std::string_view device_id); + using ListDevicesFn = std::vector (*)(); -/// Gets the list of devices for a particular input identified by the given ID. -std::vector GetDeviceListForInput(InputType input_type); + /// Type of this input. + InputType type; + /// Name for this input. + std::string_view name; + /// Whether the input is backed by real devices. + bool real; + /// A method to call to construct an instance of this type of input. + FactoryFn create_input; + /// A method to call to list available devices. + ListDevicesFn list_devices; +}; -/// Creates an audio input identified by the given device ID. -std::unique_ptr CreateInputFromID(InputType input_type, std::string_view device_id); +/// Lists all available input types. +std::vector ListInputs(); + +/// Gets the details of an input type. +const InputDetails& GetInputDetails(InputType input_type); } // namespace AudioCore diff --git a/src/audio_core/sink_details.cpp b/src/audio_core/sink_details.cpp index 9d547d656..961e040b3 100644 --- a/src/audio_core/sink_details.cpp +++ b/src/audio_core/sink_details.cpp @@ -21,20 +21,6 @@ namespace AudioCore { namespace { -struct SinkDetails { - using FactoryFn = std::unique_ptr (*)(std::string_view); - using ListDevicesFn = std::vector (*)(); - - /// Type of this sink. - SinkType type; - /// Name for this sink. - std::string_view name; - /// A method to call to construct an instance of this type of sink. - FactoryFn factory; - /// A method to call to list available devices. - ListDevicesFn list_devices; -}; - // sink_details is ordered in terms of desirability, with the best choice at the top. constexpr std::array sink_details = { #ifdef HAVE_CUBEB @@ -64,6 +50,11 @@ constexpr std::array sink_details = { }, [] { return std::vector{"None"}; }}, }; +} // Anonymous namespace + +std::vector ListSinks() { + return {sink_details.begin(), sink_details.end()}; +} const SinkDetails& GetSinkDetails(SinkType sink_type) { auto iter = std::find_if( @@ -81,21 +72,5 @@ const SinkDetails& GetSinkDetails(SinkType sink_type) { return *iter; } -} // Anonymous namespace - -std::string_view GetSinkName(SinkType sink_type) { - if (sink_type == SinkType::Auto) { - return "Auto"; - } - return GetSinkDetails(sink_type).name; -} - -std::vector GetDeviceListForSink(SinkType sink_type) { - return GetSinkDetails(sink_type).list_devices(); -} - -std::unique_ptr CreateSinkFromID(SinkType sink_type, std::string_view device_id) { - return GetSinkDetails(sink_type).factory(device_id); -} } // namespace AudioCore diff --git a/src/audio_core/sink_details.h b/src/audio_core/sink_details.h index 4d0929a6b..2a0d202a7 100644 --- a/src/audio_core/sink_details.h +++ b/src/audio_core/sink_details.h @@ -20,17 +20,26 @@ enum class SinkType : u32 { Cubeb = 2, OpenAL = 3, SDL2 = 4, - - NumSinkTypes, }; -/// Gets the name of a sink type. -std::string_view GetSinkName(SinkType sink_type); +struct SinkDetails { + using FactoryFn = std::unique_ptr (*)(std::string_view); + using ListDevicesFn = std::vector (*)(); -/// Gets the list of devices for a particular sink identified by the given ID. -std::vector GetDeviceListForSink(SinkType sink_type); + /// Type of this sink. + SinkType type; + /// Name for this sink. + std::string_view name; + /// A method to call to construct an instance of this type of sink. + FactoryFn create_sink; + /// A method to call to list available devices. + ListDevicesFn list_devices; +}; -/// Creates an audio sink identified by the given device ID. -std::unique_ptr CreateSinkFromID(SinkType sink_type, std::string_view device_id); +/// Lists all available sink types. +std::vector ListSinks(); + +/// Gets the details of an sink type. +const SinkDetails& GetSinkDetails(SinkType input_type); } // namespace AudioCore diff --git a/src/citra_qt/CMakeLists.txt b/src/citra_qt/CMakeLists.txt index a783f121a..8edba366b 100644 --- a/src/citra_qt/CMakeLists.txt +++ b/src/citra_qt/CMakeLists.txt @@ -351,10 +351,6 @@ if(UNIX AND NOT APPLE) install(TARGETS citra-qt RUNTIME DESTINATION "${CMAKE_INSTALL_PREFIX}/bin") endif() -if (NOT APPLE) - target_compile_definitions(citra-qt PRIVATE HAS_OPENGL) -endif() - if (CITRA_USE_PRECOMPILED_HEADERS) target_precompile_headers(citra-qt PRIVATE precompiled_headers.h) endif() diff --git a/src/citra_qt/configuration/configure_audio.cpp b/src/citra_qt/configuration/configure_audio.cpp index f921c3a62..bf301d41e 100644 --- a/src/citra_qt/configuration/configure_audio.cpp +++ b/src/citra_qt/configuration/configure_audio.cpp @@ -21,9 +21,10 @@ ConfigureAudio::ConfigureAudio(bool is_powered_on, QWidget* parent) ui->setupUi(this); ui->output_type_combo_box->clear(); - for (u32 type = 0; type < static_cast(AudioCore::SinkType::NumSinkTypes); type++) { - ui->output_type_combo_box->addItem(QString::fromUtf8( - AudioCore::GetSinkName(static_cast(type)).data())); + ui->output_type_combo_box->addItem(tr("Auto"), QVariant::fromValue(AudioCore::SinkType::Auto)); + for (const auto& sink : AudioCore::ListSinks()) { + ui->output_type_combo_box->addItem(QString::fromUtf8(sink.name), + QVariant::fromValue(sink.type)); } ui->emulation_combo_box->setEnabled(!is_powered_on); @@ -32,9 +33,10 @@ ConfigureAudio::ConfigureAudio(bool is_powered_on, QWidget* parent) &ConfigureAudio::SetVolumeIndicatorText); ui->input_type_combo_box->clear(); - for (u32 type = 0; type < static_cast(AudioCore::InputType::NumInputTypes); type++) { - ui->input_type_combo_box->addItem(QString::fromUtf8( - AudioCore::GetInputName(static_cast(type)).data())); + ui->input_type_combo_box->addItem(tr("Auto"), QVariant::fromValue(AudioCore::InputType::Auto)); + for (const auto& input : AudioCore::ListInputs()) { + ui->input_type_combo_box->addItem(QString::fromUtf8(input.name), + QVariant::fromValue(input.type)); } ui->volume_label->setVisible(Settings::IsConfiguringGlobal()); @@ -89,8 +91,18 @@ void ConfigureAudio::SetConfiguration() { } void ConfigureAudio::SetOutputTypeFromSinkType() { - ui->output_type_combo_box->setCurrentIndex( - static_cast(Settings::values.output_type.GetValue())); + int new_index = -1; + + for (int index = 0; index < ui->output_type_combo_box->count(); index++) { + const auto sink_type = + static_cast(ui->output_type_combo_box->itemData(index).toUInt()); + if (Settings::values.output_type.GetValue() == sink_type) { + new_index = index; + break; + } + } + + ui->output_type_combo_box->setCurrentIndex(new_index); } void ConfigureAudio::SetOutputDeviceFromDeviceID() { @@ -108,8 +120,18 @@ void ConfigureAudio::SetOutputDeviceFromDeviceID() { } void ConfigureAudio::SetInputTypeFromInputType() { - ui->input_type_combo_box->setCurrentIndex( - static_cast(Settings::values.input_type.GetValue())); + int new_index = -1; + + for (int index = 0; index < ui->input_type_combo_box->count(); index++) { + const auto input_type = + static_cast(ui->input_type_combo_box->itemData(index).toUInt()); + if (Settings::values.input_type.GetValue() == input_type) { + new_index = index; + break; + } + } + + ui->input_type_combo_box->setCurrentIndex(new_index); } void ConfigureAudio::SetInputDeviceFromDeviceID() { @@ -142,30 +164,34 @@ void ConfigureAudio::ApplyConfiguration() { if (Settings::IsConfiguringGlobal()) { Settings::values.output_type = - static_cast(ui->output_type_combo_box->currentIndex()); + static_cast(ui->output_type_combo_box->currentData().toUInt()); Settings::values.output_device = ui->output_device_combo_box->currentText().toStdString(); Settings::values.input_type = - static_cast(ui->input_type_combo_box->currentIndex()); + static_cast(ui->input_type_combo_box->currentData().toUInt()); Settings::values.input_device = ui->input_device_combo_box->currentText().toStdString(); } } void ConfigureAudio::UpdateAudioOutputDevices(int sink_index) { - auto sink_type = static_cast(sink_index); + auto sink_type = + static_cast(ui->output_type_combo_box->itemData(sink_index).toUInt()); + auto& sink_details = AudioCore::GetSinkDetails(sink_type); ui->output_device_combo_box->clear(); ui->output_device_combo_box->addItem(QString::fromUtf8(AudioCore::auto_device_name)); - for (const auto& device : AudioCore::GetDeviceListForSink(sink_type)) { + for (const auto& device : sink_details.list_devices()) { ui->output_device_combo_box->addItem(QString::fromStdString(device)); } } void ConfigureAudio::UpdateAudioInputDevices(int input_index) { - auto input_type = static_cast(input_index); + auto input_type = + static_cast(ui->input_type_combo_box->itemData(input_index).toUInt()); + auto& input_details = AudioCore::GetInputDetails(input_type); #if defined(__APPLE__) - if (input_type != AudioCore::InputType::Null && input_type != AudioCore::InputType::Static) { + if (input_details.real) { AppleAuthorization::CheckAuthorizationForMicrophone(); } #endif @@ -173,7 +199,7 @@ void ConfigureAudio::UpdateAudioInputDevices(int input_index) { ui->input_device_combo_box->clear(); ui->input_device_combo_box->addItem(QString::fromUtf8(AudioCore::auto_device_name)); - for (const auto& device : AudioCore::GetDeviceListForInput(input_type)) { + for (const auto& device : input_details.list_devices()) { ui->input_device_combo_box->addItem(QString::fromStdString(device)); } } diff --git a/src/common/settings.h b/src/common/settings.h index ee3cb177f..4f308a7c3 100644 --- a/src/common/settings.h +++ b/src/common/settings.h @@ -441,8 +441,13 @@ struct Values { Setting allow_plugin_loader{true, "allow_plugin_loader"}; // Renderer - SwitchableSetting graphics_api{GraphicsAPI::OpenGL, GraphicsAPI::Software, - GraphicsAPI::Vulkan, "graphics_api"}; + SwitchableSetting graphics_api{ +#ifdef HAS_OPENGL + GraphicsAPI::OpenGL, +#else + GraphicsAPI::Vulkan, +#endif + GraphicsAPI::Software, GraphicsAPI::Vulkan, "graphics_api"}; SwitchableSetting physical_device{0, "physical_device"}; Setting use_gles{false, "use_gles"}; Setting renderer_debug{false, "renderer_debug"}; diff --git a/src/core/hle/service/mic/mic_u.cpp b/src/core/hle/service/mic/mic_u.cpp index 04c80521b..fab204fda 100644 --- a/src/core/hle/service/mic/mic_u.cpp +++ b/src/core/hle/service/mic/mic_u.cpp @@ -375,8 +375,8 @@ struct MIC_U::Impl { mic.reset(); } - mic = AudioCore::CreateInputFromID(Settings::values.input_type.GetValue(), - Settings::values.input_device.GetValue()); + mic = AudioCore::GetInputDetails(Settings::values.input_type.GetValue()) + .create_input(Settings::values.input_device.GetValue()); if (was_sampling) { StartSampling(); }