在閱讀本文之前,大家可以先到這裡下載API與範例程式,從文件及範例程式,我整理出幾個設計程式的步驟:
- 製訂好你設計的 spatializer 有哪些參數可供外部設定,然後參考InternalRegisterEffectDefinition 函式內的寫法去註冊你的參數。
- 前面訂好參數後,可實作 SetFloatParameterCallback 這個 interface 來實現與外部控制單元的接口,往後在 C# script 端就可以透過 Unity audio source 物件的 setSpatializerFloat 函式將 Unity 場景中某個 audio source 的參數給傳進 plugin。
- CreateCallback 與 ReleaseCallback 相當然爾就是對應到 audio source 物件生成與消失時底層 plugin 物件實體的生成/消失。
- ProcessCallback,最重要的部份,audio source 上掛的音檔,在執行過程中會以一個個 audio frame 的方式透過 ProcessCallback 不斷的傳進來 plugin,我們必須實作這個函式去進 input buffer 進行處理後再填到 output buffer,最後就會往 audio source 所指定的 Mixer 送去。這裡需要注意的是第四個參數 length,它的單位是 sample,假設它的值是 1024,如果 inchannels = 2 (雙聲道),則代表 input buffer 一共有 1024 * 2 個 floats。
- 別忘了在某個地方加上這行:
definition.flags |= UnityAudioEffectDefinitionFlags_IsSpatializer; - 善用 UnityAudioSpatializerData 去取得當前 audio source 及 audio listener 的位置/旋轉資訊。
這裡我跳過對每個函式的解說,直接分享幾個心得,或許對初次接觸 audio plugin 的人沒有幫助,但相信對有開發經驗的人能節省一些錯誤的嘗試。
- audio 演算法的設計,在這個架構下必須要將「控制」(SetFloatParameterCallback) 與「計算」(ProcessCallback) 分開,剛從 Matlab 跨過來的人可能會有點障礙。
- Unity audio source 生成、執行、到消失時,可確定其呼叫順序為 CreateCallback -> ProcessCallback (多次) -> ReleaseCallback。
- Unity C# script 的遊戲物通常會繼承 MonoBehavior class,它也有 Awake, Start, Enable, Update, OnDestroy 之類的 API,來對應它的 life cycle。不幸的是,這些 API 跟前面提到的 callback 沒有任何先後關係。也就是說,如果你想設計一個遊戲物件去影響一個 audio source 的效果 (例如有殘響效果的房間),請小心處理兩者的 life cycle,否則會出現你想 Update 但 audio source 已被 destroyed 的情況。
- 當audio source播完你指定的audio clip時,Unity 5.6 及之前的版本會繼續空轉 ProcessCallback,造成CPU資源的浪費,2017 以後的版本才會停止呼叫。這算是 Unity 之前版本的 bug,但我們也是有辦法可以讓他停啦...
半年一篇CS豆知識,希望以後還有時間分享。