A Web Audio API é utilizada para o processamento e síntese de audio diretamente no browser.
Tal como na utilização do Canvas é necessaria a definição de um contexto, para a manipulação de som tambem será necessário um: AudioContext. No entanto, recomenda-se a utilização só de um contexto audio por aplicação, pois é capaz de manipular diversas entradas de audio.
var AudioContext = window.AudioContext || window.webkitAudioContext;
var audioCtx = new AudioContext();
Um conceito presente nesta API (e também comum a outras ferramentas de manipulação de audio) é o conceito de nós e audio routes. Estes conceitos são um modo simples de representar as ligações entre uma fonte de som e um destino (pex colunas), que, entre elas, pode existir outros nós que podem aplicar efeitos sonoros ao som original ou outro tipo de manipulação.
//Exemplo: criou-se um oscilador como fonte, que se ligou a um nó para
//controlo do volume, e por fim ligou-se este nó ao nó final que
//normalmente são as colunas do computador.
var oscillator = audioCtx.createOscillator();
var gainNode = audioCtx.createGain();
oscillator.connect(gainNode);
gainNode.connect(audioCtx.destination);
Para além do contexto, é necessário uma fonte de audio, pode ser:
<video>
ou <audio>
;Para este exemplo, optou-se por utilizar um (oscilador)[https://en.wikibooks.org/wiki/Sound_Synthesis_Theory/Oscillators_and_Wavetables] para gerar um tom simples, que será ligado às colunas do computador, context.destination
. Para poder ouvir o som, tem-se que se dar início ao oscilador. No exemplo seguinte, tambem se incluiu (na ultima linha) o pedido para o oscilador terminar ao fim de 3 segundos.
var oscillator = audioCtx.createOscillator();
oscillator.connect(audioCtx.destination);
oscillator.start();
oscillator.stop(3);
Neste exemplo tambem se pretende visualizar o sinal de audio, para isso vamos acrescentar um nó ao nosso grafo: AnalyserNode
. Este nó não modifica o sinal, mas permite utilizar esses dados. O analyser extrai a frequência, onda, entre outros dados.
var analyser = audioCtx.createAnalyser();
oscillator.connect(analyser);
analyser.connect(audioCtx.destination);
O nó analyser captura os dados audio atraves da Transformada de Fourier a uma determinada frequência definida pela propriedade AnalyserNode.fft
(por omissão 2048Hz). Existem diversos métodos para capturar diferentes tipos de dados, neste caso utilzou-se: AnalyserNode.getByteTimeDomainData()
, devolve um array de inteiros (8bit), (Uint8Array)[https://developer.mozilla.org/en-US/docs/Web/JavaScript/Typed_arrays].
bufferLength = analyser.frequencyBinCount;
dataArray = new Uint8Array(bufferLength);
analyser.getByteTimeDomainData(dataArray);
Então, aqui criou-se um array Uint8Array, de tamanho determinado pelo AnalyserNode.frequencyBinCount
e preencheu-se esse array com os dados lidos pelo analyser atraves de AnalyserNode.getByteTimeDomainData(dataArray)
.
Para o desenho da onda, sempre que se atualiza o desenho (chama-se a função draw()
), tem de se atualizar os dados do dataArray
, ou seja, tem que se ler os dados que passam do oscilador para o analyser atraves da função AnalyserNode.getByteTimeDomainData(dataArray)
. Após essa atualização, tem de se percorrer todos esses valores, para atualizar o valor de y
(o valor de x
corresponde a uma divisão da largura do canvas em partes iguais).
Nota: na demonstração seguinte o oscilador termina ao fim de 20s.
var canvas, ctx,
oscillator, audioCtx, analyser,
freq, waveType, bufferLength, dataArray,
width = 300, height = 300, x, y;
function setup() {
var canvas = document.getElementById('canvas');
freq = 180;
waveType = 'sine';
ctx = canvas.getContext("2d");
ctx.strokeStyle = 'rgb(0, 0, 255)';
audioCtx = new (window.AudioContext || window.webkitAudioContext)();
analyser = audioCtx.createAnalyser();
oscillator = audioCtx.createOscillator();
oscillator.connect(analyser);
analyser.connect(audioCtx.destination);
bufferLength = analyser.frequencyBinCount;
dataArray = new Uint8Array(bufferLength);
analyser.getByteTimeDomainData(dataArray);
oscillator.type = waveType;
oscillator.frequency.value = freq;
oscillator.start();
draw();
}
function draw() {
requestAnimationFrame(draw);
analyser.getByteTimeDomainData(dataArray);
ctx.clearRect(0, 0, width, height);
ctx.beginPath();
var sliceWidth = width / bufferLength;
x = 0;
y = (dataArray[0] / 128.0) * height/2;
ctx.moveTo(x, y);
for(var i = 1; i != bufferLength; i++){
x += sliceWidth;
y = (dataArray[i] / 128.0) * height/2;
ctx.lineTo(x, y );
}
ctx.stroke();
}