feat: add sound callback
This commit is contained in:
parent
c061c8c802
commit
88c758b47c
|
|
@ -29,6 +29,42 @@ namespace kiwano
|
|||
namespace audio
|
||||
{
|
||||
|
||||
class VoiceCallback : public IXAudio2VoiceCallback
|
||||
{
|
||||
public:
|
||||
SoundCallback* cb;
|
||||
|
||||
VoiceCallback(SoundCallback* cb)
|
||||
: cb(cb)
|
||||
{
|
||||
}
|
||||
|
||||
~VoiceCallback() {}
|
||||
|
||||
STDMETHOD_(void, OnBufferStart(void* pBufferContext))
|
||||
{
|
||||
cb->OnStart(nullptr);
|
||||
}
|
||||
|
||||
STDMETHOD_(void, OnLoopEnd(void* pBufferContext))
|
||||
{
|
||||
cb->OnLoopEnd(nullptr);
|
||||
}
|
||||
|
||||
STDMETHOD_(void, OnBufferEnd(void* pBufferContext))
|
||||
{
|
||||
cb->OnEnd(nullptr);
|
||||
}
|
||||
|
||||
STDMETHOD_(void, OnStreamEnd()) {}
|
||||
|
||||
STDMETHOD_(void, OnVoiceProcessingPassEnd()) {}
|
||||
|
||||
STDMETHOD_(void, OnVoiceProcessingPassStart(UINT32 SamplesRequired)) {}
|
||||
|
||||
STDMETHOD_(void, OnVoiceError(void* pBufferContext, HRESULT Error)) {}
|
||||
};
|
||||
|
||||
AudioModule::AudioModule()
|
||||
: x_audio2_(nullptr)
|
||||
, mastering_voice_(nullptr)
|
||||
|
|
@ -121,10 +157,12 @@ bool AudioModule::CreateSound(Sound& sound, TranscoderPtr transcoder)
|
|||
|
||||
if (SUCCEEDED(hr))
|
||||
{
|
||||
auto chain = sound.GetCallbackChain();
|
||||
chain->SetNative(VoiceCallback{ chain.Get() });
|
||||
auto callback = const_cast<VoiceCallback*>(chain->GetNative().CastPtr<VoiceCallback>());
|
||||
|
||||
IXAudio2SourceVoice* voice = nullptr;
|
||||
|
||||
hr = x_audio2_->CreateSourceVoice(&voice, buffer.format, 0, XAUDIO2_DEFAULT_FREQ_RATIO);
|
||||
|
||||
hr = x_audio2_->CreateSourceVoice(&voice, buffer.format, 0, XAUDIO2_DEFAULT_FREQ_RATIO, callback);
|
||||
if (SUCCEEDED(hr))
|
||||
{
|
||||
sound.Close();
|
||||
|
|
@ -143,7 +181,6 @@ bool AudioModule::CreateSound(Sound& sound, TranscoderPtr transcoder)
|
|||
void AudioModule::Open()
|
||||
{
|
||||
KGE_ASSERT(x_audio2_ && "AudioModule hasn't been initialized!");
|
||||
|
||||
if (x_audio2_)
|
||||
x_audio2_->StartEngine();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -27,9 +27,13 @@ namespace kiwano
|
|||
namespace audio
|
||||
{
|
||||
|
||||
SoundPtr Sound::Preload(const String& file_path)
|
||||
SoundPtr Sound::Preload(const String& file_path, std::initializer_list<SoundCallbackPtr> callbacks)
|
||||
{
|
||||
auto ptr = MakePtr<Sound>();
|
||||
for (auto& cb : callbacks)
|
||||
{
|
||||
ptr->AddCallback(cb);
|
||||
}
|
||||
|
||||
size_t hash_code = std::hash<String>{}(file_path);
|
||||
if (TranscoderPtr transcoder = TranscoderCache::GetInstance().Get(hash_code))
|
||||
|
|
@ -46,9 +50,13 @@ SoundPtr Sound::Preload(const String& file_path)
|
|||
return ptr;
|
||||
}
|
||||
|
||||
SoundPtr Sound::Preload(const Resource& res)
|
||||
SoundPtr Sound::Preload(const Resource& res, std::initializer_list<SoundCallbackPtr> callbacks)
|
||||
{
|
||||
auto ptr = MakePtr<Sound>();
|
||||
for (auto& cb : callbacks)
|
||||
{
|
||||
ptr->AddCallback(cb);
|
||||
}
|
||||
|
||||
size_t hash_code = res.GetId();
|
||||
if (TranscoderPtr transcoder = TranscoderCache::GetInstance().Get(hash_code))
|
||||
|
|
@ -80,6 +88,7 @@ Sound::Sound(const Resource& res)
|
|||
Sound::Sound()
|
||||
: opened_(false)
|
||||
, playing_(false)
|
||||
, volume_(1.f)
|
||||
{
|
||||
}
|
||||
|
||||
|
|
@ -129,6 +138,11 @@ bool Sound::Load(TranscoderPtr transcoder)
|
|||
return false;
|
||||
}
|
||||
|
||||
// reset volume
|
||||
const float old_volume = volume_;
|
||||
volume_ = 0.f;
|
||||
SetVolume(old_volume);
|
||||
|
||||
coder_ = transcoder;
|
||||
opened_ = true;
|
||||
return true;
|
||||
|
|
@ -181,8 +195,14 @@ void Sound::Pause()
|
|||
auto voice = GetNativePtr<IXAudio2SourceVoice>();
|
||||
KGE_ASSERT(voice != nullptr && "IXAudio2SourceVoice* is NULL");
|
||||
|
||||
if (SUCCEEDED(voice->Stop()))
|
||||
HRESULT hr = voice->Stop();
|
||||
if (SUCCEEDED(hr))
|
||||
playing_ = false;
|
||||
|
||||
if (FAILED(hr))
|
||||
{
|
||||
KGE_ERRORF("Pause voice failed with HRESULT of %08X", hr);
|
||||
}
|
||||
}
|
||||
|
||||
void Sound::Resume()
|
||||
|
|
@ -190,8 +210,14 @@ void Sound::Resume()
|
|||
auto voice = GetNativePtr<IXAudio2SourceVoice>();
|
||||
KGE_ASSERT(voice != nullptr && "IXAudio2SourceVoice* is NULL");
|
||||
|
||||
if (SUCCEEDED(voice->Start()))
|
||||
HRESULT hr = voice->Start();
|
||||
if (SUCCEEDED(hr))
|
||||
playing_ = true;
|
||||
|
||||
if (FAILED(hr))
|
||||
{
|
||||
KGE_ERRORF("Start voice failed with HRESULT of %08X", hr);
|
||||
}
|
||||
}
|
||||
|
||||
void Sound::Stop()
|
||||
|
|
@ -209,6 +235,11 @@ void Sound::Stop()
|
|||
|
||||
if (SUCCEEDED(hr))
|
||||
playing_ = false;
|
||||
|
||||
if (FAILED(hr))
|
||||
{
|
||||
KGE_ERRORF("Stop voice failed with HRESULT of %08X", hr);
|
||||
}
|
||||
}
|
||||
|
||||
void Sound::Close()
|
||||
|
|
@ -246,21 +277,169 @@ bool Sound::IsPlaying() const
|
|||
|
||||
float Sound::GetVolume() const
|
||||
{
|
||||
auto voice = GetNativePtr<IXAudio2SourceVoice>();
|
||||
KGE_ASSERT(voice != nullptr && "IXAudio2SourceVoice* is NULL");
|
||||
|
||||
float volume = 0.0f;
|
||||
voice->GetVolume(&volume);
|
||||
return volume;
|
||||
return volume_;
|
||||
}
|
||||
|
||||
void Sound::SetVolume(float volume)
|
||||
{
|
||||
if (volume_ == volume)
|
||||
{
|
||||
return;
|
||||
}
|
||||
volume_ = volume;
|
||||
|
||||
float actual_volume = GetCallbackChain()->OnVolumeChanged(this, volume_);
|
||||
|
||||
auto voice = GetNativePtr<IXAudio2SourceVoice>();
|
||||
KGE_ASSERT(voice != nullptr && "IXAudio2SourceVoice* is NULL");
|
||||
|
||||
volume = std::min(std::max(volume, -XAUDIO2_MAX_VOLUME_LEVEL), XAUDIO2_MAX_VOLUME_LEVEL);
|
||||
voice->SetVolume(volume);
|
||||
actual_volume = std::min(std::max(actual_volume, -XAUDIO2_MAX_VOLUME_LEVEL), XAUDIO2_MAX_VOLUME_LEVEL);
|
||||
voice->SetVolume(actual_volume);
|
||||
}
|
||||
|
||||
SoundCallbackPtr Sound::GetCallbackChain()
|
||||
{
|
||||
class SoundCallbackChain : public SoundCallback
|
||||
{
|
||||
public:
|
||||
Sound* sound;
|
||||
|
||||
void OnStart(Sound*) override
|
||||
{
|
||||
for (auto& cb : sound->GetCallbacks())
|
||||
{
|
||||
if (cb)
|
||||
{
|
||||
cb->OnStart(sound);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void OnLoopEnd(Sound*) override
|
||||
{
|
||||
for (auto& cb : sound->GetCallbacks())
|
||||
{
|
||||
if (cb)
|
||||
{
|
||||
cb->OnLoopEnd(sound);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void OnEnd(Sound*) override
|
||||
{
|
||||
for (auto& cb : sound->GetCallbacks())
|
||||
{
|
||||
if (cb)
|
||||
{
|
||||
cb->OnEnd(sound);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
float OnVolumeChanged(Sound*, float volume) override
|
||||
{
|
||||
float actual_volume = volume;
|
||||
for (auto& cb : sound->GetCallbacks())
|
||||
{
|
||||
if (cb)
|
||||
{
|
||||
actual_volume = cb->OnVolumeChanged(sound, volume);
|
||||
}
|
||||
}
|
||||
return actual_volume;
|
||||
}
|
||||
};
|
||||
|
||||
if (!callback_chain_)
|
||||
{
|
||||
auto chain = MakePtr<SoundCallbackChain>();
|
||||
chain->sound = this;
|
||||
callback_chain_ = chain;
|
||||
}
|
||||
return callback_chain_;
|
||||
}
|
||||
|
||||
SoundCallbackPtr SoundCallback::OnStart(const Function<void(Sound* sound)>& cb)
|
||||
{
|
||||
class SoundCallbackFunc : public SoundCallback
|
||||
{
|
||||
public:
|
||||
Function<void(Sound* sound)> cb;
|
||||
|
||||
void OnStart(Sound* sound) override
|
||||
{
|
||||
if (cb)
|
||||
{
|
||||
cb(sound);
|
||||
}
|
||||
}
|
||||
};
|
||||
auto ptr = MakePtr<SoundCallbackFunc>();
|
||||
ptr->cb = cb;
|
||||
return ptr;
|
||||
}
|
||||
|
||||
SoundCallbackPtr SoundCallback::OnLoopEnd(const Function<void(Sound* sound)>& cb)
|
||||
{
|
||||
class SoundCallbackFunc : public SoundCallback
|
||||
{
|
||||
public:
|
||||
Function<void(Sound* sound)> cb;
|
||||
|
||||
void OnLoopEnd(Sound* sound) override
|
||||
{
|
||||
if (cb)
|
||||
{
|
||||
cb(sound);
|
||||
}
|
||||
}
|
||||
};
|
||||
auto ptr = MakePtr<SoundCallbackFunc>();
|
||||
ptr->cb = cb;
|
||||
return ptr;
|
||||
}
|
||||
|
||||
SoundCallbackPtr SoundCallback::OnEnd(const Function<void(Sound* sound)>& cb)
|
||||
{
|
||||
class SoundCallbackFunc : public SoundCallback
|
||||
{
|
||||
public:
|
||||
Function<void(Sound* sound)> cb;
|
||||
|
||||
void OnEnd(Sound* sound) override
|
||||
{
|
||||
if (cb)
|
||||
{
|
||||
cb(sound);
|
||||
}
|
||||
}
|
||||
};
|
||||
auto ptr = MakePtr<SoundCallbackFunc>();
|
||||
ptr->cb = cb;
|
||||
return ptr;
|
||||
}
|
||||
|
||||
SoundCallbackPtr SoundCallback::OnVolumeChanged(const Function<float(Sound* sound, float volume)>& cb)
|
||||
{
|
||||
class SoundCallbackFunc : public SoundCallback
|
||||
{
|
||||
public:
|
||||
Function<float(Sound* sound, float volume)> cb;
|
||||
|
||||
float OnVolumeChanged(Sound* sound, float volume) override
|
||||
{
|
||||
if (cb)
|
||||
{
|
||||
return cb(sound, volume);
|
||||
}
|
||||
return volume;
|
||||
}
|
||||
};
|
||||
auto ptr = MakePtr<SoundCallbackFunc>();
|
||||
ptr->cb = cb;
|
||||
return ptr;
|
||||
}
|
||||
|
||||
} // namespace audio
|
||||
} // namespace kiwano
|
||||
|
|
|
|||
|
|
@ -29,8 +29,10 @@ namespace kiwano
|
|||
namespace audio
|
||||
{
|
||||
class AudioModule;
|
||||
class SoundPlayer;
|
||||
|
||||
KGE_DECLARE_SMART_PTR(Sound);
|
||||
KGE_DECLARE_SMART_PTR(SoundCallback);
|
||||
|
||||
/**
|
||||
* \addtogroup Audio
|
||||
|
|
@ -39,20 +41,64 @@ KGE_DECLARE_SMART_PTR(Sound);
|
|||
|
||||
/**
|
||||
* \~chinese
|
||||
* @brief 音频对象
|
||||
* @brief 音频回调
|
||||
*/
|
||||
class KGE_API SoundCallback : public NativeObject
|
||||
{
|
||||
public:
|
||||
/// \~chinese
|
||||
/// @brief 创建一个回调,在音频开始播放时执行
|
||||
static SoundCallbackPtr OnStart(const Function<void(Sound* sound)>& cb);
|
||||
|
||||
/// \~chinese
|
||||
/// @brief 创建一个回调,在音频循环结束时执行
|
||||
static SoundCallbackPtr OnLoopEnd(const Function<void(Sound* sound)>& cb);
|
||||
|
||||
/// \~chinese
|
||||
/// @brief 创建一个回调,在音频结束时执行
|
||||
static SoundCallbackPtr OnEnd(const Function<void(Sound* sound)>& cb);
|
||||
|
||||
/// \~chinese
|
||||
/// @brief 创建一个回调,在音频修改音量时执行
|
||||
static SoundCallbackPtr OnVolumeChanged(const Function<float(Sound* sound, float volume)>& cb);
|
||||
|
||||
/// \~chinese
|
||||
/// @brief 在音频开始播放时执行
|
||||
virtual inline void OnStart(Sound* sound) {}
|
||||
|
||||
/// \~chinese
|
||||
/// @brief 在音频循环结束时执行
|
||||
virtual inline void OnLoopEnd(Sound* sound) {}
|
||||
|
||||
/// \~chinese
|
||||
/// @brief 在音频结束时执行
|
||||
virtual inline void OnEnd(Sound* sound) {}
|
||||
|
||||
/// \~chinese
|
||||
/// @brief 在音频修改音量时执行
|
||||
virtual inline float OnVolumeChanged(Sound* sound, float volume)
|
||||
{
|
||||
return volume;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* \~chinese
|
||||
* @brief 音频
|
||||
*/
|
||||
class KGE_API Sound : public NativeObject
|
||||
{
|
||||
friend class AudioModule;
|
||||
friend class SoundPlayer;
|
||||
|
||||
public:
|
||||
/// \~chinese
|
||||
/// @brief 预加载音频
|
||||
static SoundPtr Preload(const String& file_path);
|
||||
static SoundPtr Preload(const String& file_path, std::initializer_list<SoundCallbackPtr> callbacks = {});
|
||||
|
||||
/// \~chinese
|
||||
/// @brief 预加载音频资源
|
||||
static SoundPtr Preload(const Resource& res);
|
||||
static SoundPtr Preload(const Resource& res, std::initializer_list<SoundCallbackPtr> callbacks = {});
|
||||
|
||||
/// \~chinese
|
||||
/// @brief 创建音频对象
|
||||
|
|
@ -68,21 +114,6 @@ public:
|
|||
|
||||
virtual ~Sound();
|
||||
|
||||
/// \~chinese
|
||||
/// @brief 打开本地音频文件
|
||||
/// @param res 本地音频文件路径
|
||||
bool Load(const String& file_path);
|
||||
|
||||
/// \~chinese
|
||||
/// @brief 打开音频资源
|
||||
/// @param res 音频资源
|
||||
bool Load(const Resource& res);
|
||||
|
||||
/// \~chinese
|
||||
/// @brief 打开音频资源
|
||||
/// @param res 音频资源
|
||||
bool Load(TranscoderPtr transcoder);
|
||||
|
||||
/// \~chinese
|
||||
/// @brief 播放
|
||||
/// @param loop_count 播放循环次数,设置 -1 为循环播放
|
||||
|
|
@ -117,13 +148,62 @@ public:
|
|||
/// @param volume 音量大小,1.0 为原始音量, 大于 1 为放大音量, 0 为最小音量
|
||||
void SetVolume(float volume);
|
||||
|
||||
/// \~chinese
|
||||
/// @brief 添加回调
|
||||
void AddCallback(SoundCallbackPtr callback);
|
||||
|
||||
/// \~chinese
|
||||
/// @brief 获取所有回调
|
||||
List<SoundCallbackPtr>& GetCallbacks();
|
||||
|
||||
/// \~chinese
|
||||
/// @brief 获取所有回调
|
||||
const List<SoundCallbackPtr>& GetCallbacks() const;
|
||||
|
||||
protected:
|
||||
/// \~chinese
|
||||
/// @brief 打开本地音频文件
|
||||
/// @param res 本地音频文件路径
|
||||
bool Load(const String& file_path);
|
||||
|
||||
/// \~chinese
|
||||
/// @brief 打开音频资源
|
||||
/// @param res 音频资源
|
||||
bool Load(const Resource& res);
|
||||
|
||||
/// \~chinese
|
||||
/// @brief 打开音频资源
|
||||
/// @param res 音频资源
|
||||
bool Load(TranscoderPtr transcoder);
|
||||
|
||||
SoundCallbackPtr GetCallbackChain();
|
||||
|
||||
private:
|
||||
bool opened_;
|
||||
bool playing_;
|
||||
float volume_;
|
||||
TranscoderPtr coder_;
|
||||
|
||||
SoundCallbackPtr callback_chain_;
|
||||
List<SoundCallbackPtr> callbacks_;
|
||||
};
|
||||
|
||||
/** @} */
|
||||
|
||||
inline List<SoundCallbackPtr>& kiwano::audio::Sound::GetCallbacks()
|
||||
{
|
||||
return callbacks_;
|
||||
}
|
||||
|
||||
inline const List<SoundCallbackPtr>& kiwano::audio::Sound::GetCallbacks() const
|
||||
{
|
||||
return callbacks_;
|
||||
}
|
||||
|
||||
inline void kiwano::audio::Sound::AddCallback(SoundCallbackPtr callback)
|
||||
{
|
||||
callbacks_.push_back(callback);
|
||||
}
|
||||
|
||||
} // namespace audio
|
||||
} // namespace kiwano
|
||||
|
|
|
|||
Loading…
Reference in New Issue