Cómo: transmitir un sonido de un disco

Nota

Este contenido solo se aplica a las aplicaciones de escritorio y requerirá una revisión para funcionar en una Windows Store. Consulte la documentación de CreateFile2, CreateEventEx, WaitForSingleObjectEx, SetFilePointerExy GetOverlappedResultEx. Consulte el ejemplo de Windows 8 StreamEffect en la galería de ejemplos Windows SDK de .

Puede transmitir datos de audio en XAudio2 mediante la creación de un subproceso independiente y realizar lecturas en búfer de los datos de audio en el subproceso de streaming y, a continuación, usar devoluciones de llamada para controlar ese subproceso.

Realizar lecturas de búfer en el subproceso de streaming

Para realizar lecturas de búfer en el subproceso de streaming, siga estos pasos:

  1. Cree una matriz de búferes de lectura.

    #define STREAMING_BUFFER_SIZE 65536
    #define MAX_BUFFER_COUNT 3
    BYTE buffers[MAX_BUFFER_COUNT][STREAMING_BUFFER_SIZE];
    
  2. Inicialice una estructura OVERLAPPED.

    La estructura se usa para comprobar cuándo ha finalizado una lectura de disco asincrónica.

    OVERLAPPED Overlapped = {0};
    Overlapped.hEvent = CreateEvent( NULL, TRUE, FALSE, NULL );
    
  3. Llame a la función Start en la voz de origen que reproducirá el audio de streaming.

    hr = pSourceVoice->Start( 0, 0 );
    
  4. Bucle mientras no se pasa la posición de lectura actual al final del archivo de audio.

    CurrentDiskReadBuffer = 0;
    CurrentPosition = 0;
    while ( CurrentPosition < cbWaveSize )
    {
        ...
    }
    

    En el bucle , haga lo siguiente:

    1. Lee un fragmento de datos del disco en el búfer de lectura actual.

      DWORD dwRead;
      if( SUCCEEDED(hr) && 0 == ReadFile( hFile, pData, dwDataSize, &dwRead, pOverlapped ) )
          hr = HRESULT_FROM_WIN32( GetLastError() );
          DWORD cbValid = min( STREAMING_BUFFER_SIZE, cbWaveSize - CurrentPosition );
          DWORD dwRead;
          if( 0 == ReadFile( hFile, buffers[CurrentDiskReadBuffer], STREAMING_BUFFER_SIZE, &dwRead, &Overlapped ) )
              hr = HRESULT_FROM_WIN32( GetLastError() );
          Overlapped.Offset += cbValid;
      
          //update the file position to where it will be once the read finishes
          CurrentPosition += cbValid;
      
    2. Use la función GetOverlappedResult para esperar a que finalice el evento que indica que la lectura ha finalizado.

      DWORD NumberBytesTransferred;
      ::GetOverlappedResult(hFile,&Overlapped,&NumberBytesTransferred, TRUE);
      
    3. Espere a que el número de búferes en cola en la voz de origen sea menor que el número de búferes de lectura.

      El estado de la voz de origen se comprueba con la función GetState.

      XAUDIO2_VOICE_STATE state;
      while( pSourceVoice->GetState( &state ), state.BuffersQueued >= MAX_BUFFER_COUNT - 1)
      {
          WaitForSingleObject( Context.hBufferEndEvent, INFINITE );
      }
      
    4. Envíe el búfer de lectura actual a la voz de origen mediante la función SubmitSourceBuffer.

      XAUDIO2_BUFFER buf = {0};
      buf.AudioBytes = cbValid;
      buf.pAudioData = buffers[CurrentDiskReadBuffer];
      if( CurrentPosition >= cbWaveSize )
      {
          buf.Flags = XAUDIO2_END_OF_STREAM;
      }
      pSourceVoice->SubmitSourceBuffer( &buf );
      
    5. Establezca el índice de búfer de lectura actual en el siguiente búfer.

      CurrentDiskReadBuffer++;
      CurrentDiskReadBuffer %= MAX_BUFFER_COUNT;
      
  5. Una vez finalizado el bucle, espere a que los búferes en cola restantes terminen de reproducirse.

    Cuando los búferes restantes han terminado de reproducirse, el sonido se detiene y el subproceso puede salir o reutilizarse para transmitir otro sonido.

    XAUDIO2_VOICE_STATE state;
    while( pSourceVoice->GetState( &state ), state.BuffersQueued > 0 )
    {
        WaitForSingleObjectEx( Context.hBufferEndEvent, INFINITE, TRUE );
    }
    

Creación de la clase de devolución de llamada

Para crear la clase de devolución de llamada, cree una clase que herede de la interfaz IXAudio2VoiceCallback.

La clase debe establecer un evento en su método OnBufferEnd. Esto permite que el subproceso de streaming se ponga en suspensión hasta que el evento le indica que XAudio2 ha terminado de leer desde un búfer de audio. Para obtener más información sobre el uso de devoluciones de llamada con XAudio2, vea Cómo: Usar devoluciones de llamada de voz de origen.

struct StreamingVoiceContext : public IXAudio2VoiceCallback
{
    HANDLE hBufferEndEvent;
    StreamingVoiceContext(): hBufferEndEvent( CreateEvent( NULL, FALSE, FALSE, NULL ) ){}
    ~StreamingVoiceContext(){ CloseHandle( hBufferEndEvent ); }
    void OnBufferEnd( void* ){ SetEvent( hBufferEndEvent ); }
    ...
};

Streaming de datos de audio

Devoluciones de llamadas de XAudio2

Guía de programación de XAudio2

Cómo: crear un gráfico de procesamiento de audio básico

Cómo: usar devoluciones de llamadas de voces de origen