Working around Lilypond quirks to generate consistent output

A significant feature of Yockÿrr Editions is the ability to transcribe music in a variety of tactuses: quaver, crotchet, minim all the way down to breve. This sliding duration is a core function of each edition and is simply achieved by using the non-documented Lilypond function \shiftDurations here which is a wrapper around the Scheme function shift-duration-log here. \shiftDurations takes three arguments, the first of which is the power of two by which to shift the duration, second the number of dots to add and finally the music expression to apply this to. I write all the music in crotchet tactus then use this function to shift the durations and time signature into whichever tactus the user selects.

However, the style of music at the time uses a lot of very long notes often indicated in the source by a wide longa. In the source this note is indeterminate in length and there are cadential cues in the music that indicate when it is right to move off the long note. Modern musicians and Lilypond need a little more help than this so part of the editorial process is to set a length for these notes so that the music moves off together after the long note. I write this in Lilypond by using the syntax \longa*n where n is the factor by which the duration of this note should be multiplied by. This does not work well with the shifting durations because when the music is shifted into a longer tactus, the written longas are shifted out of font and Lilypond complains:

warning: none of note heads `noteheads.s' or `noteheads.d' found

In the output there will be a space for this note, but no note printed! Lilypond’s font rightly only contains noteheads up to longa which has duration log of -2. When the duration log gets lower than -2 (i.e. longer than a longa), Lilypond cannot match this number to a notehead and does nothing instead. What I want to happen instead is that notes longer than a longa are represented by a longa which is… longer. This is standard practice in typeset scores and follows on from the practice of longas being indeterminate. In order to accomplish this, I need to find the notes which are shifted too far and replace them with a longa that is correctly scaled. I can do this using the (similarly undocumented) music-map function which functions exactly like the standard map but works recursively on music expressions.

brevemult = #(define-music-function (mus) (ly:music?)
               "Turns notes that are longer than longas into longas with artificial scaling"
               (music-map (lambda (e)
                             ((d (ly:music-property e 'duration (ly:make-duration -2)))
                              (l (ly:duration-log d))
                              (c (ly:duration-dot-count d))
                              (s (ly:duration-scale d))
                              (if (< l -2)
                                  (ly:music-set-property! e 'duration (ly:make-duration -2 c (* s (expt 2 (- -2 l)))))
                            ) mus)

Prefix this function to \shiftDurations and it will re-shift longas back into view.