方法: ディスクからのサウンドのストリーム

Note

このコンテンツはデスクトップ アプリにのみ適用され (UWP アプリで機能するためにはリビジョンが必要になり) ます。 CreateFile2CreateEventExWaitForSingleObjectExSetFilePointerEx および GetOverlappedResultEx のドキュメントを参照してください。 現在アーカイブされている Windows SDK サンプル ギャラリーからの XAudio2 オーディオ ストリーム効果 Windows 8 サンプル アプリを参照してください。

別個のスレッドを作成してオーディオ データをストリーミングし、ストリーミング スレッドでそのオーディオ データのバッファー読み取りを実行したら、コールバックを使用してそのスレッドを制御できます。

ストリーミング スレッドでのバッファー読み取りの実行

ストリーミング スレッドでバッファー読み取りを実行するには、次の手順に従います。

  1. 読み取りバッファーの配列を作成します。

    #define STREAMING_BUFFER_SIZE 65536
    #define MAX_BUFFER_COUNT 3
    BYTE buffers[MAX_BUFFER_COUNT][STREAMING_BUFFER_SIZE];
    
  2. OVERLAPPED 構造体を初期化します。

    この構造体は、非同期ディスク読み取りがいつ終了したかを確認するために使用されます。

    OVERLAPPED Overlapped = {0};
    Overlapped.hEvent = CreateEvent( NULL, TRUE, FALSE, NULL );
    
  3. ストリーミング オーディオを再生するソース音声Start 関数を呼び出します。

    hr = pSourceVoice->Start( 0, 0 );
    
  4. 現在の読み取り位置がオーディオ ファイルの末尾を通過しない限り、ループします。

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

    ループでは、次を行います。

    1. ディスクからのデータ チャンクを現在の読み取りバッファーに読み取ります。

      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. GetOverlappedResult 関数を使用して、読み取りが完了したことを伝えるイベントを待機します。

      DWORD NumberBytesTransferred;
      ::GetOverlappedResult(hFile,&Overlapped,&NumberBytesTransferred, TRUE);
      
    3. ソース音声でキューに登録されているバッファーの数が読み取りバッファーの数より少なくなるまで待機します。

      ソース音声の状態は、GetState 関数でチェックされます。

      XAUDIO2_VOICE_STATE state;
      while( pSourceVoice->GetState( &state ), state.BuffersQueued >= MAX_BUFFER_COUNT - 1)
      {
          WaitForSingleObject( Context.hBufferEndEvent, INFINITE );
      }
      
    4. 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. 現在の読み取りバッファー インデックスを次のバッファーに設定します。

      CurrentDiskReadBuffer++;
      CurrentDiskReadBuffer %= MAX_BUFFER_COUNT;
      
  5. ループが完了したら、キューに登録されている残りのバッファーの再生が完了するまで待機します。

    残りのバッファーの再生が完了すると、サウンドが停止し、スレッドは終了するか、別のサウンドをストリーミングするために再利用できます。

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

コールバック クラスの作成

コールバック クラスを作成するには、IXAudio2VoiceCallback インターフェイスから継承するクラスを作成します。

クラスは、OnBufferEnd メソッドでイベントを設定する必要があります。 これにより、ストリーミング スレッドは、XAudio2 がオーディオ バッファーからの読み取りを完了したことをイベントから通知されるまで、スリープ状態になります。 XAudio2 でコールバックを使用する方法の詳細については、「方法: ソース音声のコールバックを使用する」を参照してください。

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

オーディオ データのストリーミング

XAudio2 のコールバック

XAudio2 プログラミング ガイド

方法: 基本的なオーディオ処理グラフの作成

方法: ソース ボイスのコールバックの使用