in Audio/AudioEngine.cpp [823:1028]
void AudioEngine::Impl::AllocateVoice(
const WAVEFORMATEX* wfx,
SOUND_EFFECT_INSTANCE_FLAGS flags,
bool oneshot,
IXAudio2SourceVoice** voice)
{
if (!wfx)
throw std::invalid_argument("Wave format is required\n");
// No need to call IsValid on wfx because CreateSourceVoice will do that
if (!voice)
throw std::invalid_argument("Voice pointer must be non-null");
*voice = nullptr;
if (!xaudio2 || mCriticalError)
return;
#ifndef NDEBUG
const float maxFrequencyRatio = XAudio2SemitonesToFrequencyRatio(12);
assert(maxFrequencyRatio <= XAUDIO2_DEFAULT_FREQ_RATIO);
#endif
unsigned int voiceKey = 0;
if (oneshot)
{
if (flags & (SoundEffectInstance_Use3D | SoundEffectInstance_ReverbUseFilters | SoundEffectInstance_NoSetPitch))
{
DebugTrace((flags & SoundEffectInstance_NoSetPitch)
? "ERROR: One-shot voices must support pitch-shifting for voice reuse\n"
: "ERROR: One-use voices cannot use 3D positional audio\n");
throw std::invalid_argument("Invalid flags for one-shot voice");
}
#ifdef VERBOSE_TRACE
if (wfx->wFormatTag == WAVE_FORMAT_EXTENSIBLE)
{
DebugTrace("INFO: Requesting one-shot: Format Tag EXTENSIBLE %u, %u channels, %u-bit, %u blkalign, %u Hz\n",
GetFormatTag(wfx), wfx->nChannels, wfx->wBitsPerSample, wfx->nBlockAlign, wfx->nSamplesPerSec);
}
else
{
DebugTrace("INFO: Requesting one-shot: Format Tag %u, %u channels, %u-bit, %u blkalign, %u Hz\n",
wfx->wFormatTag, wfx->nChannels, wfx->wBitsPerSample, wfx->nBlockAlign, wfx->nSamplesPerSec);
}
#endif
if (!(mEngineFlags & AudioEngine_DisableVoiceReuse))
{
voiceKey = makeVoiceKey(wfx);
if (voiceKey != 0)
{
auto it = mVoicePool.find(voiceKey);
if (it != mVoicePool.end())
{
// Found a matching (stopped) voice to reuse
assert(it->second != nullptr);
*voice = it->second;
mVoicePool.erase(it);
// Reset any volume/pitch-shifting
HRESULT hr = (*voice)->SetVolume(1.f);
ThrowIfFailed(hr);
hr = (*voice)->SetFrequencyRatio(1.f);
ThrowIfFailed(hr);
if (wfx->nChannels == 1 || wfx->nChannels == 2)
{
// Reset any panning
float matrix[16] = {};
ComputePan(0.f, wfx->nChannels, matrix);
hr = (*voice)->SetOutputMatrix(nullptr, wfx->nChannels, masterChannels, matrix);
ThrowIfFailed(hr);
}
}
else if ((mVoicePool.size() + mOneShots.size() + 1) >= maxVoiceOneshots)
{
DebugTrace("WARNING: Too many one-shot voices in use (%zu + %zu >= %zu); one-shot not played\n",
mVoicePool.size(), mOneShots.size() + 1, maxVoiceOneshots);
return;
}
else
{
// makeVoiceKey already constrained the supported wfx formats to those supported for reuse
char buff[64] = {};
auto wfmt = reinterpret_cast<WAVEFORMATEX*>(buff);
uint32_t tag = GetFormatTag(wfx);
switch (tag)
{
case WAVE_FORMAT_PCM:
CreateIntegerPCM(wfmt, defaultRate, wfx->nChannels, wfx->wBitsPerSample);
break;
case WAVE_FORMAT_IEEE_FLOAT:
CreateFloatPCM(wfmt, defaultRate, wfx->nChannels);
break;
case WAVE_FORMAT_ADPCM:
{
auto wfadpcm = reinterpret_cast<const ADPCMWAVEFORMAT*>(wfx);
CreateADPCM(wfmt, sizeof(buff), defaultRate, wfx->nChannels, wfadpcm->wSamplesPerBlock);
}
break;
#ifdef DIRECTX_ENABLE_XMA2
case WAVE_FORMAT_XMA2:
CreateXMA2(wfmt, sizeof(buff), defaultRate, wfx->nChannels, 65536, 2, 0);
break;
#endif
}
#ifdef VERBOSE_TRACE
DebugTrace("INFO: Allocate reuse voice: Format Tag %u, %u channels, %u-bit, %u blkalign, %u Hz\n",
wfmt->wFormatTag, wfmt->nChannels, wfmt->wBitsPerSample, wfmt->nBlockAlign, wfmt->nSamplesPerSec);
#endif
assert(voiceKey == makeVoiceKey(wfmt));
HRESULT hr = xaudio2->CreateSourceVoice(voice, wfmt, 0, XAUDIO2_DEFAULT_FREQ_RATIO, &mVoiceCallback, nullptr, nullptr);
if (FAILED(hr))
{
DebugTrace("ERROR: CreateSourceVoice (reuse) failed with error %08X\n", static_cast<unsigned int>(hr));
throw std::runtime_error("CreateSourceVoice");
}
}
assert(*voice != nullptr);
HRESULT hr = (*voice)->SetSourceSampleRate(wfx->nSamplesPerSec);
if (FAILED(hr))
{
DebugTrace("ERROR: SetSourceSampleRate failed with error %08X\n", static_cast<unsigned int>(hr));
throw std::runtime_error("SetSourceSampleRate");
}
}
}
}
if (!*voice)
{
if (oneshot)
{
if ((mVoicePool.size() + mOneShots.size() + 1) >= maxVoiceOneshots)
{
DebugTrace("WARNING: Too many one-shot voices in use (%zu + %zu >= %zu); one-shot not played; see TrimVoicePool\n",
mVoicePool.size(), mOneShots.size() + 1, maxVoiceOneshots);
return;
}
}
else if ((mVoiceInstances + 1) >= maxVoiceInstances)
{
DebugTrace("ERROR: Too many instance voices (%zu >= %zu); see TrimVoicePool\n",
mVoiceInstances + 1, maxVoiceInstances);
throw std::runtime_error("Too many instance voices");
}
UINT32 vflags = (flags & SoundEffectInstance_NoSetPitch) ? XAUDIO2_VOICE_NOPITCH : 0u;
HRESULT hr;
if (flags & SoundEffectInstance_Use3D)
{
XAUDIO2_SEND_DESCRIPTOR sendDescriptors[2] = {};
sendDescriptors[0].Flags = sendDescriptors[1].Flags = (flags & SoundEffectInstance_ReverbUseFilters)
? XAUDIO2_SEND_USEFILTER : 0u;
sendDescriptors[0].pOutputVoice = mMasterVoice;
sendDescriptors[1].pOutputVoice = mReverbVoice;
const XAUDIO2_VOICE_SENDS sendList = { mReverbVoice ? 2U : 1U, sendDescriptors };
#ifdef VERBOSE_TRACE
DebugTrace("INFO: Allocate voice 3D: Format Tag %u, %u channels, %u-bit, %u blkalign, %u Hz\n",
wfx->wFormatTag, wfx->nChannels, wfx->wBitsPerSample, wfx->nBlockAlign, wfx->nSamplesPerSec);
#endif
hr = xaudio2->CreateSourceVoice(voice, wfx, vflags, XAUDIO2_DEFAULT_FREQ_RATIO, &mVoiceCallback, &sendList, nullptr);
}
else
{
#ifdef VERBOSE_TRACE
DebugTrace("INFO: Allocate voice: Format Tag %u, %u channels, %u-bit, %u blkalign, %u Hz\n",
wfx->wFormatTag, wfx->nChannels, wfx->wBitsPerSample, wfx->nBlockAlign, wfx->nSamplesPerSec);
#endif
hr = xaudio2->CreateSourceVoice(voice, wfx, vflags, XAUDIO2_DEFAULT_FREQ_RATIO, &mVoiceCallback, nullptr, nullptr);
}
if (FAILED(hr))
{
DebugTrace("ERROR: CreateSourceVoice failed with error %08X\n", static_cast<unsigned int>(hr));
throw std::runtime_error("CreateSourceVoice");
}
else if (!oneshot)
{
++mVoiceInstances;
}
}
if (oneshot)
{
assert(*voice != nullptr);
mOneShots.emplace_back(std::make_pair(voiceKey, *voice));
}
}