03 – Javascript – Web Audio API – Convertendo Notas Midi em Frequência

Transformando Notas Midi em Frequências

Definindo Notas com Cálculos Matemáticos

Definindo Notas Midi com Web Audio API

Definindo Notas Midi com Web Audio API

Vamos usar a matemática para definir as notas cromaticamente em 4 faixas horizontais no browser.

Para entender melhor o que é Musical Instrument Digital Interface (MIDI), acesse:

http://newt.phys.unsw.edu.au/jw/notes.html

Podemos dizer que MIDI é um padrão usado para fazer com que o som gerado por diferentes sintetizadores corresponda exatamente às mesmas notas dos instrumentos, ou seja, as frequências das notas.

Contruindo nosso segundo sintetizador web

São três arquivos para esse experimento, mas, o projeto compartilha as pastas css e dependencies, como no exemplo do Theremin.

Obs. As pastas css e dependencies em comum, estão no mesmo nível da pasta de cada projeto.

Dentro da pasta do projeto que eu chamei de Convertendo-Notas-Midi-em-Frequencia temos:

  • Uma pasta css com o color.css que é um css específico para esse experimento;
  • O index_math_notes.html;
  • E o script math_notes.js.

O conteúdo do Web_Audio_API/Convertendo-Notas-Midi-em-Frequencia/css/color.css é:


* {
box-sizing: border-box;
}
body,
html {
height: 100%;
padding: 0;
margin: 0;
font-family: 'Andale Mono', monospace;
font-size: 18px;
text-align: center;
cursor: default;
}
h3 {
display: inline-block;
margin: 0;
padding: 1em;
background-color: white;
-webkit-user-select: none;
}
p {
max-width: 600px;
margin: 0 auto;
margin-bottom: 2em;
line-height: 1.5em;
text-align: left;
}
a {
padding: 0.1em 0.25em;
background-color: #FF4D51;
text-decoration: none;
color: white;
}
a:hover {
background-color: #ff1a1f;
}
.intro {
max-width: 600px;
margin: 0 auto;
}
ul {
list-style-type: none;
text-align: left;
}
ul li {
line-height: 1.5em;
}
.boxes {
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
z-index: -1;
cursor: default;
}
.box {
width: 7.6923076923076925%;
height: 100%;
float: left;
background-color: #d0d0d0;
}
.box:nth-child(2n) {
background-color: #f4f4f4;
}
.horizontal_band {
position: absolute;
left: 0;
right: 0;
}
.horizontal_band--1 {
top: 25%;
bottom: 50%;
background-color: rgba(255, 0, 0, 0.2);
}
.horizontal_band--2 {
top: 50%;
bottom: 25%;
background-color: rgba(255, 0, 0, 0.4);
}
.horizontal_band--3 {
top: 75%;
bottom: 0;
background-color: rgba(255, 0, 0, 0.6);
}
* {
box-sizing: border-box;
}
body,
html {
height: 100%;
padding: 0;
margin: 0;
font-family: 'Andale Mono', monospace;
font-size: 18px;
text-align: center;
cursor: default;
}
h3 {
display: inline-block;
margin: 0;
padding: 1em;
background-color: white;
-webkit-user-select: none;
}
p {
max-width: 600px;
margin: 0 auto;
margin-bottom: 2em;
line-height: 1.5em;
text-align: left;
}
a {
padding: 0.1em 0.25em;
background-color: #FF4D51;
text-decoration: none;
color: white;
}
a:hover {
background-color: #ff1a1f;
}
.intro {
max-width: 600px;
margin: 0 auto;
}
ul {
list-style-type: none;
text-align: left;
}
ul li {
line-height: 1.5em;
}
.boxes {
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
z-index: -1;
cursor: default;
}
.box {
width: 7.6923076923076925%;
height: 100%;
float: left;
background-color: #d0d0d0;
}
.box:nth-child(2n) {
background-color: #f4f4f4;
}
.horizontal_band {
position: absolute;
left: 0;
right: 0;
}
.horizontal_band--1 {
top: 25%;
bottom: 50%;
background-color: rgba(0, 153, 255, 0.2);
}
.horizontal_band--2 {
top: 50%;
bottom: 25%;
background-color: rgba(0, 153, 255, 0.4);
}
.horizontal_band--3 {
top: 75%;
bottom: 0;
background-color: rgba(0, 92, 153, 0.6);
}

O css anterior é específico para o index_math_notes.html, ele define cores horizontais e muda a cor do fundo de dois em dois para dá o efeito de quadriculado na tela do browser.

O Web_Audio_API/Convertendo-Notas-Midi-em-Frequencia/index_math_notes.html é:

<html>
    <head>
        <title>Web Audio API - Usando a matem&aacute;tica para definir notas</title>
        <script src="../dependencies/jquery.min.js"></script>
        <script src="math_notes.js"></script>
        <link rel="stylesheet/less" href="../css/site.less">
        <link rel="stylesheet" type="text/css" href="./css/color.css">
        <script src="../dependencies/less.min.js"></script>
    </head>
    <body>
        <div class="boxes">
            <!-- Faixas verticais -->
            <div class="box"></div><div class="box"></div>
            <div class="box"></div><div class="box"></div>
            <div class="box"></div><div class="box"></div>
            <div class="box"></div><div class="box"></div>
            <div class="box"></div><div class="box"></div>
            <div class="box"></div><div class="box"></div>
            <div class="box"></div>
            <!-- Faixas horizontais -->
            <div class="horizontal_band horizontal_band--1"></div><!--Segunda faixa horizontal-->
            <div class="horizontal_band horizontal_band--2"></div><!--Terceira faixa horizontal-->
            <div class="horizontal_band horizontal_band--3"></div><!--Quarta faixa horizontal-->
        </div>
        <h3>Usando a matem&aacute;tica para definir notas</h3>
        <h2><!-- Mostra as coordenadas -->
            <span id="x">x = 0</span>
            <span id="y">y = 0</span>
        </h2>
    </body>
</html>

O index_math_notes.html declara as dependências de scripts e css no head do html, inclusive o nosso: Convertendo-Notas-Midi-em-Frequencia/css/color.css específico para esse experimento.

No body ele tem uma class chamada boxes, onde serão colocados os boxes individuais, representando as linhas verticais.

E as classe horizontal_band fazendo as linhas horizontais.

Abaixo do H3 tem um H2 com um span dentro, só para mostrar as coordenadas dos eixos X e Y do movimento do mouse.

O Web_Audio_API/Convertendo-Notas-Midi-em-Frequencia/math_notes.js é:


$( function() {

  // pega um contexto de áudio
  var ctx = new AudioContext();

  // variável que vai representar o oscilador
  var osc;

  // obtém os elementos span x e y para que possamos exibir valores deles
  var horizontal = $('#x');
  var vertical   = $('#y');

  // pega a largura e a altura
  var width   = $(window).width();
  var height  = $(window).height();
  var x = 0;
  var y = 0;

  // recalcula a largura e a altura se o tamanho da janela mudar
  $(window).resize( function() {
    width   = $(this).width();
    height  = $(this).height();
  });

  $('body').on('mousedown', function(e) {
    if (osc) {
      osc.stop(0);
    }
    // cria o oscilador
    osc = ctx.createOscillator();
    // define o tipo do oscilador
    osc.type = 'triangle'; // sine, triangle, sawtooth

    // a função mtof recebe uma nota midi como argumento e retorna a freqüência correspondente em Hertz.
    // a função mtof define a frequência com base nos valores x e y
    osc.frequency.value = mtof(x + y);
    // conecta-o à saída, isto é, o destino
    osc.connect(ctx.destination);
    // inicia a nota
    osc.start(0);
  });

  $('body').on('mouseup', function(e) {
    // para a nota quando desclica o botão do mouse (mouseup)
    osc.stop(0);
  });

  $('body').on('mousemove', function(e) {
    var x = e.clientX;
    var y = e.clientY;

    //Mostra as coordenadas na tela
    horizontal.text("X = " + x);
    vertical.text("Y = " + y);
    // faz algumas contas para colocar os valores em intervalos de números:
    // converte (0 <-> largura da janela) para (0 <-> 13) e arredonda a parte decimal pare baixo
    x = 45 + Math.floor( e.clientX / width * 13 );

    // converte (0 <-> altura da janela) para (0 <-> 4) e arredonda a parte decimal pare baixo
    y = Math.floor( e.clientY / height * 4 ) * 12;
    
    if (!osc) {
      return;
    }

    // versão menos precisa
    //osc.frequency.value = mtof(x + y);

    // versão usando setValueAtTime que é mais preciso em relação ao tempo
    osc.frequency.setValueAtTime( mtof(x + y), ctx.currentTime );
  });

});

// mtof = notas midi para Frequencias
// input: 0 - 127 (embora você pudesse subir mais se quisesse)
// output: frequencia em Hz, de ~8Hz até ~12543Hz
function mtof(note) {
  return ( Math.pow(2, ( note-69 ) / 12) ) * 440.0;
}

O código acima inicia o contexto de audio, declara a variável osc que vai representar o oscillator.

Depois pega a largura e a altura da janela e define as coordenadas x e y como zero (0).

Em seguida recalcula a largura e a altura se o tamanho da janela mudar.

Logo depois ele pega o click do mouse no corpo da página, com o evento mousedown, aí, se já existe um oscillador funcionando ele para esse oscilador.

Agora é criado um oscilador e definido o tipo de onda que o ele vai usar.

Após isso, é usada a função mtof que converte midi em frequência para gerar as frequências baseadas nos eixos x e y e conecta o oscilador a à saída, isto é, ao destino.

Nesse ponto, o oscillator é iniciado de fato.

Depois, vem a funçao que para a nota quando o botão do mouse é liberado do click, ou seja, desclicado. É o evento mouseup.

Agora a função que pega o movimento do mouse e faz as contas matemáticas para definir as alturas das notas em cada pondo dos quadriculados formados pelas linha horizintais e verticais.

Logo abaixo duas linhas:

horizontal.text(“X = ” + x);
vertical.text(“Y = ” + y);

Só pra mostrar as coordenadas na tela do browser.

Foi usada a função Math.floor para efetuar o cálculo para a definição da altura das notas.

A Math.floor funciona da seguinte forma, ela arredonda o número para baixo e dispensa a casa decimal.


Math.floor( 45.95); //  45
Math.floor(-45.95); // -46

Através do evento do mouse e.clientX pega a posição do eixo x na tela e e.clientY a posição do y, com esses números é feita a conta.

Converte (0 <-> largura da janela) para (0 <-> 13) e arredonda a parte decimal pare baixo com o Math.floor, com:

x = 45 + Math.floor( e.clientX / width * 13 );

E converte (0 <-> altura da janela) para (0 <-> 4) e arredonda a parte decimal pare baixo com:

y = Math.floor( e.clientY / height * 4 ) * 12;

Se não tiver nenhum oscilador em execução ele simplesmente retorna.

Em osc.frequency.setValueAtTime( mtof(x + y), ctx.currentTime );

Os valores de x e y são passados para o mtof que usa o currentTime do contexto, para ter uma maior precisão ao invés de usar só o osc.frequency.value = mtof(x + y); que não oferece uma precisão tão boa.

E por último a função mtof é definida.

Ela recebe a nota midi baseada na posição x e y do mouse quando clicado e arrastado pelo corpo da página, faz as contas baseado nas especificações midi e retorna a frequência relativa a cada nota na escala cromática.

O código está disponível em:
https://github.com/toticavalcanti/web_audio_api na pasta Convertendo-Notas-Midi-em-Frequencia.

Lembrando que todos os experimentos nesse repositório dependem das pastas css e dependencies.

É isso aí, construímos mais um instrumento web experimental, nos encontramos na próxima aula. \o/

Curta ? a página do Código Fluente no Facebook
https://www.facebook.com/Codigofluente-338485370069035/

Vou deixar meu link de referidos na digitalocean pra vocês.

Quem se cadastrar por esse link, ganha $100.00 dólares de crédito na digitalocean:

Digital Ocean

Esse outro link é da one.com:

One.com

Obrigado, até a próxima e bons estudos. 😉

About The Author
-

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>