Hi all,

Not sure if this should be on the User mailing list or one of the others.

The attached code replicates a strange behavior I have encountered several
different times, where seemingly random and irrelevant musical details
cause staves to incorrectly disappear when using the divisi snippet I
posted a few months ago. I also tested this with variants of Thomas
Morley's version of the snippet, so I'm convinced it's not anything to do
with the snippet itself.

In my attached example, notice that the Oboe 2 staff disappears when it
shouldn't, but if you remove the hairpin from the flute part, the Oboe 2
staff reappears. I believe that this is caused by the Oboe 2 staff having
'make-dead-when set to the combined staff, which itself has 'make-dead-when
set to Oboe 1. Hara_kiri_group_spanner::request_suicide checks each foe
staff for is_live and request_suicide_alone, but not request_suicide (to
avoid infinite loops, I guess). So if Oboe1+2 becomes dead due to
'make-dead-when, when on its own it would have stayed alive, it may either
appear alive or dead to Oboe 2, depending on which staff happens to be
evaluated first.

To avoid this race condition, one has to avoid setting 'keep-dead-when to
point to other staves that themselves have 'keep-dead-when set. In other
words, for some staves to be masters, others to be slaves. Which is
probably a good idea anyway, but it leaves a gap in functionality when it
comes to 3+ stave divisi. For 3 staves, you have:

1+2+3:  Alive if BOTH 1 and 3 are dead
1+2: Alive if 1 is dead AND 3 is alive
2+3: Alive if 1 is alive AND 3 is dead
1: Master staff
2: Alive if BOTH 1 and 3 are alive
3: Master staff

With the semantics of the existing 'make-dead-when interface, this is
doable by making, for instance, 1+2 and 2+3 dead whenever 1+2+3 is alive,
but as I noticed, this type of inter-dependency leads to unpredictable
behavior. A better way forward would be to expand the 'make-dead-when
mechanism to support a complete variety of logical conditions, such as ALL
vs. ANY and combinations of alive and dead conditions.

Saul
\version "2.19.82"
\language "english"

remove-if-sibling = #(define-scheme-function (offsets) (list?)
           (lambda (grob)
             (let* (
                     ;; The parent of a VerticalAxisGroup is a VerticalAlignment
                     (parent (ly:grob-parent grob 1))
                     ;; Get the children VerticalAxisGroups of the parent
                     (siblings (ly:grob-object parent 'elements))
                     (siblings-list
                      (if (ly:grob-array? siblings)
                          (ly:grob-array->list siblings)
                          '()))
                     ;; Find the siblings above or below me by offsets
                     (my-vindex (ly:grob-get-vertical-axis-group-index grob))
                     (enemy-indices (map (lambda (offset) (+ offset my-vindex)) offsets))
                     (enemy-vaxis? (lambda (v) (member (ly:grob-get-vertical-axis-group-index v)
                                                 enemy-indices)))
                     (enemy-vaxes
                      (filter enemy-vaxis? siblings-list))
                     )
               ;; Suicide if an enemy sibling is alive
               (map
                (lambda (enemy-vaxis)
                  (ly:pointer-group-interface::add-grob grob 'make-dead-when enemy-vaxis))
                enemy-vaxes)
               )
             )
           )

global = {
  s1
  \break
  s1*3
  \break
  s1
  \bar "||"
}

flute = {
  \clef treble
  \set Voice.midiInstrument = "flute"
  R1*3 |
  % Notice that without the hairpin, the Ob. 2 staff doesn't disappear
  c''1\< |
  c''1 <>\! |
}

oboeI = {
  \clef treble
  \set Voice.midiInstrument = "oboe"
  \context Staff = "Ob1" { \unset Staff.keepAliveInterfaces }
  R1 |
  c''1 |
  R1 |
  \context Staff = "Ob1" { \set Staff.keepAliveInterfaces = #'() }
  R1*2 |
}

oboeII = {
  \clef treble
  \set Voice.midiInstrument = "oboe"
  R1*2 |
  c'1 |
  R1*2 |
}

\score {
  <<
    \new Staff = "Fl1" \with {
      instrumentName = "Flute"
      shortInstrumentName = "Fl."
    } << \global \flute >>
    \new StaffGroup \with {
      instrumentName = "Oboe"
      shortInstrumentName = "Ob."
      systemStartDelimiter = #'SystemStartSquare
      \override SystemStartSquare.collapse-height = #5
    } <<
      \new Staff = "Ob1+2" \with {
        instrumentName = \markup\right-column {"1" "2"}
        shortInstrumentName = \markup\right-column {"1" "2"}
        \override VerticalAxisGroup.before-line-breaking = \remove-if-sibling #'(1)
      } { <> << \global \partcombine \oboeI \oboeII >> }
      \new Staff = "Ob1" \with {
        instrumentName = "1"
        shortInstrumentName = "1"
        \override VerticalAxisGroup.remove-empty = ##t
        \override VerticalAxisGroup.remove-first = ##t
      } << \global \oboeI >>
      \new Staff = "Ob2" \with {
        instrumentName = "2"
        shortInstrumentName = "2"
        \override VerticalAxisGroup.before-line-breaking = \remove-if-sibling #'(-2)
      } << \global \oboeII >>
    >>
  >>
  \layout {
    \context {
      \Score
      \override InstrumentName.self-alignment-X = #1
      \override InstrumentName.padding = #3
    }
    \context {
      \Staff
      \override InstrumentName.padding = 0.3
    }
  }
}
_______________________________________________
lilypond-user mailing list
lilypond-user@gnu.org
https://lists.gnu.org/mailman/listinfo/lilypond-user

Reply via email to