對於常見的音頻播放,使用XAudio2足夠了。
時間是把殺豬刀,滑稽的是我成了豬
早在Windows Vista中,M$推出了新的音頻架構UAA,其中的CoreAudio接替了DSound、WaveXxx、MediaFundation,通過Core Audio APIs,Windows的音頻性能可以與MacOS X相媲美(手動偷笑)。
Universal Audio Architecture (UAA)
CoreAudio屬於UAA,只在用戶層進行一系列音頻處理,而系統內核只負責遞交緩沖數據給音頻驅動。
在UAA出現之前,程序跑起來是這樣的:
現在程序在UAA跑起來是這樣的:
WASAPI和XAudio2
Core Audio APIs的明星:WASAPI
WASAPI可以不進行SRC直接輸出,還能提供極低的音頻延遲。
為了降低音頻延遲,更像AISO,WASAPI的使用方式分為兩種:一種是push,組成緩沖區隊列,常用於音頻播放。一種是event(必須獨占),由硬件時鍾或音頻API提供事件來驅動你提交音頻數據(好復雜啊真心沒用過,緩存欠載怎么辦),這樣就可以大幅降低音頻延遲,好像連DMA都會跳過(未查證),適用於游戲、實時混音等對實時性要求比較苛刻的場合。
在保真度上,WASAPI被Foobar2000用戶吹得神乎其神。而它充其量就是少了個SRC過程,至於兩種模式的區別,娛樂一下就行了。什么人聲甜美聲場寬毛刺少等等故弄玄虛之流,有這功夫還是花點錢吧,你需要更好的器材。
對用戶而言,WASAPI真正厲害的在於APO(Audio Processing Objects),基於此技術的音效理論上兼容所有設備(可能是DSP算法的問題,我遇到了采樣率上的限制,一旦高於96000Hz就失效了)。
出自DirectX的XAudio2:
XAudio2在采用UAA的Windows版本中就是對WASAPI的調用。
關於保真度,你的程序在Xp上跑的是DSound,使用UAA的系統則直接對應WASAPI,同樣可以跳過SRC,而且音頻低延遲的表現足以滿足音樂游戲之類的需求。
最初的XAudio用於Xbox,XAudio2一開始可用於三紅機和Vista及兩者以上。現在的大一統環境中(使用Windows原生API),播放音頻不是WASAPI就是XAudio2。使用XAudio2最大好處是比WASAPI更易於音頻編程,播放音頻的時候你只需不斷地提交音頻緩沖區的數據逐漸組成隊列就好,缺點是.。。對於音頻播放的常規開發,目前沒看到缺點,除了達不到那種連DMA都能跳過的超低延遲(換來的就是更高的硬件資源占用和幾乎感受不出的體驗提升)。對系統資源的占用還有延遲和WASAPI看不出來有什么差距。僅從音頻播放上,個人推薦使用XAudio2而非WASAPI。
目前在Windows10中它的版本是2.9,並繼續在WinRT與UWP技術中供開發者使用。
說閑話 IS EASY,這就給你CODE:
這個是我做語音合成的時候寫的一個簡單示例,代表了最常用的音頻播放場景,全是定式:
PS:合成出來的SampleRate低於聲卡工作的輸出頻率,必須經過軟件SRC(Sample Rate Converter),否則出來的就是快放效果,創建音源時默認參數是開啟其內置的SRC。
// 需要的頭文件和靜態庫 #include <Windows.h> #include <XAudio2.h> #pragma comment(lib,"xaudio2.lib") /* #include"SpeechSynthesis.h"這是SpeechSynthesis的頭文件 */ SpeechSynthesis syth(SC_Mandarin); // 語音合成器-簡體中文轉普通話 WAVEFORMATEX format = syth.wfx; // 波形格式 IXAudio2 * XAudioEngine = NULL; // IXAduio2音頻引擎 IXAudio2MasteringVoice * pmaster = NULL; // 聲音管理器 IXAudio2SourceVoice * pSource = NULL; // 音源 XAUDIO2_BUFFER sBuffer = {}; // 音源緩沖區 XAUDIO2_VOICE_STATE pState = {}; // 緩沖區狀態 // 單獨列出初始化和卸載,可以加入到你的構造-析構函數中 BOOL Init() { // XAudio2初始化 CoInitializeEx(NULL, COINIT_MULTITHREADED); if (FAILED(XAudio2Create(&XAudioEngine)))return FALSE; if (FAILED(XAudioEngine->CreateMasteringVoice(&pmaster)))return FALSE; if (FAILED(XAudioEngine->CreateSourceVoice(&pSource, &format)))return FALSE; return TRUE; } void Uninit() { XAudioEngine->Release(); CoUninitialize(); } // 語音合成與音頻呈現的函數 BOOL Vocalize(wchar_t * srcSentence) { pSource->Start(); // 開啟音源 sBuffer.pAudioData = syth.Synthesize(srcSentence); // 合成音頻 sBuffer.AudioBytes = syth.pcm_data.size(); // 一次性載入音頻緩沖區 if (FAILED(pSource->SubmitSourceBuffer(&sBuffer)))return 0; // 裝載音頻 // 等待播放完畢或者打斷 for (pSource->GetState(&pState); pState.BuffersQueued; pSource->GetState(&pState)) Sleep(1); pSource->Stop(); // 關閉音源 pSource->FlushSourceBuffers(); // 緩沖區清空(可選) return TRUE; } int main(int argc, char *argv[]) { Init(); Vocalize(L"這是一段測試語音"); Vocalize(L"節選於《讓我在你的心里自由自在》"); Vocalize(L"她瞬間就摘下了自己平靜的面具,陽光般的笑容撒滿她的眼睛。她向我投來真實的目光,她的聲音讓我無所適從地被打動。她時而安靜、柔順得像個鄰家女孩,時而一雙又黑又大的眼睛閃爍出精靈的光芒。在她的音樂里,她就是一只在草原蔚藍澄明的天際自由翱翔的蒼鷹,釋放着自己寬闊的憧憬與廣袤的理想。她的內心孤獨而迷茫,躑躅而憂郁,但依然頑固地看守着精神的自由,也學會了在現實生活中默默承受。"); Uninit(); return 0; }
可見流程非常簡單:
初始化之后可以先pSource->Start()也可以先配置sBuffer和SubmitSourceBuffer,三者順序很自由,符合常理就行;對於sBuffer,這里只需要讓它知道音頻緩沖區的指針和緩沖區長度,至於loop等參數,使用起來顧名思義,這里沒必要啰嗦。
然后就是輪詢音頻緩沖區狀態,以防緩沖區欠載。
停止播放的時候可以清除音源的緩沖區隊列,有些情況需要你打斷並播放別的聲音,需要使用FlushSourceBuffers();否則效果就是隊列式地把聲音全部播放完。
下次播放直接重復上述操作。
提示:
前面已提到,音源初始化的時候默認是開啟SRC的,也就是進行自動重采樣來保證正確的播放速度。
創建音源的時候有一個int型Flag叫做:XAUDIO2_VOICE_NOSRC。你可以在初始化的時候修改第三個參數來實現無SRC:
CreateSourceVoice(&pSource, &format,XAUDIO2_VOICE_NOSRC);
顧名思義,當你保證音頻采樣率和設備輸出設定一致的時候就可以這樣跳過SRC,你要的bit-perfect並沒有因為DX而閹割,而且代碼沒有WASAPI那么冗雜。