I've used this XAudio2 streaming tutorial in the past with success in creating a program to stream and play audio from disk (https://msdn.microsoft.com/en-us/library/windows/desktop/ee415791(v=vs.85).aspx).
Now that I am reworking the code, something odd stands out to me. They create a separate thread to read the audio data in small chunks from file and submit those chunks to the voice buffer. Great, now the main thread can do whatever it needs while the audio plays, but why also use asynchronous file reads in the streaming thread? As far as I can tell, a file read is performed at each loop iteration and the thread immediately waits until the chunk is fully read before submitting it to the voice buffer.
Is there an advantage to what they are doing or is it unnecessary?
The reason the XAudio2 sample code uses 'asynchronous' rather than 'blocking' I/O is best-practice. The reason it's done in a 'worker thread' is mostly for simplicity since in a game typically the audio is handled by it's own thread to avoid glitches and keeping the render/update loop distinct.
You could handle the async/submit as part of your Update
loop, but only if you were sure it would always get processed quickly enough to avoid the glitches. This is in fact the approach I took with the latest legacy DirectX SDK version of the XAudio2 streaming sample on GitHub
A fancier implementation could have used
ReadFileEx
, but the worker-thread approach was simpler. Also due to some quirks of how API partitioning was implemented back in Windows 8,ReadFileEx
was not supported on the Windows Store platform / Windows phone 8 / UWP so the tutorials/samples avoided it to keep from pointing you in a direction that wasn't applicable to all the Microsoft platforms. FWIW,ReadFileEx
andWriteFileEx
got added back to UWP in the Windows 10 Anniversary Update (14393).
Note there are some quirks in the event waiting with overlapped I/O in how ERROR_IO_PENDING
scenarios are handled. This is improved & simplified in Windows 8 or later by the GetOverlappedResultEx
function. For my wave bank reader in DirectX Tool Kit for Audio, I used this pattern to build for both older and newer platforms:
bool wait = false;
if( !ReadFile( hFile.get(), &m_data, sizeof( m_data ), nullptr, &request ) )
{
DWORD error = GetLastError();
if ( error != ERROR_IO_PENDING )
return HRESULT_FROM_WIN32( error );
wait = true;
}
#if (_WIN32_WINNT >= _WIN32_WINNT_WIN8)
result = GetOverlappedResultEx( hFile.get(), &request, &bytes, INFINITE, FALSE );
#else
if ( wait )
(void)WaitForSingleObject( m_event.get(), INFINITE );
result = GetOverlappedResult( hFile.get(), &request, &bytes, FALSE );
#endif
if ( !result || ( bytes != sizeof( m_data ) ) )
{
return HRESULT_FROM_WIN32( GetLastError() );
}