4

Consider the example below. It has 6 systems, and if I force LilyPond to put all 6 onto one page with min-systems-per-page = #6, it does, and it works well. The issue is that in real life, not every page fits exactly 6 systems and not every page needs them -- with this option, even a page where the contents would be nice with just 3 systems gets stretched to have 6 systems.

However, when I attempt to use the provided page-breaking = #ly:minimal-breaking, it fails to fit the 6 systems onto the page. Still in the first example with min-systems-per-page, it doesn't seem that any compression is happening as annotate-spacing shows the same spacing everywhere basically in the two examples.

Any idea how to get actual minimal breaking? Or in other words, how to force LilyPond to actually use the space it has on the page?

\version "2.24.2"

#(set-default-paper-size "a4")
#(set-global-staff-size 20)

\paper {

    %annotate-spacing = ##t

    oddHeaderMarkup = ##f
    evenHeaderMarkup = ##f
    oddFooterMarkup = ##f
    evenFooterMarkup = ##f
    tagline = ##f
    bookTitleMarkup = ##f
    scoreTitleMarkup = ##f

    ragged-bottom = ##t
    ragged-last-bottom = ##t

    markup-system-spacing = #'((basic-distance . 1) (minimum-distance . 0) (padding . 2) (stretchability . 1))
    score-markup-spacing = #'((basic-distance . 1) (minimum-distance . 0) (padding . 2) (stretchability . 1))
    score-system-spacing = #'((basic-distance . 1) (minimum-distance . 0) (padding . 3) (stretchability . 1))
    system-system-spacing = #'((basic-distance . 1) (minimum-distance . 0) (padding . 2) (stretchability . 1))
    markup-markup-spacing = #'((basic-distance . 1) (minimum-distance . 0) (padding . 1) (stretchability . 1))
    last-bottom-spacing = #'((basic-distance . 1) (minimum-distance . 0) (padding . 1) (stretchability . 1))
    top-system-spacing = #'((basic-distance . 1) (minimum-distance . 0) (padding . 1) (stretchability . 1))
    top-markup-spacing = #'((basic-distance . 1) (minimum-distance . 0) (padding . 1) (stretchability . 1))

    % If this line is kept (and the one later is commented out),
    % it's obvious that there's plenty of vertical space on the page for all 6 systems
    min-systems-per-page = #6

    % If this line is kept (and the one above is commented out),
    % it's obviously not minimal
    %page-breaking = #ly:minimal-breaking

    % So how can one get the true minimal page breaking when the number of systems that fit into a page is not fixed betwen pages?
}

\score {
    <<
        \new Lyrics = "lyr"
        \new PianoStaff <<
            \time 2/4
            \new Staff = "up" \relative << \new Voice = "cf" { \voiceOne c'''2 c c c } \\ { c,, c c c } >>
            \new Staff = "down" \relative << \clef bass { c'2 c c c } \\ { a,, c c c } >>
        >>
        \context Lyrics = "lyr" \lyricsto "cf" { La la la la }
    >>
    \layout {
        system-count = #3
    }
}

\score {
    <<
        \new Lyrics = "lyr"
        \new PianoStaff <<
            \time 2/4
            \new Staff = "up" \relative << \new Voice = "cf" { \voiceOne c'''2 c c c } \\ { c,, c c c } >>
            \new Staff = "down" \relative << \clef bass { c'2 c c c } \\ { c,, c c c } >>
        >>
        \context Lyrics = "lyr" \lyricsto "cf" { La la la la }
    >>
    \layout {
        system-count = #3
    }
}

version with minimal-breaking

1
  • 3
    I think you are misunderstanding the meaning of "minimal" in this context. ly:minimal-breaking is an algorithm that is designed to run fast by doing minimal work, not to produce output with a minimal number of pages. Nov 23, 2023 at 13:36

1 Answer 1

5

Spacing is an incredibly complex thing. If you look at your example you will see that it fits only because by a chance of luck the Lyrics do not collide with the stems above, so the systems can overlap a bit. The problem is that this does depend on line breaking (which depends on page breaking). Generally page breaking depends on spacing information that may only be correctly calculated after page breaking.

So what Lilypond does is: It first estimates these measures, and calculates page breaking by these estimates (annotate-spacing shows this as extent-estimate). Now, there is no real way to estimate chances of luck for letting things overlap. So Lilypond cannot really use this while calculating page breaks.

Setting min-systems-per-page or page-count will force Lilypond to do things differently even if it leads to need of compression.

Now, you could use page-count = 1. But be aware that page-count will have Lilypond compress everything that is too much onto the last page (so you get n-1 uncompressed and one last compressed page).

Another thing you could do is to tell Lilypond than in act your systems are a bit smaller than what it estimates.

Lilypond will space out things using a simple ”stretchable-glue“ paradigm. Between all systems you get some glue with some base distance, some minimum distance and some ”stretchyness“. When you have too little space Lilypond will compress these glues depending on the stretchyness, but at most to the minimal space (anything more will be counted as compression).

These distances are not counted by distance, but by distance between baselines. So setting distance to 0 will mean on top of each other. To avoid collision Lilypond has a padding parameter which specifies the minimum amount of space between the printed ink.

So what we can do is to set a negative padding — this way you’ll tell Lilypond ”it is okay to overlap a bit“, which allows Lilypond to make up this overlap:

\paper {
    annotate-spacing = ##t

    oddHeaderMarkup = ##f
    evenHeaderMarkup = ##f
    oddFooterMarkup = ##f
    evenFooterMarkup = ##f
    tagline = ##f
    bookTitleMarkup = ##f
    scoreTitleMarkup = ##f

    ragged-bottom = ##f
    ragged-last-bottom = ##f

    %page-count = 1

    score-system-spacing.padding = #-2.1 %default 1
    system-system-spacing.padding = #-2.1 % default 1
}


\score {
    <<
        \new Lyrics = "lyr"
        \new PianoStaff <<
            \time 2/4
            \new Staff = "up" \relative << \new Voice = "cf" { \voiceOne c'''2 c c c } \\ { c,, c c c } >>
            \new Staff = "down" \relative << \clef bass { c'2 c c c } \\ { a,, c c c } >>
        >>
        \context Lyrics = "lyr" \lyricsto "cf" { La la la la }
    >>
    \layout {
        system-count = #3
    }
}

\score {
    <<
        \new Lyrics = "lyr"
        \new PianoStaff <<
            \time 2/4
            \new Staff = "up" \relative << \new Voice = "cf" { \voiceOne c'''2 c c c } \\ { c,, c c c } >>
            \new Staff = "down" \relative << \clef bass { c'2 c c c } \\ { c,, c c c } >>
        >>
        \context Lyrics = "lyr" \lyricsto "cf" { La la la la }
    >>
    \layout {
        system-count = #3
    }
}
8
  • Thanks for the explanation. Do you know if it is possible to tell LilyPond to use a better page breaking algorithm? In my real life situation I managed to get LilyPond to report that it had to squeeze a page, yet it left over 1cm of empty space in the bottom. Also, in my real life, you can't really squeeze lyrics next to notes from predecing systems, it's just in this minimal one. I'm happy to compose a better example that shows how bad this is. How it is is really unacceptable and I solved it just by setting bottom margin 0 and overlay the footer with zero height, but this solution sucks...
    – yo'
    Nov 23, 2023 at 13:30
  • Also, LilyPond seems to try multiple passes (it has this "calculating if music will fit on x or x+1 pages"), so why it still fails to fit something on one page that clearly fits on one page? I mean, I'm afraid that for the whole book I'll just have to make the individual pieces with a lot of tweaks, and then use e.g. LaTeX to compose these together. (I have actually done that before where I forced LilyPond into one system per page, then used pdfcrop to crop them, and then composed them. I still saved a few pages this way.)
    – yo'
    Nov 23, 2023 at 13:32
  • 1
    @yo' No, Lilypond does not do multiple passes. Lilypond tries to find a theoretical opimum of pages, and will allow a solution to take up one more page. But there is no multipassing going on. As I said, the problem is estimating the actual extent of your systems. You can of course tell Lilypond to use a better algorithm, you’d just have to come up with a better one. It might always be possible to improve the estimated extent. One could probably do a solution that first calculates the system, does no stretching or compressing at all and simply spaces out these systems on as little pages ap.
    – Lazy
    Nov 23, 2023 at 13:46
  • 1
    @yo' Maybe Lilypond could also multipass to refine its estimates. If you are interested, feel free to try some things.
    – Lazy
    Nov 23, 2023 at 13:48
  • 1
    +1 for padding with a negative number. I didn't know that was possible.
    – ksnortum
    Nov 23, 2023 at 16:02

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.