\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") } } }