From a86febd5f9226571baf5b25c3b3e25a085c9c10d Mon Sep 17 00:00:00 2001 From: Ian Karlsson Date: Fri, 9 Apr 2021 01:56:39 +0200 Subject: [PATCH] Implement audio driver/device selection --- src/audio_manager.cpp | 207 +++++++++++++++++++++++++++++------------- src/audio_manager.h | 21 +++-- src/main.cpp | 21 ++++- src/main_window.cpp | 44 +++++++++ 4 files changed, 221 insertions(+), 72 deletions(-) diff --git a/src/audio_manager.cpp b/src/audio_manager.cpp index ac2d36c..c0924d9 100644 --- a/src/audio_manager.cpp +++ b/src/audio_manager.cpp @@ -11,7 +11,7 @@ //! First time initialization Audio_Manager::Audio_Manager() - : audio_enabled(0) + : driver_sig(-1) , driver_id(-1) , device_id(-1) , sample_rate(44100) @@ -22,32 +22,21 @@ Audio_Manager::Audio_Manager() , window_handle(nullptr) , driver_handle(nullptr) , waiting_for_handle(false) + , audio_initialized(false) , driver_opened(false) , device_opened(false) - , driver_names() - , device_names() + , driver_list() + , device_list() { if (Audio_Init()) { fprintf(stderr, "Warning: audio initialization failed. Muting audio\n"); - set_audio_enabled(false); } else { - set_audio_enabled(true); + audio_initialized = true; + enumerate_drivers(); } - - enumerate_drivers(); - while(driver_id < (int)driver_names.size()) - { - if(!open_driver()) - break; - driver_id++; - } - if(driver_opened) - open_device(); - else - set_audio_enabled(false); } //! get the singleton instance of Audio_Manager @@ -57,16 +46,121 @@ Audio_Manager& Audio_Manager::get() return instance; } -//! Set global volume -void Audio_Manager::set_audio_enabled(bool flag) +void Audio_Manager::set_driver(int new_driver_sig, int new_device_id) +{ + std::string name = ""; + + if(!audio_initialized) + return; + + // Reset device to default if we have already opened a device + device_id = new_device_id; + + close_driver(); + + // Find a driver ID + driver_sig = new_driver_sig; + driver_id = -1; + if(driver_sig != -1) + { + auto it = driver_list.find(driver_sig); + + if(it != driver_list.end()) + { + driver_id = it->second.first; + name = it->second.second; + open_driver(); + } + else + { + fprintf(stderr, "Warning: Driver id %02x unavailable, using default\n", driver_sig); + } + } + + if(driver_id == -1) + { + // pick the next one automatically if loading fails + for(auto && i : driver_list) + { + driver_sig = i.first; + driver_id = i.second.first; + name = i.second.second; + if(!open_driver()) + break; + } + } + + if(driver_opened) + { + fprintf(stdout, "Loaded audio driver: '%s'\n", name.c_str()); + enumerate_devices(); + set_device(device_id); + } + else + { + if(driver_id == -1) + fprintf(stderr, "Warning: No available drivers\n"); + else + fprintf(stderr, "Warning: Failed to open driver '%s'\n", name.c_str()); + } +} + +void Audio_Manager::set_device(int new_device_id) { - audio_enabled = flag; + std::string name = ""; + + if(!driver_opened) + return; + + close_device(); + + // Find a driver ID + device_id = -1; + if(new_device_id != -1) + { + auto it = device_list.find(new_device_id); + + if(it != device_list.end()) + { + device_id = it->first; + name = it->second; + open_device(); + } + else + { + fprintf(stderr, "Warning: Device id %02x unavailable, using default\n", new_device_id); + } + } + + if(device_id == -1) + { + // pick the next one automatically if loading fails + for(auto && i : device_list) + { + device_id = i.first; + name = i.second; + if(!open_device()) + break; + } + } + + if(!device_opened) + { + if(device_id == -1) + fprintf(stderr, "Warning: No available devices\n"); + else + fprintf(stderr, "Warning: Failed to open device '%s'\n", name.c_str()); + } + else + { + fprintf(stdout, "Loaded audio device: '%s'\n", name.c_str()); + } } //! Get global volume bool Audio_Manager::get_audio_enabled() const { - return audio_enabled; + return audio_initialized && driver_opened && device_opened; } //! Set window handle (for APIs where this is required) @@ -75,11 +169,8 @@ void Audio_Manager::set_window_handle(void* new_handle) window_handle = new_handle; if(waiting_for_handle) { - waiting_for_handle = false; - if(open_driver()) - set_audio_enabled(false); - else - set_audio_enabled(true); + if(!open_driver()) + waiting_for_handle = false; } if(driver_opened && !device_opened) { @@ -130,7 +221,7 @@ int Audio_Manager::add_stream(std::shared_ptr stream) void Audio_Manager::clean_up() { close_driver(); - if(audio_enabled) + if(audio_initialized) Audio_Deinit(); } @@ -138,69 +229,57 @@ void Audio_Manager::clean_up() void Audio_Manager::enumerate_drivers() { + driver_list.clear(); + unsigned int driver_count = Audio_GetDriverCount(); if(!driver_count) { fprintf(stderr, "Warning: no audio drivers available. Muting audio\n"); - set_audio_enabled(0); - return; } - - printf("Available audio drivers ...\n"); - for(unsigned int i = 0; i < driver_count; i++) + else { - AUDDRV_INFO* info; - Audio_GetDriverInfo(i, &info); - - if(info->drvType) + printf("Available audio drivers ...\n"); + for(unsigned int i = 0; i < driver_count; i++) { - printf("%d = '%s' (type = %02x, sig = %02x)\n", i, info->drvName, info->drvType, info->drvSig); - driver_names[i] = info->drvName; - } + AUDDRV_INFO* info; + Audio_GetDriverInfo(i, &info); - // Select the first available non-logging driver. - if(info->drvType == ADRVTYPE_OUT && driver_id == -1) - { - driver_id = i; + if(info->drvType == ADRVTYPE_OUT) + { + printf("%d = '%s' (type = %02x, sig = %02x)\n", i, info->drvName, info->drvType, info->drvSig); + driver_list[info->drvSig] = std::make_pair(i, info->drvName); + } } } - printf("default = %d\n", driver_id); } void Audio_Manager::enumerate_devices() { - if(driver_opened) + if(!driver_opened) return; - const AUDIO_DEV_LIST* list; - list = AudioDrv_GetDeviceList(driver_handle); + device_list.clear(); + const AUDIO_DEV_LIST* list = AudioDrv_GetDeviceList(driver_handle); if(!list->devCount) { fprintf(stderr, "Warning: no audio devices available. Muting audio\n"); - set_audio_enabled(0); - return; } - - printf("Available audio devices ...\n"); - for(unsigned int i = 0; i < list->devCount; i++) + else { - AUDDRV_INFO* info; - Audio_GetDriverInfo(i, &info); - - if(info->drvType) + printf("Available audio devices ...\n"); + for(unsigned int i = 0; i < list->devCount; i++) { - printf("%d = '%s'\n", i, list->devNames[i]); - device_names[i] = list->devNames[i]; - } + AUDDRV_INFO* info; + Audio_GetDriverInfo(i, &info); - // Select the first available device - if(device_id == -1) - { - device_id = i; + if(info->drvType) + { + printf("%d = '%s'\n", i, list->devNames[i]); + device_list[i] = list->devNames[i]; + } } } - printf("default = %d\n", device_id); } //! Open a driver diff --git a/src/audio_manager.h b/src/audio_manager.h index 8756d38..4c51e92 100644 --- a/src/audio_manager.h +++ b/src/audio_manager.h @@ -78,7 +78,6 @@ class Audio_Manager static Audio_Manager& get(); - void set_audio_enabled(bool status); bool get_audio_enabled() const; void set_window_handle(void* new_handle); @@ -87,8 +86,17 @@ class Audio_Manager void set_volume(float new_volume); float get_volume() const; + void set_driver(int new_driver_sig, int new_device_id = -1); + inline int get_driver() const { return driver_sig; }; + + void set_device(int new_device_id); + inline int get_device() const { return device_id; }; + int add_stream(std::shared_ptr stream); + const std::map>& get_driver_list() const { return driver_list; } + const std::map& get_device_list() const { return device_list; } + void clean_up(); private: @@ -107,10 +115,10 @@ class Audio_Manager static uint32_t callback(void* drv_struct, void* user_param, uint32_t buf_size, void* data); - bool audio_enabled; + int driver_sig; // Actual driver signature, -1 if not loaded + int driver_id; // Actual driver id, -1 if not loaded + int device_id; // Actual device id, -1 if not loaded - int driver_id; - int device_id; uint32_t sample_rate; uint32_t sample_size; @@ -122,11 +130,12 @@ class Audio_Manager void* driver_handle; bool waiting_for_handle; + bool audio_initialized; bool driver_opened; bool device_opened; - std::map driver_names; - std::map device_names; + std::map> driver_list; + std::map device_list; std::mutex mutex; }; diff --git a/src/main.cpp b/src/main.cpp index 89b2f0b..d0b0f9b 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -11,6 +11,7 @@ #include #include #include +#include #include // TODO : https://www.gnu.org/software/libc/manual/html_node/Backtraces.html @@ -60,7 +61,6 @@ using namespace gl; // Our state Main_Window main_window; - static void sig_handler(int signal) { // dump current editor state @@ -102,8 +102,24 @@ static void restyle_with_scale(float scale) ImGui::GetIO().Fonts->AddFontDefault(&font_config); } -int main(int, char**) +int main(int argc, char* argv[]) { + int driver_id = -1; + int device_id = -1; + int carg = 1; + while(carg < argc) + { + if(!std::strcmp(argv[carg], "--driver-id") && (argc > carg)) + { + driver_id = strtol(argv[++carg], NULL, 0); + } + if(!std::strcmp(argv[carg], "--device-id") && (argc > carg)) + { + device_id = strtol(argv[++carg], NULL, 0); + } + carg++; + } + // Setup window glfwSetErrorCallback(glfw_error_callback); if (!glfwInit()) @@ -141,6 +157,7 @@ int main(int, char**) // libvgm DSound support may require the window handle Audio_Manager::get().set_window_handle(glfwGetWin32Window(window)); #endif + Audio_Manager::get().set_driver(driver_id, device_id); // Initialize OpenGL loader #if defined(IMGUI_IMPL_OPENGL_LOADER_GL3W) diff --git a/src/main_window.cpp b/src/main_window.cpp index b850c45..57c3d8a 100644 --- a/src/main_window.cpp +++ b/src/main_window.cpp @@ -2,6 +2,7 @@ #include "main_window.h" #include "editor_window.h" #include "config_window.h" +#include "audio_manager.h" #include #include @@ -35,6 +36,7 @@ static bool debug_imgui_demo_windows = false; static bool debug_imgui_metrics = false; #endif static bool debug_state_window = false; +static bool debug_audio_window = false; static void debug_menu() { @@ -44,6 +46,7 @@ static void debug_menu() #ifndef IMGUI_DISABLE_DEMO_WINDOWS ImGui::MenuItem("ImGui demo windows", NULL, &debug_imgui_demo_windows); #endif + ImGui::MenuItem("Select audio device", NULL, &debug_audio_window); ImGui::MenuItem("Display dump state", NULL, &debug_state_window); if (ImGui::MenuItem("Quit")) { @@ -84,6 +87,47 @@ static void debug_window() ImGui::EndChild(); ImGui::End(); } + if(debug_audio_window) + { + ImGui::Begin("Select Audio Device", &debug_audio_window, ImGuiWindowFlags_NoResize | ImGuiWindowFlags_AlwaysAutoResize); + auto& am = Audio_Manager::get(); + auto driver_list = am.get_driver_list(); + auto driver = am.get_driver(); + if (ImGui::ListBoxHeader("Audio driver", 5)) + { + if(ImGui::Selectable("Default", driver == -1)) + { + am.set_driver(-1, -1); + } + for(auto && i : driver_list) + { + if(ImGui::Selectable(i.second.second.c_str(), driver == i.first)) + { + am.set_driver(i.first, -1); + } + } + ImGui::ListBoxFooter(); + } + + auto device_list = am.get_device_list(); + auto device = am.get_device(); + if (ImGui::ListBoxHeader("Audio device", 5)) + { + if(ImGui::Selectable("Default", device == -1)) + { + am.set_device(-1); + } + for(auto && i : device_list) + { + if(ImGui::Selectable(i.second.c_str(), device == i.first)) + { + am.set_device(i.first); + } + } + ImGui::ListBoxFooter(); + } + ImGui::End(); + } } //=====================================================================