Share via


So wird's gemacht: Streamen von Sound von einem Datenträger

Hinweis

Dieser Inhalt gilt nur für Desktop-Apps und erfordert eine Überarbeitung, um in einer Windows Store-App zu funktionieren. Weitere Informationen finden Sie in der Dokumentation zu CreateFile2, CreateEventEx, WaitForSingleObjectEx, SetFilePointerEx und GetOverlappedResultEx. Sehen Sie sich das StreamEffect-Windows 8 Beispiel aus dem 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 Pufferlesevorgängen im Streamingthread

Führen Sie die folgenden Schritte aus, um Pufferlesevorgänge 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 Startfunktion für die Quellstimme auf, die das Streamingaudio abgibt.

    hr = pSourceVoice->Start( 0, 0 );
    
  4. Schleife, während die aktuelle Leseposition nicht am Ende der Audiodatei übergeben wird.

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

    Führen Sie in der Schleife die folgenden Schritte aus:

    1. Liest einen Datenblock 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 GetOverlappedResult-Funktion , 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 in der Quellstimme in die Warteschlange eingereiht wurden, kleiner als die Anzahl der Lesepuffer ist.

      Der Zustand 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 mithilfe der SubmitSourceBuffer-Funktion an die Quellstimme.

      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. Nachdem die Schleife abgeschlossen ist, warten Sie, bis die restlichen Puffer in der Warteschlange mit der Wiedergabe fertig sind.

    Wenn die restlichen Puffer die Wiedergabe beendet haben, wird der Sound beendet, und der Thread kann beendet oder wiederverwendet werden, um einen anderen Sound zu streamen.

    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 ihrer OnBufferEnd-Methode festlegen. Dadurch kann sich der Streamingthread in den Ruhezustand versetzen, bis das Ereignis signalisiert, dass XAudio2 das Lesen aus einem Audiopuffer beendet hat. Weitere Informationen zur Verwendung von Rückrufen mit XAudio2 finden Sie unter Vorgehensweise: Verwenden von Quell-VoIP-Rückrufen.

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

Streaming von Audiodaten

XAudio2-Rückrufe

XAudio2-Programmieranleitung

So wird's gemacht: Erstellen eines grundlegenden Audioverarbeitungsdiagramms

So wird's gemacht: Verwenden der Rückrufe für Quellstimmen