Skip to content

Commit

Permalink
Recreate ma_device on failure (#4729)
Browse files Browse the repository at this point in the history
  • Loading branch information
pajlada authored Jul 22, 2023
1 parent 0e83367 commit 5c8c05b
Show file tree
Hide file tree
Showing 3 changed files with 62 additions and 24 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
76 changes: 53 additions & 23 deletions src/controllers/sound/SoundController.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@ constexpr const auto NUM_SOUNDS = 4;
SoundController::SoundController()
: context(std::make_unique<ma_context>())
, resourceManager(std::make_unique<ma_resource_manager>())
, device(std::make_unique<ma_device>())
, engine(std::make_unique<ma_engine>())
{
}
Expand Down Expand Up @@ -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;
}

Expand Down Expand Up @@ -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());
}
Expand Down Expand Up @@ -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";
Expand Down Expand Up @@ -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<ma_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)
{
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
9 changes: 8 additions & 1 deletion src/controllers/sound/SoundController.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ class SoundController : public Singleton
// Used for storing & reusing sounds to be played
std::unique_ptr<ma_resource_manager> resourceManager;
// The sound device we're playing sound into
std::unique_ptr<ma_device> device;
std::unique_ptr<ma_device> device{nullptr};
// The engine is a high-level API for playing sounds from paths in a simple & efficient-enough manner
std::unique_ptr<ma_engine> engine;

Expand All @@ -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;
};

Expand Down

0 comments on commit 5c8c05b

Please sign in to comment.