Procedura: Trasmissione di un suono in un flusso da disco

Nota

Questo contenuto si applica solo alle app desktop e richiederà la revisione per funzionare in un'app di Windows Store. Fare riferimento alla documentazione per CreateFile2, CreateEventEx, WaitForSingleObjectEx, SetFilePointerEx e GetOverlappedResultEx. Vedere l'esempio streamEffect Windows 8 dalla raccolta di esempi di Windows SDK.

 

È possibile trasmettere i dati audio in XAudio2 creando un thread separato ed eseguendo letture buffer dei dati audio nel thread di streaming e quindi usare i callback per controllare tale thread.

Esecuzione di letture del buffer nel thread di streaming

Per eseguire letture del buffer nel thread di streaming, seguire questa procedura:

  1. Creare una matrice di buffer di lettura.

    #define STREAMING_BUFFER_SIZE 65536
    #define MAX_BUFFER_COUNT 3
    BYTE buffers[MAX_BUFFER_COUNT][STREAMING_BUFFER_SIZE];
    
  2. Inizializzare una struttura OVERLAPPED.

    La struttura viene usata per controllare quando è stata completata la lettura di un disco asincrono.

    OVERLAPPED Overlapped = {0};
    Overlapped.hEvent = CreateEvent( NULL, TRUE, FALSE, NULL );
    
  3. Chiamare la funzione Start sulla voce di origine che eseguirà la riproduzione dell'audio di streaming.

    hr = pSourceVoice->Start( 0, 0 );
    
  4. Ciclo mentre la posizione di lettura corrente non viene passata alla fine del file audio.

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

    Nel ciclo eseguire le operazioni seguenti:

    1. Leggere un blocco di dati dal disco nel buffer di lettura corrente.

      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. Usare la funzione GetOverlappedResult per attendere l'evento che segnala il completamento della lettura.

      DWORD NumberBytesTransferred;
      ::GetOverlappedResult(hFile,&Overlapped,&NumberBytesTransferred, TRUE);
      
    3. Attendere che il numero di buffer accodati nella voce di origine sia minore del numero di buffer di lettura.

      Lo stato della voce di origine viene controllato con la funzione GetState .

      XAUDIO2_VOICE_STATE state;
      while( pSourceVoice->GetState( &state ), state.BuffersQueued >= MAX_BUFFER_COUNT - 1)
      {
          WaitForSingleObject( Context.hBufferEndEvent, INFINITE );
      }
      
    4. Inviare il buffer di lettura corrente alla voce di origine usando la funzione 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. Impostare l'indice del buffer di lettura corrente sul buffer successivo.

      CurrentDiskReadBuffer++;
      CurrentDiskReadBuffer %= MAX_BUFFER_COUNT;
      
  5. Al termine del ciclo, attendere il completamento della riproduzione dei buffer in coda rimanenti.

    Al termine della riproduzione dei buffer rimanenti, il suono si arresta e il thread può uscire o essere riutilizzato per trasmettere un altro suono.

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

Creazione della classe di callback

Per creare la classe di callback, creare una classe che eredita dall'interfaccia IXAudio2VoiceCallback .

La classe deve impostare un evento nel relativo metodo OnBufferEnd . In questo modo il thread di streaming può essere sospeso fino a quando l'evento segnala che XAudio2 ha terminato la lettura da un buffer audio. Per altre informazioni sull'uso dei callback con XAudio2, vedere Procedura: Usare i callback vocali di origine.

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

Streaming di dati audio

Callback di XAudio2

Guida alla programmazione di XAudio2

Procedura: Creare un grafico di elaborazione audio di base

Procedura: Usare callback di voci di origine