XAudio2: SourceChannels doesn't match source voice format

Jun 1, 2014 at 8:42 PM
Hi,

I'm trying to implement a simple audio engine using the XAudio2 facilities provided by this library but have run into some issues using 3D sound (everything else works great). I've set up my implementation to use the Audio library as described in its documentations, but it seems that anytime after I have started a sound effect instance playing and call Apply3D, I get the exception shown in the subject line.

My usage is summarized below:
void Project::AudioSystem::playSoundEffect3D(const std::wstring& filename, const BaseCamera& observer, const Vector3f& soundPosition, const bool reverb)
{
    using namespace DirectX;
    SOUND_EFFECT_INSTANCE_FLAGS flags = SoundEffectInstance_Use3D | (reverb ? SoundEffectInstance_ReverbUseFilters : SoundEffectInstance_Default);

    SoundEffectInstance3D& sfx3d = m_pSoundEffect3DTracks[findNextAvailableSoundEffect3DTrack()];
    std::unique_ptr<DirectX::SoundEffectInstance>& sfxInstance = sfx3d.pSoundEffectInstance;
    sfxInstance = m_audioManager.getAudio(m_pAudioEngine.get(), filename, flags);
    
    sfx3d.listener = AudioListener{};
    sfx3d.listener.SetPosition(observer.getPosition());
    sfx3d.listener.SetOrientation(observer.getLookVector(), observer.getUpVector());

    sfx3d.emitter = AudioEmitter{};
    sfx3d.emitter.SetPosition(XMFLOAT3{soundPosition.x, soundPosition.y, soundPosition.z});

    sfxInstance->Apply3D(sfx3d.listener, sfx3d.emitter);
    sfxInstance->Play(false);
    sfxInstance->SetVolume(m_soundEffectVolume);
}



void Project::AudioSystem::update(const Scene& scene, const float32 deltaTime)
{
    BaseCamera& camera = *scene.getCameras()[0];
    for(uint32 i = 0u, j = static_cast<uint32>(m_pSoundEffect3DTracks.size()); i < j; ++i)
    {
        SoundEffectInstance3D& sfx3d = m_pSoundEffect3DTracks[i];
        if(sfx3d.pSoundEffectInstance && sfx3d.pSoundEffectInstance->GetState() == DirectX::SoundState::PLAYING)
        {
            sfx3d.listener.Update(camera.getPositionXM(), camera.getUpVectorXM(), deltaTime);
            sfx3d.emitter.Update(DirectX::XMLoadFloat3(&sfx3d.emitter.Position), camera.getWorldUpVectorXM(), deltaTime);
            //sfx3d.pSoundEffectInstance->Apply3D(sfx3d.listener, sfx3d.emitter);
        }
    }

    if(!m_pAudioEngine->Update())
    {
        getLogger().warn(L"Unable to update the audio engine.");
        if(m_pAudioEngine->IsCriticalError())
        {
            getLogger().error(L"Critical error in audio engine.");
        }
    }
}
As shown, the first call to Apply3D does not crash the application, but the subsequent calls (during the update loop which is called every frame) are what throw the error.

SoundEffectInstance3D is just a struct I've defined to hold a unique_ptr to a SoundEffectInstance and an emitter and listener associated with it so they can be updated over time to hopefully include 3D sound and doppler effects.

There is nothing special about the audio file I'm using, just a short .wav file. Playing the same .wav file without applying 3D works perfectly fine.

Any ideas or help on this would be greatly appreciated.
Jun 2, 2014 at 12:51 AM
As an update, I've found that this only gets triggered when in my debug build when I am creating the AudioEngine with the DirectX::AudioEngine_Debug flag. Below is how I'm constructing the audio engine (wrapped in a std::unique_ptr).
m_pAudioEngine(new DirectX::AudioEngine(
    DirectX::AudioEngine_Default
#if DEBUG || _DEBUG
    | DirectX::AudioEngine_Debug
#endif
    | DirectX::AudioEngine_EnvironmentalReverb
    | DirectX::AudioEngine_ReverbUseFilters
    | DirectX::AudioEngine_UseMasteringLimiter
    ))
Coordinator
Jun 2, 2014 at 6:10 AM
Are you using XAudio 2.8 or XAudio 2.7 (DirectX SDK)?

There is a known issue when you enable the debugging on XAudio 2.7 that triggers an exception. You can just ignore the first one and things will proceed.
Jun 2, 2014 at 2:10 PM
I am using XAudio 2.8. I built it in Visual Studio 2013 on Windows 8.1. I am using the 64-bit build for both release and debug builds (my project only supports 64-bit).
Coordinator
Jun 2, 2014 at 5:33 PM
If you are getting an exception with debug enabled on XAudio 2.8, then you probably have a bug in your code. Take a look at the debug output for any clues as to why you are hitting a failure.
Jun 2, 2014 at 11:42 PM
Edited Jun 2, 2014 at 11:48 PM
Here is the entirety of the debug output regarding XAudio. Maybe there's something I'm missing?
INFO: XAudio 2.8 debugging enabled
INFO: mastering voice has 2 channels, 48000 sample rate, 00000003 channel mask
INFO: Mastering volume limiter enabled
INFO: I3DL2 reverb effect enabled for 3D positional audio
XAudio2: SourceChannels doesn't match source voice format
Project.exe has triggered a breakpoint.
Anyway, the fact that I can do release builds and everything works fine will work for now. For the time being I've just surrounded the additional Apply3D calls with #if !_DEBUG so it won't crash.

The only thing I can possibly think of off the top of my head (and I'm pretty unfamiliar with XAudio, obviously), is this block in SoundCommon.cpp (and also because this is around where the breakpoint says it's being hit):
    auto direct = mDirectVoice;
    assert( direct != 0 );
    voice->SetOutputMatrix( direct, mDSPSettings.SrcChannelCount, mDSPSettings.DstChannelCount, matrix );

    if ( reverb )
    {
        voice->SetOutputMatrix( reverb, 1, 1, &mDSPSettings.ReverbLevel );
    }
Thanks for the responses.
Coordinator
Jun 3, 2014 at 7:17 AM
What's the details on the source voice format you are using?
Jun 3, 2014 at 1:56 PM
I finally figured out what the issue was when I opened the .wav files in Audacity to get some information from them. I was using stereo sounds, when they needed to be mono. Although, the Position 3D Audio of this page doesn't explicitly call it out, the SetPan documentation on that same page (and directly above the 3D Audio section, no less) does declare that it will throw an exception if the sounds aren't mono. This makes sense since when applying 3D audio, it should be up to the audio processing engine to decide where the sound originates and how to balance it on the user's speakers, and naturally that would require the need to pan the sound on the engine side.

Thanks for your help in discovering the issue, walbourn. Hopefully it will come in handy to others in the future.
Coordinator
Jun 3, 2014 at 7:16 PM
Edited Jun 3, 2014 at 7:29 PM
The SetPan function is only defined for mono because panning isn't strictly defined for multi-channel sources.

X3DXAudio does support multi-channel sources for 3D positional audio. It does require some special handling. DirectX Tool Kit for Audio supports up to XAUDIO2_MAX_AUDIO_CHANNELS source channels and 8 output channels (that's the maximum space it allocates for the matrix coefficients).

However, the default constructor for AudioEmitter sets the ChannelCount to 1, so it 'defaults' to a setup for mono sources. I believe this is the issue you encountered. I.e. you had a multi-channel sound source, but did not have the AudioEmitter (aka X3DAUDIO_EMITTER) set up for a multi-channel source. This mismatch was noticed by XAudio2 when you called SetOutputMatrix hence the debug output you saw.

To properly handle multi-channel (i.e. stereo, etc.) 3D source emitters, you have to adjust ChannelCount, ChannelRadius, and the EmitterAzimuths array appropriately. I'll make a note to add a helper function for this to AudioEmitter.

I've updated AudioEmitter's doc page to address this explicitly.
Marked as answer by walbourn on 6/3/2014 at 11:16 AM
Jun 3, 2014 at 8:18 PM
Aha, yes that's exactly what I needed. Thanks again for the help, walbourn, everything is working well now.