Jump to content

Initial clef change: Difference between revisions

From LilyPond wiki
m Reverted edit by Ksnortum (talk) to last revision by Lemzwerg
Tag: Rollback
No edit summary
 
Line 1: Line 1:
This snippet handles initial clef changes with a custom engraver. This engraver checks whether there is a difference between the initial clef (as given by <code>\with { \clef ... }</code> and the clef at the first timestep. If any difference is encountered a new clef (spaced like a cue clef) is created, while the original clef and any key signatures are modified to look like the initial clef values.
This snippet handles initial clef changes with a custom engraver. This engraver checks whether there is a difference between the initial clef (as given by <code>\with { \clef ... }</code> and the clef at the first timestep. If any difference is encountered a new clef (spaced like a cue clef) is created, while the original clef and any key signatures are modified to look like the initial clef values.  See also the snippets [[Clef change at the beginning of a piece]] and [[Clef change at the beginning of a piece (alternative)]].


<lilypond version="2.24">
<lilypond version="2.24">

Latest revision as of 17:22, 8 April 2026

This snippet handles initial clef changes with a custom engraver. This engraver checks whether there is a difference between the initial clef (as given by \with { \clef ... } and the clef at the first timestep. If any difference is encountered a new clef (spaced like a cue clef) is created, while the original clef and any key signatures are modified to look like the initial clef values. See also the snippets Clef change at the beginning of a piece and Clef change at the beginning of a piece (alternative).

\version "2.24"

%%% This engraver records the initial clef properties (e.g. what is set by \with { \clef ... })
%%% If in the first timestep these changed, engrave the original clef, and change formatting and break
%%% alignment of the actual clef to mimic a clef change clef. Duplicates some procedure from clef engraver
%%% and could easily be integrated.
#(define (initial-clef-change-engraver context)
   (let ((initial-clef-properties #f) (cclef #f) (keysigs '()))
     ; macro for checking if any clef property has changed
     (define (clef-changed)
       (>
        (length initial-clef-properties)
        (length
         (filter
          (lambda (x) (equal? (cdr x) (ly:context-property context (car x))))
          initial-clef-properties))))
     (make-engraver
      ; Record initials propertis
      ((initialize engraver)
       (set!
        initial-clef-properties
        `((clefGlyph . ,(ly:context-property context 'clefGlyph))
          (clefPosition  . ,(ly:context-property context 'clefPosition))
          (middleCClefPosition . ,(ly:context-property context 'middleCClefPosition))
          (clefTransposition  . ,(ly:context-property context 'clefTransposition)))))
      ; Record the actual clef to adjust. Use details.muted to not acknowledge clef created by this engraver.
      (acknowledgers
       ((clef-interface engraver grob source-engraver)
        (if (not (assoc-get 'muted (ly:grob-property grob 'details) #f))
            (set! cclef grob)))
       ((key-signature-interface engraver grob source-engraver)
        (set! keysigs (cons grob keysigs))))
      ; Create a clef if necessary
      ((process-music engraver)
       (if (and initial-clef-properties (clef-changed))
           (let ((clef (ly:engraver-make-grob engraver 'Clef '())))
             (ly:grob-set-property! clef 'staff-position (assoc-get 'clefPosition initial-clef-properties))
             (ly:grob-set-property! clef 'glyph (assoc-get 'clefGlyph initial-clef-properties))
             (ly:grob-set-nested-property! clef '(details muted) #t)
             (if ((lambda (x) (and (number? x) (not (= 0 x))))
                  (assoc-get 'clefTransposition initial-clef-properties 0))
                 (let ((mod (ly:engraver-make-grob engraver 'ClefModifier '()))
                       (formatter (ly:context-property context 'clefTranspositionFormatter))
                       (style (ly:context-property context 'clefTranspositionStyle))
                       (dir (sign (assoc-get 'clefTransposition initial-clef-properties 0)))
                       (abs_trans (1+ (abs (assoc-get 'clefTransposition initial-clef-properties 0)))))
                   (if (procedure? formatter)
                       (ly:grob-set-property! mod 'text (formatter (number->string abs_trans) style)))
                   (ly:grob-set-object! mod 'side-support-elements (ly:grob-list->grob-array (list clef)))
                   (ly:grob-set-parent! mod X clef)
                   (ly:grob-set-parent! mod Y clef)
                   (ly:grob-set-property! mod 'direction dir))))))
      ; Adjust the actual clef and key signatures
      ((process-acknowledged engraver)
       (if (and cclef initial-clef-properties (clef-changed))
           (begin

            ; Key signatures need to be handled if they appear before the cue-clef
            ; This requires the break alignment information, so this only sets a
            ; hook which will be processed during `before-line-breaking` of the
            ; BreakAlignGroups
            (for-each
             (lambda (keysig)
               (let ((det (ly:grob-property keysig 'details))
                     (initial-clef-properties initial-clef-properties))
                 (ly:grob-set-property!
                  keysig
                  'details
                  (acons
                   'break-alignment-handler
                   (lambda (grob break-alignment)
                     (let ((start-of-line
                            (vector-ref
                             (ly:grob-property break-alignment 'break-align-orders)
                             2)))
                       (if (member 'cue-clef (or (member 'key-signature start-of-line) '()))
                           (ly:grob-set-property!
                            grob 'c0-position
                            (assoc-get 'middleCClefPosition initial-clef-properties)))))
                   det))))
             keysigs)

            (ly:grob-set-property! cclef 'non-default #t)
            (ly:grob-set-property! cclef 'break-align-symbol 'cue-clef)
            (if (not (eq? #t (ly:grob-property cclef 'full-size-change)))
                (ly:grob-set-property! cclef 'glyph-name
                                      (format #f "~a_change" (ly:grob-property cclef 'glyph)))))))
      ; Unset parameters
      ((stop-translation-timestep engraver)
       (set! initial-clef-properties #f)
       (set! clef #f)
       (set! keysigs '())))))

\layout {
  \context {
    \Staff
    \consists #initial-clef-change-engraver
  }
  \context {
    \Score
    \override BreakAlignGroup.before-line-breaking =
    #(lambda (grob)
       (let ((grobs (ly:grob-object grob 'elements))
             (break-alignment (ly:grob-parent grob X)))
         (if (not (null? grobs))
             (set! grobs (ly:grob-array->list grobs)))
         (for-each
          (lambda (grob)
            (let* ((det (ly:grob-property grob 'details))
                   (handler (assoc-get 'break-alignment-handler det)))
              (if (procedure? handler)
                  (handler grob break-alignment))))
          grobs)))
  }
}

\new Staff \with { \clef "bass^15" } {
  \key bes\major
  \clef "treble_8"
  c'1 c'1
  \clef bass
  c'1
}

\score {
  \layout {
    \context {
      \Score
      \override BreakAlignment.break-align-orders =
      ##((staff-ellipsis
          left-edge
          cue-end-clef
          ambitus
          breathing-sign
          optional-material-end-bracket
          signum-repetitionis
          clef
          cue-clef
          staff-bar
          key-cancellation
          key-signature
          time-signature
          optional-material-start-bracket
          custos)
         (staff-ellipsis
          left-edge
          optional-material-end-bracket
          cue-end-clef
          ambitus
          breathing-sign
          signum-repetitionis
          clef
          cue-clef
          staff-bar
          key-cancellation
          key-signature
          time-signature
          optional-material-start-bracket
          custos)
         (staff-ellipsis
          left-edge
          optional-material-end-bracket
          ambitus
          breathing-sign
          signum-repetitionis
          clef
          cue-clef
          key-cancellation
          key-signature
          time-signature
          staff-bar
          optional-material-start-bracket
          custos))
    }
  }
  \new Staff \with { \clef "bass^15" } {
    \key bes\major
    \clef "treble_8"
    c'1 c'1
    \clef bass
    c'1
  }
}

% Basic usage:
\score {
  <<
    % Just the treble clef
    \new Staff {
      \key bes \major
      c''1
    }
      
    % Just the bass clef
    \new Staff \with { \clef bass  } {
      \key bes \major
      c1
    }
    
    % Initial treble clef, then bass clef
    \new Staff {
      \key bes \major
      \clef bass 
      c1
    }
    
    % Initial bass clef, then treble clef
    \new Staff \with { \clef bass  } {
      \key bes \major
      \clef treble 
      c''1
    }
  >>

  \layout {
    \context {
      \Staff
      \consists #initial-clef-change-engraver
    }
  }
}