As I was browsing through the LilyPond Snippet Repository (LSR), looking for something completely different, I stumbled upon this little trick for adjusting measure width. Unfortunately, it is only helpful for increasing the width. Let's start with a simple score:
The issue we'd like to remedy is the horizontal spacing given to the dynamics in the first measure, which we will adjust in the third measure for comparison. The real trick here is to use a parallel voice that contains basically no musical information, but allows us to override some dimensions. Using a MultiMeasureRest (MMR) gives us access to a property called "minimum-length". This is what we will use to force the measure to extend in length, thus creating more space for the second dynamic marking. To make it more useful, I wrapped the code within a music function that we can reuse.\new Voice \relative c' { \dynamicUp << { c1 } { s2\f s2\p } >> | \noBreak f4 e d c | \noBreak << { c1 } { s2\f s2\p } >> | \noBreak d2 e | \break }
extender = #(define-music-function (parser location length music) (number? ly:music?) #{ << { $music } \new Voice { \override MultiMeasureRest.transparent = ##t \override MultiMeasureRest.minimum-length = #length R1 } >> #})
This function takes two arguments. The first sets the value for "minimum-length". By definition, if the measure is already as long or longer than this value, then nothing changes. The second argument is the music expression that the spacing should apply to. There are two things to remember with this:
- The above code assumes the largest duration is a whole note, so you will need to change the duration of the MMR if you are using a different time signature ("R2." for 3/4 time, etc.).
- It's designed to extend the length of ONE measure only. Repeat as necessary.
Now, let's put it to use:
\version "2.18.2" \paper { line-width = 9\cm indent = 0 } extender = #(define-music-function (parser location length music) (number? ly:music?) #{ << { $music } \new Voice { \override MultiMeasureRest.transparent = ##t \override MultiMeasureRest.minimum-length = #length R1 } >> #}) \markup "Before" \new Voice \relative c' { \dynamicUp << { c1 } { s2\f s2\p } >> | \noBreak f4 e d c | \noBreak << { c1 } { s2\f s2\p } >> | \noBreak d2 e | \break } \markup "After" \new Voice \relative c' { \dynamicUp << { c1 } { s2\f s2\p } >> | \noBreak f4 e d c | \noBreak \extender #12 << { c1 } { s2\f s2\p } >> | \noBreak d2 e | \break }
I'm still on the lookout for a more convenient solution for "shrinking" a measure, but some of the methods in the NR (above) can help with this.
Got any other great ideas for shrinking or expanding measures to make the page look better? Share them in the comments!
It should be possible to make the Rest the length of $music, isn't it? That would make the function more general, at least as long the music doesn't exceed one measure.
ReplyDeleteI guess you could do that, but what I was trying to accomplish here was to force the length of $music to be the same as the length of Rest, not the other way around. There are lots of factors that determine how long $music will end up being, which are decided automatically by the layout algorithms and are more difficult to control externally. Adjusting the length of Rest is a direct way to ensure that $length gets a certain amount of horizontal space and no less. The end result is a length that is the greater of the default measure length or the length of Rest.
ReplyDeleteYou know what, Urs,I mis-understood what you were saying. I absolutely agree that that would be more desirable, removing one more thing that would otherwise need to be input by the user. I'll look into it and see if I can't figure out how to do that. What a great idea!
ReplyDeleteLeigh, thank you for your post. I've been searching off and on for a fix to how LilyPond squashes a coda on a lead sheet with \paper { ragged-last = ##t }. I found the snippet you referenced, but I only understood it, and got it to work, with your function and example.
ReplyDeletePerhaps you could revisit this to, as Urs suggests, coerce the 'R1' in the function to match $music.duration (or whatever it would be ... ).