Upgrade to Pro — share decks privately, control downloads, hide ads and more …

A gift of sound and vision

A gift of sound and vision

Sabemos que conseguimos extrair informação de áudio e imagem usando nossas queridas APIs no browser. Nessa palestra eu demonstro algumas propriedades interessantes envolvendo esses dados. Se a Web Audio API e Canvas nos fornecem arrays, podemos inverter as coisas e filtrar uma imagem? Como um pedal de guitarra afetaria a Mona Lisa? Foi sobre isso que David Bowie cantou quando escreveu "Sound and Vision"?

Rafael Specht da Silva

August 25, 2018
Tweet

More Decks by Rafael Specht da Silva

Other Decks in Programming

Transcript

  1. Em 1977 David Bowie lançou o álbum Low, uma das

    maiores obras musicais da humanidade. Curiosidade: esse disco contém alguns dos melhores usos de sintetizadores que conhecemos enquanto civilização.
  2. Esse incrível álbum contém a música “Sound and Vision”. Inclusive,

    o remaster do single de 2013 tem uma onda sonora na capa. Nessa palestra eu PROVO que “A Gift of Sound and Vision” se refere às APIs que temos no browser hoje em dia. Pois sabemos que nosso querido Starman era um visionário!
  3. Rafael Specht da Silva - Graduado em Telecomunicações - Entusiasta

    JS - Vivendo o sexto BrazilJS - Enciclopédia de Choque de Cultura
  4. const canvas = document.querySelector('#canvas') const context = canvas.getContext('2d') const baseImage

    = new Image() baseImage.src = '../david-bowie-low-small.jpg' baseImage.onload = () => { const { width, height } = context.canvas context.drawImage(baseImage, 0, 0, width, height) const imageInfo = context.getImageData(0, 0, width, height) // imageInfo.data // R G B A R G B A R G B A // [72, 36, 22, 255, 75, 37, 24, 255, 78, 35, 26, 255] }
  5. Canvas nos permite adquirir dados da imagem renderizada nele com

    o método .getImageData. Esse método retorna um array com dados de cor (R, G, B, A) de cada pixel.
  6. Quando plotamos esse array o formato parece um pente ou

    serrote. Porque em muitos casos o valor de ALPHA é 255. Então o array nessa forma não é muito útil
  7. Mesmo quando removemos o alpha do array, os dados não

    parecem muito claros porque é fácil que o valor de Vermelho, Verde e Azul sejam muito diferentes
  8. Mas quando iteramos o array separando cada cor em um

    array separado temos informações interessantes. Quando plotamos as cores separadas da primeira linha da imagem, conseguimos ver nitidamente o quanto há de vermelho a mais que as outras duas. Isso pela forte presença do laranja na linha.
  9. Se plotamos a linha de número 200 podemos ver intensidades

    similares. É interessante de ver como as 3 cores tem redução abrupta por causa da cor escura do casaco do Bowie.
  10. A Web Audio API permite que adicionemos nós com funções

    específicas para modificar/ produzir som.
  11. const SAMPLE_RATE = 44100 const audioContext = new AudioContext() const

    increment = 1 / SAMPLE_RATE const signal = [] for (let t = 0; t < 1; t += increment) { const value = Math.sin(6.28 * 300 * t) signal.push(value) } const node = audioContext.createBufferSource() const buffer = audioContext.createBuffer(1, signal.length, SAMPLE_RATE) const data = buffer.getChannelData(0) for (let i = 0; i < signal.length; i += 1) { data[i] = signal[i] } node.buffer = buffer node.connect(audioContext.destination) node.start(audioContext.currentTime)
  12. Vamos criar um array que servirá como uma onda sonora

    (seno de 300 Hertz). Sample Rate ou taxa de amostragem é basicamente quantas amostras/valores temos em 1 segundo de som. Nesse caso estamos lidando com 44100.
  13. const SAMPLE_RATE = 44100 const audioContext = new AudioContext() const

    increment = 1 / SAMPLE_RATE const signal = [] for (let t = 0; t < 1; t += increment) { const value = Math.sin(6.28 * 300 * t) signal.push(value) } const node = audioContext.createBufferSource() const buffer = audioContext.createBuffer(1, signal.length, SAMPLE_RATE) const data = buffer.getChannelData(0) for (let i = 0; i < signal.length; i += 1) { data[i] = signal[i] } node.buffer = buffer node.connect(audioContext.destination) node.start(audioContext.currentTime)
  14. Vamos criar um bufferSource e um buffer onde adicionaremos os

    valores desse sinal. Conectamos a fonte à saída de áudio e chamamos o método .start Então temos uma senóide de 300 hertz tocando!
  15. Nesse exemplo criaremos um buffer e usaremos diferentes funções para

    produzir sons. Desde tons simples até um violino passando por uma pistola laser! (aperte as teclas de a até g no teclado para evitar som) https://github.com/rssilva/presentations/tree/master/braziljs-2018/pages/playing-array
  16. Se lembrarmos que cores de imagens são arrays de números,

    podemos usar um array como buffer e ouvir uma cor? SIM! https://github.com/rssilva/presentations/tree/master/braziljs-2018/pages/playing-image-color
  17. Gráfico de frequência (azul e vermelho) e tempo (branco) da

    cor vermelha extraída da capa do Low tocando. O raio é meramente ilustrativo
  18. Podemos extrair dados da imagem no canvas e reescrevê-los como

    quisermos https://github.com/rssilva/presentations/tree/master/braziljs-2018/pages/signals-on-image
  19. const green = splitted.green.map((val, index) => { return 155 +

    100 * (Math.sin(index / VALUE)) }) const blue = splitted.blue.map((val, index) => { return 155 + 100 * (Math.cos(index / VALUE)) })
  20. Imagem: de 0 a 255 Áudio: ~ -1 a 1

    Os dados da imagem variam de 0 a 255 e de áudio sem ganho ou efeitos estão entre -1 e 1. Podemos fazer uma regra de três e parseá-los!
  21. Se podemos usar as cores das imagens como áudio elas

    são suscetíveis aos nós da Web Audio API. Vamos usar um passa-baixas e um passa- altas nos exemplos. https://github.com/rssilva/presentations/tree/master/braziljs-2018/pages/filters
  22. Quando aplicamos o filtro nas cores de uma imagem separadamente

    temos esse resultado para o passa-baixas. As transições entre os pixels suavizam e borram (efeito blur)
  23. Se podemos extrair informação (um array) de uma música tocando

    com Web Audio API e podemos escrever no canvas, logo, podemos usar uma música em vez de uma imagem. https://github.com/rssilva/presentations/tree/master/braziljs-2018/pages/sound-as-image
  24. Sons não tem uma alta variação de amplitude como os

    arrays de imagens. Como os valores são próximos um do outro temos muitos tons de cinza na imagem. Cores que tem valores parecidos e próximos da média entre 0 e 255 são cinzas
  25. Na imagem da esquerda estamos usando o alpha de todos

    os pixels como 255 e na da direita eles são usados para armazenar informações do som.
  26. Eu usei muito do que o Sam Bellen (@sambego no

    twitter) construiu nos efeitos. Se você toca algum instrumento eu recomendo fortemente conferir :) https://sambego.github.io/pedalboard/ https://github.com/Sambego/audio-effects/ https://www.youtube.com/watch?v=BEdFcnI-ppk
  27. Delay Quando adicionamos um delay entre a entrada e a

    saída não é muito útil para tocar guitarra porque o nó simplesmente produz um atraso (aha!) no sinal
  28. Se usarmos as cores como entrada do delay, elas serão

    deslocadas e quando reconstruímos a imagem esse deslocamento é visível! https://github.com/rssilva/presentations/blob/master/braziljs-2018/pages/pedals/delay-without-direct.html
  29. Se quisermos um delay mais útil para uma guitarra precisamos

    adicionar uma ligação direta entre a saída e a entrada E também um delay.
  30. Como temos uma ligação direta entre a entrada e saída

    a imagem original será preservada e o delay resultará em outra saída deslocada! https://github.com/rssilva/presentations/blob/master/braziljs-2018/pages/pedals/delay-direct.html
  31. Podemos criar um efeito interessante ligando o delay a um

    nó de ganho e o ganho de volta ao delay (e à saída também). Isso cria uma estrutura de looping!
  32. Flanger O efeito de Flanger é interessante porque tem uma

    estrutura de looping e delay E um oscilador
  33. Sinal de entrada Sinal na saída do flanger Sinal da

    saída do flanger com valor mais baixo de delay
  34. É interessante notar que o oscilador faz com que a

    imagem oscile também! https://github.com/rssilva/presentations/blob/master/braziljs-2018/pages/pedals/flanger.html
  35. Reverb Para criar um efeito de reverb nós vamos carregar

    um áudio que consiste de uma pessoa batendo uma palma (não 2, não 3 :P) dentro de um hall e setá-lo como buffer do nó (que se chama Convolver, por isso o C no diagrama).
  36. A reverberação afeta a amplitude e basicamente muda todas as

    propriedades da imagem transformando numa estampa para camisa hippie bem bonitona! https://github.com/rssilva/presentations/blob/master/braziljs-2018/pages/pedals/reverb.html
  37. Em vários posts/blogs/MDN temos o uso funções sigmoid que distorcem

    o sinal na razão da sua transição pelo ponto 0. A curva em branco é o caso menos distorcido e a laranja é o mais distorcido.
  38. Sinal de entrada Sinal na saída do waveshaper mais distorcida

    Sinal na saída do waveshaper menos distorcida
  39. A distorção afeta a amplitude arredondando os valores fazendo que

    as imagens fiquem com cores menos vívidas https://github.com/rssilva/presentations/blob/master/braziljs-2018/pages/pedals/waveshaper.html
  40. Se arredondarmos valores de amplitude de uma onda sonora para

    um dicionário de valores conhecidos (e bem restritos) essa música vai lembrar música de video game! https://github.com/rssilva/presentations/blob/master/braziljs-2018/pages/gameretro/playretro.html
  41. Nesse exemplo carregamos os modelos 3D e controlamos a intensidade

    da luz conforme a média das frequências da música https://github.com/rssilva/presentations/tree/master/braziljs-2018/aframe