diff --git a/CHANGELOG.md b/CHANGELOG.md index e316459e3e4..acb0a16b465 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -21,6 +21,7 @@ - Bugfix: Fixed partially broken filters on Qt 6 builds. (#4702) - Bugfix: Fixed some network errors having `0` as their HTTP status. (#4704) - Bugfix: Fixed crash that could occurr when closing the usercard too quickly after blocking or unblocking a user. (#4711) +- Bugfix: Fixed highlights sometimes not working after changing sound device, or switching users in your operating system. (#4729) - Bugfix: Fixed key bindings not showing in context menus on Mac. (#4722) - Dev: Added command to set Qt's logging filter/rules at runtime (`/c2-set-logging-rules`). (#4637) - Dev: Added the ability to see & load custom themes from the Themes directory. No stable promises are made of this feature, changes might be made that breaks custom themes without notice. (#4570) diff --git a/src/controllers/sound/SoundController.cpp b/src/controllers/sound/SoundController.cpp index 449bc3c479b..da7871a7950 100644 --- a/src/controllers/sound/SoundController.cpp +++ b/src/controllers/sound/SoundController.cpp @@ -20,7 +20,6 @@ constexpr const auto NUM_SOUNDS = 4; SoundController::SoundController() : context(std::make_unique()) , resourceManager(std::make_unique()) - , device(std::make_unique()) , engine(std::make_unique()) { } @@ -66,27 +65,9 @@ void SoundController::initialize(Settings &settings, Paths &paths) this->defaultPingData = defaultPingFile.readAll(); /// Initialize a sound device - auto deviceConfig = ma_device_config_init(ma_device_type_playback); - deviceConfig.playback.pDeviceID = nullptr; - deviceConfig.playback.format = this->resourceManager->config.decodedFormat; - deviceConfig.playback.channels = 0; - deviceConfig.pulse.pStreamNamePlayback = "Chatterino MA"; - deviceConfig.sampleRate = this->resourceManager->config.decodedSampleRate; - deviceConfig.dataCallback = ma_engine_data_callback_internal; - deviceConfig.pUserData = this->engine.get(); - - result = - ma_device_init(this->context.get(), &deviceConfig, this->device.get()); - if (result != MA_SUCCESS) + if (!this->recreateDevice()) { - qCWarning(chatterinoSound) << "Error initializing device:" << result; - return; - } - - result = ma_device_start(this->device.get()); - if (result != MA_SUCCESS) - { - qCWarning(chatterinoSound) << "Error starting device:" << result; + qCWarning(chatterinoSound) << "Failed to create the initial device"; return; } @@ -172,7 +153,11 @@ SoundController::~SoundController() } ma_engine_uninit(this->engine.get()); - ma_device_uninit(this->device.get()); + if (this->device) + { + ma_device_uninit(this->device.get()); + this->device.reset(); + } ma_resource_manager_uninit(this->resourceManager.get()); ma_context_uninit(this->context.get()); } @@ -204,7 +189,12 @@ void SoundController::play(const QUrl &sound) { qCWarning(chatterinoSound) << "Failed to start the sound device" << result; - return; + + if (!this->recreateDevice()) + { + qCWarning(chatterinoSound) << "Failed to recreate device"; + return; + } } qCInfo(chatterinoSound) << "Successfully restarted the sound device"; @@ -234,4 +224,44 @@ void SoundController::play(const QUrl &sound) } } +bool SoundController::recreateDevice() +{ + ma_result result{}; + + if (this->device) + { + // Release the previous device first + qCDebug(chatterinoSound) << "Uniniting previously created device"; + ma_device_uninit(this->device.get()); + } + + this->device = std::make_unique(); + + auto deviceConfig = ma_device_config_init(ma_device_type_playback); + deviceConfig.playback.pDeviceID = nullptr; + deviceConfig.playback.format = this->resourceManager->config.decodedFormat; + deviceConfig.playback.channels = 0; + deviceConfig.pulse.pStreamNamePlayback = "Chatterino MA"; + deviceConfig.sampleRate = this->resourceManager->config.decodedSampleRate; + deviceConfig.dataCallback = ma_engine_data_callback_internal; + deviceConfig.pUserData = this->engine.get(); + + result = + ma_device_init(this->context.get(), &deviceConfig, this->device.get()); + if (result != MA_SUCCESS) + { + qCWarning(chatterinoSound) << "Error initializing device:" << result; + return false; + } + + result = ma_device_start(this->device.get()); + if (result != MA_SUCCESS) + { + qCWarning(chatterinoSound) << "Error starting device:" << result; + return false; + } + + return true; +} + } // namespace chatterino diff --git a/src/controllers/sound/SoundController.hpp b/src/controllers/sound/SoundController.hpp index 5591b982f55..fbd3ac87fc2 100644 --- a/src/controllers/sound/SoundController.hpp +++ b/src/controllers/sound/SoundController.hpp @@ -45,7 +45,7 @@ class SoundController : public Singleton // Used for storing & reusing sounds to be played std::unique_ptr resourceManager; // The sound device we're playing sound into - std::unique_ptr device; + std::unique_ptr device{nullptr}; // The engine is a high-level API for playing sounds from paths in a simple & efficient-enough manner std::unique_ptr engine; @@ -64,6 +64,13 @@ class SoundController : public Singleton bool initialized{false}; + // Recreates the sound device + // This is used during initialization, and can also be used if the device + // needs to be recreated during playback + // + // Returns false on failure + bool recreateDevice(); + friend class Application; };