Robotica educativa/Musica maestro

Indice del libro

Ora che si sono sperimentate le programmazioni di base la sfida è cimentarsi con un algoritmo più complesso.


Qui si fa musica

modifica

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:

 

Per riprodurre Tanti auguri a te (o qualsiasi altra musica di proprio gusto), saranno necessari tre ingredienti base:

  1. l'elenco di tutte le note musicali con le rispettive frequenze;
  2. la musica da riprodurre, che dovrà contenere note e loro durata;
  3. il programma vero e proprio che eseguirà la canzone.

E, naturalmente, il circuito. Partiamo da quest'ultimo.

Circuito elettrico

modifica

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.

 
Circuito elettrico con la cicalina piezoelettrica montata direttamente su Arduino.

Programma principale

modifica

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 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

modifica

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:

Note musicali
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.