lilypond-user
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: Automatic Clef Change


From: Jean Abou Samra
Subject: Re: Automatic Clef Change
Date: Fri, 7 Jan 2022 00:00:42 +0100
User-agent: Mozilla/5.0 (X11; Linux x86_64; rv:91.0) Gecko/20100101 Thunderbird/91.3.1

Le 05/01/2022 à 07:51, Calvin Ransom a écrit :
Hi everyone,
I want to avoid excessive ledger lines in my score and have LilyPond change the clef automatically. I am creating a computer generated piece with LilyPond that covers the entire pitch gamut. \autochange does not work for this application because it can only use two clefs and it uses two staves. I would like to use the treble, bass and the octave transposition bass and treble clefs. Does anyone have a pre-existing function for this or for something similar?
Regards,


It is relatively straightforward to implement
this in its basic principle, but much less
straightforward to define an algorithm to choose
the clef at each point. Here is a toy implementation.
It computes the set of clefs that make all notes
printed without ledgers (or on the first ledger)
and picks one by preference order: treble, bass,
treble^8, bass_8. If this is too simple and you
need better heuristics, it would be helpful if you
showed the piece or an excerpt so we get an idea.


\version "2.22.1"

#(use-modules (ice-9 match)
              (scm display-lily))

#(define (pitch<=? a b)
   (or (ly:pitch<? a b)
       (equal? a b)))

%% Maps clef names to the range of pitches
%% that can be used with them.  Ordered by
%% preference, earlier entries are picked first
%% if possible.
#(define clef-range-alist
   `(("treble" . (,#{ c' #} . ,#{ a'' #}))
     ("bass" . (,#{ e, #} . ,#{ c' #}))
     ("treble^8" . (,#{ c'' #} . ,#{ a''' #}))
     ("bass_8" . (,#{ e,, #} . ,#{ c #}))))

#(define (allowable-clefs pitch)
   (filter-map
    (match-lambda
      ((clef . (min-pitch . max-pitch))
         (and (pitch<=? min-pitch pitch)
              (pitch<=? pitch max-pitch)
              clef)))
    clef-range-alist))

#(define allowable-clef-names (map car clef-range-alist))

#(define (apply-clef! context clef-input)
   (let* ((clef-set (clef clef-input))
          (sequential (ly:music-property clef-set 'element))
          (elements (ly:music-property sequential 'elements)))
     (for-each
      (lambda (elt)
        (with-music-match
         (elt (music 'PropertySet value ?value symbol ?symbol))
         (ly:context-set-property! context ?symbol ?value))
        (with-music-match
         (elt (music 'ApplyContext procedure ?procedure))
         (?procedure context)))
      elements)))


#(define (Auto_clef_engraver context)
   (let ((allowable allowable-clef-names)
         (previous-clef #f))
     (make-engraver
       (listeners
         ((note-event engraver event)
            (let* ((pitch (ly:event-property event 'pitch)))
              (set! allowable
                    (lset-intersection eq? allowable (allowable-clefs pitch)))
              (apply-clef! context
                           (if (null? allowable)
                               (begin
                                 (ly:event-warning event
                                                   "No clef keeping all notes in range found")
                                 "treble")
                               (car allowable))))))
       ((stop-translation-timestep engraver)
          (set! allowable allowable-clef-names)))))

\layout {
  \context {
    \Staff
    \consists #Auto_clef_engraver
  }
}

\relative {
  e,, f g a b c d e f g a b c d e f g a b
  c d e f g a b c d e f g a b c d e f g a
}

{
  <c' c''>1
  <c'' c'''>
  <g c'>
  <e, c'>
  <c, c>
}


Best,
Jean




reply via email to

[Prev in Thread] Current Thread [Next in Thread]