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 aplicación de Windows Store. Consulte la documentación de CreateFile2, CreateEventEx, WaitForSingleObjectEx, SetFilePointerEx y GetOverlappedResultEx. Consulte el ejemplo de Windows 8 StreamEffect de la Galería de ejemplos del SDK de Windows.

 

Puede transmitir datos de audio en XAudio2 creando un subproceso independiente y realizando 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 SUPERPUESTA.

    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. Loop 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. 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 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 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 indique 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 How to: Use Source Voice Callbacks.

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