From 36a9cfbe8bb396d3e90e9ec87a0e31a5aacf8f71 Mon Sep 17 00:00:00 2001 From: Lorenzooone Date: Sun, 2 Feb 2025 09:59:51 +0100 Subject: [PATCH] Add ability to choose audio output device via a menu --- CMakeLists.txt | 2 +- include/Menus/AudioDeviceMenu.hpp | 31 ++++++++ include/Menus/AudioMenu.hpp | 2 +- include/audio_data.hpp | 13 ++++ include/display_structs.hpp | 2 +- include/frontend.hpp | 5 ++ .../Nisetro/cypress_nisetro_acquisition.cpp | 8 +++ source/Menus/AudioDeviceMenu.cpp | 71 +++++++++++++++++++ source/Menus/AudioMenu.cpp | 11 +-- source/WindowScreen_Menu.cpp | 71 ++++++++++++++----- source/audio_data.cpp | 35 +++++++++ source/cc3dsfs.cpp | 65 ++++++++++++++++- 12 files changed, 284 insertions(+), 32 deletions(-) create mode 100644 include/Menus/AudioDeviceMenu.hpp create mode 100644 source/Menus/AudioDeviceMenu.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 29e7b04..0f36e63 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -489,7 +489,7 @@ if(MSVC) else() set_source_files_properties(source/conversions.cpp PROPERTIES COMPILE_OPTIONS "$<$:-O3>") endif() -add_executable(${OUTPUT_NAME} source/cc3dsfs.cpp source/utils.cpp source/audio_data.cpp source/audio.cpp source/frontend.cpp source/TextRectangle.cpp source/WindowScreen.cpp source/WindowScreen_Menu.cpp source/devicecapture.cpp source/conversions.cpp source/ExtraButtons.cpp source/Menus/ConnectionMenu.cpp source/Menus/OptionSelectionMenu.cpp source/Menus/MainMenu.cpp source/Menus/VideoMenu.cpp source/Menus/CropMenu.cpp source/Menus/PARMenu.cpp source/Menus/RotationMenu.cpp source/Menus/OffsetMenu.cpp source/Menus/AudioMenu.cpp source/Menus/BFIMenu.cpp source/Menus/RelativePositionMenu.cpp source/Menus/ResolutionMenu.cpp source/Menus/FileConfigMenu.cpp source/Menus/ExtraSettingsMenu.cpp source/Menus/StatusMenu.cpp source/Menus/LicenseMenu.cpp source/WindowCommands.cpp source/Menus/ShortcutMenu.cpp source/Menus/ActionSelectionMenu.cpp source/Menus/ScalingRatioMenu.cpp source/Menus/ISNitroMenu.cpp source/Menus/VideoEffectsMenu.cpp source/CaptureDataBuffers.cpp source/Menus/InputMenu.cpp ${TOOLS_DATA_DIR}/font_ttf.cpp ${TOOLS_DATA_DIR}/shaders_list.cpp ${SOURCE_CPP_EXTRA_FILES}) +add_executable(${OUTPUT_NAME} source/cc3dsfs.cpp source/utils.cpp source/audio_data.cpp source/audio.cpp source/frontend.cpp source/TextRectangle.cpp source/WindowScreen.cpp source/WindowScreen_Menu.cpp source/devicecapture.cpp source/conversions.cpp source/ExtraButtons.cpp source/Menus/ConnectionMenu.cpp source/Menus/OptionSelectionMenu.cpp source/Menus/MainMenu.cpp source/Menus/VideoMenu.cpp source/Menus/CropMenu.cpp source/Menus/PARMenu.cpp source/Menus/RotationMenu.cpp source/Menus/OffsetMenu.cpp source/Menus/AudioMenu.cpp source/Menus/BFIMenu.cpp source/Menus/RelativePositionMenu.cpp source/Menus/ResolutionMenu.cpp source/Menus/FileConfigMenu.cpp source/Menus/ExtraSettingsMenu.cpp source/Menus/StatusMenu.cpp source/Menus/LicenseMenu.cpp source/WindowCommands.cpp source/Menus/ShortcutMenu.cpp source/Menus/ActionSelectionMenu.cpp source/Menus/ScalingRatioMenu.cpp source/Menus/ISNitroMenu.cpp source/Menus/VideoEffectsMenu.cpp source/CaptureDataBuffers.cpp source/Menus/InputMenu.cpp source/Menus/AudioDeviceMenu.cpp ${TOOLS_DATA_DIR}/font_ttf.cpp ${TOOLS_DATA_DIR}/shaders_list.cpp ${SOURCE_CPP_EXTRA_FILES}) if(NOT ("${EXTRA_DEPENDENCIES}" STREQUAL "")) diff --git a/include/Menus/AudioDeviceMenu.hpp b/include/Menus/AudioDeviceMenu.hpp new file mode 100644 index 0000000..d9ff14e --- /dev/null +++ b/include/Menus/AudioDeviceMenu.hpp @@ -0,0 +1,31 @@ +#ifndef __AUDIODEVICEMENU_HPP +#define __AUDIODEVICEMENU_HPP + +#include "OptionSelectionMenu.hpp" +#include + +#include "TextRectangle.hpp" +#include "sfml_gfx_structs.hpp" +#include "display_structs.hpp" +#include "audio_data.hpp" + +#define AUDIODEVICE_MENU_NO_ACTION -1 +#define AUDIODEVICE_MENU_BACK -2 + +class AudioDeviceMenu : public OptionSelectionMenu { +public: + AudioDeviceMenu(bool font_load_success, sf::Font &text_font); + ~AudioDeviceMenu(); + void prepare(float scaling_factor, int view_size_x, int view_size_y, const audio_output_device_data curr_device_data); + void insert_data(std::vector* possible_devices); + int selected_index = AUDIODEVICE_MENU_NO_ACTION; + void reset_output_option(); +protected: + void set_output_option(int index, int action); + int get_num_options(); + std::string get_string_option(int index, int action); + void class_setup(); +private: + std::vector* possible_devices; +}; +#endif diff --git a/include/Menus/AudioMenu.hpp b/include/Menus/AudioMenu.hpp index c07d857..9657e92 100755 --- a/include/Menus/AudioMenu.hpp +++ b/include/Menus/AudioMenu.hpp @@ -19,7 +19,7 @@ enum AudioMenuOutAction{ AUDIO_MENU_OUTPUT_DEC, AUDIO_MENU_OUTPUT_INC, AUDIO_MENU_RESTART, - AUDIO_MENU_NEXT_DEVICE, + AUDIO_MENU_CHANGE_DEVICE, }; class AudioMenu : public OptionSelectionMenu { diff --git a/include/audio_data.hpp b/include/audio_data.hpp index f6b6d30..7c61694 100755 --- a/include/audio_data.hpp +++ b/include/audio_data.hpp @@ -2,10 +2,18 @@ #define __AUDIO_DATA_HPP #include +#include #define MAX_MAX_AUDIO_LATENCY 10 enum AudioOutputType {AUDIO_OUTPUT_STEREO, AUDIO_OUTPUT_MONO, AUDIO_OUTPUT_END}; +struct audio_output_device_data { + bool preference_requested = false; + std::string preferred = ""; +}; + +int searchAudioDevice(std::string device_name, std::vector &audio_devices); + class AudioData { public: void reset(); @@ -27,12 +35,15 @@ public: bool load_audio_data(std::string key, std::string value); std::string save_audio_data(); void set_audio_volume(int new_volume); + audio_output_device_data get_audio_output_device_data(); + void set_audio_output_device_data(audio_output_device_data new_device_data); private: int volume; int max_audio_latency; bool mute; AudioOutputType output_type; + audio_output_device_data output_device; bool restart_request = false; bool text_updated; std::string text; @@ -44,6 +55,8 @@ private: const std::string volume_str = "volume"; const std::string mute_str = "mute"; const std::string output_type_str = "audio_output_type"; + const std::string device_request_str = "audio_output_device_requested"; + const std::string device_name_str = "audio_output_device_name"; }; #endif diff --git a/include/display_structs.hpp b/include/display_structs.hpp index 838922d..ad5208a 100755 --- a/include/display_structs.hpp +++ b/include/display_structs.hpp @@ -14,7 +14,7 @@ enum ScreenType { TOP, BOTTOM, JOINT }; enum BottomRelativePosition { UNDER_TOP, LEFT_TOP, ABOVE_TOP, RIGHT_TOP, BOT_REL_POS_END }; enum NonIntegerScalingModes { SMALLER_PRIORITY, INVERSE_PROPORTIONAL_PRIORITY, EQUAL_PRIORITY, PROPORTIONAL_PRIORITY, BIGGER_PRIORITY, END_NONINT_SCALE_MODES }; -enum CurrMenuType { DEFAULT_MENU_TYPE, CONNECT_MENU_TYPE, MAIN_MENU_TYPE, VIDEO_MENU_TYPE, AUDIO_MENU_TYPE, CROP_MENU_TYPE, TOP_PAR_MENU_TYPE, BOTTOM_PAR_MENU_TYPE, ROTATION_MENU_TYPE, OFFSET_MENU_TYPE, BFI_MENU_TYPE, LOAD_MENU_TYPE, SAVE_MENU_TYPE, RESOLUTION_MENU_TYPE, EXTRA_MENU_TYPE, STATUS_MENU_TYPE, LICENSES_MENU_TYPE, RELATIVE_POS_MENU_TYPE, SHORTCUTS_MENU_TYPE, ACTION_SELECTION_MENU_TYPE, SCALING_RATIO_MENU_TYPE, ISN_MENU_TYPE, VIDEO_EFFECTS_MENU_TYPE, INPUT_MENU_TYPE }; +enum CurrMenuType { DEFAULT_MENU_TYPE, CONNECT_MENU_TYPE, MAIN_MENU_TYPE, VIDEO_MENU_TYPE, AUDIO_MENU_TYPE, CROP_MENU_TYPE, TOP_PAR_MENU_TYPE, BOTTOM_PAR_MENU_TYPE, ROTATION_MENU_TYPE, OFFSET_MENU_TYPE, BFI_MENU_TYPE, LOAD_MENU_TYPE, SAVE_MENU_TYPE, RESOLUTION_MENU_TYPE, EXTRA_MENU_TYPE, STATUS_MENU_TYPE, LICENSES_MENU_TYPE, RELATIVE_POS_MENU_TYPE, SHORTCUTS_MENU_TYPE, ACTION_SELECTION_MENU_TYPE, SCALING_RATIO_MENU_TYPE, ISN_MENU_TYPE, VIDEO_EFFECTS_MENU_TYPE, INPUT_MENU_TYPE, AUDIO_DEVICE_MENU_TYPE }; enum InputColorspaceMode { FULL_COLORSPACE, DS_COLORSPACE, GBA_COLORSPACE, INPUT_COLORSPACE_END }; enum FrameBlendingMode { NO_FRAME_BLENDING, FULL_FRAME_BLENDING, DS_3D_BOTH_SCREENS_FRAME_BLENDING, FRAME_BLENDING_END }; diff --git a/include/frontend.hpp b/include/frontend.hpp index 3a2f192..2ddfc3b 100755 --- a/include/frontend.hpp +++ b/include/frontend.hpp @@ -33,6 +33,7 @@ #include "ISNitroMenu.hpp" #include "VideoEffectsMenu.hpp" #include "InputMenu.hpp" +#include "AudioDeviceMenu.hpp" #include "display_structs.hpp" #include "event_structs.hpp" #include "shaders_list.hpp" @@ -153,12 +154,14 @@ private: ScalingRatioMenu *scaling_ratio_menu; VideoEffectsMenu *video_effects_menu; InputMenu *input_menu; + AudioDeviceMenu *audio_device_menu; std::vector possible_crops; std::vector possible_crops_ds; std::vector possible_crops_with_games; std::vector possible_crops_ds_with_games; std::vector possible_pars; std::vector possible_resolutions; + std::vector possible_audio_devices; std::vector possible_files; std::vector possible_buttons_names; std::vector possible_buttons_ptrs; @@ -226,6 +229,7 @@ private: void fast_poll_change(); void padding_change(); void game_crop_enable_change(); + void audio_device_change(audio_output_device_data new_device_data); void is_nitro_capture_type_change(bool positive); void is_nitro_capture_speed_change(bool positive); void is_nitro_capture_reset_hw(); @@ -299,6 +303,7 @@ private: void setup_bfi_menu(bool reset_data = true); void setup_fileconfig_menu(bool is_save, bool reset_data = true, bool skip_setup_check = false); void setup_resolution_menu(bool reset_data = true); + void setup_audio_devices_menu(bool reset_data = true); void setup_extra_menu(bool reset_data = true); void setup_shortcuts_menu(bool reset_data = true); void setup_action_selection_menu(bool reset_data = true); diff --git a/source/CaptureDeviceSpecific/Nisetro/cypress_nisetro_acquisition.cpp b/source/CaptureDeviceSpecific/Nisetro/cypress_nisetro_acquisition.cpp index b27ed2b..16728cd 100644 --- a/source/CaptureDeviceSpecific/Nisetro/cypress_nisetro_acquisition.cpp +++ b/source/CaptureDeviceSpecific/Nisetro/cypress_nisetro_acquisition.cpp @@ -262,6 +262,8 @@ static void cypress_device_read_frame_cb(void* user_data, int transfer_length, i if((*cypress_device_capture_recv_data->consecutive_output_to_thread) > NUM_CONSECUTIVE_NEEDED_OUTPUT) *cypress_device_capture_recv_data->errors_since_last_output = 0; *cypress_device_capture_recv_data->last_index = cypress_device_capture_recv_data->index; + + // Realign, with multiple of max_usb_packet_size if((*cypress_device_capture_recv_data->is_active_special_read) && (((int32_t)(cypress_device_capture_recv_data->index - (*cypress_device_capture_recv_data->active_special_read_index))) >= 0)) { *cypress_device_capture_recv_data->is_active_special_read = false; return end_cypress_device_read_frame_cb(cypress_device_capture_recv_data); @@ -394,6 +396,12 @@ void reset_cypress_device_status(CypressDeviceCaptureReceivedData* cypress_devic } void recalibration_reads(cyni_device_device_handlers* handlers, const cyni_device_usb_device* usb_device_desc, CypressDeviceCaptureReceivedData* cypress_device_capture_recv_data, CypressDeviceCaptureReceivedData* chosen_buffer, uint32_t &index, CaptureScreensType curr_capture_type) { + // Enforce properly synchronized reads. + // Reduces complexity and latency, at the cost of some extra + // time between lid reopening and visible video output. + // For differences which are multiple of max_usb_packet_size, + // regular reads can be used. For non-multiples, use this, + // which adds sleeps to better align. CaptureData* capture_data = cypress_device_capture_recv_data[0].capture_data; do { if(get_cypress_device_status(cypress_device_capture_recv_data) < 0) diff --git a/source/Menus/AudioDeviceMenu.cpp b/source/Menus/AudioDeviceMenu.cpp new file mode 100644 index 0000000..94514e3 --- /dev/null +++ b/source/Menus/AudioDeviceMenu.cpp @@ -0,0 +1,71 @@ +#include "AudioDeviceMenu.hpp" + +AudioDeviceMenu::AudioDeviceMenu(bool font_load_success, sf::Font &text_font) : OptionSelectionMenu(){ + this->initialize(font_load_success, text_font); +} + +AudioDeviceMenu::~AudioDeviceMenu() { +} + +void AudioDeviceMenu::class_setup() { + this->num_options_per_screen = 5; + this->min_elements_text_scaling_factor = num_options_per_screen + 2; + this->width_factor_menu = 16; + this->width_divisor_menu = 9; + this->base_height_factor_menu = 12; + this->base_height_divisor_menu = 6; + this->min_text_size = 0.3; + this->max_width_slack = 1.1; + this->menu_color = sf::Color(30, 30, 60, 192); + this->title = "Audio Device Selection"; + this->show_back_x = true; + this->show_x = false; + this->show_title = true; +} + +void AudioDeviceMenu::insert_data(std::vector* possible_devices) { + this->possible_devices = possible_devices; + this->prepare_options(); +} + +void AudioDeviceMenu::reset_output_option() { + this->selected_index = AUDIODEVICE_MENU_NO_ACTION; +} + +void AudioDeviceMenu::set_output_option(int index, int action) { + if(index == BACK_X_OUTPUT_OPTION) + this->selected_index = AUDIODEVICE_MENU_BACK; + else + this->selected_index = index; +} + +int AudioDeviceMenu::get_num_options() { + return this->possible_devices->size() + 1; +} + +std::string AudioDeviceMenu::get_string_option(int index, int action) { + if(index == 0) + return "System Preference"; + return (*this->possible_devices)[index - 1]; +} + +void AudioDeviceMenu::prepare(float menu_scaling_factor, int view_size_x, int view_size_y, const audio_output_device_data curr_device_data) { + int num_pages = this->get_num_pages(); + if(this->future_data.page >= num_pages) + this->future_data.page = num_pages - 1; + int start = this->future_data.page * this->num_options_per_screen; + for(int i = 0; i < this->num_options_per_screen + 1; i++) { + int index = (i * this->single_option_multiplier) + this->elements_start_id; + if(!this->future_enabled_labels[index]) + continue; + int mode_index = start + i; + int device_index = -1; + if(curr_device_data.preference_requested) + device_index = searchAudioDevice(curr_device_data.preferred, *this->possible_devices); + if(mode_index == (device_index + 1)) + this->labels[index]->setText("< " + this->get_string_option(mode_index, DEFAULT_ACTION) + " >"); + else + this->labels[index]->setText(this->get_string_option(mode_index, DEFAULT_ACTION)); + } + this->base_prepare(menu_scaling_factor, view_size_x, view_size_y); +} diff --git a/source/Menus/AudioMenu.cpp b/source/Menus/AudioMenu.cpp index bf95fe7..f262f6b 100755 --- a/source/Menus/AudioMenu.cpp +++ b/source/Menus/AudioMenu.cpp @@ -40,9 +40,9 @@ static const AudioMenuOptionInfo audio_restart_option = { .out_action = AUDIO_MENU_RESTART}; static const AudioMenuOptionInfo audio_next_device_option = { -.base_name = "Output Device", .false_name = "", +.base_name = "Select Output Device", .false_name = "", .is_inc = false, .dec_str = "", .inc_str = "", .inc_out_action = AUDIO_MENU_NO_ACTION, -.out_action = AUDIO_MENU_NEXT_DEVICE}; +.out_action = AUDIO_MENU_CHANGE_DEVICE}; static const AudioMenuOptionInfo* pollable_options[] = { @@ -50,8 +50,8 @@ static const AudioMenuOptionInfo* pollable_options[] = { &audio_mute_option, &audio_max_latency_option, &audio_output_type_option, -&audio_restart_option, &audio_next_device_option, +&audio_restart_option, }; AudioMenu::AudioMenu(bool font_load_success, sf::Font &text_font) : OptionSelectionMenu(){ @@ -144,11 +144,6 @@ void AudioMenu::prepare(float menu_scaling_factor, int view_size_x, int view_siz case AUDIO_MENU_MUTE: this->labels[index]->setText(this->setTextOptionBool(real_index, audio_data->get_mute())); break; - case AUDIO_MENU_NEXT_DEVICE: - if (std::optional cur_dev = sf::PlaybackDevice::getDevice()) { - this->labels[index]->setText(this->setTextOptionString(real_index, *cur_dev)); - } - break; default: break; } diff --git a/source/WindowScreen_Menu.cpp b/source/WindowScreen_Menu.cpp index 49bc2f1..bcf0e6e 100755 --- a/source/WindowScreen_Menu.cpp +++ b/source/WindowScreen_Menu.cpp @@ -115,6 +115,7 @@ void WindowScreen::init_menus() { this->is_nitro_menu = new ISNitroMenu(this->font_load_success, this->text_font); this->video_effects_menu = new VideoEffectsMenu(this->font_load_success, this->text_font); this->input_menu = new InputMenu(this->font_load_success, this->text_font); + this->audio_device_menu = new AudioDeviceMenu(this->font_load_success, this->text_font); } void WindowScreen::destroy_menus() { @@ -139,6 +140,7 @@ void WindowScreen::destroy_menus() { delete this->is_nitro_menu; delete this->video_effects_menu; delete this->input_menu; + delete this->audio_device_menu; } void WindowScreen::set_close(int ret_val) { @@ -289,6 +291,11 @@ void WindowScreen::offset_change(float &value, float change) { this->future_operations.call_screen_settings_update = true; } +void WindowScreen::audio_device_change(audio_output_device_data new_device_data) { + this->audio_data->set_audio_output_device_data(new_device_data); + this->print_notification("Audio device changed"); +} + void WindowScreen::menu_scaling_change(bool positive) { double old_scaling = this->m_info.menu_scaling_factor; if(positive) @@ -694,6 +701,20 @@ void WindowScreen::setup_input_menu(bool reset_data) { } } +void WindowScreen::setup_audio_devices_menu(bool reset_data){ + if(!this->can_setup_menu()) + return; + if(this->curr_menu != AUDIO_DEVICE_MENU_TYPE) { + this->possible_audio_devices.clear(); + this->possible_audio_devices = sf::PlaybackDevice::getAvailableDevices(); + this->curr_menu = AUDIO_DEVICE_MENU_TYPE; + if(reset_data) + this->audio_device_menu->reset_data(); + this->audio_device_menu->insert_data(&this->possible_audio_devices); + this->last_menu_change_time = std::chrono::high_resolution_clock::now(); + } +} + void WindowScreen::setup_video_menu(bool reset_data) { if(!this->can_setup_menu()) return; @@ -1374,24 +1395,9 @@ void WindowScreen::poll(bool do_everything) { case AUDIO_MENU_RESTART: this->audio_data->request_audio_restart(); break; - case AUDIO_MENU_NEXT_DEVICE: - if (std::optional cur_dev = sf::PlaybackDevice::getDevice()) { - std::cout << *cur_dev << std::endl; - int idx = 0; - auto audio_devices = sf::PlaybackDevice::getAvailableDevices(); - for (const std::string& device : audio_devices) { - if (device == *cur_dev) { - break; - } - idx++; - } - idx = std::max((idx + 1) % static_cast(audio_devices.size()), 0); - std::cout << "next device: " << audio_devices.at(idx) << std::endl; - if (sf::PlaybackDevice::setDevice(audio_devices.at(idx))) { - std::cout << "Device selected: " << audio_devices.at(idx) << std::endl; - // this->audio_data->request_audio_restart(); - } - } + case AUDIO_MENU_CHANGE_DEVICE: + this->setup_audio_devices_menu(); + done = true; break; default: break; @@ -1984,6 +1990,29 @@ void WindowScreen::poll(bool do_everything) { continue; } break; + case AUDIO_DEVICE_MENU_TYPE: + if(this->audio_device_menu->poll(event_data)) { + switch(this->audio_device_menu->selected_index) { + case AUDIODEVICE_MENU_BACK: + this->setup_audio_menu(false); + done = true; + break; + case AUDIODEVICE_MENU_NO_ACTION: + break; + default: + audio_output_device_data tmp_out_device; + int output_device_index = this->audio_device_menu->selected_index - 1; + if((output_device_index >= 0) && (output_device_index < this->possible_audio_devices.size())) { + tmp_out_device.preference_requested = true; + tmp_out_device.preferred = this->possible_audio_devices[output_device_index]; + } + this->audio_device_change(tmp_out_device); + break; + } + this->audio_device_menu->reset_output_option(); + continue; + } + break; default: break; } @@ -2257,6 +2286,9 @@ void WindowScreen::prepare_menu_draws(int view_size_x, int view_size_y) { case INPUT_MENU_TYPE: this->input_menu->prepare(this->loaded_info.menu_scaling_factor, view_size_x, view_size_y, &this->shared_data->input_data); break; + case AUDIO_DEVICE_MENU_TYPE: + this->audio_device_menu->prepare(this->loaded_info.menu_scaling_factor, view_size_x, view_size_y, this->audio_data->get_audio_output_device_data()); + break; default: break; } @@ -2333,6 +2365,9 @@ void WindowScreen::execute_menu_draws() { case INPUT_MENU_TYPE: this->input_menu->draw(this->loaded_info.menu_scaling_factor, this->m_win); break; + case AUDIO_DEVICE_MENU_TYPE: + this->audio_device_menu->draw(this->loaded_info.menu_scaling_factor, this->m_win); + break; default: break; } diff --git a/source/audio_data.cpp b/source/audio_data.cpp index 2e4a908..93f9c2d 100755 --- a/source/audio_data.cpp +++ b/source/audio_data.cpp @@ -1,5 +1,15 @@ #include "audio_data.hpp" +int searchAudioDevice(std::string device_name, std::vector &audio_devices) { + int index = -1; + for(int i = 0; i < audio_devices.size(); i++) + if(audio_devices[i] == device_name) { + index = i; + break; + } + return index; +} + void AudioData::reset() { this->volume = 100; this->mute = false; @@ -7,6 +17,8 @@ void AudioData::reset() { this->output_type = AUDIO_OUTPUT_STEREO; this->restart_request = false; this->text_updated = false; + this->output_device.preference_requested = false; + this->output_device.preferred = ""; } void AudioData::change_max_audio_latency(bool is_change_positive) { @@ -77,6 +89,19 @@ bool AudioData::has_text_to_print() { return retval; } +void AudioData::set_audio_output_device_data(audio_output_device_data new_device_data) { + bool had_preference = this->output_device.preference_requested; + if(had_preference) + this->output_device.preference_requested = new_device_data.preference_requested; + this->output_device.preferred = new_device_data.preferred; + if(!had_preference) + this->output_device.preference_requested = new_device_data.preference_requested; +} + +audio_output_device_data AudioData::get_audio_output_device_data() { + return this->output_device; +} + std::string AudioData::text_to_print() { return this->text; } @@ -147,6 +172,14 @@ bool AudioData::load_audio_data(std::string key, std::string value) { this->set_audio_output_type(std::stoi(value)); return true; } + if (key == this->device_request_str) { + this->output_device.preference_requested = std::stoi(value); + return true; + } + if (key == this->device_name_str) { + this->output_device.preferred = value; + return true; + } return false; } @@ -156,6 +189,8 @@ std::string AudioData::save_audio_data() { out_str += this->volume_str + "=" + std::to_string(this->volume) + "\n"; out_str += this->max_audio_latency_str + "=" + std::to_string(this->max_audio_latency) + "\n"; out_str += this->output_type_str + "=" + std::to_string(this->output_type) + "\n"; + out_str += this->device_request_str + "=" + std::to_string(this->output_device.preference_requested) + "\n"; + out_str += this->device_name_str + "=" + this->output_device.preferred + "\n"; return out_str; } diff --git a/source/cc3dsfs.cpp b/source/cc3dsfs.cpp index a5fc24d..a266307 100755 --- a/source/cc3dsfs.cpp +++ b/source/cc3dsfs.cpp @@ -328,12 +328,25 @@ static void executeSoundRestart(Audio &audio, AudioData* audio_data, bool do_res audio.play(); } +static bool setDefaultAudioDevice(Audio &audio) { + bool success = false; + std::optional default_device = sf::PlaybackDevice::getDefaultDevice(); + if(default_device) { + audio.stop_audio(); + audio.stop(); + success = sf::PlaybackDevice::setDevice(default_device.value()); + } + return success; +} + static void soundCall(AudioData *audio_data, CaptureData* capture_data) { std::int16_t (*out_buf)[MAX_SAMPLES_IN] = new std::int16_t[NUM_CONCURRENT_AUDIO_BUFFERS][MAX_SAMPLES_IN]; Audio audio(audio_data); int audio_buf_counter = 0; const bool endianness = is_big_endian(); volatile int loaded_samples; + audio_output_device_data in_use_audio_output_device_data; + std::optional curr_device = sf::PlaybackDevice::getDevice(); while(capture_data->status.running) { if(capture_data->status.connected && capture_data->status.device.has_audio) { @@ -361,14 +374,22 @@ static void soundCall(AudioData *audio_data, CaptureData* capture_data) { loaded_samples = audio.samples.size(); if(audio.getStatus() != sf::SoundStream::Status::Playing) { audio.stop_audio(); - if(loaded_samples > 0) - executeSoundRestart(audio, audio_data, audio.restart); + if(loaded_samples > 0) { + bool do_restart = audio.restart; + executeSoundRestart(audio, audio_data, do_restart); + if(do_restart) { + in_use_audio_output_device_data.preference_requested = false; + curr_device = sf::PlaybackDevice::getDevice(); + } + } } else { if(loaded_samples > 0) { if(audio.hasTooMuchTimeElapsed()) { audio.stop_audio(); executeSoundRestart(audio, audio_data, true); + in_use_audio_output_device_data.preference_requested = false; + curr_device = sf::PlaybackDevice::getDevice(); } } audio.update_volume(); @@ -379,6 +400,44 @@ static void soundCall(AudioData *audio_data, CaptureData* capture_data) { audio.stop(); default_sleep(); } + + // Code for audio device selection + audio_output_device_data new_audio_output_device_data = audio_data->get_audio_output_device_data(); + if((new_audio_output_device_data.preference_requested != in_use_audio_output_device_data.preference_requested) || (new_audio_output_device_data.preference_requested && (new_audio_output_device_data.preferred != in_use_audio_output_device_data.preferred))) { + int index = -1; + bool success = false; + if(new_audio_output_device_data.preference_requested) { + std::vector audio_devices = sf::PlaybackDevice::getAvailableDevices(); + index = searchAudioDevice(new_audio_output_device_data.preferred, audio_devices); + if(index != -1) { + audio.stop_audio(); + audio.stop(); + success = sf::PlaybackDevice::setDevice(audio_devices[index]); + } + } + if(index == -1) + success = setDefaultAudioDevice(audio); + if(success) + curr_device = sf::PlaybackDevice::getDevice(); + } + in_use_audio_output_device_data = new_audio_output_device_data; + if(!in_use_audio_output_device_data.preference_requested) { + std::optional default_device = sf::PlaybackDevice::getDefaultDevice(); + if(default_device != curr_device) { + bool success = setDefaultAudioDevice(audio); + if(success) + curr_device = sf::PlaybackDevice::getDevice(); + } + } + else if(curr_device) { + std::vector audio_devices = sf::PlaybackDevice::getAvailableDevices(); + int index = searchAudioDevice(curr_device.value(), audio_devices); + if(index == -1) { + bool success = setDefaultAudioDevice(audio); + if(success) + curr_device = sf::PlaybackDevice::getDevice(); + } + } } audio.stop_audio(); @@ -765,7 +824,7 @@ int main(int argc, char **argv) { std::cout << " even if multiple are present." << std::endl; std::cout << " --failure_close Automatically closes the software if the first connection" << std::endl; std::cout << " doesn't succeed." << std::endl; - std::cout << " --failure_close Automatically closes the software on disconnect." << std::endl; + std::cout << " --auto_close Automatically closes the software on disconnect." << std::endl; std::cout << " --profile Loads the profile with the specified ID at startup" << std::endl; std::cout << " instead of the default one. When the program closes," << std::endl; std::cout << " the data is also saved to the specified profile." << std::endl;