如何製作出精彩的音訊
簡介
JAM with Chrome 是 Google 推出的網路音樂專案,透過 JAM with Chrome,來自世界各地的使用者可以組成樂團,並在瀏覽器中即時即興創作音樂。我們 DinahMoe 很榮幸參與這個專案。我們的角色是為應用程式製作音樂,以及設計及開發音樂元件。開發工作分為三個主要領域:音樂工作站,包括 MIDI 播放、軟體取樣器、音效、路由和混音;音樂邏輯引擎,可即時互動控制音樂;同步元件,確保工作階段中的所有玩家都能在同一時間聽到音樂,這是能否一起演奏的必要條件。
為達到最高程度的真實性、準確性和音訊品質,我們選擇使用 Web Audio API。本個案研究將討論我們面臨的部分挑戰,以及解決這些挑戰的方式。HTML5Rocks 上已經有許多優質的入門文章,可讓您開始使用 Web Audio,因此我們將直接深入探討。
撰寫自訂音效
Web Audio API 規格中包含許多實用的效果,但我們需要在 JAM 中為 Chrome 的樂器提供更精細的效果。舉例來說,Web Audio 中有原生延遲節點,但延遲類型有很多種,包括立體聲延遲、ping pong 延遲、回授延遲等等。幸運的是,您可以使用原生效果節點和一些創意,在 Web Audio 中製作上述效果。
我們希望能夠盡可能以這種透明的方式使用原生節點和自訂效果,因此決定建立可達成此目標的包裝函式格式。Web Audio 中的原生節點會使用其連結方法將節點連結在一起,因此我們需要模擬這種行為。基本概念如下所示:
var MyCustomNode = function(){
this.input = audioContext.createGain();
var output = audioContext.createGain();
this.connect = function(target){
output.connect(target);
};
};
使用這種模式,我們就能盡可能接近原生節點。讓我們來看看這項功能的用法。
//create a couple of native nodes and our custom node
var gain = audioContext.createGain(),
customNode = new MyCustomNode(),
anotherGain = audioContext.createGain();
//connect our custom node to the native nodes and send to the output
gain.connect(customNode.input);
customNode.connect(anotherGain);
anotherGain.connect(audioContext.destination);
自訂節點與原生節點唯一的差異,就是我們必須連結至自訂節點的輸入屬性。我相信還是有辦法可以規避這個問題,但這已經足以達到我們的目的。這個模式可以進一步模擬原生 AudioNode 的斷開方法,以及在連線時支援使用者定義的輸入/輸出等。請參閱規格,瞭解原生節點的功能。
我們已經掌握建立自訂效果的基本模式,接下來要為自訂節點提供一些自訂行為。我們來看看 slapback 延遲節點。
用力回擊
回授延遲效果 (有時稱為回授迴音) 是許多樂器的經典效果,從 50 年代風格的人聲到衝浪吉他皆可使用。這項效果會擷取傳入的音訊,並播放音訊副本,但會稍微延遲約 75 到 250 毫秒。這會讓人有聲音被彈回的感覺,因此得名。我們可以建立以下效果:
var SlapbackDelayNode = function(){
//create the nodes we'll use
this.input = audioContext.createGain();
var output = audioContext.createGain(),
delay = audioContext.createDelay(),
feedback = audioContext.createGain(),
wetLevel = audioContext.createGain();
//set some decent values
delay.delayTime.value = 0.15; //150 ms delay
feedback.gain.value = 0.25;
wetLevel.gain.value = 0.25;
//set up the routing
this.input.connect(delay);
this.input.connect(output);
delay.connect(feedback);
delay.connect(wetLevel);
feedback.connect(delay);
wetLevel.connect(output);
this.connect = function(target){
output.connect(target);
};
};
如同部分讀者可能已察覺到,這個延遲效果也可以搭配較長的延遲時間使用,進而成為帶有回授的一般單聲道延遲效果。以下是使用此延遲效果的示例,讓您聽聽實際效果。
音訊路由
在專業音訊應用程式中使用不同樂器和音樂部分時,必須具備靈活的路由系統,才能以有效的方式混合及調變聲音。在 JAM with Chrome 中,我們開發了類似於實體混音板的音訊總線系統。這樣一來,我們就能將需要混響效果的所有樂器連接至通用匯流排或通道,然後將混響效果加入該匯流排,而非為每個樂器個別加入混響效果。這是重大的最佳化方式,因此建議您一開始使用較複雜的應用程式時,就採取類似做法。
幸好,在 Web Audio 中實現這項功能非常簡單。我們基本上可以使用為效果定義的骨架,並以相同方式使用。
var AudioBus = function(){
this.input = audioContext.createGain();
var output = audioContext.createGain();
//create effect nodes (Convolver and Equalizer are other custom effects from the library presented at the end of the article)
var delay = new SlapbackDelayNode(),
convolver = new tuna.Convolver(),
equalizer = new tuna.Equalizer();
//route 'em
//equalizer -> delay -> convolver
this.input.connect(equalizer);
equalizer.connect(delay.input);
delay.connect(convolver);
convolver.connect(output);
this.connect = function(target){
output.connect(target);
};
};
使用方式如下:
//create some native oscillators and our custom audio bus
var bus = new AudioBus(),
instrument1 = audioContext.createOscillator(),
instrument2 = audioContext.createOscillator(),
instrument3 = audioContext.createOscillator();
//connect our instruments to the same bus
instrument1.connect(bus.input);
instrument2.connect(bus.input);
instrument3.connect(bus.input);
bus.connect(audioContext.destination);
瞧,我們以一半的成本,就將延遲、均衡和混響 (從效能角度來說,這是相當昂貴的效果) 套用到每個樂器上。如果想為總線增添一些特別效果,可以新增兩個新的增益節點:preGain 和 postGain,這樣就能以兩種方式關閉或淡出總線中的聲音。preGain 會放在效果之前,而 postGain 會放在鏈結的結尾。如果我們接著淡出 preGain,效果會在增益值達到最低值後仍會產生共鳴,但如果我們淡出 postGain,所有聲音都會同時靜音。
接下來要做什麼?
我在此處所述的方法可以且應進一步開發。如自訂節點的輸入和輸出,以及連接方法等,可以/應使用以原型為基礎的繼承方式實作。總管應可透過傳遞效果清單,動態建立效果。
為慶祝 JAM with Chrome 的推出,我們決定將特效框架開放原始碼。如果這篇簡短的介紹讓您心動,歡迎查看並隨時提供意見。這裡的討論主題是關於為自訂 Web Audio 實體格式制定標準。與觀眾同樂!