/* * Translates a subset of ABC music notation into sound. * Requires the "beep" program (johnath.com/beep) to be installed. */ %{ typedef struct { double frequency; double length; } note_t; double default_length; void play(double frequency, double length); int yylex(void); void yyerror(const char * msg); %} %union { note_t note; double num; } %type note pitch %type length num digit %% tune : measures ; measures : /* empty */ | notes | notes '|' measures ; notes : group | group notes ; group : note { play($1.frequency, $1.length); } | broken | triplet ; broken : note '>' note { play($1.frequency, 1.5 * $1.length); play($3.frequency, 0.5 * $3.length); } ; triplet : '(' '3' ':' num pitch pitch pitch { play($5.frequency, default_length * $4 / $2); play($6.frequency, default_length * $4 / $2); play($7.frequency, default_length * $4 / $2); } ; note : pitch { $$ = $1; } | pitch length { $$.frequency = $1.frequency; $$.length = $2; } ; pitch : 'C' { $$.frequency = 261.6; $$.length = default_length; } | 'D' { $$.frequency = 293.7; $$.length = default_length; } | 'E' { $$.frequency = 329.6; $$.length = default_length; } | 'F' { $$.frequency = 349.2; $$.length = default_length; } | 'G' { $$.frequency = 392.0; $$.length = default_length; } | 'A' { $$.frequency = 440.0; $$.length = default_length; } | 'B' { $$.frequency = 493.9; $$.length = default_length; } | 'c' { $$.frequency = 523.2; $$.length = default_length; } ; length : '/' { $$ = default_length / 2; } | '/' num { $$ = default_length / $2; } | num { $$ = default_length * $1; } ; num : digit { $$ = $1; } | num digit { $$ = 10 * $1 + $2; } ; digit : '0' { $$ = 0; } | '1' { $$ = 1; } | '2' { $$ = 2; } | '3' { $$ = 3; } | '4' { $$ = 4; } | '5' { $$ = 5; } | '6' { $$ = 6; } | '7' { $$ = 7; } | '8' { $$ = 8; } | '9' { $$ = 9; } ; %% #include #include #include double default_length = 500; const double DUTY_CYCLE = 0.9; void play(double frequency, double length) { char buf[80]; snprintf(buf, 80, "beep -f %f -l %f -D %f", frequency, length * DUTY_CYCLE, length * (1.0 - DUTY_CYCLE)); system(buf); } int yylex(void) { int c; while ((c = getc(stdin)) != EOF) { if (isspace(c)) continue; return c; } return 0; } void yyerror(const char * msg) { fprintf(stderr, "%s\n", msg); } int main(void) { return yyparse(); }