XNA Creators Club Online
Page 1 of 1 (1 items)
Sort Posts: Previous Next

XAudio2: Problem playing a resubmitted buffer on a stopped voice after flushing using xWMA

Last post 22/04/2009 21:36 by GameDevDude. 0 replies.
  • 22/04/2009 21:36

    XAudio2: Problem playing a resubmitted buffer on a stopped voice after flushing using xWMA

    I have a source voice playing xWMA data, and I do the following:
    1)      Stop the voice
    2)      Flush the buffer
    3)      Wait for the buffer to be flushed
    4)      Submit the same buffer (to restart the file I was playing)
    5)      Start the voice

    The problem is that when the voice is started in step 5, it immediately reaches the end of the buffer without playing any of the supplied audio. In my tests, it always immediately finishes the first buffer when starting over on an xWMA file, but if there are multiple buffers queued, it will happily continue on the second buffer. However, it will always skip the first buffer completely. If I do the exact same thing to a voice playing a WAV file, it functions as expected and restarts the audio file with no problems. I have tried this with the November 2008 and March 2009 versions of the DirectX SDK, and the problem happens in both. Does anyone have any idea why this doesn’t work?

    I’ve uploaded the code, VS2005 project, and audio test files here: http://www.binarycreativity.com/files/VoiceRestartingProblem.zip

    The code is pasted below. Thanks for your help!

    #define _WIN32_DCOM  
    #define _CRT_SECURE_NO_DEPRECATE
    #include <windows.h>  
    #include <xaudio2.h>
    #include <strsafe.h>  
    #include <shellapi.h>
    #include <mmsystem.h>  
    #include <conio.h>
    #include <Xact3wb.h>  
    #include <assert.h>
    #define USE_XWMA_FILE ( 1 ) // If we use the XWMA file, this example code doesn't function correctly  
    // It works just fine with the wave equivalent  
     
    //--------------------------------------------------------------------------------------  
    // Callback structure  
    //--------------------------------------------------------------------------------------  
    struct StreamingVoiceContext : public IXAudio2VoiceCallback  
    {  
        STDMETHOD_( void, OnVoiceProcessingPassStart )( UINT32 )  
        {  
        }  
        STDMETHOD_( void, OnVoiceProcessingPassEnd )()  
        {  
        }  
        STDMETHOD_( void, OnStreamEnd )()  
        {  
        }  
        STDMETHOD_( void, OnBufferStart )( void* )  
        {  
        }  
        STDMETHOD_( void, OnBufferEnd )( void* )  
        {  
            wprintf( L"Buffer End\n" );  
            SetEvent( hBufferEndEvent );  
        }  
        STDMETHOD_( void, OnLoopEnd )( void* )  
        {  
        }  
        STDMETHOD_( void, OnVoiceError )( void*, HRESULT )  
        {  
        }  
     
        HANDLE hBufferEndEvent;  
     
        StreamingVoiceContext() : hBufferEndEvent( CreateEvent( NULL, FALSE, FALSE, NULL ) )  
        {  
        }  
     
        virtual ~StreamingVoiceContext()  
        {  
            CloseHandle( hBufferEndEvent );  
        }  
    };  
     
     
    struct xWMAExtraInfo  
    {  
        UINT32 packetCount;  
        UINT32 *dpds;  
    };  
     
    struct DataBuffer  
    {  
        UINT32 size;  
        BYTE *data;  
    };  
     
    WAVEFORMATEX *ReadWave( DataBuffer *buffer, HMMIO hmmio )  
    {  
        MMCKINFO riff;  
        // Search the input file for for the 'fmt ' chunk.  
        riff.ckid = mmioFOURCC( 'f', 'm', 't', ' ' );  
        if( 0 != mmioDescend( hmmio, &riff, NULL, MMIO_FINDCHUNK ) )  
        {  
            return NULL;  
        }  
     
        // Expect the 'fmt' chunk to be at least as large as <PCMWAVEFORMAT>;  
        // if there are extra parameters at the end, we'll ignore them  
        if( riff.cksize < ( LONG )sizeof( PCMWAVEFORMAT ) )  
        {  
            return NULL;  
        }  
     
        // Read the 'fmt ' chunk into <pcmWaveFormat>.  
        PCMWAVEFORMAT pcmWaveFormat;  // Temp PCM structure to load in.  
     
        if( mmioRead( hmmio, ( HPSTR )&pcmWaveFormat, sizeof( pcmWaveFormat ) ) != sizeof( pcmWaveFormat ) )  
        {  
            return NULL;  
        }  
     
        // Allocate the waveformatex, but if its not pcm format, read the next  
        // word, and thats how many extra bytes to allocate.  
        assert( pcmWaveFormat.wf.wFormatTag == WAVE_FORMAT_PCM );  
        WAVEFORMATEX *wfx = (WAVEFORMATEX *)malloc( sizeof( WAVEFORMATEX ) );  
     
        // Copy the bytes from the pcm structure to the waveformatex structure  
        memcpy( wfx, &pcmWaveFormat, sizeof( pcmWaveFormat ) );  
        wfx->cbSize = 0;  
     
        // Ascend the input file out of the 'fmt ' chunk.  
        if( 0 != mmioAscend( hmmio, &riff, 0 ) )  
        {  
            free( wfx );  
            return NULL;  
        }  
     
        riff.ckid = mmioFOURCC( 'd', 'a', 't', 'a' );  
        if( 0 != mmioDescend( hmmio, &riff, NULL, MMIO_FINDCHUNK ) )  
        {  
            free( wfx );  
            return NULL;  
        }  
        buffer->size = riff.cksize;  
        buffer->data = (BYTE*)malloc( riff.cksize );  
        if( mmioRead( hmmio, ( HPSTR )buffer->data, riff.cksize ) != riff.cksize )  
        {  
            free( wfx );  
            free( buffer->data );  
            return NULL;  
        }     
        return wfx;  
    }  
     
    WAVEFORMATEX *ReadXWMA( DataBuffer *buffer, xWMAExtraInfo *extraInfo, HMMIO hmmio )  
    {  
        MMCKINFO riff;  
        // Search the input file for for the 'fmt ' chunk.  
        riff.ckid = mmioFOURCC( 'f', 'm', 't', ' ' );  
        if( 0 != mmioDescend( hmmio, &riff, NULL, MMIO_FINDCHUNK ) )  
        {  
            return NULL;  
        }  
     
        // Expect the 'fmt' chunk to be at least as large as <PCMWAVEFORMAT>;  
        // if there are extra parameters at the end, we'll ignore them  
        if( riff.cksize < ( LONG )sizeof( PCMWAVEFORMAT ) )  
        {  
            return NULL;  
        }  
     
        // Read the 'fmt ' chunk into <pcmWaveFormat>.  
        PCMWAVEFORMAT pcmWaveFormat;  // Temp PCM structure to load in.  
     
        if( mmioRead( hmmio, ( HPSTR )&pcmWaveFormat, sizeof( pcmWaveFormat ) ) != sizeof( pcmWaveFormat ) )  
        {  
            return NULL;  
        }  
        // Read in length of extra bytes.  
        WORD cbExtraBytes = 0L;  
        if( mmioRead( hmmio, ( CHAR* )&cbExtraBytes, sizeof( WORD ) ) != sizeof( WORD ) )  
        {  
            return NULL;  
        }  
     
        const int formatSize = sizeof( WAVEFORMATEX ) + cbExtraBytes;  
        WAVEFORMATEX *wfx = ( WAVEFORMATEX* )malloc( formatSize );  
        if( NULL == wfx )  
        {  
            return NULL;  
        }  
     
        // Copy the bytes from the pcm structure to the waveformatex structure  
        memcpy( wfx, &pcmWaveFormat, sizeof( pcmWaveFormat ) );  
        wfx->cbSize = cbExtraBytes;  
     
        // Now, read those extra bytes into the structure, if cbExtraAlloc != 0.  
        if( mmioRead( hmmio, ( CHAR* )( ( ( BYTE* )&( wfx->cbSize ) ) + sizeof( WORD ) ),  
            cbExtraBytes ) != cbExtraBytes )  
        {  
            free( wfx );  
            return NULL;  
        }  
     
        // Ascend the input file out of the 'fmt ' chunk.  
        if( 0 != mmioAscend( hmmio, &riff, 0 ) )  
        {  
            free( wfx );  
            return NULL;  
        }  
     
        // Search the input file for for the 'dpds' chunk.  
        riff.ckid = mmioFOURCC( 'd', 'p', 'd', 's' );  
        if( 0 != mmioDescend( hmmio, &riff, NULL, MMIO_FINDCHUNK ) )  
        {  
            free( wfx );  
            return NULL;  
        }  
     
        extraInfo->packetCount = riff.cksize / sizeof( UINT32 );  
        extraInfo->dpds = (UINT32*)malloc( riff.cksize );  
        if( mmioRead( hmmio, ( HPSTR )extraInfo->dpds, riff.cksize ) != riff.cksize )  
        {  
            free( wfx );  
            free( extraInfo->dpds );  
            return NULL;  
        }     
        // Ascend the input file out of the 'fmt ' chunk.  
        if( 0 != mmioAscend( hmmio, &riff, 0 ) )  
        {  
            free( wfx );  
            free( extraInfo->dpds );  
            return NULL;  
        }  
     
        riff.ckid = mmioFOURCC( 'd', 'a', 't', 'a' );  
        if( 0 != mmioDescend( hmmio, &riff, NULL, MMIO_FINDCHUNK ) )  
        {  
            free( wfx );  
            free( extraInfo->dpds );  
            return NULL;  
        }  
     
        buffer->size = riff.cksize;  
        buffer->data = (BYTE*)malloc( riff.cksize );  
        if( mmioRead( hmmio, ( HPSTR )buffer->data, riff.cksize ) != riff.cksize )  
        {  
            free( wfx );  
            free( extraInfo->dpds );  
            free( buffer->data );  
            return NULL;  
        }     
        return wfx;  
    }  
     
    WAVEFORMATEX *ReadAudioFile( LPTSTR name, DataBuffer *buffer, xWMAExtraInfo *extraInfo )  
    {  
        HMMIO hmmio = mmioOpen( name, NULL, MMIO_ALLOCBUF | MMIO_READ );  
     
        MMCKINFO riff;  
     
        if( ( 0 != mmioDescend( hmmio, &riff, NULL, 0 ) ) )  
        {  
            mmioClose( hmmio, 0 );  
            return 0;  
        }  
     
        // Check to make sure this is a valid wave file  
        if( riff.ckid != FOURCC_RIFF )   
        {  
            mmioClose( hmmio, 0 );  
            return 0;  
        }  
     
        WAVEFORMATEX *wfx = NULL;  
     
        switch( riff.fccType )  
        {  
            case  mmioFOURCC( 'W', 'A', 'V', 'E' ):  
            {  
                wfx = ReadWave( buffer, hmmio );  
                break;  
            }  
     
            case mmioFOURCC( 'X', 'W', 'M', 'A' ):  
            {  
                wfx = ReadXWMA( buffer, extraInfo, hmmio );  
                break;  
            }  
        }  
        mmioClose( hmmio, 0 );  
        return wfx;  
    }  
     
     
    //--------------------------------------------------------------------------------------  
    // Entry point to the program  
    //--------------------------------------------------------------------------------------  
    int main()  
    {  
        HRESULT hr;  
     
        // Initialize XAudio2  
        CoInitializeEx( NULL, COINIT_MULTITHREADED );  
     
        IXAudio2* pXAudio2 = NULL;  
     
        UINT32 flags = 0;
    #ifdef _DEBUG  
        flags |= XAUDIO2_DEBUG_ENGINE;
    #endif  
     
        if( FAILED( hr = XAudio2Create( &pXAudio2, flags ) ) )  
        {  
            wprintf( L"Failed to init XAudio2 engine: %#X\n", hr );  
            CoUninitialize();  
            return 0;  
        }  
     
        IXAudio2MasteringVoice* pMasteringVoice = NULL;  
     
        if( FAILED( hr = pXAudio2->CreateMasteringVoice( &pMasteringVoice ) ) )  
        {  
            wprintf( L"Failed creating mastering voice: %#X\n", hr );  
            pXAudio2->Release();  
            CoUninitialize();  
            return 0;  
        }
    #if USE_XWMA_FILE   
        // counting.xwm is generated by the command: "xwmaencode counting.wav"  
        // I am using the March 2009 version of the DirectX 9.0c SDK  
        const LPTSTR name = L"counting.xwm";
    #else  
        const LPTSTR name = L"counting.wav";
    #endif   
        xWMAExtraInfo extraInfo;  
        DataBuffer dataBuffer;  
        // Read in our audio file  
        WAVEFORMATEX *wfx = ReadAudioFile( name, &dataBuffer, &extraInfo );  
     
        if( wfx == NULL )  
        {  
            wprintf( L"Invalid format\n" );  
            return 1;  
        }  
     
        bool xwma = wfx->wFormatTag == WAVE_FORMAT_WMAUDIO2 || wfx->wFormatTag == WAVE_FORMAT_WMAUDIO3;  
        StreamingVoiceContext voiceContext;  
        IXAudio2SourceVoice* pSourceVoice;  
        if( FAILED( hr = pXAudio2->CreateSourceVoice( &pSourceVoice, wfx, 0, 1.0f, &voiceContext ) ) )  
        {  
            wprintf( L"\nError %#X creating source voice\n", hr );  
            return hr;  
        }  
        free( wfx );  
          
        // set up the buffer  
        XAUDIO2_BUFFER buf = {0};  
        buf.AudioBytes = dataBuffer.size;  
        buf.pAudioData = dataBuffer.data;  
        buf.Flags = XAUDIO2_END_OF_STREAM;  
        XAUDIO2_BUFFER_WMA bufferWma;  
        XAUDIO2_BUFFER_WMA *wma = NULL;  
          
        if( xwma )  
        {  
            wma = &bufferWma;  
            bufferWma.PacketCount = extraInfo.packetCount;  
            bufferWma.pDecodedPacketCumulativeBytes = extraInfo.dpds;  
        }  
        pSourceVoice->SubmitSourceBuffer( &buf, wma );  
     
        // We've submitted one buffer and started the audio stream. Everything is working fine so far.  
        pSourceVoice->Start( 0, 0 );  
     
        // Wait a second.  
        Sleep( 1000 );  
     
        // stop the source voice  
        pSourceVoice->Stop( 0 );  
        wprintf( L"About to flush buffers\n" );  
        pSourceVoice->FlushSourceBuffers();  
     
        // wait for the buffer to get flushed  
        WaitForSingleObject( voiceContext.hBufferEndEvent, INFINITE );  
     
        // Start over from the beginning by submitting the exact same buffer with the   
        // exact same parameters.  
        pSourceVoice->SubmitSourceBuffer( &buf, wma );  
        wprintf( L"About to start playing again\n" );  
        pSourceVoice->Start( 0 );  
     
        // Wait for the voice to reach the end of the resubmitted buffer. With wave files, it starts over   
        // properly and the sound file is played out in its entirety. However, with xWMA files,   
        // it immediately reaches the end of the buffer. In my tests, it always dumps the first   
        // buffer when starting over on an xWMA file, but if there are multiple buffers queued,   
        // it will happily continue on the second buffer. However, it will always skip the first   
        // buffer completely.   
        WaitForSingleObject( voiceContext.hBufferEndEvent, INFINITE );  
        wprintf( L"Finished playing the buffer in its entirety (assuming you were using a wave file)\n" );  
          
        // Clean up  
        pSourceVoice->DestroyVoice();  
        pMasteringVoice->DestroyVoice();  
     
        pXAudio2->Release();  
        CoUninitialize();  
     
        free( dataBuffer.data );  
        if( xwma )  
        {  
            free( extraInfo.dpds );  
        }  
     
        return 0;  
    }  
     

     

Page 1 of 1 (1 items) Previous Next