Como: Fazer o streaming de um som do disco

Observação

Esse conteúdo se aplica somente a aplicativos da área de trabalho e exigirá revisão para funcionar em um aplicativo da Windows Store. Consulte a documentação de CreateFile2, CreateEventEx, WaitForSingleObjectEx, SetFilePointerEx e GetOverlappedResultEx. Consulte o exemplo streamEffect Windows 8 da Galeria de Exemplos do SDK do Windows.

 

Você pode transmitir dados de áudio no XAudio2 criando um thread separado e executar leituras de buffer dos dados de áudio no thread de streaming e, em seguida, usar retornos de chamada para controlar esse thread.

Executando leituras de buffer no thread de streaming

Para executar leituras de buffer no thread de streaming, siga estas etapas:

  1. Crie uma matriz de buffers de leitura.

    #define STREAMING_BUFFER_SIZE 65536
    #define MAX_BUFFER_COUNT 3
    BYTE buffers[MAX_BUFFER_COUNT][STREAMING_BUFFER_SIZE];
    
  2. Inicialize uma estrutura OVERLAPPED.

    A estrutura é usada para marcar quando uma leitura de disco assíncrona é concluída.

    OVERLAPPED Overlapped = {0};
    Overlapped.hEvent = CreateEvent( NULL, TRUE, FALSE, NULL );
    
  3. Chame a função Iniciar na voz de origem que reproduzirá o áudio de streaming.

    hr = pSourceVoice->Start( 0, 0 );
    
  4. Loop enquanto a posição de leitura atual não é passada no final do arquivo de áudio.

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

    No loop, faça o seguinte:

    1. Leia uma parte dos dados do disco no buffer de leitura atual.

      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 a função GetOverlappedResult para aguardar o evento que sinaliza que a leitura foi concluída.

      DWORD NumberBytesTransferred;
      ::GetOverlappedResult(hFile,&Overlapped,&NumberBytesTransferred, TRUE);
      
    3. Aguarde até que o número de buffers enfileirados na voz de origem seja menor que o número de buffers de leitura.

      O estado da voz de origem é verificado com a função GetState .

      XAUDIO2_VOICE_STATE state;
      while( pSourceVoice->GetState( &state ), state.BuffersQueued >= MAX_BUFFER_COUNT - 1)
      {
          WaitForSingleObject( Context.hBufferEndEvent, INFINITE );
      }
      
    4. Envie o buffer de leitura atual para a voz de origem usando a função 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. Defina o índice de buffer de leitura atual como o próximo buffer.

      CurrentDiskReadBuffer++;
      CurrentDiskReadBuffer %= MAX_BUFFER_COUNT;
      
  5. Depois que o loop for concluído, aguarde até que os buffers enfileirados restantes terminem de ser reproduzidos.

    Quando os buffers restantes terminarem de ser reproduzidos, o som será interrompido e o thread poderá sair ou ser reutilizado para transmitir outro som.

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

Criando a classe de retorno de chamada

Para criar a classe de retorno de chamada, crie uma classe que herda da interface IXAudio2VoiceCallback .

A classe deve definir um evento em seu método OnBufferEnd . Isso permite que o thread de streaming se coloque em suspensão até que o evento sinalize que xAudio2 terminou de ler de um buffer de áudio. Para obter mais informações sobre como usar retornos de chamada com XAudio2, consulte Como usar retornos de chamada de voz de origem.

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

Streaming de dados de áudio

Retorno de chamadas XAudio2

Guia de Programação em XAudio2

Como: Compilar um gráfico de processamento de áudio básico

Como: Usar retornos de chamadas de voz de origem