Hi Dave,
unfortunately I don't have enough time at the moment to think about your
underlying problem and what the best solution in LilyPond might be
(maybe other people will be willing to chime in), but I can try to
answer your specific questions:
First, in the manual, for both define-scheme-function and
define-music-function, it seems to say that you need to declare the
types of the parameters. So how come 'parser' and 'location' are not
specified? I couldn't find anything in the documentation about the
first two parameters being the parser and the location?
parser and location were extra arguments that had to be mentioned in the
definition of any scheme- or music function, even though they are rarely
needed explicitly. They do not have corresponding "type declarations"
(to be precise: predicates that are supposed to nod if the parameters
the function is called with are of an acceptable type), so it's normal
to have two less predicates than formal parameters:
(define-music-function (parser location var1 var2 ... varn) (pred1?
pred2? ... predn?) ...)
_But_ changes in LilyPond have made it possible to omit parser/location
in most function definitions in the 2.20 and 2.21 versions. If you use a
current version of LilyPond, you may just write
(define-music-function (var1 var2 ... varn) (pred1? pred2? ... predn?) ...)
which is a small step for your fingers at the keyboard but a giant leap
in intuitiveness of the syntax :-).
The second question is: why the double definition? Why define a music
function inside a Scheme function? My first attempts at achieving this
before I found the above (which were moderately successful but much
more complicated than the above) used only a music function. Does the
original author know something I don't about how this all works (my
default assumption) or is it unnecessary?
The function you posted is a bit involved in that it defines a function
that creates a function. The assignment
voicing = \makevoicing < g a c e a >
assigns the name \voicing to the _result_ of \makevoicing < g a c e a >,
which is
(define-music-function (parser location d) (ly:duration?)
(map-some-music
(lambda (m)
(and (ly:duration? (ly:music-property m 'duration))
(begin
(set! (ly:music-property m 'duration) d)
m)))
(ly:music-deep-copy m))))
where now m is a music variable containig the chord < g a c e a >.
Hence, \voicing is now a _function_ (in which the chord <g a c e a> is
hard-wired) accepting a duration.
The same may be achieved in current LilyPond (2.20 and above) by the
\etc idiom:
\version "2.20.0"
chordVoicing =
#(define-music-function (m d) (ly:music? ly:duration?)
(map-some-music
(lambda (m)
(and (ly:duration? (ly:music-property m 'duration))
(begin
(set! (ly:music-property m 'duration) d)
m)))
(ly:music-deep-copy m)))
voicing = \chordVoicing <g a c e a> \etc % dom7, with 7th in bass
\new Staff {
\voicing 4 \voicing 8 \voicing 16.
}
That is, you can now define a function accepting multiple parameters and
define a "partial" function call inserting only some of these parameters.
HTH
Lukas