X-Git-Url: https://git.donarmstrong.com/?a=blobdiff_plain;f=scripts%2Fmusicxml2ly.py;h=14c09dadbded444d28d8077e1d695c70481af767;hb=ce1940b6a390c8df724ce2bfb28e9bcf50530b8b;hp=8f3b42a3967aaf8cc376f6e95b2ae5468e30e1f8;hpb=23efbcbaa7dab9303faec9fc0aca4fec805e550c;p=lilypond.git diff --git a/scripts/musicxml2ly.py b/scripts/musicxml2ly.py index 8f3b42a396..14c09dadbd 100644 --- a/scripts/musicxml2ly.py +++ b/scripts/musicxml2ly.py @@ -25,11 +25,11 @@ from rational import Rational options = None def progress (str): - sys.stderr.write (str + '\n') + ly.stderr_write (str + '\n') sys.stderr.flush () def error_message (str): - sys.stderr.write (str + '\n') + ly.stderr_write (str + '\n') sys.stderr.flush () needed_additional_definitions = [] @@ -196,7 +196,6 @@ class PartGroupInfo: error_message ("Unprocessed PartGroupInfo %s encountered" % self) return '' - def staff_attributes_to_string_tunings (mxl_attr): details = mxl_attr.get_maybe_exist_named_child ('staff-details') if not details: @@ -219,7 +218,7 @@ def staff_attributes_to_string_tunings (mxl_attr): step = i.get_named_child (u'tuning-step') step = step.get_text ().strip () - p.step = (ord (step) - ord ('A') + 7 - 2) % 7 + p.step = musicxml_step_to_lily (step) octave = i.get_named_child (u'tuning-octave') octave = octave.get_text ().strip () @@ -757,27 +756,41 @@ def musicxml_spanner_to_lily_event (mxl_event): return ev def musicxml_direction_to_indicator (direction): - return { "above": 1, "upright": 1, "up":1, "below": -1, "downright": -1, "down": -1 }.get (direction, 0) + return { "above": 1, "upright": 1, "up":1, "below": -1, "downright": -1, "down": -1, "inverted": -1 }.get (direction, 0) def musicxml_fermata_to_lily_event (mxl_event): ev = musicexp.ArticulationEvent () - ev.type = "fermata" + txt = mxl_event.get_text () + # The contents of the element defined the shape, possible are normal, angled and square + ev.type = { "angled": "shortfermata", "square": "longfermata" }.get (txt, "fermata") if hasattr (mxl_event, 'type'): dir = musicxml_direction_to_indicator (mxl_event.type) if dir and options.convert_directions: ev.force_direction = dir return ev - def musicxml_arpeggiate_to_lily_event (mxl_event): ev = musicexp.ArpeggioEvent () ev.direction = musicxml_direction_to_indicator (getattr (mxl_event, 'direction', None)) return ev - def musicxml_tremolo_to_lily_event (mxl_event): ev = musicexp.TremoloEvent () - ev.bars = mxl_event.get_text () + txt = mxl_event.get_text () + if txt: + ev.bars = txt + else: + ev.bars = "3" + return ev + +def musicxml_falloff_to_lily_event (mxl_event): + ev = musicexp.BendEvent () + ev.alter = -4 + return ev + +def musicxml_doit_to_lily_event (mxl_event): + ev = musicexp.BendEvent () + ev.alter = 4 return ev def musicxml_bend_to_lily_event (mxl_event): @@ -785,6 +798,12 @@ def musicxml_bend_to_lily_event (mxl_event): ev.alter = mxl_event.bend_alter () return ev +def musicxml_caesura_to_lily_event (mxl_event): + ev = musicexp.MarkupEvent () + # FIXME: default to straight or curved caesura? + ev.contents = "\\musicglyph #\"scripts.caesura.straight\"" + ev.force_direction = 1 + return ev def musicxml_fingering_event (mxl_event): ev = musicexp.ShortArticulationEvent () @@ -835,13 +854,13 @@ articulations_dict = { "accidental-mark": musicxml_accidental_mark, "bend": musicxml_bend_to_lily_event, "breath-mark": (musicexp.NoDirectionArticulationEvent, "breathe"), - #"caesura": "caesura", + "caesura": musicxml_caesura_to_lily_event, #"delayed-turn": "?", "detached-legato": (musicexp.ShortArticulationEvent, "_"), # or "portato" - #"doit": "", + "doit": musicxml_doit_to_lily_event, #"double-tongue": "", "down-bow": "downbow", - #"falloff": "", + "falloff": musicxml_falloff_to_lily_event, "fingering": musicxml_fingering_event, #"fingernails": "", #"fret": "", @@ -868,7 +887,7 @@ articulations_dict = { "strong-accent": (musicexp.ShortArticulationEvent, "^"), # or "marcato" #"tap": "", "tenuto": (musicexp.ShortArticulationEvent, "-"), # or "tenuto" - #"thumb-position": "", + "thumb-position": "thumb", #"toe": "", "turn": "turn", "tremolo": musicxml_tremolo_to_lily_event, @@ -1071,26 +1090,34 @@ instrument_drumtype_dict = { def musicxml_note_to_lily_main_event (n): pitch = None duration = None - - mxl_pitch = n.get_maybe_exist_typed_child (musicxml.Pitch) event = None + + mxl_pitch = n.get_maybe_exist_typed_child (musicxml.Pitch) if mxl_pitch: pitch = musicxml_pitch_to_lily (mxl_pitch) - event = musicexp.NoteEvent() + event = musicexp.NoteEvent () event.pitch = pitch acc = n.get_maybe_exist_named_child ('accidental') if acc: # let's not force accs everywhere. event.cautionary = acc.editorial + + elif n.get_maybe_exist_typed_child (musicxml.Unpitched): + # Unpitched elements have display-step and can also have + # display-octave. + unpitched = n.get_maybe_exist_typed_child (musicxml.Unpitched) + event = musicexp.NoteEvent () + event.pitch = musicxml_unpitched_to_lily (unpitched) elif n.get_maybe_exist_typed_child (musicxml.Rest): # rests can have display-octave and display-step, which are # treated like an ordinary note pitch rest = n.get_maybe_exist_typed_child (musicxml.Rest) - event = musicexp.RestEvent() + event = musicexp.RestEvent () pitch = musicxml_restdisplay_to_lily (rest) event.pitch = pitch + elif n.instrument_name: event = musicexp.NoteEvent () drum_type = instrument_drumtype_dict.get (n.instrument_name) @@ -1099,11 +1126,13 @@ def musicxml_note_to_lily_main_event (n): else: n.message ("drum %s type unknown, please add to instrument_drumtype_dict" % n.instrument_name) event.drum_type = 'acousticsnare' - - if not event: + + else: n.message ("cannot find suitable event") - event.duration = musicxml_duration_to_lily (n) + if event: + event.duration = musicxml_duration_to_lily (n) + return event @@ -1150,7 +1179,7 @@ class LilyPondVoiceBuilder: self.set_duration (duration) # Insert all pending dynamics right after the note/rest: - if isinstance (music, musicexp.EventChord) and self.pending_dynamics: + if isinstance (music, musicexp.ChordEvent) and self.pending_dynamics: for d in self.pending_dynamics: music.append (d) self.pending_dynamics = [] @@ -1187,10 +1216,23 @@ class LilyPondVoiceBuilder: if diff > Rational (0) and not (self.ignore_skips and moment == 0): skip = musicexp.SkipEvent() - skip.duration.duration_log = 0 - skip.duration.factor = diff + duration_factor = 1 + duration_log = {1: 0, 2: 1, 4:2, 8:3, 16:4, 32:5, 64:6, 128:7, 256:8, 512:9}.get (diff.denominator (), -1) + duration_dots = 0 + if duration_log > 0: # denominator is a power of 2... + if diff.numerator () == 3: + duration_log -= 1 + duration_dots = 1 + else: + duration_factor = Rational (diff.numerator ()) + else: + duration_log = 0 + duration_factor = diff + skip.duration.duration_log = duration_log + skip.duration.factor = duration_factor + skip.duration.dots = duration_dots - evc = musicexp.EventChord () + evc = musicexp.ChordEvent () evc.elements.append (skip) self.add_music (evc, diff) @@ -1201,16 +1243,16 @@ class LilyPondVoiceBuilder: value = None - # if the position matches, find the last EventChord, do not cross a bar line! + # if the position matches, find the last ChordEvent, do not cross a bar line! at = len( self.elements ) - 1 while (at >= 0 and - not isinstance (self.elements[at], musicexp.EventChord) and + not isinstance (self.elements[at], musicexp.ChordEvent) and not isinstance (self.elements[at], musicexp.BarLine)): at -= 1 if (self.elements and at >= 0 - and isinstance (self.elements[at], musicexp.EventChord) + and isinstance (self.elements[at], musicexp.ChordEvent) and self.begin_moment == starting_at): value = self.elements[at] else: @@ -1221,7 +1263,7 @@ class LilyPondVoiceBuilder: def correct_negative_skip (self, goto): self.end_moment = goto self.begin_moment = goto - evc = musicexp.EventChord () + evc = musicexp.ChordEvent () self.elements.append (evc) @@ -1232,12 +1274,21 @@ class VoiceData: self.lyrics_dict = {} self.lyrics_order = [] +def musicxml_step_to_lily (step): + if step: + return (ord (step) - ord ('A') + 7 - 2) % 7 + else: + return None + def musicxml_voice_to_lily_voice (voice): tuplet_events = [] modes_found = {} lyrics = {} return_value = VoiceData () return_value.voicedata = voice + + # First pitch needed for relative mode (if selected in command-line options) + first_pitch = None # Needed for melismata detection (ignore lyrics on those notes!): inside_slur = False @@ -1338,15 +1389,16 @@ def musicxml_voice_to_lily_voice (voice): voice_builder.add_bar_check (num) main_event = musicxml_note_to_lily_main_event (n) + if main_event and not first_pitch: + first_pitch = main_event.pitch ignore_lyrics = inside_slur or is_tied or is_chord - if hasattr (main_event, 'drum_type') and main_event.drum_type: + if main_event and hasattr (main_event, 'drum_type') and main_event.drum_type: modes_found['drummode'] = True - ev_chord = voice_builder.last_event_chord (n._when) if not ev_chord: - ev_chord = musicexp.EventChord() + ev_chord = musicexp.ChordEvent() voice_builder.add_music (ev_chord, n._duration) grace = n.get_maybe_exist_typed_child (musicxml.Grace) @@ -1355,14 +1407,12 @@ def musicxml_voice_to_lily_voice (voice): if n.get_maybe_exist_typed_child (musicxml.Chord) and ev_chord.grace_elements: grace_chord = ev_chord.grace_elements.get_last_event_chord () if not grace_chord: - grace_chord = musicexp.EventChord () + grace_chord = musicexp.ChordEvent () ev_chord.append_grace (grace_chord) if hasattr (grace, 'slash'): # TODO: use grace_type = "appoggiatura" for slurred grace notes if grace.slash == "yes": ev_chord.grace_type = "acciaccatura" - elif grace.slash == "no": - ev_chord.grace_type = "grace" # now that we have inserted the chord into the grace music, insert # everything into that chord instead of the ev_chord ev_chord = grace_chord @@ -1386,8 +1436,7 @@ def musicxml_voice_to_lily_voice (voice): # +fermata | arpeggiate | non-arpeggiate | # accidental-mark | other-notation for notations in notations_children: - if notations.get_tuplet(): - tuplet_event = notations.get_tuplet() + for tuplet_event in notations.get_tuplets(): mod = n.get_maybe_exist_typed_child (musicxml.Time_modification) frac = (1,1) if mod: @@ -1451,12 +1500,6 @@ def musicxml_voice_to_lily_voice (voice): # 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') @@ -1507,7 +1550,7 @@ def musicxml_voice_to_lily_voice (voice): tuplet_events.append ((ev_chord, tuplet_event, frac)) ## force trailing mm rests to be written out. - voice_builder.add_music (musicexp.EventChord (), Rational (0)) + voice_builder.add_music (musicexp.ChordEvent (), Rational (0)) ly_voice = group_tuplets (voice_builder.elements, tuplet_events) ly_voice = group_repeats (ly_voice) @@ -1527,6 +1570,12 @@ def musicxml_voice_to_lily_voice (voice): if len (modes_found) > 1: error_message ('Too many modes found %s' % modes_found.keys ()) + + if options.relative: + v = musicexp.RelativeMusic () + v.element = seq_music + v.basepitch = first_pitch + seq_music = v return_value.ly_voice = seq_music for mode in modes_found.keys (): @@ -1537,7 +1586,6 @@ def musicxml_voice_to_lily_voice (voice): return return_value - def musicxml_id_to_lily (id): digits = ['Zero', 'One', 'Two', 'Three', 'Four', 'Five', 'Six', 'Seven', 'Eight', 'Nine', 'Ten'] @@ -1549,20 +1597,30 @@ def musicxml_id_to_lily (id): id = re.sub ('[^a-zA-Z]', 'X', id) return id - def musicxml_pitch_to_lily (mxl_pitch): - p = musicexp.Pitch() + p = musicexp.Pitch () p.alteration = mxl_pitch.get_alteration () - p.step = (ord (mxl_pitch.get_step ()) - ord ('A') + 7 - 2) % 7 + p.step = musicxml_step_to_lily (mxl_pitch.get_step ()) p.octave = mxl_pitch.get_octave () - 4 return p +def musicxml_unpitched_to_lily (mxl_unpitched): + p = None + step = mxl_unpitched.get_step () + if step: + p = musicexp.Pitch () + p.step = musicxml_step_to_lily (step) + octave = mxl_unpitched.get_octave () + if octave and p: + p.octave = octave - 4 + return p + def musicxml_restdisplay_to_lily (mxl_rest): p = None step = mxl_rest.get_step () if step: - p = musicexp.Pitch() - p.step = (ord (step) - ord ('A') + 7 - 2) % 7 + p = musicexp.Pitch () + p.step = musicxml_step_to_lily (step) octave = mxl_rest.get_octave () if octave and p: p.octave = octave - 4 @@ -1602,7 +1660,7 @@ def get_all_voices (parts): def option_parser (): - p = ly.get_option_parser(usage=_ ("musicxml2ly FILE.xml"), + p = ly.get_option_parser(usage=_ ("musicxml2ly [options] FILE.xml"), version=('''%prog (LilyPond) @TOPLEVEL_VERSION@\n\n''' + _ ("""This program is free software. It is covered by the GNU General Public @@ -1611,8 +1669,9 @@ under certain conditions. Invoke as `%s --warranty' for more information.""") % 'lilypond' + """ Copyright (c) 2005--2007 by - Han-Wen Nienhuys and - Jan Nieuwenhuizen + Han-Wen Nienhuys , + Jan Nieuwenhuizen and + Reinhold Kainhofer """), description=_ ("Convert %s to LilyPond input.") % 'MusicXML' + "\n") p.add_option ('-v', '--verbose', @@ -1632,6 +1691,11 @@ Copyright (c) 2005--2007 by default = False, help = _ ("Input file is a zip-compressed MusicXML file.")) + p.add_option ('-r', '--relative', + action = "store_true", + dest = "relative", + help = _ ("Convert pitches in relative mode.")) + p.add_option ('-l', '--language', action = "store", help = _ ("Use a different language file, e.g. 'deutsch' for deutsch.ly."))