Compartir a través de


Tutorial: Transmitir un sonido desde el disco

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 la aplicación de ejemplo de transmisión de audio XAudio2 de Windows 8 desde la Galería de ejemplos de Windows SDK ahora archivada.

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

Realización de 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. Realice un bucle mientras la posición de lectura actual no se pasa al final del archivo de audio.

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

    En el bucle, haga lo siguiente:

    1. Lea 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 al 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 búfer siguiente.

      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 señale 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, consulte Tutorial: Uso de 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 ); }
    ...
};

Reproducción de datos de audio

Devoluciones de llamada de XAudio2

Guía de programación de XAudio2

Tutorial: Creación de un gráfico básico de procesamiento de audio

Tutorial: Uso de devoluciones de llamada de voz de origen