dualB / Melody

Melody offers a simple an powerful music text notation called MELO.
MIT License
14 stars 2 forks source link

La version en français est disponible plus bas.

Melody library

This library sets up a simple but complete notation system, called MELO, in order to write melodies (a series of musical notes in a single voice) in order to facilitate the bridge between musical notation and sound generation.

Motivations for the library

On the one hand, musical notation makes it possible to write a series of notes, according to standards known for a long time; letters and symbols are used to name notes, to specify rhythm changes or alterations (sharps, flats), or even repetitions.

On the other hand, the generation of a sound with Arduino (usually using a buzzer), should rather be expressed by the frequency of the sound (in Hz), its duration (in milliseconds) and its loudness (which depends on the material used).

As a musician, writing music for Arduino, in terms of Hz and milliseconds is definitely a drag and weighs down writing, while not taking advantage of the already rich musical notation. The Melody library is the bridge between the two.

The MELO notation

The MELO notation (MELO stands for Melody) is a textual format which allows the simplified writing of a melody of a single voice. This notation is notably inspired by standard musical notation.

Examples

C scale

" c d e f g a b c* "

Wolfgang Amadeus Mozart, Eine kleine Nachtmusik (KV 525):

"g<<r-d- | g<< r-d-(g<dg<b)-d<*r | c*<<r-a-c*<<r-a- |(c*<af#<a)-d<r | (gr)- g. (bag | (gag)/3:1 f#)- f#. (ac*f# | ag)- g.  (bag | (gag)/3:1 f#)- f#. (ac*f#)- | ((grgr)-- (gf#ef#)--)>> ((grgr)-- (baga)--)> | (brbr)-- (d*c*bc*)-- d*< r | ((de)+  | (d-c.)-c (c-b_.)-  b_ | (( b-a.)- a (gf#ef# | (grarbr)>)- r )_)> "

Au Clair de la Lune (french song)

" (cccde+d+  ceddc++)x2  |  dddd (aa)+_ dc(b a g++)_  |  cccde+d+ ceddc++ "

MELO notation rules

A sequence

A sequence is the basic form of the MELO notation. It is made up of a series of notes or groups of notes, followed by a modifier. You can space these series of notes or groups apart or not. Each modifier will modify the note or the group of notes that precede it.

Sequence

Examples

Note

A note is represented by a letter (uppercase or lowercase), according to the standard nomenclature C (do), D (ré), E (mi) F (fa), G (sol), A (la), B (si) . We also add the letter R (rest) for the rests. By default, the notes are those of the 4th octave, where the note A is of frequency 440 Hz; they all have a relative duration of 1, corresponding to a 'quarter note' in standard musical notation.

Note

Examples

Group

A group makes it easy to apply a modifier to an entire sequence at once, in order to lighten the writing. A group is delimited by a pair of parentheses.

Group

Examples

Modifier

A modifier is a sequence of alterations that allows you to modify the pitch, duration, number of repetitions or loudness of a note or group of notes. You can specify several modifiers in a row; they have the properties of being commutative, that is to say that the order in which they are written will not matter.

Modifier

Examples

Pitch

A pitch modifier lets you raise or lower a note by a semitone or an octave.

Pitch

Duration

A duration modifier allows you to multiply the duration of a note by a simple factor (2, 1/2 or 3/2); it also allows you to specify more precisely any type of time weighting (for example, for triplets). By default, all notes have a duration of 1.

Duration

Repetition

A repeat modifier allows you to specify a number of consecutive repetitions for the note or group of notes concerned.

Repetition

Loudness

A sound intensity modifier allows you to increase or decrease the strength of the sound. A note has, by default, an intensity of 0; we can therefore go there with positive or negative values. Since the actual loudness depends on the hardware used, it is suggested to use a relative scale ranging from -3 (really really soft, or ppp) to +3 (really really loud, or fff).

Loudness

Integer

An integer must be strictly positive (cannot be zero).

Integer

Space

Allowed spaces include characters commonly used as spacers; we add the vertical bar '|', because it is often used in musical notation to identify measures and facilitate reading for the user. These spaces are optional and are ignored when decoding the text.

Space

-" | cdec | cdec | " is equivalent to "cdeccdec"

Use

Note : It is suggested to use the library Musician with Melody to make it easier to play sound with your hardware.

#include <Melody.h>

#define PIN_BUZZER 12

Melody melody("c d e f g a c*");

void setup() {
   melody.setTempo(120);               //May be changed whenever you want
}

void loop() {

  melody.restart();

  while(melody.hasNext()){
    melody.next();

    unsigned int freq = melody.getFrequency(); 
    unsigned long duration = melody.getDuration();
    int loudness = melody.getLoudness();

    freq > 0 ? tone(PIN_BUZZER,freq,duration) : noTone();

    // loudness could be use with a mapping, according to your buzzer or sound-producing hardware
    //For Example :
    /*
      { 
        int realIntensity = map(loudness, -4, 4, 0, 1023);
        myBuzzer.setIntensity(realIntensity);
      }

    */

  }
}

Constructors and methods

Melody(const char* score);
Melody(String score);
Melody(const char* score, unsigned int tempo);
Melody(String score, unsigned int tempo);

The constructor must receive the melody score (const char* or String), which is a text string formatted according to the MELO notation. You can specify the score and tempo right from the start, if needed.

Error handling

If your MELO text string contains an invalid caracter or an invalid syntax, the Melody object will simply return the portion of the melody before the error was incountered.

For example:

Melody melody(" c d e f g k b");

Because of the invalid caracter 'k', this melody will be equivalent to:

Melody melody(" c d e f g ");

void setScore(const char * score)
void setScore(String score)

This method allows you to modify the score used, which is a text string formatted according to the MELO notation.


void setTempo(unsigned int tempo)
int getTempo()

This method allows you to read and modify the tempo of the music, which corresponds to the number of musical beats during one minute. For example, a tempo of 120 means that there will be 120 1-beat notes played for 1 minute - or each note will be 0.5 seconds long.


void restart()

This method allows you to restart melody iteration.


int length()

This method returns the total number of notes in the melody.


bool hasNext()

This method lets you know if there is a next note to play in the melody. It must be used with the next()) method;


void next()

This method skips to the next note of the melody.


int index()

This method returns the index of the current note, in zero base.


 unsigned int getFrequency()

This method returns the value in Hz of the current note. If the value is zero, it means it is silence.


 unsigned long getDuration()

This method returns the duration of the current note, in milliseconds.


 int getLoudness()

This method returns the relative loudness of the current note.


Librairie Melody

Cette librairie instaure un système de notation simple mais complet, appelé MELO, afin d'écrire des mélodies (suite de notes musicales à une seule voix) dans le but de faciliter le pont entre la notation musicale et la génération du son.

Raison d'être de la librairie

D'un côté, la notation musicale permet d'écrire une suite de notes, selon des standards connus depuis longtemps; on y utilise des lettres et des symboles afin de nommer les notes, spécifier des changements de rythmes ou des altérations (dièses, bémols), ou même des répétitions.

De l'autre côté, la génération d'un son avec Arduino (habituellement à l'aide d'un buzzer), doit plutôt être exprimé par la fréquence du son (en Hz), sa durée (en millisecondes) et son intensité sonore (qui dépend du matériel utilisé).

En tant que musicien, écrire de la musique pour Arduino, en terme de Hz et de millisecondes est définitivement un frein et alourdit l'écriture, tout en ne mettant pas à profit la notation musicale déjà très riche. La librairie Melody vient faire le pont entre les deux.

La notation MELO

La notation MELO (pour Melody) est un format textuel qui permet l'écriture simplifié d'une mélodie à une voix. Cette notation s'inspire notamment de la notation musicale standard.

Exemple :

Gamme de do

" c d e f g a b c* "

Wolfgang Amadeus Mozart, Eine kleine Nachtmusik (KV 525):

"g<<r-d- | g<< r-d-(g<dg<b)-d<*r | c*<<r-a-c*<<r-a- |(c*<af#<a)-d<r | (gr)- g. (bag | (gag)/3:1 f#)- f#. (ac*f# | ag)- g.  (bag | (gag)/3:1 f#)- f#. (ac*f#)- | ((grgr)-- (gf#ef#)--)>> ((grgr)-- (baga)--)> | (brbr)-- (d*c*bc*)-- d*< r | ((de)+  | (d-c.)-c (c-b_.)-  b_ | (( b-a.)- a (gf#ef# | (grarbr)>)- r )_)> "

Au Clair de la Lune

" (cccde+d+  ceddc++)x2  |  dddd (aa)+_ dc(b a g++)_  |  cccde+d+ ceddc++ "

Mélodie

La mélodie est la forme de base de la notation. Elle est formée d'une suite de note ou de groupe de notes, suivi de modificateur. On peut espacer ou non ces suites de notes ou groupes. Chaque modifieur viendra modifier la note ou le groupe de notes qui le précède.

Mélodie

Exemples

Note

Une note est représenté par une lettre (majuscule ou minuscule), selon la nomenclature standard C (do), D (ré), E (mi) F (fa), G (sol), A (la), B (si). On ajoute également la lettre R (rest) pour les silences. Par défaut, les notes sont celles de la 4e octave, où la note LA est de fréquence 440 Hz; elles ont toutes une durée relative de 1, correspondant à une noire en notation musicale standard.

Note

Exemples

Group

Un groupe permet facilement d'appliquer un modifieur à toute une mélodie d'un seul coup, afin d'alléger l'écriture. Un groupe est délimité par une paire de parenthèses.

Group

Exemples

Modifier

Un modifieur est une suite d'altération qui permet de modifier la hauteur, la durée, le nombre de répétition ou l'intensité sonore d'une note ou d'un groupe de notes. On peut spécifier plusieurs modifieurs de suite; ils ont la propriétés d'être commutatif, c'est-à-dire que l'ordre dans lequel on les écrit n'aura pas d'importance.

Modifier

Exemples

Pitch

Un modifieur de hauteur du son permet d'augmenter ou d'abaisser une note d'un demi-ton ou d'un octave.

Pitch

Duration

Un modifieur de durée permet de multiplier la durée d'une note par un facteur simple (2, 1/2 ou 3/2); il permet aussi de spécifier plus précisément n'importe quel type de pondération du temps (par exemple, pour les triolets). Par défaut, les notes ont toutes une durée de 1.

Duration

Repetition

Un modifieur de répétition permet de spécifier un nombre de répétition consécutifs pour la note ou le groupe de notes concernés.

Repetition

Loudness

Un modifieur d'intensité sonore permet d'augmenter ou de diminuer la force du son. Une note a, par défaut, une intensité de 0; on peut donc y aller avec des valeurs positives ou négatives. Puisque l'intensité sonore réelle dépend du matériel utilisé, il est suggéré d'utiliser une échelle relative allant de -3 (ppp) à +3 (fff).

Loudness

Integer

Un nombre entier doit être strictement positif (ne peut pas valoir zéro).

Integer

Space

Les espaces permis incluent les caractères couramment utilisé comme espaceur; on y ajoute la barre verticale '|', car elle est souvent utilisée en notation musicale pour repérer les mesures et faciliter la lecture pour l'utilisateur. Ces espaces sont facultatifs et sont ignorés lors du décodage du texte.

Space

-" | cdec | cdec | " est équivalent à "cdeccdec"

Utilisation

Note : Il est fortement suggéré d'utiliser la librairie Musician avec Melody pour faciliter le production du son avec votre périphérique.

#include <Melody.h>

#define PIN_BUZZER 12

Melody melody("c d e f g a c*");

void setup() {
  melody.setTempo(120);               //May be changed whenever you want
}

void loop() {

  melody.restart();

  while(melody.hasNext()){
    melody.next();

    unsigned int freq = melody.getFrequency(); 
    unsigned long duration = melody.getDuration();
    int loudness = melody.getLoudness();

    freq > 0 ? tone(PIN_BUZZER,freq,duration) : noTone();

    // loudness could be use with a mapping, according to your buzzer or sound-producing hardware
    //For Example :
    /*
      { 
        int realIntensity = map(loudness, -4, 4, 0, 1023);
        myBuzzer.setIntensity(realIntensity);
      }

    */

  }
}

Constructeurs

Melody(const char* score);
Melody(String score);
Melody(const char* score, unsigned int tempo);
Melody(String score, unsigned int tempo);

Le constructeur doit recevoir le score du Melody (const char* ou String), soit une chaîne de texte formaté selon la notation MELO. On peut spécifier dès le départ le score et le tempo, au besoin.

Gestion des erreurs

Si votre chaîne de texte en notation MELO contient un caractère invalide ou une syntaxe invalide, l'objet Melody créé contiendra seulement la portion de la mélodie avant que l'erreur soit détectée.

Par exemple:

Melody melody(" c d e f g k b");

En raison de la lettre inconnue 'k', cette mélodie sera équivalente à :

Melody melody(" c d e f g ");

void setScore(const char * score)
void setScore(String score)

Cette méthode permet de modifier le score du Melody, soit une chaîne de texte formaté selon la notation MELO.


void setTempo(unsigned int tempo)
int getTempo()

Cette méthode permet de lire et modifier le tempo de la musique, qui correspond au nombre de temps musicaux durant une minute. Par exemple, un tempo de 120 signifie qu'il y aura 120 notes de 1 temps joué durant 1 minute - ou que chaque note durera 0.5 seconde.


void restart()

Cette méthode permet de redémarrer l'itération de la mélodie.


int length()

Cette méthode retourne le nombre total de notes dans la mélodie.


bool hasNext()

Cette méthode permet de savoir s'il y a une prochaine note à lire dans la mélodie. On doit l'utiliser avec la méthode next();


void next()

Cette méthode permet de passer à la prochaine note de la mélodie.


int index()

Cette méthode retourne l'index de la note courante, en base zéro.


 unsigned int getFrequency()

Cette méthode retourne la valeur en Hz de la note courante. Si la valeur est de zéro, cela signifie qu'il s'agit d'un silence.


 unsigned long getDuration()

Cette méthode retourne la durée de la note courante, en millisecondes.


 int getLoudness()

Cette méthode retourne l'intensité sonore relative de la note courante.