Day 18 – ABC Module

Instead of focusing on a particular feature of Perl 6 today, I’d like to take you on a quick tour of the ABC module. I think it’s a nice example of some of the strengths of Perl 6.

ABC Notation is a simple text file format designed to make it easy to support musical notation. It’s widely used in the world of traditional dance music because it is very lightweight and more than powerful enough to support notating jigs and reels. Here’s an example in honor of the season:

X:1
T:While Shepherds Watched Their Flocks
M:5/4
L:1/4
O:Green's Harbour, Newfoundland
K:A major
E|:[M:5/4] A A/B/ B2 A|B c/B/ A2 A/B/|
[M:6/4]c d/c/ B2 B2|[M:4/4] A3 E|AB AG|
FE FE|AB AG|F2 F2|E2 G2|A/G/ F/E/ DF|
[1 [M:6/4] E C/B,/ A,3 E:|[2 [M:5/4] E C/B,/ A,3|]

I won’t get into the details — here’s a tutorial if you’d like to know more — but the structure of the file is simple. The first section is the header, with general information about the tune. The remainder is the tune itself. This one is a bit more complicated than many because of all the embedded time signature changes, like [M:6/4].

I was always surprised there wasn’t a CPAN module for handling ABC notation, and even seriously considered writing one myself at one point. But parsing ABC is a complicated process, and I gave up in frustration before getting very far.

Enter Perl 6 and its grammars. About 60 lines of simple regexes is all that is needed to parse most of the tunes I am interested in. (Several of the more complicated features of ABC, like lyrics and multi-staff music are not implemented yet.) Here’s a snatch of it:

    regex basenote { <[a..g]+[A..G]> }
    regex octave { "'"+ | ","+ }
    regex accidental { '^' | '^^' | '_' | '__' | '=' }
    regex pitch { <accidental>? <basenote> <octave>? }

Compare that to the model BNF grammar for ABC:

basenote ::= %x43 / %x44 / %x45 / %x46 / %x47 / %x41 / %x42 / %x63 / %x64 / %x65 / %x66 / %x67 / %x61 / %x62 ; CDEFGABcdefgab
octave ::= 1*"'" / 1*"," 
accidental ::= "^" / "^^" / "_" / "__" / "=" 
pitch ::= [accidental] basenote [octave]

It’s clearly a very straightforward translation process.

By default, parsing with a Perl 6 grammar just gives you a basic Match object. Adding an object to specify actions to go with the grammar allows you to easily process the information as it is parsed. A simple example is

    method rest($/) {
        make ABC::Rest.new(~$<rest_type>, 
                           $<note_length>.ast);
    }

Whenever the rest regex fires, it returns a new ABC::Rest object. The constructor is passed the string form of the rest_type regex, and an ABC::Duration object created by the action for note_length.

Speaking of durations, another feature of Perl 6 comes very handy here. Durations are represented exactly using the rational number Rat type. If we didn’t have them available, we’d have to code up something similar by hand in order to handle things like triplets whose durations cannot be exactly represented by a floating point number.

So far there is only one significant application using these tools — the abc2ly.pl script included in the ABC module. It converts ABC files to the Lilypond music notation format. Lilypond is a very powerful open source music notation system which produces gorgeous sheet music output. This is a great way to print out good looking music notation from ABC files. (I know there is an abc2ly program included with Lilypond, but last time I checked its output looked tragically bad. abc2ly.pl is already working well enough that I’m hoping to produce a book of sheet music using it in 2011.) So let me leave you with the PDF of the above carol, produced using these tools, Rakudo Perl 6, and Lilypond.