Anleitung: Streamen eines Sounds von einem Datenträger

Hinweis

Dieser Inhalt gilt nur für Desktop-Apps und (und würde eine Überarbeitung erfordern, um in einer UWP-App zu funktionieren). Bitte lesen Sie die Dokumentation für CreateFile2, CreateEventEx, WaitForSingleObjectEx, SetFilePointerEx und GetOverlappedResultEx. Sehen Sie sich die Windows 8-Beispiel-App für XAudio2-Audiostreameffekte aus dem jetzt archivierten Windows SDK-Beispielkatalog an.

Sie können Audiodaten in XAudio2 streamen, indem Sie einen separaten Thread erstellen und Pufferlesevorgänge der Audiodaten im Streamingthread ausführen und dann Rückrufe verwenden, um diesen Thread zu steuern.

Ausführen von Pufferlesungen im Streamingthread

Führen Sie die folgenden Schritte aus, um Pufferlesungen im Streamingthread auszuführen:

  1. Erstellen Sie ein Array von Lesepuffern.

    #define STREAMING_BUFFER_SIZE 65536
    #define MAX_BUFFER_COUNT 3
    BYTE buffers[MAX_BUFFER_COUNT][STREAMING_BUFFER_SIZE];
    
  2. Initialisieren Sie eine ÜBERLAPPENDE Struktur.

    Die Struktur wird verwendet, um zu überprüfen, wann ein asynchroner Datenträgerlesevorgang abgeschlossen ist.

    OVERLAPPED Overlapped = {0};
    Overlapped.hEvent = CreateEvent( NULL, TRUE, FALSE, NULL );
    
  3. Rufen Sie die Funktion Start in der Quellstimme auf, die das Streamingaudio wiedergibt.

    hr = pSourceVoice->Start( 0, 0 );
    
  4. Schleife, solange die aktuelle Leseposition nicht das Ende der Audiodatei passiert hat.

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

    Gehen Sie in der Schleife wie folgt vor:

    1. Lesen Sie einen Datenabschnitt vom Datenträger in den aktuellen Lesepuffer.

      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. Verwenden Sie die Funktion GetOverlappedResult, um auf das Ereignis zu warten, das signalisiert, dass der Lesevorgang abgeschlossen ist.

      DWORD NumberBytesTransferred;
      ::GetOverlappedResult(hFile,&Overlapped,&NumberBytesTransferred, TRUE);
      
    3. Warten Sie, bis die Anzahl der Puffer, die für die Quellstimme in die Warteschlange gestellt wurden, kleiner als die Anzahl der Lesepuffer ist.

      Der Status der Quellstimme wird mit der GetState-Funktion überprüft.

      XAUDIO2_VOICE_STATE state;
      while( pSourceVoice->GetState( &state ), state.BuffersQueued >= MAX_BUFFER_COUNT - 1)
      {
          WaitForSingleObject( Context.hBufferEndEvent, INFINITE );
      }
      
    4. Übermitteln Sie den aktuellen Lesepuffer an die Quellstimme mithilfe der Funktion 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. Legen Sie den aktuellen Lesepufferindex auf den nächsten Puffer fest.

      CurrentDiskReadBuffer++;
      CurrentDiskReadBuffer %= MAX_BUFFER_COUNT;
      
  5. Warten Sie nach Abschluss der Schleife, bis die Wiedergabe der verbleibenden Puffer in der Warteschlange abgeschlossen ist.

    Wenn die Wiedergabe der verbleibenden Puffer abgeschlossen ist, wird der Ton gestoppt und der Thread kann beendet oder zum Streamen eines anderen Tons erneut verwendet werden.

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

Erstellen der Rückrufklasse

Um die Rückrufklasse zu erstellen, erstellen Sie eine Klasse, die von der IXAudio2VoiceCallback-Schnittstelle erbt.

Die Klasse sollte ein Ereignis in der OnBufferEnd-Methode festlegen. Dadurch kann sich der Streaming-Thread selbst in den Ruhezustand versetzen, bis das Ereignis ihm signalisiert, dass XAudio2 mit dem Lesen aus einem Audiopuffer fertig ist. Weitere Informationen zur Verwendung von Rückrufen mit XAudio2 finden Sie unter Anleitung: Rückrufe für Quellstimmen verwenden.

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

Streamen von Audiodaten

XAudio2-Rückrufe

Programmierhandbuch für XAudio2

Anleitung: Erstellen eines einfachen Audioverarbeitungsdiagramms

Anleitung: Rückrufe für Quellstimmen verwenden