Web Audio API:為什麼要編寫代碼?

已發表: 2022-03-11

Web 音頻 API 的第一個草案於 2011 年出現在 W3C 中。雖然網頁中的音頻已經得到了很長時間的支持,但直到最近才出現從 Web 瀏覽器生成音頻的正確方法。 我個人將這歸功於谷歌瀏覽器,因為對於谷歌的興趣,瀏覽器開始成為計算機中最重要的部分。 您可能還記得,在 Google Chrome 出現之前,Web 瀏覽器的領域並沒有開始發生太大變化。 如果您此時在網頁中使用聲音,那將是一個糟糕的設計決定。 但自從網絡實驗的想法出現後,網絡音頻又開始變得有意義了。 現在的網絡瀏覽器是另一種藝術表達的工具,網絡瀏覽器中的視頻和音頻在其中起著至關重要的作用。

Web Audio API:為什麼要編寫代碼?

Web Audio API:為什麼要編寫代碼?
鳴叫

Web Audio API 可能很難用於某些目的,因為它仍在開發中,但已經存在許多 JavaScript 庫以使事情變得更容易。 在本例中,我將向您展示如何使用名為 Tone.js 的庫開始使用 Web Audio API。 有了這個,您將能夠僅通過學習基礎知識來滿足您的大部分瀏覽器聲音需求。

你好網絡音頻 API

入門

我們將在不使用庫的情況下開始。 我們的第一個實驗將涉及製作三個正弦波。 由於這將是一個簡單的示例,我們將只創建一個名為 hello.html 的文件,這是一個帶有少量標記的純 HTML 文件。

 <!DOCTYPE html> <html> <head> <meta charset="utf‐8"> <title> Hello web audio </title> </head> <body> </body> <script> </script> </html>

Web Audio API 的核心是音頻上下文。 音頻上下文是一個包含與網絡音頻相關的所有內容的對象。 在單個項目中擁有多個音頻上下文並不是一種好的做法。 我們將首先按照 Mozilla 的 Web Audio API 文檔給出的建議實例化一個音頻上下文。

 var audioCtx = new (window.AudioContext || window.webkitAudioContext);

製作振盪器

實例化音頻上下文後,您已經有了一個音頻組件:audioCtx.destination。 這就像你的揚聲器。 要發出聲音,您必須將其連接到 audioCtx.destination。 現在要產生一些聲音,讓我們創建一個振盪器:

 var sine = audioCtx.createOscillator();

很棒,但還不夠。 它還需要啟動並連接到我們的 audioCtx.destination:

 sine.start(); sine.connect(audioCtx.destination);

通過這四行代碼,您將擁有一個播放正弦聲音的非常煩人的網頁,但現在您了解了模塊如何相互連接。 在下面的腳本中,將有三個連接到輸出的正弦音調,每個都有不同的音調。 代碼非常不言自明:

 //create the context for the web audio var audioCtx = new (window.AudioContext || window.webkitAudioContext)(); //create, tune, start and connect each oscillator sinea, sineb and sinec var sinea = audioCtx.createOscillator(); sinea.frequency.value = 440; sinea.type = "sine"; sinea.start(); sinea.connect(audioCtx.destination); var sineb = audioCtx.createOscillator(); sineb.frequency.value = 523.25; sineb.type = "sine"; sineb.start(); sineb.connect(audioCtx.destination); var sinec = audioCtx.createOscillator(); sinec.frequency.value = 698.46; sinec.type = "sine"; sinec.start(); sinec.connect(audioCtx.destination);

振盪器不僅限於正弦波,還可以是三角形、鋸齒形、方形和自定義形狀,如 MDN 中所述。

Web音頻的補丁邏輯

接下來,我們將在我們的 Web Audio 組件管弦樂隊中添加一個增益模塊。 這個模塊允許我們改變聲音的幅度。 它類似於音量旋鈕。 我們已經使用 connect 函數將振盪器連接到音頻輸出。 我們可以使用相同的連接函數來連接任何音頻組件。 如果您使用的是 Firefox,並查看網絡音頻控制台,您將看到以下內容:

如果我們想改變音量,我們的補丁應該是這樣的:

這意味著振盪器不再連接到音頻目標,而是連接到增益模塊,並且該增益模塊連接到目標。 總是想像你用吉他踏板和電纜來做這件事是件好事。 代碼將如下所示:

 var audioCtx = new (window.AudioContext || window.webkitAudioContext) // we create the gain module, named as volume, and connect it to our var volume = audioCtx.createGain(); volume.connect(audioCtx.destination); //these sines are the same, exept for the last connect statement. //Now they are connected to the volume gain module and not to the au var sinea = audioCtx.createOscillator(); sinea.frequency.value = 440; sinea.type = "sine"; sinea.start(); sinea.connect(volume); var sineb = audioCtx.createOscillator(); sineb.frequency.value = 523.25; sineb.type = "sine"; sineb.start(); sineb.connect(volume); var sinec = audioCtx.createOscillator(); sinec.frequency.value = 698.46; sinec.type = "sine"; sinec.start(); sinec.connect(volume); volume.gain.value=0.2;

您可以在 https://github.com/autotel/simple-webaudioapi/ 找到解決方案。

GainNode 是最基本的效果單元,但也有延遲、卷積器、雙二次濾波器、立體聲聲相器、波形整形器等等。 您可以從 Tone.js 等庫中獲取新效果。

將這些聲音補丁之一存儲在它們自己的對像中將允許您根據需要重用它們,並使用更少的代碼創建更複雜的編排。 這可能是未來帖子的主題。

使用 Tone.js 讓事情變得更簡單

現在我們已經簡要了解了 vanilla Web Audio 模塊的工作原理,讓我們來看看很棒的 Web Audio 框架:Tone.js。 有了這個(以及用於用戶界面組件的 NexusUI),我們可以非常輕鬆地構建更有趣的合成器和聲音。 為了嘗試一下,讓我們製作一個採樣器並對其應用一些用戶交互效果,然後我們將為這個示例添加一些簡單的控件。

Tone.js 採樣器

我們可以從創建一個簡單的項目結構開始:

 simpleSampler |-- js |-- nexusUI.js |-- Tone.js |-- noisecollector_hit4.wav |-- sampler.html

我們的 JavaScript 庫將駐留在js目錄中。 出於本演示的目的,我們可以使用 NoiseCollector 的 hit4.wav 文件,該文件可以從 Freesound.org 下載。

Tone.js 通過 Player 對象提供其功能。 該對象的基本功能是加載樣本,並循環播放或播放一次。 我們的第一步是在 sampler.html 文件中的“sampler”變量中創建一個播放器對象:

 <!doctype html> <html> <head> <title> Sampler </title> <script type="text/javascript" src="js/nexusUI.js" ></script> <script type="text/javascript" src="js/Tone.js" ></script> <script> var sampler = new Tone.Player("noisecollector_hit4.wav", function() { console.log("samples loaded"); }); </script> </head> <body> </body> </html>

注意播放器構造函數的第一個參數是WAV文件的名稱,第二個是回調函數。 WAV 不是唯一受支持的文件類型,兼容性更多地取決於網絡瀏覽器而不是庫。 當播放器完成將樣本加載到其緩衝區中時,回調函數將運行。

我們還必須將採樣器連接到輸出。 Tone.js 的做法是:

 sampler.toMaster();

... 其中 sampler 是一個 Tone.Player 對象,在第 10 行之後。toMaster 函數是 connect(Tone.Master) 的簡寫。

如果您在開發人員控制台打開的情況下打開 Web 瀏覽器,您應該會看到“已加載樣本”消息,表明播放器已正確創建。 在這一點上,您可能想听到樣本。 為此,我們需要在網頁上添加一個按鈕,並對其進行編程以在按下後播放樣本。 我們將在正文中使用 NexusUI 按鈕:

 <canvas nx="button"></canvas>

您現在應該會在文檔中看到一個圓形按鈕。 為了對其進行編程以播放我們的示例,我們添加了一個 NexusUI 偵聽器,如下所示:

 button1.on('*',function(data) { console.log("button pressed!"); })

NexusUI 的突出之處在於它為每個 NexusUI 元素創建了一個全局變量。 您可以將 NexusUI 設置為不這樣做,而是通過將 nx.globalWidgets 設置為 false 將這些變量僅存在於 nx.widgets[] 中。 在這裡,我們將只創建幾個元素,所以我們將堅持這種行為。

和 jQuery 一樣,我們可以放置這些 .on 事件,第一個參數是事件名稱。 在這裡,我們只是為按鈕所做的任何事情分配一個功能。 這不管寫成“*”。 您可以了解有關 NexusUI API 中每個元素的事件的更多信息。 要在按下按鈕時播放樣本而不是記錄消息,我們應該運行採樣器的啟動函數。

 nx.onload = function() { button1.on('*',function(data) { console.log("button pressed!"); sampler.start(); }); }

另請注意,偵聽器進入 onload 回調。 NexusUI 元素是在畫布中繪製的,在 nx 調用 onload 函數之前,您無法引用它們。 就像在 jQuery 中處理 DOM 元素一樣。

該事件在鼠標按下和釋放時觸發。 如果您希望它僅在按下時觸發,則必須評估 event.press 是否等於 1。

有了這個,您應該有一個按鈕,可以在每次按下時播放樣本。 如果您將 sampler.retrigger 設置為 true,它將允許您播放樣本,無論它是否正在播放。 否則,您必須等到示例完成才能重新觸發它。

應用效果

使用 Tone.js,我們可以輕鬆創建延遲:

 var delay= new Tone.FeedbackDelay("16n",0.5).toMaster();

第一個參數是延遲時間,可以用樂譜編寫,如下所示。 第二個是濕電平,這意味著原始聲音和對其有影響的聲音之間的混合。 對於延遲,您通常不希望 100% 濕潤,因為延遲相對於原始聲音來說很有趣,而單獨的濕潤並不是很吸引人,因為兩者結合在一起。

下一步是從 master 上拔下我們的採樣器並將其插入延遲。 調整採樣器連接到主控的線路:

 sampler.connect(delay);

現在再試一次按鈕,看看有什麼不同。

接下來,我們將在文檔正文中添加兩個刻度盤:

 <canvas nx="dial"></canvas> <canvas nx="dial"></canvas>

我們使用 NexusUIlistener 將錶盤的值應用於延遲效果:

 dial1.on('*',function(data) { delay.delayTime.value=data.value; }) dial2.on('*',function(data) { delay.feedback.value=data.value; })

您可以在每個事件上調整的參數可以在 Tone.js 文檔中找到。 為了延遲,它就在這裡。 現在您已準備好嘗試該示例並使用 NexusUI 撥盤調整延遲參數。 這個過程可以通過每個 NexusUI 元素輕鬆完成,不僅限於效果。 例如,還可以嘗試添加另一個錶盤,並添加其偵聽器,如下所示:

 dial3.on('*',function(data) { sampler.playbackRate=data.value; })

您可以在 github.com/autotel/simpleSampler 找到這些文件

結論

當我瀏覽這些 API 時,我開始對開始出現在我腦海中的所有可能性和想法感到不知所措。 這種音頻實現與傳統數字音頻實現的最大區別不在於音頻本身,而在於上下文。 這裡沒有新的合成方法。 相反,創新在於音頻和音樂製作現在正在滿足網絡技術。

我個人參與了電子音樂製作,這個領域一直存在這種矛盾,即在實際演奏音樂和按播放錄製的曲目之間存在歧義。 如果您想真正製作現場電子音樂,您必須能夠創建自己的表演工具或“音樂製作機器人”進行現場即興創作。 但如果電子音樂的演奏變成簡單地在預先準備好的音樂製作算法中調整參數,那麼觀眾也可以參與到這個過程中。 我一直在做一些關於將網絡和音頻整合到眾包音樂中的小實驗,也許很快我們就會參加那些音樂來自觀眾通過智能手機的聚會。 畢竟,這與我們在洞穴時代可能享受的節奏即興演奏並沒有什麼不同。


進一步閱讀 Toptal 工程博客:

  • WebAssembly/Rust 教程:音高完美的音頻處理
  • MIDI 教程:創建由 MIDI 硬件控制的基於瀏覽器的音頻應用程序