Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for sound backends & some miniaudio changes #4978

Merged
merged 13 commits into from
Nov 26, 2023
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
- Minor: The `/usercard` command now accepts user ids. (#4934)
- Minor: Add menu actions to reply directly to a message or the original thread root. (#4923)
- Minor: The `/reply` command now replies to the latest message of the user. (#4919)
- Minor: All sound capabilities can now be disabled by setting your "Sound backend" setting to "Null" and restarting Chatterino. (#4978)
- Bugfix: Fixed an issue where certain emojis did not send to Twitch chat correctly. (#4840)
- Bugfix: Fixed capitalized channel names in log inclusion list not being logged. (#4848)
- Bugfix: Trimmed custom streamlink paths on all platforms making sure you don't accidentally add spaces at the beginning or end of its path. (#4834)
Expand Down Expand Up @@ -41,6 +42,7 @@
- Bugfix: Fixed lookahead/-behind not working in _Ignores_. (#4965)
- Bugfix: Fixed Image Uploader accidentally deleting images with some hosts when link resolver was enabled. (#4971)
- Bugfix: Fixed rare crash with Image Uploader when closing a split right after starting an upload. (#4971)
- Dev: Run miniaudio in a separate thread, and simplify it to not manage the device ourselves. There's a chance the simplification is a bad idea. (#4978)
- Dev: Change clang-format from v14 to v16. (#4929)
- Dev: Fixed UTF16 encoding of `modes` file for the installer. (#4791)
- Dev: Temporarily disable High DPI scaling on Qt6 builds on Windows. (#4767)
Expand Down
6 changes: 6 additions & 0 deletions mocks/include/mocks/EmptyApplication.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,12 @@ class EmptyApplication : public IApplication
return nullptr;
}

ISoundController *getSound() override
{
assert(!"getSound was called without being initialized");
return nullptr;
}

ITwitchLiveController *getTwitchLiveController() override
{
return nullptr;
Expand Down
39 changes: 37 additions & 2 deletions src/Application.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,14 @@
#include "controllers/hotkeys/HotkeyController.hpp"
#include "controllers/ignores/IgnoreController.hpp"
#include "controllers/notifications/NotificationController.hpp"
#include "controllers/sound/ISoundController.hpp"
#include "providers/seventv/SeventvAPI.hpp"
#include "singletons/ImageUploader.hpp"
#ifdef CHATTERINO_HAVE_PLUGINS
# include "controllers/plugins/PluginController.hpp"
#endif
#include "controllers/sound/SoundController.hpp"
#include "controllers/sound/MiniaudioBackend.hpp"
#include "controllers/sound/NullBackend.hpp"
#include "controllers/twitch/LiveController.hpp"
#include "controllers/userdata/UserDataController.hpp"
#include "debug/AssertInGuiThread.hpp"
Expand Down Expand Up @@ -57,6 +59,34 @@

#include <atomic>

namespace {

using namespace chatterino;

ISoundController *makeSoundController(Settings &settings)
{
SoundBackend soundBackend = settings.soundBackend;
switch (soundBackend)
{
case SoundBackend::Miniaudio: {
return new MiniaudioBackend();
}
break;

case SoundBackend::Null: {
return new NullBackend();
}
break;

default: {
return new MiniaudioBackend();
}
break;
}
}

} // namespace

namespace chatterino {

static std::atomic<bool> isAppInitialized{false};
Expand Down Expand Up @@ -92,7 +122,7 @@ Application::Application(Settings &_settings, Paths &_paths)
, ffzBadges(&this->emplace<FfzBadges>())
, seventvBadges(&this->emplace<SeventvBadges>())
, userData(&this->emplace<UserDataController>())
, sound(&this->emplace<SoundController>())
, sound(&this->emplace<ISoundController>(makeSoundController(_settings)))
, twitchLiveController(&this->emplace<TwitchLiveController>())
#ifdef CHATTERINO_HAVE_PLUGINS
, plugins(&this->emplace<PluginController>())
Expand Down Expand Up @@ -260,6 +290,11 @@ IUserDataController *Application::getUserData()
return this->userData;
}

ISoundController *Application::getSound()
{
return this->sound;
}

ITwitchLiveController *Application::getTwitchLiveController()
{
return this->twitchLiveController;
Expand Down
13 changes: 12 additions & 1 deletion src/Application.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ class HighlightController;
class HotkeyController;
class IUserDataController;
class UserDataController;
class ISoundController;
class SoundController;
class ITwitchLiveController;
class TwitchLiveController;
Expand Down Expand Up @@ -67,6 +68,7 @@ class IApplication
virtual FfzBadges *getFfzBadges() = 0;
virtual SeventvBadges *getSeventvBadges() = 0;
virtual IUserDataController *getUserData() = 0;
virtual ISoundController *getSound() = 0;
virtual ITwitchLiveController *getTwitchLiveController() = 0;
virtual ImageUploader *getImageUploader() = 0;
virtual SeventvAPI *getSeventvAPI() = 0;
Expand Down Expand Up @@ -109,7 +111,7 @@ class Application : public IApplication
FfzBadges *const ffzBadges{};
SeventvBadges *const seventvBadges{};
UserDataController *const userData{};
SoundController *const sound{};
ISoundController *const sound{};

private:
TwitchLiveController *const twitchLiveController{};
Expand Down Expand Up @@ -172,6 +174,7 @@ class Application : public IApplication
return this->seventvBadges;
}
IUserDataController *getUserData() override;
ISoundController *getSound() override;
ITwitchLiveController *getTwitchLiveController() override;
ImageUploader *getImageUploader() override
{
Expand Down Expand Up @@ -200,6 +203,14 @@ class Application : public IApplication
return *t;
}

template <typename T,
typename = std::enable_if_t<std::is_base_of<Singleton, T>::value>>
T &emplace(T *t)
{
this->singletons_.push_back(std::unique_ptr<T>(t));
return *t;
}

NativeMessagingServer nmServer{};
};

Expand Down
7 changes: 5 additions & 2 deletions src/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -224,8 +224,11 @@ set(SOURCE_FILES
controllers/plugins/LuaUtilities.cpp
controllers/plugins/LuaUtilities.hpp

controllers/sound/SoundController.cpp
controllers/sound/SoundController.hpp
controllers/sound/ISoundController.hpp
controllers/sound/MiniaudioBackend.cpp
controllers/sound/MiniaudioBackend.hpp
controllers/sound/NullBackend.cpp
controllers/sound/NullBackend.hpp

controllers/twitch/LiveController.cpp
controllers/twitch/LiveController.hpp
Expand Down
55 changes: 55 additions & 0 deletions src/common/ChatterinoSetting.hpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
#pragma once

#include <magic_enum.hpp>
#include <pajlada/settings.hpp>
#include <QString>

Expand Down Expand Up @@ -85,4 +86,58 @@ class EnumSetting
}
};

/**
* Setters in this class allow for bad values, it's only the enum-specific getters that are protected.
* If you get a QString from this setting, it will be the raw value from the settings file.
* Use the explicit Enum conversions or getEnum to get a typed check with a default
**/
template <typename Enum>
class EnumStringSetting : public pajlada::Settings::Setting<QString>
{
public:
EnumStringSetting(const std::string &path, const Enum &defaultValue_)
: pajlada::Settings::Setting<QString>(path)
, defaultValue(defaultValue_)
{
_registerSetting(this->getData());
}

template <typename T2>
EnumStringSetting<Enum> &operator=(Enum newValue)
{
std::string enumName(magic_enum::enum_name(newValue));
auto qEnumName = QString::fromStdString(enumName);

this->setValue(qEnumName.toLower());

return *this;
}

EnumStringSetting<Enum> &operator=(QString newValue)
{
this->setValue(newValue.toLower());

return *this;
}

operator Enum()
{
return this->getEnum();
}

Enum getEnum()
{
return magic_enum::enum_cast<Enum>(this->getValue().toStdString(),
magic_enum::case_insensitive)
.value_or(this->defaultValue);
}

Enum defaultValue;

using pajlada::Settings::Setting<QString>::operator==;
using pajlada::Settings::Setting<QString>::operator!=;

using pajlada::Settings::Setting<QString>::operator QString;
};

} // namespace chatterino
4 changes: 2 additions & 2 deletions src/controllers/notifications/NotificationController.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
#include "Application.hpp"
#include "common/QLogging.hpp"
#include "controllers/notifications/NotificationModel.hpp"
#include "controllers/sound/SoundController.hpp"
#include "controllers/sound/ISoundController.hpp"
#include "messages/Message.hpp"
#include "providers/twitch/api/Helix.hpp"
#include "providers/twitch/TwitchIrcServer.hpp"
Expand Down Expand Up @@ -105,7 +105,7 @@ void NotificationController::playSound()
getSettings()->notificationPathSound.getValue())
: QUrl("qrc:/sounds/ping2.wav");

getApp()->sound->play(highlightSoundUrl);
getIApp()->getSound()->play(highlightSoundUrl);
}

NotificationModel *NotificationController::createModel(QObject *parent,
Expand Down
38 changes: 38 additions & 0 deletions src/controllers/sound/ISoundController.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
#pragma once

#include "common/Singleton.hpp"

#include <QUrl>

namespace chatterino {

class Settings;
class Paths;

enum class SoundBackend {
Miniaudio,
Null,
};

/**
* @brief Handles sound loading & playback
**/
class ISoundController : public Singleton
{
public:
ISoundController() = default;
~ISoundController() override = default;
ISoundController(const ISoundController &) = delete;
ISoundController(ISoundController &&) = delete;
ISoundController &operator=(const ISoundController &) = delete;
ISoundController &operator=(ISoundController &&) = delete;

// Play a sound from the given url
// If the url points to something that isn't a local file, it will play
// the default sound initialized in the initialize method
//
// This function should not block
virtual void play(const QUrl &sound) = 0;
};

} // namespace chatterino
Loading
Loading