音訊

Emscripten 自帶 OpenAL 1.1 API 的實作,使用 Web Audio API 作為後端。

您可以合理地預期移植的 OpenAL 應用程式「可以直接執行」,無需額外的工作。只需使用 -lopenal 連結器旗標進行連結即可。

有一些特定於實作的方面值得考慮,並在此處記錄。

警告

過去使用 OpenAL 時,不需要將額外的旗標傳遞給編譯器。然而,如上所述指定 -lopenal 應被視為強制性(在未來某個時間點,它將會是!)

支援的 OpenAL 擴充功能

以下擴充功能受 Emscripten 的 OpenAL 實作支援。

  • ALC_SOFT_pause_device;

  • ALC_SOFT_HRTF;

  • AL_EXT_float32;

  • AL_SOFT_loop_points;

  • AL_SOFT_source_length;

  • AL_EXT_source_distance_model;

  • AL_SOFT_source_spatialize;

警告

這並不意味著您應該假設它們的存在!為了正確性,您應該在每次使用之前始終檢查是否支援擴充功能,就像一個好的應用程式會做的那樣。

Emscripten 上音訊的指南

請注意,您的應用程式需要讓步給 Javascript 主迴圈才能進行音訊處理(請參閱瀏覽器主迴圈)。簡而言之,這類程式碼會無限期地阻塞

while(nframes < THE_NUMBER_OF_FRAMES_WE_WANT)
    alcGetIntegerv(device, ALC_CAPTURE_SAMPLES, 1, &nframes);

上述程式碼片段通常在原生應用程式中有效,因為大多數 OpenAL 實作擁有並管理一個或多個獨立的執行緒。這在 Emscripten 中並如此。您必須做的是,在每次「主迴圈迭代」中(即您透過 emscripten_set_main_loop()emscripten_set_main_loop_arg() 提供的回呼)僅執行一次此類查詢。

您可能遇到的另一個問題是,瀏覽器不會允許在使用者輸入之前播放音訊。這會阻止網頁自動播放媒體,如果您不想要這樣,這可能會很煩人。Emscripten 的 OpenAL 實作(以及 SDL1)會自動監聽使用者在文件和畫布上的點擊或按鍵,並為您恢復音訊。這表示一旦使用者在頁面上執行某些操作,音訊就會開始播放。(請參閱 autoResumeAudioContext() 了解其內部運作方式。)

Emscripten 特定的捕捉行為

嘗試開啟使用者音訊捕捉裝置的輸入串流會導致非同步出現一個小的瀏覽器特定對話方塊,要求使用者允許,在某些瀏覽器中,還會要求選擇捕捉裝置。

考慮到這一點,當使用有效且受支援的參數呼叫 alcCaptureOpenDevice() 時,會傳回一個「代理」裝置,該裝置在使用者於上述對話方塊中點擊「允許」之前,不會成功捕捉到任何樣本。

這表示,當呼叫 alcGetIntegerv(device, ALC_CAPTURE_SAMPLES, 1, &nframes) 時,nframes 會保持設定為零,直到使用者點擊「允許」。您可能希望讓您的應用程式處理此特定行為。

如果使用者點擊「拒絕」,裝置會失效(因為這有點類似於拔掉實體裝置),然後對該裝置呼叫 alcCapture* 函數會持續失敗並出現 ALC_INVALID_DEVICE。您的應用程式應該準備好正確處理此情況。

注意

某些瀏覽器會「記住」此選擇,並在每次再次詢問時自動套用。實作無法偵測到此行為。

OpenAL 捕捉的實用實作詳細資訊

在內部,Web Audio 的捕捉資料始終由 Javascript Float32Array 支援。因此,AL_FORMAT_MONO_FLOAT32AL_FORMAT_STEREO_FLOAT32 是唯二不需要將取得的樣本從其初始類型轉換為另一個類型的格式。

此外,從裝置取得樣本的實際取樣率目前由瀏覽器和硬體決定,而不是使用者程式碼決定。如果此取樣率與您的應用程式請求的取樣率不符,則實作會要求代表您執行重新取樣。

該取樣率由 audioCtx.sampleRate 給出,其中 audioCtx 是相關捕捉 ALCdevice 在內部使用的 AudioContext 物件。目前,Emscripten 沒有提供讓應用程式直接存取此值的方法,但這可能會透過特定於 Emscripten 的 OpenAL 擴充功能提供(由於需要註冊,因此尚未提供)。

不過,現在有一種快速且事實上可靠的方法可以做到這一點(C 範例)

#ifdef __EMSCRIPTEN__

#include <emscripten.h>

// Avoid calling this more than once! Caching the value is up to you.
unsigned query_sample_rate_of_audiocontexts() {
    return EM_ASM_INT({
        var AudioContext = window.AudioContext || window.webkitAudioContext;
        var ctx = new AudioContext();
        var sr = ctx.sampleRate;
        ctx.close();
        return sr;
    });
}
#endif

可以合理地預期此取樣率為 44100Hz 或 48000Hz。如果您好奇,可以將 javascript 部分複製到瀏覽器的控制台中立即進行測試。

改進和擴充實作

目前,OpenAL 捕捉實作執行簡單的線性重新取樣,因為它很簡單,並且品質的小幅損失被認為是可以接受的。

當然,如果您願意,也歡迎您透過貢獻來改進這個部分!例如,請參閱這個議題

同樣地,如果您希望看到實作特定的擴充功能(無論是否已註冊),最好的方式是提交一個議題(前提是之前沒有相關的議題),或是直接貢獻程式碼!請參閱貢獻指南