我过去曾使用过这个XAudio2流媒体教程,并成功地创建了一个程序来流式传输和播放磁盘音频(https://msdn.microsoft.com/en-us/library/windows/desktop/ee415791(v=vs.85).aspx)。
现在我正在修改代码,一些奇怪的事情对我来说很突出。他们创建一个单独的线程,从文件中读取小块中的音频数据,并将这些块提交到语音缓冲区。太好了,现在主线程可以在播放音频时做任何需要的事情,但为什么要在流式处理线程中使用异步文件读取呢?据我所知,每次循环迭代都会执行文件读取,线程会立即等待,直到块完全读取后再将其提交到语音缓冲区。
他们正在做的事情是否有优势或没有必要?
XAudio2 示例代码使用"异步"而不是"阻塞"I/O 的原因是最佳实践。在"工作线程"中完成的原因主要是为了简单起见,因为在游戏中,音频通常由它自己的线程处理,以避免故障并保持渲染/更新循环的独特性。
您可以将异步/提交作为Update
循环的一部分进行处理,但前提是您确定它总是得到足够快的处理以避免故障。这实际上是我在GitHub上使用XAudio2流式处理示例的最新旧版DirectX SDK版本所采用的方法
更花哨的实现可以使用
ReadFileEx
,但工作线程方法更简单。此外,由于 Windows 8 中如何实现 API 分区的一些怪癖,Windows 应用商店平台/Windows Phone 8/UWP 不支持ReadFileEx
,因此教程/示例避免使用它,以防止指向不适用于所有Microsoft平台的方向。FWIW,ReadFileEx
和WriteFileEx
在Windows 10周年更新(14393)中被添加回UWP。
请注意,在如何处理ERROR_IO_PENDING
方案时,在等待重叠 I/O 的事件中存在一些怪癖。这在Windows 8或更高版本中通过GetOverlappedResultEx
功能进行了改进和简化。对于音频 DirectX 工具包中的波形库读取器,我使用此模式为较旧和较新的平台进行构建:
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() );
}