lilypond-user
[Top][All Lists]
Advanced

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

Re: Scheme function question


From: David Kastrup
Subject: Re: Scheme function question
Date: Fri, 22 Nov 2013 15:30:23 +0100
User-agent: Gnus/5.13 (Gnus v5.13) Emacs/24.3.50 (gnu/linux)

James Worlton <address@hidden> writes:

> On Fri, Nov 22, 2013 at 6:58 AM, David Kastrup <address@hidden> wrote:
>
>>
>> See issue 3673.  If that makes it in, you can do
>>
>> doubleStem =
>> #(define-music-function
>>   (parser location note)
>>   (ly:music?)
>>   (make-relative (note) note
>>   #{
>>     << { \voiceOne $note } \new Voice { \voiceTwo $note } >> \oneVoice
>>   #}))
>>
>> and the behavior in- and outside of \relative will be just as you would
>> expect.
>>
>
> I've been looking through the documentation for references/explanations of
> make-relative and can't find anything, so I'm not sure how your proposed
> function quoted here is different than the fixed one (using $note) from
> earlier in the thread. Could you point me to the proper location? I want to
> understand this.

The only documentation so far is the function documentation string as
submitted in the patch:

(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."
...)

The examples after convert-ly has been run should also be instructive:
<URL:https://codereview.appspot.com/30890043/diff/1/input/regression/make-relative.ly>
<URL:https://codereview.appspot.com/30890043/diff/1/input/regression/make-relative-music.ly>

And the commit message:

commit c91703570c5cc606324d2c93c266023a960847fb
Author: David Kastrup <address@hidden>
Date:   Fri Nov 22 09:54:26 2013 +0100

    Make make-relative able to deal with music rather than just pitches
    
    The old behavior of make-relative was too cumbersome to use in many
    cases.
    
    This variant allows, for example:
    
    withOctave =
      (ly:music?)
      (make-relative
       (music) music
       #{ \context Bottom << $music \transpose c c' $music >> #}))
    
    \relative
    \new Staff \withOctave {
      \partial 4. c'8 e g |
      c2 e,4 g |
      c,8 c' b a <g d'> <f c'> <e b'> <d a'> |
      <c g'>1 | \bar "|."
    }


But as I said: _this_ version of make-relative has just been put into
review (yes, triggered on your mail and my dissatisfaction with not
being able to provide a simple turn-key solution).

Of course, you can just copy it into a source file of yours from the
patch, but it will not appear in the developer versions before 2.19.0,
and though I am wanting this augmented version to make it into 2.18.0 as
well, I'm not sure it will make it.


#(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.
"

  ;; pitch and music generator might be stored instead in music
  ;; properties, and it might make sense to create a music type of its
  ;; own for this kind of construct rather than using
  ;; RelativeOctaveMusic
  (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)
                                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)
                                   v))
                             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))

The definition itself is rather short and matter-of-fact, but I would
not recommend trying to understand it until you have progressed further
with Scheme.  It's tricky.

-- 
David Kastrup



reply via email to

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