]> git.donarmstrong.com Git - lilypond.git/blobdiff - scripts/musicxml2ly.py
Merge branch 'master' of ssh+git://git.sv.gnu.org/srv/git/lilypond
[lilypond.git] / scripts / musicxml2ly.py
index 4513e363e653eadca56b949302dd04b0146977e5..a429b752510dbcb104937f080be03548f4493572 100644 (file)
@@ -29,6 +29,10 @@ class Conversion_Settings:
        self.ignore_beaming = False
 
 conversion_settings = Conversion_Settings ()
+# Use a global variable to store the setting needed inside a \layout block.
+# whenever we need to change a setting or add/remove an engraver, we can access 
+# this layout and add the corresponding settings
+layout_information = musicexp.Layout ()
 
 def progress (str):
     ly.stderr_write (str + '\n')
@@ -61,7 +65,7 @@ additional_definitions = {
 def round_to_two_digits (val):
     return round (val * 100) / 100
 
-def extract_layout_information (tree):
+def extract_paper_information (tree):
     paper = musicexp.Paper ()
     defaults = tree.get_maybe_exist_named_child ('defaults')
     if not defaults:
@@ -267,9 +271,9 @@ def staff_attributes_to_lily_staff (mxl_attr):
             clef_sign = {"percussion": "percussion", "TAB": "tab"}.get (sign.get_text (), None)
 
     lines = 5
-    details = attributes.get_maybe_exist_named_child ('staff-details')
-    if details:
-        staff_lines = details.get_maybe_exist_named_child ('staff-lines')
+    details = attributes.get_named_children ('staff-details')
+    for d in details:
+        staff_lines = d.get_maybe_exist_named_child ('staff-lines')
         if staff_lines:
             lines = string.atoi (staff_lines.get_text ())
 
@@ -290,10 +294,10 @@ def staff_attributes_to_lily_staff (mxl_attr):
     return staff
 
 
-def extract_score_layout (part_list, staffinfo):
-    layout = musicexp.StaffGroup (None)
+def extract_score_structure (part_list, staffinfo):
+    structure = musicexp.StaffGroup (None)
     if not part_list:
-        return layout
+        return structure
 
     def read_score_part (el):
         if not isinstance (el, musicxml.Score_part):
@@ -418,8 +422,8 @@ def extract_score_layout (part_list, staffinfo):
     if len (staves) == 1:
         return staves[0]
     for i in staves:
-        layout.append_staff (i)
-    return layout
+        structure.append_staff (i)
+    return structure
 
 
 
@@ -1377,6 +1381,7 @@ class LilyPondVoiceBuilder:
                 else:
                     duration_factor = Rational (diff.numerator ())
             else:
+                # for skips of a whole or more, simply use s1*factor
                 duration_log = 0
                 duration_factor = diff
             skip.duration.duration_log = duration_log
@@ -1445,6 +1450,7 @@ def musicxml_voice_to_lily_voice (voice):
     inside_slur = False
     is_tied = False
     is_chord = False
+    is_beamed = False
     ignore_lyrics = False
 
     current_staff = None
@@ -1542,7 +1548,8 @@ def musicxml_voice_to_lily_voice (voice):
         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
+        # ignore lyrics for notes inside a slur, tie, chord or beam
+        ignore_lyrics = inside_slur or is_tied or is_chord or is_beamed
 
         if main_event and hasattr (main_event, 'drum_type') and main_event.drum_type:
             modes_found['drummode'] = True
@@ -1641,7 +1648,13 @@ def musicxml_voice_to_lily_voice (voice):
                 ev = musicxml_spanner_to_lily_event (a)
                 if ev:
                     ev_chord.append (ev)
-                
+
+            # accidental-marks are direct children of <notation>!
+            for a in notations.get_named_children ('accidental-mark'):
+                ev = musicxml_articulation_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 |
@@ -1674,22 +1687,6 @@ def musicxml_voice_to_lily_voice (voice):
                     if ev:
                         ev_chord.append (ev)
 
-        # Extract the lyrics
-        if not rest and not ignore_lyrics:
-            note_lyrics_processed = []
-            note_lyrics_elements = n.get_typed_children (musicxml.Lyric)
-            for l in note_lyrics_elements:
-                if l.get_number () < 0:
-                    for k in lyrics.keys ():
-                        lyrics[k].append (l.lyric_to_text ())
-                        note_lyrics_processed.append (k)
-                else:
-                    lyrics[l.number].append(l.lyric_to_text ())
-                    note_lyrics_processed.append (l.number)
-            for lnr in lyrics.keys ():
-                if not lnr in note_lyrics_processed:
-                    lyrics[lnr].append ("\skip4")
-
 
         mxl_beams = [b for b in n.get_named_children ('beam')
                      if (b.get_type () in ('begin', 'end')
@@ -1698,6 +1695,10 @@ def musicxml_voice_to_lily_voice (voice):
             beam_ev = musicxml_spanner_to_lily_event (mxl_beams[0])
             if beam_ev:
                 ev_chord.append (beam_ev)
+                if beam_ev.span_direction == -1: # beam and thus melisma starts here
+                    is_beamed = True
+                elif beam_ev.span_direction == 1: # beam and thus melisma ends here
+                    is_beamed = False
             
         if tuplet_event:
             mod = n.get_maybe_exist_typed_child (musicxml.Time_modification)
@@ -1707,6 +1708,22 @@ def musicxml_voice_to_lily_voice (voice):
                 
             tuplet_events.append ((ev_chord, tuplet_event, frac))
 
+        # Extract the lyrics
+        if not rest and not ignore_lyrics:
+            note_lyrics_processed = []
+            note_lyrics_elements = n.get_typed_children (musicxml.Lyric)
+            for l in note_lyrics_elements:
+                if l.get_number () < 0:
+                    for k in lyrics.keys ():
+                        lyrics[k].append (l.lyric_to_text ())
+                        note_lyrics_processed.append (k)
+                else:
+                    lyrics[l.number].append(l.lyric_to_text ())
+                    note_lyrics_processed.append (l.number)
+            for lnr in lyrics.keys ():
+                if not lnr in note_lyrics_processed:
+                    lyrics[lnr].append ("\skip4")
+
     ## force trailing mm rests to be written out.   
     voice_builder.add_music (musicexp.ChordEvent (), Rational (0))
     
@@ -1881,6 +1898,12 @@ Copyright (c) 2005--2008 by
                   dest = "convert_directions",
                   help = _ ("do not convert directions (^, _ or -) for articulations, dynamics, etc."))
 
+    p.add_option ('--no-beaming', 
+                  action = "store_false",
+                  default = True,
+                  dest = "convert_beaming",
+                  help = _ ("do not convert beaming information, use lilypond's automatic beaming instead"))
+
     p.add_option ('-o', '--output',
                   metavar = _ ("FILE"),
                   action = "store",
@@ -1968,6 +1991,11 @@ def update_score_setup (score_structure, part_list, voices):
             staves_info.append (format_staff_info (part_id, None, thisstaff_raw_voices))
         score_structure.set_part_information (part_id, staves_info)
 
+# Set global values in the \layout block, like auto-beaming etc.
+def update_layout_information ():
+    if not conversion_settings.ignore_beaming and layout_information:
+        layout_information.set_context_item ('Score', 'autoBeaming = ##f')
+
 def print_ly_preamble (printer, filename):
     printer.dump_version ()
     printer.print_verbatim ('%% automatically converted from %s\n' % filename)
@@ -2031,7 +2059,7 @@ def convert (filename, options):
 
     tree = read_musicxml (filename, options.compressed, options.use_lxml)
     score_information = extract_score_information (tree)
-    layout_information = extract_layout_information (tree)
+    paper_information = extract_paper_information (tree)
 
     parts = tree.get_typed_children (musicxml.Part)
     (voices, staff_info) = get_all_voices (parts)
@@ -2039,11 +2067,13 @@ def convert (filename, options):
     score_structure = None
     mxl_pl = tree.get_maybe_exist_typed_child (musicxml.Part_list)
     if mxl_pl:
-        score_structure = extract_score_layout (mxl_pl, staff_info)
+        score_structure = extract_score_structure (mxl_pl, staff_info)
         part_list = mxl_pl.get_named_children ("score-part")
 
     # score information is contained in the <work>, <identification> or <movement-title> tags
     update_score_setup (score_structure, part_list, voices)
+    # After the conversion, update the list of settings for the \layout block
+    update_layout_information ()
 
     if not options.output_name:
         options.output_name = os.path.basename (filename) 
@@ -2063,6 +2093,8 @@ def convert (filename, options):
     print_ly_additional_definitions (printer, filename)
     if score_information:
         score_information.print_ly (printer)
+    if paper_information:
+        paper_information.print_ly (printer)
     if layout_information:
         layout_information.print_ly (printer)
     print_voice_definitions (printer, part_list, voices)
@@ -2104,6 +2136,7 @@ def main ():
         musicexp.set_pitch_language (options.language)
         needed_additional_definitions.append (options.language)
         additional_definitions[options.language] = "\\include \"%s.ly\"\n" % options.language
+    conversion_settings.ignore_beaming = not options.convert_beaming
 
     # Allow the user to leave out the .xml or xml on the filename
     filename = get_existing_filename_with_extension (args[0], "xml")