lilypond-devel
[Top][All Lists]
Advanced

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

Re: (doc help) escaping a ly:music? variable


From: Nicolas Sceaux
Subject: Re: (doc help) escaping a ly:music? variable
Date: Wed, 03 May 2006 22:21:48 +0200
User-agent: Gnus/5.11 (Gnus v5.11) Emacs/22.0.50 (darwin)

Mats Bengtsson <address@hidden> writes:

> Quoting Graham Percival <address@hidden>:
>
>> Even music expressions can be passed in.  Note that since we
>> want an articulation attached to the second variable, we
>> must #####.
>>
>> pattern = #(define-music-function (parser location x y) (ly:music? ly:music?)
>> #{
>>   $x e8-. a-. b-. $y-.-> b-. a-. e-.
>> #})
>>
>> \relative c''{
>>   \pattern c8 c8
>>   \pattern d8 ais8
>>   \pattern cis8 des8
>> }

> One ugly solution is to attach the articulation to a spacer note
> in parallel to the music in the second variable.
>
> pattern = #(define-music-function (parser location x y) (ly:music? ly:music?)
> #{
>   $x e8-. a-. b-. << $y s1*0-.-> >> b8-. a-. e-.
> #})
>
> \relative c''{
>   \pattern c8 c8
>   \pattern d8 ais8
>   \pattern cis8 des8
> }
>
> Otherwise, I guess that you have to use Scheme constructs.

Yes.
To Graham:

A $variable inside the #{ #} notation is like using a regular \variable
in classical LilyPond notation. The following:

  { \music -. -> }

is not legal LilyPond.

When you have to manually build music expression using scheme,  things
become a bit more difficult. Here is the methodology:

1) write in classic LilyPond notation the expression you want to
   generate, for instance: c-^

2) Use the \displayMusic music function to see how such an expression is
   internal represented in scheme:

   \displayMusic c-^
==>
(make-music 'EventChord
  'elements (list (make-music 'NoteEvent
                    'duration (ly:make-duration 2 0 1 1)
                    'pitch    (ly:make-pitch -1 0 0))
                  (make-music 'ArticulationEvent
                    'articulation-type "marcato")))

  \displayMusic c
==>
(make-music 'EventChord
  'elements (list (make-music 'NoteEvent
                    'duration (ly:make-duration 2 0 1 1)
                    'pitch    (ly:make-pitch -1 0 0))))

You see that a note (c) is represented as an EventChord expression, with
a NoteEvent expression in its elements list. To add a marcato
articulation, an ArticulationEvent expression has to be added to the
elements property of the EventChord expression. You have to know some
bits of scheme to do that. I'll write the function as a whole, then
explain the different parts.

3) write a function that builds the expression.

  (define (add-marcato event-chord)
    "Add a marcato ArticulationEvent to the elements of `event-chord',
    which is supposed to be an EventChord expression."
    (let ((result-event-chord (ly:music-deep-copy event-chord)))
      (set! (ly:music-property result-event-chord 'elements)
            (cons (make-music 'ArticulationEvent
                    'articulation-type "marcato")
                  (ly:music-property result-event-chord 'elements)))
      result-event-chord))

  (define (add-marcato event-chord)

is the way to define a function in scheme: add-marcato is the function
name, and event-chord is the name of the argument. Thus this function
takes one argument, which should be, as its name implies, an EventChord
expression. In scheme, what kind the arguments should be is often told
by the name hey are given.

    "Add a MarcatoEvent to the elements of `event-chord',
    which is supposed to be an EventChord expression."

The following lines is a description of what does the function. It is
not mandatory to add a documentation string to a function, but it
helps. the following lines are the body of the function:

    (let ((result-event-chord (ly:music-deep-copy event-chord)))

`let' is used to declare local variables. Here we use one local
variable, named `result-event-chord', to which we give the value 
  (ly:music-deep-copy event-chord)
`ly:music-deep-copy' is a function specific to LilyPond, like all
functions prefixed by "ly:". It is use to make a copy of a music
expression. Here, we copy the parameter of the function,
`event-chord'. Remember: the purpose is to add a marcato to a EventChord
expression. It's better to not modify the EventChord which is given as
an argument, because it may be used elsewhere.

Thus we now have a result-event-chord, which is a NoteEventChord
expression, copy of event-chord. In the following expression, we
actually add the marcato to its elements list property. the syntax is
  (set! place new-value)
Here, what we want to set (the "place") is the "elements" property of
result-event-chord expression:
  (ly:music-property result-event-chord 'elements)
ly:music-property is the function used to access music properties (the
'elements, 'duration, 'pitch, etc, you see in the \displayMusic output
above).
the new value is the former elemnts property, with an extra thing: the
MarcatoEvent expression, that we copy-paste from the \displayMusic
output:
  (cons (make-music 'ArticulationEvent
          'articulation-type "marcato")
        (ly:music-property result-event-chord 'elements))
`cons' is used to add an element to a list. This is what we want: the
same list as before + the new ArticulationEvent expression. The order
inside the elements property is not important here.

And finally, once we have added the MarcatoEvent to its elements
property, we can return the result-event-chord, hence the last line of
the function.

4) make it a music function and test it
It's easier to test a function that builds music expression with a music
function. Thus, we transform the add-marcato function into a music
function.

addMarcato = 
#(define-music-function (parser location event-chord)
                        (ly:music?)
    "Add a marcato ArticulationEvent to the elements of `event-chord',
    which is supposed to be an EventChord expression."
    (let ((result-event-chord (ly:music-deep-copy event-chord)))
      (set! (ly:music-property result-event-chord 'elements)
            (cons (make-music 'ArticulationEvent
                    'articulation-type "marcato")
                  (ly:music-property result-event-chord 'elements)))
      result-event-chord))

First, we see the name that is given to the music function: addMarcato
(a LilyPond variable). It is given a value: the music function defined
using `define-music-function', which as the following form:

  (define-music-function (<parser> <location> <arg1> <arg2> ...)
                         (<arg1-type-predicate> <arg2-type-predicate> ...)
    ...function body...)

The `parser' and `location' argument are mandatory, and used in some
more advanced situations. the parser argument is used for instance to
access to the value of another LilyPond variable. The location argument
is used to set the "origin" of the music expression that is built by the
music function, so that, in case of syntax error, the lilypond compiler
should tell the user an appropriate place to look in the input file.

After those two mandatory arguments, we recognize the `event-chord'
argument of our former `add-marcato' function. Following is a list of
type predicate for each parameter of the function (except parser and
location). A type predicate is function telling if its argument is of
the given type or not. A type predicate often used in music function is
`ly:music?', which tells whether something is a music expression. Some
other type predicate are: markup?, number?, string?, list?,  which check
if an object is a markup, a number, a string, or a list,
respectively. It is a convention in scheme to name a type predicate with
the name of the type, followed by a question mark.

Here, we have only one type predicate in the list, ly:music?, because
the function as only one argument besides parser and location:
event-chord, which should be an EventChord, that is more generally
speaking a music expression.

Following is the body of the add-marcato function we have written above.

We can now check whether this function does the job we hope:

\displayMusic \addMarcato c
==>
(make-music 'EventChord
  'elements (list (make-music 'ArticulationEvent
                    'articulation-type "marcato")
                  (make-music 'NoteEvent
                    'duration (ly:make-duration 2 0 1 1)
                    'pitch    (ly:make-pitch -1 0 0))))

except the order of the ArticulationEvent and NoteEvent elements (not
important in that case), this is the same expression tht we obtained by
doing:

\displayMusic c-^
==>
(make-music 'EventChord
  'elements (list (make-music 'NoteEvent
                    'duration (ly:make-duration 2 0 1 1)
                    'pitch    (ly:make-pitch -1 0 0))
                  (make-music 'ArticulationEvent
                    'articulation-type "marcato")))

[it's late, short story for the rest]

#(define (add-articulation articulation event-chord)
   "Add the given articulation type ArticulationEvent to the elements
   of `event-chord', which is supposed to be an EventChord expression."
    (let ((result-event-chord (ly:music-deep-copy event-chord)))
      (set! (ly:music-property result-event-chord 'elements)
            (cons (make-music 'ArticulationEvent
                    'articulation-type articulation)
                  (ly:music-property result-event-chord 'elements)))
      result-event-chord))

pattern = 
#(define-music-function (parser location x y) 
                        (ly:music? ly:music?)
  (let ((x-staccato (add-articulation "staccato" x))
        (y-staccato-accent (add-articulation "marcato"
                              (add-articulation "accent" y))))
    #{
       $x-staccato e8-. a-. b-. $y-staccato-accent b-. a-. e-.
    #}))

\relative c''{
  \pattern c8 c8
  \pattern d8 ais8
  \pattern cis8 des8
}

nicolas




reply via email to

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