Jump to content

Line breaking rehearsal mark (obsolete in 2.23)

From LilyPond wiki
Revision as of 18:06, 14 November 2025 by Jean Abou Samra (talk | contribs) (Import snippet from LSR)
(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)

If you need at a line break at the same time step two rehearsal marks, one rehearsal mark at the end of the line with right aligned text, and another rehearsal mark at the beginning of the next line with left aligned text, but if there is no line break these two marks should be concatenated with a space between, try this \doubleMark command. It takes two arguments (string or markup).
Additionally one of the two arguments may be the boolean ##f to indicate a begin-of-line-invisible and right alinged resp. a end-of-line-invisible and left aligned rehearsal mark, and if one of the two arguments is the boolean value ##t then the remaining text will be centered if it occurs within the line.
Override the value of gap in Score.RehearsalMark if you need a different spacing between the two text markups.
 
Handicaps:

  • An argument '\default' is not supported.

Also providing some functions to separately address RehearsalMarks depending on their break-direction, to tweak direction and padding.
And for compatibility when using the poly-mark-engraver (LSR No. 976) a \doublePolyMark command is provided (which otherwise will not work).

This snippet is obsolete starting from LilyPond version 2.23.14, which introduces the \textMark and \textEndMark commands. There is no restriction similar to rehearsal marks: there can be several text marks at the same moment.

Thus unapproving it --Harm

\version "2.24.0"

%by: ArnoldTheresius
% update for cooperation with poly-mark-engraver or multi-mark-engraver

% see LSR No. 976 for the poly-mark-engraver
% \include "poly-mark-engraver.ly"
% The compatibility test with the poly-mark-engraver below
% is commented out, too!

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% START of my personal include file 'double-mark.ly'
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

#(define-public (string-or-markup-or-boolean? e)
  (or (string? e) (markup? e) (boolean? e)))

#(define (music-property-description symbol type? description)
  (if (not (equal? #f (object-property symbol 'music-doc)))
      (ly:error (_ "symbol ~S redefined") symbol))
  (set-object-property! symbol 'music-type? type?)
  (set-object-property! symbol 'music-doc description)
  symbol)

#(for-each
  (lambda (x)
    (apply music-property-description x))
    `((left-label
       ,string-or-markup-or-boolean?
       "set the left part of a RehearsalMark")
      (right-label
       ,string-or-markup-or-boolean?
       "set the right part of a RehearsalMark")
       ))

#(define (double-rehearsalmark-stencil grob)
  (let*
   ((grobs-event (ly:grob-property grob 'cause '()))
    (left-label (ly:event-property grobs-event 'left-label))
    (right-label (ly:event-property grobs-event 'right-label))
    (gap (ly:grob-property grob 'gap 1.4)))
   (if (not (or (null? left-label) (null? right-label)))
    (case (ly:item-break-dir grob)
     ((-1)
      (if (boolean? left-label) empty-stencil
       (grob-interpret-markup grob
        (make-right-align-markup left-label))))
     ((1)
      (if (boolean? right-label) empty-stencil
       (grob-interpret-markup grob
        (make-left-align-markup right-label))))
     (else
      (if (boolean? left-label)
       (grob-interpret-markup grob
        (if left-label
         (make-center-align-markup right-label)
         (make-left-align-markup right-label)))
       (if (boolean? right-label)
        (grob-interpret-markup grob
         (if right-label
          (make-center-align-markup left-label)
          (make-right-align-markup left-label)))
        (ly:stencil-add
         (ly:stencil-translate
          (grob-interpret-markup grob
           (make-right-align-markup left-label))
          (cons (* -0.5 gap) 0.0))
         (ly:stencil-translate
          (grob-interpret-markup grob
           (make-left-align-markup right-label))
          (cons (* 0.5 gap) 0.0)))))))
    (begin
     (ly:warning "\"doubleMark stencil\" did not find \"doubleMark texts\".")
     (ly:warning "fallback to using \"ly:text-interface::print\".")
     (ly:text-interface::print grob)))))

doubleMark =
#(define-music-function
  (left-string right-string)
  (string-or-markup-or-boolean? string-or-markup-or-boolean?)
  (if (and (boolean? left-string) (boolean? right-string))
   (ly:warning "~a \\doubleMark - at least one string or markup required" (*location*)))
  (make-music 'SequentialMusic
   'elements (list
    (make-music 'ContextSpeccedMusic
     'context-type 'Score
     'element
      (make-music 'OverrideProperty
       'symbol 'RehearsalMark
       'grob-value double-rehearsalmark-stencil
       'grob-property-path (list 'stencil)
       'pop-first #t
       'once #t))
    (make-music 'ContextSpeccedMusic
     'context-type 'Score
     'element
      (make-music 'OverrideProperty
       'symbol 'RehearsalMark
       'grob-value #f
       'grob-property-path (list 'self-alignment-X)
       'pop-first #t
       'once #t))
    (make-music 'ContextSpeccedMusic
     'context-type 'Score
     'element
      (make-music 'OverrideProperty
       'symbol 'RehearsalMark
       'grob-value `#(,(not (boolean? left-string))
                      #t
                      ,(not (boolean? right-string)))
       'grob-property-path (list 'break-visibility)
       'pop-first #t
       'once #t))
    (make-music 'RehearsalMarkEvent
     'label #f
     'left-label left-string
     'right-label right-string
     'origin (*location*)))))

% only for use with the poly-mark-engraver, based on a
% copy of 'mark' from music-functions.ly, then extended:
doublePolyMark =
#(define-music-function
   (key left-string right-string)
   (((lambda (x) (or (symbol? x)
                  (and (pair? x) (symbol? (car x))))) #f)
    (string-or-markup-or-boolean? #f) (string-or-markup-or-boolean? #f))
  "Make the music for the \\doublePolyMark command."
  (let* ((set (and (integer? label)
                   (context-spec-music (make-property-set 'rehearsalMark label)
                                       'Score)))
         (ev (make-music 'RehearsalMarkEvent
                         'left-label left-string
                         'right-label right-string
                         'origin (*location*)))
         (entry-key (if (symbol? key) key
                     (if (and (list? key) (symbol? (car key))) (car key)
                      #f)))
         (add-opts (if (pair? key) (cdr key) '())))

    (if (symbol? entry-key)
      (ly:music-set-property! ev 'key entry-key))
    (ly:music-set-property! ev 'additional-options
      (acons 'stencil double-rehearsalmark-stencil
        (acons 'self-alignment-X #f
          (acons 'break-visibility `#(,(not (boolean? left-string))
                                      #t
                                      ,(not (boolean? right-string)))
            add-opts))))

    (if set
        (make-sequential-music (list set ev))
        (begin
          (set! (ly:music-property ev 'label) #f)
          ev))))


%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% END of my personal include file 'double-mark.ly'
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

{
  c''1
  % make the coda sign begin-of-line-invisible
  % center it, except at the end of line, there let it be right adjusted
  \doubleMark
    \markup { \musicglyph "scripts.coda" }
    ##t
  c''
  % line breaking RehearsalMark
  % will be joined with a space of the size 'gap' of no line break occurs
  \doubleMark
    \markup { \with-color #blue { "D.C. al  " \raise #1.0 \musicglyph "scripts.coda" "e poi" } }
    \markup { \with-color #blue "CODA" }
  \break
  c''
  % make the "Fine" begin-of-line-invisible and right adjusted
  \doubleMark
    "Fine"
    ##f
  c''
  \doubleMark
    "CODA da capo al Fine"
    ##f
  \bar "|."
}

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% using \doublePolyMark,
% only possible if the poly-mark-engraver is loaded
% therefore it's commented out here!
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

%{
  c''1
  % make the coda sign begin-of-line-invisible
  % center it, except at the end of line, there let it be right adjusted
  \polyMark #'CenterDown \markup \box \fontsize #-3 \column { "another" "polyMark" }
  \doubleMark
    \markup { \musicglyph "scripts.coda" }
    ##t
  c''
  % line breaking RehearsalMark
  % will be joined with a space of the size 'gap' of no line break occurs
  \mark \markup \box \fontsize #-3 \column { "another" "mark" }
  \doublePolyMark #'anySymbol
    \markup { \with-color #blue { "D.C. al  " \raise #1.0 \musicglyph "scripts.coda" "e poi" } }
    \markup { \with-color #blue "CODA" }
  \break
  c''
  % make the "Fine" begin-of-line-invisible and right adjusted
  \mark \markup \box \fontsize #-3 \column { "another" "mark" }
  \doublePolyMark #'CenterDown
    "Fine"
    ##f
  c''
  \doublePolyMark #'CenterDown
    "CODA da capo al Fine"
    ##f
  \bar "|."
%}

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Some more functions for tweaking a broken RehearsakMark
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

%% direction

set-mark-y-dir =
#(define-music-function (ls)(list?)
"
 Sets 'direction of RehearsalMark. Depending on their break-dir.
 
 @var{ls} is supposed to be an alist with entries like
  (<break-dir of the item> . <Y-direction>)
 A full list is not needed. An empty list is possible, too, will default to all
 items UP.
 
 Example:
 `( (,left . ,DOWN) (,center . ,DOWN) (,right . ,UP) )
 Ofcourse this is the same as:
 '((-1 . -1)(0 . -1)(1 . 1))
 "
#{
  \override Score.RehearsalMark.before-line-breaking =
    #(lambda (grob) 
       (let* ((get-break-dir (ly:item-break-dir grob))
              (left-y-dir (or (assoc-get left ls) 1))
              (right-y-dir (or (assoc-get right ls) 1))
              (center-y-dir (or (assoc-get center ls) 1)))
         
         (ly:grob-set-property! grob 'direction
           (case get-break-dir
            ((-1) left-y-dir)
            ((1) right-y-dir)
            ((0) center-y-dir)
            ;; better be paranoiac
            (else 1)))))
#})
     
\relative c' {  
  \set-mark-y-dir #`( (,left . ,DOWN) (,center . ,DOWN) (,right . ,UP) )
  c4 d e f g a b c b a g f e d c b \bar "||"
  \once \override Score.RehearsalMark.self-alignment-X = #RIGHT
  \doubleMark
  \markup \fontsize #-3 \italic { D.C. al Coda }
  \markup { \musicglyph "scripts.coda" }
  \break  
  \key d \major 
  d' e fis g a b cis d\mark "XY" cis b a g fis e d cis \bar "|."  
}

%% padding

set-broken-mark-padding =
#(define-music-function (ls)(list?)
"
 Sets 'padding of RehearsalMark. Depending on their break-dir.
 
 @var{ls} is supposed to be an alist with entries like
  (<break-dir of the item> . <Y-direction>)
 A full list is not needed. An empty list is possible, too, will default to all
 items having @code{padding} 0.
 
 Example:
 `( (,left . 1) (,center . 2) (,right . 3) )
 Ofcourse this is the same as:
 '((-1 . 1)(0 . 2)(1 . 3))
 "
#{
  \override Score.RehearsalMark.padding =
    #(lambda (grob) 
       (let* ((get-break-dir (ly:item-break-dir grob))
              (y-off #f)
              (left-y-off (assoc-get left ls))
              (right-y-off (assoc-get right ls))
              (center-y-off (assoc-get center ls)))
         
           (case get-break-dir
            ((-1) left-y-off)
            ((1) right-y-off)
            ((0) center-y-off)
            ;; better be paranoiac
            (else 0))))
#})

\paper { tagline = ##f }
     
\relative c' {  
  \set-mark-y-dir #`( (,left . ,DOWN) (,center . ,DOWN)  )
  \set-broken-mark-padding #`( (,right . 3))
  
  c4 d e f g a b c b a g f e d c b \bar "||"
  \once \override Score.RehearsalMark.self-alignment-X = #RIGHT
  \doubleMark
  \markup \fontsize #-3 \italic { D.C. al Coda }
  \markup { \musicglyph "scripts.coda" }
  \break  
  \key d \major 
  d' e fis g a b cis d cis b a g fis e d cis \bar "|."  
}