Am So., 12. Apr. 2020 um 17:24 Uhr schrieb Valentin Villenave
<[email protected]>:
>
> On 4/12/20, Thomas Morley <[email protected]> wrote:
> > Over the last decades I tried again and again to find a better
> > workaround, with less manual work.
>
> Apparently we stumbled upon your white whale :-)
>
> > I now come up with the attached.
>
> Brilliant. Although it’s still a workaround.
>
> > To get the gaps in container-contexts one would need to drop the
> > relevant commands. No manual figuring out any values (apart from the
> > width of the gap).
>
> And even that may not be indispensable; AFAIK that’s not something
> ever required when dealing with mono-staff systems.
>
> The nicer implementation I can think of would be to have some
> \stopSystem \startSystem switch (possibly with the corresponding new
> event interfaces as well), with the latter reprinting all system-start
> grobs (possibly sans BarNumber). It may be cleaner that way so that
> the system delimiter engraver doesn’t need to bother which staves are
> or aren’t stopped at the given moment.
>
> That being said, I do realize that it would imply extending, and
> rewriting considerable parts of, the engraver.
>
> > I'll continue research after some break ;)
>
> Thanks for looking into it! Should we open tracker pages meanwhile?
> (The SystemStart 'brace thing looks like an intrinsically different
> issue.)
>
> Cheers,
> V.
Hi Valentin,
attached my latest take.
Though, it requires issue 5899 (in master now) and issue 5919 (on
review) to work properly.
Cheers,
Harm
\version "2.21.1"
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% Workaround to insert one or more gaps mid-line of a score
%% Fakes a system-start-delimiter at the end of every gap.
%% Usage: put \space <some-number> in every context at the place where you
%% want the gap.
%% The argument of \space determines the width of the gap.
%% Needs issue 5899 and issue 5919
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% Redefine "|" with a different name to allow `print-certain-span-bars?' to
%% differentiate between "|" and "|-g"
#(define-bar-line "|-g" "|" #f "|")
space =
#(define-music-function (span-bar-before width) ((boolean? #t) number?)
"Adding a gap of width @var{width} into a @code{StaffSymbol}.
Only works in the middle of a @code{Staff}.
Before the gap starts a bar-line with span-bars covering all staves is printed
unless optional @var{span-bar-before} is set false.
After the gap a faked SystemStartBar covering all staves is done, i.e.
@code{SpanBar} is always printed."
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; General TODO
;; Should we care about SpanBar hee at all?
;; Isn't it more due to user preferences?
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
#{
%% cadenzaOn/Off will mostly prevent line-breaks, better be paranoid
\noBreak
\cadenzaOn
\stopStaff
%% no BarNumber before gap
\once \override Score.BarNumber.break-visibility = ##(#f #f #f)
%% Print SpanBars relying on `span-bar-before' and
%% `print-certain-span-bars'
\once \override Staff.BarLine.allow-span-bar = #span-bar-before
%% Insert a very short skip-event, with a stretched TextScript
\once \textLengthOn
s1*1/1000000-\markup \with-dimensions #(cons 0 width) #'(0 . 0) \null
%% Order items after \startStaff
\once \override Score.BreakAlignment.break-align-orders =
#(make-vector 3
'(left-edge
ambitus
breathing-sign
staff-bar
clef
key-cancellation
key-signature
time-signature
custos))
%% Some trickery: set subproperty `new-staff' #t in order to flag this
%% BarLine for the 'move-system-start'-procedure
\once \override Score.BarLine.details.new-staff = ##t
%\once \override StaffGroup.BarLine.details.new-staff = ##t
%\once \override ChoirStaff.BarLine.details.new-staff = ##t
%\once \override PianoStaff.BarLine.details.new-staff = ##f
%% Use the newly defined "|-g" (apart from the name, it's the same as "|")
%% and let `print-certain-span-bars' work on it, if wished.
%% \bar "|-g" mimics the SystemStartBar, thus adjust thickness
%% TODO We assume a new mid-staff SystemStartBarshould cover all staves
%% Are there othere use-cases?
\once \override Staff.BarLine.hair-thickness = 1.6
\bar "|-g"
%% Force printing full-size Clef
%% NB setting a different Clef is not prevented
\once \override Score.Clef.full-size-change = ##t
\set Staff.forceClef = ##t
%% Print BarNumber after gap (for debugging purpose)
%\once \override Score.BarNumber.break-visibility = ##(#f #t #t)
\startStaff
\cadenzaOff
\noBreak
#})
#(define (print-certain-span-bars? glyphes)
(lambda (grob)
"Predicate whether to print @code{SpanBar} at line-end/start, and to print
selected span-bars from @var{glyphes} mid-line as well."
(or (member (ly:grob-property grob 'glyph-name) glyphes)
(not (zero? (ly:item-break-dir grob))))))
#(define (delete-duplicate-cdr lst)
"Goes through @var{lst} from right to left, deletes every element which has
the same @code{cdr} as the one to the right.
@var{lst} should be sorted, having adjacent elements with equal @code{cdr}."
(if (pair? lst)
(fold-right
(lambda (elem ret)
(if (equal? (cdr elem) (cdr (first ret)))
ret
(cons elem ret)))
(list (last lst))
lst)
'()))
#(define (move-system-start style)
(lambda (grob)
(let* (;; system-start-grobs are spanners, thus we need to go through
;; the broken parts
(orig (if (ly:spanner? grob)
(ly:grob-original grob)
#f))
(siblings (if (ly:grob? orig)
(ly:spanner-broken-into orig)
'()))
;; padding is set to different values for different
;; system-start-grobs or not set at all
;; TODO find values programmatically
(padding
(case style
((bracket) 0.8)
((line-bracket) 0)
((bar-line) 0)
((brace) -0.8)))
(spanner-id (ly:grob-property grob 'spanner-id))
;; Get indent and short-indent from \paper, these values need to
;; be respected, while moving
(indent (ly:output-def-lookup $defaultpaper 'indent))
(short-indent (ly:output-def-lookup $defaultpaper 'short-indent))
;; Get output-scale from grob-layout, above (short-)indent must
;; be scaled with this value
(output-scale
(ly:output-def-lookup (ly:grob-layout grob) 'output-scale)))
;; Walk through all siblings
;; For each sibling find the bar-lines which indicate a mid-staff gap,
;; determine their x-coordinates and move the sibling to the selected
;; bar-line (relying on spanner-id.
;; Drop superfluous siblings.
(if (pair? siblings)
(for-each
(lambda (sibling)
(if (equal? grob sibling)
(let* ((sys (ly:grob-system sibling))
(all-elts-array
(ly:grob-object sys 'all-elements))
(all-elts-list
(ly:grob-array->list all-elts-array))
;; Get all bar-lines, with set details.new-staff
(bar-lines
(filter
(lambda (elt)
(and
(grob::has-interface elt 'bar-line-interface)
(assoc-get
'new-staff
(ly:grob-property elt 'details))))
all-elts-list))
;; Assign the x-coordinate to each found bar-line.
;; Returns a list of pairs.
(bar-line-candidates
(map
(lambda (bl)
(cons bl (ly:grob-relative-coordinate bl sys X)))
bar-lines))
;; Needed? - better be paranoid
(sorted-bar-line-candidates
(sort
bar-line-candidates
(lambda (p q) (< (cdr p) (cdr q)))))
;; Keep only one bar-line for each x-coordinate
(relevant-bar-lines
(delete-duplicate-cdr
sorted-bar-line-candidates))
;; Get the relevant bar-line-x-coord, by selecting
;; from relevant-bar-lines, taking spanner-id as
;; index. Return #f for spanner-id exceeding the
;; list-length.
(bar-line-coord
(if (> spanner-id (length relevant-bar-lines))
#f
(cdr
(list-ref
relevant-bar-lines
(1- spanner-id))))))
;; Set the style of the system-start-grob to move.
;; Move it in front of the relevant bar-line.
;; Drop superfluous ones.
(if (and (equal? grob sibling) bar-line-coord)
(let ((x-off
;; Take proper scaled (short-)indent into
;; account.
;; Adjust padding.
(- bar-line-coord
(/ padding 2)
(/ (if (equal? grob (car siblings))
indent
short-indent)
output-scale))))
(ly:grob-set-property! grob 'style style)
(ly:grob-set-property! sibling 'X-offset x-off))
(ly:grob-suicide! sibling)))))
siblings)))))
#(define* (fake-mid-staff-system-start #:optional (desired-system-starts 5))
(lambda (ctx)
"Constructs @code{systemStartDelimiterHierarchy} for @var{ctx}, with the
default @code{systemStartDelimiter} and @var{desired-system-starts} instances
of nested @code{SystemStartSquare}s.
@var{desired-system-starts} will determine how many faked system-starts are
possible.
Collects those @code{SystemStartSquare}s and assigns a @code{spanner-id} to
them.
Finally assigns @code{move-system-start} to @code{after-line-breaking} of each
@code{SystemStartSquare}.
Limitations:
- @code{SystemStartSquare}s can't be used for other things any more
- A user provided @code{systemStartDelimiterHierarchy} will be dropped
- Can't be reasonably consisted in score-context."
(if desired-system-starts
(let* ((squares '())
(system-start-delimiter
(ly:context-property ctx 'systemStartDelimiter))
(style
(case system-start-delimiter
((SystemStartBracket) 'bracket)
((SystemStartBrace) 'brace)
((SystemStartBar) 'bar-line)
((SystemStartSquare) 'line-bracket))))
(define
(construct-system-start-delimiter main name counter init)
;; Returns a nested list like:
;; '(main
;; (name
;; (name
;; ...
;; ...
;; init)))
;; The nesting-level relies on `counter'. A negative value will
;; return an emty list. Zero returns '(main init).
;;
;; Used to construct a list for `systemStartDelimiterHierarchy'
(cond ((zero? counter) (list main init))
((negative? counter) '())
(else
(construct-system-start-delimiter
main
name
(1- counter)
(cons name (list init))))))
(ly:context-set-property! ctx 'systemStartDelimiterHierarchy
(construct-system-start-delimiter
;; Use default for starting
system-start-delimiter
;; Which system-start-grob do we use for the inner lists
'SystemStartSquare
;; The nesting level, determines how many mid-staff-gaps get a
;; faked SystemStartDelimiter.
;; NB For zero one gap is possible etc
;; Negative values here will return an empty list
(1- desired-system-starts)
;; The most inner list contains the system-start-grob to use as
;; before.
;; The numerical value determines the maximium of how many staves
;; are covered.
;; See discussion:
;;
;; TODO test with Aaron's engraver
;; DONE result: works nicely and makes it possible to reduce the
;; here provided number
;; Probably better to move this functionality out of
;; the engraver.
(cons 'SystemStartSquare (iota 100))))
(make-engraver
(acknowledgers
((system-start-delimiter-interface engraver grob source-engraver)
;; Always set SystemStartSquare.thickness to 0.45 to ensure
;; a sufficient thickness, if style is set 'bracket. The value
;; 0.45 is taken from IR for SystemStartBracket
;;
;; Accumulate all SystemStartSquare-grobs in `squares'.
(if (eq? (grob::name grob) 'SystemStartSquare)
(begin
(ly:grob-set-property! grob 'thickness 0.45)
(set! squares (cons grob squares))))))
((start-translation-timestep trans)
;; Assign spanner-id to each collected SystemStartSquare
(if (pair? squares)
(for-each
(lambda (i sq)
(ly:grob-set-property! sq 'spanner-id i))
(iota (length squares) 1 1)
squares)))
((finalize trans)
;; Set each SystemStartSquare.after-line-breaking to
;; `move-system-start'-procedure
(for-each
(lambda (sq)
(ly:grob-set-property! sq 'after-line-breaking
(move-system-start style)))
squares)
;; Clean up
(set! squares '()))))
;; don't do anything if `desired-system-starts' is #f
'())))
sysStart = \with { \consists #(fake-mid-staff-system-start) }
\layout {
\context {
\StaffGroup
\sysStart
}
\context {
\GrandStaff
\sysStart
}
\context {
\PianoStaff
\sysStart
}
\context {
\ChoirStaff
\override BarLine.allow-span-bar =
#(print-certain-span-bars? '("|." ".|:" ":|." "|-g"))
\sysStart
}
\context {
\Score
%% adding Span_bar_engraver
\consists "Span_bar_engraver"
%% \sysStart does not work if consisted in Score-context
}
}
%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% EXAMPLES
%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%
%% Tests
%%%%%%%%%%%%%%%%%%%%%%
chrds = \chordmode {
c1 \space #5 d \space #5 e
\break
f \space #5 c \space #5 d
\break
e
\break
f \space #5 c \space #5 d \space #5 e
\break
f \space #5 c \space #5 d \space #5 e \space #5 f
}
musI = {
R1 \space #5 R \space #5 R
\break
R \space #5 R \space #5 R
\break
R
\break
R \space #5 R \space #5 R \space #5 R
\break
R \space #5 R \space #5 R \space #5 R \space #5 R
}
testMusic =
<<
\new Staff \musI
\new Staff \musI
>>
\new StaffGroup
\with {
instrumentName = "StaffGroup"
shortInstrumentName = "StGr"
}
<<
\new ChordNames \chrds
\testMusic
>>
\markup \draw-hline
\new PianoStaff
\with {
instrumentName = "PianoStaff"
shortInstrumentName = "PiSt"
}
\testMusic
\markup \draw-hline
\new GrandStaff
\with {
instrumentName = "GrandStaff"
shortInstrumentName = "GrSt"
}
\testMusic
\markup \draw-hline
\new ChoirStaff
\with {
instrumentName = "ChoirStaff"
shortInstrumentName = "ChSt"
}
\testMusic
%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% Example by Helge
%%%%%%%%%%%%%%%%%%%%%%%%%%%%
global = {
\key es \major
\time 12/8
}
testSpaceI = {
R1*12/8*8
\space #15
R1*12/8
}
testSpaceII = {
\break
R1*12/8
\space #15
R1*12/8
\break
R1*12/8
\space #15
R1*12/8
}
tenorVoice = \relative c'' {
\global
\testSpaceI
\break
\repeat volta 2 {
bes8 a bes c bes c d4. r
%% probably
%\once \override Score.RehearsalMark.self-alignment-X = #RIGHT
\mark
\markup
\vcenter \smaller {
"dal" \musicglyph #"scripts.segno" "al" \musicglyph #"scripts.coda"
}
}
\space #10
\key es \major
\mark \markup \smaller { \musicglyph #"scripts.coda" }
bes4. bes c d |
es1.
\testSpaceII
\bar "|."
}
verseTenorVoice = \lyricmode {
ha -- ben wir ei -- nes er -- kannt
größ -- te Schatz den's gibt.
}
verseTenorVoiceAlt = \lyricmode {
wir a -- ber hal -- ten zu -- samm':
}
baritonVoice = \relative c' {
\global
\testSpaceI
\break
\repeat volta 2 {
d8 d d es es es d4. r
}
\space #10
\key es \major
<<
{
g4. g as as g1.
}
\new Voice {
\voiceTwo
es4. es es f bes,1.
}
>>
\testSpaceII
\bar "|."
}
verseBaritonVoice = \lyricmode {
ha -- ben wir ei -- nes er -- kannt
größ -- te Schatz den's gibt.
}
verseBaritonVoiceAlt = \lyricmode {
wir a -- ber hal -- ten zu -- samm':
}
bassVoice = \relative c {
\global
\testSpaceI
\break
%\once \override Staff.BarLine.allow-span-bar = ##f
\repeat volta 2 {
bes'8 8 8 a8 a a bes4. bes,
}
%\space ##f #10
\space #10
\key es \major
bes'4. g f bes, es1.
\testSpaceII
\bar "|."
}
verseBassVoice = \lyricmode {
\repeat unfold 7 \skip 1
Ein größ -- te Schatz den's gibt.
}
right = \relative c' {
\global
\testSpaceI
\break
<d f bes>4. <es f a c> <d f bes> <bes bes'>
\space #10
\key es \major
<es g bes>4. <es g bes>4. <es as c> <f as d>
<es g bes es>1.
\testSpaceII
\bar "|."
}
left = \relative c {
\global
\testSpaceI
\break
bes4. f bes bes
\space #10
\key es \major
bes'4. g f bes, | es1.
\testSpaceII
\bar "|."
}
tenorVoicePart =
<<
\new Voice = "tenor" \tenorVoice
\new Lyrics \lyricsto "tenor" { \verseTenorVoice }
\new Lyrics \lyricsto "tenor" { \verseTenorVoiceAlt }
>>
baritonVoicePart =
<<
\new Voice = "bariton" \baritonVoice
\new Lyrics \lyricsto "bariton" { \verseBaritonVoice }
\new Lyrics \lyricsto "bariton" { \verseBaritonVoiceAlt }
>>
bassVoicePart =
<<
\new Voice = "bass" { \clef bass \bassVoice }
\new Lyrics \lyricsto "bass" { \verseBassVoice }
>>
\paper {
indent = 30
short-indent = 6
}
\score {
<<
\new ChoirStaff
<<
\new Staff \tenorVoicePart
\new Staff \baritonVoicePart
\new Staff \bassVoicePart
>>
\new PianoStaff
<<
\new Staff \right
\new Staff { \clef bass \left }
>>
>>
}