案例研究 - JAM with Chrome

我们是怎么让音频震撼

Oskar Eriksson
Oskar Eriksson

简介

JAM with Chrome 是由 Google 打造的基于网络的音乐项目。JAM with Chrome 可让世界各地的用户在浏览器中实时组成乐队和即兴演奏。DinahMoe 很荣幸能够参与此项目。我们的职责是为应用制作音乐,设计和开发音乐组件。这项开发工作包括三个主要方面:“音乐工作站”(包括 midi 播放)、软件采样器、音频效果、路由和混音;用于实时交互控制音乐的音乐逻辑引擎;以及一个同步组件,用于确保会话中的所有播放器都能同时听到音乐,这是一起演奏的先决条件。

为了达到尽可能高的真实性、准确性和音质,我们选择使用 Web Audio API。本案例研究将讨论我们遇到的一些挑战,以及我们如何应对这些挑战。HTML5Rocks 上已经有不少优秀的介绍文章可帮助您开始使用 Web Audio,因此我们直接进入深入探讨这一主题。

编写自定义音频效果

Web Audio API 具有许多包含在规范中的实用效果,但是我们需要对带有 Chrome 的 JAM 中的仪器进行更细致的设计。例如,网络音频中有原生延迟节点,但延迟有很多种 - 立体声延迟、乒乓球延迟、回拍延迟等等。幸运的是,凭借原生效果节点和一些想象力,您可以在 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 的断开连接方法,并在连接等情况下适应用户定义的输入/输出。查看规范,了解原生节点的功能。

至此,我们已经有了用于创建自定义效果的基本模式,下一步就是实际为自定义节点添加一些自定义行为。我们来看一个回拍延迟节点。

轻松放松

回拍延迟(有时也称为“回拍回声”)是 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 中,我们开发了一种音频总线系统,与物理混音板中的系统类似。这样一来,我们就可以将所有需要混响效果的乐器连接到一个公共总线或声道,然后将混响添加到该总线,而不必向每个单独的乐器添加混响。这是一项重大优化,强烈建议您在开始执行更复杂的应用时立即执行类似的操作。

AudioBus 的路由

幸运的是,在 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,这些节点可以让我们以两种不同的方式关闭或淡化总线中的声音。将前增益放在效果之前,将后增益放在链的末尾。如果我们随后淡出前增益,在增益达到底部后,效果仍会产生共鸣,但是如果我们淡出后增益,则所有声音会同时静音。

从这里出发呢?

我在这里介绍的这些方法可以并且应该有进一步改进。例如,可以/应该使用基于原型的继承,实现自定义节点的输入和输出以及连接方法。总线应该能够通过传递效果列表来动态创建效果。

为了庆祝 JAM with Chrome 的发布,我们决定将效果框架开源。如果这个简短的介绍符合您的想法,请尽情欣赏,并随时贡献自己的力量。此处讨论了如何为自定义网络音频实体实现格式标准化。参与进来!