XAudio2BasicStream.cpp
上传用户:May-22
上传日期:2015-07-19
资源大小:7113k
文件大小:17k
源码类别:

DirextX编程

开发平台:

Visual C++

  1. //--------------------------------------------------------------------------------------
  2. // File: XAudio2BasicStream.cpp
  3. //
  4. // XNA Developer Connection
  5. // (C) Copyright Microsoft Corp.  All rights reserved.
  6. //--------------------------------------------------------------------------------------
  7. #define _WIN32_DCOM
  8. #define _CRT_SECURE_NO_DEPRECATE
  9. #include <windows.h>
  10. #include <xaudio2.h>
  11. #include <strsafe.h>
  12. #include <shellapi.h>
  13. #include <mmsystem.h>
  14. #include <conio.h>
  15. #include <xact.h>
  16. #include <Xact2wb.h>
  17. #define STREAMING_BUFFER_SIZE 65536
  18. #define MAX_BUFFER_COUNT 3
  19. //--------------------------------------------------------------------------------------
  20. // Helper macros
  21. //--------------------------------------------------------------------------------------
  22. #ifndef SAFE_DELETE_ARRAY
  23. #define SAFE_DELETE_ARRAY(p) { if(p) { delete[] (p);   (p)=NULL; } }
  24. #endif
  25. #ifndef SAFE_RELEASE
  26. #define SAFE_RELEASE(p)      { if(p) { (p)->Release(); (p)=NULL; } }
  27. #endif
  28. //--------------------------------------------------------------------------------------
  29. // Callback structure
  30. //--------------------------------------------------------------------------------------
  31. struct StreamingVoiceContext : public IXAudio2VoiceCallback
  32. {
  33. STDMETHOD_(void, OnVoiceProcessingPassStart) () {}
  34.     STDMETHOD_(void, OnVoiceProcessingPassEnd) () {}
  35.     STDMETHOD_(void,OnStreamEnd) () {}
  36.     STDMETHOD_(void,OnBufferStart) ( void* ) {}
  37.     STDMETHOD_(void,OnBufferEnd) ( void* ) { SetEvent( hBufferEndEvent ); }
  38.     STDMETHOD_(void,OnLoopEnd) ( void* ) {}   
  39.     HANDLE hBufferEndEvent;
  40.     StreamingVoiceContext(): hBufferEndEvent( CreateEvent( NULL, FALSE, FALSE, NULL ) ){}
  41.     ~StreamingVoiceContext(){ CloseHandle( hBufferEndEvent ); }
  42. };
  43. //--------------------------------------------------------------------------------------
  44. // Wavebank class
  45. //--------------------------------------------------------------------------------------
  46. class Wavebank
  47. {
  48. public:
  49. Wavebank() : entries(NULL) {}
  50. ~Wavebank() { Release(); }
  51. HRESULT Load( const WCHAR* wbfile );
  52. void Release() { SAFE_DELETE_ARRAY( entries ); }
  53. DWORD GetEntryCount() const { return data.dwEntryCount; }
  54. DWORD GetEntryLengthInBytes( DWORD index ) { return (index < data.dwEntryCount) ? (entries[index].PlayRegion.dwLength) : 0; }
  55. DWORD GetEntryOffset( DWORD index) { return (index < data.dwEntryCount) ? (entries[index].PlayRegion.dwOffset) : 0; }
  56. HRESULT GetEntryFormat( DWORD index, WAVEFORMATEX * pFormat );
  57. private:
  58. WAVEBANKHEADER header;
  59. WAVEBANKDATA data;
  60. WAVEBANKENTRY *entries;
  61. };
  62. //--------------------------------------------------------------------------------------
  63. // Forward declaration
  64. //--------------------------------------------------------------------------------------
  65. HRESULT FindMediaFileCch( WCHAR* strDestPath, int cchDest, LPCWSTR strFilename );
  66. //--------------------------------------------------------------------------------------
  67. // Entry point to the program
  68. //--------------------------------------------------------------------------------------
  69. int main()
  70. {
  71.     HRESULT hr;
  72.     //
  73.     // Initialize XAudio2
  74.     //
  75.     CoInitializeEx( NULL, COINIT_MULTITHREADED );
  76.     IXAudio2* pXAudio2 = NULL;
  77.     UINT32 flags = 0;
  78. #ifdef _DEBUG
  79.     flags |= XAUDIO2_DEBUG_ENGINE;
  80. #endif
  81.     if ( FAILED(hr = XAudio2Create( &pXAudio2, flags ) ) )
  82.     {
  83.         wprintf( L"Failed to init XAudio2 engine: %#Xn", hr );
  84.         CoUninitialize();
  85.         return 0;
  86.     }
  87.     //
  88.     // Create a mastering voice
  89.     //
  90.     IXAudio2MasteringVoice* pMasteringVoice = NULL;
  91.     if ( FAILED(hr = pXAudio2->CreateMasteringVoice( &pMasteringVoice ) ) )
  92.     {
  93.         wprintf( L"Failed creating mastering voice: %#Xn", hr );
  94.         SAFE_RELEASE( pXAudio2 );
  95.         CoUninitialize();
  96.         return 0;
  97.     }
  98. //
  99. // Find our wave bank file
  100. // 
  101. WCHAR wavebank[ MAX_PATH ];
  102. if ( FAILED(hr = FindMediaFileCch( wavebank, MAX_PATH, L"wavebank.xwb" ) ) )
  103. {
  104. wprintf( L"Failed to find media file (%#X)n", hr );
  105. SAFE_RELEASE( pXAudio2 );
  106. CoUninitialize();
  107. return 0;
  108. }
  109. //
  110. // Extract wavebank data (entries, formats, offsets, and sizes)
  111. //
  112. // Note we are only using XACTBLD to create sector-aligned streaming data to allow us to use
  113. // async unbuffered I/O. Raw .WAV files do not meet these requirements.
  114. //
  115. Wavebank wb;
  116. if ( FAILED(hr = wb.Load( wavebank ) ) )
  117. {
  118. wprintf( L"Failed to wavebank data (%#X)n", hr );
  119. SAFE_RELEASE( pXAudio2 );
  120. CoUninitialize();
  121. return 0;
  122. }
  123. //
  124. // Open the wavebank file for async operations
  125. //
  126. HANDLE hAsync = CreateFile( wavebank, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING,
  127.     FILE_FLAG_OVERLAPPED | FILE_FLAG_NO_BUFFERING, NULL );
  128. if ( hAsync == INVALID_HANDLE_VALUE )
  129. {
  130. wprintf( L"Failed to open wavebank for async (%#X)n", HRESULT_FROM_WIN32(GetLastError()) );
  131. SAFE_RELEASE( pXAudio2 );
  132. CoUninitialize();
  133. return 0;
  134. }
  135. //
  136. // Repeated loop through all the wavebank entries
  137. //
  138. bool exit = false;
  139. while ( !exit )
  140. {
  141. for( DWORD i=0; i < wb.GetEntryCount(); ++i )
  142. {
  143. wprintf( L"Now playing wave entry %d", i );
  144.             //
  145.             // Get the info we need to play back this wave
  146.             //
  147.             WAVEFORMATEX wfx;
  148. if( FAILED( hr = wb.GetEntryFormat( i, &wfx ) ) )
  149. {
  150. wprintf( L"nCouldn't get wave format for entry %d: error 0x%xn", i, hr );
  151. exit = true;
  152. break;
  153. }
  154.             DWORD waveOffset = wb.GetEntryOffset( i );
  155.             DWORD waveLength = wb.GetEntryLengthInBytes( i );           
  156.             //
  157.             // Create an XAudio2 voice to stream this wave
  158.             //
  159.             StreamingVoiceContext voiceContext;
  160.             IXAudio2SourceVoice* pSourceVoice;
  161.             if( FAILED(hr = pXAudio2->CreateSourceVoice( &pSourceVoice, &wfx, 0, 1.0f, &voiceContext ) ) )
  162. {
  163. wprintf( L"nError %#X creating source voicen", hr );
  164. exit = true;
  165. break;
  166. }
  167.             pSourceVoice->Start( 0, 0 );
  168.             //
  169.             // Create an overlapped structure and buffers to handle the async I/O
  170.             //
  171.             OVERLAPPED ovlCurrentRequest = {0};
  172.             ovlCurrentRequest.hEvent = CreateEvent( NULL, TRUE, FALSE, NULL );
  173.             BYTE buffers[MAX_BUFFER_COUNT][STREAMING_BUFFER_SIZE];
  174.             DWORD currentDiskReadBuffer = 0;
  175.             DWORD currentPosition = 0;
  176.             //
  177.             // This sample code shows the simplest way to manage asynchronous
  178.             // streaming. There are three different processes involved. One is the management
  179.             // process, which is what we're writing here. The other two processes are
  180.             // essentially hardware operations: disk reads from the I/O system, and 
  181.             // audio processing from XAudio2. Disk reads and audio playback both happen
  182.             // without much intervention from our application, so our job is just to make
  183.             // sure that the data being read off the disk makes it over to the audio
  184.             // processor in time to be played back. 
  185.             //
  186.             // There are two events that can happen in this system. The disk I/O system can
  187.             // signal that data is ready, and the audio system can signal that it's done
  188.             // playing back data. We can handle either or both of these events either synchronously
  189.             // (via polling) or asynchronously (via callbacks or by waiting on an event
  190.             // object). 
  191.             //
  192.             while( currentPosition < waveLength )
  193.             {
  194. wprintf( L"." );
  195.                 
  196.         if ( GetAsyncKeyState( VK_ESCAPE ) )
  197. {
  198. exit = true;
  199.     while( GetAsyncKeyState( VK_ESCAPE ) )
  200.         Sleep(10);
  201. break;
  202. }
  203.                 //
  204.                 // Issue a request. 
  205.                 //
  206.                 // Note: although the file read will be done asynchronously, it is possible for the
  207.                 // call to ReadFileEx to block for longer than you might think. If the I/O system needs
  208.                 // to read the file allocation table in order to satisfy the read, it will do that
  209.                 // BEFORE returning from ReadFileEx. That means that this call could potentially
  210.                 // block for several milliseconds! In order to get "true" async I/O you should put
  211.                 // this entire loop on a separate thread.
  212.                 //
  213.                 // Second note: async requests have to be a multiple of the disk sector size. Rather than
  214.                 // handle this conditionally, make all reads the same size but remember how many 
  215.                 // bytes we actually want and only submit that many to the voice.
  216.                 //
  217.                 DWORD cbValid = min( STREAMING_BUFFER_SIZE, waveLength - currentPosition );
  218.                 ovlCurrentRequest.Offset = waveOffset + currentPosition;
  219. if ( !ReadFileEx( hAsync, buffers[ currentDiskReadBuffer ], STREAMING_BUFFER_SIZE, &ovlCurrentRequest, NULL ) )
  220. {
  221. wprintf( L"nCouldn't start async read: error %#Xn", HRESULT_FROM_WIN32( GetLastError() ) );
  222. exit = true;
  223. break;
  224. }
  225.                 currentPosition += cbValid;
  226.                 //
  227.                 // At this point the read is progressing in the background and we are free to do
  228.                 // other processing while we wait for it to finish. For the purposes of this sample,
  229.                 // however, we'll just go to sleep until the read is done.
  230.                 //
  231.                 DWORD cb;
  232.                 GetOverlappedResult( hAsync, &ovlCurrentRequest, &cb, TRUE );
  233.                 //
  234.                 // Now that the event has been signaled, we know we have audio available. The next
  235.                 // question is whether our XAudio2 source voice has played enough data for us to give
  236.                 // it another buffer full of audio. We'd like to keep no more than MAX_BUFFER_COUNT - 1
  237.                 // buffers on the queue, so that one buffer is always free for disk I/O.
  238.                 //
  239.                 XAUDIO2_VOICE_STATE state;
  240.                 while( pSourceVoice->GetState( &state ), state.BuffersQueued >= MAX_BUFFER_COUNT - 1)
  241.                 {
  242. WaitForSingleObject( voiceContext.hBufferEndEvent, INFINITE );
  243.                 }
  244. //
  245. // At this point we have a buffer full of audio and enough room to submit it, so
  246. // let's submit it and get another read request going.
  247. //
  248. XAUDIO2_BUFFER buf = {0};
  249. buf.AudioBytes = cbValid;
  250. buf.pAudioData = buffers[currentDiskReadBuffer];
  251. if( currentPosition >= waveLength ) 
  252. buf.Flags = XAUDIO2_END_OF_STREAM;
  253. pSourceVoice->SubmitSourceBuffer( &buf );
  254. currentDiskReadBuffer++;
  255. currentDiskReadBuffer %= MAX_BUFFER_COUNT;
  256.             }
  257. if ( !exit )
  258. {
  259. wprintf( L"done streaming.." );
  260. XAUDIO2_VOICE_STATE state;
  261. while( pSourceVoice->GetState( &state ), state.BuffersQueued > 0 )
  262. {
  263. wprintf( L"." );
  264. WaitForSingleObject( voiceContext.hBufferEndEvent, INFINITE );
  265. }
  266. }
  267.             //
  268.             // Clean up
  269.             //
  270.             pSourceVoice->Stop( 0 );
  271.             pSourceVoice->DestroyVoice();
  272.             CloseHandle( ovlCurrentRequest.hEvent );
  273.             wprintf( L"stoppedn" );
  274. if ( exit )
  275. break;
  276.             Sleep(500);
  277. }
  278. }
  279.     //
  280.     // Cleanup XAudio2
  281.     //
  282.     // All XAudio2 interfaces are released when the engine is destroyed, but being tidy
  283.     pMasteringVoice->DestroyVoice();
  284. CloseHandle( hAsync );
  285.     SAFE_RELEASE( pXAudio2 );
  286.     CoUninitialize();
  287. }
  288. //--------------------------------------------------------------------------------------
  289. // Wavebank methods
  290. //--------------------------------------------------------------------------------------
  291. HRESULT Wavebank::Load( const WCHAR* wbfile )
  292. {
  293. Release();
  294. HANDLE hFile = CreateFile( wbfile, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0L, NULL );
  295. if ( hFile == INVALID_HANDLE_VALUE )
  296. return HRESULT_FROM_WIN32( GetLastError() );
  297. // Read and verify header
  298. DWORD bytes;
  299. if ( !ReadFile( hFile, &header, sizeof( header ), &bytes, NULL )
  300.  || bytes != sizeof( header ) )
  301. {
  302. CloseHandle( hFile );
  303. return HRESULT_FROM_WIN32( GetLastError() );
  304. }
  305. if ( header.dwSignature != WAVEBANK_HEADER_SIGNATURE
  306. || (WAVEBANK_HEADER_VERSION < header.dwHeaderVersion) )
  307. {
  308. CloseHandle( hFile );
  309. return E_FAIL;
  310. }
  311. // Load wavebank data
  312. SetFilePointer( hFile, header.Segments[ WAVEBANK_SEGIDX_BANKDATA ].dwOffset, 0, SEEK_SET );
  313. if ( !ReadFile( hFile, &data, sizeof( data ), &bytes, NULL )
  314.  || bytes != sizeof( data ) )
  315. {
  316. CloseHandle( hFile );
  317. return HRESULT_FROM_WIN32( GetLastError() );
  318. }
  319. // Load entries
  320. DWORD cbEntries = header.Segments[ WAVEBANK_SEGIDX_ENTRYMETADATA ].dwLength;
  321. if ( data.dwEntryCount != ( cbEntries / sizeof(WAVEBANKENTRY) ) )
  322. {
  323. CloseHandle( hFile );
  324. return E_FAIL;
  325. }
  326. entries = new WAVEBANKENTRY[ data.dwEntryCount ];
  327. SetFilePointer( hFile, header.Segments[ WAVEBANK_SEGIDX_ENTRYMETADATA ].dwOffset, 0, SEEK_SET );
  328. if ( !ReadFile( hFile, entries, cbEntries, &bytes, NULL )
  329.  || bytes != cbEntries )
  330. {
  331. SAFE_DELETE_ARRAY( entries );
  332. CloseHandle( hFile );
  333. return HRESULT_FROM_WIN32( GetLastError() );
  334. }
  335. CloseHandle( hFile );
  336. return S_OK;
  337. }
  338. HRESULT Wavebank::GetEntryFormat( DWORD index, WAVEFORMATEX * pFormat )
  339. {
  340. if (index >= data.dwEntryCount || !entries)
  341. return E_FAIL;
  342.     WAVEBANKMINIWAVEFORMAT& miniFmt = (data.dwFlags & WAVEBANK_FLAGS_COMPACT)
  343.         ? data.CompactFormat : (entries[ index ].Format);
  344.     switch( miniFmt.wFormatTag )
  345.     {
  346.     case WAVEBANKMINIFORMAT_TAG_PCM:
  347.         pFormat->wFormatTag = WAVE_FORMAT_PCM;
  348.         break;
  349.     case WAVEBANKMINIFORMAT_TAG_ADPCM:
  350.         pFormat->wFormatTag = WAVE_FORMAT_ADPCM;
  351.         break;
  352. default:
  353. // WAVEBANKMINIFORMAT_TAG_XMA is only valid for Xbox
  354. return E_FAIL;
  355.     }
  356. DWORD nBlockAlign = miniFmt.BlockAlign();
  357.     pFormat->nChannels = miniFmt.nChannels;
  358.     pFormat->wBitsPerSample = miniFmt.BitsPerSample();
  359.     pFormat->nBlockAlign = (WORD)nBlockAlign;
  360.     pFormat->nSamplesPerSec = miniFmt.nSamplesPerSec;
  361.     pFormat->nAvgBytesPerSec = miniFmt.nSamplesPerSec * nBlockAlign;
  362. return S_OK;
  363. }
  364. //--------------------------------------------------------------------------------------
  365. // Helper function to try to find the location of a media file
  366. //--------------------------------------------------------------------------------------
  367. HRESULT FindMediaFileCch( WCHAR* strDestPath, int cchDest, LPCWSTR strFilename )
  368. {
  369.     bool bFound = false;
  370.     if( NULL==strFilename || strFilename[0] == 0 || NULL==strDestPath || cchDest < 10 )
  371.         return E_INVALIDARG;
  372.     // Get the exe name, and exe path
  373.     WCHAR strExePath[MAX_PATH] = {0};
  374.     WCHAR strExeName[MAX_PATH] = {0};
  375.     WCHAR* strLastSlash = NULL;
  376.     GetModuleFileName( NULL, strExePath, MAX_PATH );
  377.     strExePath[MAX_PATH-1]=0;
  378.     strLastSlash = wcsrchr( strExePath, TEXT('\') );
  379.     if( strLastSlash )
  380.     {
  381.         StringCchCopy( strExeName, MAX_PATH, &strLastSlash[1] );
  382.         // Chop the exe name from the exe path
  383.         *strLastSlash = 0;
  384.         // Chop the .exe from the exe name
  385.         strLastSlash = wcsrchr( strExeName, TEXT('.') );
  386.         if( strLastSlash )
  387.             *strLastSlash = 0;
  388.     }
  389.     StringCchCopy( strDestPath, cchDest, strFilename );
  390.     if( GetFileAttributes( strDestPath ) != 0xFFFFFFFF )
  391.         return true;
  392.     // Search all parent directories starting at . and using strFilename as the leaf name
  393.     WCHAR strLeafName[MAX_PATH] = {0};
  394.     StringCchCopy( strLeafName, MAX_PATH, strFilename );
  395.     WCHAR strFullPath[MAX_PATH] = {0};
  396.     WCHAR strFullFileName[MAX_PATH] = {0};
  397.     WCHAR strSearch[MAX_PATH] = {0};
  398.     WCHAR* strFilePart = NULL;
  399.     GetFullPathName( L".", MAX_PATH, strFullPath, &strFilePart );
  400.     if( strFilePart == NULL )
  401.         return false;
  402.     while( strFilePart != NULL && *strFilePart != '' )
  403.     {
  404.         StringCchPrintf( strFullFileName, MAX_PATH, L"%s\%s", strFullPath, strLeafName );
  405.         if( GetFileAttributes( strFullFileName ) != 0xFFFFFFFF )
  406.         {
  407.             StringCchCopy( strDestPath, cchDest, strFullFileName );
  408.             bFound = true;
  409.             break;
  410.         }
  411.         StringCchPrintf( strFullFileName, MAX_PATH, L"%s\%s\%s", strFullPath, strExeName, strLeafName );
  412.         if( GetFileAttributes( strFullFileName ) != 0xFFFFFFFF )
  413.         {
  414.             StringCchCopy( strDestPath, cchDest, strFullFileName );
  415.             bFound = true;
  416.             break;
  417.         }
  418.         StringCchPrintf( strSearch, MAX_PATH, L"%s\..", strFullPath );
  419.         GetFullPathName( strSearch, MAX_PATH, strFullPath, &strFilePart );
  420.     }
  421.     if( bFound )
  422.         return S_OK;
  423.     // On failure, return the file as the path but also return an error code
  424.     StringCchCopy( strDestPath, cchDest, strFilename );
  425.     return HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND);
  426. }