Transformando Notas Midi em Frequências

Definindo Notas com Cálculos Matemáticos

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. ;)