Hi all, I posted some earlier versions of this Scheme engraver to the user list. It provides an intuitive user interface to control whether shared or separate staves are printed, and should work for combinations of 4 or more parts. The examples are in the tests.ly file attached, while the engraver code is in the divisi_engraver.ly file.
This engraver lives at the StaffGroup level and during process-music reads from one context property, then writes to keepAliveInterfaces of each child context. Because child contexts are evaluated first for process-music, in order to get this to work I needed to patch Axis_group_engraver so that it will wait to read keepAliveInterfaces and use the updated value. Currently, Axis_group_engraver reads keepAliveInterfaces into an internal variable during process-music, then uses the stored value during acknowledge-grob. In the patched version, it waits to read keepAliveInterfaces during acknowledge-grob. Reading the property every grob is probably a performance hit, but I don't know how big. Saul
\version "2.19.82" \include "divisi_engraver.ly" \paper { short-indent = 0.5\cm } \layout { \context { \StaffGroup \consists \Condense_staves_engraver } \context { \Staff \override VerticalAxisGroup.remove-empty = ##t \override VerticalAxisGroup.remove-first = ##t keepAliveInterfaces = #'() } } % TWO PARTS global = { s1 \break s1 } I = { \context Staff = "1" { \set Staff.combineWithNext = ##f } c'4 4 4 4 \context Staff = "1" { \set Staff.combineWithNext = ##t } c'4 4 4 4 } II = { e2 2 2 2 } \new StaffGroup << \new Staff = "1+2" \with { sharingParts = #'(1 2) instrumentName = "1 2" shortInstrumentName = "1 2" } << \global \I \II >> \new Staff = "1" \with { sharingParts = #'(1) instrumentName = "1" shortInstrumentName = "1" } << \global \I >> \new Staff = "2" \with { sharingParts = #'(2) instrumentName = "2" shortInstrumentName = "2" } << \global \II >> >> % FOUR PARTS global = { s1 \break s1 \break s1 \break s1 \break s1 \break s1 \break s1 \break s1 } I = { \context Staff = "1" { \set Staff.combineWithNext = ##t } c'4 4 4 4 c'4 4 4 4 c'4 4 4 4 c'4 4 4 4 \context Staff = "1" { \set Staff.combineWithNext = ##f } c'4 4 4 4 c'4 4 4 4 c'4 4 4 4 c'4 4 4 4 } II = { \context Staff = "2" { \set Staff.combineWithNext = ##t } a4 4 4 4 a4 4 4 4 \context Staff = "2" { \set Staff.combineWithNext = ##f } a4 4 4 4 a4 4 4 4 \context Staff = "2" { \set Staff.combineWithNext = ##t } a4 4 4 4 a4 4 4 4 \context Staff = "2" { \set Staff.combineWithNext = ##f } a4 4 4 4 a4 4 4 4 } III = { \context Staff = "3" { \set Staff.combineWithNext = ##t } f4 4 4 4 \context Staff = "3" { \set Staff.combineWithNext = ##f } f4 4 4 4 \context Staff = "3" { \set Staff.combineWithNext = ##t } f4 4 4 4 \context Staff = "3" { \set Staff.combineWithNext = ##f } f4 4 4 4 \context Staff = "3" { \set Staff.combineWithNext = ##t } f4 4 4 4 \context Staff = "3" { \set Staff.combineWithNext = ##f } f4 4 4 4 \context Staff = "3" { \set Staff.combineWithNext = ##t } f4 4 4 4 \context Staff = "3" { \set Staff.combineWithNext = ##f } f4 4 4 4 } IV = { \repeat unfold 8 { d4 4 4 4 } } \new StaffGroup << \new Staff = "1+2+3+4" \with { sharingParts = #'(1 2 3 4) instrumentName = "1 2 3 4" shortInstrumentName = "1 2 3 4" } << \global \I \II \III \IV >> \new Staff = "1+2+3" \with { sharingParts = #'(1 2 3) instrumentName = "1 2 3" shortInstrumentName = "1 2 3" } << \global \I \II \III >> \new Staff = "1+2" \with { sharingParts = #'(1 2) instrumentName = "1 2" shortInstrumentName = "1 2" } << \global \I \II >> \new Staff = "1" \with { sharingParts = #'(1) instrumentName = "1" shortInstrumentName = "1" } << \global \I >> \new Staff = "2+3+4" \with { sharingParts = #'(2 3 4) instrumentName = "2 3 4" shortInstrumentName = "2 3 4" } << \global \II \III \IV >> \new Staff = "2+3" \with { sharingParts = #'(2 3) instrumentName = "2 3" shortInstrumentName = "2 3" } << \global \II \III >> \new Staff = "2" \with { sharingParts = #'(2) instrumentName = "2" shortInstrumentName = "2" } << \global \II >> \new Staff = "3+4" \with { sharingParts = #'(3 4) instrumentName = "3 4" shortInstrumentName = "3 4" } << \global \III \IV >> \new Staff = "3" \with { sharingParts = #'(3) instrumentName = "3" shortInstrumentName = "3" } << \global \III >> \new Staff = "4" \with { sharingParts = #'(4) instrumentName = "4" shortInstrumentName = "4" } << \global \IV >> >>
axis_patch.patch
Description: Binary data
\version "2.19.82" % From define-context-properties.scm #(define (translator-property-description symbol type? description) (if (not (and (symbol? symbol) (procedure? type?) (string? description))) (throw 'init-format-error)) (if (not (equal? #f (object-property symbol 'translation-doc))) (ly:error (_ "symbol ~S redefined") symbol)) (set-object-property! symbol 'translation-type? type?) (set-object-property! symbol 'translation-doc description) (set! all-translation-properties (cons symbol all-translation-properties)) symbol) #(translator-property-description 'sharingParts list? "List of consecutive ints, indices of parts sharing this staff.") #(translator-property-description 'combineWithNext boolean? "Is it okay for this music to share a staff with the music in the next staff?") %%% #(define (segment pred lst) "Segments a list into sublists, such that all elements satisfy pred except the last element of each sublist." (fold-right (lambda (x y) (if (and (pred x) (pair? y)) (cons (cons x (car y)) (cdr y)) (cons (list x) y))) '() lst)) % #(display (segment cdr '((1 . #f) (2 . #f) (3 . #f)))) #(define (proper-subset? x y) "Is y a proper subset of x?" (and (every (lambda (x) x) (map (lambda (ely) (member ely x)) y)) (< (length y) (length x)))) Condense_staves_engraver = #(lambda (ctx) (let* ((all-hk-spanners '()) (all-staves '()) (solo-staves '())) (make-engraver (acknowledgers ((hara-kiri-group-spanner-interface engraver grob source-engraver) ; We need an alist of child contexts, but getting them this way ; means the engraver can't operate on the first measure. ; Not sure what the proper way to get them is? (let* ((child-ctx (ly:translator-context source-engraver)) (child-parts (ly:context-property child-ctx 'sharingParts))) (set! all-staves (assoc-set! all-staves child-parts child-ctx)) (set! all-hk-spanners (assoc-set! all-hk-spanners child-parts grob)) ; Build alist of just the staves for a single part. (if (and (eq? 1 (length child-parts)) (not (assoc child-parts solo-staves))) (set! solo-staves (merge solo-staves (acons child-parts child-ctx '()) (lambda (x y) (< (caar x) (caar y))))))))) ((process-music translator) (let* ( ; Build alist of index: combineWithNext value for solo staves (combine-which-parts (fold-right (lambda (kv result) (acons (car kv) (ly:context-property (cdr kv) 'combineWithNext) result)) '() solo-staves)) ; Split the list by which parts can be combined ; Then just keep the part indices (groups (map (lambda (sublst) (map caar sublst)) (segment cdr combine-which-parts))) ; Use the index lists to select staves (live-ctxs (if (pair? groups) (map (lambda (k) (assoc-get k all-staves)) groups) '())) ) (display (ly:context-current-moment ctx)) ; (display live-ctxs) (display groups) ; Set keepAliveInterfaces = '() for all staves (map (lambda (kv) (ly:context-set-property! (cdr kv) 'keepAliveInterfaces '())) all-staves) ; Set keepAliveInterfaces to the default for just the staves ; Corresponding to the current part combinations (map (lambda (ctx) (ly:context-unset-property ctx 'keepAliveInterfaces)) live-ctxs))) ((finalize translator) (let* ((get-subgroups (lambda (me) (filter (lambda (them) (proper-subset? (car me) (car them))) all-hk-spanners))) (set-my-enemies (lambda (me) (map (lambda (enemy) (ly:pointer-group-interface::add-grob (cdr me) 'make-dead-when (cdr enemy))) (get-subgroups me))))) (map set-my-enemies all-hk-spanners) )))))
_______________________________________________ lilypond-devel mailing list lilypond-devel@gnu.org https://lists.gnu.org/mailman/listinfo/lilypond-devel