2018-08-06 21:40 GMT+02:00 Thomas Morley <thomasmorle...@gmail.com>:
> 2018-08-05 18:14 GMT+02:00 Thomas Morley <thomasmorle...@gmail.com>:
>
>> Inspired by your work I come up with the attached.
>
> Attached an improved and simplified version.
> It's tested with 2/3/4-voices divisi, all in one score.
> Although not tested, I see no reason why it shouldn't work with even
> more voices.


Further improvements.

It's now possible to omit 'catch-me, _iff_ single instrument-staves
and divisi-staves belong exculsively to the same container-context
like StaffGroup, GrandStaff, ChoirStaff or PianoStaff with removed
"Keep_alive_together_engraver", which is most likely always the case.
For ungrouped single/divisi 'catch-me is still neccesary.

Cheers,
  Harm
\version "2.19.82"

\pointAndClickOff

%% relies on:
%%
%% VerticalAxisGroup.details.catch-me
%% VerticalAxisGroup.details.combined
%%
%% About `catch-me`
%%   Inside of container-contexts like StaffGroup, GrandStaff, ChoirStaff or
%%   PianoStaff with removed "Keep_alive_together_engraver"
%%   `catch-me` may be omitted.
%%   To get divisis work outside of such containers `catch-me` needs to be set
%%   to a value matching one of the optional arguments of the divisis-procedure
%%     Currently we use strings for `catch-me` and the arguments of the
%%     divisis-procedure, a symbol would work as well.
%%
%% About `combined`
%%   `combined` labels divisi-staffs. It is supposed to be a list of positive
%%   integers, representing the combined instruments.
%%   p.e. To combine flute-III and flute-IV, set `combined` to '(3 4)
%%
%%
%% More inline comments below
%%
%%
%% Limitations:
%%   Staves can't be started/ended mid-line.
%% TODOs
%%   Killing/awakening relies on the order single instrument-staves are entered.
%%   Is it safe to assume the order of lists with VerticalAxisGroups will
%%   not be confused?

%% c/p from lily-library.scm
#(define (split-at-predicate pred lst)
  "Split LST into two lists at the first element that returns #f for
  (PRED previous_element element).  Return the two parts as a pair.
  Example: (split-at-predicate < '(1 2 3 2 1)) ==> ((1 2 3) . (2 1))"
  (let ((i (and (pair? lst)
                (list-index (lambda (x y) (not (pred x y)))
                            lst
                            (cdr lst)))))
    (if i
        (call-with-values
            (lambda () (split-at lst (1+ i)))
          cons)
        (list lst))))

#(define (sort-vags vags)
  ;; Returns sublists of VAGs in same StaffGrouper
  (define (helper l1 l2)
    (if (null? l1)
        (reverse l2)
        (let ((splitted-l1
                (split-at-predicate
                  (lambda (x y)
                    (equal?
                      (ly:grob-object x 'staff-grouper)
                      (ly:grob-object y 'staff-grouper)))
                   l1)))
          (helper (cdr splitted-l1) (cons (car splitted-l1) l2)))))
  (helper vags '()))

#(define (select-vags vags)
  ;; Return two sublists containing VerticalAxisGroups with and without
  ;; setted 'combined.
  (call-with-values
    (lambda ()
      (partition
        (lambda (vag)
          (pair?
            (assoc-get
              'combined (ly:grob-property vag 'details) '())))
        vags))
    (lambda (x y) (list x y))))

#(define (kill-selected-vags sorted-vags)
  ;; `sorted-vags` is supposed to be a list of two sublists, containing
  ;; VerticalAxisGroups for divisi and non-divisi-staves
  (if (pair? (car sorted-vags))
      (for-each
        (lambda (i)
          ;; if divisi staves are alive, kill selected other staves
          ;; relying on the combined-values
          (for-each
            (lambda (x)
              (ly:pointer-group-interface::add-grob
                x 'make-dead-when (list-ref (car sorted-vags) i)))
            ;; get a list of VerticalAxisGroups used with
            ;; divisi-staves
            (map
              (lambda (index) (list-ref (cadr sorted-vags) (1- index)))
              ;; get the 'combined-value from 'details, which is
              ;; supposed to be a list.
              ;; TODO 'combined is read here and in `splitted` above.
              ;;      Find a method to spare one go
              (assoc-get
                'combined
                (ly:grob-property
                  (list-ref (car sorted-vags) i) 'details)
                '()))))
        (iota (length (car sorted-vags))))))

#(define (divisis . divisi-groups)
  ;; Kill selected other Staffs, if divisi-Staffs are alive.
  ;; Do it separatly for every instrument-group specified by `divisi-groups`.
  (lambda (grob)
    ;; `grob` is supposed to be `VerticalAlignment`, per default living in
    ;; Score-context
    (let* (;; Get all `VerticalAxisGroup`s from `VerticalAlignment`
           ;; Those are the relevant grobs to look at to make others dead
           ;; or not
           (vags-array (ly:grob-object grob 'elements))
           (vags-list ;; empty lists removed
             (if (null? vags-array)
                 '()
                 (remove null? (ly:grob-array->list vags-array))))
           ;; return two sublists containing VerticalAxisGroups with and
           ;; without setted 'catch-me
           (labeled-vags-list
             (call-with-values
               (lambda ()
                 (partition
                   (lambda (vag)
                     (let ((label
                             (assoc-get
                               'catch-me (ly:grob-property vag 'details) #f)))
                       (and label
                            (member label divisi-groups))))
                   vags-list))
               (lambda (x y) (list x y))))
           ;; remove entries without 'staff-grouper from unlabeled VAGs
           (cleared-unlabeled-vags-list
             (remove
               (lambda (vag) (null? (ly:grob-object vag 'staff-grouper)))
               (cadr labeled-vags-list)))
           ;; get sublists of unlabeled VAGs with same StaffGrouper from
           ;; `cleared-unlabeled-vags-list`
           (grouped-unlabeled-vags
             (sort-vags cleared-unlabeled-vags-list))
           ;; labeled VAGs are grouped into sublists according to 'catch-me
           (grouped-labeled-vags
             (map
               (lambda (group)
                 (filter
                   (lambda (vag)
                     (equal?
                       group
                       (assoc-get 'catch-me (ly:grob-property vag 'details))))
                   (car labeled-vags-list)))
                divisi-groups)))

      (for-each
        (lambda (vags)
          (let* ((splitted (select-vags vags)))
            (kill-selected-vags splitted)))
        (append
          grouped-unlabeled-vags
          grouped-labeled-vags)))))

%% Short-cuts to switch on/off Staves
%% They should be inserted at line-breaks.
switchOff = \set Staff.keepAliveInterfaces = #'()
switchOn = \unset Staff.keepAliveInterfaces

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% EXAMPLE
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

#(set-default-paper-size "a2")
#(set-global-staff-size 15)

\paper {
  indent = 20
  short-indent = 20
}

%%
%% the actual music
%%
fluteI = \repeat unfold 12 c''2
fluteII = \repeat unfold 12 e'2

trumpetI = \repeat unfold 6 { g'4 c'' e'' c'' }
trumpetII = \repeat unfold 6 { e'4 g' c'' g' }
trumpetIII = \repeat unfold 6 { c'4 e' g' e' }

corI = \repeat unfold 6 { c''2 c''4 c'' }
corII = \repeat unfold 6 { g'2 g'4 g' }
corIII = \repeat unfold 6 { e'2 e'4 e' }
corIV = \repeat unfold 6 { c'2 c'4 c' }

%%
%% controlling divisis
%%   colors only for better viewing; ofcourse coding could be more concise
%%   p.e. deleting redundant \break-commands, they should likely be part of an
%%   own voice anyway; using s1*x etc
%%
%% divisi-Staffs are switched off per default, they may be switched on as wished
%%
%% flutes
combined-flutes-I-II = {
  \override NoteHead.color = #green
%% 1
  \switchOff
  s1 \break
%% 2
  \switchOn
  s1 \break
%% 3
  s1 \break
%% 4
  \switchOff
  s1 \break
%% 5
  \switchOn
  s1 \break
%% 6
  s1 \break
}

%% trumpets
combined-trumpets-I-II-III = {
  \override NoteHead.color = #red
%% 1
  \switchOff
  s1 \break
%% 2
  s1 \break
%% 3
  \switchOn
  s1 \break
%% 4
  \switchOff
  s1 \break
%% 5
  s1 \break
%% 6
  s1 \break
}

combined-trumpets-I-II = {
  \override NoteHead.color = #(x11-color 'orange)
%% 1
  \switchOff
  s1 \break
%% 2
  s1 \break
%% 3
  s1 \break
%% 4
  \switchOn
  s1 \break
%% 5
  \switchOff
  s1 \break
%% 6
  s1 \break
}

combined-trumpets-II-III = {
  \override NoteHead.color = #yellow
%% 1
  \switchOff
  s1 \break
%% 2
  s1 \break
%% 3
  s1 \break
%% 4
  s1 \break
%% 5
  s1 \break
%% 6
  \switchOn
  s1 \break
}

%% cors
combined-cors-I-II-III-IV = {
  \override NoteHead.color = #red
%% 1
  \switchOff
  s1 \break
%% 2
  s1 \break
%% 3
  \switchOn
  s1 \break
%% 4
  \switchOff
  s1 \break
%% 5
  s1 \break
%% 6
  s1 \break
}

combined-cors-I-II = {
  \override NoteHead.color = #(x11-color 'orange)
%% 1
  \switchOff
  \switchOn
  s1 \break
%% 2
  \switchOff
  s1 \break
%% 3
  s1 \break
%% 4
  \switchOn
  s1 \break
%% 5
  \switchOff
  s1 \break
%% 6
  s1 \break
}

combined-cors-II-III = {
  \override NoteHead.color = #yellow
%% 1
  \switchOff
  s1 \break
%% 2
  s1 \break
%% 3
  s1 \break
%% 4
  s1 \break
%% 5
  s1 \break
%% 6
  \switchOn
  s1 \break
}

combined-cors-III-IV = {
  \override NoteHead.color = #(x11-color 'orange)
%% 1
  \switchOff
  s1 \break
%% 2
  s1 \break
%% 3
  s1 \break
%% 4
  \switchOn
  s1 \break
%% 5
  \switchOff
  s1 \break
%% 6
  s1 \break
}

combined-cors-II-III-IV = {
  \override NoteHead.color = #cyan
%% 1
  \switchOff
  s1 \break
%% 2
  s1 \break
%% 3
  s1 \break
%% 4
  s1 \break
%% 5
  \switchOn
  s1 \break
%% 6
  \switchOff
  s1 \break
}

%% For divisi instruments initiate Staff-contexts for every single instrument.
%% Set `VerticalAxisGroup.details.catch-me` appropiate.
%% If those staves are part of a StaffGroup, GrandStaff, ChoirStaff or
%% PianoStaff with removed "Keep_alive_together_engraver" 'catch-me may left
%% unset.
%%
%% Initiate every desired divisi-Staff.
%% Staves meant for divisi should be labed with
%% `VerticalAxisGroup.details.combined` set to a list of positive integers,
%% representing the combined voices.
%% Set appropriate `catch-me` as well, it may be omitted as described above.
%% Apply \RemoveAllEmptyStaves to every divisi-Staff.
%%
%% While divisi-staves are alive (controlled by switchOn/Off, see above)
%% other selected staves are killed and restored when the divisi dies.
%% This relies on the values used for 'combined
%%
%% This is initiated by a call at Score-level to
%% \override VerticalAlignment.before-line-breaking = #(divisis)
%%
%% If divisis are used outside of container-contexts:
%% StaffGroup, GrandStaff, ChoirStaff or PianoStaff with removed
%% "Keep_alive_together_engraver"
%% `divisis` may get additional optional arguments like: (divisis "flutes")
%% This should ofcourse match the setted `catch-me` value.
\score {
  <<
    %% A Staff which should not becomes part of any divisi
    \new Staff = "up"
      \with {
        instrumentName = "Picc"
        shortInstrumentName = "picc"
      }
      { \repeat unfold 48 c''8 }

    %% FLUTES
    %% To get divisis work outside of container-contexts one needs to set
    %% `catch-me`
%    \new StaffGroup
%      \with {
%        \override SystemStartBracket.collapse-height = 1
%        \override SystemBar.collapse-height = 1
%        instrumentName = \markup { \rotate #90 "FLUTES" \hspace #16 }
%        shortInstrumentName = \markup { \rotate #90 "FLUTES" \hspace #16 }
%      }
      <<
      \new Staff = "fl1"
        \with {
          instrumentName = "Fl 1"
          shortInstrumentName = "Fl 1"
          \override VerticalAxisGroup.details.catch-me = "flutes"
        }
        \fluteI
      \new Staff = "fl2"
        \with {
          instrumentName = "Fl 2"
          shortInstrumentName = "Fl 2"
          \override VerticalAxisGroup.details.catch-me = "flutes"
        }
        \fluteII
      \new Staff = "fl1+2"
        \with {
          instrumentName = "Fl 1+2"
          shortInstrumentName = "Fl 1+2"
          \RemoveAllEmptyStaves
          \override VerticalAxisGroup.details.catch-me = "flutes"
          \override VerticalAxisGroup.details.combined = #'(1 2)
        }
        \new Voice
          <<
            \combined-flutes-I-II
            \fluteI \fluteII
          >>
    >>

    %% TRUMPETS
    \new StaffGroup
      \with {
        \override SystemStartBracket.collapse-height = 1
        instrumentName = \markup { \rotate #90 "TUMPETS" \hspace #16 }
        shortInstrumentName = \markup { \rotate #90 "TRUMPETS" \hspace #16 }
      }
      <<
      \new Staff = "tr1"
        \with {
          instrumentName = "Tr 1"
          shortInstrumentName = "Tr 1"
        }
        \trumpetI
      \new Staff = "tr2"
        \with {
          instrumentName = "Tr 2"
          shortInstrumentName = "Tr 2"
        }
        \trumpetII
      \new Staff = "tr3"
        \with {
          instrumentName = "Tr 3"
          shortInstrumentName = "Tr 3"
        }
        \trumpetIII
      \new Staff = "tr1+2+3"
        \with {
          instrumentName = "Tr 1+2+3"
          shortInstrumentName = "Tr 1+2+3"
          \RemoveAllEmptyStaves
          \override VerticalAxisGroup.details.combined = #'(1 2 3)
        }
        \new Voice
          <<
            \combined-trumpets-I-II-III
            \trumpetI \trumpetII \trumpetIII
          >>
      \new Staff = "tr1+2"
        \with {
          instrumentName = "Tr 1+2"
          shortInstrumentName = "Tr 1+2"
          \RemoveAllEmptyStaves
          \override VerticalAxisGroup.details.combined = #'(1 2)
          alignAboveContext = "tr3"
        }
        \new Voice
          <<
            \combined-trumpets-I-II
            \trumpetI \trumpetII
          >>
      \new Staff = "tr2+3"
        \with {
          instrumentName = "Tr 2+3"
          shortInstrumentName = "Tr 2+3"
          \RemoveAllEmptyStaves
          \override VerticalAxisGroup.details.combined = #'(2 3)
          alignBelowContext = "tr1+2"
        }
        \new Voice
          <<
            \combined-trumpets-II-III
            \trumpetII \trumpetIII
          >>
    >>

    %% CORS
    \new StaffGroup
      \with {
        \override SystemStartBracket.collapse-height = 1
        instrumentName = \markup { \rotate #90 "CORS" \hspace #16 }
        shortInstrumentName = \markup { \rotate #90 "CORS" \hspace #16 }
      }
      <<
      \new Staff = "c1"
        \with {
          instrumentName = "Cor 1"
          shortInstrumentName = "Cor 1"
        }
        \corI
      \new Staff = "c2"
        \with {
          instrumentName = "Cor 2"
          shortInstrumentName = "Cor 2"
        }
        \corII
      \new Staff = "c3"
        \with {
          instrumentName = "Cor 3"
          shortInstrumentName = "Cor 3"
        }
        \corIII
      \new Staff = "c4"
        \with {
          instrumentName = "Cor 4"
          shortInstrumentName = "Cor 4"
        }
        \corIV
      \new Staff = "c1+2+3+4"
        \with {
          instrumentName = "Cors 1+2+3+4"
          shortInstrumentName = "Cors 1+2+3+4"
          \RemoveAllEmptyStaves
          \override VerticalAxisGroup.details.combined = #'(1 2 3 4)
        }
        \new Voice
          <<
            \combined-cors-I-II-III-IV
            \corI \corII \corIII \corIV
          >>
      \new Staff = "c1+2"
        \with {
          instrumentName = "Cors 1+2"
          shortInstrumentName = "Cors 1+2"
          \RemoveAllEmptyStaves
          \override VerticalAxisGroup.details.combined = #'(1 2)
          alignAboveContext = "c3"
        }
        \new Voice
          <<
            \combined-cors-I-II
            \corI \corII
          >>
      \new Staff = "c2+3"
        \with {
          instrumentName = "Cors 2+3"
          shortInstrumentName = "Cors 2+3"
          \RemoveAllEmptyStaves
          \override VerticalAxisGroup.details.combined = #'(2 3)
          alignAboveContext = "c4"
        }
        \new Voice
          <<
            \combined-cors-II-III
            \corII \corIII
          >>
      \new Staff = "c3+4"
        \with {
          instrumentName = "Cors 3+4"
          shortInstrumentName = "Cors 3+4"
          \RemoveAllEmptyStaves
          \override VerticalAxisGroup.details.combined = #'(3 4)
          alignBelowContext = "c1+2"
        }
        \new Voice
          <<
            \combined-cors-III-IV
            \corIII \corIV
          >>
      \new Staff = "c2+3+4"
        \with {
          instrumentName = "Cors 2+3+4"
          shortInstrumentName = "Cors 2+3+4"
          \RemoveAllEmptyStaves
          \override VerticalAxisGroup.details.combined = #'(2 3 4)
          alignBelowContext = "c1"
        }
        \new Voice
          <<
            \combined-cors-II-III-IV
            \corII \corIII \corIV
          >>
    >>

    %% A Staff which should not becomes part of any divisi
    \new Staff = "bass"
      \with {
        instrumentName = "Bass"
        shortInstrumentName = "Bass"
      }
      { \clef bass \repeat unfold 6 c1 }
  >>

  \layout {
    \context {
      \Score
      \override VerticalAlignment.before-line-breaking =
        %#(divisis "flutes" "trumpets" "cors")
        %#(divisis)
        #(divisis "flutes")
    }
  }
}
_______________________________________________
lilypond-user mailing list
lilypond-user@gnu.org
https://lists.gnu.org/mailman/listinfo/lilypond-user

Reply via email to