標籤:alt replace queue 理論 pretty 常見 bar directx 生活
對於常見的音頻播放,使用XAudio2足夠了。
時間是把殺豬刀,滑稽的是我成了豬
早在Windows Vista中,M$推出了新的音頻架構UAA,其中的CoreAudio接替了DSound、WaveXxx、MediaFundation,通過Core Audio APIs,Windows的音頻效能可以與MacOS X相媲美(手動偷笑)。
Universal Audio Architecture (UAA)
CoreAudio屬於UAA,只在使用者層進行一系列音頻處理,而系統核心只負責遞交緩衝資料給音頻驅動。
在UAA出現之前,程式跑起來是這樣的:
現在程式在UAA跑起來是這樣的:
WASAPI和XAudio2Core 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那麼冗雜。
Windows基礎-使用XAudio2播放音頻(本質是WASAPI)