|
#include <ctype.h> |
|
|
|
/* const short tones[] PROGMEM = { */ |
|
|
|
static const short tones[] = { |
|
/* C C#/D! D D#/E! E F F#/G! G G#/A! A A#/B! B */ |
|
27, 29, 30, /* Octave 0 */ |
|
32, 34, 36, 38, 41, 43, 46, 48, 51, 55, 58, 61, /* Octave 1 */ |
|
65, 69, 73, 77, 82, 87, 92, 97, 103, 110, 116, 123, /* Octave 2 */ |
|
130, 138, 146, 155, 164, 174, 184, 195, 207, 220, 233, 246, /* Octave 3 */ |
|
261, 277, 293, 311, 329, 349, 369, 391, 415, 440, 466, 493, /* Octave 4 */ |
|
523, 554, 587, 622, 659, 698, 739, 783, 830, 880, 932, 987, /* Octave 5 */ |
|
1046, 1108, 1174, 1244, 1318, 1396, 1479, 1567, 1661, 1760, 1864, 1975, /* Octave 6 */ |
|
2093, 2217, 2349, 2489, 2637, 2793, 2959, 3135, 3322, 3520, 3729, 3951, /* Octave 7 */ |
|
4186, |
|
}; |
|
|
|
/* max nested repeats */ |
|
#define NREPEATS 4 |
|
|
|
struct REPEAT { |
|
const char *save; |
|
char count; |
|
}; |
|
|
|
struct MUSIC { |
|
short note; |
|
short octave; |
|
short ms; |
|
short qms; |
|
short beat; |
|
short basic_note; |
|
const char *save; |
|
|
|
struct REPEAT repeats[NREPEATS]; |
|
unsigned char rc; |
|
}; |
|
|
|
/* |
|
s sixteenth |
|
i eighth |
|
q quarter |
|
h half |
|
w whole |
|
. time and a half |
|
= whole rest |
|
- half rest |
|
; quarter rest |
|
: eighth rest |
|
*/ |
|
|
|
char parse(const char *in, struct MUSIC *M) { |
|
short offset, time, octave; |
|
|
|
const short pause = 32; |
|
|
|
if(in) { |
|
M->octave = 4; |
|
M->beat = 256; |
|
M->basic_note = M->beat; /* Basic note is a quarter by default */ |
|
M->rc = 0; |
|
} else { |
|
in = M->save; |
|
} |
|
|
|
change_note: |
|
time = M->basic_note; |
|
|
|
start: |
|
|
|
if(!*in) { |
|
/* no more music to play */ |
|
return 0; |
|
} |
|
|
|
switch(*in++) { |
|
case 'C' : offset = 0; break; |
|
case 'D' : offset = 2; break; |
|
case 'E' : offset = 4; break; |
|
case 'F' : offset = 5; break; |
|
case 'G' : offset = 7; break; |
|
case 'A' : offset = 9; break; |
|
case 'B' : offset = 11; break; |
|
case '>' : M->octave++; goto start; |
|
case '<' : M->octave--; goto start; |
|
case 's' : time = M->beat >> 2; if(*in == '.') {time += time >> 1; in++;} goto start; |
|
case 'i' : time = M->beat >> 1; if(*in == '.') {time += time >> 1; in++;} goto start; |
|
case 'q' : time = M->beat << 0; if(*in == '.') {time += time >> 1; in++;} goto start; |
|
case 'h' : time = M->beat << 1; if(*in == '.') {time += time >> 1; in++;} goto start; |
|
case 'w' : time = M->beat << 2; if(*in == '.') {time += time >> 1; in++;} goto start; |
|
case 'S' : M->basic_note = M->beat >> 2; if(*in == '.') {M->basic_note += M->basic_note >> 1; in++;} goto change_note; |
|
case 'I' : M->basic_note = M->beat >> 1; if(*in == '.') {M->basic_note += M->basic_note >> 1; in++;} goto change_note; |
|
case 'Q' : M->basic_note = M->beat << 0; if(*in == '.') {M->basic_note += M->basic_note >> 1; in++;} goto change_note; |
|
case 'H' : M->basic_note = M->beat << 1; if(*in == '.') {M->basic_note += M->basic_note >> 1; in++;} goto change_note; |
|
case 'W' : M->basic_note = M->beat << 2; if(*in == '.') {M->basic_note += M->basic_note >> 1; in++;} goto change_note; |
|
case ':' : time = M->beat >> 1; goto rest; |
|
case ';' : time = M->beat << 0; goto rest; |
|
case '-' : time = M->beat << 1; goto rest; |
|
case '=' : time = M->beat << 2; goto rest; |
|
case '|' : goto start; |
|
case '[' : |
|
if(M->rc < NREPEATS) { |
|
struct REPEAT *rp = &M->repeats[M->rc]; |
|
rp->save = in; |
|
rp->count = 1; |
|
M->rc++; |
|
} |
|
goto start; |
|
case ']' : |
|
if(isdigit(*in) && M->rc > 0) { |
|
char reps = *in - '0'; |
|
struct REPEAT *rp = &M->repeats[M->rc - 1]; |
|
if(rp->count < reps) { |
|
rp->count++; |
|
in = rp->save; |
|
} else { |
|
M->rc--; |
|
} |
|
} |
|
goto start; |
|
default: goto start; |
|
} |
|
|
|
/* I'm just going to assume my users know |
|
better than to try B# or C! */ |
|
if(*in == '#') { |
|
offset++; |
|
in++; |
|
} else if(*in == '!' || *in == '_') { |
|
offset--; |
|
in++; |
|
} |
|
|
|
octave = M->octave; |
|
if(*in >= '1' && *in <= '7') { |
|
octave = *in - '0'; |
|
in++; |
|
} |
|
|
|
offset = offset + ((octave - 1) * 12) + 3; |
|
if(offset < 0) offset = 0; |
|
if(offset >= 88) offset = 87; |
|
|
|
/* |
|
#if defined (ARDUINO_AVR_UNO) |
|
M->note = pgm_read_word_near(tones + offset); |
|
#else |
|
M->note = tones[offset]; |
|
#endif |
|
*/ |
|
M->note = tones[offset]; |
|
|
|
if(*in == '^') { |
|
M->ms = time; |
|
M->qms = 0; |
|
in++; |
|
} else { |
|
M->ms = time - pause; |
|
M->qms = pause; |
|
} |
|
|
|
M->save = in; |
|
|
|
return 1; |
|
|
|
rest: |
|
if(*in == '.') { |
|
time += time >> 1; |
|
in++; |
|
} |
|
M->note = 0; |
|
M->ms = time; |
|
M->save = in; |
|
return 1; |
|
} |
|
|
|
static const char *melody = /*, */ |
|
|
|
#if 0 |
|
"H CCGGAAG;FFEEDDC;GGFFEED;GGFFEED;CCGGAAG;FFEEDDC" |
|
#elif 0 |
|
/* https://wikisource.org/wiki/Prelude,_Cello_Suite_No._1_in_G_major,_BWV_1007 */ |
|
/* Sharpen F */ |
|
/* 1 */ "[G2^D3B3A3 B3^D3^B3^D3]2 | G2^E3C4B3 C4^E3^C4E3 G2^E3C4B3 C4^E3^C4^E3" |
|
/* 3 */ "[G2^F#3C4B3 C4^F#3^C4^F#3]2 | G2^G3B3A3 B3^G3^B3^G3 G2^G3B3A3 B3^G3^B3^F#3" |
|
/* 5 */ "G2^E3B3A3 B3^G3^F#3^G3 E3^G3^F#3^G3 B2^D3^C#3^B2 | C#3^G3A3G3 A3^G3^A3^G3 C#3G3A3G3 A3^G3^A3^G3" |
|
/* 7 */ "F3^A3D4C#4 D4^A3^G3^A3 F#3^A3^G3^A3 D3^F#3^E3^D3 | [C2^B2G3F#3 G3^B2^G3^B2]2" |
|
/* 9 */ "E2^C#3^D3^E3 D3^C#3^B3^A3 G3^F#3^E3D4 C#4B3A3G3 | F3^E3^D3^D4 A3D4F#3A3 D3E3F#3A3 G3F#3E3D3" |
|
/* 11 */ "G#3D3^F3^E3 F3D3G#3D3 B3D3F3E3 F3D3G#3F3 | C3^E3^A3^B3 C4^A3^E3^D3 C3^E3^A3^B3 C4^A3^F#3^E3" |
|
/* 13 */ "D#3^F#3D#3F#3 A3^F#3^A3^F#3 D#3^F#3D#3F#3 A3^F#3^A3^F#3 | G3^F#3^E3G3 F#3G3A3F#3 G3F#3E3D3 C3B3A3G2" |
|
/* 15 */ "G2^C3E3D3 E3^C3^E3^C3 G2^C3D3C3 D3^C3^D3^C3 | G2^B2F3E3 F3^B2^F3^B2 G2^B2F3E3 F3^B2^F3^B2" |
|
/* 17 */ "G2^C3E3D3 E3^C3^E3^C3 G2^C3E3D3 E3^C3^G3^C3 | G2^F#3C4B3 C4^F#3^C4^F#3 G2^F#3C4B3 C4^F#3^C4F#3" |
|
/* 19 */ "G2^D3B3A3 B3^G3^F#3^E3 D3C3B2A2 G2^F2^E2^D2 | C#2^A2E3F#3 G3^E3^F#3^G3 C#2^A2E3F#3 G3^E3^F#3^G3" |
|
#elif 0 |
|
/* Man who sold the world */ |
|
"[AAAG^GiAi^B!^AG]6 wA^ wA" |
|
/*"[F;FF^FFFF]2 wD^wD [A;AA^AAAA]2 [D;DD^DDDD]2 [A;AA^AAAA]2 [F;FF^FFFF]2 [C;CC^CCCC]2 [A;AA^AAAA]2" */ |
|
#elif 1 |
|
/* Game of Thrones */ |
|
/* https://musescore.com/user/158751/scores/2163051 */ |
|
/* flatten ABE */ |
|
"[GCiE!iF]4 [GCiEiF]4 " |
|
"h.G h.C iCiFhG hCiE!iF B!3G3iB!3iC DG3iB!3iC B!3G3iB!3iC DG3B!3 iCiFhG" |
|
"h.F h.B!3 iB!3iE!hF hB!3iE!iD A!3F3iA!3iB!3 CF3iA!3iB!3 CF3A!3" |
|
"h.G h.C iCiFhG h.CiE!iF B!3G3iB!3iC DG3iB!3iC B!3G3iB!3iC DG3B!3" |
|
"h.F h.B!3 iB!3iDhF hB!3iE!iD A!3G3iA!3iB!3 CG3iA!3iB!3 A!3G3iA!3iB!3 CG3C" |
|
"h.G h.C iC^iF^hG hC^iE!^iF hD^iB!3^iC hD^iB!3^iC" |
|
"hD^iB!3^iC DB!3D h.F h.B!3 hDE! h.D" |
|
"[C^G3iA!3^B!3]3 " |
|
/*"F3F3F3" */ |
|
"hB!3" /*"B!3^A!3^G3" */ |
|
/* @57 "C5^EiA!^iB! C5^EiA!^iC B!^EiG^iA!" */ |
|
#elif 0 |
|
/* Cantina song */ |
|
"AD5AD5 | iAD5iA^iAiG#A | I AG#AG#^GF#GF | Q q.F iD^D ;" |
|
"AD5AD5 | iAD5iA^iAiG#A | GG:F#G | iC5B!iAiA^AG: |" |
|
"AD5AD5 | iAD5iA^iAiG#A | C5C5:iAG | q.FeD^D; |" |
|
"q.B!3iB!3q.DiD | q.FiFq.AiA | E!5D5iG#AiF^hF- |" |
|
"> : AiFA; | : AiFA; | : AiFiF#AiF^q.FiD^D; " |
|
" : AiFA; | : AiFA; | : AiFiG#A iG^q.GiC^q.C4iG4^G4; " |
|
" : AiFA; | : AiFA; | : AiFiG#A iF^q.FiD^D; " |
|
" iB!4iDF iB!4iDF | iG#A iD^D; | < I DFB!D5G# qA F^ Q hF-| " |
|
#elif 0 |
|
/* Imperial march (?) */ |
|
"HAAAq.FqC5Aq.FqC5h.A- E5E5E5q.F5qC5G#q.FqC5h.A-" |
|
#elif 1 |
|
/* Indiana Jones */ |
|
"iEiF^q.GhC5iDiEh.FiGi^Aq.BhF5iAiBq.C5q.D5q.E5" |
|
#else |
|
/* Popcorn song */ |
|
"Q DCDA3F3A3D3: DCDA3F3A3D3: DEFE FDED ECDA3F3A3D:" |
|
#endif |
|
; |
|
|
|
#include <stdio.h> |
|
#include <conio.h> |
|
#include <dos.h> |
|
|
|
int main() { |
|
|
|
struct MUSIC M; |
|
|
|
char c = parse(melody, &M); |
|
|
|
printf("Playing...\n"); |
|
|
|
while(c) { |
|
if(kbhit()) { |
|
if(getch() == 27) |
|
goto end; |
|
} |
|
|
|
if(M.note) { |
|
sound(M.note); |
|
} else { |
|
nosound(); |
|
} |
|
delay(M.ms); |
|
|
|
if(M.qms) { |
|
nosound(); |
|
delay(M.qms); |
|
} |
|
c = parse(NULL, &M); |
|
} |
|
|
|
printf("Done.\n"); |
|
|
|
end: |
|
nosound(); |
|
|
|
return 0; |
|
} |