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. Veja a documentação de createfile2, CreateEventEx, WaitForSingleObjectEx, SetFilePointerExe GetOverlappedResultEx. Consulte o exemplo do Windows 8 do StreamEffect na Galeria de exemplos 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. Inicializar uma estrutura SOBREPOSTA.

    A estrutura é usada para verificar quando uma leitura de disco assíncrono foi concluída.

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

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

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

    No loop, faça o seguinte:

    1. Ler um bloco de dados do disco para o 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 do buffer de leitura atual para o próximo buffer.

      CurrentDiskReadBuffer++;
      CurrentDiskReadBuffer %= MAX_BUFFER_COUNT;
      
  5. Após a conclusão do loop, aguarde até que os buffers restantes da fila terminem de ser executados.

    Quando os buffers restantes terminaram de ser executados, o som é interrompido e o thread pode 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 o XAudio2 concluiu a leitura 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 ); }
    ...
};

Transmitindo 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