Search code examples
c++xaudio2

Xaudio2 crashes if I exit application while sound is playing


I have been tying to setup xaudio2 and while I thought I succeeded, I quit the application while the sound is still playing and the program crashed. originally I made it so that when receiving a WM_CLOSE message I would make sure that the source voices had stopped. this drastically reduced the number of crashes, but still around 1/5 times I exit the application the program crashes. this is what it says "Exception thrown at 0x00007FF9974E38C7 (XAudio2_9.dll) in Game.exe: 0xC0000005: Access violation reading location 0x0000025274A680C4.". I am assuming that the xaudio2 thread is still trying to read the buffer even though I have disposed of it. I have set it up in a way so that there is a central SoundSystem class which has 2 vectors of Voices(another class), idleVoices and activeVoices. when I play a sound I grab a voice from the idle channels and play a sound, then when it is done I put it back in the idle channels and repeat.

here is the .h file

    #pragma once

    #include <vector>
    #include <memory>
    #include <xaudio2.h>
    #include <algorithm>
    #include "Sound.h"


    class SoundSystem
    {

        friend class Voice;
        friend class Window;
    public:

        static SoundSystem& getInstance();





        SoundSystem(SoundSystem&) = delete;
        SoundSystem& operator=(SoundSystem&) = delete;


        void playSound(Sound& sound, float frequency = 1.0f, float volume = 1.0f);



        ~SoundSystem();

    private:

        class Voice
        {
            friend class SoundSystem;
            friend class Window;
        public:

            Voice()
            {

            }

            Voice(SoundSystem* soundSystem);




            void playSound(Sound& sound, float frequency = 1.0f, float volume = 1.0f);
            void stop();


            ~Voice();
        private:


            class VoiceCallback : public IXAudio2VoiceCallback
            {
            public:
                void STDMETHODCALLTYPE OnStreamEnd() override
                {}
                void STDMETHODCALLTYPE OnVoiceProcessingPassEnd() override
                {}
                void STDMETHODCALLTYPE OnVoiceProcessingPassStart(UINT32 SamplesRequired) override
                {}
                void STDMETHODCALLTYPE OnBufferEnd(void* pBufferContext) override;
                void STDMETHODCALLTYPE OnBufferStart(void* pBufferContext) override
                {}
                void STDMETHODCALLTYPE OnLoopEnd(void* pBufferContext) override
                {}
                void STDMETHODCALLTYPE OnVoiceError(void* pBufferContext, HRESULT Error) override
                {}
            };


            IXAudio2SourceVoice* sourceVoice = nullptr;
            XAUDIO2_BUFFER buffer = { 0 };

        };


        void deactivateVoice(Voice* voice);

        SoundSystem();

        IXAudio2* audioEngine = nullptr;
        IXAudio2MasteringVoice* masteringVoice = nullptr;


        std::vector<std::unique_ptr<Voice>> idleVoices;
        std::vector<std::unique_ptr<Voice>> activeVoices;
        const unsigned int maxVoices = 256;

    };








and the .cpp file

#include "SoundSystem.h"
#include "..\Exception.h"



SoundSystem::Voice::Voice(SoundSystem* soundSystem)
{


    HRESULT errorCode;

    static VoiceCallback voiceCallback;

    buffer.pContext = this;
    
    if (FAILED(errorCode = soundSystem->audioEngine->CreateSourceVoice(&sourceVoice, &WaveFile::validFormat, 0, 2, &voiceCallback)))
    {
        throw Exception::AudioException("CreateSourceVoice failed", __FILE__, __LINE__, errorCode);
    }
    

}


void SoundSystem::Voice::playSound(Sound& sound, float frequency, float volume)
{

    HRESULT errorCode;

    buffer.AudioBytes = sound.audioSize;
    buffer.pAudioData = sound.audioBuffer;
    if (FAILED(errorCode = sourceVoice->SubmitSourceBuffer(&buffer, nullptr)))
    {
        throw Exception::AudioException("SubmitSourceBuffer failed", __FILE__, __LINE__, errorCode);
    }
    if (FAILED(errorCode = sourceVoice->SetFrequencyRatio(frequency)))
    {
        throw Exception::AudioException("SetFrequencyRatio failed", __FILE__, __LINE__, errorCode);
    }
    if (FAILED(errorCode = sourceVoice->SetVolume(volume)))
    {
        throw Exception::AudioException("SetVolume failed", __FILE__, __LINE__, errorCode);
    }
    if (FAILED(errorCode = sourceVoice->Start()))
    {
        throw Exception::AudioException("Start failed", __FILE__, __LINE__, errorCode);
    }

}

void SoundSystem::Voice::stop()
{
    sourceVoice->Stop();
    sourceVoice->FlushSourceBuffers();
}

SoundSystem::Voice::~Voice()
{
    stop();


    sourceVoice->DestroyVoice();
    sourceVoice = nullptr;


}





void __stdcall SoundSystem::Voice::VoiceCallback::OnBufferEnd(void* pBufferContext)
{
    Voice* voice = reinterpret_cast<Voice*>(pBufferContext);

    voice->stop();
    SoundSystem::getInstance().deactivateVoice(voice);

}





SoundSystem& SoundSystem::getInstance()
{
    static SoundSystem instance;
    return instance;
}


void SoundSystem::deactivateVoice(Voice* voice)
{

    auto it = std::find_if(activeVoices.begin(), activeVoices.end(), [&](const std::unique_ptr<Voice>& v) -> bool
        {
            return voice = v.get();
        });
    idleVoices.push_back(std::move(*it));
    activeVoices.erase(it);


}

SoundSystem::SoundSystem()
{

    HRESULT errorCode;

    if (FAILED(errorCode = CoInitializeEx(nullptr, COINITBASE_MULTITHREADED)))
    {
        throw Exception::AudioException("CoInitializeEx failed", __FILE__, __LINE__, errorCode);
    }


    if (FAILED(errorCode = XAudio2Create(&audioEngine, 0)))
    {
        throw Exception::AudioException("XAudio2Create failed", __FILE__, __LINE__, errorCode);
    }

    if (FAILED(errorCode = audioEngine->CreateMasteringVoice(&masteringVoice)))
    {
        throw Exception::AudioException("CreateMasteringVoice failed", __FILE__, __LINE__, errorCode);
    }

    



    for (int i = 0; i < maxVoices; i++)
    {
        idleVoices.push_back(std::make_unique<Voice>(this));
    }

}

void SoundSystem::playSound(Sound& sound, float frequency, float volume)
{
    if (idleVoices.size() > 0)
    {
        activeVoices.push_back(std::move(idleVoices.back()));
        idleVoices.pop_back();
        activeVoices.back()->playSound(sound, frequency, volume);
    }
}

SoundSystem::~SoundSystem()
{


    for (auto& a : idleVoices)
    {
        a.reset();
    }
    for (auto& a : activeVoices)
    {
        a.reset();
    }


    audioEngine->Release();
    audioEngine = nullptr;

    
    masteringVoice = nullptr;

    CoUninitialize();

}

and here is the WM_CLOSE message handling

case WM_CLOSE:

        for (auto& a : SoundSystem::getInstance().activeVoices)
        {
            
            a.get()->stop();
        
        }


        ApplicationEvent applicationEvent(ApplicationEventType::WindowClose);
        EventSystem::getInstance().notify(&applicationEvent);

        PostQuitMessage(0);
        return 0;
        break;

I am assuming the close message handling has something to do with it, as stopping the source voices helped, but it still crashes. any help would be appreciated.

edit: I have figured out that after I have received the WM_CLOSE message I am still able to play sounds effectively undoing the fact I am stopping them. is there a way to make sure the program terminates right there?


Solution

  • You should add a call to StopEngine in the WM_CLOSE. This will stop all processing of the worker thread and likely resolve your crash.

    You may want to take a look at DirectX Tool Kit for Audio.