Conceitos da API de áudio da Web



Bom dia amigos

Este artigo explica alguns conceitos da teoria da música na qual a Web Audio API (WAA) opera. Conhecendo esses conceitos, é possível tomar decisões informadas ao projetar o áudio em um aplicativo. Este artigo não fará de você um engenheiro de som experiente, mas ajudará você a entender por que o WAA funciona da maneira que funciona.

Circuito de áudio


A essência do WAA é executar algumas operações com som dentro de um contexto de áudio. Essa API foi projetada especificamente para roteamento modular. As operações básicas com som são nós de áudio, interconectados e formando um diagrama de roteamento (gráfico de roteamento de áudio). Várias fontes - com diferentes tipos de canais - são processadas em um único contexto. Esse design modular fornece a flexibilidade necessária para criar funções complexas com efeitos dinâmicos.

Os nós de áudio são interconectados através de entradas e saídas, formam uma cadeia que inicia de uma ou mais fontes, passa por um ou mais nós e termina no destino. Em princípio, você pode ficar sem um destino, por exemplo, se apenas queremos visualizar alguns dados de áudio. Um fluxo de trabalho típico de áudio da Web se parece com isso:

  1. Crie um contexto de áudio
  2. Dentro do contexto, crie fontes - como <audio>, um oscilador (gerador de som) ou fluxo
  3. Crie nós de efeito como reverb , filtro biquad, panner ou compressor
  4. Selecione um destino para o áudio, como alto-falantes no computador do usuário
  5. Estabelecer uma conexão entre fontes através de efeitos para um destino



Designação de canal


O número de canais de áudio disponíveis é frequentemente indicado em formato numérico, por exemplo, 2.0 ou 5.1. Isso é chamado de designação de canal. O primeiro dígito indica a faixa completa de frequências que o sinal inclui. O segundo dígito indica o número de canais reservados para as saídas de efeito de baixa frequência - subwoofers .

Cada entrada ou saída consiste em um ou mais canais construídos de acordo com um determinado circuito de áudio. Existem várias estruturas de canais discretos, como mono, estéreo, quad, 5.1, etc.



As fontes de áudio podem ser obtidas de várias maneiras. O som pode ser:

  • Gerado por JavaScript através de um nó de áudio (como um oscilador)
  • Criado a partir de dados brutos usando PCM (Pulse Code Modulation)
  • Derivado de elementos de mídia HTML (como <video> ou <audio>)
  • Derivado de um fluxo de mídia WebRTC (como uma webcam ou microfone)

Dados de áudio: o que está na amostra


Amostragem significa converter um sinal contínuo em um sinal discreto (dividido) (analógico para digital). Em outras palavras, uma onda sonora contínua, como um concerto ao vivo, é convertida em uma sequência de amostras, o que permite ao computador processar o áudio em blocos separados.

Buffer de áudio: quadros, amostras e canais


O AudioBuffer aceita o número de canais como parâmetros (1 para mono, 2 para estéreo etc.), comprimento - o número de quadros de amostra dentro do buffer e frequência de amostragem - o número de quadros por segundo.

Uma amostra é um valor simples de ponto flutuante de 32 bits (float32), que é o valor do fluxo de áudio em um ponto específico no tempo e em um canal específico (esquerda ou direita, etc.). Um quadro de amostra ou quadro é um conjunto de valores de todos os canais reproduzidos em um determinado momento: todas as amostras de todos os canais são reproduzidos ao mesmo tempo (dois para estéreo, seis para 5.1, etc.).

A taxa de amostragem é o número de amostras (ou quadros, já que todas as amostras em um quadro são reproduzidas ao mesmo tempo), reproduzidas em um segundo, medidas em hertz (Hz). Quanto maior a frequência, melhor a qualidade do som.

Vejamos os buffers mono e estéreo, cada um com um segundo de comprimento, reproduzidos com uma frequência de 44100 Hz:

  • O buffer mono terá 44100 amostras e 44100 quadros. O valor da propriedade "length" é 44100
  • O buffer estéreo terá 88.200 amostras, mas também 44.100 quadros. O valor da propriedade "length" será 44100 - o comprimento é igual ao número de quadros



Quando a reprodução do buffer começa, ouvimos primeiro o quadro mais à esquerda da amostra, depois o quadro mais próximo à direita, etc. No caso do estéreo, ouvimos os dois canais simultaneamente. Os quadros de amostra são independentes do número de canais e oferecem a oportunidade de processamento de áudio muito preciso.

Nota: para obter o tempo em segundos a partir do número de quadros, é necessário dividir o número de quadros pela taxa de amostragem. Para obter o número de quadros do número de amostras, divida o último pelo número de canais.

Exemplo:

let context = new AudioContext()
let buffer = context.createBuffer(2, 22050, 44100)

Nota: no áudio digital, 44100 Hz ou 44,1 kHz é a frequência de amostragem padrão. Mas por que 44,1 kHz?

Em primeiro lugar, porque o intervalo de frequências audíveis (frequências distinguíveis pelo ouvido humano) varia de 20 a 20.000 Hz. De acordo com o teorema de Kotelnikov, a frequência de amostragem deve mais que dobrar a frequência mais alta no espectro de sinal. Portanto, a frequência de amostragem deve ser maior que 40 kHz.

Em segundo lugar, os sinais devem ser filtrados usando um filtro passa-baixo.antes da amostragem, caso contrário, haverá uma sobreposição de “caudas” espectrais (troca de frequência, mascaramento de frequência, pseudônimo) e a forma do sinal reconstruído será distorcida. Idealmente, um filtro passa-baixas deve passar frequências abaixo de 20 kHz (sem atenuação) e diminuir frequências acima de 20 kHz. Na prática, é necessária alguma banda de transição (entre a banda passante e a banda de supressão), onde as frequências são parcialmente atenuadas. Uma maneira mais fácil e econômica de fazer isso é usar um filtro anti-mudança. Para uma frequência de amostragem de 44,1 kHz, a banda de transição é de 2,05 kHz.

No exemplo acima, obtemos um buffer estéreo com dois canais, reproduzido em um contexto de áudio com uma frequência de 44100 Hz (padrão) e 0,5 segundo de comprimento (22050 quadros / 44100 Hz = 0,5 s).

let context = new AudioContext()
let buffer = context.createBuffer(1, 22050, 22050)

Nesse caso, obtemos um buffer mono com um canal, reproduzido em um contexto de áudio com uma frequência de 44100 Hz, sobreamostrando para 44100 Hz (e aumentando os quadros para 44100), com 1 segundo de duração (44100 quadros / 44100 Hz = 1 s).

Nota: A reamostragem de áudio ("reamostragem") é muito semelhante a redimensionar ("redimensionar") imagens. Suponha que tenhamos uma imagem 16x16, mas queremos preencher essa área com um tamanho 32x32. Nós redimensionamos. O resultado será menos qualidade (pode ficar embaçado ou rasgado, dependendo do algoritmo de zoom), mas funciona. Áudio reamostrado é a mesma coisa: economizamos espaço, mas, na prática, é improvável que você obtenha som de alta qualidade.

Tampões planares e listrados


WAA usa um formato de buffer planar. Os canais esquerdo e direito interagem da seguinte maneira:

LLLLLLLLLLLLLLLLRRRRRRRRRRRRRRRR ( ,   16 )

Nesse caso, cada canal funciona independentemente dos outros.

Uma alternativa é usar um formato alternado:

LRLRLRLRLRLRLRLRLRLRLRLRLRLRLRLR ( ,   16 )

Esse formato é frequentemente usado para decodificação de MP3.

O WAA usa apenas o formato planar, pois é mais adequado para processamento de som. O formato plano é convertido para alternar quando os dados são enviados para a placa de som para reprodução. Ao decodificar MP3s, o inverso é convertido.

Canais de áudio


Buffers diferentes contêm um número diferente de canais: de mono simples (um canal) e estéreo (canais esquerdo e direito) a conjuntos mais complexos, como quad e 5.1 com um número diferente de amostras em cada canal, o que fornece um som mais rico (mais rico). Os canais são geralmente representados por abreviações:
Mono0: M: mono
Estéreo0: L: esquerda
1: R: direita
quadro0: L: esquerda
1: R: direita
3: SL: esquerda adicional (canal esquerdo criando o ambiente; surround esquerda)
4: SR: direita adicional (surround direita)
5.10: L: esquerda
1: R: direita
2: C: centro
3: LFE: subwoofer
4: SL: extra esquerda
5: SR: extra direita

Up-mix e down-mix


Quando o número de canais na entrada e na saída não corresponder, aplique mixagem para cima ou para baixo. A mistura é controlada pela propriedade AudioNode.channelInterpretation:
Canais de entradaCanais de saídaRegras de mistura
Esquemas de mixagem de canal padrão - usados ​​quando a propriedade channelInterpretation está definida para alto-falantes (alto-falantes).
1 (Mono)2 (estéreo)Misture de mono para estéreo.
O canal de entrada M é usado para os dois canais de saída (L e R).
output.L = input.M
output.R = input.M
1 (Mono)4 (quad)Misture de mono para quad.
O canal de entrada M é usado para os canais principais (L e R). Canais adicionais estão abafados.
output.L = input.M
output.R = input.M
output.SL = 0
output.SR = 0
1 (Mono)6 (5,1)5.1.
©. (L, R, LFE, SL SR) .
output.L = 0
output.R = 0
output.C = input.M
output.LFE = 0
output.SL = 0
output.SR = 0
2 ()1 ().
(L R) (M).
output.M = 0.5 * (input.L + input.R)
2 ()4 ().
L R (L R). (SL SR) .
output.L = input.L
output.R = input.R
output.SL = 0
output.SR = 0
2 ()6 (5.1)5.1.
L R (L R). (SL SR), © (LFE) .
output.L = input.L
output.R = input.R
output.C = 0
output.LFE = 0
output.SL = 0
output.SR = 0
4 ()1 ().
(L, R, SL SR) (M).
output.M = 0.25 * (input.L + input.R + input.SL + input.SR)
4 ()2 ().
(L SL) (L). (R SR) — ®.
output.L = 0.5 * (input.L + input.SL)
output.R = 0.5 * (input.R + input.SR)
4 ()6 (5.1)5.1.
L, R, SL SR . © (LFE) .
output.L = input.L
output.R = input.R
output.C = 0
output.LFE = 0
output.SL = input.SL
output.SR = input.SR
6 (5.1)1 ()5.1 .
(L SL), (R SR) () . , , — √2 / 2. (LFE) .
output.M = 0.7071 * (input.L + input.R) + input.C + 0.5 * (input.SL + input.SR)
6 (5.1)2 ()5.1 .
© (SL SR) (L R). √2 / 2. (LFE) .
output.L = input.L + 0.7071 * (input.C + input.SL)
output.R = input.R + 0.7071 * (input.C + input.SR)
6 (5.1)4 ()5.1 .
() (L R). √2 / 2. . (LFE) .
output.L = input.L + 0.7071 * input.C
output.R = input.R + 0.7071 * input.C
output.SL = input.SL
output.SR = input.SR
— , channelInterpretation discrete.
(x)(y), x<y.
( ). , , .
(x)(y), x>yCada canal de saída é combinado com sua entrada analógica (com o mesmo índice). Canais que não possuem canais de saída correspondentes são descartados.

Visualização


A visualização é baseada no recebimento de dados de áudio de saída, como dados de amplitude ou frequência, e seu processamento subsequente usando qualquer tecnologia gráfica. O WAA possui um AnalyzerNode que não distorce o sinal que passa por ele. Ao mesmo tempo, é capaz de extrair dados do áudio e transferi-los ainda mais, por exemplo, para o & ltcanvas>.



Os seguintes métodos podem ser usados ​​para extrair dados:

  • AnalyzerNode.getFloatByteFrequencyData () - copia os dados de frequência atuais para o Float32Array
  • AnalyzerNode.getByteFrequencyData () - copia os dados de frequência atuais para um Uint8Array (matriz de bytes não assinados)
  • AnalyserNode.getFloatTimeDomainData() — Float32Array
  • AnalyserNode.getByteTimeDomainData() — Uint8Array


A espacialização do áudio (processada pelo PannerNode e AudioListener) permite simular a posição e a direção do sinal em um ponto específico do espaço, bem como a posição do ouvinte.

A posição do panner é descrita usando coordenadas cartesianas destras; para o movimento, o vetor de velocidade necessário para criar o efeito Doppler é usado ; para a direção, o cone de diretividade é usado. Este cone pode ser muito grande no caso de fontes de som multidirecionais.



A posição do ouvinte é descrita a seguir: movimento - usando o vetor de velocidade, a direção em que a cabeça do ouvinte está - usando dois vetores direcionais, frontal e superior. O encaixe é feito na parte superior da cabeça e do nariz do ouvinte em ângulo reto.



Junção e ramificação


Uma conexão descreve um processo no qual um ChannelMergerNode recebe várias fontes mono de entrada e as combina em um único sinal de saída multicanal.



Ramificação é o processo inverso (implementado através do ChannelSplitterNode).



Um exemplo de trabalho com a WAA pode ser encontrado aqui . O código fonte do exemplo está aqui . Aqui está um artigo sobre como tudo funciona.

Obrigado pela atenção.

All Articles