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 Windows Store. Vedere la documentazione per CreateFile2, CreateEventEx, WaitForSingleObjectEx, SetFilePointerEx e GetOverlappedResultEx. Vedere l'esempio streamEffect Windows 8 dalla raccolta esempi di SDK Windows.

 

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

Esecuzione di letture del buffer nel thread di streaming

Per eseguire le 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 verificare 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 verrà riprodotta l'audio di streaming.

    hr = pSourceVoice->Start( 0, 0 );
    
  4. Loop 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 che la lettura è stata completata.

      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 callback

Per creare la classe 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 completato la lettura da un buffer audio. Per altre informazioni sull'uso dei callback con XAudio2, vedere Procedura: Usare 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