How to: Load audio data files in XAudio2
Note
This content applies only to desktop apps (and would require revision in order to function in a UWP app). Please refer to the documentation for CreateFile2, CreateEventEx, WaitForSingleObjectEx, SetFilePointerEx, and GetOverlappedResultEx. See SoundFileReader.h
and .cpp
in the BasicSound Windows 8 sample app from now-archived Windows SDK Samples Gallery.
To populate the structures required to play audio data in XAudio2:
- You can load or stream an audio file,
- or you can generate a waveform of your own and represent that waveform as samples in a buffer (see A simple XAudio2 project in full).
This topic focuses on the method of loading an audio file. The following steps load the fmt
and data
chunks of an audio file, and use them to populate a WAVEFORMATEXTENSIBLE structure and an XAUDIO2_BUFFER structure.
Preparing to parse the audio file
Audio files supported by XAudio2 are formatted with the Resource Interchange File Format (RIFF). RIFF is described in the topic Resource Interchange File Format (RIFF). Audio data in a RIFF file is loaded by finding the RIFF chunk, and then looping through the chunk to find individual chunks contained in the RIFF chunk. The following functions are examples of code to find chunks and load data contained in the chunks.
To find a chunk in a RIFF file:
#ifdef _XBOX //Big-Endian #define fourccRIFF 'RIFF' #define fourccDATA 'data' #define fourccFMT 'fmt ' #define fourccWAVE 'WAVE' #define fourccXWMA 'XWMA' #define fourccDPDS 'dpds' #endif #ifndef _XBOX //Little-Endian #define fourccRIFF 'FFIR' #define fourccDATA 'atad' #define fourccFMT ' tmf' #define fourccWAVE 'EVAW' #define fourccXWMA 'AMWX' #define fourccDPDS 'sdpd' #endif HRESULT FindChunk(HANDLE hFile, DWORD fourcc, DWORD & dwChunkSize, DWORD & dwChunkDataPosition) { HRESULT hr = S_OK; if( INVALID_SET_FILE_POINTER == SetFilePointer( hFile, 0, NULL, FILE_BEGIN ) ) return HRESULT_FROM_WIN32( GetLastError() ); DWORD dwChunkType; DWORD dwChunkDataSize; DWORD dwRIFFDataSize = 0; DWORD dwFileType; DWORD bytesRead = 0; DWORD dwOffset = 0; while (hr == S_OK) { DWORD dwRead; if( 0 == ReadFile( hFile, &dwChunkType, sizeof(DWORD), &dwRead, NULL ) ) hr = HRESULT_FROM_WIN32( GetLastError() ); if( 0 == ReadFile( hFile, &dwChunkDataSize, sizeof(DWORD), &dwRead, NULL ) ) hr = HRESULT_FROM_WIN32( GetLastError() ); switch (dwChunkType) { case fourccRIFF: dwRIFFDataSize = dwChunkDataSize; dwChunkDataSize = 4; if( 0 == ReadFile( hFile, &dwFileType, sizeof(DWORD), &dwRead, NULL ) ) hr = HRESULT_FROM_WIN32( GetLastError() ); break; default: if( INVALID_SET_FILE_POINTER == SetFilePointer( hFile, dwChunkDataSize, NULL, FILE_CURRENT ) ) return HRESULT_FROM_WIN32( GetLastError() ); } dwOffset += sizeof(DWORD) * 2; if (dwChunkType == fourcc) { dwChunkSize = dwChunkDataSize; dwChunkDataPosition = dwOffset; return S_OK; } dwOffset += dwChunkDataSize; if (bytesRead >= dwRIFFDataSize) return S_FALSE; } return S_OK; }
To read data in a chunk after it has been located.
Once a desired chunk is found, its data can be read by adjusting the file pointer to the beginning of the data section of the chunk. A function to read the data from a chunk once it is found might look like this.
HRESULT ReadChunkData(HANDLE hFile, void * buffer, DWORD buffersize, DWORD bufferoffset) { HRESULT hr = S_OK; if( INVALID_SET_FILE_POINTER == SetFilePointer( hFile, bufferoffset, NULL, FILE_BEGIN ) ) return HRESULT_FROM_WIN32( GetLastError() ); DWORD dwRead; if( 0 == ReadFile( hFile, buffer, buffersize, &dwRead, NULL ) ) hr = HRESULT_FROM_WIN32( GetLastError() ); return hr; }
Populating XAudio2 structures with the contents of RIFF chunks
In order for XAudio2 to play audio with a source voice, it needs a WAVEFORMATEX structure and an XAUDIO2_BUFFER structure. The WAVEFORMATEX structure may be a larger structure such as WAVEFORMATEXTENSIBLE that contains a WAVEFORMATEX structure as its first member. See the WAVEFORMATEX reference page for more information.
In this example a WAVEFORMATEXTENSIBLE is being used to allow loading of PCM audio files with more than two channels.
The following steps illustrate using the functions described above to populate a WAVEFORMATEXTENSIBLE structure and an XAUDIO2_BUFFER structure. In this case, the audio file being loaded contains PCM data, and will only contain a 'RIFF', 'fmt ', and 'data' chunk. Other formats may contain additional chunk types as described in Resource Interchange File Format (RIFF).
Declare WAVEFORMATEXTENSIBLE and XAUDIO2_BUFFER structures.
WAVEFORMATEXTENSIBLE wfx = {0}; XAUDIO2_BUFFER buffer = {0};
Open the audio file with CreateFile.
#ifdef _XBOX char * strFileName = "game:\\media\\MusicMono.wav"; #else TCHAR * strFileName = _TEXT("media\\MusicMono.wav"); #endif // Open the file HANDLE hFile = CreateFile( strFileName, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL ); if( INVALID_HANDLE_VALUE == hFile ) return HRESULT_FROM_WIN32( GetLastError() ); if( INVALID_SET_FILE_POINTER == SetFilePointer( hFile, 0, NULL, FILE_BEGIN ) ) return HRESULT_FROM_WIN32( GetLastError() );
Locate the 'RIFF' chunk in the audio file, and check the file type.
DWORD dwChunkSize; DWORD dwChunkPosition; //check the file type, should be fourccWAVE or 'XWMA' FindChunk(hFile,fourccRIFF,dwChunkSize, dwChunkPosition ); DWORD filetype; ReadChunkData(hFile,&filetype,sizeof(DWORD),dwChunkPosition); if (filetype != fourccWAVE) return S_FALSE;
Locate the 'fmt ' chunk, and copy its contents into a WAVEFORMATEXTENSIBLE structure.
FindChunk(hFile,fourccFMT, dwChunkSize, dwChunkPosition ); ReadChunkData(hFile, &wfx, dwChunkSize, dwChunkPosition );
Locate the 'data' chunk, and read its contents into a buffer.
//fill out the audio data buffer with the contents of the fourccDATA chunk FindChunk(hFile,fourccDATA,dwChunkSize, dwChunkPosition ); BYTE * pDataBuffer = new BYTE[dwChunkSize]; ReadChunkData(hFile, pDataBuffer, dwChunkSize, dwChunkPosition);
Populate an XAUDIO2_BUFFER structure.
buffer.AudioBytes = dwChunkSize; //size of the audio buffer in bytes buffer.pAudioData = pDataBuffer; //buffer containing audio data buffer.Flags = XAUDIO2_END_OF_STREAM; // tell the source voice not to expect any data after this buffer