diff --git a/Kiwano/Kiwano.vcxproj b/Kiwano/Kiwano.vcxproj index f4e698c3..d9a53e54 100644 --- a/Kiwano/Kiwano.vcxproj +++ b/Kiwano/Kiwano.vcxproj @@ -9,10 +9,9 @@ - + - @@ -120,10 +119,9 @@ - + - diff --git a/Kiwano/Kiwano.vcxproj.filters b/Kiwano/Kiwano.vcxproj.filters index 3cf3f207..9588a803 100644 --- a/Kiwano/Kiwano.vcxproj.filters +++ b/Kiwano/Kiwano.vcxproj.filters @@ -267,18 +267,12 @@ audio - - audio - audio audio - - audio - network @@ -321,6 +315,9 @@ third-party\ImGui + + audio + @@ -455,18 +452,12 @@ audio - - audio - audio audio - - audio - network @@ -491,5 +482,8 @@ third-party\ImGui + + audio + \ No newline at end of file diff --git a/Kiwano/audio/Player.cpp b/Kiwano/audio/Player.cpp index f056ca8b..096f603c 100644 --- a/Kiwano/audio/Player.cpp +++ b/Kiwano/audio/Player.cpp @@ -36,63 +36,59 @@ namespace kiwano bool Player::Load(Resource const& res) { size_t hash_code = res.GetHashCode(); - if (musics_cache_.end() != musics_cache_.find(hash_code)) + if (sound_cache_.end() != sound_cache_.find(hash_code)) return true; - MusicPtr music = new (std::nothrow) Music(); + SoundPtr sound = new (std::nothrow) Sound(); - if (music) + if (sound) { - if (music->Load(res)) + if (sound->Load(res)) { - music->SetVolume(volume_); - musics_cache_.insert(std::make_pair(hash_code, music)); + sound->SetVolume(volume_); + sound_cache_.insert(std::make_pair(hash_code, sound)); return true; } } return false; } - bool Player::Play(Resource const& res, int loop_count) + void Player::Play(Resource const& res, int loop_count) { if (Load(res)) { size_t hash_code = res.GetHashCode(); - auto music = musics_cache_[hash_code]; - if (music->Play(loop_count)) - { - return true; - } + if (sound_cache_.end() != sound_cache_.find(hash_code)) + sound_cache_[hash_code]->Play(loop_count); } - return false; } void Player::Pause(Resource const& res) { size_t hash_code = res.GetHashCode(); - if (musics_cache_.end() != musics_cache_.find(hash_code)) - musics_cache_[hash_code]->Pause(); + if (sound_cache_.end() != sound_cache_.find(hash_code)) + sound_cache_[hash_code]->Pause(); } void Player::Resume(Resource const& res) { size_t hash_code = res.GetHashCode(); - if (musics_cache_.end() != musics_cache_.find(hash_code)) - musics_cache_[hash_code]->Resume(); + if (sound_cache_.end() != sound_cache_.find(hash_code)) + sound_cache_[hash_code]->Resume(); } void Player::Stop(Resource const& res) { size_t hash_code = res.GetHashCode(); - if (musics_cache_.end() != musics_cache_.find(hash_code)) - musics_cache_[hash_code]->Stop(); + if (sound_cache_.end() != sound_cache_.find(hash_code)) + sound_cache_[hash_code]->Stop(); } bool Player::IsPlaying(Resource const& res) { size_t hash_code = res.GetHashCode(); - if (musics_cache_.end() != musics_cache_.find(hash_code)) - return musics_cache_[hash_code]->IsPlaying(); + if (sound_cache_.end() != sound_cache_.find(hash_code)) + return sound_cache_[hash_code]->IsPlaying(); return false; } @@ -104,7 +100,7 @@ namespace kiwano void Player::SetVolume(float volume) { volume_ = std::min(std::max(volume, -224.f), 224.f); - for (const auto& pair : musics_cache_) + for (const auto& pair : sound_cache_) { pair.second->SetVolume(volume_); } @@ -112,7 +108,7 @@ namespace kiwano void Player::PauseAll() { - for (const auto& pair : musics_cache_) + for (const auto& pair : sound_cache_) { pair.second->Pause(); } @@ -120,7 +116,7 @@ namespace kiwano void Player::ResumeAll() { - for (const auto& pair : musics_cache_) + for (const auto& pair : sound_cache_) { pair.second->Resume(); } @@ -128,7 +124,7 @@ namespace kiwano void Player::StopAll() { - for (const auto& pair : musics_cache_) + for (const auto& pair : sound_cache_) { pair.second->Stop(); } @@ -136,6 +132,6 @@ namespace kiwano void Player::ClearCache() { - musics_cache_.clear(); + sound_cache_.clear(); } } \ No newline at end of file diff --git a/Kiwano/audio/Player.h b/Kiwano/audio/Player.h index a22da56b..3c157c82 100644 --- a/Kiwano/audio/Player.h +++ b/Kiwano/audio/Player.h @@ -28,7 +28,7 @@ namespace kiwano class KGE_API Player : protected Object { - using MusicMap = Map; + using MusicMap = Map; public: Player(); @@ -41,7 +41,7 @@ namespace kiwano ); // 播放音乐 - bool Play( + void Play( Resource const& res, /* 音乐资源 */ int loop_count = 0 /* 播放循环次数 (-1 为循环播放) */ ); @@ -88,6 +88,6 @@ namespace kiwano protected: float volume_; - MusicMap musics_cache_; + MusicMap sound_cache_; }; } diff --git a/Kiwano/audio/Music.cpp b/Kiwano/audio/Sound.cpp similarity index 55% rename from Kiwano/audio/Music.cpp rename to Kiwano/audio/Sound.cpp index 11ffac6c..903b49a7 100644 --- a/Kiwano/audio/Music.cpp +++ b/Kiwano/audio/Sound.cpp @@ -19,32 +19,32 @@ // THE SOFTWARE. #include "../kiwano-audio.h" -#include "Music.h" +#include "Sound.h" #include "Transcoder.h" namespace kiwano { - Music::Music() + Sound::Sound() : opened_(false) , playing_(false) - , wave_data_(nullptr) , size_(0) + , wave_data_(nullptr) , voice_(nullptr) { } - Music::Music(Resource const& res) - : Music() + Sound::Sound(Resource const& res) + : Sound() { Load(res); } - Music::~Music() + Sound::~Sound() { Close(); } - bool Music::Load(Resource const& res) + bool Sound::Load(Resource const& res) { if (opened_) { @@ -74,7 +74,7 @@ namespace kiwano return false; } - hr = Audio::Instance().CreateVoice(voice_, transcoder.GetWaveFormatEx()); + hr = Audio::Instance().CreateVoice(&voice_, transcoder.GetWaveFormatEx()); if (FAILED(hr)) { if (wave_data_) @@ -90,56 +90,86 @@ namespace kiwano return true; } - bool Music::Play(int loop_count) + void Sound::Play(int loop_count) { if (!opened_) { - KGE_ERROR_LOG(L"Music must be opened first!"); - return false; + KGE_ERROR_LOG(L"Sound must be opened first!"); + return; } - UINT32 buffers_queued = 0; - voice_.GetBuffersQueued(&buffers_queued); - if (buffers_queued) + KGE_ASSERT(voice_ != nullptr && "IXAudio2SourceVoice* is NULL"); + + // if sound stream is not empty, stop() will clear it + XAUDIO2_VOICE_STATE state; + voice_->GetState(&state); + if (state.BuffersQueued) Stop(); - if (loop_count < 0) - loop_count = XAUDIO2_LOOP_INFINITE; - else - loop_count = std::min(loop_count, XAUDIO2_LOOP_INFINITE - 1); + // clamp loop count + loop_count = (loop_count < 0) ? XAUDIO2_LOOP_INFINITE : std::min(loop_count, XAUDIO2_LOOP_INFINITE - 1); + + XAUDIO2_BUFFER buffer = { 0 }; + buffer.pAudioData = wave_data_; + buffer.Flags = XAUDIO2_END_OF_STREAM; + buffer.AudioBytes = size_; + buffer.LoopCount = static_cast(loop_count); + + HRESULT hr = voice_->SubmitSourceBuffer(&buffer); + if (SUCCEEDED(hr)) + { + hr = voice_->Start(); + } - HRESULT hr = voice_.Play(wave_data_, size_, static_cast(loop_count)); if (FAILED(hr)) { KGE_ERROR_LOG(L"Submitting source buffer failed with HRESULT of %08X", hr); } playing_ = SUCCEEDED(hr); - - return playing_; } - void Music::Pause() + void Sound::Pause() { - if (SUCCEEDED(voice_.Pause())) + KGE_ASSERT(voice_ != nullptr && "IXAudio2SourceVoice* is NULL"); + + if (SUCCEEDED(voice_->Stop())) playing_ = false; } - void Music::Resume() + void Sound::Resume() { - if (SUCCEEDED(voice_.Resume())) + KGE_ASSERT(voice_ != nullptr && "IXAudio2SourceVoice* is NULL"); + + if (SUCCEEDED(voice_->Start())) playing_ = true; } - void Music::Stop() + void Sound::Stop() { - if (SUCCEEDED(voice_.Stop())) + KGE_ASSERT(voice_ != nullptr && "IXAudio2SourceVoice* is NULL"); + + HRESULT hr = voice_->Stop(); + + if (SUCCEEDED(hr)) + hr = voice_->ExitLoop(); + + if (SUCCEEDED(hr)) + hr = voice_->FlushSourceBuffers(); + + if (SUCCEEDED(hr)) playing_ = false; } - void Music::Close() + void Sound::Close() { - voice_.Destroy(); + if (voice_) + { + voice_->Stop(); + voice_->FlushSourceBuffers(); + voice_->DestroyVoice(); + voice_ = nullptr; + } if (wave_data_) { @@ -151,27 +181,37 @@ namespace kiwano playing_ = false; } - bool Music::IsPlaying() const + bool Sound::IsPlaying() const { if (opened_) { - UINT32 buffers_queued = 0; - voice_.GetBuffersQueued(&buffers_queued); + if (!voice_) + return false; + + XAUDIO2_VOICE_STATE state; + voice_->GetState(&state); + UINT32 buffers_queued = state.BuffersQueued; + if (buffers_queued && playing_) return true; } return false; } - float Music::GetVolume() const + float Sound::GetVolume() const { - float volume = 0.f; - voice_.GetVolume(&volume); + KGE_ASSERT(voice_ != nullptr && "IXAudio2SourceVoice* is NULL"); + + float volume = 0.0f; + voice_->GetVolume(&volume); return volume; } - bool Music::SetVolume(float volume) + void Sound::SetVolume(float volume) { - return SUCCEEDED(voice_.SetVolume(volume)); + KGE_ASSERT(voice_ != nullptr && "IXAudio2SourceVoice* is NULL"); + + volume = std::min(std::max(volume, -224.f), 224.f); + voice_->SetVolume(volume); } } \ No newline at end of file diff --git a/Kiwano/audio/Music.h b/Kiwano/audio/Sound.h similarity index 91% rename from Kiwano/audio/Music.h rename to Kiwano/audio/Sound.h index 98acdb85..f2c0dd97 100644 --- a/Kiwano/audio/Music.h +++ b/Kiwano/audio/Sound.h @@ -19,24 +19,24 @@ // THE SOFTWARE. #pragma once -#include "Voice.h" +#include namespace kiwano { - KGE_DECLARE_SMART_PTR(Music); + KGE_DECLARE_SMART_PTR(Sound); // 音乐对象 - class KGE_API Music + class KGE_API Sound : public virtual Object { public: - Music(); + Sound(); - Music( + Sound( Resource const& res /* 音乐资源 */ ); - virtual ~Music(); + virtual ~Sound(); // 打开音乐资源 bool Load( @@ -44,7 +44,7 @@ namespace kiwano ); // 播放 - bool Play( + void Play( int loop_count = 0 /* 播放循环次数 (-1 为循环播放) */ ); @@ -67,7 +67,7 @@ namespace kiwano float GetVolume() const; // 设置音量 - bool SetVolume( + void SetVolume( float volume /* 1 为原始音量, 大于 1 为放大音量, 0 为最小音量 */ ); @@ -76,6 +76,6 @@ namespace kiwano bool playing_; UINT32 size_; BYTE* wave_data_; - Voice voice_; + IXAudio2SourceVoice* voice_; }; } diff --git a/Kiwano/audio/Voice.cpp b/Kiwano/audio/Voice.cpp deleted file mode 100644 index 29203a8e..00000000 --- a/Kiwano/audio/Voice.cpp +++ /dev/null @@ -1,148 +0,0 @@ -// Copyright (c) 2016-2018 Kiwano - Nomango -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. - -#include "../kiwano-audio.h" -#include "Voice.h" - -namespace kiwano -{ - Voice::Voice() - : source_voice_(nullptr) - { - } - - Voice::Voice(IXAudio2SourceVoice * source_voice) - : source_voice_(source_voice) - { - } - - Voice::~Voice() - { - Destroy(); - - Audio::Instance().DeleteVoice(this); - } - - HRESULT Voice::Play(const BYTE * wave_data, UINT32 data_size, UINT32 loop_count) - { - if (!source_voice_) - return E_UNEXPECTED; - - XAUDIO2_BUFFER buffer = { 0 }; - buffer.pAudioData = wave_data; - buffer.Flags = XAUDIO2_END_OF_STREAM; - buffer.AudioBytes = data_size; - buffer.LoopCount = loop_count; - - HRESULT hr = source_voice_->SubmitSourceBuffer(&buffer); - if (SUCCEEDED(hr)) - { - hr = source_voice_->Start(); - } - return hr; - } - - HRESULT Voice::Pause() - { - if (!source_voice_) - return E_UNEXPECTED; - - return source_voice_->Stop(); - } - - HRESULT Voice::Resume() - { - if (!source_voice_) - return E_UNEXPECTED; - - return source_voice_->Start(); - } - - HRESULT Voice::Stop() - { - if (!source_voice_) - return E_UNEXPECTED; - - HRESULT hr = source_voice_->Stop(); - if (SUCCEEDED(hr)) - { - hr = source_voice_->ExitLoop(); - } - - if (SUCCEEDED(hr)) - { - hr = source_voice_->FlushSourceBuffers(); - } - return hr; - } - - HRESULT Voice::GetVolume(float * volume) const - { - if (!source_voice_) - return E_UNEXPECTED; - - if (volume == nullptr) - return E_POINTER; - - source_voice_->GetVolume(volume); - return S_OK; - } - - HRESULT Voice::SetVolume(float volume) - { - if (!source_voice_) - return E_UNEXPECTED; - - volume = std::min(std::max(volume, -224.f), 224.f); - return source_voice_->SetVolume(volume); - } - - HRESULT Voice::GetBuffersQueued(UINT32 * queued) const - { - if (!source_voice_) - return E_UNEXPECTED; - - if (queued == nullptr) - return E_POINTER; - - XAUDIO2_VOICE_STATE state; - source_voice_->GetState(&state); - *queued = state.BuffersQueued; - return S_OK; - } - - void Voice::Destroy() - { - if (source_voice_) - { - source_voice_->Stop(); - source_voice_->FlushSourceBuffers(); - source_voice_->DestroyVoice(); - source_voice_ = nullptr; - } - } - - void Voice::SetSourceVoice(IXAudio2SourceVoice * source_voice) - { - Destroy(); - source_voice_ = source_voice; - } - -} \ No newline at end of file diff --git a/Kiwano/audio/Voice.h b/Kiwano/audio/Voice.h deleted file mode 100644 index 6df6f98d..00000000 --- a/Kiwano/audio/Voice.h +++ /dev/null @@ -1,72 +0,0 @@ -// Copyright (c) 2016-2018 Kiwano - Nomango -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. - -#pragma once -#include - -namespace kiwano -{ - class KGE_API Voice - : protected Noncopyable - { - public: - Voice(); - - Voice( - IXAudio2SourceVoice* source_voice - ); - - ~Voice(); - - HRESULT Play( - const BYTE* wave_data, - UINT32 data_size, - UINT32 loop_count - ); - - HRESULT Pause(); - - HRESULT Resume(); - - HRESULT Stop(); - - HRESULT GetVolume( - float* volume - ) const; - - HRESULT SetVolume( - float volume - ); - - HRESULT GetBuffersQueued( - UINT32* queued - ) const; - - void Destroy(); - - void SetSourceVoice( - IXAudio2SourceVoice* source_voice - ); - - protected: - IXAudio2SourceVoice* source_voice_; - }; - -} diff --git a/Kiwano/audio/audio.cpp b/Kiwano/audio/audio.cpp index 481890da..9a6a9606 100644 --- a/Kiwano/audio/audio.cpp +++ b/Kiwano/audio/audio.cpp @@ -57,8 +57,6 @@ namespace kiwano { KGE_LOG(L"Destroying audio resources"); - ClearVoiceCache(); - if (mastering_voice_) { mastering_voice_->DestroyVoice(); @@ -74,34 +72,17 @@ namespace kiwano modules::MediaFoundation::Get().MFShutdown(); } - HRESULT Audio::CreateVoice(Voice& voice, const WAVEFORMATEX* wfx) + HRESULT Audio::CreateVoice(IXAudio2SourceVoice** voice, const WAVEFORMATEX* wfx) { - HRESULT hr; - IXAudio2SourceVoice* source_voice; - - hr = x_audio2_->CreateSourceVoice(&source_voice, wfx, 0, XAUDIO2_DEFAULT_FREQ_RATIO); - if (SUCCEEDED(hr)) + if (voice == nullptr) { - voice.SetSourceVoice(source_voice); - voice_cache_.insert(&voice); + return E_UNEXPECTED; } + + HRESULT hr = x_audio2_->CreateSourceVoice(voice, wfx, 0, XAUDIO2_DEFAULT_FREQ_RATIO); return hr; } - void Audio::DeleteVoice(Voice* voice) - { - voice_cache_.erase(voice); - } - - void Audio::ClearVoiceCache() - { - for (auto voice : voice_cache_) - { - voice->Destroy(); - } - voice_cache_.clear(); - } - void Audio::Open() { x_audio2_->StartEngine(); diff --git a/Kiwano/audio/audio.h b/Kiwano/audio/audio.h index 06aac9d5..b31ec309 100644 --- a/Kiwano/audio/audio.h +++ b/Kiwano/audio/audio.h @@ -19,7 +19,6 @@ // THE SOFTWARE. #pragma once -#include "Voice.h" namespace kiwano { @@ -29,8 +28,6 @@ namespace kiwano { KGE_DECLARE_SINGLETON(Audio); - using VoiceMap = UnorderedSet; - public: void SetupComponent(Application*) override; @@ -43,23 +40,16 @@ namespace kiwano void Close(); HRESULT CreateVoice( - Voice& voice, + IXAudio2SourceVoice** voice, const WAVEFORMATEX* wfx ); - void DeleteVoice( - Voice* voice - ); - - void ClearVoiceCache(); - protected: Audio(); ~Audio(); protected: - VoiceMap voice_cache_; IXAudio2* x_audio2_; IXAudio2MasteringVoice* mastering_voice_; }; diff --git a/Kiwano/kiwano-audio.h b/Kiwano/kiwano-audio.h index 539922b8..e5753889 100644 --- a/Kiwano/kiwano-audio.h +++ b/Kiwano/kiwano-audio.h @@ -22,5 +22,5 @@ #include "kiwano.h" #include "audio/audio.h" -#include "audio/Music.h" +#include "audio/Sound.h" #include "audio/Player.h" diff --git a/samples/Samples/Demo3.h b/samples/Samples/Demo3.h index bc41ee6f..139db5e6 100644 --- a/samples/Samples/Demo3.h +++ b/samples/Samples/Demo3.h @@ -6,7 +6,7 @@ class Demo3 : public Scene { - MusicPtr music; // 音乐对象 + SoundPtr bgmusic; // 音乐对象 TextPtr volume_text; // 音量文字 TextPtr state_text; // 播放状态文字 @@ -19,17 +19,17 @@ public: Demo3() { // 加载音乐 - music = new Music; - if (!music->Load(L"res/splash.mp3")) + bgmusic = new Sound; + if (!bgmusic->Load(L"res/splash.mp3")) { - music = nullptr; + bgmusic = nullptr; TextPtr err = new Text(L"音频文件加载失败"); this->AddChild(err); } // 播放音乐(参数用来设置播放循环次数,-1 表示循环播放) - music->Play(-1); + bgmusic->Play(-1); // 创建说明文字 TextPtr intro_text = new Text(L"按上下键调整音量\n按空格键暂停或继续"); @@ -51,12 +51,12 @@ public: void OnUpdate(Duration dt) override { - if (music == nullptr) + if (bgmusic == nullptr) return; // 获取音量和播放状态 - float volume = music->GetVolume(); - bool playing = music->IsPlaying(); + float volume = bgmusic->GetVolume(); + bool playing = bgmusic->IsPlaying(); // 修改文本 volume_text->SetText(L"当前音量:" + std::to_wstring(volume)); @@ -68,17 +68,17 @@ public: // 按空格键暂停或继续 if (input.WasPressed(KeyCode::Space)) { - music->IsPlaying() ? music->Pause() : music->Resume(); + bgmusic->IsPlaying() ? bgmusic->Pause() : bgmusic->Resume(); } // 按上下键调整音量 if (input.WasPressed(KeyCode::Up)) { - music->SetVolume(volume + 0.1f); + bgmusic->SetVolume(volume + 0.1f); } else if (input.WasPressed(KeyCode::Down)) { - music->SetVolume(volume - 0.1f); + bgmusic->SetVolume(volume - 0.1f); } } }; diff --git a/samples/Samples/main.cpp b/samples/Samples/main.cpp index 329f9801..5d0096e9 100644 --- a/samples/Samples/main.cpp +++ b/samples/Samples/main.cpp @@ -53,7 +53,7 @@ public: s_CurrIndex = index; String title = s_Demos[index].title; - GetWindow()->SetTitle(title); + GetWindow()->SetTitle(L"Kiwano示例程序 - " + title); ScenePtr scene = s_Demos[index].Create(); EnterScene(scene);