From bb641f642aef4866223930498de213573d3fc52a Mon Sep 17 00:00:00 2001 From: Reinhold Kainhofer Date: Sun, 30 Nov 2008 20:56:42 +0100 Subject: [PATCH] MusicXML: Add support for microtone (pitches and key signatures) --- .../musicxml/00q-Basics-Microtones.xml | 132 ++++++++++++++++++ .../musicxml/21c-KeySignatures-Microtones.xml | 63 +++++++++ python/musicexp.py | 51 ++++--- python/musicxml.py | 29 ++-- 4 files changed, 241 insertions(+), 34 deletions(-) create mode 100644 input/regression/musicxml/00q-Basics-Microtones.xml create mode 100644 input/regression/musicxml/21c-KeySignatures-Microtones.xml diff --git a/input/regression/musicxml/00q-Basics-Microtones.xml b/input/regression/musicxml/00q-Basics-Microtones.xml new file mode 100644 index 0000000000..659d11c3ce --- /dev/null +++ b/input/regression/musicxml/00q-Basics-Microtones.xml @@ -0,0 +1,132 @@ + + + + + + Some microtones: c + flat-and-a-half, d half-flat, e half-sharp, f sharp-and-a half. + Once in the lower and once in the upper region of the + staff. + + + + + MusicXML Part + + Acoustic Grand Piano + + + 1 + 1 + + + + + + + + 1 + + 0 + major + + + + G + 2 + + + + + C + -1.5 + 4 + + 1 + 1 + quarter + + + + D + -0.5 + 4 + + 1 + 1 + quarter + + + + E + 0.5 + 4 + + 1 + 1 + quarter + + + + F + 1.5 + 4 + + 1 + 1 + quarter + + + + + + + C + -1.5 + 5 + + 1 + 1 + quarter + + + + D + -0.5 + 5 + + 1 + 1 + quarter + + + + E + 0.5 + 5 + + 1 + 1 + quarter + + + + F + 1.5 + 5 + + 1 + 1 + quarter + + + light-heavy + + + + + diff --git a/input/regression/musicxml/21c-KeySignatures-Microtones.xml b/input/regression/musicxml/21c-KeySignatures-Microtones.xml new file mode 100644 index 0000000000..0819bcf5ed --- /dev/null +++ b/input/regression/musicxml/21c-KeySignatures-Microtones.xml @@ -0,0 +1,63 @@ + + + + + + Non-traditional key signatures + with microtone alterations: (g flat-and-a-half, + a flat, b half-flat, c natural, d half-sharp, e sharp, f + sharp-and-a-half). + + + + + MusicXML Part + + + + + + + 1 + + 4 + -1.5 + 5 + -1 + 6 + -0.5 + 0 + 0 + 1 + 0.5 + 2 + 1 + 3 + 1.5 + + + + G + 2 + + + + + C + 4 + + 2 + 1 + half + + + light-heavy + + + + + diff --git a/python/musicexp.py b/python/musicexp.py index a2d405973b..9672a29fa7 100644 --- a/python/musicexp.py +++ b/python/musicexp.py @@ -43,7 +43,6 @@ class Output_printer: Music expression as a .ly file. """ - ## TODO: support for \relative. def __init__ (self): self._line = '' @@ -205,11 +204,20 @@ class Duration: # Implement the different note names for the various languages def pitch_generic (pitch, notenames, accidentals): str = notenames[pitch.step] - # TODO: Handle microtones! - if pitch.alteration < 0: - str += accidentals[0] * (-pitch.alteration) + halftones = int (pitch.alteration) + if halftones < 0: + str += accidentals[0] * (-halftones) elif pitch.alteration > 0: - str += accidentals[3] * (pitch.alteration) + str += accidentals[3] * (halftones) + # Handle remaining fraction to pitch.alteration (for microtones) + if (halftones != pitch.alteration): + if None in accidentals[1:3]: + warning (_ ("Language does not support microtones contained in the piece")) + else: + try: + str += {-0.5: accidentals[1], 0.5: accidentals[2]}[pitch.alteration-halftones] + except KeyError: + warning (_ ("Language does not support microtones contained in the piece")) return str def pitch_general (pitch): @@ -231,7 +239,7 @@ def pitch_norsk (pitch): return pitch_deutsch (pitch) def pitch_svenska (pitch): - str = pitch_generic (pitch, ['c', 'd', 'e', 'f', 'g', 'a', 'h'], ['ess', '', '', 'iss']) + str = pitch_generic (pitch, ['c', 'd', 'e', 'f', 'g', 'a', 'h'], ['ess', None, None, 'iss']) return str.replace ('hess', 'b').replace ('aes', 'as').replace ('ees', 'es') def pitch_italiano (pitch): @@ -242,11 +250,11 @@ def pitch_catalan (pitch): return pitch_italiano (pitch) def pitch_espanol (pitch): - str = pitch_generic (pitch, ['do', 're', 'mi', 'fa', 'sol', 'la', 'si'], ['b', '', '', 's']) + str = pitch_generic (pitch, ['do', 're', 'mi', 'fa', 'sol', 'la', 'si'], ['b', None, None, 's']) return str def pitch_vlaams (pitch): - str = pitch_generic (pitch, ['do', 're', 'mi', 'fa', 'sol', 'la', 'si'], ['b', '', '', 'k']) + str = pitch_generic (pitch, ['do', 're', 'mi', 'fa', 'sol', 'la', 'si'], ['b', None, None, 'k']) return str def set_pitch_language (language): @@ -1298,9 +1306,9 @@ class TremoloEvent (ArticulationEvent): class BendEvent (ArticulationEvent): def __init__ (self): Event.__init__ (self) - self.alter = 0 + self.alter = None def ly_expression (self): - if self.alter: + if self.alter != None: return "-\\bendAfter #%s" % self.alter else: return '' @@ -1428,15 +1436,24 @@ class KeySignatureChange (Music): self.non_standard_alterations = None def format_non_standard_alteration (self, a): - alter_dict = { -2: ",DOUBLE-FLAT", - -1: ",FLAT", - 0: ",NATURAL", - 1: ",SHARP", - 2: ",DOUBLE-SHARP"} + alter_dict = { -2: ",DOUBLE-FLAT", + -1.5: ",THREE-Q-FLAT", + -1: ",FLAT", + -0.5: ",SEMI-FLAT", + 0: ",NATURAL", + 0.5: ",SEMI-SHARP", + 1: ",SHARP", + 1.5: ",THREE-Q-SHARP", + 2: ",DOUBLE-SHARP"} + try: + accidental = alter_dict[a[1]] + except KeyError: + warning (_ ("Unable to convert alteration %s to a lilypond expression") % a[1]) + return '' if len (a) == 2: - return "( %s . %s )" % (a[0], alter_dict.get (a[1], a[1])) + return "( %s . %s )" % (a[0], accidental) elif len (a) == 3: - return "(( %s . %s ) . %s )" % (a[2], a[0], alter_dict.get (a[1], a[1])) + return "(( %s . %s ) . %s )" % (a[2], a[0], accidental) else: return '' diff --git a/python/musicxml.py b/python/musicxml.py index 8413480aef..5c845f841a 100644 --- a/python/musicxml.py +++ b/python/musicxml.py @@ -35,6 +35,13 @@ def musicxml_duration_to_log (dur): 'longa': -2, 'long': -2}.get (dur, 0) +def interpret_alter_element (alter_elm): + alter = 0 + if alter_elm: + val = eval(alter_elm.get_text ()) + if type (val) in (int, float): + alter = val + return alter class Xml_node: @@ -252,10 +259,7 @@ class Pitch (Music_xml_node): def get_alteration (self): ch = self.get_maybe_exist_typed_child (get_class (u'alter')) - alter = 0 - if ch: - alter = int (ch.get_text ().strip ()) - return alter + return interpret_alter_element (ch) class Unpitched (Music_xml_node): def get_step (self): @@ -415,7 +419,7 @@ class Attributes (Measure_element): if isinstance (i, KeyStep): current_step = int (i.get_text ()) elif isinstance (i, KeyAlter): - alterations.append ([current_step, int (i.get_text ())]) + alterations.append ([current_step, interpret_alter_element (i)]) elif isinstance (i, KeyOctave): nr = -1 if hasattr (i, 'number'): @@ -1059,10 +1063,7 @@ class DirType (Music_xml_node): class Bend (Music_xml_node): def bend_alter (self): alter = self.get_maybe_exist_named_child ('bend-alter') - if alter: - return alter.get_text() - else: - return 0 + return interpret_alter_element (alter) class Words (Music_xml_node): pass @@ -1080,10 +1081,7 @@ class ChordPitch (Music_xml_node): return ch.get_text ().strip () def get_alteration (self): ch = self.get_maybe_exist_typed_child (get_class (self.alter_class_name ())) - alter = 0 - if ch: - alter = int (ch.get_text ().strip ()) - return alter + return interpret_alter_element (ch) class Root (ChordPitch): pass @@ -1106,10 +1104,7 @@ class ChordModification (Music_xml_node): return value def get_alter (self): ch = self.get_maybe_exist_typed_child (get_class (u'degree-alter')) - value = 0 - if ch: - value = int (ch.get_text ().strip ()) - return value + return interpret_alter_element (ch) class Frame (Music_xml_node): -- 2.39.5