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

Reply via email to