mirror of
https://github.com/ncblakely/GiantsTools
synced 2024-11-24 07:05:37 +01:00
623 lines
18 KiB
C++
623 lines
18 KiB
C++
|
//--------------------------------------------------------------------------------------
|
||
|
// File: SoundEffect.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 "WAVFileReader.h"
|
||
|
#include "SoundCommon.h"
|
||
|
|
||
|
#include <list>
|
||
|
|
||
|
#if (defined(_XBOX_ONE) && defined(_TITLE)) || defined(_GAMING_XBOX)
|
||
|
#include <apu.h>
|
||
|
#include <shapexmacontext.h>
|
||
|
#endif
|
||
|
|
||
|
using namespace DirectX;
|
||
|
|
||
|
|
||
|
//======================================================================================
|
||
|
// SoundEffect
|
||
|
//======================================================================================
|
||
|
|
||
|
// Internal object implementation class.
|
||
|
class SoundEffect::Impl : public IVoiceNotify
|
||
|
{
|
||
|
public:
|
||
|
explicit Impl(_In_ AudioEngine* engine) :
|
||
|
mWaveFormat(nullptr),
|
||
|
mStartAudio(nullptr),
|
||
|
mAudioBytes(0),
|
||
|
mLoopStart(0),
|
||
|
mLoopLength(0),
|
||
|
mEngine(engine),
|
||
|
mOneShots(0)
|
||
|
#ifdef DIRECTX_ENABLE_SEEK_TABLES
|
||
|
, mSeekCount(0)
|
||
|
, mSeekTable(nullptr)
|
||
|
#endif
|
||
|
#ifdef DIRECTX_ENABLE_XMA2
|
||
|
, mXMAMemory(nullptr)
|
||
|
#endif
|
||
|
{
|
||
|
assert(mEngine != nullptr);
|
||
|
mEngine->RegisterNotify(this, false);
|
||
|
}
|
||
|
|
||
|
Impl(Impl&&) = default;
|
||
|
Impl& operator= (Impl&&) = default;
|
||
|
|
||
|
Impl(Impl const&) = delete;
|
||
|
Impl& operator= (Impl const&) = delete;
|
||
|
|
||
|
~Impl() override
|
||
|
{
|
||
|
if (!mInstances.empty())
|
||
|
{
|
||
|
DebugTrace("WARNING: Destroying SoundEffect with %zu outstanding SoundEffectInstances\n", mInstances.size());
|
||
|
|
||
|
for (auto it = mInstances.begin(); it != mInstances.end(); ++it)
|
||
|
{
|
||
|
assert(*it != nullptr);
|
||
|
(*it)->OnDestroyParent();
|
||
|
}
|
||
|
|
||
|
mInstances.clear();
|
||
|
}
|
||
|
|
||
|
if (mOneShots > 0)
|
||
|
{
|
||
|
DebugTrace("WARNING: Destroying SoundEffect with %u outstanding one shot effects\n", mOneShots);
|
||
|
}
|
||
|
|
||
|
if (mEngine)
|
||
|
{
|
||
|
mEngine->UnregisterNotify(this, true, false);
|
||
|
mEngine = nullptr;
|
||
|
}
|
||
|
|
||
|
#ifdef DIRECTX_ENABLE_XMA2
|
||
|
if (mXMAMemory)
|
||
|
{
|
||
|
ApuFree(mXMAMemory);
|
||
|
mXMAMemory = nullptr;
|
||
|
}
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
HRESULT Initialize(_In_ AudioEngine* engine, _Inout_ std::unique_ptr<uint8_t[]>& wavData,
|
||
|
_In_ const WAVEFORMATEX* wfx, _In_reads_bytes_(audioBytes) const uint8_t* startAudio, size_t audioBytes,
|
||
|
#ifdef DIRECTX_ENABLE_SEEK_TABLES
|
||
|
_In_reads_opt_(seekCount) const uint32_t* seekTable, size_t seekCount,
|
||
|
#endif
|
||
|
uint32_t loopStart, uint32_t loopLength) noexcept;
|
||
|
|
||
|
void Play(float volume, float pitch, float pan);
|
||
|
|
||
|
// IVoiceNotify
|
||
|
void __cdecl OnBufferEnd() override
|
||
|
{
|
||
|
InterlockedDecrement(&mOneShots);
|
||
|
}
|
||
|
|
||
|
void __cdecl OnCriticalError() override
|
||
|
{
|
||
|
mOneShots = 0;
|
||
|
}
|
||
|
|
||
|
void __cdecl OnReset() override
|
||
|
{
|
||
|
// No action required
|
||
|
}
|
||
|
|
||
|
void __cdecl OnUpdate() override
|
||
|
{
|
||
|
// We do not register for update notification
|
||
|
assert(false);
|
||
|
}
|
||
|
|
||
|
void __cdecl OnDestroyEngine() noexcept override
|
||
|
{
|
||
|
mEngine = nullptr;
|
||
|
mOneShots = 0;
|
||
|
}
|
||
|
|
||
|
void __cdecl OnTrim() override
|
||
|
{
|
||
|
// No action required
|
||
|
}
|
||
|
|
||
|
void __cdecl GatherStatistics(AudioStatistics& stats) const noexcept override
|
||
|
{
|
||
|
stats.playingOneShots += mOneShots;
|
||
|
stats.audioBytes += mAudioBytes;
|
||
|
|
||
|
#ifdef DIRECTX_ENABLE_XMA2
|
||
|
if (mXMAMemory)
|
||
|
stats.xmaAudioBytes += mAudioBytes;
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
void __cdecl OnDestroyParent() noexcept override
|
||
|
{
|
||
|
}
|
||
|
|
||
|
const WAVEFORMATEX* mWaveFormat;
|
||
|
const uint8_t* mStartAudio;
|
||
|
uint32_t mAudioBytes;
|
||
|
uint32_t mLoopStart;
|
||
|
uint32_t mLoopLength;
|
||
|
AudioEngine* mEngine;
|
||
|
std::list<IVoiceNotify*> mInstances;
|
||
|
uint32_t mOneShots;
|
||
|
|
||
|
#ifdef DIRECTX_ENABLE_SEEK_TABLES
|
||
|
uint32_t mSeekCount;
|
||
|
const uint32_t* mSeekTable;
|
||
|
#endif
|
||
|
|
||
|
private:
|
||
|
std::unique_ptr<uint8_t[]> mWavData;
|
||
|
|
||
|
#ifdef DIRECTX_ENABLE_XMA2
|
||
|
void* mXMAMemory;
|
||
|
#endif
|
||
|
};
|
||
|
|
||
|
|
||
|
_Use_decl_annotations_
|
||
|
HRESULT SoundEffect::Impl::Initialize(AudioEngine* engine, std::unique_ptr<uint8_t[]>& wavData,
|
||
|
const WAVEFORMATEX* wfx, const uint8_t* startAudio, size_t audioBytes,
|
||
|
#ifdef DIRECTX_ENABLE_SEEK_TABLES
|
||
|
const uint32_t* seekTable, size_t seekCount,
|
||
|
#endif
|
||
|
uint32_t loopStart, uint32_t loopLength) noexcept
|
||
|
{
|
||
|
if (!engine || !IsValid(wfx) || !startAudio || !audioBytes || !wavData)
|
||
|
return E_INVALIDARG;
|
||
|
|
||
|
if (audioBytes > UINT32_MAX)
|
||
|
return E_INVALIDARG;
|
||
|
|
||
|
switch (GetFormatTag(wfx))
|
||
|
{
|
||
|
case WAVE_FORMAT_PCM:
|
||
|
case WAVE_FORMAT_IEEE_FLOAT:
|
||
|
case WAVE_FORMAT_ADPCM:
|
||
|
// Take ownership of the buffer
|
||
|
mWavData.reset(wavData.release());
|
||
|
|
||
|
// WARNING: We assume the wfx and startAudio parameters are pointers into the wavData memory buffer
|
||
|
mWaveFormat = wfx;
|
||
|
mStartAudio = startAudio;
|
||
|
break;
|
||
|
|
||
|
#ifdef DIRECTX_ENABLE_XWMA
|
||
|
|
||
|
case WAVE_FORMAT_WMAUDIO2:
|
||
|
case WAVE_FORMAT_WMAUDIO3:
|
||
|
if (!seekCount || !seekTable)
|
||
|
{
|
||
|
DebugTrace("ERROR: SoundEffect format xWMA requires seek table\n");
|
||
|
return E_FAIL;
|
||
|
}
|
||
|
|
||
|
if (seekCount > UINT32_MAX)
|
||
|
return E_INVALIDARG;
|
||
|
|
||
|
// Take ownership of the buffer
|
||
|
mWavData.reset(wavData.release());
|
||
|
|
||
|
// WARNING: We assume the wfx, startAudio, and mSeekTable parameters are pointers into the wavData memory buffer
|
||
|
mWaveFormat = wfx;
|
||
|
mStartAudio = startAudio;
|
||
|
mSeekCount = static_cast<uint32_t>(seekCount);
|
||
|
mSeekTable = seekTable;
|
||
|
break;
|
||
|
|
||
|
#endif // xWMA
|
||
|
|
||
|
#ifdef DIRECTX_ENABLE_XMA2
|
||
|
|
||
|
case WAVE_FORMAT_XMA2:
|
||
|
if (!seekCount || !seekTable)
|
||
|
{
|
||
|
DebugTrace("ERROR: SoundEffect format XMA2 requires seek table\n");
|
||
|
return E_FAIL;
|
||
|
}
|
||
|
|
||
|
if (seekCount > UINT32_MAX)
|
||
|
return E_INVALIDARG;
|
||
|
|
||
|
{
|
||
|
HRESULT hr = ApuAlloc(&mXMAMemory, nullptr,
|
||
|
static_cast<UINT32>(audioBytes), SHAPE_XMA_INPUT_BUFFER_ALIGNMENT);
|
||
|
if (FAILED(hr))
|
||
|
{
|
||
|
DebugTrace("ERROR: ApuAlloc failed. Did you allocate a large enough heap with ApuCreateHeap for all your XMA wave data?\n");
|
||
|
return hr;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
memcpy(mXMAMemory, startAudio, audioBytes);
|
||
|
mStartAudio = reinterpret_cast<const uint8_t*>(mXMAMemory);
|
||
|
|
||
|
mWavData.reset(new (std::nothrow) uint8_t[sizeof(XMA2WAVEFORMATEX) + (seekCount * sizeof(uint32_t))]);
|
||
|
if (!mWavData)
|
||
|
return E_OUTOFMEMORY;
|
||
|
|
||
|
memcpy(mWavData.get(), wfx, sizeof(XMA2WAVEFORMATEX));
|
||
|
mWaveFormat = reinterpret_cast<WAVEFORMATEX*>(mWavData.get());
|
||
|
|
||
|
// XMA seek table is Big-Endian
|
||
|
{
|
||
|
auto dest = reinterpret_cast<uint32_t*>(mWavData.get() + sizeof(XMA2WAVEFORMATEX));
|
||
|
for (size_t k = 0; k < seekCount; ++k)
|
||
|
{
|
||
|
dest[k] = _byteswap_ulong(seekTable[k]);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
mSeekCount = static_cast<uint32_t>(seekCount);
|
||
|
mSeekTable = reinterpret_cast<const uint32_t*>(mWavData.get() + sizeof(XMA2WAVEFORMATEX));
|
||
|
|
||
|
wavData.reset();
|
||
|
break;
|
||
|
|
||
|
#endif // XMA2
|
||
|
|
||
|
default:
|
||
|
{
|
||
|
DebugTrace("ERROR: SoundEffect encountered an unsupported format tag (%u)\n", wfx->wFormatTag);
|
||
|
return HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
mAudioBytes = static_cast<uint32_t>(audioBytes);
|
||
|
mLoopStart = loopStart;
|
||
|
mLoopLength = loopLength;
|
||
|
|
||
|
return S_OK;
|
||
|
}
|
||
|
|
||
|
|
||
|
void SoundEffect::Impl::Play(float volume, float pitch, float pan)
|
||
|
{
|
||
|
assert(volume >= -XAUDIO2_MAX_VOLUME_LEVEL && volume <= XAUDIO2_MAX_VOLUME_LEVEL);
|
||
|
assert(pitch >= -1.f && pitch <= 1.f);
|
||
|
assert(pan >= -1.f && pan <= 1.f);
|
||
|
|
||
|
IXAudio2SourceVoice* voice = nullptr;
|
||
|
mEngine->AllocateVoice(mWaveFormat, SoundEffectInstance_Default, true, &voice);
|
||
|
|
||
|
if (!voice)
|
||
|
return;
|
||
|
|
||
|
if (volume != 1.f)
|
||
|
{
|
||
|
HRESULT hr = voice->SetVolume(volume);
|
||
|
ThrowIfFailed(hr);
|
||
|
}
|
||
|
|
||
|
if (pitch != 0.f)
|
||
|
{
|
||
|
float fr = XAudio2SemitonesToFrequencyRatio(pitch * 12.f);
|
||
|
|
||
|
HRESULT hr = voice->SetFrequencyRatio(fr);
|
||
|
ThrowIfFailed(hr);
|
||
|
}
|
||
|
|
||
|
if (pan != 0.f)
|
||
|
{
|
||
|
float matrix[16];
|
||
|
if (ComputePan(pan, mWaveFormat->nChannels, matrix))
|
||
|
{
|
||
|
HRESULT hr = voice->SetOutputMatrix(nullptr, mWaveFormat->nChannels, mEngine->GetOutputChannels(), matrix);
|
||
|
ThrowIfFailed(hr);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
HRESULT hr = voice->Start(0);
|
||
|
ThrowIfFailed(hr);
|
||
|
|
||
|
XAUDIO2_BUFFER buffer = {};
|
||
|
buffer.AudioBytes = mAudioBytes;
|
||
|
buffer.pAudioData = mStartAudio;
|
||
|
buffer.Flags = XAUDIO2_END_OF_STREAM;
|
||
|
buffer.pContext = this;
|
||
|
|
||
|
#ifdef DIRECTX_ENABLE_XWMA
|
||
|
uint32_t tag = GetFormatTag(mWaveFormat);
|
||
|
if (tag == WAVE_FORMAT_WMAUDIO2 || tag == WAVE_FORMAT_WMAUDIO3)
|
||
|
{
|
||
|
XAUDIO2_BUFFER_WMA wmaBuffer = {};
|
||
|
wmaBuffer.PacketCount = mSeekCount;
|
||
|
wmaBuffer.pDecodedPacketCumulativeBytes = mSeekTable;
|
||
|
|
||
|
hr = voice->SubmitSourceBuffer(&buffer, &wmaBuffer);
|
||
|
}
|
||
|
else
|
||
|
#endif // xWMA
|
||
|
{
|
||
|
hr = voice->SubmitSourceBuffer(&buffer, nullptr);
|
||
|
}
|
||
|
if (FAILED(hr))
|
||
|
{
|
||
|
DebugTrace("ERROR: SoundEffect failed (%08X) when submitting buffer:\n", static_cast<unsigned int>(hr));
|
||
|
DebugTrace("\tFormat Tag %u, %u channels, %u-bit, %u Hz, %u bytes\n",
|
||
|
mWaveFormat->wFormatTag, mWaveFormat->nChannels, mWaveFormat->wBitsPerSample, mWaveFormat->nSamplesPerSec, mAudioBytes);
|
||
|
throw std::exception("SubmitSourceBuffer");
|
||
|
}
|
||
|
|
||
|
InterlockedIncrement(&mOneShots);
|
||
|
}
|
||
|
|
||
|
|
||
|
//--------------------------------------------------------------------------------------
|
||
|
// SoundEffect
|
||
|
//--------------------------------------------------------------------------------------
|
||
|
|
||
|
// Public constructors.
|
||
|
_Use_decl_annotations_
|
||
|
SoundEffect::SoundEffect(AudioEngine* engine, const wchar_t* waveFileName)
|
||
|
: pImpl(std::make_unique<Impl>(engine))
|
||
|
{
|
||
|
WAVData wavInfo;
|
||
|
std::unique_ptr<uint8_t[]> wavData;
|
||
|
HRESULT hr = LoadWAVAudioFromFileEx(waveFileName, wavData, wavInfo);
|
||
|
if (FAILED(hr))
|
||
|
{
|
||
|
DebugTrace("ERROR: SoundEffect failed (%08X) to load from .wav file \"%ls\"\n",
|
||
|
static_cast<unsigned int>(hr), waveFileName);
|
||
|
throw std::exception("SoundEffect");
|
||
|
}
|
||
|
|
||
|
#ifdef DIRECTX_ENABLE_SEEK_TABLES
|
||
|
hr = pImpl->Initialize(engine, wavData, wavInfo.wfx, wavInfo.startAudio, wavInfo.audioBytes,
|
||
|
wavInfo.seek, wavInfo.seekCount,
|
||
|
wavInfo.loopStart, wavInfo.loopLength);
|
||
|
#else
|
||
|
hr = pImpl->Initialize(engine, wavData, wavInfo.wfx, wavInfo.startAudio, wavInfo.audioBytes,
|
||
|
wavInfo.loopStart, wavInfo.loopLength);
|
||
|
#endif
|
||
|
|
||
|
if (FAILED(hr))
|
||
|
{
|
||
|
DebugTrace("ERROR: SoundEffect failed (%08X) to intialize from .wav file \"%ls\"\n",
|
||
|
static_cast<unsigned int>(hr), waveFileName);
|
||
|
throw std::exception("SoundEffect");
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
_Use_decl_annotations_
|
||
|
SoundEffect::SoundEffect(AudioEngine* engine, std::unique_ptr<uint8_t[]>& wavData,
|
||
|
const WAVEFORMATEX* wfx, const uint8_t* startAudio, size_t audioBytes)
|
||
|
: pImpl(std::make_unique<Impl>(engine))
|
||
|
{
|
||
|
#ifdef DIRECTX_ENABLE_SEEK_TABLES
|
||
|
HRESULT hr = pImpl->Initialize(engine, wavData, wfx, startAudio, audioBytes, nullptr, 0, 0, 0);
|
||
|
#else
|
||
|
HRESULT hr = pImpl->Initialize(engine, wavData, wfx, startAudio, audioBytes, 0, 0);
|
||
|
#endif
|
||
|
if (FAILED(hr))
|
||
|
{
|
||
|
DebugTrace("ERROR: SoundEffect failed (%08X) to intialize\n", static_cast<unsigned int>(hr));
|
||
|
throw std::exception("SoundEffect");
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
_Use_decl_annotations_
|
||
|
SoundEffect::SoundEffect(AudioEngine* engine, std::unique_ptr<uint8_t[]>& wavData,
|
||
|
const WAVEFORMATEX* wfx, const uint8_t* startAudio, size_t audioBytes,
|
||
|
uint32_t loopStart, uint32_t loopLength)
|
||
|
: pImpl(std::make_unique<Impl>(engine))
|
||
|
{
|
||
|
#ifdef DIRECTX_ENABLE_SEEK_TABLES
|
||
|
HRESULT hr = pImpl->Initialize(engine, wavData, wfx, startAudio, audioBytes, nullptr, 0, loopStart, loopLength);
|
||
|
#else
|
||
|
HRESULT hr = pImpl->Initialize(engine, wavData, wfx, startAudio, audioBytes, loopStart, loopLength);
|
||
|
#endif
|
||
|
if (FAILED(hr))
|
||
|
{
|
||
|
DebugTrace("ERROR: SoundEffect failed (%08X) to intialize\n", static_cast<unsigned int>(hr));
|
||
|
throw std::exception("SoundEffect");
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
#ifdef DIRECTX_ENABLE_SEEK_TABLES
|
||
|
|
||
|
_Use_decl_annotations_
|
||
|
SoundEffect::SoundEffect(AudioEngine* engine, std::unique_ptr<uint8_t[]>& wavData,
|
||
|
const WAVEFORMATEX* wfx, const uint8_t* startAudio, size_t audioBytes,
|
||
|
const uint32_t* seekTable, size_t seekCount)
|
||
|
{
|
||
|
HRESULT hr = pImpl->Initialize(engine, wavData, wfx, startAudio, audioBytes, seekTable, seekCount, 0, 0);
|
||
|
if (FAILED(hr))
|
||
|
{
|
||
|
DebugTrace("ERROR: SoundEffect failed (%08X) to intialize\n", static_cast<unsigned int>(hr));
|
||
|
throw std::exception("SoundEffect");
|
||
|
}
|
||
|
}
|
||
|
|
||
|
#endif
|
||
|
|
||
|
|
||
|
// Move constructor.
|
||
|
SoundEffect::SoundEffect(SoundEffect&& moveFrom) noexcept
|
||
|
: pImpl(std::move(moveFrom.pImpl))
|
||
|
{
|
||
|
}
|
||
|
|
||
|
|
||
|
// Move assignment.
|
||
|
SoundEffect& SoundEffect::operator= (SoundEffect&& moveFrom) noexcept
|
||
|
{
|
||
|
pImpl = std::move(moveFrom.pImpl);
|
||
|
return *this;
|
||
|
}
|
||
|
|
||
|
|
||
|
// Public destructor.
|
||
|
SoundEffect::~SoundEffect()
|
||
|
{
|
||
|
}
|
||
|
|
||
|
|
||
|
// Public methods.
|
||
|
void SoundEffect::Play()
|
||
|
{
|
||
|
pImpl->Play(1.f, 0.f, 0.f);
|
||
|
}
|
||
|
|
||
|
|
||
|
void SoundEffect::Play(float volume, float pitch, float pan)
|
||
|
{
|
||
|
pImpl->Play(volume, pitch, pan);
|
||
|
}
|
||
|
|
||
|
|
||
|
std::unique_ptr<SoundEffectInstance> SoundEffect::CreateInstance(SOUND_EFFECT_INSTANCE_FLAGS flags)
|
||
|
{
|
||
|
auto effect = new SoundEffectInstance(pImpl->mEngine, this, flags);
|
||
|
assert(effect != nullptr);
|
||
|
pImpl->mInstances.emplace_back(effect->GetVoiceNotify());
|
||
|
return std::unique_ptr<SoundEffectInstance>(effect);
|
||
|
}
|
||
|
|
||
|
|
||
|
void SoundEffect::UnregisterInstance(_In_ IVoiceNotify* instance)
|
||
|
{
|
||
|
auto it = std::find(pImpl->mInstances.begin(), pImpl->mInstances.end(), instance);
|
||
|
if (it == pImpl->mInstances.end())
|
||
|
return;
|
||
|
|
||
|
pImpl->mInstances.erase(it);
|
||
|
}
|
||
|
|
||
|
|
||
|
// Public accessors.
|
||
|
bool SoundEffect::IsInUse() const noexcept
|
||
|
{
|
||
|
return (pImpl->mOneShots > 0) || !pImpl->mInstances.empty();
|
||
|
}
|
||
|
|
||
|
|
||
|
size_t SoundEffect::GetSampleSizeInBytes() const noexcept
|
||
|
{
|
||
|
return pImpl->mAudioBytes;
|
||
|
}
|
||
|
|
||
|
|
||
|
size_t SoundEffect::GetSampleDuration() const noexcept
|
||
|
{
|
||
|
if (!pImpl->mWaveFormat || !pImpl->mWaveFormat->nChannels)
|
||
|
return 0;
|
||
|
|
||
|
switch (GetFormatTag(pImpl->mWaveFormat))
|
||
|
{
|
||
|
case WAVE_FORMAT_ADPCM:
|
||
|
{
|
||
|
auto adpcmFmt = reinterpret_cast<const ADPCMWAVEFORMAT*>(pImpl->mWaveFormat);
|
||
|
|
||
|
uint64_t duration = uint64_t(pImpl->mAudioBytes / adpcmFmt->wfx.nBlockAlign) * adpcmFmt->wSamplesPerBlock;
|
||
|
unsigned int partial = pImpl->mAudioBytes % adpcmFmt->wfx.nBlockAlign;
|
||
|
if (partial)
|
||
|
{
|
||
|
if (partial >= (7u * adpcmFmt->wfx.nChannels))
|
||
|
duration += (uint64_t(partial) * 2 / uint64_t(adpcmFmt->wfx.nChannels - 12));
|
||
|
}
|
||
|
return static_cast<size_t>(duration);
|
||
|
}
|
||
|
|
||
|
#ifdef DIRECTX_ENABLE_XWMA
|
||
|
|
||
|
case WAVE_FORMAT_WMAUDIO2:
|
||
|
case WAVE_FORMAT_WMAUDIO3:
|
||
|
if (pImpl->mSeekTable && pImpl->mSeekCount > 0)
|
||
|
{
|
||
|
return pImpl->mSeekTable[pImpl->mSeekCount - 1] / uint32_t(2 * pImpl->mWaveFormat->nChannels);
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
#endif
|
||
|
|
||
|
#ifdef DIRECTX_ENABLE_XMA2
|
||
|
|
||
|
case WAVE_FORMAT_XMA2:
|
||
|
return reinterpret_cast<const XMA2WAVEFORMATEX*>(pImpl->mWaveFormat)->SamplesEncoded;
|
||
|
|
||
|
#endif
|
||
|
|
||
|
default:
|
||
|
if (pImpl->mWaveFormat->wBitsPerSample > 0)
|
||
|
{
|
||
|
return static_cast<size_t>((uint64_t(pImpl->mAudioBytes) * 8)
|
||
|
/ (uint64_t(pImpl->mWaveFormat->wBitsPerSample) * uint64_t(pImpl->mWaveFormat->nChannels)));
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
|
||
|
size_t SoundEffect::GetSampleDurationMS() const noexcept
|
||
|
{
|
||
|
if (!pImpl->mWaveFormat || !pImpl->mWaveFormat->nSamplesPerSec)
|
||
|
return 0;
|
||
|
|
||
|
uint64_t samples = GetSampleDuration();
|
||
|
return static_cast<size_t>((samples * 1000) / pImpl->mWaveFormat->nSamplesPerSec);
|
||
|
}
|
||
|
|
||
|
|
||
|
const WAVEFORMATEX* SoundEffect::GetFormat() const noexcept
|
||
|
{
|
||
|
return pImpl->mWaveFormat;
|
||
|
}
|
||
|
|
||
|
|
||
|
#ifdef DIRECTX_ENABLE_XWMA
|
||
|
|
||
|
bool SoundEffect::FillSubmitBuffer(_Out_ XAUDIO2_BUFFER& buffer, _Out_ XAUDIO2_BUFFER_WMA& wmaBuffer) const
|
||
|
{
|
||
|
memset(&buffer, 0, sizeof(buffer));
|
||
|
memset(&wmaBuffer, 0, sizeof(wmaBuffer));
|
||
|
|
||
|
buffer.AudioBytes = pImpl->mAudioBytes;
|
||
|
buffer.pAudioData = pImpl->mStartAudio;
|
||
|
buffer.LoopBegin = pImpl->mLoopStart;
|
||
|
buffer.LoopLength = pImpl->mLoopLength;
|
||
|
|
||
|
uint32_t tag = GetFormatTag(pImpl->mWaveFormat);
|
||
|
if (tag == WAVE_FORMAT_WMAUDIO2 || tag == WAVE_FORMAT_WMAUDIO3)
|
||
|
{
|
||
|
wmaBuffer.PacketCount = pImpl->mSeekCount;
|
||
|
wmaBuffer.pDecodedPacketCumulativeBytes = pImpl->mSeekTable;
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
#else // !xWMA
|
||
|
|
||
|
void SoundEffect::FillSubmitBuffer(_Out_ XAUDIO2_BUFFER& buffer) const
|
||
|
{
|
||
|
memset(&buffer, 0, sizeof(buffer));
|
||
|
buffer.AudioBytes = pImpl->mAudioBytes;
|
||
|
buffer.pAudioData = pImpl->mStartAudio;
|
||
|
buffer.LoopBegin = pImpl->mLoopStart;
|
||
|
buffer.LoopLength = pImpl->mLoopLength;
|
||
|
}
|
||
|
|
||
|
#endif
|