locked
Releasing memory allocated for XAudio2 samples

    Question

  • Hi,

    I'm working on an XAudio2 based music app and I am having problems releasing memory allocated to my source voices. I looked at the "XAudio2 audio file playback sample" that is kind of similar to what I do, but it never releases the memory and changing the sample to call the XAudio2SoundPlayer destructor causes A/V exceptions  on sourceVoice->DestroyVoice() calls. Is there a sample around that shows how to properly load and release source voices? My app allows for mixing sessions where I load 10 wav files that add up to about 16MB in size and whenever I try to destroy the voices and later load the wav files again - my memory seems to grow by about 16MB.


    Filip Skakun

    Tuesday, October 9, 2012 8:46 PM

Answers

  • You never call 'delete' on an object that your code never called 'new' on. The correct code is

    _voice->DestroyVoice();
    _voice = nullptr;
    The second line is optional, but good practice to make sure later code isn't trying to use a 'dangling' pointer. _voice is invalid after the call to DestroyVoice.


    Thursday, October 11, 2012 5:47 PM
  • Right thanks!

    It turned out that my memory leak was actually due to not releasing XAUDIO2_BUFFER.pAudioData. I could not release it before due to detected heap corruptions when calling delete _buffer.pAudioData, but it turned out that was due to buffer overruns from when the buffer was being first populated. The size of the buffer wasn't calculated correctly in some sample I took the code from.

    The "XAudio2 audio stream effect sample" makes this calculation that I seemed to take when I was writing this code a couple months ago:

        // Get the total length of the stream in bytes
        PROPVARIANT var;
        DX::ThrowIfFailed(
            m_reader->GetPresentationAttribute(MF_SOURCE_READER_MEDIASOURCE, MF_PD_DURATION, &var)
            );
        LONGLONG duration = var.uhVal.QuadPart;
        float64 durationInSeconds = (duration / (float64)(10000 * 1000));
        m_maxStreamLengthInBytes = (uint32)(durationInSeconds * m_waveFormat.nAvgBytesPerSec);

    The problem is I was actually using the m_maxStreamLengthInBytes to create a byte array which is probably how the Developer Preview version of that sample worked and that value is 1 byte short. The current version of the sample ignores that value since it streams the audio in smaller chunks instead of keeping it all in memory as I do now.


    Filip Skakun

    • Marked as answer by Filip Skakun Saturday, October 13, 2012 12:22 AM
    Friday, October 12, 2012 5:43 PM

All replies

  • With XAUDIO2, the actual audio data is ALWAYS owned by the application. Playback of the source voice has to be fully completed/stopped before the referenced audio data can be released.

    In general, DestroyVoice() should work to stop the voice and release it but it can't be called within a callback. See MSDN.

    Tuesday, October 9, 2012 9:00 PM
  • What do you mean by "callback"? Is this some special type of callback?

    Filip Skakun

    Wednesday, October 10, 2012 8:23 PM
  • Also, the docs say "It is illegal to call DestroyVoice from within a callback. If DestroyVoice is called within a callback XAUDIO2_E_INVALID_CALL will be returned." DestroyVoice returns void, so where would I get the XAUDIO2_E_INVALID_CALL?

    Filip Skakun

    Wednesday, October 10, 2012 8:24 PM
  • Specially within an XAUDIO2 callback via IXAudio2::RegisterForCallbacks or via IXAudio2VoiceCallback.
    Wednesday, October 10, 2012 9:48 PM
  • I'm getting lost. I am not doing it from a callback. I try to do this:

    _voice->DestroyVoice();
    delete _voice;
    

    - and it seems like I am doing something wrong since I am getting an A/V when I call delete:

    > msvcr110d.dll!operator delete(void * pUserData) Line 52 C++

    /* verify block type */

    _ASSERTE(_BLOCK_TYPE_IS_VALID(pHead->nBlockUse));


    Filip Skakun

    Wednesday, October 10, 2012 10:18 PM
  • You never call 'delete' on an object that your code never called 'new' on. The correct code is

    _voice->DestroyVoice();
    _voice = nullptr;
    The second line is optional, but good practice to make sure later code isn't trying to use a 'dangling' pointer. _voice is invalid after the call to DestroyVoice.


    Thursday, October 11, 2012 5:47 PM
  • Right thanks!

    It turned out that my memory leak was actually due to not releasing XAUDIO2_BUFFER.pAudioData. I could not release it before due to detected heap corruptions when calling delete _buffer.pAudioData, but it turned out that was due to buffer overruns from when the buffer was being first populated. The size of the buffer wasn't calculated correctly in some sample I took the code from.

    The "XAudio2 audio stream effect sample" makes this calculation that I seemed to take when I was writing this code a couple months ago:

        // Get the total length of the stream in bytes
        PROPVARIANT var;
        DX::ThrowIfFailed(
            m_reader->GetPresentationAttribute(MF_SOURCE_READER_MEDIASOURCE, MF_PD_DURATION, &var)
            );
        LONGLONG duration = var.uhVal.QuadPart;
        float64 durationInSeconds = (duration / (float64)(10000 * 1000));
        m_maxStreamLengthInBytes = (uint32)(durationInSeconds * m_waveFormat.nAvgBytesPerSec);

    The problem is I was actually using the m_maxStreamLengthInBytes to create a byte array which is probably how the Developer Preview version of that sample worked and that value is 1 byte short. The current version of the sample ignores that value since it streams the audio in smaller chunks instead of keeping it all in memory as I do now.


    Filip Skakun

    • Marked as answer by Filip Skakun Saturday, October 13, 2012 12:22 AM
    Friday, October 12, 2012 5:43 PM