update Sound
This commit is contained in:
parent
b49d583fd5
commit
2ab6a7bd29
|
|
@ -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" />
|
||||||
|
|
|
||||||
|
|
@ -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>
|
||||||
|
|
@ -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();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -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_;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -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_;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
@ -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;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
@ -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_;
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
@ -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();
|
||||||
|
|
|
||||||
|
|
@ -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_;
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -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"
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue