Am So., 12. Apr. 2020 um 17:24 Uhr schrieb Valentin Villenave
<valen...@villenave.net>:
>
> On 4/12/20, Thomas Morley <thomasmorle...@gmail.com> 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 }
      >>
  >>
}

Reply via email to