Hi all,
I discovered some useful LilyPond hooks: "toplevel-music-handler" and
friends.
Often I will create a version of a piece that shows it on two types of
staff, as simultaneous music. For example, the same music on a regular
Staff and a TabStaff. That's easy enough to do by putting the music in
variables and setting up a score block with parallel staves, etc.
But the file attached shows how you can use these hooks to intercept and
modify the music on the fly with scheme. This lets you include a file
like the one attached (e.g. via a command line option) to create output
with two types of staff, without having to modify the music file.
Anyway, just a neat little trick. I'm sure there are other potential
uses for these hooks. I thought I'd share my file in case anyone else
would find it useful. Still a work in progress, proof of concept, not
well tested, won't work for all possible input, etc.
Cheers,
-Paul||||
\version "2.19.42"
clonedStaffType = #'TabStaff
% try also VaticanaStaff MensuralStaff PetrucciStaff KievanStaff etc.
#(define (clone-music-to-simultaneous-staff music staff-type)
"Takes music and copies it, converts Staff contexts to
staff-type in the copied version, and returns simultaneous
music with the original and the copy."
(let* ((music2 (ly:music-deep-copy music))
(name (ly:music-property music2 'name #f))
;; add a Staff context if needed
(music3 (cond
;; TODO: handle simultaneous music,
;; nested music with/without contexts, etc. etc.
((eq? name 'ContextSpeccedMusic) music2)
((eq? name 'SimultaneousMusic) music2)
;; (eq? name 'SequentialMusic)
;; (ly:music-property music2 'context-type #f)
(else #{ \new Staff { #music2 } #})))
;; convert all Staff contexts to staff-type instead
(music4 (map-some-music
(lambda (evt)
;; (display evt)(newline)(newline)
(cond
((eq? 'Staff (ly:music-property evt 'context-type))
(ly:music-set-property! evt 'context-type staff-type)
evt)
(else #f)))
music3)))
;; (display name)(newline)
(make-simultaneous-music (list music music4))))
#(define (modify-score-music score proc arg)
"Takes a score and a procedure proc, applies proc
to the score's music and returns the new score.
arg is just passed to proc as its 2nd argument."
(let* ((header (ly:score-header score))
(output-defs (ly:score-output-defs score))
(music (ly:score-music score))
(music2 (proc music arg))
(score2 (ly:make-score music2)))
;; restore any headers and output-defs
(if (not (null? header))
(ly:score-set-header! score2 header))
(if (not (null? output-defs))
(for-each (lambda (def)
(ly:score-add-output-def! score2 def))
output-defs))
score2))
% see ../scm/lily-library.scm
#(define (my-collect-scores-for-book score)
(ly:parser-define! 'toplevel-scores
(cons
(modify-score-music
score
clone-music-to-simultaneous-staff
clonedStaffType)
(ly:parser-lookup 'toplevel-scores))))
#(define (my-collect-music-for-book music)
"Top-level music handler."
(collect-music-aux (lambda (score)
(my-collect-scores-for-book score))
music))
% see ../ly/declarations-init.ly
#(define toplevel-score-handler my-collect-scores-for-book)
#(define toplevel-music-handler my-collect-music-for-book)
%%%%%%%%%%%%%%%%%%%%%
% Demo music
%{
{ c' }
\score {
d'
}
\score {
{ dis' }
}
\new Staff {
e'
}
\score {
\new Staff {
f'
}
}
\score {
% \new PianoStaff
<<
\new Staff
{ c' }
\new Staff
{ e' }
>>
}
% not smart enough for this kind of thing yet...
<<
{ c' }
{ e' }
>>
%}
_______________________________________________
lilypond-user mailing list
[email protected]
https://lists.gnu.org/mailman/listinfo/lilypond-user