Compartir a través de


Tutorial: Cargar archivos de datos de audio en XAudio2

Nota:

Este contenido solo se aplica a las aplicaciones de escritorio (y se necesita revisarlo para poder funcionar en una aplicación para UWP). Consulte la documentación sobre CreateFile2, CreateEventEx, WaitForSingleObjectEx, SetFilePointerEx y GetOverlappedResultEx. Consulte SoundFileReader.h y .cpp en la aplicación de ejemplo BasicSound Windows 8 desde la Galería de ejemplos del SDK de Windows ahora archivada.

Para rellenar las estructuras necesarias para reproducir datos de audio en XAudio2:

  • Puede cargar o transmitir un archivo de audio,
  • o puede generar una forma de onda propia y representar esa forma de onda como muestras en un búfer (consulte Un sencillo proyecto XAudio2 completo).

Este tema se centra en el método de carga de un archivo de audio. En los pasos siguientes se cargan los fragmentos fmt y data de un archivo de audio y se usan para rellenar una estructura WAVEFORMATEXTENSIBLE y una estructura XAUDIO2_BUFFER.

Preparativos para analizar el archivo de audio

Los archivos de audio compatibles con XAudio2 tienen el formato de archivo de intercambio de recursos (RIFF). RIFF se describe en el tema Formato de archivo de intercambio de recursos (RIFF). Los datos de audio de un archivo RIFF se cargan mediante la búsqueda del fragmento RIFF y luego examinan el fragmento para buscar fragmentos concretos contenidos en el fragmento RIFF. Las siguientes funciones son ejemplos de código para buscar fragmentos y cargar datos contenidos en los fragmentos.

  • Buscar un fragmento en un archivo 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;
    }
    
  • Leer datos en un fragmento después de que se haya localizado.

    Una vez que se encuentra el fragmento deseado, los datos se pueden leer ajustando el puntero del archivo al principio de la sección de datos del fragmento. La función para leer los datos de un fragmento una vez que se encuentra podría verse así.

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

Rellenar estructuras de XAudio2 con el contenido de fragmentos RIFF

Para que XAudio2 reproduzca audio con una voz de origen, necesita una estructura WAVEFORMATEX y una estructura XAUDIO2_BUFFER. La estructura WAVEFORMATEX puede ser una estructura mayor, como WAVEFORMATEXTENSIBLE, que contiene una estructura WAVEFORMATEX como su primer miembro. Consulte la página de referencia de WAVEFORMATEX para obtener más información.

En este ejemplo, se usa WAVEFORMATEXTENSIBLE para permitir la carga de archivos de audio PCM con más de dos canales.

En el procedimiento siguiente se ve cómo se usan las funciones descritas anteriormente para rellenar una estructura WAVEFORMATEXTENSIBLE y una estructura XAUDIO2_BUFFER. En este caso, el archivo de audio que se carga contiene datos PCM y solo contendrá un fragmento "RIFF", "fmt" y "data". Otros formatos pueden incluir tipos de fragmentos adicionales, tal y como se describe en Formato de archivo de intercambio de recursos (RIFF).

  1. Declare las estructuras WAVEFORMATEXTENSIBLE y XAUDIO2_BUFFER.

    WAVEFORMATEXTENSIBLE wfx = {0};
    XAUDIO2_BUFFER buffer = {0};
    
  2. Abra el archivo de audio con 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. Busque el fragmento "RIFF" en el archivo de audio y compruebe el tipo de archivo.

    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. Busque el fragmento "fmt" y copie su contenido en una estructura WAVEFORMATEXTENSIBLE.

    FindChunk(hFile,fourccFMT, dwChunkSize, dwChunkPosition );
    ReadChunkData(hFile, &wfx, dwChunkSize, dwChunkPosition );
    
  5. Busque el fragmento "data" y lea su contenido en un búfer.

    //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. Rellene una estructura 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