Build Fix
ci / checks (push) Failing after 1h6m51s

This commit is contained in:
khodowany
2026-09-18 21:05:38 -05:00
parent 4955405199
commit 7a74ebf04d
11 changed files with 54 additions and 18 deletions
+2
View File
@@ -71,6 +71,8 @@ Multiple staves/parts, D.C./D.S./coda/fine navigation, slurs, dynamics, articula
Layout/exporter information such as page defaults, credits, print layout, font/color/position attributes, and source pagination does not determine the small-screen layout. Engraving is regenerated. Beam groups and written note values are retained; unrelated engraving metadata does not authorize ignoring musical descendants. `defusedxml==0.7.1` forbids XML entities and external access. Ordinary MusicXML DOCTYPE declarations are allowed without fetching their DTDs.
Chord display text is not a substitute for structured harmony. Some Soundslice exports encode minor chord labels as `<kind text="-7">other</kind>` without any degrees; these fail import. Use `minor-seventh` for `-7`, `half-diminished` for `-7b5`, `major-minor` for `-maj7`, and `minor-sixth` for `-6`, keeping the `text` attribute if desired. MusicXML [`other`](https://www.w3.org/2021/06/musicxml40/musicxml-reference/data-types/kind-value/) requires explicit added `<degree>` elements defining the chord. Correct these exports before placing them in `data/`; the importer never guesses pitches from an incomplete chord label.
### Vamp Metadata
In MuseScore's score properties, set `vamp:style` to a supported player style. It exports as standard MusicXML miscellaneous metadata:
@@ -140,7 +140,7 @@
<root>
<root-step>G</root-step>
</root>
<kind text="-7">other</kind>
<kind text="-7">minor-seventh</kind>
</harmony>
<note>
<pitch>
@@ -165,7 +165,7 @@
<root>
<root-step>C</root-step>
</root>
<kind text="-7">other</kind>
<kind text="-7">minor-seventh</kind>
</harmony>
<note>
<pitch>
@@ -191,7 +191,7 @@
<root>
<root-step>F</root-step>
</root>
<kind text="-7">other</kind>
<kind text="-7">minor-seventh</kind>
</harmony>
<note>
<pitch>
@@ -218,7 +218,7 @@
<root-step>B</root-step>
<root-alter>-1</root-alter>
</root>
<kind text="-7">other</kind>
<kind text="-7">minor-seventh</kind>
</harmony>
<note>
<pitch>
@@ -279,7 +279,7 @@
<root>
<root-step>G</root-step>
</root>
<kind text="-7">other</kind>
<kind text="-7">minor-seventh</kind>
</harmony>
<note>
<pitch>
@@ -340,7 +340,7 @@
<root>
<root-step>C</root-step>
</root>
<kind text="-7">other</kind>
<kind text="-7">minor-seventh</kind>
</harmony>
<note>
<pitch>
@@ -405,7 +405,7 @@
<root>
<root-step>F</root-step>
</root>
<kind text="-7">other</kind>
<kind text="-7">minor-seventh</kind>
</harmony>
<note>
<pitch>
@@ -470,7 +470,7 @@
<root-step>B</root-step>
<root-alter>-1</root-alter>
</root>
<kind text="-7">other</kind>
<kind text="-7">minor-seventh</kind>
</harmony>
<note>
<pitch>
@@ -35,7 +35,7 @@
<root>
<root-step>G</root-step>
</root>
<kind text="-7">other</kind>
<kind text="-7">minor-seventh</kind>
</harmony>
<sound tempo="120"/>
<note>
@@ -35,7 +35,7 @@
<root>
<root-step>G</root-step>
</root>
<kind text="-7">other</kind>
<kind text="-7">minor-seventh</kind>
</harmony>
<sound tempo="120"/>
<note>
@@ -35,7 +35,7 @@
<root>
<root-step>C</root-step>
</root>
<kind text="-7">other</kind>
<kind text="-7">minor-seventh</kind>
</harmony>
<sound tempo="120"/>
<note>
@@ -35,7 +35,7 @@
<root>
<root-step>G</root-step>
</root>
<kind text="-7b5">other</kind>
<kind text="-7b5">half-diminished</kind>
</harmony>
<sound tempo="120"/>
<note>
@@ -124,7 +124,7 @@
<root>
<root-step>F</root-step>
</root>
<kind text="-maj7">other</kind>
<kind text="-maj7">major-minor</kind>
</harmony>
<note>
<pitch>
@@ -158,7 +158,7 @@
<root>
<root-step>F</root-step>
</root>
<kind text="-6">other</kind>
<kind text="-6">minor-sixth</kind>
</harmony>
<note>
<pitch>
@@ -35,7 +35,7 @@
<root>
<root-step>C</root-step>
</root>
<kind text="-7b5">other</kind>
<kind text="-7b5">half-diminished</kind>
</harmony>
<sound tempo="120"/>
<note>
@@ -126,7 +126,7 @@
<root-step>B</root-step>
<root-alter>-1</root-alter>
</root>
<kind text="-maj7">other</kind>
<kind text="-maj7">major-minor</kind>
</harmony>
<note>
<pitch>
@@ -162,7 +162,7 @@
<root-step>B</root-step>
<root-alter>-1</root-alter>
</root>
<kind text="-6">other</kind>
<kind text="-6">minor-sixth</kind>
</harmony>
<note>
<pitch>
+2
View File
@@ -49,6 +49,8 @@ def degrees(harmony: dict) -> dict[int, int]:
else:
raise ValueError(f'unsupported degree operation: {operation}')
if kind != 'none' and not result:
if kind == 'other':
raise ValueError("harmony kind 'other' requires explicit added degrees; use a standard <kind> or add <degree> elements; display text alone does not define pitches")
raise ValueError('harmony contains no pitch information')
return result
+1 -1
View File
@@ -591,9 +591,9 @@ class Importer:
self.children(item, {'duration', 'voice', 'staff'})
self.identity(item)
duration = self.number(item.findtext('duration'), positive=True) / self.divisions
self.sequence += 1
from generator.model import make_event
if item.find('voice') is not None:
self.sequence += 1
voices.setdefault(self.voice_id, []).append(make_event(f'spacer-{self.sequence}', cursor, duration, 'spacer') | {'voice': self.voice_id})
cursor += duration
extent = max(extent, cursor)
+32
View File
@@ -60,6 +60,14 @@ class MusicXMLTests(unittest.TestCase):
a = parse(xml_score(xml_note(duration=4)))
b = parse(xml_score(xml_note(duration=1920), divisions=480))
self.assertEqual(a, b)
def test_cursor_only_forward_does_not_change_event_ids(self):
movement = '<forward><duration>1</duration></forward><backup><duration>1</duration></backup>'
self.assertEqual(parse(xml_score(movement + xml_note())), parse(xml_score(xml_note())))
def test_voiced_forward_has_its_own_event_id(self):
content = '<forward><duration>1</duration><voice>1</voice></forward>' + xml_note(duration=3, kind='half', extra='<dot/>')
events = notes(parse(xml_score(content)))
self.assertEqual([(n['id'], n['kind']) for n in events], [('spacer-1', 'spacer'), ('note-2', 'note')])
self.assertEqual([fraction(n['duration']) for n in events], [1, 3])
def test_doctype_allowed_but_entities_rejected(self):
source = xml_score(xml_note())
source = source.replace('<score-partwise', '<!DOCTYPE score-partwise SYSTEM "https://invalid.test/partwise.dtd"><score-partwise', 1)
@@ -83,6 +91,30 @@ class MusicXMLTests(unittest.TestCase):
self.assertEqual(fraction(result[0]['pitches'][1]['duration']), 1)
self.assertEqual(result[0]['pitches'][0]['tie_to'], result[1]['pitches'][0]['id'])
self.assertNotIn('tie_to', result[0]['pitches'][1])
def test_standard_minor_harmonies_preserve_display_and_pitches(self):
from generator.harmony import pitch_classes
for text, kind, expected in (('-7', 'minor-seventh', (0, 3, 7, 10)), ('-7b5', 'half-diminished', (0, 3, 6, 10)), ('-maj7', 'major-minor', (0, 3, 7, 11)), ('-6', 'minor-sixth', (0, 3, 7, 9))):
with self.subTest(text=text):
content = f'<harmony><root><root-step>C</root-step></root><kind text="{text}">{kind}</kind></harmony>'
score = parse(xml_score(content + xml_note()))
chord = score['staves'][0]['measures'][0]['harmonies'][0]
self.assertEqual(chord['symbol'], 'C' + text)
self.assertEqual(pitch_classes(chord), expected)
def test_other_harmony_requires_explicit_degrees(self):
for kind in ('<kind>other</kind>', '<kind text="">other</kind>', '<kind text="-7">other</kind>', '<kind text="unknown">other</kind>'):
with self.subTest(kind=kind), self.assertRaisesRegex(MusicXMLError, r"fixture.xml: part P1, measure 1: harmony kind 'other' requires explicit added degrees"):
parse(xml_score('<harmony><root><root-step>C</root-step></root>' + kind + '</harmony>' + xml_note()))
def test_other_harmony_with_explicit_degrees_remains_supported(self):
from generator.harmony import pitch_classes
degrees = ''.join(f'<degree><degree-value>{value}</degree-value><degree-alter>{alter}</degree-alter><degree-type>add</degree-type></degree>' for value, alter in ((1, 0), (3, -1), (5, 0), (7, 0)))
content = '<harmony><root><root-step>C</root-step></root><kind text="-7">other</kind>' + degrees + '</harmony>'
score = parse(xml_score(content + xml_note()))
chord = score['staves'][0]['measures'][0]['harmonies'][0]
self.assertEqual(chord['kind'], 'other')
self.assertEqual(chord['symbol'], 'C-7')
self.assertEqual(pitch_classes(chord), (0, 3, 7, 10))
with self.assertRaisesRegex(MusicXMLError, 'contradictory harmony text'):
parse(xml_score(content.replace('text="-7"', 'text="maj7"') + xml_note()))
def test_grace_cue_and_measured_tremolo(self):
grace = '<note><grace slash="yes"/><pitch><step>D</step><octave>4</octave></pitch><type>16th</type></note>'
regular = xml_note(extra='<notations><ornaments><tremolo type="single">3</tremolo></ornaments></notations>', prefix='<cue/>')