個案研究 - 使用 Chrome 的 JAM

如何製作出精彩的音訊

Oskar Eriksson
Oskar Eriksson

簡介

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);
    };
};
Slapback 節點的內部路由

如同部分讀者可能已察覺到,這個延遲效果也可以搭配較長的延遲時間使用,進而成為帶有回授的一般單聲道延遲效果。以下是使用此延遲效果的示例,讓您聽聽實際效果。

音訊路由

在專業音訊應用程式中使用不同樂器和音樂部分時,必須具備靈活的路由系統,才能以有效的方式混合及調變聲音。在 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 實體格式制定標準。與觀眾同樂!