Практическое руководство. Загрузка файлов звуковых данных в XAudio2
Примечание.
Это содержимое применяется только к классическим приложениям (и требуется редакция для работы в приложении UWP). См. документацию по CreateFile2, CreateEventEx, WaitForSingleObjectEx, SetFilePointerEx и GetOverlappedResultEx. Ознакомьтесь SoundFileReader.h
с .cpp
примером приложения Для Windows 8 и в коллекции примеров пакета SDK для Windows.
Чтобы заполнить структуры, необходимые для воспроизведения звуковых данных в XAudio2:
- Вы можете загрузить или потоковую передачу аудиофайла,
- или вы можете создать собственную волновую форму и представить ее как примеры в буфере (см . простой проект XAudio2 в полном объеме).
В этом разделе рассматриваются метод загрузки звукового файла. Следующие шаги загружают fmt
и блоки звукового файла и используют их для заполнения структуры WAVEFORMATEXTENSIBLE и структуры XAUDIO2_BUFFER.data
Подготовка к анализу звукового файла
Звуковые файлы, поддерживаемые 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; }
Заполнение структур XAudio2 с содержимым блоков RIFF
Чтобы XAudio2 воспроизводил звук с исходным голосом, он нуждается в структуре WAVEFORMATEX и структуре XAUDIO2_BUFFER . Структура WAVEFORMATEX может быть большей структурой, такой как WAVEFORMATEXTENSIBLE , которая содержит структуру WAVEFORMATEX в качестве первого элемента. Дополнительные сведения см. на странице справочника ПО WAVEFORMATEX .
В этом примере используется WAVEFORMATEXTENSIBLE для загрузки аудиофайлов PCM с более чем двумя каналами.
Следующие шаги иллюстрируют использование функций, описанных выше, для заполнения структуры WAVEFORMATEXTENSIBLE и структуры XAUDIO2_BUFFER . В этом случае звуковой файл, загруженный, содержит данные PCM и будет содержать только блок RIFF, fmt и data. Другие форматы могут содержать дополнительные типы блоков, как описано в формате файла обмена ресурсами (RIFF).
Объявите структуры WAVEFORMATEXTENSIBLE и XAUDIO2_BUFFER .
WAVEFORMATEXTENSIBLE wfx = {0}; XAUDIO2_BUFFER buffer = {0};
Откройте звуковой файл с помощью 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() );
Найдите блок 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;
Найдите блок fmt и скопируйте его содержимое в структуру WAVEFORMATEXTENSIBLE .
FindChunk(hFile,fourccFMT, dwChunkSize, dwChunkPosition ); ReadChunkData(hFile, &wfx, dwChunkSize, dwChunkPosition );
Найдите блок "данные" и считывайте его содержимое в буфер.
//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);
Заполните структуру 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