Case study - Fai una JAM session con Chrome

Come abbiamo reso l'audio rock

Oskar Eriksson
Oskar Eriksson

Introduzione

JAM con Chrome è un progetto musicale basato sul web creato da Google. JAM con Chrome consente a persone di tutto il mondo di formare una band e di suonare in tempo reale nel browser. Noi di DinahMoe abbiamo avuto il grande piacere di far parte di questo progetto. Il nostro compito era produrre musica per l'applicazione, nonché progettare e sviluppare il componente musicale. Lo sviluppo comprendeva tre aree principali: una "workstation musicale" che comprendeva riproduzione MIDI, campionamenti software, effetti audio, routing e mix, un motore logico della musica per controllare la musica in modo interattivo in tempo reale e un componente di sincronizzazione che fa in modo che tutti i player di una sessione ascoltino la musica contemporaneamente, prerequisito per la riproduzione insieme.

Per raggiungere il massimo livello possibile di autenticità, precisione e qualità audio abbiamo scelto di utilizzare l'API Web Audio. Questo case study illustrerà alcune delle sfide che ci sono state presentate e il modo in cui le abbiamo risolte. su HTML5Rocks puoi trovare già una serie di articoli introduttivi utili per iniziare a utilizzare l'audio web, quindi passeremo subito alla parte finale del pool.

Scrivere effetti audio personalizzati

L'API Web Audio ha una serie di effetti utili inclusi nelle specifiche, ma avevamo bisogno di effetti più elaborati per i nostri strumenti in JAM con Chrome. Ad esempio, nell'audio web è presente un nodo di ritardo nativo, ma esistono molti tipi di ritardi: ritardo stereo, ritardo ping pong, ritardo slapback e l'elenco potrebbe continuare. Fortunatamente, è possibile creare tutti questi elementi nell'audio per il web utilizzando i nodi degli effetti nativi e un po' di immaginazione.

Poiché volevamo poter utilizzare i nodi nativi e i nostri effetti personalizzati nel modo più trasparente possibile, abbiamo deciso di creare un formato wrapper che potesse raggiungere questo obiettivo. I nodi nativi in Web Audio usano il suo metodo di connessione per collegare i nodi tra loro, quindi abbiamo dovuto emulare questo comportamento. Ecco l'idea di base:

var MyCustomNode = function(){
    this.input = audioContext.createGain();
    var output = audioContext.createGain();

    this.connect = function(target){
       output.connect(target);
    };
};

Con questo pattern siamo molto vicini ai nodi nativi. Vediamo come viene utilizzato.

//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);
Routing del nodo personalizzato

L'unica differenza tra il nostro nodo personalizzato e uno nativo è che dobbiamo connetterci alla proprietà di input dei nodi personalizzati. Sicuramente ci sono modi per aggirare questa pratica, ma per i nostri scopi si è trattato di una soluzione abbastanza simile. Questo pattern può essere sviluppato ulteriormente per simulare i metodi di disconnessione degli AudioNodes nativi, nonché per ospitare ingressi/uscite definiti dall'utente durante la connessione e così via. Dai un'occhiata alla specifica per scoprire cosa possono fare i nodi nativi.

Ora che abbiamo visto il nostro pattern di base per creare effetti personalizzati, il passo successivo è stato associare effettivamente al nodo personalizzato un comportamento personalizzato. Diamo un'occhiata a un nodo di ritardo slapback.

Schiacciata

Il ritardo slapback, a volte chiamato eco slapback, è un effetto classico utilizzato su numerosi strumenti, dalle voci in stile anni '50 alle chitarre surf. L'effetto prende il suono in ingresso e riproduce una copia del suono con un leggero ritardo di circa 75-250 millisecondi. Questo ti dà la sensazione che il suono venga schiaffeggiato, come si può notare nel nome. Possiamo creare l'effetto in questo modo:

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);
    };
};
Routing interno del nodo slapback

Come alcuni di voi hanno già capito, questo ritardo potrebbe essere utilizzato anche con tempi di ritardo maggiori e quindi diventare un normale ritardo mono con feedback. Ecco un esempio di utilizzo di questo ritardo per farti sentire il suo suono.

Routing dell'audio

Quando lavori con strumenti e parti musicali diversi in applicazioni audio professionali, è essenziale disporre di un sistema di routing flessibile che ti consenta di mixare e modulare i suoni in modo efficace. In JAM con Chrome abbiamo sviluppato un sistema bus audio, simile a quelli che si trovano nei mixer fisici. Questo ci permette di collegare tutti gli strumenti che hanno bisogno di un effetto di riverbero a un bus o canale comune e quindi di aggiungere il riverbero a quel bus invece di aggiungere un riverbero a ogni singolo strumento. Questa è un'ottimizzazione importante e ti consigliamo piuttosto di eseguire un'operazione simile non appena inizi a creare applicazioni più complesse.

Routing dell'AudioBus

Fortunatamente, questo è davvero facile da ottenere nell'audio per il web. In pratica, possiamo utilizzare lo scheletro definito per gli effetti e utilizzarlo nello stesso modo.

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);
    };
};

Dovrebbe essere utilizzato nel seguente modo:

//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);

E voilà, abbiamo applicato ritardo, equalizzazione e riverbero (un effetto piuttosto costoso, dal punto di vista delle prestazioni) a metà del costo, come se avessimo applicato gli effetti a ogni singolo strumento. Se volessi aggiungere un po' di pepe in più all'autobus, potremmo aggiungere due nuovi nodi di guadagno, preGain e postGain, che ci permetterebbero di disattivare o dissolvere i suoni su un autobus in due modi diversi. Il preGain viene posizionato prima degli effetti e il postGain viene inserito alla fine della catena. Se applichiamo la dissolvenza al valore pre-Gain, gli effetti continueranno a risuonare anche dopo che il guadagno avrà raggiunto il fondo, ma se applichiamo la dissolvenza in post-Guadagno, l'audio verrà disattivato contemporaneamente.

Da dove andiamo?

I metodi che ho descritto qui possono e dovrebbero essere ulteriormente sviluppati. Aspetti come l'input e l'output dei nodi personalizzati e i metodi di connessione potrebbero/devono essere implementati utilizzando l'ereditarietà basata su prototipi. Gli autobus dovrebbero essere in grado di creare effetti in modo dinamico, facendo passare un elenco di effetti.

Per celebrare il lancio di JAM con Chrome, abbiamo deciso di rendere il nostro framework di effetti open source. Se questa breve introduzione ti ha sollecito, dai un'occhiata e non esitare a contribuire. C'è una discussione in corso qui in merito alla standardizzazione di un formato per gli elementi audio web personalizzati. Partecipa