From 0ab607a65fefe22209037fa7a7449a0c75d2c6d5 Mon Sep 17 00:00:00 2001 From: Reinhold Kainhofer Date: Mon, 20 Aug 2007 00:21:30 +0200 Subject: [PATCH] Convert dynamic marks (given in a 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 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 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