music-function for ending TextSpanners sometimes fail for skip-events

classic Classic list List threaded Threaded
8 messages Options
Reply | Threaded
Open this post in threaded view
|

music-function for ending TextSpanners sometimes fail for skip-events

Thomas Morley-2
Hi,

for some reason I wanted a music-function which should end a
TextSpanner automagically at the next event which starts a new
TextSpanner or the last possible event at all.

Although it needs further testing I already found a glitch I really
can't explain or cope with: Sometimes, when the TextSpanner is
attached to skip-events, ending them gets confused. It sometimes can
be cured, if the skips are devided, i.e. s2 becomes s4 s4.
So why????
And how to fix the function, which is inspired by our endSpanners-function.

Here the code:

\version "2.19.82"

endTextSpanners =
#(define-music-function (music) (ly:music?)
   (_i "Terminate a TextSpanner at the last rhythmic-event or at the next
rhythmic-event which starts a new TextSpanner, without the need to use
@code{\\stopTextSpan}.")
  (let ((counter 0)
        (last-rhythmic-ev #f)
        (end-span-ev-chrd
          (make-event-chord
            (list (make-music
                    'TextSpanEvent
                    'span-direction
                    1)))))
    (map-some-music
      (lambda (m)
        (and (or (music-is-of-type? m 'rhythmic-event)
                 (music-is-of-type? m 'skip-event))
             (let* ((mus (ly:music-deep-copy m)))
               (set! last-rhythmic-ev mus)
               (cond ((and (zero? counter)
                           (pair? (extract-typed-music mus 'text-span-event)))
                        (begin
                          (set! counter (1+ counter))
                          mus))
                     ((pair? (extract-typed-music mus 'text-span-event))
                      (make-sequential-music
                        (list
                          end-span-ev-chrd
                          mus)))
                     (else mus)))))
      music)
    (map-some-music
      (lambda (mc)
        (and (equal? mc last-rhythmic-ev)
             (make-sequential-music
               (list
                 end-span-ev-chrd
                 mc))))
       music)
     (set! counter 0)
     (set! last-rhythmic-ev #f)
     music))

%% Only for better viewing
\layout {
  \override TextSpanner.style = #'solid
  \override TextSpanner.bound-details.left.text = "X"
}

%% This returns:
%% warning: cannot find start of text spanner
\endTextSpanners
  {
     s2 -\startTextSpan
     s2
     s2
  }

%% This one works !!
\endTextSpanners
  {
     s2 -\startTextSpan
     s4 s
     s2
  }

%% This one as well
\endTextSpanners
  {
     c'2 -\startTextSpan
     d'2
     e'2
  }


Thanks,
  Harm

_______________________________________________
lilypond-user mailing list
[hidden email]
https://lists.gnu.org/mailman/listinfo/lilypond-user
Reply | Threaded
Open this post in threaded view
|

Re: music-function for ending TextSpanners sometimes fail for skip-events

David Kastrup
Thomas Morley <[hidden email]> writes:

> Although it needs further testing I already found a glitch I really
> can't explain or cope with: Sometimes, when the TextSpanner is
> attached to skip-events, ending them gets confused. It sometimes can
> be cured, if the skips are devided, i.e. s2 becomes s4 s4.
> So why????

Haven't analyzed this at all, basically just looked for "buzzwords"
related to your symptom:

>     (map-some-music
>       (lambda (mc)
>         (and (equal? mc last-rhythmic-ev)

> %% This returns:
> %% warning: cannot find start of text spanner
> \endTextSpanners
>   {
>      s2 -\startTextSpan
>      s2
>      s2
>   }

Here you had three basic events, all of them comparing as equal.

> %% This one works !!
> \endTextSpanners
>   {
>      s2 -\startTextSpan
>      s4 s
>      s2
>   }

Here only the first and last events compare as equal.

> %% This one as well
> \endTextSpanners
>   {
>      c'2 -\startTextSpan
>      d'2
>      e'2
>   }

Here none compare as equal.

So you should figure out what your comparison as equal is supposed to be
doing in the first place.  I have a hard time figuring out how such a
comparison matches your description and am too lazy to try.

--
David Kastrup

_______________________________________________
lilypond-user mailing list
[hidden email]
https://lists.gnu.org/mailman/listinfo/lilypond-user
Reply | Threaded
Open this post in threaded view
|

Re: music-function for ending TextSpanners sometimes fail for skip-events

Thomas Morley-2
Am Di., 23. Okt. 2018 um 23:54 Uhr schrieb David Kastrup <[hidden email]>:

> So you should figure out what your comparison as equal is supposed to be
> doing in the first place.

Yep, that's the culprit.

So the question is how to find the last rhythmic event of a
music-expression and place <>\stopTextSpan right before it.

Thanks,
  Harm

_______________________________________________
lilypond-user mailing list
[hidden email]
https://lists.gnu.org/mailman/listinfo/lilypond-user
Reply | Threaded
Open this post in threaded view
|

Re: music-function for ending TextSpanners sometimes fail for skip-events

David Kastrup
Thomas Morley <[hidden email]> writes:

> Am Di., 23. Okt. 2018 um 23:54 Uhr schrieb David Kastrup <[hidden email]>:
>
>> So you should figure out what your comparison as equal is supposed to be
>> doing in the first place.
>
> Yep, that's the culprit.
>
> So the question is how to find the last rhythmic event of a
> music-expression and place <>\stopTextSpan right before it.

Why wouldn't you just put \stopTextSpan on it?  It gets evaluated at the
same point of time either way.

--
David Kastrup

_______________________________________________
lilypond-user mailing list
[hidden email]
https://lists.gnu.org/mailman/listinfo/lilypond-user
Reply | Threaded
Open this post in threaded view
|

Re: music-function for ending TextSpanners sometimes fail for skip-events

Thomas Morley-2
Am Mi., 24. Okt. 2018 um 01:07 Uhr schrieb David Kastrup <[hidden email]>:

>
> Thomas Morley <[hidden email]> writes:
>
> > Am Di., 23. Okt. 2018 um 23:54 Uhr schrieb David Kastrup <[hidden email]>:
> >
> >> So you should figure out what your comparison as equal is supposed to be
> >> doing in the first place.
> >
> > Yep, that's the culprit.
> >
> > So the question is how to find the last rhythmic event of a
> > music-expression and place <>\stopTextSpan right before it.
>
> Why wouldn't you just put \stopTextSpan on it?

The more or less single reason: because my initial posted function was
modeled after the built-in 'endSpanners' and there it's done this way.
Btw, in the line
                  (music-clone m 'span-direction STOP))
all music-properties are cloned as well.
Makes not much sense to me. P.e. why should a tweak for
bound-details.left.text of a TextSpanner be copied in the ending
script?
Right now I'm not aware of any property I would want to be copied into
the ending script.
Or did I overlook use-cases?
If not why not simply use a new constructed
          (make-event-chord
            (list (make-music
                    'what-ever-event
                    'span-direction
                    1)))
?

Back to the topic of this thread.
If someone's interested, I now come up with a code which was built after:
http://lsr.di.unimi.it/LSR/Item?id=83

\version "2.19.82"

%% after 'Adding extra fingering with scheme'
%% http://lsr.di.unimi.it/LSR/Item?id=83

endTextSpanners =
#(define-music-function (parser location music) (ly:music?)
   (let ((script #{ \stopTextSpan #})
         (do-it? #f)
         (last-seen #f))

     (define (append-script-at! my-music prop)
       (set! (ly:music-property my-music prop)
             (append (ly:music-property my-music prop)
                     (list (ly:music-deep-copy script))))
       my-music)
     (map-some-music
      (lambda (mus)
        (case (ly:music-property mus 'name)
          ((EventChord)
            (set! last-seen mus)
            (let* ((starts (extract-typed-music mus 'text-span-event)))
              (cond ((and (not do-it?) (pair? starts))
                     (set! do-it? #t)
                     mus)
                    ((and do-it? (pair? starts))
                     (append-script-at! mus 'elements))
                    (else mus))))
          ((NoteEvent RestEvent SkipEvent)
            (set! last-seen mus)
            (let* ((starts (extract-typed-music mus 'text-span-event)))
              (cond ((and (not do-it?) (pair? starts))
                     (set! do-it? #t)
                     mus)
                    ((and do-it? (pair? starts))
                     (append-script-at! mus 'articulations))
                    (else mus))))
          (else #f)))
      music)

      (case (ly:music-property last-seen 'name)
          ((EventChord)
           (append-script-at! last-seen 'elements))
          ((NoteEvent RestEvent SkipEvent)
           (append-script-at! last-seen 'articulations))
          (else #f))
      (set! do-it? #f)
      (set! last-seen #f)
      music))

 \endTextSpanners
   { c'4-3\startTextSpan <c' e' g'>\startTextSpan r s2 }

 \endTextSpanners
 {
   c'4 4\startTextSpan 4 4
   \repeat volta 2 {
     4\startTextSpan 4
     \tuplet 3/2 { 4\startTextSpan 4 <c' e'>4 }
   }
 }

Cheers,
  Harm

_______________________________________________
lilypond-user mailing list
[hidden email]
https://lists.gnu.org/mailman/listinfo/lilypond-user
Reply | Threaded
Open this post in threaded view
|

Re: music-function for ending TextSpanners sometimes fail for skip-events

David Kastrup
Thomas Morley <[hidden email]> writes:

> Am Mi., 24. Okt. 2018 um 01:07 Uhr schrieb David Kastrup <[hidden email]>:
>>
>> Thomas Morley <[hidden email]> writes:
>>
>> > Am Di., 23. Okt. 2018 um 23:54 Uhr schrieb David Kastrup <[hidden email]>:
>> >
>> >> So you should figure out what your comparison as equal is supposed to be
>> >> doing in the first place.
>> >
>> > Yep, that's the culprit.
>> >
>> > So the question is how to find the last rhythmic event of a
>> > music-expression and place <>\stopTextSpan right before it.
>>
>> Why wouldn't you just put \stopTextSpan on it?
>
> The more or less single reason: because my initial posted function was
> modeled after the built-in 'endSpanners' and there it's done this way.
> Btw, in the line
>                   (music-clone m 'span-direction STOP))
> all music-properties are cloned as well.
> Makes not much sense to me. P.e. why should a tweak for
> bound-details.left.text of a TextSpanner be copied in the ending
> script?
> Right now I'm not aware of any property I would want to be copied into
> the ending script.
> Or did I overlook use-cases?
> If not why not simply use a new constructed
>           (make-event-chord
>             (list (make-music
>                     'what-ever-event
>                     'span-direction
>                     1)))
> ?

Because 'what-ever-event is not known?  It's been implemented that way
from its inception in

    commit 1cdc9680f2094525103d335d80bc3950f918ed03
    Author: Han-Wen Nienhuys <[hidden email]>
    Date:   Sat Feb 3 17:45:22 2007 +0100

        New music function \endSpanners.

It probably was a nuisance to fish out the actual music type name from
the music expression, so cloning was easier?

I don't really know.

--
David Kastrup

_______________________________________________
lilypond-user mailing list
[hidden email]
https://lists.gnu.org/mailman/listinfo/lilypond-user
Reply | Threaded
Open this post in threaded view
|

Re: music-function for ending TextSpanners sometimes fail for skip-events

Thomas Morley-2
Am Mi., 24. Okt. 2018 um 16:00 Uhr schrieb David Kastrup <[hidden email]>:

>
> Thomas Morley <[hidden email]> writes:
>
> > Am Mi., 24. Okt. 2018 um 01:07 Uhr schrieb David Kastrup <[hidden email]>:
> >>
> >> Thomas Morley <[hidden email]> writes:
> >>
> >> > Am Di., 23. Okt. 2018 um 23:54 Uhr schrieb David Kastrup <[hidden email]>:
> >> >
> >> >> So you should figure out what your comparison as equal is supposed to be
> >> >> doing in the first place.
> >> >
> >> > Yep, that's the culprit.
> >> >
> >> > So the question is how to find the last rhythmic event of a
> >> > music-expression and place <>\stopTextSpan right before it.
> >>
> >> Why wouldn't you just put \stopTextSpan on it?
> >
> > The more or less single reason: because my initial posted function was
> > modeled after the built-in 'endSpanners' and there it's done this way.
> > Btw, in the line
> >                   (music-clone m 'span-direction STOP))
> > all music-properties are cloned as well.
> > Makes not much sense to me. P.e. why should a tweak for
> > bound-details.left.text of a TextSpanner be copied in the ending
> > script?
> > Right now I'm not aware of any property I would want to be copied into
> > the ending script.
> > Or did I overlook use-cases?
> > If not why not simply use a new constructed
> >           (make-event-chord
> >             (list (make-music
> >                     'what-ever-event
> >                     'span-direction
> >                     1)))
> > ?
>
> Because 'what-ever-event is not known?  It's been implemented that way
> from its inception in
>
>     commit 1cdc9680f2094525103d335d80bc3950f918ed03
>     Author: Han-Wen Nienhuys <[hidden email]>
>     Date:   Sat Feb 3 17:45:22 2007 +0100
>
>         New music function \endSpanners.
>
> It probably was a nuisance to fish out the actual music type name from
> the music expression, so cloning was easier?
>
> I don't really know.
>
> --
> David Kastrup

Well, the code below seems to work here:

endSpanners =
#(define-music-function (music) (ly:music?)
   (_i "Terminate the next spanner prematurely after exactly one note
without the need of a specific end spanner.")
   (let* ((start-span-evs
            (filter
              (lambda (ev) (eqv? (ly:music-property ev 'span-direction) START))
              (extract-typed-music music 'span-event)))
          (stop-span-evs
            (map
              (lambda (m)
                (make-music (ly:music-property m 'name) 'span-direction STOP))
              start-span-evs))
          (end-ev-chord (make-event-chord stop-span-evs))
          (total (make-sequential-music (list music end-ev-chord))))
     total))


%% from NR
\relative c'' {
  \endSpanners
  c2 \startTextSpan c2 c2
  \endSpanners
  c2 \< c2 c2
}


%% input/regression/music-function-end-spanners.ly
\paper{
  ragged-right = ##T
}

\relative
<< {   c''4 c c c }
   \\
   {
     \override TextSpanner.bound-details.left.text = "x"
     \endSpanners c,2\<\startTextSpan c2

   }
 >>

Seems to work sufficiently and cleaner.

I'm tempted to create a patch along these lines.
Though, there is no other usage of 'music-clone' in our code-base, afaict.
It's far to useful to risk someone deletes it lateron regarding it as
unused function.
I think a regtest for 'music-clone' should be created, probably a
snippet (which may be a doc-tagged-LSR-snippet) as well.
I'll have a look through my local usage of 'music-clone', coming back,
if I find some nice usage.

Cheers,
  Harm

_______________________________________________
lilypond-user mailing list
[hidden email]
https://lists.gnu.org/mailman/listinfo/lilypond-user
Reply | Threaded
Open this post in threaded view
|

Re: music-function for ending TextSpanners sometimes fail for skip-events

David Kastrup
Thomas Morley <[hidden email]> writes:

> I'm tempted to create a patch along these lines.
> Though, there is no other usage of 'music-clone' in our code-base, afaict.
> It's far to useful to risk someone deletes it lateron regarding it as
> unused function.

Its functionality is rather similar to ly:music-deep-copy and, actually,
make-music (which can take existing music as a template for filling in
the mutable elements of a music expression).

So there is a fair bit of redundancy here though not a perfect
replacement.

> I think a regtest for 'music-clone' should be created, probably a
> snippet (which may be a doc-tagged-LSR-snippet) as well.
> I'll have a look through my local usage of 'music-clone', coming back,
> if I find some nice usage.

--
David Kastrup

_______________________________________________
lilypond-user mailing list
[hidden email]
https://lists.gnu.org/mailman/listinfo/lilypond-user