우수사례 - JAM with Chrome

오디오 록을 만든 방법

Oskar Eriksson
Oskar Eriksson

소개

JAM with Chrome은 Google에서 만든 웹 기반 음악 프로젝트입니다. JAM with Chrome을 사용하면 전 세계 사람들이 브라우저에서 밴드를 결성하고 실시간으로 음악을 즐길 수 있습니다. DinahMoe는 이 프로젝트에 참여할 수 있어서 큰 영광이었습니다. 우리의 역할은 애플리케이션에 사용할 음악을 제작하고 음악 구성요소를 설계 및 개발하는 것이었습니다. 이 개발은 세 가지 주요 영역으로 구성되었습니다. 미디 재생, 소프트웨어 샘플러, 오디오 효과, 라우팅, 믹싱을 포함한 '음악 워크스테이션', 실시간으로 음악을 양방향으로 제어하는 음악 논리 엔진, 세션의 모든 플레이어가 정확히 동시에 음악을 들을 수 있도록 하는 동기화 구성요소로, 함께 연주하기 위한 전제 조건입니다.

가능한 한 가장 높은 수준의 신뢰성, 정확성, 오디오 품질을 달성하기 위해 Google은 Web Audio API를 사용하기로 결정했습니다. 이 우수사례에서는 Google이 당면한 몇 가지 과제와 그 해결 방법을 설명합니다. HTML5Rocks에는 웹 오디오를 시작하는 데 도움이 되는 여러 가지 훌륭한 소개 글이 이미 많이 있습니다. 지금부터 이 자료의 가장 깊은 부분으로 들어가 보겠습니다.

맞춤 오디오 효과 작성

Web Audio API에는 사양에 포함된 유용한 효과가 많지만 JAM with Chrome의 악기를 위한 보다 정교한 효과가 필요했습니다. 예를 들어 웹 오디오에는 기본 지연 노드가 있지만 스테레오 지연, 핑퐁 지연, 슬랩백 지연 등 여러 유형의 지연이 있습니다. 다행히 이 모든 작업은 네이티브 효과 노드와 약간의 상상력을 사용하여 웹 오디오에서 만들 수 있습니다.

우리는 네이티브 노드와 자체 사용자 지정 효과를 가능한 한 투명한 방식으로 사용할 수 있기를 원했기 때문에 이를 달성할 수 있는 래퍼 형식을 만들어야 한다고 결정했습니다. 웹 오디오의 네이티브 노드는 연결 메서드를 사용하여 노드를 함께 연결하므로 이 동작을 에뮬레이션해야 했습니다. 기본 아이디어는 다음과 같습니다.

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 라우팅

다행히 웹 오디오에서는 이를 매우 쉽게 달성할 수 있습니다. 기본적으로 효과에 대해 정의한 스켈레톤을 사용하고 같은 방식으로 사용할 수 있습니다.

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은 체인의 끝에 배치됩니다. 그런 다음 프리게인을 페이드 아웃하면 게인이 바닥에 도달한 후에도 효과가 계속 울려 퍼지지만, 게인 후 페이드 아웃하면 모든 사운드가 동시에 음소거됩니다.

다음 단계

여기서 설명한 이러한 방법은 더 발전될 수 있으며 앞으로 더 발전해야 합니다. 커스텀 노드의 입력과 출력, 연결 메서드 같은 사항은 프로토타입 기반 상속을 사용하여 구현할 수 있거나 구현해야 합니다. 버스는 효과 목록을 전달하여 동적으로 효과를 만들 수 있어야 합니다.

JAM with Chrome의 출시를 기념하여 Google은 효과 프레임워크를 오픈소스로 만들기로 결정했습니다. 이 짧은 소개가 마음에 든다면 살펴보시고 언제든지 참여해 주세요. 맞춤 웹 오디오 항목의 형식을 표준화하는 방법은 여기에서 확인하세요. 시청자와 소통하세요