mirror of
https://github.com/ncblakely/GiantsTools
synced 2024-11-22 14:15:37 +01:00
342 lines
7.9 KiB
C++
342 lines
7.9 KiB
C++
|
//--------------------------------------------------------------------------------------
|
||
|
// File: SoundEffectInstance.cpp
|
||
|
//
|
||
|
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||
|
// Licensed under the MIT License.
|
||
|
//
|
||
|
// http://go.microsoft.com/fwlink/?LinkId=248929
|
||
|
// http://go.microsoft.com/fwlink/?LinkID=615561
|
||
|
//--------------------------------------------------------------------------------------
|
||
|
|
||
|
#include "pch.h"
|
||
|
#include "SoundCommon.h"
|
||
|
|
||
|
using namespace DirectX;
|
||
|
|
||
|
|
||
|
//======================================================================================
|
||
|
// SoundEffectInstance
|
||
|
//======================================================================================
|
||
|
|
||
|
// Internal object implementation class.
|
||
|
class SoundEffectInstance::Impl : public IVoiceNotify
|
||
|
{
|
||
|
public:
|
||
|
Impl(_In_ AudioEngine* engine, _In_ SoundEffect* effect, SOUND_EFFECT_INSTANCE_FLAGS flags) :
|
||
|
mBase(),
|
||
|
mEffect(effect),
|
||
|
mWaveBank(nullptr),
|
||
|
mIndex(0),
|
||
|
mLooped(false)
|
||
|
{
|
||
|
assert(engine != nullptr);
|
||
|
engine->RegisterNotify(this, false);
|
||
|
|
||
|
assert(mEffect != nullptr);
|
||
|
mBase.Initialize(engine, effect->GetFormat(), flags);
|
||
|
}
|
||
|
|
||
|
Impl(_In_ AudioEngine* engine, _In_ WaveBank* waveBank, uint32_t index, SOUND_EFFECT_INSTANCE_FLAGS flags) :
|
||
|
mBase(),
|
||
|
mEffect(nullptr),
|
||
|
mWaveBank(waveBank),
|
||
|
mIndex(index),
|
||
|
mLooped(false)
|
||
|
{
|
||
|
assert(engine != nullptr);
|
||
|
engine->RegisterNotify(this, false);
|
||
|
|
||
|
char buff[64] = {};
|
||
|
auto wfx = reinterpret_cast<WAVEFORMATEX*>(buff);
|
||
|
assert(mWaveBank != nullptr);
|
||
|
mBase.Initialize(engine, mWaveBank->GetFormat(index, wfx, sizeof(buff)), flags);
|
||
|
}
|
||
|
|
||
|
Impl(Impl&&) = default;
|
||
|
Impl& operator= (Impl&&) = default;
|
||
|
|
||
|
Impl(Impl const&) = delete;
|
||
|
Impl& operator= (Impl const&) = delete;
|
||
|
|
||
|
~Impl() override
|
||
|
{
|
||
|
mBase.DestroyVoice();
|
||
|
|
||
|
if (mBase.engine)
|
||
|
{
|
||
|
mBase.engine->UnregisterNotify(this, false, false);
|
||
|
mBase.engine = nullptr;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void Play(bool loop);
|
||
|
|
||
|
// IVoiceNotify
|
||
|
void __cdecl OnBufferEnd() override
|
||
|
{
|
||
|
// We don't register for this notification for SoundEffectInstances, so this should not be invoked
|
||
|
assert(false);
|
||
|
}
|
||
|
|
||
|
void __cdecl OnCriticalError() override
|
||
|
{
|
||
|
mBase.OnCriticalError();
|
||
|
}
|
||
|
|
||
|
void __cdecl OnReset() override
|
||
|
{
|
||
|
mBase.OnReset();
|
||
|
}
|
||
|
|
||
|
void __cdecl OnUpdate() override
|
||
|
{
|
||
|
// We do not register for update notification
|
||
|
assert(false);
|
||
|
}
|
||
|
|
||
|
void __cdecl OnDestroyEngine() noexcept override
|
||
|
{
|
||
|
mBase.OnDestroy();
|
||
|
}
|
||
|
|
||
|
void __cdecl OnTrim() override
|
||
|
{
|
||
|
mBase.OnTrim();
|
||
|
}
|
||
|
|
||
|
void __cdecl GatherStatistics(AudioStatistics& stats) const noexcept override
|
||
|
{
|
||
|
mBase.GatherStatistics(stats);
|
||
|
}
|
||
|
|
||
|
void __cdecl OnDestroyParent() noexcept override
|
||
|
{
|
||
|
mBase.OnDestroy();
|
||
|
mWaveBank = nullptr;
|
||
|
mEffect = nullptr;
|
||
|
}
|
||
|
|
||
|
SoundEffectInstanceBase mBase;
|
||
|
SoundEffect* mEffect;
|
||
|
WaveBank* mWaveBank;
|
||
|
uint32_t mIndex;
|
||
|
bool mLooped;
|
||
|
};
|
||
|
|
||
|
|
||
|
void SoundEffectInstance::Impl::Play(bool loop)
|
||
|
{
|
||
|
if (!mBase.voice)
|
||
|
{
|
||
|
if (mWaveBank)
|
||
|
{
|
||
|
char buff[64] = {};
|
||
|
auto wfx = reinterpret_cast<WAVEFORMATEX*>(buff);
|
||
|
mBase.AllocateVoice(mWaveBank->GetFormat(mIndex, wfx, sizeof(buff)));
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
assert(mEffect != nullptr);
|
||
|
mBase.AllocateVoice(mEffect->GetFormat());
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (!mBase.Play())
|
||
|
return;
|
||
|
|
||
|
// Submit audio data for STOPPED -> PLAYING state transition
|
||
|
XAUDIO2_BUFFER buffer = {};
|
||
|
|
||
|
#ifdef DIRECTX_ENABLE_XWMA
|
||
|
|
||
|
bool iswma = false;
|
||
|
XAUDIO2_BUFFER_WMA wmaBuffer = {};
|
||
|
if (mWaveBank)
|
||
|
{
|
||
|
iswma = mWaveBank->FillSubmitBuffer(mIndex, buffer, wmaBuffer);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
assert(mEffect != nullptr);
|
||
|
iswma = mEffect->FillSubmitBuffer(buffer, wmaBuffer);
|
||
|
}
|
||
|
|
||
|
#else // !xWMA
|
||
|
|
||
|
if (mWaveBank)
|
||
|
{
|
||
|
mWaveBank->FillSubmitBuffer(mIndex, buffer);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
assert(mEffect != nullptr);
|
||
|
mEffect->FillSubmitBuffer(buffer);
|
||
|
}
|
||
|
|
||
|
#endif
|
||
|
|
||
|
buffer.Flags = XAUDIO2_END_OF_STREAM;
|
||
|
if (loop)
|
||
|
{
|
||
|
mLooped = true;
|
||
|
buffer.LoopCount = XAUDIO2_LOOP_INFINITE;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
mLooped = false;
|
||
|
buffer.LoopCount = buffer.LoopBegin = buffer.LoopLength = 0;
|
||
|
}
|
||
|
buffer.pContext = nullptr;
|
||
|
|
||
|
HRESULT hr;
|
||
|
#ifdef DIRECTX_ENABLE_XWMA
|
||
|
if (iswma)
|
||
|
{
|
||
|
hr = mBase.voice->SubmitSourceBuffer(&buffer, &wmaBuffer);
|
||
|
}
|
||
|
else
|
||
|
#endif
|
||
|
{
|
||
|
hr = mBase.voice->SubmitSourceBuffer(&buffer, nullptr);
|
||
|
}
|
||
|
|
||
|
if (FAILED(hr))
|
||
|
{
|
||
|
#ifdef _DEBUG
|
||
|
DebugTrace("ERROR: SoundEffectInstance failed (%08X) when submitting buffer:\n", static_cast<unsigned int>(hr));
|
||
|
|
||
|
char buff[64] = {};
|
||
|
auto wfx = (mWaveBank) ? mWaveBank->GetFormat(mIndex, reinterpret_cast<WAVEFORMATEX*>(buff), sizeof(buff))
|
||
|
: mEffect->GetFormat();
|
||
|
|
||
|
size_t length = (mWaveBank) ? mWaveBank->GetSampleSizeInBytes(mIndex) : mEffect->GetSampleSizeInBytes();
|
||
|
|
||
|
DebugTrace("\tFormat Tag %u, %u channels, %u-bit, %u Hz, %zu bytes\n",
|
||
|
wfx->wFormatTag, wfx->nChannels, wfx->wBitsPerSample, wfx->nSamplesPerSec, length);
|
||
|
#endif
|
||
|
mBase.Stop(true, mLooped);
|
||
|
throw std::exception("SubmitSourceBuffer");
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
//--------------------------------------------------------------------------------------
|
||
|
// SoundEffectInstance
|
||
|
//--------------------------------------------------------------------------------------
|
||
|
|
||
|
// Private constructors
|
||
|
_Use_decl_annotations_
|
||
|
SoundEffectInstance::SoundEffectInstance(AudioEngine* engine, SoundEffect* effect, SOUND_EFFECT_INSTANCE_FLAGS flags) :
|
||
|
pImpl(std::make_unique<Impl>(engine, effect, flags))
|
||
|
{
|
||
|
}
|
||
|
|
||
|
_Use_decl_annotations_
|
||
|
SoundEffectInstance::SoundEffectInstance(AudioEngine* engine, WaveBank* waveBank, unsigned int index, SOUND_EFFECT_INSTANCE_FLAGS flags) :
|
||
|
pImpl(std::make_unique<Impl>(engine, waveBank, index, flags))
|
||
|
{
|
||
|
}
|
||
|
|
||
|
|
||
|
// Move constructor.
|
||
|
SoundEffectInstance::SoundEffectInstance(SoundEffectInstance&& moveFrom) noexcept
|
||
|
: pImpl(std::move(moveFrom.pImpl))
|
||
|
{
|
||
|
}
|
||
|
|
||
|
|
||
|
// Move assignment.
|
||
|
SoundEffectInstance& SoundEffectInstance::operator= (SoundEffectInstance&& moveFrom) noexcept
|
||
|
{
|
||
|
pImpl = std::move(moveFrom.pImpl);
|
||
|
return *this;
|
||
|
}
|
||
|
|
||
|
|
||
|
// Public destructor.
|
||
|
SoundEffectInstance::~SoundEffectInstance()
|
||
|
{
|
||
|
if (pImpl)
|
||
|
{
|
||
|
if (pImpl->mWaveBank)
|
||
|
{
|
||
|
pImpl->mWaveBank->UnregisterInstance(pImpl.get());
|
||
|
pImpl->mWaveBank = nullptr;
|
||
|
}
|
||
|
|
||
|
if (pImpl->mEffect)
|
||
|
{
|
||
|
pImpl->mEffect->UnregisterInstance(pImpl.get());
|
||
|
pImpl->mEffect = nullptr;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
// Public methods.
|
||
|
void SoundEffectInstance::Play(bool loop)
|
||
|
{
|
||
|
pImpl->Play(loop);
|
||
|
}
|
||
|
|
||
|
|
||
|
void SoundEffectInstance::Stop(bool immediate) noexcept
|
||
|
{
|
||
|
pImpl->mBase.Stop(immediate, pImpl->mLooped);
|
||
|
}
|
||
|
|
||
|
|
||
|
void SoundEffectInstance::Pause() noexcept
|
||
|
{
|
||
|
pImpl->mBase.Pause();
|
||
|
}
|
||
|
|
||
|
|
||
|
void SoundEffectInstance::Resume()
|
||
|
{
|
||
|
pImpl->mBase.Resume();
|
||
|
}
|
||
|
|
||
|
|
||
|
void SoundEffectInstance::SetVolume(float volume)
|
||
|
{
|
||
|
pImpl->mBase.SetVolume(volume);
|
||
|
}
|
||
|
|
||
|
|
||
|
void SoundEffectInstance::SetPitch(float pitch)
|
||
|
{
|
||
|
pImpl->mBase.SetPitch(pitch);
|
||
|
}
|
||
|
|
||
|
|
||
|
void SoundEffectInstance::SetPan(float pan)
|
||
|
{
|
||
|
pImpl->mBase.SetPan(pan);
|
||
|
}
|
||
|
|
||
|
|
||
|
void SoundEffectInstance::Apply3D(const AudioListener& listener, const AudioEmitter& emitter, bool rhcoords)
|
||
|
{
|
||
|
pImpl->mBase.Apply3D(listener, emitter, rhcoords);
|
||
|
}
|
||
|
|
||
|
|
||
|
// Public accessors.
|
||
|
bool SoundEffectInstance::IsLooped() const noexcept
|
||
|
{
|
||
|
return pImpl->mLooped;
|
||
|
}
|
||
|
|
||
|
|
||
|
SoundState SoundEffectInstance::GetState() noexcept
|
||
|
{
|
||
|
return pImpl->mBase.GetState(true);
|
||
|
}
|
||
|
|
||
|
|
||
|
IVoiceNotify* SoundEffectInstance::GetVoiceNotify() const noexcept
|
||
|
{
|
||
|
return pImpl.get();
|
||
|
}
|