Hello, I've created this small mockup of how we can store additional information in scores and let Lilypond manage it. The idea is: One might have different versions of a part of a score, be it different sources or different edits within a source.
So instead of selecting some version (and maybe putting the other ones into a critical apparatus) we can have Lilypond store this additional information and then do the selecting programmatically. As I said this is a mockup and is missing many important features, like detailed overrides, nested edits and such. Cheers, Valentin
\version "2.22"
%%% includeable BACKEND, content at line 225
#(define EDIT_DEBUG_MODE 'warning)
%{
Takes a list of edit ids and music and assigns ids to music. ids can be given as pair
'(id . val) where val is the local prevalence of the edit.
%}
edits =
#(define-music-function (ids music) (list? ly:music?)
(define (get-symbol tag) (if (symbol? tag) tag (car tag)))
(define (tag-music id music)
(ly:music-set-property! music 'edit-id (get-symbol id)))
(map tag-music ids (ly:music-property music 'elements))
(ly:music-set-property! music 'edit-ids ids)
music)
%{
Pick specific edits. Collisions are resolved using prevalence value. Triggers a
warning if in some place no specified edit exists. Change the value of
EDIT_DEBUG_MODE to 'suppress to not trigger a warning or to anything else to
trigger an error if this happens.
%}
selectEdits =
#(define-music-function (edit-ids-prevs music) (list? ly:music?)
(define (get-symbol tag) (if (symbol? tag) tag (car tag)))
(define (get-prev tag) (if (symbol? tag) 0 (cdr tag)))
(define (symbol-prev tag) (cons (get-symbol tag) (get-prev tag)))
(define ((complete-prev prev-vals) tag)
(if (pair? tag) tag
(cons tag (assoc-get tag prev-vals 0))))
(define (handle-hub music)
(let* ((local-tags (ly:music-property music 'edit-ids))
(edit-ids (map get-symbol edit-ids-prevs))
(prev-vals (map symbol-prev edit-ids-prevs))
(filtered-tags (filter (lambda (x) (member (get-symbol x) edit-ids)) local-tags))
(preved-tags (map (complete-prev prev-vals) filtered-tags))
(max-tag (car (reduce (lambda (x y) (if (> (cdr x) (cdr y)) x y)) (cons #f #f) preved-tags)))
(music-length (ly:music-length music))
(elements (ly:music-property music 'elements))
(selected-elements (if max-tag (filter (lambda (x) (equal? (ly:music-property x 'edit-id) max-tag)) elements) '()))
(tmp-joined-element (make-music 'SimultaneousMusic 'elements selected-elements))
(selected-length (ly:music-length tmp-joined-element))
(adjusted-elements (if (equal? selected-length music-length) selected-elements
(append selected-elements (list (skip-of-length music))))))
(ly:music-set-property! music 'elements adjusted-elements)
(if (not max-tag)
(if (equal? EDIT_DEBUG_MODE 'warning)
(ly:music-warning music "No available edit ID was selected!")
(if (not (equal? EDIT_DEBUG_MODE 'suppress))
(ly:music-error music "No available edit ID was selected!"))))))
(define (iter music)
(let ((elt (ly:music-property music 'element))
(elts (ly:music-property music 'elements))
(edit-ids (ly:music-property music 'edit-ids)))
(if (null? edit-ids)
(begin
(if (not (null? elt)) (iter elt))
(map iter elts))
(handle-hub music))))
(iter music)
music)
%{
Color specific edits some way
%}
colorEdits =
#(define-music-function (edit-colors grobs music) (list? list? ly:music?)
(define (color-tweaks grobs color)
(if (null? grobs) '()
(cons (cons (cons (car grobs) 'color) color) (color-tweaks (cdr grobs) color))))
(define (iter music current-color)
(let* ((elt (ly:music-property music 'element))
(elts (ly:music-property music 'elements))
(edit-id (ly:music-property music 'edit-id))
(this-color (assoc-get edit-id edit-colors current-color))
(tweaks (ly:music-property music 'tweaks)))
(if (not (null? this-color))
(ly:music-set-property! music 'tweaks (append tweaks (color-tweaks grobs this-color))))
(if (not (null? elt)) (iter elt this-color))
(map (lambda (x) (iter x this-color)) elts)))
(iter music '())
music)
%{
Add footnotes to edits
%}
annotateEdits =
#(define-music-function (edit-details music) (list? ly:music?)
(define (iter music carry-edit)
(let* ((elt (ly:music-property music 'element))
(elts (ly:music-property music 'elements))
(edit-id (ly:music-property music 'edit-id))
(edit-det-raw (assoc-get edit-id edit-details #f))
(edit-det (if edit-det-raw edit-det-raw carry-edit))
(tweaks (ly:music-property music 'tweaks))
(fn-music (cons 'footnote-music
(make-music
'FootnoteEvent
'footnote-text edit-det
'text (markup #:null)
'automatically-numbered #t
'Y-offset -1
'X-offset 0)))
(footnoteable (or (music-is-of-type? music 'note-event) (music-is-of-type? music 'event-chord)))
(new-carry (if footnoteable #f edit-det)))
(if (not (null? edit-det))
(if footnoteable
(begin (ly:music-set-property! music 'tweaks `(,fn-music . ,tweaks)) #f)
(let ((new-carry-b (if (not (null? elt)) (iter elt new-carry) new-carry)))
(fold (lambda (x y) (if y (iter x y) y)) new-carry-b elts)))
(begin
(if (not (null? elt)) (iter elt '()))
(map (lambda (x) (iter x '())) elts)))))
(iter music '())
music)
%{
helper for creating a footnote score
%}
createScore=
#(define-scheme-function (music) (ly:music?)
#{
\score {
\new Staff { #music }
\layout {
indent = 0
\override Score.Clef.space-alist.first-note = #'(minimum-fixed-space . 3.3)
\context {
\Staff
fontSize = #(magnification->font-size 0.55)
\override StaffSymbol.staff-space = #0.55
\omit KeySignature
\omit TimeSignature
}
}
}
#})
%{
Select some edits and place the rest as footnote
%}
annotateAlternateEdits =
#(define-music-function (edit-ids-prevs edit-details music) (list? list? ly:music?)
(define (get-symbol tag) (if (symbol? tag) tag (car tag)))
(define (get-prev tag) (if (symbol? tag) 0 (cdr tag)))
(define (symbol-prev tag) (cons (get-symbol tag) (get-prev tag)))
(define ((complete-prev prev-vals) tag)
(if (pair? tag) tag
(cons tag (assoc-get tag prev-vals 0))))
(define (create-footnote-markup elts)
(if (null? elts) ""
(let* ((elt (car elts))
(ed-id (ly:music-property elt 'edit-id))
(ed-det (assoc-get ed-id edit-details ""))
(score-m (markup ed-det #:raise 0.9 #:score (createScore elt) #:hspace 3))) ; (ly:make-score elt))))
(markup score-m (create-footnote-markup (cdr elts))))))
(define (create-footnote music un-elts todo)
(let* ((elt (ly:music-property music 'element))
(elts (ly:music-property music 'elements))
(tweaks (ly:music-property music 'tweaks))
(footnoteable (or (music-is-of-type? music 'note-event) (music-is-of-type? music 'event-chord)))
(fn-music (if footnoteable
(cons 'footnote-music
(make-music
'FootnoteEvent
'footnote-text (create-footnote-markup un-elts)
'text (markup #:null)
'automatically-numbered #t
'Y-offset -1
'X-offset 0)))))
(if footnoteable
(begin (ly:music-set-property! music 'tweaks `(,fn-music . ,tweaks)) #f)
(fold
(lambda (x y) (if y (create-footnote x un-elts y) y))
(if (null? elt) #t (create-footnote elt un-elts #t))
elts))))
(define (handle-hub music)
(let* ((local-tags (ly:music-property music 'edit-ids))
(edit-ids (map get-symbol edit-ids-prevs))
(prev-vals (map symbol-prev edit-ids-prevs))
(filtered-tags (filter (lambda (x) (member (get-symbol x) edit-ids)) local-tags))
(preved-tags (map (complete-prev prev-vals) filtered-tags))
(max-tag (car (reduce (lambda (x y) (if (> (cdr x) (cdr y)) x y)) (cons #f #f) preved-tags)))
(music-length (ly:music-length music))
(elements (ly:music-property music 'elements))
(selected-elements (if max-tag (filter (lambda (x) (equal? (ly:music-property x 'edit-id) max-tag)) elements) '()))
(unselected-elements (if max-tag (filter (lambda (x) (not (equal? (ly:music-property x 'edit-id) max-tag))) elements) '()))
(tmp-joined-element (make-music 'SimultaneousMusic 'elements selected-elements))
(selected-length (ly:music-length tmp-joined-element))
(adjusted-elements (if (equal? selected-length music-length) selected-elements
(append selected-elements (list (skip-of-length music))))))
(ly:music-set-property! music 'elements adjusted-elements)
(create-footnote music unselected-elements #t)
(if (not max-tag)
(if (equal? EDIT_DEBUG_MODE 'warning)
(ly:music-warning music "No available edit ID was selected!")
(if (not (equal? EDIT_DEBUG_MODE 'suppress))
(ly:music-error music "No available edit ID was selected!"))))))
(define (iter music)
(let ((elt (ly:music-property music 'element))
(elts (ly:music-property music 'elements))
(edit-ids (ly:music-property music 'edit-ids)))
(if (null? edit-ids)
(begin
(if (not (null? elt)) (iter elt))
(map iter elts))
(handle-hub music))))
(iter music)
music)
%%% OTHER STUFF ONLY NESCESSARY FOR FORMATTING THIS DEMONSTRATION
qscore=
#(define-scheme-function (m) (ly:music?)
#{ \score { #m \layout { indent = 0 } } #})
#(define-markup-command (mscore layout props m) (ly:music?)
(interpret-markup layout props
(markup #:raise 0.6 #:score (qscore m))))
\layout { indent = 0 }
%%% REPRESENTATION INTERFACE
music = \new Staff { \edits #'((a . 1) b c) << c'2 { e'4 fis'8 } b4 >> d'2 }
%%% SCRORING INTERFACE
\markup\huge "MUSIC WITH THREE VERSIONS a,b,c"
{ \music }
\markup\column{
\huge "SELECTING EDITS"
\vspace #0.5
\justify-line {
\line { a: \mscore { \selectEdits #'(a) \music } }
\line { b: \mscore { \selectEdits #'(b) \music } }
\line { c: \mscore { \selectEdits #'(c) \music } }
\line { d: \mscore { \selectEdits #'(d) \music } }
}
\vspace #1.5
}
\markup\column{
\huge "SELECTING MULTIPLE EDITS"
\vspace #0.5
\justify-line {
""
\line { a+b: \mscore { \selectEdits #'(a b) \music } }
\line { a+b with higher priority for b: \mscore { \selectEdits #'(a (b . 2)) \music } }
""
}
\vspace #1.5
}
\markup\huge "COLORING EDITS"
\colorEdits #`((b . ,red) (c . ,green)) #'(NoteHead Stem Flag Beam Accidental) \music
\markup\huge "COLORING + SELECTION"
\colorEdits #`((b . ,red) (c . ,green)) #'(NoteHead Stem Flag Beam Accidental)
\selectEdits #'(b) \music
\markup\huge "ANNOTATING EDITS"
\annotateEdits #'((a . "Source A") (b . "Source B") (c . "Source C")) \music
\markup\huge "SELECT EDITS AND ANNOTATE AS FOOTNOTE"
\annotateAlternateEdits #'(a) #'((a . "Source A") (b . "Source B") (c . "Source C")) \music
edits.pdf
Description: Adobe PDF document
