X-Git-Url: https://git.donarmstrong.com/?a=blobdiff_plain;f=scripts%2Fmusicxml2ly.py;h=a429b752510dbcb104937f080be03548f4493572;hb=e1b49a5a91d91cbb1be3e8ee9fd182cb845a850a;hp=2e22f8e76cab500203df528807d619599e349126;hpb=92aecd273c6940deba6c6ec70770817d8f5094d5;p=lilypond.git diff --git a/scripts/musicxml2ly.py b/scripts/musicxml2ly.py index 2e22f8e76c..a429b75251 100644 --- a/scripts/musicxml2ly.py +++ b/scripts/musicxml2ly.py @@ -24,6 +24,16 @@ from rational import Rational # Store command-line options in a global variable, so we can access them everythwere options = None +class Conversion_Settings: + def __init__(self): + 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') sys.stderr.flush () @@ -55,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: @@ -177,6 +187,19 @@ def extract_score_information (tree): set_if_exists ('encoder', ids.get_encoding_person ()) set_if_exists ('encodingdescription', ids.get_encoding_description ()) + # Finally, apply the required compatibility modes + # Some applications created wrong MusicXML files, so we need to + # apply some compatibility mode, e.g. ignoring some features/tags + # in those files + software = ids.get_encoding_software_list () + + # Case 1: "Sibelius 5.1" with the "Dolet 3.4 for Sibelius" plugin + # is missing all beam ends => ignore all beaming information + if "Dolet 3.4 for Sibelius" in software: + conversion_settings.ignore_beaming = True + progress (_ ("Encountered file created by Dolet 3.4 for Sibelius, containing wrong beaming information. All beaming information in the MusicXML file will be ignored")) + # TODO: Check for other unsupported features + return header class PartGroupInfo: @@ -248,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 ()) @@ -271,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): @@ -399,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 @@ -1358,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 @@ -1426,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 @@ -1523,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 @@ -1622,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 ! + 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 | @@ -1655,30 +1687,18 @@ 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') and b.is_primary ())] - if mxl_beams: + if mxl_beams and not conversion_settings.ignore_beaming: 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) @@ -1688,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)) @@ -1862,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", @@ -1949,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) @@ -2009,21 +2056,24 @@ def read_musicxml (filename, compressed, use_lxml): def convert (filename, options): progress (_ ("Reading MusicXML from %s ...") % filename) - + tree = read_musicxml (filename, options.compressed, options.use_lxml) + score_information = extract_score_information (tree) + paper_information = extract_paper_information (tree) + parts = tree.get_typed_children (musicxml.Part) (voices, staff_info) = get_all_voices (parts) 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 , or tags - score_information = extract_score_information (tree) - layout_information = extract_layout_information (tree) 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) @@ -2043,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) @@ -2084,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")