@@ -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/>')
|
||||
|
||||
Reference in New Issue
Block a user