8va / 8vb and Clefs

2015-09-17 — By Paul Morris — LilyPond

Back in May (how time flies!) I mentioned some improvements to the code for rendering Clairnote notation with LilyPond.  These started with a fix for a bug with 8va and 8vb music, and led to better code for clefs as well.  The details are technical, having to do with LilyPond’s internals, and probably more than anyone would ever care to know, but it was a real breakthrough in a longstanding challenge in my work implementing alternative music notation systems in LilyPond.  So here goes…

The bug: “The notes in the 8va and 8vb sections were not being transposed accurately, leaving the notes at the wrong staff positions.”  The fix entailed writing a custom Scheme “engraver” that “listens” for “rhythmic events”.  Such events are just one stage in LilyPond’s “pipeline” or sequential process of converting text input into graphical output.  When a new 8va or 8vb section reaches this engraver, the engraver changes the transposition values (the “middleCOffset” staff context property) from base seven for traditional notation to base twelve for Clairnote (because there are seven notes per octave on the traditional staff and twelve notes per octave on a chromatic staff).  The result is that the notes are transposed by the correct amount for the Clairnote staff.

I realized this was also a better way to handle clef settings, since the staff context settings for clefs are very similar to the 8va/8vb context settings.  The clef settings determine, for a given clef, things like where middle C is on the staff and where the clef glyph is positioned vertically on the staff.  They have to be customized for Clairnote otherwise notes and clef glyphs aren’t positioned correctly.

Clefs are one of the trickier things to customize in LilyPond, at least if you want different kinds of staves to do different things with the same clef input, like when you want to display the same music on both a Clairnote staff and a traditional staff.  The problem is that the clef settings are set globally for all of the staves produced by a given file (or set of files), whereas most other things can be customized on a “per staff” basis.  It is fairly easy to introduce new clefs or change the settings of existing clefs, but then those changes apply to all the staves.  This is fine if all the staves are Clairnote staves, or all traditional staves, but if you want to show traditional notation alongside Clairnote for comparison, then you have a problem.

The previous way around this was to modify the global clef settings so they worked for Clairnote, and then modify the music that was headed for a traditional staff with a LilyPond “music function” that would adjust the clef settings to suit the traditional staff.  Instead of plain music input:

\new Staff {
  c4 d e f
}

You just had to process the music with the custom \clefsTrad music function:

\new TradStaff {
  \clefsTrad { c4 d e f }
}

This was not really a big deal, but it felt like a kludge, and (more importantly) it only worked for treble, bass, and alto clefs.  Any other clefs would result in incorrect output on the traditional staff.  (See this post for why this is the case and why this \clefsTrad music function approach was nevertheless still better than the previous approach.)

Earlier on I had attempted to use a custom Scheme engraver that “acknowledged” clef “grobs” (graphical objects) as a way to customize the clef settings on a “per staff” basis. This is one way that many things can be successfully customized (key signatures, accidental signs, etc.).  But it didn’t work for clefs because the changes to the staff context settings for clefs went into effect too late, after at least one note was already positioned on the staff incorrectly.  “Acknowledging grobs” was a step too far down the LilyPond pipeline from input to output to work.

However, a custom Scheme engraver that “listens” for “events” (as with the solution to the 8va/8vb bug) is effectively one step further up the pipeline, in just the right place to successfully adjust the clef settings from their traditional values to Clairnote values before they are used (and without actually ever changing the global clef properties).  This means that the same clef input in the music can be interpreted differently by whatever staff it is sent to.  So now there’s no more need for the \clefsTrad music function and all types of clefs work correctly on any staff.

\new TradStaff {
  c4 d e f
}

Along the way there were a few other internal improvements made.  The only other user-facing change is that I renamed the custom “StaffTrad” context to “TradStaff” to match the LilyPond convention for other staff contexts like MensuralStaff, VaticanaStaff, or TabStaff.  So TradStaff is the new StaffTrad.

All of these changes are in the current clairnote-code.ly clairnote.ly file that is available for download from the Software: LilyPond page. If anyone wants to know more about LilyPond’s internal pipeline, check out the LilyPond user mailing list thread here and here, along with (of course) the official documentation.

Update: the “clairnote-code.ly” file was renamed “clairnote.ly” on May 15, 2017.