방법: 디스크에서 소리 스트리밍

참고

이 콘텐츠는 데스크톱 앱에만 적용되며 Windows Microsoft Store 앱에서 작동하려면 수정이 필요합니다. CreateFile2, CreateEventEx, WaitForSingleObjectEx, SetFilePointerExGetOverlappedResultEx에 대한 설명서를 참조하세요. Windows SDK 샘플 갤러리에서 StreamEffect Windows 8 샘플을 참조하세요.

 

별도의 스레드를 만들어 XAudio2에서 오디오 데이터를 스트리밍하고 스트리밍 스레드에서 오디오 데이터의 버퍼 읽기를 수행한 다음 콜백을 사용하여 해당 스레드를 제어할 수 있습니다.

스트리밍 스레드에서 버퍼 읽기 수행

스트리밍 스레드에서 버퍼 읽기를 수행하려면 다음 단계를 수행합니다.

  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. 현재 읽기 위치가 오디오 파일의 끝에 전달되지 않는 동안 Loop.

    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 프로그래밍 지침

방법: 기본 오디오 처리 그래프 빌드

방법: 원본 음성 콜백 사용