Thursday, January 28, 2010

Holding Pattern Continued

Well, a week has gone by, and Rakudo's ng branch is definitely not ready for prime time yet. It's completely understandable, but it does put a bit of a kink in my plan to start working on getting actions attached to the Perl 6 ABC grammar using ng.

Okay, then, I guess the thing to do is to find a goal for the project that can be done now. And hey, there it is! I will attempt to get my hands on some examples of Irish traditional music (again, as colomon suggested) and make sure they work in our current parser. In turn, that will require a decent way of checking whether an entire tune has been read into the grammar or not. That sounds like a solid goal for the next week...

Thursday, January 21, 2010

Holding Pattern

Well, I meant to get to adding actions to my grammar. But I have a great excuse for not making progress. At the moment, there is a big push underway in Rakudo development to replace the current master branch with the ng branch. As I understand it, ng implements actions a bit differently than master -- it is more true to the Perl 6 spec. But right now, ng isn't ready to use, and there's not much point in targeting an out-of-date implementation that is going to go away in the next couple of weeks. So I'm putting this on hold until ng is in place.

If you're following along at home, I did go ahead and add grace notes, chords, nth endings, and rolls and staccato markings. It's so straightforward it's not really describing how I did it, but it is uploaded to github if you'd like to try it. I don't know if it will properly support Irish music yet, as a commenter requested last time, but it ought to be closer, anyway.

Thursday, January 14, 2010

Putting it together

Just to get more dramatic results, here's script to check if an ABC tune can be played on a one-row button accordion in G. It's pretty easy to put together given what we've got so far:
use v6;

BEGIN { push @*INC, "lib" }
use ABC;

my @matches = $*IN.slurp.comb(m/ <ABC::tune> /, :match);

my %dg_notes = {
'g' => 1,
'a' => 1,
'b' => 1,
'c' => 1,
'd' => 1,
'e' => 1,
'^f' => 1
}

for @matches {
my %header = header_hash(.<ABC::tune><header>);
say %header<T> ~ ":";

my @notes = gather for .<ABC::tune><music><line_of_music> -> $line
{
for $line<bar> -> $bar
{
for $bar<element>
{
when .<broken_rhythm> { take .<broken_rhythm><note>[0]; take .<broken_rhythm><note>[1]; }
when .<note> { take .<note>; }
}
}
}

my %key_signature = key_signature(%header<K>);

my @trouble = @notes.map({apply_key_signature(%key_signature, .<pitch>)}).grep({!%dg_notes.exists(lc($_))});
say @trouble.perl;
}


Basically, we set up @matches with all the tunes in the file, and %dg_notes with all the notes that can be played (though it's actually just the key of G for now). Then for each tune, we loop through and collect all the notes, factored into the appropriate key signature. Then we just grep against the notes that are allowed to make a list of the notes not present on a G accordion. Here are the results on my sample file:


Cuckold Come Out o' the Amrey:
["^c", "^c", "^c", "^c", "^c", "^c", "^c", "^c", "=f", "^c", "^c", "^c", "^c"]
Elsie Marley:
["=F", "=f"]
Peacock Followed the Hen. JWDM.07:
[]


So in this case, "Cuckold" has a bunch of c-sharps and an f-natural, "Elsie Marley" has a couple of f-naturals, and "Peacock" is solidly playable on a G accordion.

While this approach has been fun, I think it's time to dig in use Perl 6 grammar actions to build a smarter data structure for ABC tunes. Should be exciting...

Wednesday, January 6, 2010

Extracting the Tunes from a File

Just a quick post today. While reworking my test script for the DG accordion testing program, I decided it would be better if the script could accept an ABC file with multiple tunes in it. After about ten minutes of fiddling around, I came up with this:

my @matches = $*IN.slurp.comb(m/ <ABC::tune> /, :match);

Actually, that's a lie. I just came up with that a second ago, when I said to myself, "Wait a minute, maybe slurp works on $*IN?" Previously I was using lines and join to get the same effect.

The scary thing is, that's just the cleanest way I've been able to find to do it in Rakudo. In ideal Perl 6, I believe you could just say

my @matches = slurp.comb(m/ <ABC::tune> /, :match);


I believe in ideal Perl 6 that would be lazy, too, using the Cat class internally to get a lazy string.

Anyway, that gives you an array of Match objects, one for each ABC tune in the file. But there is one big gotcha with this formulation: if something happens to abort parsing the ABC::tune early (say it hits an ABC directive we haven't implemented yet), comb will merrily skip the rest of the tune without any warning. So this is probably a less than ideal approach in the long run. But my attempts to make a regex for the entire file have failed so far, and this works quite nicely on my sample data.