Alphabet Project, Part 9
- 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
- Alphabet Project, Part 8
Refactoring LilyPond output - Part Deux
I ended the last post with a list of nice-to-have improvements. To refresh both our memories:
- 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)
Today, I’m going to try to get through this list and end with a pretty, legible score with extracted parts.
Attach consonant phonemes
I’ve started here because this is by far the easiest task out of the lot. Currently our code looks like this:
The important lines for our purposes today are
All we need to do is update the return tuple in the last line there to prepend the consonant phoneme to the vowel.
In the American English International Phonetic Alphabet, most English consonants are represented by their own character, so we only need to handle a few small cases when mapping between them. So let’s add a function to do that
If the letter we pass in to consonant_phoneme_for/1
is a key in the map,
it returns the value for that key, otherwise, we can use the letter character
itself, so we just return that. Now we can easily plug this in to consonant_phoneme_modulation_points/1
like so
And there we go! The vowel parts remain the same, but the consonant parts also print their consonant phoneme.
Onwards!
Clean up repeated dynamics
Let’s look at the last page of the score:
Every measure has a dynamic attached to it, even when that dynamic is the same as the measure before. Traditionally, we would only display the dynamic when it changes, and doing so here will make the score look cleaner.
All we need to do is compare each measure with the measure before it, and, if it has the same dynamic, remove it so it won’t print.
My first attempt at this was a very Elixir-esque, recurse-with-an-accumulator:
But then I read a blog post1 that mentioned how, while this is indeed a very Erlang/Elixir way
of doing things, the Enum
module often provides sufficient functionality
to be able to accomplish the same work in a single Enum.map
loop, so I thought
I’d give it a try:
As far as code size goes, the second solution is definitely shorter, and does reduce a bit of the mental overhead required to parse it, since it’s only one function. Admittedly, there’s something fun about tail recursion, but since I’d like to be able to read this code again in the future, and since they both return the same results, let’s stick with the second, shorter solution. Thanks for the tip, forgotten blog author!
No repeated dynamics! Well, there are still a few, but they only reappear after an entire measure of rest (see the bottom line). This is acceptable, since after rests it can be helpful to re-state the dynamic.
Add hairpins
To give the piece a bit of movement, and, honestly, make the score a bit more interesting to read, I want to add some crescendos and decrescendos when the dynamics shift. My plan is to add a full measure dynamic to the measure before a dynamic change.
Because we need to know the last printed dynamic in the piece, we need to keep an
accumulator of sorts for dynamic to keep track of the last non-nil
value. To do this,
we need to return to the tail recursion approach we tried and rejected above:
Then we also need to make a couple small changes to our Measure
struct to print out these hairpins:
And…
Great! But there’s always one more thing, isn’t there?
Yes there is, and in this case it’s that we’re always attaching dynamics and phonemes to the first event of a measure, whether or not it’s a rest:
Rests can have neither dynamics nor pronunciation2, and so it makes little sense to attach such markup to them. Right now we’re attaching markup with this code
where we deconstruct the list of events and write all the markup to the head of the list. Instead, we want to find the first event that isn’t a rest and attach to that. Let’s see what that looks like
Hey, much better! Except, where did our beaming go? Let’s look at the code that generates the beaming
There’s the issue in the case
statement. We’re making sure all the events in the measure
can be beamed (i.e. are 8th or 16th notes), by checking that each measure event ends
with an 8th/16th note (dotted or otherwise). But now that we’re adding the markup before
we check for beaming, the markup keeps the event string from ending with the duration
notation, thus causing our case
statement to always return false, and thus, no beaming.
Fortunately, this is an easy fix. We can just change the Regex match statement to check for the presense of the correct duration notation anywhere in the event string, rather than just at the end
And there we go!
That’s 3/5 of the remaining issues I listed at the beginning of the article. Extracting parts will be the topic of the next post, and cleaning up repeated 8th and 16th notes is a task I’m likely to pass on for now, the reasoning behind which will be a brief subtopic in the next post. So thanks for reading, and stay tuned!
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)!
-
I’m pretty sure it was by José Valim or Chris McCord, either of whom definitely know what they’re talking about as far as Elixir is concerned, but I can’t track it down again. ↩
-
Though I’m sure there are composers out there who have determined they do in the context of a piece. This is not such a piece. ↩