Next round of patches: After Han-Wens comments about coding style (no exceptions, spaces, etc.), I reworked all my patches so far. Here they are all again:
-) 0001-Convert-articulations-like-fermata-staccato-tenuto.patch: Convert articulations like fermata, staccato, tenuto, tremolo (only single-note tremolo), accents, etc. These entries in the xml are inside the <notation>...</notation> tags, listed in the <ornaments>, <technical> and <articulations> tags. -) 0002-Sorting-of-the-parts-in-the-.ly-output.-Currently-t.patch: Sorting of the parts in the .ly output. Currently, their order was not first staff, second staff, third, etc. but seemingly random Basically, I use the part_list to sort the individual music voices in the order as they appear in the score before printing them out. -) 0003-Also-convert-0-in-part-ids-to-Zero-simplify-tha.patch: Also convert '0' in part ids to 'Zero', simplify that code a bit. -) 0004-Don-t-crash-when-a-score-does-not-have-an-explicit-k.patch: Don't crash when a score does not have an explicit key or clef set (e.g. Rosegarden produces such files). -) 0005-Convert-dynamic-marks-given-in-a-direction-tag-a.patch: Convert dynamic marks (given in a <direction> tag, assigned to the staff at a given position in xml, not to a note like in lilypond) In the LilyPondVoiceBuilder, I added a method to store any pending dynamics and print them out only after the next note or rest (everything with duration>0) is encountered. Also convert (de-)crescendo (begin/end also given in a <direction> tag, not assigned to a particular note) Comment about broken dynamics, when they appear as first element of a part before any note (so that no voice_id is known yet). Cheers, Reinhold -- ------------------------------------------------------------------ Reinhold Kainhofer, Vienna University of Technology, Austria email: [EMAIL PROTECTED], http://reinhold.kainhofer.com/ * Financial and Actuarial Mathematics, TU Wien, http://www.fam.tuwien.ac.at/ * K Desktop Environment, http://www.kde.org, KOrganizer maintainer * Chorvereinigung "Jung-Wien", http://www.jung-wien.at/
From ad228b1e645d8f8e0bf0116d784a76f73dbc0a67 Mon Sep 17 00:00:00 2001 From: Reinhold Kainhofer <[EMAIL PROTECTED]> Date: Sun, 19 Aug 2007 23:50:29 +0200 Subject: [PATCH] Convert articulations like fermata, staccato, tenuto, tremolo (only single-note tremolo), accents, etc. These entries in the xml are inside the <notation>...</notation> tags, listed in the <ornaments>, <technical> and <articulations> tags. --- python/musicexp.py | 29 ++++++++++- python/musicxml.py | 36 +++++++++++++ scripts/musicxml2ly.py | 136 ++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 200 insertions(+), 1 deletions(-) diff --git a/python/musicexp.py b/python/musicexp.py index e7b3870..56252f3 100644 --- a/python/musicexp.py +++ b/python/musicexp.py @@ -498,7 +498,34 @@ class TieEvent(Event): def ly_expression (self): return '~' - + +class ArticulationEvent (Event): + def __init__ (self): + self.type = None + self.force_direction = None + + def direction_mod (self): + dirstr = { 1: '^', -1: '_', 0: '-' }.get (self.force_direction) + if dirstr: + return dirstr + else: + return '' + + def ly_expression (self): + return '%s\\%s' % (self.direction_mod (), self.type) + + +class TremoloEvent (Event): + def __init__ (self): + self.bars = 0; + + def ly_expression (self): + str='' + if self.bars > 0: + str += ':%s' % (2 ** (2 + string.atoi (self.bars))) + return str + + class RhythmicEvent(Event): def __init__ (self): Event.__init__ (self) diff --git a/python/musicxml.py b/python/musicxml.py index 3466473..304b2c6 100644 --- a/python/musicxml.py +++ b/python/musicxml.py @@ -450,6 +450,32 @@ class Staff (Music_xml_node): class Instrument (Music_xml_node): pass +class Fermata (Music_xml_node): + pass +class Dynamics (Music_xml_node): + pass +class Articulations (Music_xml_node): + pass +class Accent (Music_xml_node): + pass +class Staccato (Music_xml_node): + pass +class Tenuto (Music_xml_node): + pass +class Tremolo (Music_xml_node): + pass +class Technical (Music_xml_node): + pass +class Ornaments (Music_xml_node): + pass + + +class Direction (Music_xml_node): + pass +class DirType (Music_xml_node): + pass + + ## need this, not all classes are instantiated ## for every input file. class_dict = { @@ -477,6 +503,16 @@ class_dict = { 'type': Type, 'part-list': Part_list, 'staff': Staff, + 'fermata': Fermata, + 'articulations': Articulations, + 'accent': Accent, + 'staccato': Staccato, + 'tenuto': Tenuto, + 'tremolo': Tremolo, + 'technical': Technical, + 'ornaments': Ornaments, + 'direction': Direction, + 'direction-type': DirType } def name2class_name (name): diff --git a/scripts/musicxml2ly.py b/scripts/musicxml2ly.py index e358268..3a5f465 100644 --- a/scripts/musicxml2ly.py +++ b/scripts/musicxml2ly.py @@ -168,6 +168,99 @@ def musicxml_spanner_to_lily_event (mxl_event): return ev +def musicxml_direction_to_indicator (direction): + val = { "above": 1, "upright": 1, "below": -1, "downright": -1 }.get (direction) + if val: + return val + else: + return '' + +def musicxml_fermata_to_lily_event (mxl_event): + ev = musicexp.ArticulationEvent () + ev.type = "fermata" + if hasattr (mxl_event, 'type'): + dir = musicxml_direction_to_indicator (mxl_event.type) + if dir: + ev.force_direction = dir + return ev + +def musicxml_tremolo_to_lily_event(mxl_event): + if mxl_event.get_name () != "tremolo": + return + ev = musicexp.TremoloEvent () + ev.bars = mxl_event.get_text () + return ev + +# TODO: Some translations are missing! +articulations_dict = { + ##### ORNAMENTS + "trill-mark": "trill", + "turn": "turn", + #"delayed-turn": "?", + "inverted-turn": "reverseturn", + #"shake": "?", + #"wavy-line": "?", + "mordent": "mordent", + #"inverted-mordent": "?", + #"schleifer": "?" + ##### TECHNICALS + "up-bow": "upbow", + "down-bow": "downbow", + #"harmonic": "", + #"open-string": "", + #"thumb-position": "", + #"fingering": "", + #"pluck": "", + #"double-tongue": "", + #"triple-tongue": "", + #"stopped": "", + #"snap-pizzicato": "", + #"fret": "", + #"string": "", + #"hammer-on": "", + #"pull-off": "", + #"bend": "", + #"tap": "", + #"heel": "", + #"toe": "", + #"fingernails": "" + ##### ARTICULATIONS + "accent": "accent", + "strong-accent": "marcato", + "staccato": "staccato", + "tenuto": "tenuto", + #"detached-legato": "", + "staccatissimo": "staccatissimo", + #"spiccato": "", + #"scoop": "", + #"plop": "", + #"doit": "", + #"falloff": "", + "breath-mark": "breathe", + #"caesura": "caesura", + #"stress": "", + #"unstress": "" +} + +def musicxml_articulation_to_lily_event(mxl_event): + ev = musicexp.ArticulationEvent () + tp = articulations_dict.get (mxl_event.get_name ()) + if not tp: + return + + ev.type = tp + + # Some articulations use the type attribute, other the placement... + dir = None + if hasattr (mxl_event, 'type'): + dir = musicxml_direction_to_indicator (mxl_event.type) + if hasattr (mxl_event, 'placement'): + dir = musicxml_direction_to_indicator (mxl_event.placement) + if dir: + ev.force_direction = dir + return ev + + instrument_drumtype_dict = { 'Acoustic Snare Drum': 'acousticsnare', 'Side Stick': 'sidestick', @@ -353,6 +446,12 @@ def musicxml_voice_to_lily_voice (voice): notations = n.get_maybe_exist_typed_child (musicxml.Notations) tuplet_event = None span_events = [] + + # The <notation> element can have the following children (+ means implemented, ~ partially, - not): + # +tied | +slur | +tuplet | glissando | slide | + # ornaments | technical | articulations | dynamics | + # +fermata | arpeggiate | non-arpeggiate | + # accidental-mark | other-notation if notations: if notations.get_tuplet(): tuplet_event = notations.get_tuplet() @@ -375,6 +474,43 @@ def musicxml_voice_to_lily_voice (voice): mxl_tie = notations.get_tie () if mxl_tie and mxl_tie.type == 'start': ev_chord.append (musicexp.TieEvent ()) + + fermatas = notations.get_named_children ('fermata') + for a in fermatas: + ev = musicxml_fermata_to_lily_event (a); + if ev: + ev_chord.append (ev) + + # Articulations can contain the following child elements: + # accent | strong-accent | staccato | tenuto | + # detached-legato | staccatissimo | spiccato | + # scoop | plop | doit | falloff | breath-mark | + # caesura | stress | unstress + # Technical can contain the following child elements: + # up-bow | down-bow | harmonic | open-string | + # thumb-position | fingering | pluck | double-tongue | + # triple-tongue | stopped | snap-pizzicato | fret | + # string | hammer-on | pull-off | bend | tap | heel | + # toe | fingernails | other-technical + # Ornaments can contain the following child elements: + # trill-mark | turn | delayed-turn | inverted-turn | + # shake | wavy-line | mordent | inverted-mordent | + # schleifer | tremolo | other-ornament, accidental-mark + ornaments = notations.get_named_children ('ornaments') + for a in ornaments: + for ch in a.get_named_children ('tremolo'): + ev = musicxml_tremolo_to_lily_event (ch) + if ev: + ev_chord.append (ev) + + ornaments += notations.get_named_children ('articulations') + ornaments += notations.get_named_children ('technical') + + for a in ornaments: + for ch in a.get_all_children (): + ev = musicxml_articulation_to_lily_event (ch) + if ev: + ev_chord.append (ev) mxl_beams = [b for b in n.get_named_children ('beam') if (b.get_type () in ('begin', 'end') -- 1.5.2.3
From a09121d886f75c61bdd6c4544f0b3b1215988777 Mon Sep 17 00:00:00 2001 From: Reinhold Kainhofer <[EMAIL PROTECTED]> Date: Sun, 19 Aug 2007 23:51:51 +0200 Subject: [PATCH] Sorting of the parts in the .ly output. Currently, their order was not first staff, second staff, third, etc. but seemingly random Basically, I use the part_list to sort the individual music voices in the order as they appear in the score before printing them out. --- scripts/musicxml2ly.py | 10 +++++++--- 1 files changed, 7 insertions(+), 3 deletions(-) diff --git a/scripts/musicxml2ly.py b/scripts/musicxml2ly.py index 3a5f465..7307dd2 100644 --- a/scripts/musicxml2ly.py +++ b/scripts/musicxml2ly.py @@ -648,9 +648,13 @@ def music_xml_voice_name_to_lily_name (part, name): str = "Part%sVoice%s" % (part.id, name) return musicxml_id_to_lily (str) -def print_voice_definitions (printer, voices): +def print_voice_definitions (printer, part_list, voices): + part_dict={} for (part, nv_dict) in voices.items(): - + part_dict[part.id] = (part, nv_dict) + + for part in part_list: + (part, nv_dict) = part_dict[part.id] for (name, (voice, mxlvoice)) in nv_dict.items (): k = music_xml_voice_name_to_lily_name (part, name) printer.dump ('%s = ' % k) @@ -763,7 +767,7 @@ def convert (filename, options): printer.set_file (open (defs_ly_name, 'w')) print_ly_preamble (printer, filename) - print_voice_definitions (printer, voices) + print_voice_definitions (printer, part_list, voices) printer.close () -- 1.5.2.3
From 0ab607a65fefe22209037fa7a7449a0c75d2c6d5 Mon Sep 17 00:00:00 2001 From: Reinhold Kainhofer <[EMAIL PROTECTED]> Date: Mon, 20 Aug 2007 00:21:30 +0200 Subject: [PATCH] Convert dynamic marks (given in a <direction> tag, assigned to the staff at a given position in xml, not to a note like in lilypond) In the LilyPondVoiceBuilder, I added a method to store any pending dynamics and print them out only after the next note or rest (everything with duration>0) is encountered. Also convert (de-)crescendo (begin/end also given in a <direction> tag, not assigned to a particular note) Comment about broken dynamics, when they appear as first element of a part before any note (so that no voice_id is known yet). --- python/musicexp.py | 44 +++++++++++++++++++++++++++++++++++++ python/musicxml.py | 12 +++++++-- scripts/musicxml2ly.py | 57 ++++++++++++++++++++++++++++++++++++++++++++++- 3 files changed, 108 insertions(+), 5 deletions(-) diff --git a/python/musicexp.py b/python/musicexp.py index 56252f3..b543dbc 100644 --- a/python/musicexp.py +++ b/python/musicexp.py @@ -499,6 +499,50 @@ class TieEvent(Event): return '~' +class HairpinEvent (Event): + def __init__ (self, type): + self.type = type + def hairpin_to_ly (self): + val = '' + tp = { 0: '\!', 1: '\<', -1: '\>' }.get (self.type) + if tp: + val += tp + return val + + def ly_expression (self): + return self.hairpin_to_ly () + + def print_ly (self, printer): + val = self.hairpin_to_ly () + if val: + printer.dump (val) + + + +class DynamicsEvent (Event): + def __init__ (self): + self.type = None + self.available_commands = [ "ppppp", "pppp", "ppp", "pp", "p", + "mp", "mf", + "f", "ff", "fff", "ffff", + "fp", "sf", "sff", "sp", "spp", "sfz", "rfz" ]; + def ly_expression (self): + if self.type == None: + return; + elif self.type in self.available_commands: + return '\%s' % self.type + else: + return '\markup{ \dynamic %s }' % self.type + + def print_ly (self, printer): + if self.type == None: + return + elif self.type in self.available_commands: + printer.dump ("\\%s" % self.type) + else: + printer.dump ("\\markup{ \\dynamic %s }" % self.type) + + class ArticulationEvent (Event): def __init__ (self): self.type = None diff --git a/python/musicxml.py b/python/musicxml.py index e7fe77d..132ce5a 100644 --- a/python/musicxml.py +++ b/python/musicxml.py @@ -357,14 +357,16 @@ class Part (Music_xml_node): for n in elements: voice_id = n.get_maybe_exist_typed_child (class_dict['voice']) - if not (voice_id or isinstance (n, Attributes)): + # TODO: If the first element of a voice is a dynamics entry, + # then voice_id is not yet set! Thus it will currently be ignored + if not (voice_id or isinstance (n, Attributes) or isinstance (n, Direction) ): continue if isinstance (n, Attributes) and not start_attr: start_attr = n continue - if isinstance (n, Attributes): + if isinstance (n, Attributes) or isinstance (n, Direction): for v in voices.values (): v.add_element (n) continue @@ -477,6 +479,8 @@ class Direction (Music_xml_node): pass class DirType (Music_xml_node): pass +class Wedge (Music_xml_node): + pass ## need this, not all classes are instantiated @@ -515,7 +519,9 @@ class_dict = { 'technical': Technical, 'ornaments': Ornaments, 'direction': Direction, - 'direction-type': DirType + 'direction-type': DirType, + 'dynamics': Dynamics, + 'wedge': Wedge } def name2class_name (name): diff --git a/scripts/musicxml2ly.py b/scripts/musicxml2ly.py index 4bb3081..9a09fa2 100644 --- a/scripts/musicxml2ly.py +++ b/scripts/musicxml2ly.py @@ -261,13 +261,43 @@ def musicxml_articulation_to_lily_event(mxl_event): return ev +def musicxml_direction_to_lily( n ): + # TODO: Handle the <staff> element! + res = [] + dirtype = n.get_maybe_exist_typed_child (musicxml.DirType) + if not dirtype: + return res + + for entry in dirtype.get_all_children (): + if entry.get_name () == "dynamics": + for dynentry in entry.get_all_children (): + dynamics_available = ( "p", "pp", "ppp", "pppp", "ppppp", "pppppp", + "f", "ff", "fff", "ffff", "fffff", "ffffff", + "mp", "mf", "sf", "sfp", "sfpp", "fp", + "rf", "rfz", "sfz", "sffz", "fz" ) + if not dynentry.get_name() in dynamics_available: + continue + event = musicexp.DynamicsEvent () + event.type = dynentry.get_name () + res.append (event) + + if entry.get_name() == "wedge": + if hasattr (entry, 'type'): + wedgetype = entry.type; + wedgetypeval = {"crescendo" : 1, "decrescendo" : -1, + "diminuendo" : -1, "stop" : 0 }.get (wedgetype) + if wedgetypeval != None: + event = musicexp.HairpinEvent (wedgetypeval) + res.append (event) + + return res + instrument_drumtype_dict = { 'Acoustic Snare Drum': 'acousticsnare', 'Side Stick': 'sidestick', 'Open Triangle': 'opentriangle', 'Mute Triangle': 'mutetriangle', - 'Tambourine': 'tambourine', - + 'Tambourine': 'tambourine' } def musicxml_note_to_lily_main_event (n): @@ -312,6 +342,7 @@ class NegativeSkip: class LilyPondVoiceBuilder: def __init__ (self): self.elements = [] + self.pending_dynamics = [] self.end_moment = Rational (0) self.begin_moment = Rational (0) self.pending_multibar = Rational (0) @@ -339,6 +370,16 @@ class LilyPondVoiceBuilder: self.begin_moment = self.end_moment self.end_moment = self.begin_moment + duration + # Insert all pending dynamics right after the note/rest: + if duration > Rational (0): + for d in self.pending_dynamics: + self.elements.append (d) + self.pending_dynamics = [] + + def add_dynamics (self, dynamic): + # store the dynamic item(s) until we encounter the next note/rest: + self.pending_dynamics.append (dynamic) + def add_bar_check (self, number): b = musicexp.BarCheck () b.bar_number = number @@ -390,6 +431,11 @@ def musicxml_voice_to_lily_voice (voice): if n.get_name () == 'forward': continue + if isinstance (n, musicxml.Direction): + for a in musicxml_direction_to_lily (n): + voice_builder.add_dynamics (a) + continue + if not n.get_maybe_exist_named_child ('chord'): try: voice_builder.jumpto (n._when) @@ -512,6 +558,13 @@ def musicxml_voice_to_lily_voice (voice): if ev: ev_chord.append (ev) + dynamics = notations.get_named_children ('dynamics') + for a in dynamics: + for ch in a.get_all_children (): + ev = musicxml_dynamics_to_lily_event (ch) + if ev: + ev_chord.append (ev) + mxl_beams = [b for b in n.get_named_children ('beam') if (b.get_type () in ('begin', 'end') and b.is_primary ())] -- 1.5.2.3
From 39ded4e618d27ff62eedea7fa69f2958c06ea8e9 Mon Sep 17 00:00:00 2001 From: Reinhold Kainhofer <[EMAIL PROTECTED]> Date: Sun, 19 Aug 2007 23:53:26 +0200 Subject: [PATCH] Also convert '0' in part ids to 'Zero', simplify that code a bit. --- scripts/musicxml2ly.py | 7 +++---- 1 files changed, 3 insertions(+), 4 deletions(-) diff --git a/scripts/musicxml2ly.py b/scripts/musicxml2ly.py index 7307dd2..a82f3dd 100644 --- a/scripts/musicxml2ly.py +++ b/scripts/musicxml2ly.py @@ -558,12 +558,11 @@ def musicxml_voice_to_lily_voice (voice): def musicxml_id_to_lily (id): - digits = ['one', 'two', 'three', 'four', 'five', 'six', 'seven', 'eight', - 'nine', 'ten'] + digits = ['Zero', 'One', 'Two', 'Three', 'Four', 'Five', 'Six', 'Seven', 'Eight', + 'Nine', 'Ten'] for dig in digits: - d = digits.index (dig) + 1 - dig = dig[0].upper() + dig[1:] + d = digits.index (dig) id = re.sub ('%d' % d, dig, id) id = re.sub ('[^a-zA-Z]', 'X', id) -- 1.5.2.3
From 7c9b19bb8d3e4a83cb5e11d9155d014d0a6e583f Mon Sep 17 00:00:00 2001 From: Reinhold Kainhofer <[EMAIL PROTECTED]> Date: Sun, 19 Aug 2007 23:58:27 +0200 Subject: [PATCH] Don't crash when a score does not have an explicit key or clef set (e.g. Rosegarden produces such files). --- python/musicxml.py | 7 +++++-- scripts/musicxml2ly.py | 4 ++-- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/python/musicxml.py b/python/musicxml.py index 304b2c6..e7fe77d 100644 --- a/python/musicxml.py +++ b/python/musicxml.py @@ -39,10 +39,13 @@ class Xml_node: p = p.get_parent () def get_typed_children (self, klass): - return [c for c in self._children if isinstance(c, klass)] + if not klass: + return [] + else: + return [c for c in self._children if isinstance(c, klass)] def get_named_children (self, nm): - return self.get_typed_children (class_dict[nm]) + return self.get_typed_children (class_dict.get (nm)) def get_named_child (self, nm): return self.get_maybe_exist_named_child (nm) diff --git a/scripts/musicxml2ly.py b/scripts/musicxml2ly.py index a82f3dd..4bb3081 100644 --- a/scripts/musicxml2ly.py +++ b/scripts/musicxml2ly.py @@ -131,10 +131,10 @@ def musicxml_attributes_to_lily (attrs): 'key': musicxml_key_to_lily } for (k, func) in attr_dispatch.items (): - childs = attrs.get_named_children (k) + children = attrs.get_named_children (k) ## ugh: you get clefs spread over staves for piano - if childs: + if children: elts.append (func (attrs)) return elts -- 1.5.2.3
_______________________________________________ lilypond-devel mailing list lilypond-devel@gnu.org http://lists.gnu.org/mailman/listinfo/lilypond-devel