XAudio2
是一個跨平台的API,在Xbox 360及Windows中得到支持。在Xbox 360上, XAudio2作為一個靜態庫編譯到游戲可執行文件中。在Windows上,XAudio2提供一個動態鏈接庫(DLL)。以下例子只使用了其中的一部分功能,並不全面。詳情請看微軟技術頁的XAudio2編程相關(英文)。 使用XAudio2來播放未壓縮的PCM音頻數據的過程並不復雜,主要有以下幾個步驟:
1. 建立XAudio2 引擎
使用XAudio2Create函數,該函數的功能是創建一個XAudio2對象(IXAudio2接口)。
示例:XAudio2Create( &pXAudio2, 0, XAUDIO2_DEFAULT_PROCESSOR );
2. 使用第1步建立的引擎建立MasteringVoice
使用IXAudio2成員函數CreateMasteringVoice,該函數功能是創建並設置一個MasteringVoice 原型
示例:pXAudio2->CreateMasteringVoice(&pMasterVoice);
3. 使用第一步建立的引擎建立SourceVoice(或SubmixVoice,以下按SourceVoice舉例) 使用IXAudio2成員函數CreateSourceVoice,該函數功能是創建並設置一個SourceVoice 原型
示例:
pXAudio2->CreateSourceVoice(&pSourceVoice,&format,0,XAUDIO2_DEFAULT_FREQ_RATIO,NULL,NULL,NULL);
其中format這樣設置:(位數為bits,聲道數為channels,采樣率為hz)
WAVEFORMATEX format;
format.wFormatTag = WAVE_FORMAT_PCM;//PCM格式
format.wBitsPerSample = bits;//位數
format.nChannels = channels;//聲道數
format.nSamplesPerSec = hz;//采樣率
format.nBlockAlign = bits*channels/8;//數據塊調整
format.nAvgBytesPerSec = format.nBlockAlign*hz;//平均傳輸速率
format.cbSize = 0;//附加信息
4. 呈交音頻數據
使用IXAudio2SourceVoice的成員函數SubmitSourceBuffer,該函數功能是呈交一個XAUDIO2_BUFFER 原型
示例:pSourceVoice->SubmitSourceBuffer(&XAudio2Buffer,NULL);
其中XAudio2Buffer這樣設置:
XAUDIO2_BUFFER XAudio2Buffer;
XAudio2Buffer.Flags = 0;//可以設為0或XAUDIO2_END_OF_STREAM,當設為后者時,將使
XAudio2播放完該數據塊后自動停止,不再播放下一個數據塊
XAudio2Buffer.AudioBytes = BufferSize;// 音頻數據的長度,按字節算
XAudio2Buffer.pAudioData = pBuffer;//具體音頻數據的地址,unsigned char pBuffer[]
XAudio2Buffer.PlayBegin = 0;//起始播放地址
XAudio2Buffer.PlayLength = 0;//播放長度,0為整數據塊
XAudio2Buffer.LoopBegin = 0;//循環起始位置
XAudio2Buffer.LoopLength = 0;//循環長度,按字節算
XAudio2Buffer.LoopCount = 0;//循環次數,0為不循環,255為無限循環
XAudio2Buffer.pContext = NULL;//這里的pContext用來標識該數據塊,供回調用,可以是NULL
5. 繼續呈交數據和播放數據:
播放呈交的數據使用IXAudio2SourceVoice的成員函數Start,該函數功能是開始播放。
原型:HRESULT Start(
UINT32 Flags,//必須是0
UINT32 OperationSet = XAUDIO2_COMMIT_NOW//使用XAUDIO2_COMMIT_NOW將立即生效,
使用XAUDIO2_COMMIT_ALL將掛起,等待其它的數值的OperationSet的處理完
);
示例:pSourceVoice->Start(0, XAUDIO2_COMMIT_NOW);
第5步做完之后,XAudio2將一塊接一塊地播放呈交的數據塊。我們只需不斷重復第四步,就能不斷地播放音頻數據了。需要注意的是,在XAudio2播放完某個XAudio2Buffer之前,該XAudio2Buffer以及XAudio2Buffer.pAudioData所指向的內存不能被修改或刪除,否則將發生錯誤。但是某個
XAudio2Buffer一旦被播放完,就能被修改了。為此,我們可以創建一個數組XAUDIO2_BUFFER []來循環呈交和更新數據。那怎么知道XAudio2到底播放了幾個XAudio2Buffer呢,可以使用
IXAudio2SourceVoice的成員函數
原型:GetState(
XAUDIO2_VOICE_STATE *pVoiceState,// 這里返回結構體指針
[optional] UINT32 Flags//獲取方式,可選,默認0.設為XAUDIO2_VOICE_NOSAMPLESPLAYED將
只獲取掛起(包括正在播放)的XAudio2Buffer數量,速度較快。注意:DirectX SDK版本沒有此參數 );
XAUDIO2_VOICE_STATE包含三個成員:
void * pCurrentBufferContext//對應XAUDIO2_BUFFER中的pContext
UINT32 BuffersQueued//掛起(包括正在播放)的XAudio2Buffer數量
UINT64 SamplesPlayed//已播放的樣本數
示例: pSourceVoice->GetState(&state);
6. 暫停和停止播放
暫停播放使用IXAudio2SourceVoice的成員函數:Stop
原型:HRESULT Stop(
UINT32 Flags,// 設為0或XAUDIO2_PLAY_TAILS,后者代表等待音效放完
UINT32 OperationSet = XAUDIO2_COMMIT_NOW// XAUDIO2_COMMIT_NOW立即生效
);
如果設定XAUDIO2_PLAY_TAILS,應在音效輸出完成后設定0,再Stop一次。
暫停后再次調用Start將在暫停的位置開始播放。
如果要完全停止,還需要使用IXAudio2SourceVoice的成員函數FlushSourceBuffers,該函數功能是清除掛起的XAudio2Buffer隊列。
原型:HRESULT FlushSourceBuffers();
說明:該函數使用后要到XAudio2播放完一個XAudio2Buffer才生效,建議在回調中使用。使用該函數后,XAudio2Buffer隊列計數將置0
7. IXAudio2SourceVoice的其他功能:設置聲調使用SetFrequencyRatio函數
原型:HRESULT SetFrequencyRatio(
float Ratio,//1.0為正常聲調,>1.0為高聲調快放,<1.0為低聲調慢放
UINT32 OperationSet = XAUDIO2_COMMIT_NOW
);
IXAudio2SourceVoice繼承自,所以還有許多IXAudio2Voice的功能,比如設置音量用SetVolume等。
注意:以上IXAudio2SourceVoice的成員函數中, Stop、GetState、 FlushSourceBuffers可以在回調中使用 釋放相關實例的順序與創建他們的順序相反。需要包含頭文件Xaudio2.h和Objbase.h以及鏈接ole32.lib(而不是Microsoft網站上的Xaudio2.lib)
=============================================================================================================
以上摘抄
程序說明:
1、參考DirectX SDK 例程XAudio2BasicSound編寫。
2、實現功能:驗證XAduio2播放語音流程,實現播放二進制存儲的PCM格式音頻數據文件。
- // XAudio2_test.cpp : 定義控制台應用程序的入口點。
- //
- #include "stdafx.h"
- #include <Windows.h>
- #include "XAudio2.h"
- //--------------------------------------------------------------------------------------
- // Helper macros
- //--------------------------------------------------------------------------------------
- #ifndef SAFE_DELETE_ARRAY
- #define SAFE_DELETE_ARRAY(p) { if(p) { delete[] (p); (p)=NULL; } }
- #endif
- #ifndef SAFE_RELEASE
- #define SAFE_RELEASE(p) { if(p) { (p)->Release(); (p)=NULL; } }
- #endif
- int main()
- {
- IXAudio2 *pXAudio2;//這里返回XAudio2對象的指針
- CoInitializeEx(NULL, COINIT_MULTITHREADED);
- HRESULT hr;
- if (FAILED(hr = XAudio2Create(&pXAudio2, 0, XAUDIO2_DEFAULT_PROCESSOR)))
- {
- wprintf(L"Failed to init XAudio2 engine: %#X\n", hr);
- CoUninitialize();
- return 0;
- }
- //
- // Create a mastering voice
- //
- IXAudio2MasteringVoice* pMasteringVoice = NULL;
- if (FAILED(hr = pXAudio2->CreateMasteringVoice(&pMasteringVoice)))
- {
- wprintf(L"Failed creating mastering voice: %#X\n", hr);
- SAFE_RELEASE(pXAudio2);
- CoUninitialize();
- return 0;
- }
- WAVEFORMATEX pwfx;
- pwfx.wFormatTag = WAVE_FORMAT_PCM; //PCM格式
- pwfx.wBitsPerSample = 16; //位數
- pwfx.nChannels = 1; //聲道數
- pwfx.nSamplesPerSec = 8000; //采樣率
- pwfx.nBlockAlign = 16*1 / 8; //數據塊調整
- pwfx.nAvgBytesPerSec = pwfx.nBlockAlign * 8000; //平均傳輸速率
- pwfx.cbSize = 0; //附加信息
- //
- // Play the wave using a XAudio2SourceVoice
- //
- // Create the source voice
- IXAudio2SourceVoice* pSourceVoice;
- if (FAILED(hr = pXAudio2->CreateSourceVoice(&pSourceVoice, &pwfx)))
- {
- wprintf(L"Error %#X creating source voice\n", hr);
- return hr;
- }
- FILE *fp;
- //SPEECHDECODE文件說明: 該文件為保存的二進制音頻數據,格式:16位,采樣率8K.
- int re = fopen_s(&fp, "SPEECHDECODE", "rb");
- XAUDIO2_BUFFER XBuffer[4];
- BYTE *pRBuffer[4];
- int i = 0;
- for (i = 0; i < 4; i++)
- {
- pRBuffer[i] = (BYTE *)malloc(640);
- fread(pRBuffer[i], 640, 1, fp);
- XBuffer[i].AudioBytes = 640;
- XBuffer[i].Flags = 0;
- XBuffer[i].LoopBegin = 0;
- XBuffer[i].LoopCount = 0;
- XBuffer[i].LoopLength = 0;
- XBuffer[i].pAudioData = pRBuffer[i];
- XBuffer[i].pContext = (void*)new int[1];
- XBuffer[i].PlayBegin = 0;
- XBuffer[i].PlayLength = 0;
- if (FAILED(hr = pSourceVoice->SubmitSourceBuffer(&XBuffer[i])))
- {
- wprintf(L"Error %#X submitting source buffer\n", hr);
- pSourceVoice->DestroyVoice();
- return hr;
- }
- }
- hr = pSourceVoice->Start(0);
- BOOL isRunning = TRUE;
- i = 0;
- while (SUCCEEDED(hr) && isRunning)
- {
- XAUDIO2_VOICE_STATE state;
- //檢查播放緩存區緩沖塊數量,當緩存塊數量不足時,則新加入
- pSourceVoice->GetState(&state);
- isRunning = (state.BuffersQueued > 0) != 0;
- // Wait till the escape key is pressed
- if (GetAsyncKeyState(VK_ESCAPE)) //Esc鍵退出
- break;
- printf("state.BuffersQueued = %d\n", state.BuffersQueued);
- if (state.BuffersQueued <= 2)
- {
- if (fread(pRBuffer[i], 640, 1, fp) <= 0) break;
- XBuffer[i].pAudioData = pRBuffer[i];
- if (FAILED(hr = pSourceVoice->SubmitSourceBuffer(&XBuffer[i])))
- {
- wprintf(L"Error %#X submitting source buffer\n", hr);
- pSourceVoice->DestroyVoice();
- return hr;
- }
- i = (i + 1) % 4;
- }
- Sleep(10);
- }
- END:
- hr = pSourceVoice->Stop(0);
- pSourceVoice->DestroyVoice();
- for (i = 0; i < 4; i++)
- {
- if (pRBuffer[i]) free(pRBuffer[i]);
- }
- if (pXAudio2) {
- pXAudio2->Release();
- pXAudio2 = NULL;
- }
- CoUninitialize();
- return 0;
- }