From c84481ad23d3c527489d491bc589ca99de504c1c Mon Sep 17 00:00:00 2001 From: Reinhold Kainhofer Date: Fri, 26 Oct 2007 16:38:08 +0200 Subject: [PATCH] MusicXML: Convert fretboards from MusicXML to Lilypond In MusicXML, fretboards are inside elements, assigned to a particular staff, while in lilypond the \fret-diagram is a text markup assigned to a particular note. So we run into the same problems as we do with dynamics (i.e. we need to find a proper note to assign the element to, which is not very robust). For single-voice harmony, this works quite well so far. --- .../musicxml/17b-Fretboards-Finale.xml | 229 ++++++++++ .../musicxml/17c-ChordsFrets-Finale.xml | 430 ++++++++++++++++++ python/musicexp.py | 35 ++ python/musicxml.py | 46 +- scripts/musicxml2ly.py | 44 +- 5 files changed, 779 insertions(+), 5 deletions(-) create mode 100644 input/regression/musicxml/17b-Fretboards-Finale.xml create mode 100644 input/regression/musicxml/17c-ChordsFrets-Finale.xml diff --git a/input/regression/musicxml/17b-Fretboards-Finale.xml b/input/regression/musicxml/17b-Fretboards-Finale.xml new file mode 100644 index 0000000000..bfd24c5e5f --- /dev/null +++ b/input/regression/musicxml/17b-Fretboards-Finale.xml @@ -0,0 +1,229 @@ + + + + Fretboards + + + Finale 2007 for Windows + Dolet Light for Finale 2007 + 2007-10-25 + + + + + MusicXML Part + + Acoustic Grand Piano + + + 1 + 1 + + + + + + + + 1 + + 0 + major + + + + G + 2 + + + + + + D + + major + + 6 + 4 + + 6 + 0 + + + 5 + 0 + + + 4 + 0 + + + 3 + 2 + + + 2 + 3 + + + 1 + 2 + + + + + + A + 4 + + 1 + 1 + quarter + up + + + + C + + dominant + + 6 + 4 + + 6 + 0 + + + 5 + 3 + + + 4 + 2 + + + 3 + 3 + + + 2 + 1 + + + 1 + 0 + + + + + + A + 4 + + 1 + 1 + quarter + up + + + + C + + minor-seventh + + 6 + 4 + 3 + + 6 + 0 + + + 5 + 3 + + + 4 + 5 + + + 3 + 3 + + + 2 + 4 + + + 1 + 3 + + + + + + A + 4 + + 1 + 1 + quarter + up + + + + C + + dominant + + 13 + -1 + add + + + 6 + 4 + 8 + + 6 + 8 + + + 4 + 8 + + + 3 + 9 + + + 2 + 9 + + + 1 + 0 + + + + + + A + 4 + + 1 + 1 + quarter + up + + + light-heavy + + + + + diff --git a/input/regression/musicxml/17c-ChordsFrets-Finale.xml b/input/regression/musicxml/17c-ChordsFrets-Finale.xml new file mode 100644 index 0000000000..06eca4ace3 --- /dev/null +++ b/input/regression/musicxml/17c-ChordsFrets-Finale.xml @@ -0,0 +1,430 @@ + + + + Chords and fretboards example + + + Finale 2007 for Windows + Dolet Light for Finale 2007 + 2007-10-25 + + + + + MusicXML Part + + Acoustic Grand Piano + + + 1 + 1 + + + + + + + + 1 + + 0 + major + + + + G + 2 + + + + + + C + + major + + 6 + 4 + + 6 + 0 + + + 5 + 3 + + + 4 + 2 + + + 3 + 0 + + + 2 + 1 + + + 1 + 0 + + + + + + A + 4 + + 1 + 1 + quarter + up + + + + C + + major-seventh + + 11 + 1 + add + + + 6 + 4 + 2 + + 6 + 0 + + + 5 + 3 + + + 4 + 5 + + + 3 + 4 + + + 2 + 5 + + + 1 + 2 + + + + + + A + 4 + + 1 + 1 + quarter + up + + + + B + + dominant + + 5 + 1 + alter + + + 9 + 1 + add + + + 6 + 4 + + 6 + 0 + + + 5 + 2 + + + 4 + 1 + + + 3 + 2 + + + 2 + 3 + + + 1 + 3 + + + + + + A + 4 + + 1 + 1 + quarter + up + + + + E + -1 + + major + + 2 + 0 + add + + + 6 + 5 + 11 + + 6 + 11 + + + 5 + 13 + + + 4 + 15 + + + 3 + 12 + + + 2 + 11 + + + 1 + 11 + + + + + + A + 4 + + 1 + 1 + quarter + up + + + + + + + G + + minor + + 6 + 4 + 3 + + 6 + 3 + + + 5 + 5 + + + 4 + 5 + + + 3 + 3 + + + 2 + 3 + + + 1 + 3 + + + + + + A + 4 + + 1 + 1 + quarter + up + + + + D + 1 + + major-seventh + + 6 + 4 + + 6 + 0 + + + 5 + 0 + + + 4 + 1 + + + 3 + 3 + + + 2 + 3 + + + 1 + 3 + + + + + + A + 4 + + 1 + 1 + quarter + up + + + + A + + diminished-seventh + + 6 + 4 + + 6 + 0 + + + 5 + 0 + + + 4 + 1 + + + 3 + 2 + + + 2 + 1 + + + 1 + 2 + + + + + + A + 4 + + 1 + 1 + quarter + up + + + + A + + augmented + + 6 + 4 + + 6 + 0 + + + 5 + 0 + + + 4 + 3 + + + 3 + 2 + + + 2 + 2 + + + 1 + 0 + + + + + + A + 4 + + 1 + 1 + quarter + up + + + light-heavy + + + + + diff --git a/python/musicexp.py b/python/musicexp.py index c4ef903a32..ef896f9e53 100644 --- a/python/musicexp.py +++ b/python/musicexp.py @@ -761,6 +761,8 @@ class ArticulationEvent (Event): def __init__ (self): self.type = None self.force_direction = None + def wait_for_note (self): + return True; def direction_mod (self): return { 1: '^', -1: '_', 0: '-' }.get (self.force_direction, '') @@ -786,6 +788,39 @@ class MarkupEvent (ShortArticulationEvent): def ly_expression (self): if self.contents: return "%s\\markup { %s }" % (self.direction_mod (), self.contents) + else: + return '' + +class FretEvent (MarkupEvent): + def __init__ (self): + MarkupEvent.__init__ (self) + self.force_direction = 1 + self.strings = 6 + self.frets = 4 + self.barre = None + self.elements = [] + def ly_expression (self): + val = "" + if self.strings <> 6: + val += "w:%s;" % self.strings + if self.frets <> 4: + val += "h:%s;" % self.frets + if self.barre and len (self.barre) >= 3: + val += "c:%s-%s-%s;" % (self.barre[0], self.barre[1], self.barre[2]) + have_fingering = False + for i in self.elements: + if len (i) > 1: + val += "%s-%s" % (i[0], i[1]) + if len (i) > 2: + have_fingering = True + val += "-%s" % i[2] + val += ";" + if have_fingering: + val = "f:1;" + val + if val: + return "%s\\markup { \\fret-diagram #\"%s\" }" % (self.direction_mod (), val) + else: + return '' class TremoloEvent (ArticulationEvent): def __init__ (self): diff --git a/python/musicxml.py b/python/musicxml.py index 86eca63bf7..3c38f30828 100644 --- a/python/musicxml.py +++ b/python/musicxml.py @@ -89,6 +89,14 @@ class Xml_node: return cn[0] + def get_named_child_value_number (self, name, default): + n = self.get_maybe_exist_named_child (name) + if n: + return string.atoi (n.get_text()) + else: + return default + + class Music_xml_node (Xml_node): def __init__ (self): Xml_node.__init__ (self) @@ -610,7 +618,7 @@ class Part (Music_xml_node): if not (voice_id or isinstance (n, Attributes) or isinstance (n, Direction) or isinstance (n, Partial) or - isinstance (n, Barline) ): + isinstance (n, Barline) or isinstance (n, Harmony) ): continue if isinstance (n, Attributes) and not start_attr: @@ -630,7 +638,7 @@ class Part (Music_xml_node): voices[v].add_element (n) continue - if isinstance (n, Direction): + if isinstance (n, Direction) or isinstance (n, Harmony): staff_id = n.get_maybe_exist_named_child (u'staff') if staff_id: staff_id = staff_id.get_text () @@ -638,8 +646,11 @@ class Part (Music_xml_node): dir_voices = staff_to_voice_dict.get (staff_id, voices.keys ()) else: dir_voices = voices.keys () - for v in dir_voices: - voices[v].add_element (n) + # assign only to first voice, otherwise we'll have duplicates! + if dir_voices: + voices[dir_voices[0]].add_element (n) + #for v in dir_voices: + #voices[v].add_element (n) continue id = voice_id.get_text () @@ -783,6 +794,30 @@ class Bend (Music_xml_node): class Words (Music_xml_node): pass +class Harmony (Music_xml_node): + pass + +class Frame (Music_xml_node): + def get_frets (self): + return self.get_named_child_value_number ('frame-frets', 4) + def get_strings (self): + return self.get_named_child_value_number ('frame-strings', 6) + def get_first_fret (self): + return self.get_named_child_value_number ('first-fret', 1) +class Frame_Note (Music_xml_node): + def get_string (self): + return self.get_named_child_value_number ('string', 1) + def get_fret (self): + return self.get_named_child_value_number ('fret', 0) + def get_fingering (self): + return self.get_named_child_value_number ('fingering', -1) + def get_barre (self): + n = self.get_maybe_exist_named_child ('barre') + if n: + return getattr (n, 'type', '') + else: + return '' + ## need this, not all classes are instantiated ## for every input file. Only add those classes, that are either directly @@ -801,8 +836,11 @@ class_dict = { 'direction': Direction, 'direction-type': DirType, 'duration': Duration, + 'frame': Frame, + 'frame-note': Frame_Note, 'glissando': Glissando, 'grace': Grace, + 'harmony': Harmony, 'identification': Identification, 'lyric': Lyric, 'measure': Measure, diff --git a/scripts/musicxml2ly.py b/scripts/musicxml2ly.py index 4441f5be68..7238230cb7 100644 --- a/scripts/musicxml2ly.py +++ b/scripts/musicxml2ly.py @@ -737,6 +737,40 @@ def musicxml_direction_to_lily (n): return res +def musicxml_frame_to_lily_event (frame): + ev = musicexp.FretEvent () + ev.strings = frame.get_strings () + ev.frets = frame.get_frets () + #offset = frame.get_first_fret () - 1 + barre = [] + for fn in frame.get_named_children ('frame-note'): + fret = fn.get_fret () + if fret <= 0: + fret = "o" + el = [ fn.get_string (), fret ] + fingering = fn.get_fingering () + if fingering >= 0: + el.append (fingering) + ev.elements.append (el) + b = fn.get_barre () + if b == 'start': + barre[0] = el[0] # start string + barre[2] = el[1] # fret + elif b == 'stop': + barre[1] = el[0] # end string + if barre: + ev.barre = barre + return ev + +def musicxml_harmony_to_lily (n): + res = [] + for f in n.get_named_children ('frame'): + ev = musicxml_frame_to_lily_event (f) + if ev: + res.append (ev) + + return res + instrument_drumtype_dict = { 'Acoustic Snare Drum': 'acousticsnare', 'Side Stick': 'sidestick', @@ -938,7 +972,15 @@ def musicxml_voice_to_lily_voice (voice): else: voice_builder.add_command (a) continue - + + if isinstance (n, musicxml.Harmony): + for a in musicxml_harmony_to_lily (n): + if a.wait_for_note (): + voice_builder.add_dynamics (a) + else: + voice_builder.add_command (a) + continue + is_chord = n.get_maybe_exist_named_child ('chord') if not is_chord: try: -- 2.39.5