Arduino 11: Música com bibliotecas

Neste experimento usaremos uma mini-biblioteca (arquivo .h) de notas musicais para fazer o Arduino executar uma música.

Experimento 32 – Usando bibliotecas para produzir notas musicais

Material necessário

  • Arduino Nano + cabo USB + computador com conexão à internet
  • Alto-falante
  • Protoboard, fios e jumpers

O comando tone() gera pulsos de onda quadrada na frequência passada como parâmetro. Por exemplo, pra produzir um lá central usado para afinação, podemos mandar para um pino PWM que esteja ligado a um alto-falante um comando:

tone(440);

Para tocar uma música precisamos saber a frequência de cada nota, mas o ideal seria poder simplesmente chamar as notas pelo nome.

Existe um arquivo .h para isto. Ele atribui nomes de variáveis para cada uma das 88 notas do piano, guardando a sua frequência. É um programa de domínio público que faz parte dos exemplos da IDE do Arduino. Este é um trecho:

...
#define NOTE_A4 440
#define NOTE_AS4 466
#define NOTE_B4 494
#define NOTE_C5 523
#define NOTE_CS5 554
...

Ou seja, usando-o podemos mandar comandos:

tone(NOTE_A4);

e fazer soar um lá sem precisar lembrar da sua frequência.

NOTE_C3 corresponde ao terceiro Dó do piano. NOTE_A4 ao quarto lá, e assim por diante. O comando noTone(PINO) desliga o sinal no pino (aplica valor LOW, 0V).

Criaremos o arquivo .h localmente. Abra um sketch novo, e em seguida abra um tab (como mostrado acima no exemplo sobre arquivos .h), e cole nele conteúdo do arquivo abaixo:

https://www.arduino.cc/en/Tutorial/ToneMelody?action=sourceblock&num=2

Grave o arquivo como notas.h.

Vamos alterar o nome das variáveis para que fiquem mais curtos e seja mais fácil digitar notas. Com o tab notas.h aberto, digite Control-F (Command-F no Mac) ou selecione o item de menu Edit/Find. Abrirá a janela abaixo.

No campo “Find:” digite NOTE_ e clique no botão Replace All. Isto removerá o prefixo NOTE_ de todas as notas. O resultado deve ficar da forma abaixo:

#define B0 31
#define C1 33
#define CS1 35
#define D1 37
#define DS1 39
#define E1 41
...

Agora acrescente mais uma definição, em uma linha antes de B0:

#define ZZ 0

Esta será a nota de pausa (frequência zero) que não produzirá som algum.

Digite o programa abaixo. Ele foi adaptado dos exemplos do Arduino (“toneMelody” criado por Tom Igoe). Ele usa essa biblioteca de notas musicais para tocar uma pequena música. Nesta versão tocamos uma música diferente. As notas da música estão dentro do bloco melodia[], e os tempos correspondentes de cada nota em durações[]:

#include "notas.h"

int ALTO_FALANTE = 5; // coloque alto-falante ou buzzer entre pino 5 e GND
int NUM_NOTAS = 44;   // número de notas incluídas em melodia[]

int melodia[] = {
    G4, G4, G4, DS4, AS4, G4, DS4, AS4, G4,
    D5, D5, D5, DS5, AS4, FS4, DS4, AS5, G4,
    G5, G4, G4, G5, FS5, F5, E5, DS5, E5, ZZ,
    GS4, CS5, C5, B5, AS5, A5, AS5, ZZ,
    DS4, FS4, DS4, AS4, G4, DS4, AS5, G4};

// duração: 1 = semibreve, 2 = mínima, 4 = seminima, 8 = colcheia, etc.:
int duracoes[] = {
    4, 4, 4, 6, 8, 4, 6, 8, 2,
    4, 4, 4, 6, 8, 4, 6, 8, 2,
    4, 6, 8, 4, 6, 8, 16, 16, 8, 8,
    8, 4, 6, 8, 16, 16, 8, 8,
    8, 4, 6, 8, 4, 6, 8, 2
};

void setup() {
    for (int nota = 0; nota < NUM_NOTAS; nota++) {
        int duracao = 1000 / duracoes[nota]; // divide 1000 por cada duração
        tone(ALTO_FALANTE, melodia[nota], duracao);
        int pausa = duracao * 1.30;
        delay(pausa);
        noTone(ALTO_FALANTE);
    }
}
void loop() {}

O programa roda apenas uma vez no bloco setup(). Se quiser que ela toque sem parar, você pode transferir todo o código para o bloco loop(), incluindo um delay(1000) no final para que haja um intervalo entre as execuções.

O programa usa um bloco de repetição for() para executar cada nota (será explicado mais adiante.)

Construa o circuito abaixo (que talvez seja o circuito mais simples criado no curso), ligando um dos fios do alto-falante no pino 5, e o outro terminal em GND.

Logo após a transferência, a música começará a tocar. Experimente criar outras músicas usando essa biblioteca.

O código-fonte deste experimento (o sketch e arquivo notas.h já alterado) também está disponível para download em repositório GitHub (veja link na introdução da apostila).

Listas

O código do programa anterior possui várias estruturas que ainda não vimos. Uma delas é a lista (também chamado de array, ou vetor). Há duas listas no código do experimento anterior, representadas pelas variáveis durações[] e melodia[]. Listas são declaradas com seus itens entre chaves, separados por vírgula. O tipo de dados se refere ao tipo de cada elemento da lista:

float precos = {23.56, 9.99, 11.99, 25.49};

Cada item da lista é referenciado por um numero que é seu índice. A contagem começa em zero, portanto, na lista acima, os índices são 0, 1, 2 e 3. Eles são usados para referenciar itens da lista. Por exemplo, precos[1] é 9.99 e precos[3] é 25.49.

As duas listas do programa do experimento anterior têm 8 elementos, e seus índices variam de 0 a 7.

Repetição com for

O bloco for é uma estrutura de repetição. Consiste de uma declaração entre parênteses e um conteúdo (a ser repetido) entre chaves:

for( declaração ) { /* conteúdo a ser repetido */ }

A declaração tem três partes (separadas por ponto e vírgula):

  • O valor inicial de uma variável
  • A condição envolvendo a variável que deve ser verdadeira enquanto o bloco é repetido
  • Uma instrução para mudar o valor da a variável

Blocos de repetição for() são frequentemente usados com listas. Para imprimir no monitor serial cada um dos itens da lista precos (de 4 elementos) acima, pode-se usar o seguinte for():

for(int i = 0; i < 4; i = i + 1) {
    Serial.println( precos[i] );
}

A cada passada no for(), o valor de i será diferente. Iniciará em zero, e será 1, depois 2, depois 3. Na próxima passada i é 4, a condição se tornará falsa, e o bloco será encerrado.

No código do experimento anterior, o for() incrementa o índice (variável nota) usando nota++. Isto é o mesmo que fazer:

nota = nota + 1;

O for() executa apenas uma vez a primeira parte da declaração (inicialização da variável que controla a repetição), mas a cada repetição a condição da segunda parte é testada, o bloco entre chaves é executado, e por fim a expressão da terceira parte é executada, nesta ordem.

É importante que o valor da variável mude de forma que faça o for() terminar em algum momento (a menos que você queira um loop infinito – mas para isto você já tem a função loop() que é mais simples). Normalmente as expressões usadas na terceira parte do for() são apenas para incrementar (somar um) ou decrementar (subtrair um) da variável de controle.