[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