lilypond-user
[Top][All Lists]
Advanced

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

Re: relative music inside music functions explodes when used twice


From: David Kastrup
Subject: Re: relative music inside music functions explodes when used twice
Date: Mon, 13 Oct 2014 15:21:31 +0200
User-agent: Gnus/5.13 (Gnus v5.13) Emacs/24.4.50 (gnu/linux)

Janek Warchoł <address@hidden> writes:

> 2014-10-12 12:45 GMT+02:00 David Kastrup <address@hidden>:
>> I'm not even sure I understand _how_ you want the relativization to
>> happen.  One after the other?
>
> If you mean \musicII should be relativized after \music, that's
> roughly what i want.
>
>> In that case, you can use
>>
>> voiceDivisi =
>> #(define-music-function (parser location m1 m2) (ly:music? ly:music?)
>>   (make-relative (m1 m2) #{ #m1 #m2 #}
>>   #{
>>       \tag divI { $m1 }
>>       \tag divII { $m2 }
>>       \tag together << { \dynamicUp $m1 } \\ { \dynamicDown $m2 } >>
>>   #}))
>
> Seems to work! :)
>
>> > How should i work around this?  Maybe instead of using tags i should
>> > write a function with a switch statement inside?  I know that i can
>> > put \relative command inside \voiceDivisi, but i'd like to avoid this
>> > as it would add a lot of typing.
>>
>> It seems like the ingenuity of my make-relative macro never really
>> caught on...
>
> I've found https://code.google.com/p/lilypond/issues/detail?id=3118
> and looked at input/regression/make-relative.ly but i don't think i
> really understand what it does (and how).

Probably neither do I.  Let's see.

(defmacro-public make-relative (variables reference music)
  "The list of pitch or music variables in @var{variables} is used as
a sequence for creating relativable music from @var{music}.

When the constructed music is used outside of @code{\\relative}, it
just reflects plugging in the @var{variables} into @var{music}.

The action inside of @code{\\relative}, however, is determined by
first relativizing the surrogate @var{reference} with the variables
plugged in and then using the variables relativized as a side effect
of relativizing @var{reference} for evaluating @var{music}.

Since pitches don't have the object identity required for tracing the
effect of the reference call, they are replaced @emph{only} for the
purpose of evaluating @var{reference} with simple pitched note events.

The surrogate @var{reference} expression has to be written with that
in mind.  In addition, it must @emph{not} contain @emph{copies} of
music that is supposed to be relativized but rather the
@emph{originals}.  This @emph{includes} the pitch expressions.  As a
rule, inside of @address@hidden@address@hidden variables must @emph{only} be
introduced using @code{#}, never via the copying construct @code{$}.
The reference expression will usually just be a sequential or chord
expression naming all variables in sequence, implying that following
music will be relativized according to the resulting pitch of the last
or first variable, respectively.

Since the usual purpose is to create more complex music from general
arguments and since music expression parts must not occur more than
once, one @emph{does} generally need to use copying operators in the
@emph{replacement} expression @var{music} when using an argument more
than once there.  Using an argument more than once in @var{reference},
in contrast, does not make sense.

There is another fine point to mind: @var{music} must @emph{only}
contain freshly constructed elements or copied constructs.  This will
be the case anyway for regular LilyPond code inside of
@address@hidden@address@hidden, but any other elements (apart from the
@var{variables} themselves which are already copied) must be created
or copied as well.

The reason is that it is usually permitted to change music in-place as
long as one does a @var{ly:music-deep-copy} on it, and such a copy of
the whole resulting expression will @emph{not} be able to copy
variables/values inside of closures where the information for
relativization is being stored.
"

That's some DOC string alright.  So how does it work?  Short of the doc
string, the definition is

(defmacro-public make-relative (variables reference music)
  (define ((make-relative::to-relative-callback variables music-call ref-call)
           music pitch)
    (let* ((ref-vars (map (lambda (v)
                            (if (ly:pitch? v)
                                (make-music 'NoteEvent 'pitch v)
                                (ly:music-deep-copy v)))
                          variables))
           (after-pitch (ly:make-music-relative! (apply ref-call ref-vars) 
pitch))
           (actual-vars (map (lambda (v r)
                               (if (ly:pitch? v)
                                   (ly:music-property r 'pitch)
                                   r))
                             variables ref-vars))
           (rel-music (apply music-call actual-vars)))
      (set! (ly:music-property music 'element) rel-music)
      after-pitch))
  `(make-music 'RelativeOctaveMusic
               'to-relative-callback
               (,make-relative::to-relative-callback
                (list ,@variables)
                (lambda ,variables ,music)
                (lambda ,variables ,reference))
               'element ,music))

So it creates "RelativeOctaveMusic" (which is just a wrapper for
absolute and/or relativized music) but with an additional to-relative
callback.

When used outside of \relative, the music is used verbatim (as 'element
field).  So in absolute mode, << ... \\ ... >> or anything else should
work.

But if the music is of form #{ ... #}, each copy of that music is
interpreted separately, so the changes done to 'element ,music don't
propagate to (lambda ,variables ,music).  So voicify-music will not in
generally reach in there unless you do something like

(let ((mus #{ << #m1 \\ #m2 >> #}))
  (make-relative () #{ #m1 #m2 #} mus))

In that case, the relation of the m1 and m2 used in the input no longer
travel into the macro via the macro parameters but via the variables
itself.  I have no actual idea what this would do, really.

At any rate, you are better off not putting _anything_ into the argument
of make-relative that would require scorification (like << \\ >> or
chord repeats or pitch-less durations) for now.

This is really hairy stuff, and maybe I did not properly think it
through.  Still, it tends to produce better results than most other
approaches.

-- 
David Kastrup




reply via email to

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