Antes do elemento <audio> do HTML5, era necessário o Flash ou outro plug-in
para quebrar o silêncio da Web. Embora o áudio na Web não exija mais um plug-in, a tag de áudio traz limitações significativas para a implementação de jogos sofisticados e aplicativos interativos.
A API Web Audio é uma API JavaScript de alto nível para processar e sintetizar áudio em aplicativos da Web. O objetivo dessa API é incluir recursos encontrados em mecanismos de áudio de jogos modernos e algumas das tarefas de mixagem, processamento e filtragem encontradas em aplicativos modernos de produção de áudio para computadores. A seguir, apresentamos uma introdução ao uso dessa API avançada.
Introdução ao AudioContext
Um AudioContext é usado para gerenciar e reproduzir todos os sons. Para produzir um som usando a API Web Audio, crie uma ou mais fontes de som e conecte-as ao destino de som fornecido pela instância AudioContext. Essa conexão não precisa ser direta e pode passar por
qualquer número de AudioNodes intermediários que atuam como módulos de processamento
do sinal de áudio. Essa rota é descrita com mais
detalhes na especificação da API Web Audio .
Uma única instância de AudioContext pode oferecer suporte a várias entradas de som e gráficos de áudio complexos. Portanto, só vamos precisar de uma delas para cada aplicativo de áudio que criarmos.
O snippet a seguir cria um AudioContext:
var context;
window.addEventListener('load', init, false);
function init() {
try {
context = new AudioContext();
}
catch(e) {
alert('Web Audio API is not supported in this browser');
}
}
Para navegadores mais antigos baseados no WebKit, use o webkit prefixo, como em
webkitAudioContext.
Muitas das funcionalidades interessantes da API Web Audio, como a criação de AudioNodes e a decodificação de dados de arquivos de áudio, são métodos de AudioContext.
Carregar sons
A API Web Audio usa um AudioBuffer para sons de curta a média duração. A abordagem básica é usar XMLHttpRequest para buscar arquivos de som.
A API oferece suporte ao carregamento de dados de arquivos de áudio em vários formatos, como WAV, MP3, AAC, OGG e outros. O suporte do navegador para diferentes formatos de áudio varia.
O snippet a seguir demonstra o carregamento de uma amostra de som:
var dogBarkingBuffer = null;
var context = new AudioContext();
function loadDogSound(url) {
var request = new XMLHttpRequest();
request.open('GET', url, true);
request.responseType = 'arraybuffer';
// Decode asynchronously
request.onload = function() {
context.decodeAudioData(request.response, function(buffer) {
dogBarkingBuffer = buffer;
}, onError);
}
request.send();
}
Os dados do arquivo de áudio são binários (não texto). Portanto, definimos o responseType
da solicitação como 'arraybuffer'. Para mais informações sobre
ArrayBuffers, consulte este artigo sobre XHR2.
Depois que os dados do arquivo de áudio (não decodificados) forem recebidos, eles poderão ser mantidos para decodificação posterior ou decodificados imediatamente usando o método decodeAudioData() do AudioContext. Esse método usa o ArrayBuffer de dados de arquivos de áudio armazenados em request.response e o decodifica de forma assíncrona (sem bloquear a linha de execução principal do JavaScript).
Quando decodeAudioData() termina, ele chama uma função de callback que fornece os dados de áudio PCM decodificados como um AudioBuffer.
Reproduzir sons
Depois que um ou mais AudioBuffers forem carregados, estaremos prontos para reproduzir sons. Vamos supor que acabamos de carregar um AudioBuffer com o som de um cachorro latindo e que o carregamento foi concluído. Em seguida, podemos reproduzir esse buffer com o código a seguir.
var context = new AudioContext();
function playSound(buffer) {
var source = context.createBufferSource(); // creates a sound source
source.buffer = buffer; // tell the source which sound to play
source.connect(context.destination); // connect the source to the context's destination (the speakers)
source.noteOn(0); // play the source now
}
Essa função playSound() pode ser chamada sempre que alguém pressionar uma tecla ou clicar em algo com o mouse.
A função noteOn(time) facilita o agendamento da reprodução precisa de som para jogos e outros aplicativos com tempo crítico. No entanto, para que esse agendamento funcione corretamente, verifique se os buffers de som estão pré-carregados.
Abstrair a API Web Audio
É claro que seria melhor criar um sistema de carregamento mais geral que não seja codificado para carregar esse som específico. Há muitas abordagens para lidar com os sons de curta a média duração que um aplicativo ou jogo de áudio usaria. Aqui está uma maneira de usar um BufferLoader (não faz parte do padrão da Web).
A seguir, apresentamos um exemplo de como usar a classe BufferLoader.
Vamos criar dois AudioBuffers e, assim que forem carregados, vamos reproduzi-los ao mesmo tempo.
window.onload = init;
var context;
var bufferLoader;
function init() {
context = new AudioContext();
bufferLoader = new BufferLoader(
context,
[
'../sounds/hyper-reality/br-jam-loop.wav',
'../sounds/hyper-reality/laughter.wav',
],
finishedLoading
);
bufferLoader.load();
}
function finishedLoading(bufferList) {
// Create two sources and play them both together.
var source1 = context.createBufferSource();
var source2 = context.createBufferSource();
source1.buffer = bufferList[0];
source2.buffer = bufferList[1];
source1.connect(context.destination);
source2.connect(context.destination);
source1.noteOn(0);
source2.noteOn(0);
}
Lidar com o tempo: reproduzir sons com ritmo
A API Web Audio permite que os desenvolvedores agendem a reprodução com precisão. Para demonstrar isso, vamos configurar uma faixa de ritmo simples. Provavelmente, o padrão de bateria mais conhecido é o seguinte:
em que um hi-hat é tocado a cada oitava nota, e o bumbo e a caixa são tocados alternadamente a cada quarto, em 4/4.
Supondo que tenhamos carregado os buffers kick, snare e hihat, o código para fazer isso é simples:
for (var bar = 0; bar < 2; bar++) {
var time = startTime + bar * 8 * eighthNoteTime;
// Play the bass (kick) drum on beats 1, 5
playSound(kick, time);
playSound(kick, time + 4 * eighthNoteTime);
// Play the snare drum on beats 3, 7
playSound(snare, time + 2 * eighthNoteTime);
playSound(snare, time + 6 * eighthNoteTime);
// Play the hi-hat every eighth note.
for (var i = 0; i < 8; ++i) {
playSound(hihat, time + i * eighthNoteTime);
}
}
Aqui, fazemos apenas uma repetição em vez do loop ilimitado que vemos na partitura. A função playSound é um método que reproduz um buffer em um horário especificado, da seguinte maneira:
function playSound(buffer, time) {
var source = context.createBufferSource();
source.buffer = buffer;
source.connect(context.destination);
source.noteOn(time);
}
Mudar o volume de um som
Uma das operações mais básicas que você pode querer fazer com um som é mudar o volume. Usando a API Web Audio, podemos rotear nossa origem para o destino por meio de um AudioGainNode para manipular o volume:

Essa configuração de conexão pode ser feita da seguinte maneira:
// Create a gain node.
var gainNode = context.createGainNode();
// Connect the source to the gain node.
source.connect(gainNode);
// Connect the gain node to the destination.
gainNode.connect(context.destination);
Depois que o gráfico for configurado, você poderá mudar o volume programaticamente manipulando o gainNode.gain.value da seguinte maneira:
// Reduce the volume.
gainNode.gain.value = 0.5;
Crossfade entre dois sons
Agora, suponha que temos um cenário um pouco mais complexo, em que estamos reproduzindo vários sons, mas queremos fazer um crossfade entre eles. Esse é um caso comum em um aplicativo de DJ, em que temos dois toca-discos e queremos poder fazer a panorâmica de uma fonte de som para outra.
Isso pode ser feito com o seguinte gráfico de áudio:
Para configurar isso, basta criar dois AudioGainNodes e conectar cada fonte pelos nós, usando algo como esta função:
function createSource(buffer) {
var source = context.createBufferSource();
// Create a gain node.
var gainNode = context.createGainNode();
source.buffer = buffer;
// Turn on looping.
source.loop = true;
// Connect source to gain.
source.connect(gainNode);
// Connect gain to destination.
gainNode.connect(context.destination);
return {
source: source,
gainNode: gainNode
};
}
Crossfade de potência igual
Uma abordagem de crossfade linear ingênua exibe uma queda de volume ao fazer a panorâmica entre as amostras.
Para resolver esse problema, usamos uma curva de potência igual, em que as curvas de ganho correspondentes são não lineares e se cruzam em uma amplitude maior. Isso minimiza as quedas de volume entre as regiões de áudio, resultando em um crossfade mais uniforme entre regiões que podem ser ligeiramente diferentes em nível.
Transição crossfade de playlists
Outro aplicativo de crossfader comum é para um aplicativo de player de música.
Quando uma música muda, queremos atenuar a faixa atual e aumentar a nova para evitar uma transição desagradável. Para fazer isso, agende um crossfade para o futuro. Embora possamos usar setTimeout para fazer este
agendamento, isto não é preciso. Com a API Web Audio, podemos usar a interface AudioParam para agendar valores futuros para parâmetros como o valor de ganho de um AudioGainNode.
Assim, dada uma playlist, podemos fazer a transição entre as faixas agendando uma diminuição de ganho na faixa em reprodução e um aumento de ganho na próxima, ambos um pouco antes da faixa atual terminar a reprodução:
function playHelper(bufferNow, bufferLater) {
var playNow = createSource(bufferNow);
var source = playNow.source;
var gainNode = playNow.gainNode;
var duration = bufferNow.duration;
var currTime = context.currentTime;
// Fade the playNow track in.
gainNode.gain.linearRampToValueAtTime(0, currTime);
gainNode.gain.linearRampToValueAtTime(1, currTime + ctx.FADE_TIME);
// Play the playNow track.
source.noteOn(0);
// At the end of the track, fade it out.
gainNode.gain.linearRampToValueAtTime(1, currTime + duration-ctx.FADE_TIME);
gainNode.gain.linearRampToValueAtTime(0, currTime + duration);
// Schedule a recursive track change with the tracks swapped.
var recurse = arguments.callee;
ctx.timer = setTimeout(function() {
recurse(bufferLater, bufferNow);
}, (duration - ctx.FADE_TIME) - 1000);
}
A API Web Audio fornece um conjunto conveniente de RampToValue métodos para
mudar gradualmente o valor de um parâmetro, como
linearRampToValueAtTime e exponentialRampToValueAtTime.
Embora a função de tempo de transição possa ser escolhida entre as lineares e exponenciais integradas (como acima), também é possível especificar sua própria curva de valor usando uma matriz de valores com a função setValueCurveAtTime.
Aplicar um efeito de filtro simples a um som

A API Web Audio permite canalizar o som de um nó de áudio para outro, criando uma cadeia de processadores potencialmente complexa para adicionar efeitos complexos aos seus sons.
Uma maneira de fazer isso é colocar BiquadFilterNodes entre a fonte e o destino do som. Esse tipo de nó de áudio pode fazer uma variedade de filtros de baixa ordem que podem ser usados para criar equalizadores gráficos e efeitos ainda mais complexos, principalmente para selecionar quais partes do espectro de frequência de um som enfatizar e quais atenuar.
Os tipos de filtros com suporte incluem:
- Filtro passa-baixa
- Filtro passa-alta
- Filtro passa-banda
- Filtro de prateleira baixa
- Filtro de prateleira alta
- Filtro de pico
- Filtro de entalhe
- Filtro passa-tudo
Todos os filtros incluem parâmetros para especificar alguma quantidade de ganho, a frequência em que o filtro será aplicado e um fator de qualidade. O filtro passa-baixa mantém o intervalo de frequência mais baixo, mas descarta as frequências altas. O ponto de interrupção é determinado pelo valor da frequência, e o fator Q não tem unidade e determina a forma do gráfico. O ganho afeta apenas determinados filtros, como os filtros de prateleira baixa e de pico, e não esse filtro passa-baixa.
Vamos configurar um filtro passa-baixa simples para extrair apenas as bases de uma amostra de som:
// Create the filter
var filter = context.createBiquadFilter();
// Create the audio graph.
source.connect(filter);
filter.connect(context.destination);
// Create and specify parameters for the low-pass filter.
filter.type = 0; // Low-pass filter. See BiquadFilterNode docs
filter.frequency.value = 440; // Set cutoff to 440 HZ
// Playback the sound.
source.noteOn(0);
Em geral, os controles de frequência precisam ser ajustados para funcionar em uma escala logarítmica, já que a audição humana funciona no mesmo princípio (ou seja, A4 é 440 Hz e A5 é 880 Hz). Para mais detalhes, consulte a função FilterSample.changeFrequency no link do código-fonte acima.
Por fim, observe que o código de amostra permite conectar e desconectar o filtro, mudando dinamicamente o gráfico do AudioContext. Podemos desconectar AudioNodes do gráfico chamando node.disconnect(outputNumber).
Por exemplo, para redirecionar o gráfico de um filtro para uma conexão direta, podemos fazer o seguinte:
// Disconnect the source and filter.
source.disconnect(0);
filter.disconnect(0);
// Connect the source directly.
source.connect(context.destination);
Mais informações
Abordamos os princípios básicos da API, incluindo o carregamento e a reprodução de amostras de áudio. Criamos gráficos de áudio com nós de ganho e filtros, e sons agendados e ajustes de parâmetros de áudio para ativar alguns efeitos sonoros comuns. Neste ponto, você está pronto para criar alguns aplicativos de áudio da Web incríveis.
Se você estiver buscando inspiração, muitos desenvolvedores já criaram um ótimo trabalho usando a API Web Audio. Alguns dos meus favoritos incluem:
- AudioJedit, uma ferramenta de emenda de som no navegador que usa permalinks do SoundCloud.
- ToneCraft, um sequenciador de som em que os sons são criados empilhando blocos 3D.
- Plink, um jogo colaborativo de criação de música usando Web Audio e Web Sockets.