Hi Kieren,

While it is theoretically possible to compare two streams of music events it 
would also require lots of logic to properly do what we want.

One way to achieve what you want could be storing the whole history in the 
source.

I have recently sent out this showcase example of how we can store more 
information in our score and then extract this information automatically.

For example you could save each change with the date when you did it and 
suddenly you can very easily compare different states of the score.

A different idea might be to use the point and click information stored in the 
pdf. We could parse both pdfs of point and click coordinates, use diffs of the 
source files to translate these coordinates and this determine which parts of 
the pdfs have changed (or are at least close to a change).

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

Attachment: signature.asc
Description: This is a digitally signed message part.

Reply via email to