6

In 2011, Xavier Scheuer published a code snippet allowing consecutive rehearsal marks using the multi-mark engraver. The code worked until now. In Lilypond 2.24.1 it fails. Here is an example that worked before (sorry, a lot of code):

\version "2.22.1"
\language "italiano"

#(set-global-staff-size 18)

openBracket =
    \tweak self-alignment-X #'()
    \tweak break-visibility #end-of-line-invisible
    \mark \markup {
        \path #0.2 #'((moveto 0 0) (lineto 0 2) (lineto 1.3 2))
    }
closeBracket =
    \tweak self-alignment-X #'()
    \tweak break-visibility #begin-of-line-invisible
    \mark \markup {
        \path #0.2 #'((moveto 0 0) (lineto 0 2) (lineto -1.3 2))
    }
doubleBracket =
    \tweak self-alignment-X #'()
    \tweak break-visibility #begin-of-line-invisible
    \mark \markup {
        \path #0.2 #'((moveto -0.2 0) (lineto -0.2 2) (lineto -1.5 2)
                      (moveto  0.2 0) (lineto  0.2 2) (lineto  1.5 2))
    }

#(define (multi-mark-engraver ctx)
    (let
        ((texts '())
         (final-texts '())
         (events '()))

      `(
            (start-translation-timestep
                . ,(lambda (trans)
                (set! final-texts '())))

            (listeners
                (mark-event
                    . ,(lambda (trans ev)
                    (set! events (cons ev events)))))

            (acknowledgers
                (break-alignment-interface
                    . ,(lambda (trans grob source)
                        (for-each
                            (lambda (mark)
                                (set! (ly:grob-parent mark X) grob))
                            texts))))

            (process-music
                . ,(lambda (trans)
                    (for-each
                        (lambda (ev)
                            (let*
                                (
                                    (mark-grob (ly:engraver-make-grob trans 'RehearsalMark ev))
                                    (label (ly:event-property ev 'label))
                                    (formatter (ly:context-property ctx 'markFormatter))
                                )

                                (if
                                    (and (procedure? formatter) (not (markup? label)))
                                    (begin
                                        (if (not (number? label))
                                            (set! label
                                                (ly:context-property ctx 'rehearsalMark)))

                                        (if (and (integer? label)
                                            (exact? label))
                                            (set!
                                                (ly:context-property ctx 'rehearsalMark)
                                                (1+ label)))

                                        (if (number? label)
                                            (set! label (apply formatter (list label ctx)))
                                            (ly:warning "rehearsalMark must have integer value"))))

                                (if (markup? label)
                                    (begin
                                        (set! (ly:grob-property mark-grob 'text) label)
                                        (let ((dir (ly:event-property ev 'direction)))
                                            (and (ly:dir? dir)
                                                (set! (ly:grob-property mark-grob 'direction) dir))))
                                    (ly:warning "mark label must be a markup object"))

                                (set! texts (cons mark-grob texts))))
                        (reverse events))))

            (stop-translation-timestep
                . ,(lambda (trans)
                    (if (pair? texts)
                        (let
                            ((staves (ly:context-property ctx 'stavesFound)) (priority-index 0))
                            (for-each
                                (lambda (grob)
                                    (let
                                        ((my-priority (ly:grob-property grob 'outside-staff-priority 1500)))
                                        (for-each (lambda (stave) (ly:pointer-group-interface::add-grob grob 'side-support-elements stave)) staves)
                                        (set! (ly:grob-property grob 'outside-staff-priority) (+ my-priority priority-index))
                                        (set! priority-index (1+ priority-index))
                                        (set! final-texts (cons grob final-texts))))
                                (reverse texts))
                            (set! texts '())
                            (set! events '())))))

            (finalize
                . ,(lambda (trans)
                    (and
                        (pair? final-texts)
                        (for-each
                            (lambda (grob)
                                (set! (ly:grob-property grob 'break-visibility) end-of-line-visible))
                            final-texts)))))))

\score {
    \header {
        opus = "Op. 261 №7"
    }
    \new PianoStaff \with {
        instrumentName = "11."
        \override InstrumentName.font-size = #6
        \override InstrumentName.self-alignment-X = #RIGHT
    } <<
        \new Staff = "правая" \with {}
        \relative do' {
            \key do \major
            \time 2/4
            \tempo "Allegretto vivace"
            \set Timing.baseMoment = #(ly:make-moment 1/32)
            \set Timing.beatStructure = 4, 4, 4, 4
            \openBracket
            do'32-3 (_[sol-1 re'-4 sol,-1]) mi'16-!-5 \noBeam r16 mi32-3 (_[do-1 fa-4 do-1]) sol'16-!-5 \noBeam r16 |
            do,32-3 (_[sol-1 re'-4 sol,-1]) mi'16-!-5 \noBeam r16 mi32-3 (_[do-1 fa-4 do-1]) sol'16-!-5 \noBeam r16 |
            fa32-4 (_[sol-5 mi-3 sol-5]) re16-!-2 \noBeam r16 mi32-3 (_[sol-5 re-2 sol-5]) do,16-!-1 \noBeam r16 |
            \once \override Fingering.avoid-slur = #'inside
            re32-2 (sol-5 re-2 sol-5 si,-1 re-4 si-2 re-4) sol,16-!-1 [sol-!-3 sol-!-2 sol-!-1] \closeBracket |
            \openBracket
            do32-3 (_[sol-1 re'-4 sol,-1]) mi'16-!-5 \noBeam r16 mi32-3 (_[do fa-4 do]) sol'16-!-5 \noBeam r16 |
            \ottava #1 do32-3 (_[sol re'-4 sol,]) mi'16-!-5 \noBeam r16 mi32-3 (_[do fa-4 do]) sol'16-!-5 \noBeam r16 |
            \tweak outside-staff-priority #100 \doubleBracket
            \once \override Fingering.avoid-slur = #'inside
            la32-5 (la,-1 la'-5 sol fa-3 mi re-1 do-4 \ottava #0 si-3 la sol-1 fa-4 mi-3 re do-1 si-2 |
            do-1 re mi-3 fa-1 sol-2 la si do-5 do,16-.-1) r r8 \closeBracket
            \bar "|."
        }
        \new Dynamics {
            s16
            s32 s32 s8 s4 | s2 | s2 |
            s32 \> s32 s32 s32 s32 s32 s32 s32 s16 s16 s16 s16 \! |
            s32 \p s32 s32 s32 s16 s16 s32 s32 s32 s32 s16 s16 |
            s32 \mf s32 s32 s32 s16 s16 s32 s32 s32 s32 s16 s16 |
            s32 \f s32 s32 s32 s32 s32 s32 s32 s32 s32 s32 s32 s32 s32 s32 s32 |
        }
        \new Staff = "левая" \with {}
        \relative do {
            \clef bass
            \set fingeringOrientations = #'(down)
            <do-4>16-. _[r <mi-2 sol-1>-.] r do-. _[r <mi sol>-.] r |
            <do-4>-. _[r <mi-2 sol-1>-.] r do-. _[r <mi sol>-.] r |
            <do-5>-. _[r <re-3 sol-1>-.] r <do-4>-. _[r <mi-2 sol-1>-.] r |
            <sol-3 si-1>8-> r8 r4 |
            <do,-4>16-. _[r <mi-2 sol-1>-.] r do-. _[r <mi sol>-.] r |
            <do-4>-. _[r <mi-2 sol-1>-.] r do-. _[r <mi sol>-.] r |
            <fa-5 la-3 re-1>8 r <sol-5 re'-2 fa-1> r |
            <do-3 mi-1>16-. r <do mi>-. r <do mi>-. r r8
        }
    >>
    \layout {
        \context {
            \Score
            \override SpacingSpanner.spacing-increment = 1.1
            \remove "Mark_engraver"
            \consists #multi-mark-engraver
        }
    }
}

This is where it explodes:

(if (markup? label)
    (begin
        (set! (ly:grob-property mark-grob 'text) label)
        (let ((dir (ly:event-property ev 'direction)))
            (and (ly:dir? dir)
                (set! (ly:grob-property mark-grob 'direction) dir))))
    (ly:warning "mark label must be a markup object"))

The test if the label is a markup fails and the code prints “warning: mark label must be a markup object”. Later Lilypond also prints a number of

programming error: Trying to interpret a non-markup object: ()
continuing, cross fingers

I am not sure where this is coming from at all. There is no line reference.

I tried convert-ly. It was supposed to convert an outdated version of .ly to an up-to-date one. The resulting .ly produced fewer errors, but it engraved A, B, C, D, E as marks instead of brackets.

How can I replace the functionality of this script?

4
  • While convert-ly usually does a good job on pure LilyPond code, Scheme code is far trickier to convert since Scheme is a full-fledged programming language. convert-ly does have some heuristics, but they remain heuristics. All code needs maintenance and Scheme code in LilyPond files is no exception. Apr 2, 2023 at 21:11
  • @jeanas The real problem wasn't in the Scheme code. convert-ly should have changed \mark to \textMark because \mark accepts only numbers now as an argument.
    – facetus
    Apr 3, 2023 at 3:16
  • Nope, there is no bug. \mark still does accept a markup argument, for compatibility. If \mark <markup> were converted to \textMark, it would invalidate user overrides to the RehearsalMark grob, which we cannot change to TextMark unconditionally because norma rehearsal marks remain RehearsalMark grobs. (The addition of \textMark was contributed by me, FWIW.) Apr 3, 2023 at 11:34
  • @jeanas Thank you for \textMark!
    – facetus
    Apr 7, 2023 at 17:44

1 Answer 1

7

After reading the release notes, I have found a solution. It turned out we don't need anymore to use \mark to place our custom symbols and text above notes. Thus, multi-mark-engraver is not needed anymore as well. All one has to do is to use \textMark instead of \mark. The full solution for the code in the question:

\version "2.24.1"
\language "italiano"

#(set-global-staff-size 18)

openBracket =
    \tweak self-alignment-X #'()
    \tweak break-visibility #end-of-line-invisible
    \textMark \markup {
        \path #0.2 #'((moveto 0 0) (lineto 0 2) (lineto 1.3 2))
    }
closeBracket =
    \tweak self-alignment-X #'()
    \tweak break-visibility #begin-of-line-invisible
    \textMark \markup {
        \path #0.2 #'((moveto 0 0) (lineto 0 2) (lineto -1.3 2))
    }
doubleBracket =
    \tweak self-alignment-X #'()
    \tweak break-visibility #begin-of-line-invisible
    \textMark \markup {
        \path #0.2 #'((moveto -0.2 0) (lineto -0.2 2) (lineto -1.5 2)
                      (moveto  0.2 0) (lineto  0.2 2) (lineto  1.5 2))
    }

\score {
    \header {
        opus = "Op. 261 №7"
    }
    \new PianoStaff \with {
        instrumentName = "11."
        \override InstrumentName.font-size = #6
        \override InstrumentName.self-alignment-X = #RIGHT
    } <<
        \new Staff = "правая" \with {}
        \relative do' {
            \key do \major
            \time 2/4
            \tempo "Allegretto vivace"
            \set Timing.baseMoment = #(ly:make-moment 1/32)
            \set Timing.beatStructure = 4, 4, 4, 4
            \openBracket
            do'32-3 (_[sol-1 re'-4 sol,-1]) mi'16-!-5 \noBeam r16 mi32-3 (_[do-1 fa-4 do-1]) sol'16-!-5 \noBeam r16 |
            do,32-3 (_[sol-1 re'-4 sol,-1]) mi'16-!-5 \noBeam r16 mi32-3 (_[do-1 fa-4 do-1]) sol'16-!-5 \noBeam r16 |
            fa32-4 (_[sol-5 mi-3 sol-5]) re16-!-2 \noBeam r16 mi32-3 (_[sol-5 re-2 sol-5]) do,16-!-1 \noBeam r16 |
            \once \override Fingering.avoid-slur = #'inside
            re32-2 (sol-5 re-2 sol-5 si,-1 re-4 si-2 re-4) sol,16-!-1 [sol-!-3 sol-!-2 sol-!-1] \closeBracket |
            \openBracket
            do32-3 (_[sol-1 re'-4 sol,-1]) mi'16-!-5 \noBeam r16 mi32-3 (_[do fa-4 do]) sol'16-!-5 \noBeam r16 |
            \ottava #1 do32-3 (_[sol re'-4 sol,]) mi'16-!-5 \noBeam r16 mi32-3 (_[do fa-4 do]) sol'16-!-5 \noBeam r16 |
            \tweak outside-staff-priority #100 \doubleBracket
            \once \override Fingering.avoid-slur = #'inside
            la32-5 (la,-1 la'-5 sol fa-3 mi re-1 do-4 \ottava #0 si-3 la sol-1 fa-4 mi-3 re do-1 si-2 |
            do-1 re mi-3 fa-1 sol-2 la si do-5 do,16-.-1) r r8 \closeBracket
            \bar "|."
        }
        \new Dynamics {
            s16
            s32 s32 s8 s4 | s2 | s2 |
            s32 \> s32 s32 s32 s32 s32 s32 s32 s16 s16 s16 s16 \! |
            s32 \p s32 s32 s32 s16 s16 s32 s32 s32 s32 s16 s16 |
            s32 \mf s32 s32 s32 s16 s16 s32 s32 s32 s32 s16 s16 |
            s32 \f s32 s32 s32 s32 s32 s32 s32 s32 s32 s32 s32 s32 s32 s32 s32 |
        }
        \new Staff = "левая" \with {}
        \relative do {
            \clef bass
            \set fingeringOrientations = #'(down)
            <do-4>16-. _[r <mi-2 sol-1>-.] r do-. _[r <mi sol>-.] r |
            <do-4>-. _[r <mi-2 sol-1>-.] r do-. _[r <mi sol>-.] r |
            <do-5>-. _[r <re-3 sol-1>-.] r <do-4>-. _[r <mi-2 sol-1>-.] r |
            <sol-3 si-1>8-> r8 r4 |
            <do,-4>16-. _[r <mi-2 sol-1>-.] r do-. _[r <mi sol>-.] r |
            <do-4>-. _[r <mi-2 sol-1>-.] r do-. _[r <mi sol>-.] r |
            <fa-5 la-3 re-1>8 r <sol-5 re'-2 fa-1> r |
            <do-3 mi-1>16-. r <do mi>-. r <do mi>-. r r8
        }
    >>
}

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.