Alphabet Project, Part 8
- Alphabet Project, Part 1
- Alphabet Project, Part 2
- Alphabet Project, Part 3
- Alphabet Project, Part 4
- Alphabet Project, Part 5
- Alphabet Project, Part 6
- Alphabet Project, Part 7
Refactoring LilyPond output - formatting measures
In the last post I refactored code around representing measures as structs, rather than anonymous tuples. That version of the code is on Github here.
Beaming
The goal of that refactor was to clean up the code base, not to make any changes to the resulting output,
but I did end up making one small change. In Measure.events_to_lily/
I added a call to
Measure.add_beaming/1
All this does is insert [
as the second element of the events list, and ]
as the last element.
When this is compiled by LilyPond, this ensures that all the notes in a measure will be beamed together.
To illustrate, this is the difference between
which I think looks rather nicer.
Full measure rests
However, on a less aesthetic note, this is also the difference between
Clearly the beaming doesn’t work as well here. But more importantly, in both cases we should be able to display a full measure rest, instead of a tuplet made of up individual rests.
This is a small change in the Elixir code. We can add a function to check
whether every event in a measure is a rest (all_rests?/1
), and then case
on the value of that function call to either print the events as usual,
or use LilyPond’s full measure rest syntax to print out the appropriate rest
symbol for the measure.
This gives us the much more preferable
Printing durations
So far every printed note has been an 8th note. While this works for getting the basic layout of the piece set, it’s hardly ideal for a final printed score. Let’s look at the opening measure
Already we see a few odd ratios: 60:30
, 3:30
, 7:30
, 10:30
, and so on.
In many cases these are reducible, but beyond that, the tuplet should not
be using 8th notes.
There are two issues it would behoove us to solve here:
- find measures in which the tuplet divides evenly into the measure and “detupletify”
- for measures that require tuplets, but for which the ratio should not be using 8th notes, and pick a more suitable duration
Issue 2 is slightly easier to work out, and helps us on our way to solving issue 1, so we’ll start there:
Correcting notated tuplet durations
When notating tuplets in music, and even more commonly now as rhythms in contemporary music become more complex, there is some flexibility. For example, dividing a quarter note beat into quintuplets: should they be notated as 5 sped up 16th notes, or 5 slowed down 32 notes? In almost all cases, composers and performers would prefer the 16th notes, but the possibilty of alternate notations does remain.
To eliminate some of the subjectivity, or, rather, to render my general subjectivity into code, I’m going to say that, for each tuplet, we will
- find the exact, probably fractional, number of 8th notes that would be required to fit the tuplet, let’s call this value
x
- round
x
based on standard float rounding (0.5 and higher rounds up, everything else rounds down) - this gives us the base count of 8th notes to use per tuplet event
- find the closest untied, notateable duration given that 8th note count
- renotate the measure using this duration
Addendum: if x
would round to 0, the ratio is greater than 2:1, in which case we should use 16th notes against 8th notes
An example:
Given the ratio 7:30
, we want to find the untied musical duration
that best fits into 30 seven times.
30 / 7 = 4.285...
which rounds to 4
8th notes, or a half note. So the measure
would be rewritten using 7 half notes instead of 7 8th notes.
In code, this looks something like this:
Here we find the ratio for the tuplet, and based on our decisions above, pick the
most appropriate untied duration. If the time signature is set instead of the tuplet –
indicating the pulse part – we return “8”. I’ve also added the :written_duration
attribute to the Measure struct, and we store this duration in that attribute
to make it more easily accessible.
Now we need to update Measure.to_lily/1
to make sure we use this new duration:
Simple enough! Let’s see what this looks like:
Huh. Well, I won’t pretend I don’t think that looks pretty cool. But, it’s not really what we’re going for, so let’s figure out what went wrong.
Turns out it’s not too difficult to suss out the issue. Let’s take a closer look at the 8th and 9th staves:
If we look at the numbers above the staves in the 2nd measure, we see 7
and 10
, which are the tuplet values for these parts. But, the tuplet ratios are still
7:30
and 10:30
, which were specifically for 8th notes. Now, in the top line, we’re trying to fit 7 half notes into the space of 30 half notes, when instead we want
to fit 7 half notes into the space of 30 8th notes! No wonder everything looks off!
Fortunately, this is simple to fix. Based on the division and rounding done in set_proper_duration/1
above, we know how many 8th notes are in each new notated tuplet
event, so all we have to do is multiply the numerator of the tuplet by that number. For example:
Our original ratio of 7 8th notes into 30 8th notes in now 7 half notes into 30 eigth notes. Each half note contains 4 8th notes, so we transform the tuplet into
(7 * 4):30 = 28:30
. In order to do this, we should also store the multiplicand on the measure as well:
There we go! Except that some of those 8th notes really should be 16th notes, but they’re not. And that’s because my math is bad. When rounding, I said that a rounded value of 0 should return 16th notes, but 8/16 is exactly 0.5, which gets rounded up to 1, which means we stay using 8th notes.
With just a couple of small code changes, to account for being able to map to 16th notes, and to round the tuplet numerator properly:
Great! There’s only one more thing that stands out as affecting the readability of the score, and that’s the text of the tuplets. In the fourth staff, we have a tuplet of 24:30
, but
it is notated with 3 whole notes. As calculated above, the tuplet ratio is between 8th notes on both sides, but when the notation uses whole notes, 24
is less descriptive than
we might like. Fortunately, LilyPond lets us put just about anything we want in the tuplet text, so let’s go ahead and do that.
I also took this chance to address issue #1 from a ways back in this post: find measures in which the tuplet divides evenly into the measure and “detupletify” In those cases, we can print the proper generated duration without needing the tuplet markup, as you can see in the score below in the second and ninth staves.
The changes here are even more apparent on the last page of the score, where several measures of 4:2
tuplets
have been renotated as 4 untupletd 16th notes:
I think that’s enough for one post, but here’s a list of what I want to tackle next time:
- attach the consonant phonemes to their parts
- clean up repeated dynamics and add some hairpins as dynamics change
- have dynamic and phoneme markup attached to the first note in a measure, not the first event, which might be a rest
- work on being able to extract clean, legible parts for performers
- try to clean up repeated 8th and 16th rests, though this is a tricky task while retaining some semblance of traditional score legibility (i.e. notating rests on the downbeats of natural measure subdivisions, rather than willy-nilly)
The code, as it exists by the end of this post, can be found on Github here.
Thanks for reading! If you liked this post, and want to know when the next one is coming out, follow me on Twitter (link below)!