Como: transmitir um som do disco
Observação
Este conteúdo se aplica somente a aplicativos de desktop (e exigirá revisão para funcionar em um aplicativo UWP). Consulte a documentação paraCreateFile2, CreateEventEx, WaitForSingleObjectEx, SetFilePointerEx e GetOverlappedResultEx. Confira Aplicativo de amostra de efeito de fluxo de áudio XAudio2 (Windows 8) da Galeria de Exemplos do SDK do Windows agora arquivada.
Você pode transmitir dados de áudio no XAudio2 criando um thread separado e pode executar leituras de buffer dos dados de áudio no thread de streaming e, em seguida, usar retornos de chamada para controlar esse thread.
Executar leituras de buffer no thread de streaming
Para executar leituras de buffer no thread de streaming, siga estas etapas:
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];
Inicialize uma estrutura OVERLAPPED.
A estrutura é usada para verificar quando uma leitura de disco assíncrona foi concluída.
OVERLAPPED Overlapped = {0}; Overlapped.hEvent = CreateEvent( NULL, TRUE, FALSE, NULL );
Chame a função Iniciar na voz de origem que reproduzirá o áudio de streaming.
hr = pSourceVoice->Start( 0, 0 );
Loop enquanto a posição de leitura atual não passa do fim do arquivo de áudio.
CurrentDiskReadBuffer = 0; CurrentPosition = 0; while ( CurrentPosition < cbWaveSize ) { ... }
No loop, faça o seguinte:
Leia uma parte de 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;
Use a função GetOverlappedResult para aguardar o evento que sinaliza que a leitura foi concluída.
DWORD NumberBytesTransferred; ::GetOverlappedResult(hFile,&Overlapped,&NumberBytesTransferred, TRUE);
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 ); }
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 );
Defina o índice do buffer de leitura atual para o próximo buffer.
CurrentDiskReadBuffer++; CurrentDiskReadBuffer %= MAX_BUFFER_COUNT;
Após a conclusão do loop, 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 ); }
Criar a classe de retorno de chamada
Para criar a classe de retorno de chamada, crie uma classe herdada da interface IXAudio2VoiceCallback.
A classe deve definir um evento em seu método OnBufferEnd. Isso permite que o thread de streaming entre em suspensão até que o evento sinalize que o XAudio2 terminou de fazer a leitura de um buffer de áudio. Para obter mais informações sobre como usar retornos de chamada com o XAudio2, confira 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 ); }
...
};
Tópicos relacionados