DirectSound是DirectX組件之一,提供了對音頻設備的捕獲和播放能力,同時它也是唯一幾個支持Xp系統的音頻技術之一。 DirectSound主要有以下特點: 優點: 播放音頻 低延遲 。 硬體資源控制 。 同時 播放 多個 聲音。 控制硬體緩衝區的使用 優先順序 (DirectSound使 ...
DirectSound是DirectX組件之一,提供了對音頻設備的捕獲和播放能力,同時它也是唯一幾個支持Xp系統的音頻技術之一。 DirectSound主要有以下特點:
優點:
- 播放音頻低延遲。
- 硬體資源控制。
- 同時播放多個聲音。
- 控制硬體緩衝區的使用優先順序(DirectSound使用緩衝區來播放音頻)。
- 模擬3D音頻環境。
- 動態更改音效(回聲、和聲等)。
- 捕獲音頻輸入設備聲音位wav(多為PCM數據,未經壓縮)。
缺點:
- 只能播放wav音頻文件。
這裡我們說說設備操作這一塊兒。
1. 輸出設備操作
在DirectSound中,一個設備對象就代表一個音頻設備,播放設備對象對應播放設備,輸入設備對象對應輸入設備。因為DirectSound使用COM協議,因此每個設備對象都用介面來表示。這裡IDirectSound8這個介面就代表了一個輸出設備對象,應用程式可以對同一個音頻設備創建多個設備對象來進行音頻輸出操作。舊版本的DirectSound使用的是IDirectSound介面,相比前者少了一些功能。
1.1 枚舉
HRESULT WINAPI DirectSoundEnumerateW(In LPDSENUMCALLBACKW pDSEnumCallback, In_opt LPVOID pContext);
typedef BOOL (CALLBACK *LPDSENUMCALLBACKW)(LPGUID, LPCWSTR, LPCWSTR, LPVOID);
我們通過DirectSoundEnumerateW這個函數來枚舉,該函數需要傳入一個回調函數(原型見上),當枚舉到一個設備時該回調會被調用。如果我們想繼續枚舉,需要在這個回調用中返回TRUE來告訴系統,否則返回FALSE。另一個參數pContext
允許用戶傳入額外的參數,傳入回調函數的最後一個實參就是這個pContext
。枚舉時,DirectSound會將預設也認作一個單獨的設備來對待,因此預設設備會被重覆枚舉一次。當設備被作為預設設備枚舉時,它的GUID和設備描述字元串都為空,需要小心處理,這裡我直接跳過了該次枚舉:
if (DirectSoundEnumerateW(enumerateCallback, nullptr) != DS_OK) {
...
}
BOOL CALLBACK DirectSoundBasic::enumerateCallback(LPGUID guid,
LPCWSTR deviceDescription,
LPCWSTR deviceDriverModule,
LPVOID context)
{
Q_UNUSED(context);
// if primary device, skip it
if (guid == nullptr) return TRUE;
...
}
1.2 創建設備對象
HRESULT WINAPI DirectSoundCreate8(In_opt LPCGUID pcGuidDevice, Outptr LPDIRECTSOUND8 *ppDS8, Pre_null LPUNKNOWN pUnkOuter);
調用DirectSoundCreate8函數,我們可以創建一個設備對象,通過傳入一個枚舉設備時獲得的GUID,函數會返給我們一個IDirectSound8介面代表設備對象:
IDirectSound8* directSound8;
if (DirectSoundCreate8(guid, &directSound8, NULL) != DS_OK) {
std::wcout << L"[error] DirectSoundCreate8 call error!";
return TRUE; // if error, skip this device
}
1.3 設置設備對象優先順序
HRESULT IDirectSound8::SetCooperativeLevel(HWND hwnd, DWORD dwLevel)
在使用設備對象創建緩衝區(用來捕獲、播放音頻)之前,我們需要設置設備對象的協作級別。這個協作級別相當於用戶對設備進行操作的優先順序,分為:
DSSCL_EXCLUSIVE: 互斥級別。對於DirectX8.0以前版本,僅播放當前應用的音頻數據,其他應用的聲音不會被播放;對於DirectX8.0級以後版本,同DSSCL_PRIORITY版本。
- DSSCL_NORMAL: 普通級別,這種級別下的應用程式具有最平滑的多任務和資源共用表現,但是這種應用不能更改主緩衝區音頻數據格式,輸出音頻格式被限製為8位數據。在DirectSound中,次緩衝區用來填充應用程式需要播放的聲音,主緩衝區會對多個次緩衝區(可能是本應用的,也可能是其他應用的)進行混音,然後用音效卡輸出播放。
- DSSCL_PRIORITY: 優先順序別,可以更改主緩衝區數據格式。
DSSCL_WRITEPRIMARY:寫主緩衝區級別,應用可以直接寫入主緩衝區,此時所有次緩衝區不會被播放(如果設備的驅動是DirectSound模擬出來的,則不能設置該級別)。
註意該函數需要傳入一個視窗句柄,因為我們今天只介紹DirectSound的基本操作,我直接傳入桌面視窗的句柄並設定位DSSCL_NORMAL優先順序:
if (directSound8->SetCooperativeLevel(GetDesktopWindow(), DSSCL_NORMAL) != DS_OK) {
std::wcout << L"[error] SetCooperativeLevel call error!";
return TRUE;
}
1.4 設備能力
HRESULT IDirectSound8::GetCaps(LPDSCAPS pDSCaps)
不同的音頻播放設備具有不同的能力,DirectSound允許我們查詢設備的能力:
- 是否經過Microsoft認證。
- 知否支持最小最大採樣率之間的所有採樣率。
- 當沒有DirectSound驅動時模擬驅動。
- 主次緩衝區格式(16位、8位)。
- 主次緩衝區聲道支持(單聲道、立體聲即多聲道)。
- 不精準的數據(某些音效卡不支持):
- 緩衝區(靜態緩衝區、流緩衝區、3D緩衝區)最大數、空閑數。
- 音效卡上的總記憶體數量、空閑記憶體數量、最大空閑塊大小,
我們傳給GetCaps一個DSCAPS結構體地址,然後系統就幫我們填充相應的數據,調用GetCaps前需要將DSCAPS結構體的dwSize設置為DSCAPS的大小:
DSCAPS deviceCapability = { sizeof(deviceCapability) };
if (directSound8->GetCaps(&deviceCapability) != DS_OK) {
std::wcout << L"[error] GetCaps call error!";
return TRUE;
}
1.5 播放器配置
HRESULT IDirectSound8::GetSpeakerConfig(LPDWORD pdwSpeakerConfig)
HRESULT IDirectSound8::SetSpeakerConfig(LPDWORD pdwSpeakerConfig)
播放器配置只能是以下之一:
- DSSPEAKER_5POINT1_SURROUND、DSSPEAKER_5POINT1_BACK: 家庭影院配置,5個環繞揚聲器,1個低音炮。
- DSSPEAKER_DIRECTOUT:直接播放。
- DSSPEAKER_HEADPHONE:頭戴式耳機。
- DSSPEAKER_MONO:單聲道揚聲器。
- DSSPEAKER_QUAD:4聲道播放器。
- DSSPEAKER_STEREO:立體聲播放器。
- DSSPEAKER_SURROUND:環繞播放器。
- DSSPEAKER_7POINT1_WIDE、DSSPEAKER_7POINT1_SURROUND:家庭影院配置,7個環繞揚聲器,1個低音炮。
雖然MSDN文檔沒有寫清楚,但是通過查以上巨集定義我們發現它們是按大小順序定義的,因此不可能通過OR|
來包含多種可能,例子中如果調用出錯直接返回TRUE表示我們繼續枚舉設備並繼續查詢那些設備能力:
DWORD deviceSpeakerConfiguration;
if (directSound8->GetSpeakerConfig(&deviceSpeakerConfiguration) != DS_OK) {
std::wcout << L"[error] GetSpeakerConfig call error!";
return TRUE;
}
2. 運行結果
這次我們用GUI界面來顯示實例運行的結果(出於方便考慮,以後我會用控制台來顯示示例),為防止用戶誤操作更改顯示的數據我將大部分控制項都disable了:
完整代碼見鏈接。