lilypond-user
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: Solution for multiple simultaneous Text Spanners in a single voice?


From: Aaron Hill
Subject: Re: Solution for multiple simultaneous Text Spanners in a single voice?
Date: Fri, 20 Mar 2020 15:21:04 -0700
User-agent: Roundcube Webmail/1.4.2

On 2020-03-20 12:08 pm, dtsmarin wrote:
Does anyone know any elegant ways to add two or more Text Spanner without
using a separate voice?

Here's a quickly thrown together update to Text_spanner_engraver:

%%%%
\version "2.20.0"

Multiple_text_spanner_engraver =
#(lambda (context)
  (let ((starting-events '())
        (stopping-events '())
        (current-events '())
        (current-spans '())
        (finished-spans '()))

    (define (event-warn event str . args)
      (ly:input-warning (ly:event-property event 'origin)
        (apply format str args)))

    (define (assign-event-once dir id event)
      (let* ((alist (if (> 0 dir) starting-events stopping-events))
             (existing (assoc id alist)))
        (if (and existing (not (equal? (cdr existing) event)))
          (begin
            (event-warn event
              "Two simultaneous text-span events, junking this one")
            (event-warn (cdr existing)
              "Previous text-span event here"))
          (if (> 0 dir)
            (set! starting-events (assoc-set! alist id event))
            (set! stopping-events (assoc-set! alist id event))))))
    (define (handle-starting-event engraver id event)
      (let ((active (assoc id current-events)))
        (if active
          (event-warn event
            "already have a text spanner~@[ (id = ~a)~]"
            id)
          (begin
            (set! current-events (assoc-set! current-events id event))
            (let ((span (ly:engraver-make-grob
                          engraver 'TextSpanner event))
                  (dir (ly:event-property event 'direction #f)))
              (set! current-spans (assoc-set! current-spans id span))
              (if dir
                (ly:grob-set-property! span 'direction dir)))
(set! starting-events (assoc-remove! starting-events id))))))
    (define (handle-stopping-event engraver id event)
      (let ((span (assoc id current-spans)))
        (if span
          (begin
            (set! finished-spans (cons (cdr span) finished-spans))
            (ly:engraver-announce-end-grob engraver (cdr span) '())
            (set! current-spans (assoc-remove! current-spans id))
            (set! current-events (assoc-remove! current-events id)))
          (event-warn event
            "cannot find start of text spanner~@[ (id = ~a)~]"
            id))))
    (define (conditional-set-bound! span dir)
      (or (ly:grob? (ly:spanner-bound span dir))
        (ly:spanner-set-bound! span dir
          (ly:content-property context 'currentMusicalColumn))))
    (define (add-note-column span dir grob)
      (ly:pointer-group-interface::add-grob
        span 'note-columns grob)
      (or (ly:grob? (ly:spanner-bound span dir))
        (ly:spanner-set-bound! span dir grob)))

    (make-engraver
      ((finalize engraver)
        (for-each
          (lambda (span)
            (conditional-set-bound! span LEFT))
          finished-spans)
        (set! finished-spans '())
        (for-each
          (lambda (span)
            (event-warn (cdr (assoc (car span) current-events))
              "unterminated text spanner~@[ (id = ~a)~]"
            (car span))
            (ly:grob-suicide! span))
          current-spans)
        (set! current-spans '()))
      ((stop-translation-timestep engraver)
        (for-each
          (lambda (span)
            (conditional-set-bound! (cdr span) LEFT))
          current-spans)
        (for-each
          (lambda (span)
            (conditional-set-bound! span LEFT))
          finished-spans)
        (set! finished-spans '())
        (set! starting-events '())
        (set! stopping-events '()))
      ((process-music engraver)
        (for-each
          (lambda (event) (handle-stopping-event
            engraver (car event) (cdr event)))
          stopping-events)
        (for-each
          (lambda (event) (handle-starting-event
            engraver (car event) (cdr event)))
          starting-events))
      (listeners
        ((text-span-event engraver event)
          (let ((dir (ly:event-property event 'span-direction))
                (id (ly:event-property event 'spanner-id #f)))
            (assign-event-once dir id event))))
      (acknowledgers
        ((note-column-interface engraver grob source-engraver)
          (for-each
            (lambda (span)
              (add-note-column (cdr span) LEFT grob))
            current-spans)
          (for-each
            (lambda (span)
              (add-note-column span RIGHT grob))
            finished-spans))))))

\new Voice \with {
  \remove "Text_spanner_engraver"
  \consists \Multiple_text_spanner_engraver
}
{
  g'2 \tweak bound-details.left.text "g" _\startTextSpan
  <b' d''>2
    \tweak bound-details.left.text "b" \=2 \startTextSpan
    \tweak bound-details.left.text "d" \=sym ^\startTextSpan
  b'2 \=2 \stopTextSpan
  <g' d''>2 \=sym \stopTextSpan \stopTextSpan
}
%%%%


-- Aaron Hill



reply via email to

[Prev in Thread] Current Thread [Next in Thread]