From 7d969eddd83fb21e7ce54544f56850ea087aa7f9 Mon Sep 17 00:00:00 2001 From: Nomango Date: Mon, 25 Sep 2023 00:59:27 +0800 Subject: [PATCH] feat: update sound player --- src/kiwano-audio/AudioModule.cpp | 5 +- src/kiwano-audio/Sound.cpp | 32 ++++++- src/kiwano-audio/Sound.h | 2 + src/kiwano-audio/SoundPlayer.cpp | 153 +++++++++++++------------------ src/kiwano-audio/SoundPlayer.h | 107 ++++++++------------- 5 files changed, 138 insertions(+), 161 deletions(-) diff --git a/src/kiwano-audio/AudioModule.cpp b/src/kiwano-audio/AudioModule.cpp index b1a08a5d..cfc3c961 100644 --- a/src/kiwano-audio/AudioModule.cpp +++ b/src/kiwano-audio/AudioModule.cpp @@ -62,7 +62,10 @@ public: STDMETHOD_(void, OnVoiceProcessingPassStart(UINT32 SamplesRequired)) {} - STDMETHOD_(void, OnVoiceError(void* pBufferContext, HRESULT Error)) {} + STDMETHOD_(void, OnVoiceError(void* pBufferContext, HRESULT Error)) + { + KGE_ERRORF("Voice error with HRESULT of %08X", Error); + } }; AudioModule::AudioModule() diff --git a/src/kiwano-audio/Sound.cpp b/src/kiwano-audio/Sound.cpp index c1ca9d97..ba9f2275 100644 --- a/src/kiwano-audio/Sound.cpp +++ b/src/kiwano-audio/Sound.cpp @@ -139,9 +139,7 @@ bool Sound::Load(TranscoderPtr transcoder) } // reset volume - const float old_volume = volume_; - volume_ = 0.f; - SetVolume(old_volume); + ResetVolume(); coder_ = transcoder; opened_ = true; @@ -297,6 +295,14 @@ void Sound::SetVolume(float volume) voice->SetVolume(actual_volume); } +void Sound::ResetVolume() +{ + const float old_volume = volume_; + + volume_ += 1.f; + SetVolume(old_volume); +} + SoundCallbackPtr Sound::GetCallbackChain() { class SoundCallbackChain : public SoundCallback @@ -313,6 +319,7 @@ SoundCallbackPtr Sound::GetCallbackChain() cb->OnStart(sound); } } + RemoveUsedCallbacks(); } void OnLoopEnd(Sound*) override @@ -324,6 +331,7 @@ SoundCallbackPtr Sound::GetCallbackChain() cb->OnLoopEnd(sound); } } + RemoveUsedCallbacks(); } void OnEnd(Sound*) override @@ -335,6 +343,7 @@ SoundCallbackPtr Sound::GetCallbackChain() cb->OnEnd(sound); } } + RemoveUsedCallbacks(); } float OnVolumeChanged(Sound*, float volume) override @@ -349,6 +358,23 @@ SoundCallbackPtr Sound::GetCallbackChain() } return actual_volume; } + + void RemoveUsedCallbacks() + { + auto& cbs = sound->GetCallbacks(); + auto iter = cbs.begin(); + while (iter != cbs.end()) + { + if (*iter == nullptr) + { + iter = cbs.erase(iter); + } + else + { + iter++; + } + } + } }; if (!callback_chain_) diff --git a/src/kiwano-audio/Sound.h b/src/kiwano-audio/Sound.h index 9304434d..d8580d26 100644 --- a/src/kiwano-audio/Sound.h +++ b/src/kiwano-audio/Sound.h @@ -178,6 +178,8 @@ protected: SoundCallbackPtr GetCallbackChain(); + void ResetVolume(); + private: bool opened_; bool playing_; diff --git a/src/kiwano-audio/SoundPlayer.cpp b/src/kiwano-audio/SoundPlayer.cpp index 3cd35d3d..b223744f 100644 --- a/src/kiwano-audio/SoundPlayer.cpp +++ b/src/kiwano-audio/SoundPlayer.cpp @@ -19,6 +19,7 @@ // THE SOFTWARE. #include +#include namespace kiwano { @@ -32,81 +33,31 @@ SoundPlayer::SoundPlayer() SoundPlayer::~SoundPlayer() { - ClearCache(); + StopAll(); } -size_t SoundPlayer::GetId(const String& file_path) const +void SoundPlayer::Play(SoundPtr sound, int loop_count) { - return std::hash()(file_path); -} - -size_t SoundPlayer::GetId(const Resource& res) const -{ - return static_cast(res.GetId()); -} - -size_t SoundPlayer::Load(const String& file_path) -{ - size_t id = GetId(file_path); - if (sound_cache_.end() != sound_cache_.find(id)) - return id; - - SoundPtr sound = MakePtr(); - if (sound && sound->Load(file_path)) + if (sound) { - sound->SetVolume(volume_); - sound_cache_.insert(std::make_pair(id, sound)); - return id; - } - return 0; -} - -size_t SoundPlayer::Load(const Resource& res) -{ - size_t id = GetId(res); - if (sound_cache_.end() != sound_cache_.find(id)) - return id; - - SoundPtr sound = MakePtr(); - - if (sound && sound->Load(res)) - { - sound->SetVolume(volume_); - sound_cache_.insert(std::make_pair(id, sound)); - return id; - } - return 0; -} - -void SoundPlayer::Play(size_t id, int loop_count) -{ - if (auto sound = GetSound(id)) + SetCallback(sound.Get()); sound->Play(loop_count); + sound_list_.push_back(sound); + } } -void SoundPlayer::Pause(size_t id) +SoundPtr SoundPlayer::Play(const String& file_path, int loop_count, std::initializer_list callbacks) { - if (auto sound = GetSound(id)) - sound->Pause(); + SoundPtr sound = Sound::Preload(file_path, callbacks); + Play(sound, loop_count); + return sound; } -void SoundPlayer::Resume(size_t id) +SoundPtr SoundPlayer::Play(const Resource& res, int loop_count, std::initializer_list callbacks) { - if (auto sound = GetSound(id)) - sound->Resume(); -} - -void SoundPlayer::Stop(size_t id) -{ - if (auto sound = GetSound(id)) - sound->Stop(); -} - -bool SoundPlayer::IsPlaying(size_t id) -{ - if (auto sound = GetSound(id)) - return sound->IsPlaying(); - return false; + SoundPtr sound = Sound::Preload(res, callbacks); + Play(sound, loop_count); + return sound; } float SoundPlayer::GetVolume() const @@ -116,53 +67,81 @@ float SoundPlayer::GetVolume() const void SoundPlayer::SetVolume(float volume) { - volume_ = std::min(std::max(volume, -224.f), 224.f); - for (auto& pair : sound_cache_) - { - pair.second->SetVolume(volume_); - } -} - -SoundPtr SoundPlayer::GetSound(size_t id) const -{ - auto iter = sound_cache_.find(id); - if (iter != sound_cache_.end()) - return iter->second; - return SoundPtr(); + volume_ = volume; } void SoundPlayer::PauseAll() { - for (auto& pair : sound_cache_) + for (auto& sound : sound_list_) { - pair.second->Pause(); + sound->Pause(); } } void SoundPlayer::ResumeAll() { - for (auto& pair : sound_cache_) + for (auto& sound : sound_list_) { - pair.second->Resume(); + sound->Resume(); } } void SoundPlayer::StopAll() { - for (auto& pair : sound_cache_) + for (auto& sound : sound_list_) { - pair.second->Stop(); + sound->Stop(); } } -void SoundPlayer::ReleaseSound(size_t id) +void SoundPlayer::OnEnd(Sound* sound) { - sound_cache_.erase(id); + // remove callback + RemoveCallback(sound); + + // remove sound after stopped + auto iter = std::find(sound_list_.begin(), sound_list_.end(), sound); + if (iter != sound_list_.end()) + { + trash_.push_back(*iter); + sound_list_.erase(iter); + } + + // clear trash in main thread + Application::GetInstance().PerformInMainThread(std::bind(&SoundPlayer::ClearTrash, this)); } -void SoundPlayer::ClearCache() +float SoundPlayer::OnVolumeChanged(Sound* sound, float volume) { - sound_cache_.clear(); + return volume * volume_; } + +void SoundPlayer::SetCallback(Sound* sound) +{ + // add callback if not exists + auto& cbs = sound->GetCallbacks(); + auto iter = std::find_if(cbs.begin(), cbs.end(), [this](const SoundCallbackPtr& ptr) { return ptr.Get() == this; }); + if (iter == cbs.end()) + { + sound->AddCallback(this); + } + sound->ResetVolume(); +} + +void SoundPlayer::RemoveCallback(Sound* sound) +{ + auto& cbs = sound->GetCallbacks(); + auto iter = std::find_if(cbs.begin(), cbs.end(), [this](const SoundCallbackPtr& ptr) { return ptr.Get() == this; }); + if (iter != cbs.end()) + { + *iter = nullptr; // will be removed by sound + } +} + +void SoundPlayer::ClearTrash() +{ + trash_.clear(); +} + } // namespace audio } // namespace kiwano diff --git a/src/kiwano-audio/SoundPlayer.h b/src/kiwano-audio/SoundPlayer.h index 1f9acf79..66e53aa7 100644 --- a/src/kiwano-audio/SoundPlayer.h +++ b/src/kiwano-audio/SoundPlayer.h @@ -20,7 +20,6 @@ #pragma once #include -#include namespace kiwano { @@ -37,78 +36,34 @@ KGE_DECLARE_SMART_PTR(SoundPlayer); * \~chinese * @brief 音频播放器 */ -class KGE_API SoundPlayer : public ObjectBase +class KGE_API SoundPlayer : public SoundCallback { public: - using SoundMap = Map; + using SoundList = List; SoundPlayer(); ~SoundPlayer(); /// \~chinese - /// @brief 加载本地音频文件 - /// @param file_path 本地音频文件路径 - /// @return 音频标识符 - size_t Load(const String& file_path); - - /// \~chinese - /// @brief 加载音频资源 - /// @param res 音频资源 - /// @return 音频标识符 - size_t Load(const Resource& res); + /// @brief 播放音频 + /// @param sound 音频 + /// @param loop_count 播放循环次数,设置 -1 为循环播放 + void Play(SoundPtr sound, int loop_count = 0); /// \~chinese /// @brief 播放音频 - /// @param id 音频标识符 - /// @param loop_count 播放循环次数,设置 -1 为循环播放 - void Play(size_t id, int loop_count = 0); - - /// \~chinese - /// @brief 暂停音频 - /// @param id 音频标识符 - void Pause(size_t id); - - /// \~chinese - /// @brief 继续播放音频 - /// @param id 音频标识符 - void Resume(size_t id); - - /// \~chinese - /// @brief 停止音频 - /// @param id 音频标识符 - void Stop(size_t id); - - /// \~chinese - /// @brief 获取音频播放状态 - /// @param id 音频标识符 - bool IsPlaying(size_t id); - - /// \~chinese - /// @brief 获取音量 - float GetVolume() const; - - /// \~chinese - /// @brief 设置音量 - /// @param volume 音量大小,1.0 为原始音量, 大于 1 为放大音量, 0 为最小音量 - void SetVolume(float volume); - - /// \~chinese - /// @brief 获取本地音频文件id /// @param file_path 本地音频文件路径 - /// @return 音频标识符 - size_t GetId(const String& file_path) const; + /// @param loop_count 播放循环次数,设置 -1 为循环播放 + /// @param callbacks 注册回调 + SoundPtr Play(const String& file_path, int loop_count = 0, std::initializer_list callbacks = {}); /// \~chinese - /// @brief 获取音频资源id + /// @brief 播放音频 /// @param res 音频资源 - /// @return 音频标识符 - size_t GetId(const Resource& res) const; - - /// \~chinese - /// @brief 获取音乐对象 - /// @param id 音频标识符 - SoundPtr GetSound(size_t id) const; + /// @param loop_count 播放循环次数,设置 -1 为循环播放 + /// @param callbacks 注册回调 + SoundPtr Play(const Resource& res, int loop_count = 0, std::initializer_list callbacks = {}); /// \~chinese /// @brief 暂停所有音频 @@ -123,29 +78,41 @@ public: void StopAll(); /// \~chinese - /// @brief 释放音乐对象缓存 - /// @param id 音频标识符 - void ReleaseSound(size_t id); + /// @brief 获取正在播放的音频列表 + const SoundList& GetPlayingList() const; /// \~chinese - /// @brief 获取缓存 - const SoundMap& GetCache() const; + /// @brief 获取音量 + float GetVolume() const; /// \~chinese - /// @brief 清除缓存 - void ClearCache(); + /// @brief 设置音量 + /// @param volume 音量大小,1.0 为原始音量, 大于 1 为放大音量, 0 为最小音量 + void SetVolume(float volume); -private: - float volume_; +public: + void OnEnd(Sound* sound) override; - SoundMap sound_cache_; + float OnVolumeChanged(Sound* sound, float volume) override; + +protected: + void SetCallback(Sound* sound); + + void RemoveCallback(Sound* sound); + + void ClearTrash(); + +protected: + float volume_; + SoundList sound_list_; + SoundList trash_; }; /** @} */ -inline const SoundPlayer::SoundMap& SoundPlayer::GetCache() const +inline const SoundPlayer::SoundList& SoundPlayer::GetPlayingList() const { - return sound_cache_; + return sound_list_; } } // namespace audio