Skip to content

Commit 5745cb2

Browse files
Add support for diffing RepeatBrackets (e.g. first and second endings).
1 parent 0f0542e commit 5745cb2

File tree

3 files changed

+48
-7
lines changed

3 files changed

+48
-7
lines changed

musicdiff/__main__.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,12 +15,12 @@
1515
import sys
1616
import argparse
1717

18-
from musicdiff import diff
19-
from musicdiff import DetailLevel
20-
2118
import music21 as m21
2219
from converter21 import HumdrumConverter
2320

21+
from musicdiff import diff
22+
from musicdiff import DetailLevel
23+
2424
# ------------------------------------------------------------------------------
2525

2626
'''

musicdiff/m21utils.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -439,6 +439,14 @@ def get_extras(measure: m21.stream.Measure, spannerBundle: m21.spanner.SpannerBu
439439
if sp.isFirst(gn):
440440
output.append(sp)
441441

442+
# Add any RepeatBracket spanners that start on this measure
443+
rbList: List[m21.spanner.Spanner] = measure.getSpannerSites(m21.spanner.RepeatBracket)
444+
for rb in rbList:
445+
if rb not in spannerBundle:
446+
continue
447+
if rb.isFirst(measure):
448+
output.append(rb)
449+
442450
return output
443451

444452
@staticmethod
@@ -708,6 +716,10 @@ def arpeggiomark_to_string(
708716
return f'ARPS:{arp.type}:len={len(arp)}'
709717
return ''
710718

719+
@staticmethod
720+
def repeatbracket_to_string(rb: m21.spanner.RepeatBracket) -> str:
721+
return f'END:{rb.number}:len={len(rb)}'
722+
711723
@staticmethod
712724
def extra_to_string(extra: m21.base.Music21Object) -> str:
713725
if isinstance(extra, (m21.key.Key, m21.key.KeySignature)):
@@ -726,6 +738,8 @@ def extra_to_string(extra: m21.base.Music21Object) -> str:
726738
return M21Utils.tempo_to_string(extra)
727739
if isinstance(extra, m21.bar.Barline):
728740
return M21Utils.barline_to_string(extra)
741+
if isinstance(extra, m21.spanner.RepeatBracket):
742+
return M21Utils.repeatbracket_to_string(extra)
729743
if (hasattr(m21.expressions, 'ArpeggioMark')
730744
and hasattr(m21.expressions, 'ArpeggioMarkSpanner')):
731745
if isinstance(extra, (m21.expressions.ArpeggioMark, m21.expressions.ArpeggioMarkSpanner)):

musicdiff/visualization.py

Lines changed: 31 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -118,8 +118,15 @@ def mark_diffs(
118118
textExp.style.color = Visualization.INSERTED_COLOR
119119
if isinstance(extra2, m21.spanner.Spanner):
120120
insertionPoint = extra2.getFirst()
121-
insertionPoint.activeSite.insert(insertionPoint.offset, textExp)
121+
if isinstance(insertionPoint, m21.stream.Measure):
122+
# insertionPoint is a measure, put the textExp at offset 0
123+
# inside the measure
124+
insertionPoint.insert(0, textExp)
125+
else:
126+
# insertionPoint is something else, put the textExp right next to it.
127+
insertionPoint.activeSite.insert(insertionPoint.offset, textExp)
122128
else:
129+
# extra2 is not a spanner, put the textExp right next to it
123130
extra2.activeSite.insert(extra2.offset, textExp)
124131

125132
elif op[0] == "extradel":
@@ -131,8 +138,15 @@ def mark_diffs(
131138
textExp.style.color = Visualization.DELETED_COLOR
132139
if isinstance(extra1, m21.spanner.Spanner):
133140
insertionPoint = extra1.getFirst()
134-
insertionPoint.activeSite.insert(insertionPoint.offset, textExp)
141+
if isinstance(insertionPoint, m21.stream.Measure):
142+
# insertionPoint is a measure, put the textExp at offset 0
143+
# inside the measure
144+
insertionPoint.insert(0, textExp)
145+
else:
146+
# insertionPoint is something else, put the textExp right next to it.
147+
insertionPoint.activeSite.insert(insertionPoint.offset, textExp)
135148
else:
149+
# extra1 is not a spanner, put the textExp right next to it
136150
extra1.activeSite.insert(extra1.offset, textExp)
137151

138152
elif op[0] == "extrasub":
@@ -155,9 +169,22 @@ def mark_diffs(
155169
if isinstance(extra1, m21.spanner.Spanner):
156170
insertionPoint1 = extra1.getFirst()
157171
insertionPoint2 = extra2.getFirst()
158-
insertionPoint1.activeSite.insert(insertionPoint1.offset, textExp1)
159-
insertionPoint2.activeSite.insert(insertionPoint2.offset, textExp2)
172+
if isinstance(insertionPoint1, m21.stream.Measure):
173+
# insertionPoint1 is a measure, put the textExp at offset 0
174+
# inside the measure
175+
insertionPoint1.insert(0, textExp)
176+
else:
177+
# insertionPoint1 is something else, put the textExp right next to it.
178+
insertionPoint1.activeSite.insert(insertionPoint1.offset, textExp)
179+
if isinstance(insertionPoint2, m21.stream.Measure):
180+
# insertionPoint2 is a measure, put the textExp at offset 0
181+
# inside the measure
182+
insertionPoint2.insert(0, textExp)
183+
else:
184+
# insertionPoint2 is something else, put the textExp right next to it.
185+
insertionPoint2.activeSite.insert(insertionPoint2.offset, textExp)
160186
else:
187+
# extra is not a spanner, put the textExp right next to it
161188
extra1.activeSite.insert(extra1.offset, textExp1)
162189
extra2.activeSite.insert(extra2.offset, textExp2)
163190

0 commit comments

Comments
 (0)