Abstract
In this post we are going to present a more sophisticated program for playing melodies
on your Arduino. It allows you to encode repetitions and alternate endings, compacting
your longer melodies.
Keywords: tone music; pauses; augmentations; ties; repetitions.
The new playing routine
The new play function implements more sophisticated management of melodies allowing
pauses, ties, augmentations (like the past routine) and it also introduces the possibility
to use repetitions and alternate endings. This post explains how to encode all this.
The sketch with the new routine is given below. Remark that it makes use of a support class that implements a simple stack. Source files can found here: Stack.h, Stack.cpp. In order to be able to compile the sketch you should open this two files in other tabs of your Arduino IDE. You also need instructions.h which contains the encoding for the new instructions used to encode the melodies.
The sketch with the new routine is given below. Remark that it makes use of a support class that implements a simple stack. Source files can found here: Stack.h, Stack.cpp. In order to be able to compile the sketch you should open this two files in other tabs of your Arduino IDE. You also need instructions.h which contains the encoding for the new instructions used to encode the melodies.
Compatibility
All the songs encoded for the previous version of the playing routine still work for this
new version without any modification. Indeed, the new melody encoding is an extension of
the older one.
See also
Tone music surprises!
Tone music: how to encode songs
Tone music: changing the Tempo of your melodies
Enjoy!
/* * PlayMelodyAdv * * A more advanced program for playing melodies on Arduino. * * Define the label DEBUG to have some debug support * * Written by Enrico Formenti, 22 dec 2014 * * BSD license, check the License page on the blog for more information * All this text must be included in any redistribution. * * See macduino.blogspot.com for more details. */ #include "pitches.h" #include "durations.h" #include "instructions.h" #include "Stack.h" #include "Stack.cpp" #include "SilentNight.h" #define TONEPIN 8 typedef struct { struct { uint16_t N; // position in melody array uint16_t D; // position in durations array } I; // individual addressing uint32_t A; // addressing all together } POS; void playMusic( uint16_t *instr, uint16_t *durs, uint16_t len) { POS coda; // address of coda symbol POS segno; // address of segno symbol POS invRepeat; // position of inv repeat sign POS nskip; // posistion of the first note to skip POS fine; // end of music symbol position POS askip; // position of the first note to skip in alternate ending POS curPos; // reset all internal variables coda.A = 0; segno.A = 0; invRepeat.A = 0; nskip.A = 0; fine.A = 0; askip.A = 0; curPos.A = 0; // start looping while(curPos.I.N < len) { Stack<POS> reps; // make the stack local so it is emptied when the loop // is over switch(instr[curPos.I.N]) { case NOTE_PAUSE: // noTone(TONEPIN); delay(durs[curPos.I.D]); curPos.A += 0x11; break; case NOTE_CODA: if(nskip.A) { curPos.A = nskip.A; nskip.A = 0; coda.A = 0; break; } coda.A = curPos.A+0x10; // start from next position (curPos.I.N)++; break; case NOTE_DACAPO: if(reps.isEmpty() || (reps.topElement().A!=curPos.A)) { // if this event has not already occurred reps.push(curPos); // add this event curPos.A=0; // repeat from the beginning break; } // otherwise we simply skip and update the instruction pointer reps.pop(); (curPos.I.N)++; break; case NOTE_SEGNO: segno.A=curPos.A+0x10; // start from next position in note array (curPos.I.N)++; break; case NOTE_REPEAT: if(reps.isEmpty() || (reps.topElement().A != curPos.A)) { if(invRepeat.A) { // repeat from last inv repeat sign if any curPos.A=invRepeat.A+0x11; // add +1 to both N and D pointers invRepeat.A=0; reps.push(curPos);// push the event on the stack break; } // otherwise repeat from the beginning reps.push(curPos);// push the event on the stack curPos.A=0; break; } // if this is the sign that issued the event reps.pop(); (curPos.I.N)++; break; case NOTE_INVREPEAT: invRepeat.A=curPos.A+0x10; // start from the next position in notes array (curPos.I.N)++; break; case NOTE_DALSEGNO: if(reps.isEmpty() || (reps.topElement().A!=curPos.A)) { // if this has not generated the event reps.push(curPos); curPos.A=segno.A; segno.A=0; break; } // otherwise skip and update N only reps.pop(); segno.A=0; (curPos.I.N)++; break; case NOTE_DALSEGNOCODA: if(reps.isEmpty() || (reps.topElement().A!=curPos.A)) { reps.push(curPos); curPos.A = segno.A; segno.A = 0; nskip.A = coda.A+0x10; break; } // otherwise skip and update N only reps.pop(); (curPos.I.N)++; break; case NOTE_DACAPOCODA: if(reps.isEmpty() || (reps.topElement().A!=curPos.A)) { reps.push(curPos); curPos.A = 0; segno.A = 0; nskip.A = coda.A+0x10; break; } // otherwise skip and update N only reps.pop(); (curPos.I.N)++; break; case NOTE_FINE: if(fine.A) { // we already passed through the music end symbol curPos.I.N = len; // then stop playing break; } fine.A = curPos.A; (curPos.I.N)++; break; case NOTE_DACAPOFINE: curPos.A = 0; break; case NOTE_DALSEGNOFINE: curPos.A = segno.A; break; case NOTE_ALTBEGIN: if(askip.A) { // we are repeating the sequence, hence skip curPos.A = askip.A; askip.A = 0; break; } // not repeating so just keep playing (curPos.I.N)++; break; case NOTE_ALTEND: askip.A = curPos.A+0x10; // set the skip to the next instruction // so that it will be skipped during the // repetition break; default: // it is a note instruction tone(TONEPIN, instr[curPos.I.N], durs[curPos.I.D]); // to distinguish the notes, set a minimum time between them. // the note's duration + 30% seems to work well: delay(durs[curPos.I.D] * 1.3); // stop the tone playing: noTone(TONEPIN); curPos.A += 0x11; // update both N and D pointers break; } } } void setup() { #ifdef DEBUG Serial.begin(); #endif playMusic((uint16_t *)melody,(uint16_t *)noteDurations,(uint16_t)melody_len); } void loop() { // no need to repeat the melody. }
No comments :
Post a Comment