3

I have a moderately complicated, repetitive section in a piece. Thus, I want to use a shortcut to typeset it all.

However, the dynamic can change. For example, sometimes I will want to attach a \sf, sometimes an \ff, and other times nothing.

This is what I've come up with, but it doesn't work (variables read as do-sf and do-ff):

\version "2.22.1"

dramaticEnding = #(define-music-function
    (dosf doff)
    (boolean? boolean?)
    #{
        c #(cond
            (dosf \sf)
            (doff \ff))
        c
    #})

\relative c'' {
    \dramaticEnding ##t ##f
}

In the actual use case, the pitch is also a variable. Of course, I am open to other interfaces, like passing the dynamic as a variable.

2 Answers 2

4

There are two things fundamentally wrong here. First defining a new music function uses the command define-music-function, not make-music-function. Second to access something like \sf from within scheme code you need to omit the backslash. (\value will try to resolve value in the global scheme scope. If this is a music object a copy of that object is returned, if it is a music function it will try to determine the arguments and evaluate it). So you need to do

dramaticEnding = #(define-music-function
    (dosf doff)
    (boolean? boolean?)
    #{
        c #(cond
            (dosf sf)
            (doff ff))
        c
    #})

or to be on the save side even

\version "2.22.1"

dramaticEnding = #(define-music-function
    (dosf doff)
    (boolean? boolean?)
    #{
        c #(cond
            (dosf (ly:music-deep-copy sf))
            (doff (ly:music-deep-copy ff)))
        c
    #})

\relative c'' {
    \dramaticEnding ##t ##f
    \dramaticEnding ##f ##f
}

Alternatively you can create a parsing environment by #{ \sf #}. This has the advantage that lilypond will automatically be able to attach location information to the event:

\version "2.22.1"

dramaticEnding = #(define-music-function
    (dosf doff)
    (boolean? boolean?)
    #{
        c #(cond
            (dosf #{ \sf #})
            (doff #{ \ff #}))
        c
    #})

\relative c'' {
    \dramaticEnding ##t ##f
    \dramaticEnding ##f ##f
}

Alternatively though you might want to create the function rather to take hooks for you to specify the dynamics:

\version "2.22.1"

dramaticEnding = #(define-music-function
    (dyn1) ; for more places add more arguments
    ((lambda (x) (or (ly:music? x) (not x))))
    #{
        c $(if dyn1 dyn1)
        c
    #})

\relative c'' {
    \dramaticEnding \sf
    \dramaticEnding \ff
    \dramaticEnding ##f
}
1
  • Thanks, exactly what I need! I edited my question to remove my stupid make-music-function mistake.
    – hsgg
    Commented Mar 15, 2023 at 3:50
2

As an "frame challenge", you could also accomplish this by putting the dynamics in their own Dynamics context instead of "attaching" the dynamics to the notes. (See the last example in the "Dynamics" section of this LilyPond doc page, right before "Selected Snippets".) So something like this should work:

repeatednotes= {c4 d e f g a b c}

<<
  \new Staff \relative c'' {
    \repeat unfold 2 {\repeatednotes}
  }
  \new Dynamics {
    s1*2\sf |
    s1*2\ff
 }
>>
3
  • Hopefully this is relatively close to functional code; I don't have LilyPond installed on the computer I'm currently working at. If there are tweaks needed to make this work, I welcome edits to this answer. Commented Mar 15, 2023 at 13:47
  • The code works fine, it's just in a funny octave. Commented Mar 15, 2023 at 14:00
  • This could also go as the accepted answer. Very elegant!
    – hsgg
    Commented Mar 16, 2023 at 3:57

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.