#include #include #include #include #include #include #include #include namespace scwx::qt::manager { static const std::string logPrefix_ = "scwx::qt::manager::media_manager"; static const auto logger_ = scwx::util::Logger::Create(logPrefix_); class MediaManager::Impl { public: explicit Impl() { mediaParent_ = std::make_unique(); mediaParent_->moveToThread(&thread_); thread_.start(); QMetaObject::invokeMethod( mediaParent_.get(), [this]() { // QObjects are managed by the parent // NOLINTBEGIN(cppcoreguidelines-owning-memory) logger_->debug("Creating QMediaDevices"); mediaDevices_ = new QMediaDevices(mediaParent_.get()); logger_->debug("Creating QMediaPlayer"); mediaPlayer_ = new QMediaPlayer(mediaParent_.get()); logger_->debug("Creating QAudioOutput"); audioOutput_ = new QAudioOutput(mediaParent_.get()); // NOLINTEND(cppcoreguidelines-owning-memory) logger_->debug("Audio device: {}", audioOutput_->device().description().toStdString()); mediaPlayer_->setAudioOutput(audioOutput_); ConnectSignals(); }); } ~Impl() { // Delete the media parent mediaParent_.reset(); thread_.quit(); thread_.wait(); } Impl(const Impl&) = delete; Impl& operator=(const Impl&) = delete; Impl(const Impl&&) = delete; Impl& operator=(const Impl&&) = delete; void ConnectSignals(); QThread thread_ {}; std::unique_ptr mediaParent_ {nullptr}; QMediaDevices* mediaDevices_ {nullptr}; QMediaPlayer* mediaPlayer_ {nullptr}; QAudioOutput* audioOutput_ {nullptr}; }; MediaManager::MediaManager() : p(std::make_unique()) {} MediaManager::~MediaManager() = default; MediaManager::MediaManager(MediaManager&&) noexcept = default; MediaManager& MediaManager::operator=(MediaManager&&) noexcept = default; void MediaManager::Impl::ConnectSignals() { QObject::connect( mediaDevices_, &QMediaDevices::audioOutputsChanged, mediaParent_.get(), [this]() { audioOutput_->setDevice(QMediaDevices::defaultAudioOutput()); }); QObject::connect(audioOutput_, &QAudioOutput::deviceChanged, mediaParent_.get(), [this]() { logger_->debug( "Audio device changed: {}", audioOutput_->device().description().toStdString()); }); QObject::connect(mediaPlayer_, &QMediaPlayer::errorOccurred, mediaParent_.get(), [](QMediaPlayer::Error error, const QString& errorString) { logger_->error("Error {}: {}", static_cast(error), errorString.toStdString()); }); } void MediaManager::Play(types::AudioFile media) { const std::string path = types::GetMediaPath(media); Play(path); } void MediaManager::Play(const std::string& mediaPath) { logger_->debug("Playing audio: {}", mediaPath); if (p->mediaPlayer_ == nullptr) { logger_->warn("Media player is not yet initialized"); return; } if (mediaPath.starts_with(':')) { QMetaObject::invokeMethod( p->mediaPlayer_, &QMediaPlayer::setSource, QUrl(QString("qrc%1").arg(QString::fromStdString(mediaPath)))); } else { QMetaObject::invokeMethod( p->mediaPlayer_, &QMediaPlayer::setSource, QUrl::fromLocalFile(QString::fromStdString(mediaPath))); } QMetaObject::invokeMethod(p->mediaPlayer_, &QMediaPlayer::setPosition, 0); QMetaObject::invokeMethod(p->mediaPlayer_, &QMediaPlayer::play); } void MediaManager::Stop() { if (p->mediaPlayer_ == nullptr) { logger_->warn("Media player is not yet initialized"); return; } QMetaObject::invokeMethod(p->mediaPlayer_, &QMediaPlayer::stop); } std::shared_ptr MediaManager::Instance() { static std::weak_ptr mediaManagerReference_ {}; static std::mutex instanceMutex_ {}; std::unique_lock lock(instanceMutex_); std::shared_ptr mediaManager = mediaManagerReference_.lock(); if (mediaManager == nullptr) { mediaManager = std::make_shared(); mediaManagerReference_ = mediaManager; } return mediaManager; } } // namespace scwx::qt::manager