Play a Sound using XAudio2

Use XAudio2 to load and play a .wav file on the Xbox One.

The following sections describe how to use XAudio2 to load and play a sound on the Xbox One dev kit:

For a working XAudio2 sample, download the SimplePlaySound sample from the XDK Samples on the Xbox Game Developer (XGD) site. For additional instructions, see Running the XDK Samples.

Important XAudio2 Data Types:

XAudio2 provides several data types to help you play sound effects on the Xbox One. In order to play a .wav file you need at least the following data types:

In addition to the three data types mentioned above, you may also find the following data types useful:

Initializing XAudio2:

CoInitializeEx() initializes the Component Object Model (COM) for use by the current thread. Set the first parameter to NULL, and set the second parameter to COINIT_MULTITHREADED.

XAudio2Create() Creates a new XAudio2 object and returns a pointer to its IXAudio2 interface. Make sure to set the XAUDIO2_PROCESESOR to a valid value. The default value is XAUDIO2_DEFAULT_PROCESSOR.

IXAudio2::CreateMasteringVoice() creates and configures a mastering voice, and points to it with the user-provided pointer.

C++

CoInitializeEx( NULL, COINIT_MULTITHREADED );

// Create XAudio2 device
DX::ThrowIfFailed( XAudio2Create( &m_pXAudio2, 0, XAUDIO2_DEFAULT_PROCESSOR, NULL ) );

// Create XAudio2 Mastering Voice and store result
DX::ThrowIfFailed( m_pXAudio2->CreateMasteringVoice( &m_pMasteringVoice ) );  

Loading a WAV file:

Use an instance of the WaveFile class to access the audio file. Call WaveFile::Open(LPCWSTR strFileName) to open the desired Wave file.

Once you have opened the Wave file, you will need to retrieve some information stored in the Wave file header. Call WaveFile::GetFormat() to determine the format of the Wave file. To determine the number of bytes and samples in the Wave, call WaveFile::GetDuration(). Finally, read the sample data into memory by calling WaveFile::ReadSample()

C++

// Read the wave file
WaveFile WaveFile;

// Append the filename and location to the end of the installation location
WCHAR FilenameAndLocation[ 1024 ];
_snwprintf_s( FilenameAndLocation, _countof( FilenameAndLocation ), _TRUNCATE, L"%s%s", g_strCommonFileRoot, szFilename );

DX::ThrowIfFailed( WaveFile.Open( FilenameAndLocation ) );

// Read the format header
BYTE header[64];
WAVEFORMATEX* pbWfx = reinterpret_cast<WAVEFORMATEX*>(header);

DX::ThrowIfFailed( WaveFile.GetFormat( pbWfx, sizeof(header) ) );

// Calculate number of bytes and samples in the wave
DWORD cbWaveSize = WaveFile.GetDuration();

// Read the sample data into memory
BYTE* pbWaveData = new BYTE[ cbWaveSize ];
DX::ThrowIfFailed( WaveFile.ReadSample( 0, pbWaveData, cbWaveSize, &cbWaveSize ) );  

Setting a Filepath to Your WAV File:

In order to load and use audio files, you need to be able to find the installation location of your project on the local device. Use the following code to determine your local install location. Store the installation location in a string, and append the local location of the desired audio file to the end of the install location string.

C++

std::wstring installFolder = Windows::ApplicationModel::Package::Current->InstalledLocation->Path->Data();  

Playing a WAV file:

Create an IXAudio2::CreateSourceVoice() class to submit audio data to the XAudio2 processing pipeline. You must send voice data to a mastering voice to be heard, either directly or through intermediate submix voices. Create an XAUDIO2_BUFFER to store the audio file details.

Once the xaudio2 buffer and source voice have been created and initialized, call IXAudio2SourceVoice::SubmitSourceBuffer() to add the audio buffer to the voice input queue.

Now that the voice input queue is populated, play the next sound in the queue by calling IXAudio2SourceVoice::Start().

C++

// Play the wave using a new XAudio2SourceVoice
// Create the source voice
DX::ThrowIfFailed( pXaudio2->CreateSourceVoice( &m_pSourceVoice, pbWfx, 0, XAUDIO2_DEFAULT_FREQ_RATIO, &m_VoiceContext ) );

// Submit the wave sample data using an XAUDIO2_BUFFER structure
XAUDIO2_BUFFER buffer = {0};
buffer.pAudioData     = pbWaveData;
buffer.Flags          = XAUDIO2_END_OF_STREAM;
buffer.AudioBytes     = cbWaveSize;
buffer.pContext       = pbWaveData;

// Add audio buffer to voice input queue
DX::ThrowIfFailed( m_pSourceVoice->SubmitSourceBuffer( &buffer ) );

// Play the next audio buffer in the queue
DX::ThrowIfFailed( m_pSourceVoice->Start( 0 ) );  

Terminating a WAV file:

To determine if a Wave file is finished playing, you will need the current state of the source voice. Create an XAUDIO2_VOICE_STATE struct and call IXAudio2SourceVoice::GetState(). If the audio file is finished playing, call IXAudio2SourceVoice::DestroyVoice().

C++

// Determine if sound effect has finished and handle appropriate termination
XAUDIO2_VOICE_STATE state;
m_pSourceVoice->GetState( &state, XAUDIO2_VOICE_NOSAMPLESPLAYED );
if( state.BuffersQueued == 0 )
{
  // Destroy current sound effect
  m_pSourceVoice->DestroyVoice();
}