Robotica educativa/Musica maestro
Ora che si sono sperimentate le programmazioni di base la sfida è cimentarsi con un algoritmo più complesso.
Qui si fa musica
Lo scopo di questo paragrafo è far eseguire una musica ad Arduino una sola volta. Per questo motivo il codice verrà inserito nel setup.
L'obiettivo è fornire un ambiente in cui scrivere musica con una sintassi il più possibile vicina a quella di un musicista. Partiamo da un esempio molto semplice:
<score vorbis="1"> \relative c' {
\set Staff.midiInstrument = #"clarinet"
\tempo "Allegro" 4 = 140
\key f \major
\time 3/4
\partial 4 c8. c16 | d4 c f | e2 c8. c16 | d4 c g' | f2 c8. c16 | c'4 a f | e( d\fermata) bes'8. bes16 | a4 f g | f2. \bar "|."
}
\addlyrics { Hap -- py birth -- day to you, Hap -- py birth -- day to you, Hap -- py birth -- day dear [Name], __ Hap -- py birth -- day to you.
} </score>
Per riprodurre Tanti auguri a te (o qualsiasi altra musica di proprio gusto), saranno necessari tre ingredienti base:
- l'elenco di tutte le note musicali con le rispettive frequenze;
- la musica da riprodurre, che dovrà contenere note e loro durata;
- il programma vero e proprio che eseguirà la canzone.
E, naturalmente, il circuito. Partiamo da quest'ultimo.
Circuito elettrico
Il circuito è molto più semplice di quel che si possa immaginare. Oltre ad Arduino, utilizza una cicalina piezoelettrica che può essere equipaggiata nel pin 11 e GND.

Programma principale
Come solito, iniziamo dalla fine, ovvero diamo per scontato di avere note e melodia. In questo caso partiamo dal difficile: la sua riproduzione.
/***********************************************************************
* Connetti un buzzer o un altoparlante al pin 11 o scegline un altro. *
* Cambia la linea 7 per inserire una melodia differente *
***********************************************************************/
#include "pitches.h" // file contenente le note musicali
#include "happy-birthday.h" // file contenente la melodia
// pin dove si colleghera' la cicalina piezoelettrica
int buzzer = 11;
// sizeof restituisce il nunero di byte, ogni elemento può occuparne piu' di
// uno. Utilizzando due volori per nota (frequenza e durata), e' necessario
// dividere per 2 per ottenere il numero di note della melodia.
int notes = sizeof(melody) / sizeof(melody[0]) / 2;
// calcola la durata di un intero (4/4) in millisecondi
int wholenote = (60000 * 4) / tempo;
int divider = 0, noteDuration = 0;
void setup() {
// scorre le note della melodia.
// Ricorda, l'array ha due numeri gemelli (note e durate)
for (int thisNote = 0; thisNote < notes * 2; thisNote += 2) {
// calcola la durata di ciascuna nota
divider = melody[thisNote + 1];
noteDuration = (wholenote) / abs(divider);
// le note puntate hanno durata negativa ;-)
if (divider < 0)
noteDuration *= 1.5; // nel caso incrementa la loro durata del 50%
// una nota viene eseguita per il 90% del tempo, lasciando una pausa del 10%
tone(buzzer, melody[thisNote], noteDuration * 0.9);
// attende per l'intera durata della nota (100%).
delay(noteDuration);
}
// interrompe il generatore di toni.
noTone(buzzer);
}
void loop() {
// Non e' necessario ripetere la melodia.
// Il codice vero e proprio verra' inserito qui.
}
Inizialmente viene calcolato il numero di note presenti nella nella melodia tramite la formula (linea 15):
perché sizeof restituisce la dimensione in byte dell'array. Per il formato int alcuni modelli di Arduino usano 2 byte, altri 4 byte. Pertanto, per ottenere l'esatto numero di note si divide per il numero di byte occupato da un parametro (il primo) e si divide ulteriormente per 2 (nota e sua durata).
A questo punto si calcola la durata (in millisecondi) di una semibreve, ricordando che la variabile tempo – in musica – esprime la durata di una semiminima (linea 18):
Restano due cose da sottolineare:
- il ciclo
forviene incrementato a passi di 2, per eseguire l'intera melodia; - la durata della singola nota è pari a
(wholenote) / abs(divider), dove il divisore è la sua durata; - nel caso il divisore sia negativo si aggiunge un 50% alla sua durata con
noteDuration *= 1.5; - la nota viene quindi eseguita per il 90% del tempo;
- il restante 10% serve a non far percepire come un unico suono note identiche e consecutive.
Melodia
In un file separato che chiameremo melody.h (così da poter gestire tutte le melodie che si desiderano) verrà inserita la melodia da eseguire. Le regole sono semplici:
- prima le note, nella notazione anglosassone seguite dalla loro ottava. Il do centrale è alla quarta ottava;
- poi la durata. 1 è un intero, 2 una minima, 4 una semiminima, 8 una croma, 16 una semibiscroma e così via. Ecco perché nella riga 29 questo numero va a denominatore.
Riguardo la durata, questa viene espressa come nella notazione musicale dove i valori ammessi sono:
| Nota | Durata | Valore | Simbolo |
|---|---|---|---|
| semibreve | 1 | ||
| minima | 2 | ||
| semiminima | 4 | ||
| croma | 8 | ||
| semicroma | 16 | ||
| biscroma | 32 | ||
| semibiscroma | 64 |
Come anticipato, se si devono inserire punti di valore (per aumentare la durata della nota della sua metà) è sufficiente inserire il suo valore ma negativo.
Di seguito il codice happy-birthday.h, da inserire nella stessa cartella del codice principale:
/******************************************************************************
* Note della melodia seguite dalla loro durata. *
* Un 4 significa un quarto, 8 un ottavo, 16 un sedicesimo e cosi' via *
* I numeri negativi vengono utilizzati per definire note puntate, pertanto *
* -4 significa un quarto puntato, equivalente a un quarto piu' un ottavo *
******************************************************************************/
// Qui vengono definite le battute al minuto della melodia
int tempo = 140;
// Questa e' la melodia.
int melody[] = {
// Happy Birthday
NOTE_C4, -8, NOTE_C4, 16, //1
NOTE_D4, 4, NOTE_C4, 4, NOTE_F4, 4, //2
NOTE_E4, 2, NOTE_C4, -8, NOTE_C4, 16, //3
NOTE_D4, 4, NOTE_C4, 4, NOTE_G4, 4, //4
NOTE_F4, 2, NOTE_C4, -8, NOTE_C4, 16, //5
NOTE_C5, 4, NOTE_A4, 4, NOTE_F4, 4, //6
NOTE_E4, 4, NOTE_D4, 4, NOTE_AS4, -8, NOTE_AS4, 16, //7
NOTE_A4, 4, NOTE_F4, 4, NOTE_G4, 4, //8
NOTE_F4, -2 //9
};
Come si vede gli oggetti definiti sono due: tempo, una variabile intera, già utilizzata; e melody[]. Le parentesi quadre indicano ché è un vettore contenente un numero non definito di elementi.
I commenti servono solo per semplificare la lettura (dividono la melodia in battute).
Le note musicali
Infine vengono definite le note musicali con le loro frequenze, nel file pitches.h. Le note non utilizzate non occuperanno memoria, quelle utilizzate verranno inserite in forma numerica nel vettore melody[].
/*************************************************
* Note musicali
*************************************************/
#define NOTE_B0 31
#define NOTE_C1 33
#define NOTE_CS1 35
#define NOTE_D1 37
#define NOTE_DS1 39
#define NOTE_E1 41
#define NOTE_F1 44
#define NOTE_FS1 46
#define NOTE_G1 49
#define NOTE_GS1 52
#define NOTE_A1 55
#define NOTE_AS1 58
#define NOTE_B1 62
#define NOTE_C2 65
#define NOTE_CS2 69
#define NOTE_D2 73
#define NOTE_DS2 78
#define NOTE_E2 82
#define NOTE_F2 87
#define NOTE_FS2 93
#define NOTE_G2 98
#define NOTE_GS2 104
#define NOTE_A2 110
#define NOTE_AS2 117
#define NOTE_B2 123
#define NOTE_C3 131
#define NOTE_CS3 139
#define NOTE_D3 147
#define NOTE_DS3 156
#define NOTE_E3 165
#define NOTE_F3 175
#define NOTE_FS3 185
#define NOTE_G3 196
#define NOTE_GS3 208
#define NOTE_A3 220
#define NOTE_AS3 233
#define NOTE_B3 247
#define NOTE_C4 262
#define NOTE_CS4 277
#define NOTE_D4 294
#define NOTE_DS4 311
#define NOTE_E4 330
#define NOTE_F4 349
#define NOTE_FS4 370
#define NOTE_G4 392
#define NOTE_GS4 415
#define NOTE_A4 440
#define NOTE_AS4 466
#define NOTE_B4 494
#define NOTE_C5 523
#define NOTE_CS5 554
#define NOTE_D5 587
#define NOTE_DS5 622
#define NOTE_E5 659
#define NOTE_F5 698
#define NOTE_FS5 740
#define NOTE_G5 784
#define NOTE_GS5 831
#define NOTE_A5 880
#define NOTE_AS5 932
#define NOTE_B5 988
#define NOTE_C6 1047
#define NOTE_CS6 1109
#define NOTE_D6 1175
#define NOTE_DS6 1245
#define NOTE_E6 1319
#define NOTE_F6 1397
#define NOTE_FS6 1480
#define NOTE_G6 1568
#define NOTE_GS6 1661
#define NOTE_A6 1760
#define NOTE_AS6 1865
#define NOTE_B6 1976
#define NOTE_C7 2093
#define NOTE_CS7 2217
#define NOTE_D7 2349
#define NOTE_DS7 2489
#define NOTE_E7 2637
#define NOTE_F7 2794
#define NOTE_FS7 2960
#define NOTE_G7 3136
#define NOTE_GS7 3322
#define NOTE_A7 3520
#define NOTE_AS7 3729
#define NOTE_B7 3951
#define NOTE_C8 4186
#define NOTE_CS8 4435
#define NOTE_D8 4699
#define NOTE_DS8 4978
#define REST 0
Le note sono definiti in base al loro nome, alla loro eventuale alterazione (s sta per sharp, in italiano diesis) e alla loro ottava.
Espansioni suggerite
- Modificare il codice in modo tale che Arduino esegua diverse melodie alla pressione di diversi pulsanti. Questo passaggio è semplice: più impegnativo è far sì che la pressione del pulsante abbia effetto anche durante l'esecuzione della musica.
- Naturalmente Arduino ha un'uscita con potenza sufficiente per essere riprodotto anche nelle cuffie (con una miglior qualità di esecuzione). Se proprio si vuole esagerare è possibile utilizzare le casse del proprio computer: sono pensate per ricevere un segnale molto debole. Il risultato sarà di sicuro effetto e di qualità notevolmente migliore.
- Il codice può essere modificato profondamente se si ricorda che a ogni ottava la frequenza delle note musicali raddoppia, il la centrale ha una frequenza pari a , e le note – in un'ottava – sono 12 (sette note e cinque alterazioni), pertanto lo scostamento di frequenza tra una nota e la successiva (o la precedente) è pari a . Per un intervallo di un numero qualsiasi di semitoni è sufficiente elevare questa costante al numero di semitoni e si otterranno tutte le note musicali. Per gli amanti degli esponenziali.
- Più complesso: adattare uno xilofono per bambini e far suonare – tramite motori – una serie di melodie prestabilite. Potrebbe diventare un bellissimo regalo per chi ha un bimbo piccolo.