update Sound

This commit is contained in:
Nomango 2019-04-11 16:00:57 +08:00 committed by Nomango
parent b49d583fd5
commit 2ab6a7bd29
13 changed files with 138 additions and 359 deletions

View File

@ -9,10 +9,9 @@
<ClInclude Include="kiwano-network.h" /> <ClInclude Include="kiwano-network.h" />
<ClInclude Include="audio\audio-modules.h" /> <ClInclude Include="audio\audio-modules.h" />
<ClInclude Include="audio\audio.h" /> <ClInclude Include="audio\audio.h" />
<ClInclude Include="audio\Music.h" /> <ClInclude Include="audio\Sound.h" />
<ClInclude Include="audio\Player.h" /> <ClInclude Include="audio\Player.h" />
<ClInclude Include="audio\Transcoder.h" /> <ClInclude Include="audio\Transcoder.h" />
<ClInclude Include="audio\Voice.h" />
<ClInclude Include="imgui\ImGuiLayer.h" /> <ClInclude Include="imgui\ImGuiLayer.h" />
<ClInclude Include="imgui\ImGuiView.h" /> <ClInclude Include="imgui\ImGuiView.h" />
<ClInclude Include="imgui\imgui_impl_dx11.h" /> <ClInclude Include="imgui\imgui_impl_dx11.h" />
@ -120,10 +119,9 @@
<ClCompile Include="2d\Transition.cpp" /> <ClCompile Include="2d\Transition.cpp" />
<ClCompile Include="audio\audio-modules.cpp" /> <ClCompile Include="audio\audio-modules.cpp" />
<ClCompile Include="audio\audio.cpp" /> <ClCompile Include="audio\audio.cpp" />
<ClCompile Include="audio\Music.cpp" /> <ClCompile Include="audio\Sound.cpp" />
<ClCompile Include="audio\Player.cpp" /> <ClCompile Include="audio\Player.cpp" />
<ClCompile Include="audio\Transcoder.cpp" /> <ClCompile Include="audio\Transcoder.cpp" />
<ClCompile Include="audio\Voice.cpp" />
<ClCompile Include="base\AsyncTask.cpp" /> <ClCompile Include="base\AsyncTask.cpp" />
<ClCompile Include="base\EventDispatcher.cpp" /> <ClCompile Include="base\EventDispatcher.cpp" />
<ClCompile Include="base\EventListener.cpp" /> <ClCompile Include="base\EventListener.cpp" />

View File

@ -267,18 +267,12 @@
<ClInclude Include="audio\audio-modules.h"> <ClInclude Include="audio\audio-modules.h">
<Filter>audio</Filter> <Filter>audio</Filter>
</ClInclude> </ClInclude>
<ClInclude Include="audio\Music.h">
<Filter>audio</Filter>
</ClInclude>
<ClInclude Include="audio\Player.h"> <ClInclude Include="audio\Player.h">
<Filter>audio</Filter> <Filter>audio</Filter>
</ClInclude> </ClInclude>
<ClInclude Include="audio\Transcoder.h"> <ClInclude Include="audio\Transcoder.h">
<Filter>audio</Filter> <Filter>audio</Filter>
</ClInclude> </ClInclude>
<ClInclude Include="audio\Voice.h">
<Filter>audio</Filter>
</ClInclude>
<ClInclude Include="network\helper.h"> <ClInclude Include="network\helper.h">
<Filter>network</Filter> <Filter>network</Filter>
</ClInclude> </ClInclude>
@ -321,6 +315,9 @@
<ClInclude Include="third-party\ImGui\imstb_truetype.h"> <ClInclude Include="third-party\ImGui\imstb_truetype.h">
<Filter>third-party\ImGui</Filter> <Filter>third-party\ImGui</Filter>
</ClInclude> </ClInclude>
<ClInclude Include="audio\Sound.h">
<Filter>audio</Filter>
</ClInclude>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ClCompile Include="ui\Button.cpp"> <ClCompile Include="ui\Button.cpp">
@ -455,18 +452,12 @@
<ClCompile Include="audio\audio-modules.cpp"> <ClCompile Include="audio\audio-modules.cpp">
<Filter>audio</Filter> <Filter>audio</Filter>
</ClCompile> </ClCompile>
<ClCompile Include="audio\Music.cpp">
<Filter>audio</Filter>
</ClCompile>
<ClCompile Include="audio\Player.cpp"> <ClCompile Include="audio\Player.cpp">
<Filter>audio</Filter> <Filter>audio</Filter>
</ClCompile> </ClCompile>
<ClCompile Include="audio\Transcoder.cpp"> <ClCompile Include="audio\Transcoder.cpp">
<Filter>audio</Filter> <Filter>audio</Filter>
</ClCompile> </ClCompile>
<ClCompile Include="audio\Voice.cpp">
<Filter>audio</Filter>
</ClCompile>
<ClCompile Include="network\HttpClient.cpp"> <ClCompile Include="network\HttpClient.cpp">
<Filter>network</Filter> <Filter>network</Filter>
</ClCompile> </ClCompile>
@ -491,5 +482,8 @@
<ClCompile Include="third-party\ImGui\imgui_widgets.cpp"> <ClCompile Include="third-party\ImGui\imgui_widgets.cpp">
<Filter>third-party\ImGui</Filter> <Filter>third-party\ImGui</Filter>
</ClCompile> </ClCompile>
<ClCompile Include="audio\Sound.cpp">
<Filter>audio</Filter>
</ClCompile>
</ItemGroup> </ItemGroup>
</Project> </Project>

View File

@ -36,63 +36,59 @@ namespace kiwano
bool Player::Load(Resource const& res) bool Player::Load(Resource const& res)
{ {
size_t hash_code = res.GetHashCode(); 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; 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_); sound->SetVolume(volume_);
musics_cache_.insert(std::make_pair(hash_code, music)); sound_cache_.insert(std::make_pair(hash_code, sound));
return true; return true;
} }
} }
return false; return false;
} }
bool Player::Play(Resource const& res, int loop_count) void Player::Play(Resource const& res, int loop_count)
{ {
if (Load(res)) if (Load(res))
{ {
size_t hash_code = res.GetHashCode(); size_t hash_code = res.GetHashCode();
auto music = musics_cache_[hash_code]; if (sound_cache_.end() != sound_cache_.find(hash_code))
if (music->Play(loop_count)) sound_cache_[hash_code]->Play(loop_count);
{
return true;
} }
} }
return false;
}
void Player::Pause(Resource const& res) void Player::Pause(Resource const& res)
{ {
size_t hash_code = res.GetHashCode(); size_t hash_code = res.GetHashCode();
if (musics_cache_.end() != musics_cache_.find(hash_code)) if (sound_cache_.end() != sound_cache_.find(hash_code))
musics_cache_[hash_code]->Pause(); sound_cache_[hash_code]->Pause();
} }
void Player::Resume(Resource const& res) void Player::Resume(Resource const& res)
{ {
size_t hash_code = res.GetHashCode(); size_t hash_code = res.GetHashCode();
if (musics_cache_.end() != musics_cache_.find(hash_code)) if (sound_cache_.end() != sound_cache_.find(hash_code))
musics_cache_[hash_code]->Resume(); sound_cache_[hash_code]->Resume();
} }
void Player::Stop(Resource const& res) void Player::Stop(Resource const& res)
{ {
size_t hash_code = res.GetHashCode(); size_t hash_code = res.GetHashCode();
if (musics_cache_.end() != musics_cache_.find(hash_code)) if (sound_cache_.end() != sound_cache_.find(hash_code))
musics_cache_[hash_code]->Stop(); sound_cache_[hash_code]->Stop();
} }
bool Player::IsPlaying(Resource const& res) bool Player::IsPlaying(Resource const& res)
{ {
size_t hash_code = res.GetHashCode(); 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 musics_cache_[hash_code]->IsPlaying(); return sound_cache_[hash_code]->IsPlaying();
return false; return false;
} }
@ -104,7 +100,7 @@ namespace kiwano
void Player::SetVolume(float volume) void Player::SetVolume(float volume)
{ {
volume_ = std::min(std::max(volume, -224.f), 224.f); 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_); pair.second->SetVolume(volume_);
} }
@ -112,7 +108,7 @@ namespace kiwano
void Player::PauseAll() void Player::PauseAll()
{ {
for (const auto& pair : musics_cache_) for (const auto& pair : sound_cache_)
{ {
pair.second->Pause(); pair.second->Pause();
} }
@ -120,7 +116,7 @@ namespace kiwano
void Player::ResumeAll() void Player::ResumeAll()
{ {
for (const auto& pair : musics_cache_) for (const auto& pair : sound_cache_)
{ {
pair.second->Resume(); pair.second->Resume();
} }
@ -128,7 +124,7 @@ namespace kiwano
void Player::StopAll() void Player::StopAll()
{ {
for (const auto& pair : musics_cache_) for (const auto& pair : sound_cache_)
{ {
pair.second->Stop(); pair.second->Stop();
} }
@ -136,6 +132,6 @@ namespace kiwano
void Player::ClearCache() void Player::ClearCache()
{ {
musics_cache_.clear(); sound_cache_.clear();
} }
} }

View File

@ -28,7 +28,7 @@ namespace kiwano
class KGE_API Player class KGE_API Player
: protected Object : protected Object
{ {
using MusicMap = Map<size_t, MusicPtr>; using MusicMap = Map<size_t, SoundPtr>;
public: public:
Player(); Player();
@ -41,7 +41,7 @@ namespace kiwano
); );
// 播放音乐 // 播放音乐
bool Play( void Play(
Resource const& res, /* 音乐资源 */ Resource const& res, /* 音乐资源 */
int loop_count = 0 /* 播放循环次数 (-1 为循环播放) */ int loop_count = 0 /* 播放循环次数 (-1 为循环播放) */
); );
@ -88,6 +88,6 @@ namespace kiwano
protected: protected:
float volume_; float volume_;
MusicMap musics_cache_; MusicMap sound_cache_;
}; };
} }

View File

@ -19,32 +19,32 @@
// THE SOFTWARE. // THE SOFTWARE.
#include "../kiwano-audio.h" #include "../kiwano-audio.h"
#include "Music.h" #include "Sound.h"
#include "Transcoder.h" #include "Transcoder.h"
namespace kiwano namespace kiwano
{ {
Music::Music() Sound::Sound()
: opened_(false) : opened_(false)
, playing_(false) , playing_(false)
, wave_data_(nullptr)
, size_(0) , size_(0)
, wave_data_(nullptr)
, voice_(nullptr) , voice_(nullptr)
{ {
} }
Music::Music(Resource const& res) Sound::Sound(Resource const& res)
: Music() : Sound()
{ {
Load(res); Load(res);
} }
Music::~Music() Sound::~Sound()
{ {
Close(); Close();
} }
bool Music::Load(Resource const& res) bool Sound::Load(Resource const& res)
{ {
if (opened_) if (opened_)
{ {
@ -74,7 +74,7 @@ namespace kiwano
return false; return false;
} }
hr = Audio::Instance().CreateVoice(voice_, transcoder.GetWaveFormatEx()); hr = Audio::Instance().CreateVoice(&voice_, transcoder.GetWaveFormatEx());
if (FAILED(hr)) if (FAILED(hr))
{ {
if (wave_data_) if (wave_data_)
@ -90,56 +90,86 @@ namespace kiwano
return true; return true;
} }
bool Music::Play(int loop_count) void Sound::Play(int loop_count)
{ {
if (!opened_) if (!opened_)
{ {
KGE_ERROR_LOG(L"Music must be opened first!"); KGE_ERROR_LOG(L"Sound must be opened first!");
return false; return;
} }
UINT32 buffers_queued = 0; KGE_ASSERT(voice_ != nullptr && "IXAudio2SourceVoice* is NULL");
voice_.GetBuffersQueued(&buffers_queued);
if (buffers_queued) // if sound stream is not empty, stop() will clear it
XAUDIO2_VOICE_STATE state;
voice_->GetState(&state);
if (state.BuffersQueued)
Stop(); Stop();
if (loop_count < 0) // clamp loop count
loop_count = XAUDIO2_LOOP_INFINITE; loop_count = (loop_count < 0) ? XAUDIO2_LOOP_INFINITE : std::min(loop_count, XAUDIO2_LOOP_INFINITE - 1);
else
loop_count = 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<UINT32>(loop_count);
HRESULT hr = voice_->SubmitSourceBuffer(&buffer);
if (SUCCEEDED(hr))
{
hr = voice_->Start();
}
HRESULT hr = voice_.Play(wave_data_, size_, static_cast<UINT32>(loop_count));
if (FAILED(hr)) if (FAILED(hr))
{ {
KGE_ERROR_LOG(L"Submitting source buffer failed with HRESULT of %08X", hr); KGE_ERROR_LOG(L"Submitting source buffer failed with HRESULT of %08X", hr);
} }
playing_ = SUCCEEDED(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; 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; 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; playing_ = false;
} }
void Music::Close() void Sound::Close()
{ {
voice_.Destroy(); if (voice_)
{
voice_->Stop();
voice_->FlushSourceBuffers();
voice_->DestroyVoice();
voice_ = nullptr;
}
if (wave_data_) if (wave_data_)
{ {
@ -151,27 +181,37 @@ namespace kiwano
playing_ = false; playing_ = false;
} }
bool Music::IsPlaying() const bool Sound::IsPlaying() const
{ {
if (opened_) if (opened_)
{ {
UINT32 buffers_queued = 0; if (!voice_)
voice_.GetBuffersQueued(&buffers_queued); return false;
XAUDIO2_VOICE_STATE state;
voice_->GetState(&state);
UINT32 buffers_queued = state.BuffersQueued;
if (buffers_queued && playing_) if (buffers_queued && playing_)
return true; return true;
} }
return false; return false;
} }
float Music::GetVolume() const float Sound::GetVolume() const
{ {
float volume = 0.f; KGE_ASSERT(voice_ != nullptr && "IXAudio2SourceVoice* is NULL");
voice_.GetVolume(&volume);
float volume = 0.0f;
voice_->GetVolume(&volume);
return 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);
} }
} }

View File

@ -19,24 +19,24 @@
// THE SOFTWARE. // THE SOFTWARE.
#pragma once #pragma once
#include "Voice.h" #include <xaudio2.h>
namespace kiwano namespace kiwano
{ {
KGE_DECLARE_SMART_PTR(Music); KGE_DECLARE_SMART_PTR(Sound);
// 音乐对象 // 音乐对象
class KGE_API Music class KGE_API Sound
: public virtual Object : public virtual Object
{ {
public: public:
Music(); Sound();
Music( Sound(
Resource const& res /* 音乐资源 */ Resource const& res /* 音乐资源 */
); );
virtual ~Music(); virtual ~Sound();
// 打开音乐资源 // 打开音乐资源
bool Load( bool Load(
@ -44,7 +44,7 @@ namespace kiwano
); );
// 播放 // 播放
bool Play( void Play(
int loop_count = 0 /* 播放循环次数 (-1 为循环播放) */ int loop_count = 0 /* 播放循环次数 (-1 为循环播放) */
); );
@ -67,7 +67,7 @@ namespace kiwano
float GetVolume() const; float GetVolume() const;
// 设置音量 // 设置音量
bool SetVolume( void SetVolume(
float volume /* 1 为原始音量, 大于 1 为放大音量, 0 为最小音量 */ float volume /* 1 为原始音量, 大于 1 为放大音量, 0 为最小音量 */
); );
@ -76,6 +76,6 @@ namespace kiwano
bool playing_; bool playing_;
UINT32 size_; UINT32 size_;
BYTE* wave_data_; BYTE* wave_data_;
Voice voice_; IXAudio2SourceVoice* voice_;
}; };
} }

View File

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

View File

@ -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 <xaudio2.h>
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_;
};
}

View File

@ -57,8 +57,6 @@ namespace kiwano
{ {
KGE_LOG(L"Destroying audio resources"); KGE_LOG(L"Destroying audio resources");
ClearVoiceCache();
if (mastering_voice_) if (mastering_voice_)
{ {
mastering_voice_->DestroyVoice(); mastering_voice_->DestroyVoice();
@ -74,34 +72,17 @@ namespace kiwano
modules::MediaFoundation::Get().MFShutdown(); modules::MediaFoundation::Get().MFShutdown();
} }
HRESULT Audio::CreateVoice(Voice& voice, const WAVEFORMATEX* wfx) HRESULT Audio::CreateVoice(IXAudio2SourceVoice** voice, const WAVEFORMATEX* wfx)
{ {
HRESULT hr; if (voice == nullptr)
IXAudio2SourceVoice* source_voice;
hr = x_audio2_->CreateSourceVoice(&source_voice, wfx, 0, XAUDIO2_DEFAULT_FREQ_RATIO);
if (SUCCEEDED(hr))
{ {
voice.SetSourceVoice(source_voice); return E_UNEXPECTED;
voice_cache_.insert(&voice);
} }
HRESULT hr = x_audio2_->CreateSourceVoice(voice, wfx, 0, XAUDIO2_DEFAULT_FREQ_RATIO);
return hr; 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() void Audio::Open()
{ {
x_audio2_->StartEngine(); x_audio2_->StartEngine();

View File

@ -19,7 +19,6 @@
// THE SOFTWARE. // THE SOFTWARE.
#pragma once #pragma once
#include "Voice.h"
namespace kiwano namespace kiwano
{ {
@ -29,8 +28,6 @@ namespace kiwano
{ {
KGE_DECLARE_SINGLETON(Audio); KGE_DECLARE_SINGLETON(Audio);
using VoiceMap = UnorderedSet<Voice*>;
public: public:
void SetupComponent(Application*) override; void SetupComponent(Application*) override;
@ -43,23 +40,16 @@ namespace kiwano
void Close(); void Close();
HRESULT CreateVoice( HRESULT CreateVoice(
Voice& voice, IXAudio2SourceVoice** voice,
const WAVEFORMATEX* wfx const WAVEFORMATEX* wfx
); );
void DeleteVoice(
Voice* voice
);
void ClearVoiceCache();
protected: protected:
Audio(); Audio();
~Audio(); ~Audio();
protected: protected:
VoiceMap voice_cache_;
IXAudio2* x_audio2_; IXAudio2* x_audio2_;
IXAudio2MasteringVoice* mastering_voice_; IXAudio2MasteringVoice* mastering_voice_;
}; };

View File

@ -22,5 +22,5 @@
#include "kiwano.h" #include "kiwano.h"
#include "audio/audio.h" #include "audio/audio.h"
#include "audio/Music.h" #include "audio/Sound.h"
#include "audio/Player.h" #include "audio/Player.h"

View File

@ -6,7 +6,7 @@
class Demo3 class Demo3
: public Scene : public Scene
{ {
MusicPtr music; // ÒôÀÖ¶ÔÏó SoundPtr bgmusic; // ÒôÀÖ¶ÔÏó
TextPtr volume_text; // 音量文字 TextPtr volume_text; // 音量文字
TextPtr state_text; // 播放状态文字 TextPtr state_text; // 播放状态文字
@ -19,17 +19,17 @@ public:
Demo3() Demo3()
{ {
// 加载音乐 // 加载音乐
music = new Music; bgmusic = new Sound;
if (!music->Load(L"res/splash.mp3")) if (!bgmusic->Load(L"res/splash.mp3"))
{ {
music = nullptr; bgmusic = nullptr;
TextPtr err = new Text(L"音频文件加载失败"); TextPtr err = new Text(L"音频文件加载失败");
this->AddChild(err); this->AddChild(err);
} }
// 播放音乐(参数用来设置播放循环次数,-1 表示循环播放) // 播放音乐(参数用来设置播放循环次数,-1 表示循环播放)
music->Play(-1); bgmusic->Play(-1);
// 创建说明文字 // 创建说明文字
TextPtr intro_text = new Text(L"按上下键调整音量\n按空格键暂停或继续"); TextPtr intro_text = new Text(L"按上下键调整音量\n按空格键暂停或继续");
@ -51,12 +51,12 @@ public:
void OnUpdate(Duration dt) override void OnUpdate(Duration dt) override
{ {
if (music == nullptr) if (bgmusic == nullptr)
return; return;
// 获取音量和播放状态 // 获取音量和播放状态
float volume = music->GetVolume(); float volume = bgmusic->GetVolume();
bool playing = music->IsPlaying(); bool playing = bgmusic->IsPlaying();
// 修改文本 // 修改文本
volume_text->SetText(L"当前音量:" + std::to_wstring(volume)); volume_text->SetText(L"当前音量:" + std::to_wstring(volume));
@ -68,17 +68,17 @@ public:
// 按空格键暂停或继续 // 按空格键暂停或继续
if (input.WasPressed(KeyCode::Space)) if (input.WasPressed(KeyCode::Space))
{ {
music->IsPlaying() ? music->Pause() : music->Resume(); bgmusic->IsPlaying() ? bgmusic->Pause() : bgmusic->Resume();
} }
// 按上下键调整音量 // 按上下键调整音量
if (input.WasPressed(KeyCode::Up)) if (input.WasPressed(KeyCode::Up))
{ {
music->SetVolume(volume + 0.1f); bgmusic->SetVolume(volume + 0.1f);
} }
else if (input.WasPressed(KeyCode::Down)) else if (input.WasPressed(KeyCode::Down))
{ {
music->SetVolume(volume - 0.1f); bgmusic->SetVolume(volume - 0.1f);
} }
} }
}; };

View File

@ -53,7 +53,7 @@ public:
s_CurrIndex = index; s_CurrIndex = index;
String title = s_Demos[index].title; String title = s_Demos[index].title;
GetWindow()->SetTitle(title); GetWindow()->SetTitle(L"KiwanoʾÀý³ÌÐò - " + title);
ScenePtr scene = s_Demos[index].Create(); ScenePtr scene = s_Demos[index].Create();
EnterScene(scene); EnterScene(scene);