That's amazing Lukas! :chefkiss:
Thanks so much.
Drew
On Wed, 29 Jan 2025 at 15:47, Lukas-Fabian Moser <[email protected]> wrote:
> Hi Drew,
>
> ... which is great, since it means that the noteNameFunction gets called
>> with the current context (as opposed to only a pitch), which means we can
>> read the tonic from the context and do not need to write our own engraver
>> (whose job it would be to read the context's tonic and pass it to our note
>> NoteName grobs). The only caveat is that, by default, a NoteNames context
>> doesn't \consist the Key_engraver, so it knows nothing about tonics. So we
>> add this engraver, but then have to \omit the actual printed key signatures.
>>
> Here a new version (now only the NoteNames version) that allows specifying
> the syllable to use for the tonic (as defined by \key):
>
> \version "2.25.21"
>
> #(set-object-property! 'tonicSyllable 'translation-type? string?)
>
> #(define (normalize-pitch p)
> (ly:make-pitch 0 (ly:pitch-notename p) (ly:pitch-alteration p)))
>
> #(define (inv-assoc key alist default)
> (let ((resulting-pair
> (find (lambda (entry)
> (equal? (cdr entry)
> key))
> alist)))
> (if resulting-pair
> (car resulting-pair)
> default)))
>
> #(define-markup-command (highlight-tonic layout props arg) (markup?)
> (interpret-markup layout props
> (markup #:bold #:with-color red arg)))
>
> extract-syllables =
> #(define-scheme-function (unknown def) ((string? "?") ly:music?)
> (map
> (lambda (note-ev)
> (cons
> (normalize-pitch (ly:music-property note-ev 'pitch))
> (let ((text-script (extract-typed-music note-ev
> 'text-script-event)))
> (if (pair? text-script)
> (ly:music-property (car text-script) 'text unknown)
> unknown))))
> (extract-typed-music def 'note-event)))
>
> noteSyllable =
> #(define-scheme-function
> (unknown-syllable syllable-definition)
> ((string? "?") ly:music?)
> (let ((pitch-syllable-dictionary
> (extract-syllables unknown-syllable syllable-definition)))
> (pretty-print pitch-syllable-dictionary)
> (lambda (pitch context)
> (let* ((tonic-pitch (ly:context-property context 'tonic #{ c #}))
> (tonic-syllable (ly:context-property context 'tonicSyllable
> "do"))
> (do-offset (inv-assoc tonic-syllable
> pitch-syllable-dictionary #{ c #}))
> (syllable (assoc-get (normalize-pitch (- pitch
> (- tonic-pitch
> do-offset)))
> pitch-syllable-dictionary
> unknown-syllable)))
> (if (equal? syllable tonic-syllable)
> (make-highlight-tonic-markup syllable)
> syllable)))))
>
> #(set-global-staff-size 30)
>
> doSyllables = {
> % to be entered for c major
> c -do d -re e -mi f -fa g -so a -la b -ti
> cis -di dis -ri fis -fi gis -si ais -li
> des -ru es -mu ges -su as -lu bes -tu
> }
>
> \layout {
> ragged-right = ##t
> \context {
> \NoteNames
> \consists Key_engraver % we need this in order to manage the 'tonic'
> context property ...
> \omit KeySignature % ... but we don't want to see the key signature
> printed
> \omit KeyCancellation
> }
> }
>
> mus = \relative c' {
> c4 d e f8 fis
> g4 a b c \break
>
> \key a \major
> a,4 b cis d
> e4 f8 fis g gis a4 \break
>
> \key d \dorian
> \set tonicSyllable = re
> d,4 e f g
> a4 b c d \break
>
> \key d \minor
> \set tonicSyllable = la
> d,4 e f g
> a4 b cis d
> d c bes a
> }
>
> <<
> \new Staff \mus
> \new NoteNames \with {
> noteNameFunction = \noteSyllable \doSyllables
> } \mus
> >>
>
> Lukas
>