操作说明:在 XAudio2 中加载音频数据文件

注意

此内容仅适用于桌面应用(并且需要修订才能在 UWP 应用中运行)。 请参阅 CreateFile2CreateEventExWaitForSingleObjectExSetFilePointerExGetOverlappedResultEx 的文档。 请参阅现已存档的 Windows SDK 示例库中的 BasicSound Windows 8 示例应用中的 SoundFileReader.h.cpp

若要填充在 XAudio2 中播放音频数据所需的结构,请执行以下操作:

  • 可以加载或流式传输音频文件,
  • 或者,可以生成自己的波形,并将该波形表示为缓冲区中的样本(请参阅完整的简单 XAudio2 项目)。

本主题重点介绍加载音频文件的方法。 以下步骤加载音频文件的 fmtdata 区块,并使用它们填充 WAVEFORMATEXTENSIBLE 结构和 XAUDIO2_BUFFER 结构。

准备分析音频文件

XAudio2 支持的音频文件使用资源交换文件格式 (RIFF) 进行格式化。 资源交换文件格式 (RIFF) 主题中介绍了 RIFF。 RIFF 文件中的音频数据是通过查找 RIFF 区块,然后循环访问区块查找 RIFF 区块中包含的单个区块来加载的。 以下函数是用于查找区块并加载区块中包含的数据的代码示例。

  • 若要在 RIFF 文件中查找区块,请执行以下操作:

    #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;
    }
    
  • 在找到区块后读取区块中的数据。

    一旦找到所需的区块,就可以通过将文件指针调整到区块的数据段的开头来读取其数据。 一旦找到区块,从区块中读取数据的函数可能如下所示。

    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;
    }
    

使用 RIFF 区块的内容填充 XAudio2 结构

为了让 XAudio2 使用源语音播放音频,它需要 WAVEFORMATEX 结构和 XAUDIO2_BUFFER 结构。 WAVEFORMATEX 结构可能是一个更大的结构,例如 WAVEFORMATEXTENSIBLE,它包含一个 WAVEFORMATEX 结构作为其第一个成员。 有关详细信息,请参阅 WAVEFORMATEX 引用页。

在本例中,使用 WAVEFORMATEXTENSIBLE 允许加载具有两个以上的频道的 PCM 音频文件。

以下步骤说明了如何使用上述函数填充 WAVEFORMATEXTENSIBLE 结构和 XAUDIO2_BUFFER 结构。 在这种情况下,加载的音频文件包含 PCM 数据,并且仅包含“RIFF”、“fmt”和“data”区块。 其他格式可能包含其他区块类型,如资源交换文件格式 (RIFF) 中所述。

  1. 声明 WAVEFORMATEXTENSIBLEXAUDIO2_BUFFER 结构。

    WAVEFORMATEXTENSIBLE wfx = {0};
    XAUDIO2_BUFFER buffer = {0};
    
  2. 使用 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() );
    
  3. 在音频文件中找到“RIFF”区块,并检查文件类型。

    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;
    
  4. 找到“fmt”区块,并将其内容复制到 WAVEFORMATEXTENSIBLE 结构中。

    FindChunk(hFile,fourccFMT, dwChunkSize, dwChunkPosition );
    ReadChunkData(hFile, &wfx, dwChunkSize, dwChunkPosition );
    
  5. 找到“数据”区块,并将其内容读入缓冲区。

    //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);
    
  6. 填充 XAUDIO2_BUFFER 结构。

    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