\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