Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Define function inside \score in LilyPond

I compile a large song book, and for that I would like to have many local definitions of functions, that will, in the end, be in an \include d file, but that makes no difference here. For this, I need to define the functions inside \score{ ... } scope. However, LilyPond keeps throwing errors.

The non-working example:

\version "2.17.26"

\book {

    \header {
        title = "This is a book"
    }

    \score {
        xyz = { a' b' c'' }
        abc = #(define-music-function
            ( parser location musicnotes )
            ( ly:music? )
            #{
                c' $musicnotes e'
            #}
        )
        { \abc { d' } f' \xyz }
        \header {
            piece = "First piece"
            opus = "op. 1024"
        }
    }

    \score {
        xyz = { a' a' a' }
        abc = #(define-music-function
            ( parser location musicnotes )
            ( ly:music? )
            #{
                e' $musicnotes c'
            #}
        )
        { \abc { d' } f' \xyz }
        \header {
            piece = "Second piece"
            opus = "op. 1025"
        }
    }

}

Throws an error:

test.ly:10:17: error: unrecognized string, not in text script or \lyricmode   
           xyz = { a' b' c'' }

The following works, however, I have to give the functions unique names, which is frowned upon.

\version "2.17.26"

xyz = { a' b' c'' }
abc = #(define-music-function
    ( parser location musicnotes )
    ( ly:music? )
    #{
        c' $musicnotes e'
    #}
)

xxyz = { a' a' a' }
aabc = #(define-music-function
    ( parser location musicnotes )
    ( ly:music? )
    #{
        e' $musicnotes c'
    #}
)

\book {

    \header {
        title = "This is a book"
    }

    \score {
        { \abc { d' } f' \xyz }
        \header {
            piece = "First piece"
            opus = "op. 1024"
        }
    }

    \score {
        { \aabc { d' } f' \xxyz }
        \header {
            piece = "Second piece"
            opus = "op. 1025"
        }
    }

}
like image 671
yo' Avatar asked Dec 31 '13 13:12

yo'


2 Answers

Unfortunatey, it's not possible to stick assignments in a score. You can only put assignments in the following places:

  • the top level,
  • inside \display, \header, and \midi blocks

The LilyPond grammar makes this quite clear, even if the rest of the manual is a bit evasive about it. (Look at http://lilypond.org/doc/v2.17/Documentation/contributor/lilypond-grammar , and look for where the assignment rule gets used).

Assuming your assignments are not appropriate for the blocks listed above (which is definitely the case in this example), and assuming that you don't want to do something exotic like go and define your own Scheme modules and figure out how to use them in your LilyPond file, you have two choices:

  1. Define xyz and abc, then define the music that will go into the first score. Then redefine xyz and abc before defining the music for the next score. This works because assignments overwrite whatever was previously there, and because LilyPond defines are generally processed in order. However, if you want some of your defines to be used in both scores and to be the same, you may get confused.
  2. Settle for your approach, though I would pick a prefix or a suffix that makes it clearer which score the define goes with.

The first option would look something like this:

\version "2.18.0"
xyz = { a' b' c'' }
abc = #(define-music-function (parser location musicnotes)
  (ly:music?)
  #{ c' $musicnotes e' #})
smus_a = { \abc { d' } f' \xyz }

xyz = { a' a' a' }
abc = #(define-music-function (parser location musicnotes)
  (ly:music?)
  #{ e' $musicnotes c' #})
smus_b = { \abc { d' } f' \xyz }

\book {
  \header {
    title = "A Book!"
  }
  \score {
    \smus_a
    \header { piece = "First piece" }
  }
  \score {
    \smus_b
    \header { piece = "Second piece" }
  }
}

This also works if the music-defining parts are refactored out into separate LilyPond source files.

like image 81
Owen S. Avatar answered Nov 11 '22 00:11

Owen S.


It is possible! But you have to define a command to define the variable or command:

parserDefine =
#(define-void-function (parser location name val)(symbol? scheme?)
    (ly:parser-define! parser name val))

This is a void-function and can be called almost anywhere:

\score {
    {
        % you have to be in your music-expression
        \parserDefine xyz { a' a' a' }
        % There must be something between parserDefine and the call!
        c'' \xyz
        \parserDefine abc #(define-music-function
            ( parser location musicnotes )
            ( ly:music? )
            #{
                c' $musicnotes e'
            #}
            )
         a' \abc d'
    }
}

If the command is defined, you can call inside your music expressions. After you have done so, the parser needs a little lookahead, so that the variable really is available - here its the c''. You can optionally wrap the expression in another pair of curly braces.

like image 36
Jan-Peter Voigt Avatar answered Nov 11 '22 01:11

Jan-Peter Voigt