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
modificaLo 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:
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
modificaIl 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
modificaCome 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
for
viene 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
modificaIn 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
modifica
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
modifica- 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.