1 % DO NOT EDIT this file manually; it is automatically
2 % generated from Documentation/snippets/new
3 % Make any changes in Documentation/snippets/new/
4 % and then run scripts/auxiliar/makelsr.py
6 % This file is in the public domain.
7 %% Note: this file works from version 2.15.35
11 lsrtags = "staff-notation, tweaks-and-overrides, contexts-and-engravers"
12 texidoc = "This file defines and demonstrates a scheme engraver that
13 connects stems across staves. The stem length need not be specified, as
14 the code takes care of the variable distance between noteheads and staves."
15 doctitle = "Stem cross staff engraver"
20 A new stem (referred to as span in the code) is created to connect the
21 original stems. The original stems are made transparent.
23 The span is created as a child of the "root" stem, that is the stem
24 connected to a notehead with the end that is not to be extended.
26 Both stem directions are supported. Connecting more than two stems is
30 % Values are close enough to ignore the difference
31 #(define (close-enough? x y)
32 (< (abs (- x y)) 0.0001))
34 % Combine a list of extents
35 #(define (extent-combine extents)
36 (if (pair? (cdr extents))
37 (interval-union (car extents) (extent-combine (cdr extents)))
40 % Check if the stem is connectable to the root
41 #(define ((stem-connectable? ref root) stem)
42 ; The root is always connectable to itself
45 ; Horizontal positions of the stems must be almost the same
46 (close-enough? (car (ly:grob-extent root ref X))
47 (car (ly:grob-extent stem ref X)))
48 ; The stem must be in the direction away from the root's notehead
49 (positive? (* (ly:grob-property root 'direction)
50 (- (car (ly:grob-extent stem ref Y))
51 (car (ly:grob-extent root ref Y))))))))
53 % Connect stems if we have at least one stems connectable to the root
54 #(define (stem-span-stencil span)
55 (let* ((system (ly:grob-system span))
56 (root (ly:grob-parent span X))
57 (stems (filter (stem-connectable? system root)
58 (ly:grob-object span 'stems))))
59 (if (<= 2 (length stems))
60 (let* ((yextents (map (lambda (st)
61 (ly:grob-extent st system Y)) stems))
62 (yextent (extent-combine yextents))
63 (layout (ly:grob-layout root))
64 (blot (ly:output-def-lookup layout 'blot-diameter)))
67 (set! (ly:grob-property st 'transparent) #t))
69 ; Draw a nice looking stem with rounded corners
70 (ly:round-filled-box (ly:grob-extent root root X) yextent blot))
71 ; Nothing to connect, don't draw the span
74 % Create a stem span as a child of the cross-staff stem (the root)
75 #(define ((make-stem-span! stems trans) root)
76 (let ((span (ly:engraver-make-grob trans 'Stem '())))
77 (ly:grob-set-parent! span X root)
78 (set! (ly:grob-object span 'stems) stems)
79 ; Suppress positioning, the stem code is confused by this weird stem
80 (set! (ly:grob-property span 'X-offset) 0)
81 (set! (ly:grob-property span 'stencil) stem-span-stencil)))
83 % Set cross-staff property of the stem to this function to connect it to
84 % other stems automatically
85 #(define (cross-staff-connect stem)
88 % Check if automatic connecting of the stem was requested. Stems connected
89 % to cross-staff beams are cross-staff, but they should not be connected to
90 % other stems just because of that.
91 #(define (stem-is-root? stem)
92 (eq? cross-staff-connect (ly:grob-property-data stem 'cross-staff)))
94 % Create stem spans for cross-staff stems
95 #(define (make-stem-spans! ctx stems trans)
96 ; Cannot do extensive checks here, just make sure there are at least
97 ; two stems at this musical moment
98 (if (<= 2 (length stems))
99 (let ((roots (filter stem-is-root? stems)))
100 (map (make-stem-span! stems trans) roots))))
102 % Connect cross-staff stems to the stems above in the system
103 #(define (Span_stem_engraver ctx)
106 ; Record all stems for the given moment
108 ((stem-interface trans grob source)
109 (set! stems (cons grob stems))))
110 ; Process stems and reset the stem list to empty
111 ((process-acknowledged trans)
112 (make-stem-spans! ctx stems trans)
116 #(define-music-function (parser location notes) (ly:music?) #{
117 \override Stem #'cross-staff = #cross-staff-connect
119 \revert Stem #'cross-staff
125 \consists #Span_stem_engraver
129 \parallelMusic #'(voiceA voiceB voiceC) {
130 % Bar 1 - durations, beams, flags
131 g'2 g'4 g'8 [ g'16 ] g'16 |
132 \crossStaff { c'2 c'4 c'8 [ c'16 ] c'16 } |
136 g'8 \stemDown g'8 \crossStaff g'8 \stemNeutral g'8 g'4 r4 |
137 \crossStaff { c'8 \stemDown c'8 } c'8 \stemNeutral c'8 r4 r4 |
138 c8 \stemDown c8 c8 \stemNeutral \crossStaff { c8 c4 c4 } |
140 % Bar 3 - multiple voice styles
141 << c''2 \\ \crossStaff d'2 \\ a'2 \\ \crossStaff f'2 >> g'2 |
142 << b'2 \\ c'2 \\ g'2 \\ e'2 >> << e'2 \\ \\ \crossStaff c'2 >> |
143 << \crossStaff b2 \\ c2 \\ \crossStaff g2 \\ e2 >> r2 |
145 % Bar 4 - grace notes
146 \grace g'8 a'2 \stemDown \crossStaff { \grace g'8 a'2 } \stemNeutral |
147 \grace c'8 d'2 \stemDown \grace c'8 d'2 \stemNeutral |
148 \crossStaff { \grace c8 d2 } \stemDown \grace c8 d2 \stemNeutral |
150 % Bar 5 - cross-staff beams
153 \crossStaff { c8 [ \change Staff=stafftwo c''8 ] }
154 \change Staff=staffthree c8 [ \change Staff=stafftwo c''8 ] r2 |
159 \new Staff = "staffone" <<
164 \new Staff = "stafftwo" <<
169 \new Staff = "staffthree" <<
171 \autoBeamOff \clef bass \voiceC