3

I'm trying to typeset a double-choir score with piano. Overall the score is set up as Frenched Score, removing the choir staves when empty, but handling both choirs separately.

This is a distilled version of my current state:

\version "2.24.1"

\paper {
  #(set-paper-size "a4")
  left-margin = 3\cm
}

global = {
  \key c \major
  \time 4/4
}

scoreASopranoChoirI = \relative c'' {
  \global
  %\separate
  \textMark "Choir I"
  R1*20
  \repeat unfold 10 { g4 a f c }
  \repeat unfold 50 f4
  %\join
  \textMark "Choir I && II"
  \repeat unfold 50 c4
  %\separate
  \textMark "Choir I"
  \repeat unfold 50 f4
}

scoreAAltoChoirI = \relative c' {
  \global
  %\separate
  R1*20
  \repeat unfold 10 { g'4 a f c }
  \repeat unfold 50 a4
  %\join
  \repeat unfold 50 e'4
  %\separate
  \repeat unfold 50 a4
}


scoreASopranoChoirII = \relative c'' {
  \global
  %\separate
  \textMark "Choir II"
  R1*30
  \repeat unfold 50 a4
  %\join
  \repeat unfold 50 c4
  %\separate
  \textMark "Choir II"
  \repeat unfold 50 a4
}

scoreAAltoChoirII = \relative c' {
  \global
  %\join
  R1*30
  %\separate
  \repeat unfold 50 c4
  %\join
  \repeat unfold 50 e4
  %\separate
  \repeat unfold 50 c4
}

scoreARight = \relative c'' {
  \global
  \repeat unfold 30 { g4 a f c }
  \repeat unfold 50 e4
  \repeat unfold 50 c4
  \repeat unfold 50 e4
}

scoreALeft = \relative c' {
  \global
  \repeat unfold 30 { g4 a f c }
  \repeat unfold 50 a4
  \repeat unfold 50 e4
  \repeat unfold 50 a4
}

scoreAChoirIPart = \new ChoirStaff \with {
  instrumentName = "Choir I"
  shortInstrumentName = ""
  \consists Keep_alive_together_engraver
  \override VerticalAxisGroup.remove-empty = ##t
  \override VerticalAxisGroup.remove-first = ##f
} <<
  \new Staff \with {
    instrumentName = "Soprano"
    shortInstrumentName = "S.1"
  } \new Voice = "soprano" \scoreASopranoChoirI
  \new Staff \with {
    instrumentName = "Alto"
    shortInstrumentName = "A.1"
  } \new Voice = "alto" \scoreAAltoChoirI
>>

scoreAChoirIIPart = \new ChoirStaff \with {
  instrumentName = "Choir II"
  shortInstrumentName = ""
  \consists Keep_alive_together_engraver
  \override VerticalAxisGroup.remove-empty = ##t
  \override VerticalAxisGroup.remove-first = ##f
} <<
  \new Staff \with {
    instrumentName = "Soprano"
    shortInstrumentName = "S.2"
  } \new Voice = "soprano" \scoreASopranoChoirII
  \new Staff \with {
    instrumentName = "Alto"
    shortInstrumentName = "A.2"
  } \new Voice = "alto" \scoreAAltoChoirII
>>

scoreAPianoPart = \new PianoStaff \with {
  instrumentName = "Piano"
  shortInstrumentName = "Pno."
} <<
  \new Staff = "right" \with {
  } \scoreARight
  \new Staff = "left" \with {
  } { \clef bass \scoreALeft }
>>

\score {
  <<
    \scoreAChoirIPart
    \scoreAChoirIIPart
    \scoreAPianoPart
  >>
  \layout {
    \context {
      \Staff
    }
  }
}

At some point, the two choirs sign the same voices and I'd like to display them together in a single staff. To do that I'd tried to follow the solution provided here and I end up with:

\version "2.24.1"

join = {
  \set Staff.keepAliveInterfaces = #'()
}
separate = {
  \unset Staff.keepAliveInterfaces
}

\paper {
  #(set-paper-size "a4")
  left-margin = 3\cm
}

global = {
  \key c \major
  \time 4/4
}

scoreASopranoChoirI = \relative c'' {
  \global
  \separate
  \textMark "Choir I"
  R1*20
  \repeat unfold 10 { g4 a f c }
  \repeat unfold 50 f4
  \join
  \textMark "Choir I && II"
  \repeat unfold 50 c4
  \separate
  \textMark "Choir I"
  \repeat unfold 50 f4
}

scoreAAltoChoirI = \relative c' {
  \global
  \separate
  R1*20
  \repeat unfold 10 { g'4 a f c }
  \repeat unfold 50 a4
  \join
  \repeat unfold 50 e'4
  \separate
  \repeat unfold 50 a4
}


scoreASopranoChoirII = \relative c'' {
  \global
  \separate
  \textMark "Choir II"
  R1*30
  \repeat unfold 50 a4
  \join
  \repeat unfold 50 c4
  \separate
  \textMark "Choir II"
  \repeat unfold 50 a4
}

scoreAAltoChoirII = \relative c' {
  \global
  \join
  R1*30
  \separate
  \repeat unfold 50 c4
  \join
  \repeat unfold 50 e4
  \separate
  \repeat unfold 50 c4
}

scoreARight = \relative c'' {
  \global
  \repeat unfold 30 { g4 a f c }
  \repeat unfold 50 e4
  \repeat unfold 50 c4
  \repeat unfold 50 e4
}

scoreALeft = \relative c' {
  \global
  \repeat unfold 30 { g4 a f c }
  \repeat unfold 50 a4
  \repeat unfold 50 e4
  \repeat unfold 50 a4
}

scoreAChoirIPart = \new ChoirStaff \with {
  instrumentName = "Choir I"
  shortInstrumentName = ""
  \consists Keep_alive_together_engraver
  \override VerticalAxisGroup.remove-empty = ##t
  \override VerticalAxisGroup.remove-first = ##f
  \override VerticalAxisGroup.remove-layer = 1
} <<
  \new Staff \with {
    instrumentName = "Soprano"
    shortInstrumentName = "S.1"
  } \new Voice = "soprano" \scoreASopranoChoirI
  \new Staff \with {
    instrumentName = "Alto"
    shortInstrumentName = "A.1"
  } \new Voice = "alto" \scoreAAltoChoirI
>>

scoreAChoirIIPart = \new ChoirStaff \with {
  instrumentName = "Choir II"
  shortInstrumentName = ""
  \consists Keep_alive_together_engraver
  \override VerticalAxisGroup.remove-empty = ##t
  \override VerticalAxisGroup.remove-first = ##f
  \override VerticalAxisGroup.remove-layer = 1
} <<
  \new Staff \with {
    instrumentName = "Soprano"
    shortInstrumentName = "S.2"
  } \new Voice = "soprano" \scoreASopranoChoirII
  \new Staff \with {
    instrumentName = "Alto"
    shortInstrumentName = "A.2"
  } \new Voice = "alto" \scoreAAltoChoirII
>>

scoreAChoirIandIIPart = \new ChoirStaff \with {
  instrumentName = "Choir I & II"
  shortInstrumentName = "Ch. I & II"
  \consists Keep_alive_together_engraver
  \override VerticalAxisGroup.remove-first = ##t
  \override VerticalAxisGroup.remove-layer = 2
} <<
  \new Staff \with {
    instrumentName = "Soprano"
    shortInstrumentName = "S. I & II"
  } \new Voice = "soprano" \scoreASopranoChoirI
  \new Staff \with {
    instrumentName = "Alto"
    shortInstrumentName = "A. I & II"    
  } \new Voice = "alto" \scoreAAltoChoirI
>>

scoreAPianoPart = \new PianoStaff \with {
  instrumentName = "Piano"
  shortInstrumentName = "Pno."
} <<
  \new Staff = "right" \with {
  } \scoreARight
  \new Staff = "left" \with {
  } { \clef bass \scoreALeft }
>>

\score {
  <<
    \new ChoirStaff \with { 
      \consists Keep_alive_together_engraver 
    }
    <<
      \set ChoirStaff.systemStartDelimiter = #'SystemStartBar      
      \scoreAChoirIPart
      \scoreAChoirIIPart
      \scoreAChoirIandIIPart
    >>
    \scoreAPianoPart
  >>
  \layout { 
  \context {
    \Staff
  }    
  }
  \midi {
    \tempo 4=100
  }
}

This more ore less works ....

Show_joined_choirs

... but it has two problems:

  1. When both choirs are empty, the choir I & II staff pops up. Turning on remove-empty on the joined choirs staff make it disappear everywhere.
  2. When only one of the choirs is empty, both choirs are displayed.

Unwanted staves

Bonus points for how show a text mark when both choirs are displayed joined (which is not when they are ready to be joined)

2

1 Answer 1

2

I solved my problem. The solution came in two steps, for each of the two problems I described in the question:

Problem One:

When both choirs are empty, the choir I & II staff pops up. Turning on remove-empty on the joined choirs staff make it disappear everywhere.

The solution to this was inspired by this message on lilypond-user mailing list.

What I did was:

  1. Redefine my join and separate markers to be:
join = {
  \tag #'split {\set Staff.keepAliveInterfaces = #'() }
  \tag #'joined {\unset Staff.keepAliveInterfaces }
}
separate = {
  \tag #'joined {\set Staff.keepAliveInterfaces = #'() }
  \tag #'split {\unset Staff.keepAliveInterfaces }
}

As you can see, this is more or less the code I had previously, but now they're tagged so that when one is set, the other is not.

  1. Use \keepWithTag in the choir staff definition to select the right variant of the join/separate definition.

E.g for one of the choir I

scoreAChoirIPart = \new ChoirStaff \with {
  instrumentName = "Choir I"
  shortInstrumentName = ""  
  instrumentCueName ="Choir I"
    \override VerticalAxisGroup.remove-empty = ##t
    \override VerticalAxisGroup.remove-layer = 1
} <<
  \new Staff \with {
    instrumentName = "Soprano"
    shortInstrumentName = "S.1"
  } \new Voice = "soprano" \keepWithTag #'split \scoreASopranoChoirI
  \new Staff \with {
    instrumentName = "Alto"
    shortInstrumentName = "A.1"
  } \new Voice = "alto" \keepWithTag #'split \scoreAAltoChoirI
>>

(same for the choir II) ... and for the joined choir I+II

scoreAChoirIandIIPart = \new ChoirStaff \with {
  instrumentName = "Choir I & II"
  shortInstrumentName = "Ch. I & II"
  instrumentCueName ="Choir I && II"
    \override VerticalAxisGroup.remove-empty = ##t
    \override VerticalAxisGroup.remove-layer = 2
} <<
  \new Staff \with {
    instrumentName = "Soprano"
    shortInstrumentName = "S. I & II"
  } \new Voice = "soprano" \keepWithTag #'joined \scoreASopranoChoirI
  \new Staff \with {
    instrumentName = "Alto"
    shortInstrumentName = "A. I & II"
  } \new Voice = "alto" \keepWithTag #'joined \scoreAAltoChoirI
>>
  1. Make all the choir staves be remove-empty true.

And with that I solved problem one.

Problem Two:

When only one of the choirs is empty, both choirs are displayed.

This was toughter to solve. The way Keep_alive_together_engraver works with the "remove-layer" property is confusing as it is used for two contradictory purposes removing layers and keeping layers and, in my case, I wanted to remove some layers but keep others.

So, in the end I wrote a little scheme engraver to achieve that:

% Define a property that can be attached to grobs.
% This properly will name a group of staves that must be active together
#(set-object-property! 'keep-with 'backend-type? symbol?)

%
% Use the keep-with property to replace the "keep-alive-with"
% list of a grob with the collected staffs that have the same keep-with value
#(define (keep-with-engraver context)
   (let ((group-spanners '()))
     (make-engraver
      (acknowledgers
       ((hara-kiri-group-spanner-interface engraver grob source-engraver)
        (set! group-spanners (append group-spanners (list grob)))
        )
       )
      ((finalize engraver)
       (map (lambda (grob)
              (let ((keep-with (ly:grob-property grob 'keep-with))
                    (keep-list '()))
                (begin
                 (map (lambda (grob-other)
                        (if (not(eqv? grob grob-other))
                            (let ((keep-with-other (ly:grob-property grob-other 'keep-with)))
                              (if (eq? keep-with keep-with-other)
                                  (set! keep-list (append keep-list (list grob-other)))
                                  ))
                            )
                        ) group-spanners)
                 (ly:grob-set-object! grob 'keep-alive-with (ly:grob-list->grob-array keep-list))
                 )))
            group-spanners)
       ))))

The engraver follows a similar pattern to the Keep_alive_together_engraver (see code), acknowledges hara-kiri-group-spanner-interface to build a list of group-spanners and then searches through the list for values of a custom property keep-with that will tell which staves need to go together.

Final touches are:

  1. Define the keep-with for each choir staff:

On choir I: \override VerticalAxisGroup.keep-with = #'choirOne

On choir I: \override VerticalAxisGroup.keep-with = #'choirTwo

On choir I+II: \override VerticalAxisGroup.keep-with = #'choirOneAndTwo

  1. Use my custom engraver At the top level ChoirStaff and after the Keep_alive_together_engraver
\score {
  <<
    \new ChoirStaff \with {
      \consists Keep_alive_together_engraver
      \consists #keep-with-engraver
    }
    <<
      \set ChoirStaff.systemStartDelimiter = #'SystemStartBar
      \scoreAChoirIPart
      \scoreAChoirIIPart
      \scoreAChoirIandIIPart
    >>
    \scoreAPianoPart
  >>
  \layout {
  }
}

Everything coming together:

\version "2.24.1"

% Define a property that can be attached to grobs.
% This properly will name a group of staves that must be active together
#(set-object-property! 'keep-with 'backend-type? symbol?)

%
% Use the keep-with property to replace the "keep-alive-with"
% list of a grob with the collected staffs that have the same keep-with value
#(define (keep-with-engraver context)
   (let ((group-spanners '()))
     (make-engraver
      (acknowledgers
       ((hara-kiri-group-spanner-interface engraver grob source-engraver)
        (set! group-spanners (append group-spanners (list grob)))
        )
       )
      ((finalize engraver)
       (map (lambda (grob)
              (let ((keep-with (ly:grob-property grob 'keep-with))
                    (keep-list '()))
                (begin
                 (map (lambda (grob-other)
                        (if (not(eqv? grob grob-other))
                            (let ((keep-with-other (ly:grob-property grob-other 'keep-with)))
                              (if (eq? keep-with keep-with-other)
                                  (set! keep-list (append keep-list (list grob-other)))
                                  ))
                            )
                        ) group-spanners)
                 (ly:grob-set-object! grob 'keep-alive-with (ly:grob-list->grob-array keep-list))
                 )))
            group-spanners)
       ))))

join = {
  \tag #'split {\set Staff.keepAliveInterfaces = #'() }
  \tag #'joined {\unset Staff.keepAliveInterfaces }
}
separate = {
  \tag #'joined {\set Staff.keepAliveInterfaces = #'() }
  \tag #'split {\unset Staff.keepAliveInterfaces }
}

\paper {
  #(set-paper-size "a4")
  left-margin = 3\cm
}

global = {
  \key c \major
  \time 4/4
}

scoreASopranoChoirI = \relative c'' {
  \global
  \separate
  \textMark "Choir I"
  R1*20
  \repeat unfold 10 { g4 a f c }
  \repeat unfold 50 f4
  \join
  \textMark "Choir I && II"
  \repeat unfold 50 c4
  \separate
  \textMark "Choir I"
  \repeat unfold 50 f4
}

scoreAAltoChoirI = \relative c' {
  \global
  \separate
  R1*20
  \repeat unfold 10 { g'4 a f c }
  \repeat unfold 50 a4
  \join
  \repeat unfold 50 e'4
  \separate
  \repeat unfold 50 a4
}


scoreASopranoChoirII = \relative c'' {
  \global
  \separate
  \textMark "Choir II"
  R1*30
  %\repeat unfold 50 a4
  \repeat unfold 50 r4
  \join
  \repeat unfold 50 c4
  \separate
  \textMark "Choir II"
  \repeat unfold 50 a4
}

scoreAAltoChoirII = \relative c' {
  \global
  \join
  R1*30
  \separate
  \repeat unfold 50 c4
  \join
  \repeat unfold 50 e4
  \separate
  \repeat unfold 50 c4
}

scoreARight = \relative c'' {
  \global
  \repeat unfold 30 { g4 a f c }
  \repeat unfold 50 e4
  \repeat unfold 50 c4
  \repeat unfold 50 e4
}

scoreALeft = \relative c' {
  \global
  \repeat unfold 30 { g4 a f c }
  \repeat unfold 50 a4
  \repeat unfold 50 e4
  \repeat unfold 50 a4
}

scoreAChoirIPart = \new ChoirStaff \with {
  instrumentName = "Choir I"
  shortInstrumentName = ""  
    \override VerticalAxisGroup.remove-empty = ##t
    \override VerticalAxisGroup.remove-layer = 1
    \override VerticalAxisGroup.keep-with = #'choirOne
} <<
  \new Staff \with {
    instrumentName = "Soprano"
    shortInstrumentName = "S.1"
  } \new Voice = "soprano" \keepWithTag #'split \scoreASopranoChoirI
  \new Staff \with {
    instrumentName = "Alto"
    shortInstrumentName = "A.1"
  } \new Voice = "alto" \keepWithTag #'split \scoreAAltoChoirI
>>

scoreAChoirIIPart = \new ChoirStaff \with {
  instrumentName = "Choir II"
  shortInstrumentName = ""
    \override VerticalAxisGroup.remove-empty = ##t
    \override VerticalAxisGroup.remove-layer = 1
    \override VerticalAxisGroup.keep-with = #'choirTwo
} <<
  \new Staff \with {
    instrumentName = "Soprano"
    shortInstrumentName = "S.2"
  } \new Voice = "soprano" \keepWithTag #'split \scoreASopranoChoirII
  \new Staff \with {
    instrumentName = "Alto"
    shortInstrumentName = "A.2"
  } \new Voice = "alto" \keepWithTag #'split \scoreAAltoChoirII
>>

scoreAChoirIandIIPart = \new ChoirStaff \with {
  instrumentName = "Choir I & II"
  shortInstrumentName = "Ch. I & II"
    \override VerticalAxisGroup.remove-empty = ##t
    \override VerticalAxisGroup.remove-layer = 2
    \override VerticalAxisGroup.keep-with = #'choirOneAndTwo
} <<
  \new Staff \with {
    instrumentName = "Soprano"
    shortInstrumentName = "S. I & II"
  } \new Voice = "soprano" \keepWithTag #'joined \scoreASopranoChoirI
  \new Staff \with {
    instrumentName = "Alto"
    shortInstrumentName = "A. I & II"
  } \new Voice = "alto" \keepWithTag #'joined \scoreAAltoChoirI
>>

scoreAPianoPart = \new PianoStaff \with {
  instrumentName = "Piano"
  shortInstrumentName = "Pno."
} <<
  \new Staff = "right" \with {
  } \scoreARight
  \new Staff = "left" \with {
  } { \clef bass \scoreALeft }
>>

\layout {
  \context {
    \Staff
  }
}


\score {
  <<
    \new ChoirStaff \with {
      \consists Keep_alive_together_engraver
      \consists #keep-with-engraver
    }
    <<
      \set ChoirStaff.systemStartDelimiter = #'SystemStartBar
      \scoreAChoirIPart
      \scoreAChoirIIPart
      \scoreAChoirIandIIPart
    >>
    \scoreAPianoPart
  >>
  \layout {
  }
}

Page1 from Page2

0

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.