]> git.donarmstrong.com Git - lilypond.git/blob - scripts/musicxml2ly.py
Issue4986: Fix XeLaTex option consistent with XeTeX option
[lilypond.git] / scripts / musicxml2ly.py
1 #!@TARGET_PYTHON@
2 # -*- coding: utf-8 -*-
3 import optparse
4 import sys
5 import re
6 import os
7 import string
8 import codecs
9 import zipfile
10 import tempfile
11 import StringIO
12 import warnings
13
14 """
15 @relocate-preamble@
16 """
17
18 import lilylib as ly
19 _ = ly._
20
21 import utilities
22 import musicxml2ly_conversion
23 import musicxml
24 import musicexp
25
26 from rational import Rational
27
28 lilypond_version = "@TOPLEVEL_VERSION@"
29
30 # Store command-line options in a global variable, so we can access them everywhere
31 options = None
32
33 class Conversion_Settings:
34     def __init__(self):
35        self.ignore_beaming = False
36        self.convert_stem_directions = False
37        self.convert_rest_positions = True
38
39 conversion_settings = Conversion_Settings()
40 # Use a global variable to store the setting needed inside a \layout block.
41 # whenever we need to change a setting or add/remove an engraver, we can access
42 # this layout and add the corresponding settings
43 layout_information = musicexp.Layout()
44 # Use a global variable to store the setting needed inside a \paper block.
45 paper = musicexp.Paper()
46
47 needed_additional_definitions = []
48 additional_definitions = {
49   "tuplet-note-wrapper": """      % a formatter function, which is simply a wrapper around an existing
50       % tuplet formatter function. It takes the value returned by the given
51       % function and appends a note of given length.
52   #(define-public ((tuplet-number::append-note-wrapper function note) grob)
53     (let* ((txt (if function (function grob) #f)))
54       (if txt
55         (markup txt #:fontsize -5 #:note note UP)
56         (markup #:fontsize -5 #:note note UP)
57       )
58     )
59   )""",
60
61   "tuplet-non-default-denominator": """#(define ((tuplet-number::non-default-tuplet-denominator-text denominator) grob)
62   (number->string (if denominator
63                       denominator
64                       (ly:event-property (event-cause grob) 'denominator))))
65 """,
66
67   "tuplet-non-default-fraction": """#(define ((tuplet-number::non-default-tuplet-fraction-text denominator numerator) grob)
68     (let* ((ev (event-cause grob))
69            (den (if denominator denominator (ly:event-property ev 'denominator)))
70            (num (if numerator numerator (ly:event-property ev 'numerator))))
71        (format #f "~a:~a" den num)))
72 """,
73 }
74
75 def round_to_two_digits(val):
76     return round(val * 100) / 100
77
78 def extract_paper_information(score_partwise):
79     defaults = score_partwise.get_maybe_exist_named_child('defaults')
80     if not defaults:
81         return None
82     tenths = -1
83     scaling = defaults.get_maybe_exist_named_child('scaling')
84     default_tenths_to_millimeters_ratio = 0.175
85     default_staff_size = 20
86     if scaling:
87         mm = scaling.get_named_child('millimeters')
88         mm = float(mm.get_text())
89         tn = scaling.get_maybe_exist_named_child('tenths')
90         tn = float(tn.get_text())
91         # The variable 'tenths' is actually a ratio, NOT the value of <tenths>.
92         # TODO: rename and replace.
93         tenths = mm / tn
94         ratio = tenths / default_tenths_to_millimeters_ratio
95         staff_size = default_staff_size * ratio
96
97         if 1 < staff_size < 100:
98             paper.global_staff_size = staff_size
99         else:
100             msg = "paper.global_staff_size {} is too large, using defaults=20".format(
101                 staff_size)
102             warnings.warn(msg)
103             paper.global_staff_size = 20
104
105     # We need the scaling(i.e. the size of staff tenths for everything!
106     if tenths < 0:
107         return None
108
109     def from_tenths(txt):
110         return round_to_two_digits(float(txt) * tenths / 10)
111     def set_paper_variable(varname, parent, element_name):
112         el = parent.get_maybe_exist_named_child(element_name)
113         if el: # Convert to cm from tenths
114             setattr(paper, varname, from_tenths(el.get_text()))
115
116     pagelayout = defaults.get_maybe_exist_named_child('page-layout')
117     if pagelayout:
118         # TODO: How can one have different margins for even and odd pages???
119         set_paper_variable("page_height", pagelayout, 'page-height')
120         set_paper_variable("page_width", pagelayout, 'page-width')
121
122         if conversion_settings.convert_page_margins:
123             pmargins = pagelayout.get_named_children('page-margins')
124             for pm in pmargins:
125                 set_paper_variable("left_margin", pm, 'left-margin')
126                 set_paper_variable("right_margin", pm, 'right-margin')
127                 set_paper_variable("bottom_margin", pm, 'bottom-margin')
128                 set_paper_variable("top_margin", pm, 'top-margin')
129
130     systemlayout = defaults.get_maybe_exist_named_child('system-layout')
131     if systemlayout:
132         sl = systemlayout.get_maybe_exist_named_child('system-margins')
133         if sl:
134             set_paper_variable("system_left_margin", sl, 'left-margin')
135             set_paper_variable("system_right_margin", sl, 'right-margin')
136         set_paper_variable("system_distance", systemlayout, 'system-distance')
137         set_paper_variable("top_system_distance", systemlayout, 'top-system-distance')
138
139     stafflayout = defaults.get_named_children('staff-layout')
140     for sl in stafflayout:
141         nr = getattr(sl, 'number', 1)
142         dist = sl.get_named_child('staff-distance')
143         #TODO: the staff distance needs to be set in the Staff context!!!
144
145     # TODO: Finish appearance?, music-font?, word-font?, lyric-font*, lyric-language*
146     appearance = defaults.get_named_child('appearance')
147     if appearance:
148         lws = appearance.get_named_children('line-width')
149         for lw in lws:
150             # Possible types are: beam, bracket, dashes,
151             #    enclosure, ending, extend, heavy barline, leger,
152             #    light barline, octave shift, pedal, slur middle, slur tip,
153             #    staff, stem, tie middle, tie tip, tuplet bracket, and wedge
154             tp = lw.type
155             w = from_tenths(lw.get_text())
156             # TODO: Do something with these values!
157         nss = appearance.get_named_children('note-size')
158         for ns in nss:
159             # Possible types are: cue, grace and large
160             tp = ns.type
161             sz = from_tenths(ns.get_text())
162             # TODO: Do something with these values!
163         # <other-appearance> elements have no specified meaning
164
165     rawmusicfont = defaults.get_named_child('music-font')
166     if rawmusicfont:
167         # TODO: Convert the font
168         pass
169     rawwordfont = defaults.get_named_child('word-font')
170     if rawwordfont:
171         # TODO: Convert the font
172         pass
173     rawlyricsfonts = defaults.get_named_children('lyric-font')
174     for lyricsfont in rawlyricsfonts:
175         # TODO: Convert the font
176         pass
177
178     return paper
179
180
181 credit_dict = {
182     None:None,
183     '':None,
184     'page number':None, #TODO: what is it used for ?
185     'title':'title',
186     'subtitle':'subtitle',
187     'composer':'composer',
188     'arranger':'arranger',
189     'lyricist':'poet',
190     'rights':'copyright'
191 }
192 # score information is contained in the <work>, <identification> or <movement-title> tags
193 # extract those into a hash, indexed by proper lilypond header attributes
194 def extract_score_information(tree):
195     header = musicexp.Header()
196     def set_if_exists(field, value):
197         if value:
198             header.set_field(field, utilities.escape_ly_output_string(value))
199
200     movement_title = tree.get_maybe_exist_named_child('movement-title')
201     movement_number = tree.get_maybe_exist_named_child('movement-number')
202     if movement_title:
203         set_if_exists('title', movement_title.get_text())
204     if movement_number:
205           set_if_exists('movementnumber', movement_number.get_text())
206           # set_if_exists('piece', movement_number.get_text()) # the movement number should be visible in the score.
207
208     work = tree.get_maybe_exist_named_child('work')
209     if work:
210         work_number = work.get_work_number()
211         work_title = work.get_work_title()
212         # Overwrite the title from movement-title with work->title
213         set_if_exists('title', work.get_work_title())
214         set_if_exists('opus', work.get_work_number())
215         # Use movement-title as subtitle
216         if movement_title:
217               set_if_exists('subtitle', movement_title.get_text())
218
219 # TODO: Translation of opus element. Not to be confused with opus in LilyPond. MusicXML opus is a document element for opus DTD
220     identifications = tree.get_named_children('identification')
221     for ids in identifications:
222         set_if_exists('copyright', ids.get_rights())
223         set_if_exists('composer', ids.get_composer())
224         set_if_exists('arranger', ids.get_arranger())
225         set_if_exists('editor', ids.get_editor())
226         set_if_exists('poet', ids.get_poet())
227
228         set_if_exists('encodingsoftware', ids.get_encoding_software())
229         set_if_exists('encodingdate', ids.get_encoding_date())
230         set_if_exists('encoder', ids.get_encoding_person())
231         set_if_exists('encodingdescription', ids.get_encoding_description())
232         set_if_exists('source', ids.get_source())
233
234         # <miscellaneous><miscellaneous-field name="description"> ... becomes
235         # \header { texidoc = ...
236         set_if_exists('texidoc', ids.get_file_description());
237
238         # Finally, apply the required compatibility modes
239         # Some applications created wrong MusicXML files, so we need to
240         # apply some compatibility mode, e.g. ignoring some features/tags
241         # in those files
242         software = ids.get_encoding_software_list()
243
244         # Case 1: "Sibelius 5.1" with the "Dolet 3.4 for Sibelius" plugin
245         #         is missing all beam ends => ignore all beaming information
246         ignore_beaming_software = {
247             "Dolet 4 for Sibelius, Beta 2": "Dolet 4 for Sibelius, Beta 2",
248             "Dolet 3.5 for Sibelius": "Dolet 3.5 for Sibelius",
249             "Dolet 3.4 for Sibelius": "Dolet 3.4 for Sibelius",
250             "Dolet 3.3 for Sibelius": "Dolet 3.3 for Sibelius",
251             "Dolet 3.2 for Sibelius": "Dolet 3.2 for Sibelius",
252             "Dolet 3.1 for Sibelius": "Dolet 3.1 for Sibelius",
253             "Dolet for Sibelius 1.3": "Dolet for Sibelius 1.3",
254             "Noteworthy Composer": "Noteworthy Composer's nwc2xm[",
255         }
256         for s in software:
257             app_description = ignore_beaming_software.get(s, False);
258             if app_description:
259                 conversion_settings.ignore_beaming = True
260                 ly.warning(_("Encountered file created by %s, containing "
261                                "wrong beaming information. All beaming "
262                                "information in the MusicXML file will be "
263                                "ignored") % app_description)
264
265     credits = tree.get_named_children('credit')
266     has_composer = False
267     for cred in credits:
268         type = credit_dict.get(cred.get_type())
269         if(type == None):
270             type = credit_dict.get(cred.find_type(credits))
271         if(type == 'composer'):
272             if(has_composer):
273                 type = 'poet'
274             else:
275                 has_composer = True
276             set_if_exists(type, cred.get_text())
277         elif(type == 'title'):
278             if(not(work) and not(movement_title)):
279                 set_if_exists('title', cred.get_text())
280             #elif(not(movement_title)): #bullshit!
281             #    set_if_exists('subtitle', cred.get_text()) #bullshit! otherwise both title and subtitle show the work-title.
282         elif(type == None):
283             pass
284         else:
285             set_if_exists(type, cred.get_text())
286
287
288     # TODO: Check for other unsupported features
289     return header
290
291 class PartGroupInfo:
292     def __init__(self):
293         self.start = {}
294         self.end = {}
295     def is_empty(self):
296         return len(self.start) + len(self.end) == 0
297     def add_start(self, g):
298         self.start[getattr(g, 'number', "1")] = g
299     def add_end(self, g):
300         self.end[getattr(g, 'number', "1")] = g
301     def print_ly(self, printer):
302         ly.warning(_("Unprocessed PartGroupInfo %s encountered") % self)
303     def ly_expression(self):
304         ly.warning(_("Unprocessed PartGroupInfo %s encountered") % self)
305         return ''
306
307 def staff_attributes_to_string_tunings(mxl_attr):
308     details = mxl_attr.get_maybe_exist_named_child('staff-details')
309     if not details:
310         return []
311     lines = 6
312     staff_lines = details.get_maybe_exist_named_child('staff-lines')
313     if staff_lines:
314         lines = string.atoi(staff_lines.get_text())
315     tunings = [musicexp.Pitch()] * lines
316     staff_tunings = details.get_named_children('staff-tuning')
317     for i in staff_tunings:
318         p = musicexp.Pitch()
319         line = 0
320         try:
321             line = string.atoi(i.line) - 1
322         except ValueError:
323             pass
324         tunings[line] = p
325
326         step = i.get_named_child(u'tuning-step')
327         step = step.get_text().strip()
328         p.step = musicxml2ly_conversion.musicxml_step_to_lily(step)
329
330         octave = i.get_named_child(u'tuning-octave')
331         octave = octave.get_text().strip()
332         p.octave = int(octave) - 4
333
334         alter = i.get_named_child(u'tuning-alter')
335         if alter:
336             p.alteration = int(alter.get_text().strip())
337     # lilypond seems to use the opposite ordering than MusicXML...
338     tunings.reverse()
339     return tunings
340
341 def staff_attributes_to_lily_staff(mxl_attr):
342     if not mxl_attr:
343         return musicexp.Staff()
344
345     (staff_id, attributes) = mxl_attr.items()[0]
346
347     # distinguish by clef:
348     # percussion(percussion and rhythmic), tab, and everything else
349     clef_sign = None
350     clef = attributes.get_maybe_exist_named_child('clef')
351     if clef:
352         sign = clef.get_maybe_exist_named_child('sign')
353         if sign:
354             clef_sign = {"percussion": "percussion", "TAB": "tab"}.get(sign.get_text(), None)
355
356     lines = 5
357     details = attributes.get_named_children('staff-details')
358     for d in details:
359         staff_lines = d.get_maybe_exist_named_child('staff-lines')
360         if staff_lines:
361             lines = string.atoi(staff_lines.get_text())
362
363     # TODO: Handle other staff attributes like staff-space, etc.
364
365     staff = None
366     if clef_sign == "percussion" and lines == 1:
367         staff = musicexp.RhythmicStaff()
368     elif clef_sign == "percussion":
369         staff = musicexp.DrumStaff()
370         # staff.drum_style_table = ???
371     elif clef_sign == "tab":
372         staff = musicexp.TabStaff()
373         staff.string_tunings = staff_attributes_to_string_tunings(attributes)
374         # staff.tablature_format = ???
375     else:
376         staff = musicexp.Staff()
377         # TODO: Handle case with lines <> 5!
378         if(lines != 5):
379             staff.add_context_modification("\\override StaffSymbol #'line-count = #%s" % lines)
380
381     return staff
382
383 def extract_instrument_sound(score_part):
384     score_instrument = score_part.get_maybe_exist_named_child('score-instrument')
385     if not score_instrument:
386         return None
387     sound = score_instrument.get_maybe_exist_named_child('instrument-sound')
388     if sound:
389         return utilities.musicxml_sound_to_lilypond_midi_instrument(sound.get_text())
390
391 def extract_score_structure(part_list, staffinfo):
392     score = musicexp.Score()
393     structure = musicexp.StaffGroup(None)
394     score.set_contents(structure)
395
396     if not part_list:
397         return structure
398
399     def read_score_part(el):
400         if not isinstance(el, musicxml.Score_part):
401             return
402         # Depending on the attributes of the first measure, we create different
403         # types of staves(Staff, RhythmicStaff, DrumStaff, TabStaff, etc.)
404         staff = staff_attributes_to_lily_staff(staffinfo.get(el.id, None))
405         if not staff:
406             return None
407         staff.id = el.id
408         partname = el.get_maybe_exist_named_child('part-name')
409         # Finale gives unnamed parts the name "MusicXML Part" automatically!
410         if partname and partname.get_text() != "MusicXML Part":
411             staff.instrument_name = partname.get_text()
412         # part-name-display overrides part-name!
413         partname = el.get_maybe_exist_named_child("part-name-display")
414         if partname:
415             staff.instrument_name = extract_display_text(partname)
416         if hasattr(options, 'midi') and options.midi:
417             staff.sound = extract_instrument_sound(el)
418         if staff.instrument_name:
419             paper.indent = max(paper.indent, len(staff.instrument_name))
420             paper.instrument_names.append(staff.instrument_name)
421         partdisplay = el.get_maybe_exist_named_child('part-abbreviation')
422         if partdisplay:
423             staff.short_instrument_name = partdisplay.get_text()
424         # part-abbreviation-display overrides part-abbreviation!
425         partdisplay = el.get_maybe_exist_named_child("part-abbreviation-display")
426         if partdisplay:
427             staff.short_instrument_name = extract_display_text(partdisplay)
428         # TODO: Read in the MIDI device / instrument
429         if staff.short_instrument_name:
430             paper.short_indent = max(paper.short_indent, len(staff.short_instrument_name))
431
432         return staff
433
434     def read_score_group(el):
435         if not isinstance(el, musicxml.Part_group):
436             return
437         group = musicexp.StaffGroup()
438         if hasattr(el, 'number'):
439             id = el.number
440             group.id = id
441             #currentgroups_dict[id] = group
442             #currentgroups.append(id)
443         if el.get_maybe_exist_named_child('group-name'):
444             group.instrument_name = el.get_maybe_exist_named_child('group-name').get_text()
445         if el.get_maybe_exist_named_child('group-abbreviation'):
446             group.short_instrument_name = el.get_maybe_exist_named_child('group-abbreviation').get_text()
447         if el.get_maybe_exist_named_child('group-symbol'):
448             group.symbol = el.get_maybe_exist_named_child('group-symbol').get_text()
449         if el.get_maybe_exist_named_child('group-barline'):
450             group.spanbar = el.get_maybe_exist_named_child('group-barline').get_text()
451         return group
452
453
454     parts_groups = part_list.get_all_children()
455
456     # the start/end group tags are not necessarily ordered correctly and groups
457     # might even overlap, so we can't go through the children sequentially!
458
459     # 1) Replace all Score_part objects by their corresponding Staff objects,
460     #    also collect all group start/stop points into one PartGroupInfo object
461     staves = []
462     group_info = PartGroupInfo()
463     for el in parts_groups:
464         if isinstance(el, musicxml.Score_part):
465             if not group_info.is_empty():
466                 staves.append(group_info)
467                 group_info = PartGroupInfo()
468             staff = read_score_part(el)
469             if staff:
470                 staves.append(staff)
471         elif isinstance(el, musicxml.Part_group):
472             if el.type == "start":
473                 group_info.add_start(el)
474             elif el.type == "stop":
475                 group_info.add_end(el)
476     if not group_info.is_empty():
477         staves.append(group_info)
478
479     # 2) Now, detect the groups:
480     group_starts = []
481     pos = 0
482     while pos < len(staves):
483         el = staves[pos]
484         if isinstance(el, PartGroupInfo):
485             prev_start = 0
486             if len(group_starts) > 0:
487                 prev_start = group_starts[-1]
488             elif len(el.end) > 0: # no group to end here
489                 el.end = {}
490             if len(el.end) > 0: # closes an existing group
491                 ends = el.end.keys()
492                 prev_started = staves[prev_start].start.keys()
493                 grpid = None
494                 intersection = filter(lambda x:x in ends, prev_started)
495                 if len(intersection) > 0:
496                     grpid = intersection[0]
497                 else:
498                     # Close the last started group
499                     grpid = staves[prev_start].start.keys() [0]
500                     # Find the corresponding closing tag and remove it!
501                     j = pos + 1
502                     foundclosing = False
503                     while j < len(staves) and not foundclosing:
504                         if isinstance(staves[j], PartGroupInfo) and staves[j].end.has_key(grpid):
505                             foundclosing = True
506                             del staves[j].end[grpid]
507                             if staves[j].is_empty():
508                                 del staves[j]
509                         j += 1
510                 grpobj = staves[prev_start].start[grpid]
511                 group = read_score_group(grpobj)
512                 # remove the id from both the start and end
513                 if el.end.has_key(grpid):
514                     del el.end[grpid]
515                 del staves[prev_start].start[grpid]
516                 if el.is_empty():
517                     del staves[pos]
518                 # replace the staves with the whole group
519                 for j in staves[(prev_start + 1):pos]:
520                     group.append_staff(j)
521                 del staves[(prev_start + 1):pos]
522                 staves.insert(prev_start + 1, group)
523                 # reset pos so that we continue at the correct position
524                 pos = prev_start
525                 # remove an empty start group
526                 if staves[prev_start].is_empty():
527                     del staves[prev_start]
528                     group_starts.remove(prev_start)
529                     pos -= 1
530             elif len(el.start) > 0: # starts new part groups
531                 group_starts.append(pos)
532         pos += 1
533
534     for i in staves:
535         structure.append_staff(i)
536     return score
537
538
539
540
541 def musicxml_partial_to_lily(partial_len):
542     if partial_len > 0:
543         p = musicexp.Partial()
544         p.partial = musicxml2ly_conversion.rational_to_lily_duration(partial_len)
545         return p
546     else:
547         return None
548
549 # Detect repeats and alternative endings in the chord event list(music_list)
550 # and convert them to the corresponding musicexp objects, containing nested
551 # music
552 def group_repeats(music_list):
553     repeat_replaced = True
554     music_start = 0
555     i = 0
556     # Walk through the list of expressions, looking for repeat structure
557     #(repeat start/end, corresponding endings). If we find one, try to find the
558     # last event of the repeat, replace the whole structure and start over again.
559     # For nested repeats, as soon as we encounter another starting repeat bar,
560     # treat that one first, and start over for the outer repeat.
561     while repeat_replaced and i < 100:
562         i += 1
563         repeat_start = -1  # position of repeat start / end
564         repeat_end = -1 # position of repeat start / end
565         repeat_times = 0
566         ending_start = -1 # position of current ending start
567         endings = [] # list of already finished endings
568         pos = 0
569         last = len(music_list) - 1
570         repeat_replaced = False
571         final_marker = 0
572         while pos < len(music_list) and not repeat_replaced:
573             e = music_list[pos]
574             repeat_finished = False
575             if isinstance(e, musicxml2ly_conversion.RepeatMarker):
576                 if not repeat_times and e.times:
577                     repeat_times = e.times
578                 if e.direction == -1:
579                     if repeat_end >= 0:
580                         repeat_finished = True
581                     else:
582                         repeat_start = pos
583                         repeat_end = -1
584                         ending_start = -1
585                         endings = []
586                 elif e.direction == 1:
587                     if repeat_start < 0:
588                         repeat_start = 0
589                     if repeat_end < 0:
590                         repeat_end = pos
591                     final_marker = pos
592             elif isinstance(e, musicxml2ly_conversion.EndingMarker):
593                 if e.direction == -1:
594                     if repeat_start < 0:
595                         repeat_start = 0
596                     if repeat_end < 0:
597                         repeat_end = pos
598                     ending_start = pos
599                 elif e.direction == 1:
600                     if ending_start < 0:
601                         ending_start = 0
602                     endings.append([ending_start, pos])
603                     ending_start = -1
604                     final_marker = pos
605             elif not isinstance(e, musicexp.BarLine):
606                 # As soon as we encounter an element when repeat start and end
607                 # is set and we are not inside an alternative ending,
608                 # this whole repeat structure is finished => replace it
609                 if repeat_start >= 0 and repeat_end > 0 and ending_start < 0:
610                     repeat_finished = True
611
612             # Finish off all repeats without explicit ending bar(e.g. when
613             # we convert only one page of a multi-page score with repeats)
614             if pos == last and repeat_start >= 0:
615                 repeat_finished = True
616                 final_marker = pos
617                 if repeat_end < 0:
618                     repeat_end = pos
619                 if ending_start >= 0:
620                     endings.append([ending_start, pos])
621                     ending_start = -1
622
623             if repeat_finished:
624                 # We found the whole structure replace it!
625                 r = musicexp.RepeatedMusic()
626                 if repeat_times <= 0:
627                     repeat_times = 2
628                 r.repeat_count = repeat_times
629                 # don't erase the first element for "implicit" repeats(i.e. no
630                 # starting repeat bars at the very beginning)
631                 start = repeat_start + 1
632                 if repeat_start == music_start:
633                     start = music_start
634                 r.set_music(music_list[start:repeat_end])
635                 for(start, end) in endings:
636                     s = musicexp.SequentialMusic()
637                     s.elements = music_list[start + 1:end]
638                     r.add_ending(s)
639                 del music_list[repeat_start:final_marker + 1]
640                 music_list.insert(repeat_start, r)
641                 repeat_replaced = True
642             pos += 1
643         # TODO: Implement repeats until the end without explicit ending bar
644     return music_list
645
646
647 # Extract the settings for tuplets from the <notations><tuplet> and the
648 # <time-modification> elements of the note:
649 def musicxml_tuplet_to_lily(tuplet_elt, time_modification):
650     tsm = musicexp.TimeScaledMusic()
651     fraction =(1, 1)
652     if time_modification:
653         fraction = time_modification.get_fraction()
654     tsm.numerator = fraction[0]
655     tsm.denominator = fraction[1]
656
657
658     normal_type = tuplet_elt.get_normal_type()
659     if not normal_type and time_modification:
660         normal_type = time_modification.get_normal_type()
661     if not normal_type and time_modification:
662         note = time_modification.get_parent()
663         if note:
664             normal_type = note.get_duration_info()
665     if normal_type:
666         normal_note = musicexp.Duration()
667         (normal_note.duration_log, normal_note.dots) = normal_type
668         tsm.normal_type = normal_note
669
670     actual_type = tuplet_elt.get_actual_type()
671     if actual_type:
672         actual_note = musicexp.Duration()
673         (actual_note.duration_log, actual_note.dots) = actual_type
674         tsm.actual_type = actual_note
675
676     # Obtain non-default nrs of notes from the tuplet object!
677     tsm.display_numerator = tuplet_elt.get_normal_nr()
678     tsm.display_denominator = tuplet_elt.get_actual_nr()
679
680
681     if hasattr(tuplet_elt, 'bracket') and tuplet_elt.bracket == "no":
682         tsm.display_bracket = None
683     elif hasattr(tuplet_elt, 'line-shape') and getattr(tuplet_elt, 'line-shape') == "curved":
684         tsm.display_bracket = "curved"
685     else:
686         tsm.display_bracket = "bracket"
687
688     display_values = {"none": None, "actual": "actual", "both": "both"}
689     if hasattr(tuplet_elt, "show-number"):
690         tsm.display_number = display_values.get(getattr(tuplet_elt, "show-number"), "actual")
691
692     if hasattr(tuplet_elt, "show-type"):
693         tsm.display_type = display_values.get(getattr(tuplet_elt, "show-type"), None)
694
695     return tsm
696
697
698 def group_tuplets(music_list, events):
699
700
701     """Collect Musics from
702     MUSIC_LIST demarcated by EVENTS_LIST in TimeScaledMusic objects.
703     """
704
705     indices = []
706     brackets = {}
707
708     j = 0
709     for(ev_chord, tuplet_elt, time_modification) in events:
710         while(j < len(music_list)):
711             if music_list[j] == ev_chord:
712                 break
713             j += 1
714         nr = 0
715         if hasattr(tuplet_elt, 'number'):
716             nr = getattr(tuplet_elt, 'number')
717         if tuplet_elt.type == 'start':
718             tuplet_object = musicxml_tuplet_to_lily(tuplet_elt, time_modification)
719             tuplet_info = [j, None, tuplet_object]
720             indices.append(tuplet_info)
721             brackets[nr] = tuplet_info
722         elif tuplet_elt.type == 'stop':
723             bracket_info = brackets.get(nr, None)
724             if bracket_info:
725                 bracket_info[1] = j # Set the ending position to j
726                 del brackets[nr]
727
728     new_list = []
729     last = 0
730     for(i1, i2, tsm) in indices:
731         if i1 > i2:
732             continue
733
734         new_list.extend(music_list[last:i1])
735         seq = musicexp.SequentialMusic()
736         last = i2 + 1
737
738         # At this point music_list[i1:last] encompasses all the notes of the
739         # tuplet. There might be dynamics following this range, however, which
740         # apply to the last note of the tuplet. Advance last to include them
741         # in the range.
742         while last < len(music_list) and isinstance(music_list[last], musicexp.DynamicsEvent):
743             last += 1
744
745         seq.elements = music_list[i1:last]
746
747         tsm.element = seq
748
749         new_list.append(tsm)
750         #TODO: Handle nested tuplets!!!!
751
752     new_list.extend(music_list[last:])
753     return new_list
754
755
756 def musicxml_clef_to_lily(attributes):
757     change = musicexp.ClefChange()
758     (change.type, change.position, change.octave) = attributes.get_clef_information()
759     return change
760
761 def musicxml_time_to_lily(attributes):
762     change = musicexp.TimeSignatureChange()
763     # time signature function
764     if hasattr(options, 'shift_meter') and options.shift_meter:
765         tmp_meter = options.shift_meter.split("/",1)
766         sig = [int(tmp_meter[0]), int(tmp_meter[1])]
767         change.originalFractions = attributes.get_time_signature()
768     else:
769         sig = attributes.get_time_signature()
770     if not sig:
771         return None
772     change.fractions = sig
773
774     time_elm = attributes.get_maybe_exist_named_child('time')
775     if time_elm and hasattr(time_elm, 'symbol'):
776         change.style = { 'single-number': "'single-digit",
777                          'cut': None,
778                          'common': None,
779                          'normal': "'()"}.get(time_elm.symbol, "'()")
780     else:
781         change.style = "'()"
782
783     # TODO: Handle senza-misura measures
784     # TODO: Handle hidden time signatures(print-object="no")
785     # TODO: What shall we do if the symbol clashes with the sig? e.g. "cut"
786     #       with 3/8 or "single-number" with(2+3)/8 or 3/8+2/4?
787     return change
788
789 def musicxml_key_to_lily(attributes):
790     key_sig = attributes.get_key_signature()
791     if not key_sig or not(isinstance(key_sig, list) or isinstance(key_sig, tuple)):
792         ly.warning(_("Unable to extract key signature!"))
793         return None
794
795     change = musicexp.KeySignatureChange()
796
797     if len(key_sig) == 2 and not isinstance(key_sig[0], list):
798         # standard key signature,(fifths, mode)
799         (fifths, mode) = key_sig
800         change.mode = mode
801
802         start_pitch = musicexp.Pitch()
803         start_pitch.octave = 0
804         try:
805             (n, a) = {
806                 'major'     : (0, 0),
807                 'minor'     : (5, 0),
808                 'ionian'    : (0, 0),
809                 'dorian'    : (1, 0),
810                 'phrygian'  : (2, 0),
811                 'lydian'    : (3, 0),
812                 'mixolydian': (4, 0),
813                 'aeolian'   : (5, 0),
814                 'locrian'   : (6, 0),
815                 }[mode]
816             start_pitch.step = n
817             start_pitch.alteration = a
818         except  KeyError:
819             ly.warning(_("unknown mode %s, expecting 'major' or 'minor' "
820                 "or a church mode!") % mode)
821
822         fifth = musicexp.Pitch()
823         fifth.step = 4
824         if fifths < 0:
825             fifths *= -1
826             fifth.step *= -1
827             fifth.normalize()
828         for x in range(fifths):
829             start_pitch = start_pitch.transposed(fifth)
830         change.tonic = start_pitch
831
832     else:
833         # Non-standard key signature of the form [[step,alter<,octave>],...]
834         # MusicXML contains C,D,E,F,G,A,B as steps, lily uses 0-7, so convert
835         alterations = []
836         for k in key_sig:
837             k[0] = musicxml2ly_conversion.musicxml_step_to_lily(k[0])
838             alterations.append(k)
839         change.non_standard_alterations = alterations
840     return change
841
842 def musicxml_transpose_to_lily(attributes):
843     transpose = attributes.get_transposition()
844     if not transpose:
845         return None
846
847     shift = musicexp.Pitch()
848     octave_change = transpose.get_maybe_exist_named_child('octave-change')
849     if octave_change:
850         shift.octave = string.atoi(octave_change.get_text())
851     chromatic_shift = string.atoi(transpose.get_named_child('chromatic').get_text())
852     chromatic_shift_normalized = chromatic_shift % 12;
853     (shift.step, shift.alteration) = [
854         (0, 0), (0, 1), (1, 0), (2, -1), (2, 0),
855         (3, 0), (3, 1), (4, 0), (5, -1), (5, 0),
856         (6, -1), (6, 0)][chromatic_shift_normalized];
857
858     shift.octave += (chromatic_shift - chromatic_shift_normalized) / 12
859
860     diatonic = transpose.get_maybe_exist_named_child('diatonic')
861     if diatonic:
862         diatonic_step = string.atoi(diatonic.get_text()) % 7
863         if diatonic_step != shift.step:
864             # We got the alter incorrect!
865             old_semitones = shift.semitones()
866             shift.step = diatonic_step
867             new_semitones = shift.semitones()
868             shift.alteration += old_semitones - new_semitones
869
870     transposition = musicexp.Transposition()
871     transposition.pitch = musicexp.Pitch().transposed(shift)
872     return transposition
873
874 def musicxml_staff_details_to_lily(attributes):
875     details = attributes.get_maybe_exist_named_child('staff-details')
876     if not details:
877         return None
878
879     ## TODO: Handle staff-type, staff-lines, staff-tuning, capo, staff-size
880     ret = []
881
882     stafflines = details.get_maybe_exist_named_child('staff-lines')
883     if stafflines:
884         lines = string.atoi(stafflines.get_text());
885         lines_event = musicexp.StaffLinesEvent(lines);
886         ret.append(lines_event);
887
888     return ret;
889
890
891 def musicxml_attributes_to_lily(attrs):
892     elts = []
893     attr_dispatch = {
894         'clef': musicxml_clef_to_lily,
895         'time': musicxml_time_to_lily,
896         'key': musicxml_key_to_lily,
897         'transpose': musicxml_transpose_to_lily,
898         'staff-details': musicxml_staff_details_to_lily,
899     }
900     for (k, func) in attr_dispatch.items():
901         children = attrs.get_named_children(k)
902         if children:
903             ev = func(attrs)
904             if isinstance(ev, list):
905               for e in ev:
906                 elts.append(e)
907             elif ev:
908                 elts.append(ev)
909
910     return elts
911
912 def extract_display_text(el):
913     child = el.get_maybe_exist_named_child("display-text")
914     if child:
915         return child.get_text()
916     else:
917         return False
918
919
920 def musicxml_print_to_lily(el):
921     # TODO: Implement other print attributes
922     #  <!ELEMENT print (page-layout?, system-layout?, staff-layout*,
923     #          measure-layout?, measure-numbering?, part-name-display?,
924     #          part-abbreviation-display?)>
925     #  <!ATTLIST print
926     #      staff-spacing %tenths; #IMPLIED
927     #      new-system %yes-no; #IMPLIED
928     #      new-page %yes-no-number; #IMPLIED
929     #      blank-page NMTOKEN #IMPLIED
930     #      page-number CDATA #IMPLIED
931     #  >
932     elts = []
933     if (hasattr(el, "new-system") and conversion_settings.convert_system_breaks):
934         val = getattr(el, "new-system")
935         if (val == "yes"):
936             elts.append(musicexp.Break("break"))
937     if (hasattr(el, "new-page") and conversion_settings.convert_page_breaks):
938         val = getattr(el, "new-page")
939         if (val == "yes"):
940             elts.append(musicexp.Break("pageBreak"))
941     child = el.get_maybe_exist_named_child("part-name-display")
942     if child:
943         elts.append(musicexp.SetEvent("Staff.instrumentName",
944                                         "\"%s\"" % extract_display_text(child)))
945     child = el.get_maybe_exist_named_child("part-abbreviation-display")
946     if child:
947         elts.append(musicexp.SetEvent("Staff.shortInstrumentName",
948                                         "\"%s\"" % extract_display_text(child)))
949     return elts
950
951
952
953
954 spanner_event_dict = {
955     'beam' : musicexp.BeamEvent,
956     'dashes' : musicexp.TextSpannerEvent,
957     'bracket' : musicexp.BracketSpannerEvent,
958     'glissando' : musicexp.GlissandoEvent,
959     'octave-shift' : musicexp.OctaveShiftEvent,
960     'pedal' : musicexp.PedalEvent,
961     'slide' : musicexp.GlissandoEvent,
962     'slur' : musicexp.SlurEvent,
963     'wavy-line' : musicexp.TextSpannerEvent,
964     'wedge' : musicexp.HairpinEvent
965 }
966 spanner_type_dict = {
967     'start':-1,
968     'begin':-1,
969     'crescendo':-1,
970     'decreschendo':-1,
971     'diminuendo':-1,
972     'continue': 0,
973     'change': 0,
974     'up':-1,
975     'down':-1,
976     'stop': 1,
977     'end' : 1
978 }
979
980 def musicxml_spanner_to_lily_event(mxl_event):
981     ev = None
982
983     name = mxl_event.get_name()
984     func = spanner_event_dict.get(name)
985     if func:
986         ev = func()
987     else:
988         ly.warning(_('unknown span event %s') % mxl_event)
989
990     if name == "wavy-line":
991         ev.style=OrnamenthasWhat(mxl_event)
992
993     type = mxl_event.get_type()
994     span_direction = spanner_type_dict.get(type)
995     # really check for None, because some types will be translated to 0, which
996     # would otherwise also lead to the unknown span warning
997     if span_direction != None:
998         ev.span_direction = span_direction
999     else:
1000         ly.warning(_('unknown span type %s for %s') %(type, name))
1001
1002     ev.set_span_type(type)
1003     ev.line_type = getattr(mxl_event, 'line-type', 'solid')
1004
1005     # assign the size, which is used for octave-shift, etc.
1006     ev.size = mxl_event.get_size()
1007
1008     return ev
1009
1010 def musicxml_direction_to_indicator(direction):
1011     return { "above": 1, "upright": 1, "up": 1, "below":-1, "downright":-1, "down":-1, "inverted":-1 }.get(direction, 0)
1012
1013 def musicxml_fermata_to_lily_event(mxl_event):
1014
1015     ev = musicexp.ArticulationEvent()
1016     txt = mxl_event.get_text()
1017
1018     # The contents of the element defined the shape, possible are normal, angled and square
1019     ev.type = { "angled": "shortfermata", "square": "longfermata" }.get(txt, "fermata")
1020     fermata_types= { "angled": "shortfermata",
1021                      "square": "longfermata" }
1022
1023     # MusicXML fermata types can be specified in two different ways:
1024     # 1. <fermata>angled</fermata> and
1025     # 2. <fermata type="angled"/> -- both need to be handled.
1026     if hasattr(mxl_event, 'type'):
1027         fermata_type = fermata_types.get(mxl_event.type, 'fermata')
1028     else:
1029         fermata_type = fermata_types.get(mxl_event.get_text(), 'fermata')
1030
1031     ev.type = fermata_type
1032
1033     if hasattr(mxl_event, 'type'):
1034         dir = musicxml_direction_to_indicator(mxl_event.type)
1035         if dir and options.convert_directions:
1036             ev.force_direction = dir
1037     return ev
1038
1039 def musicxml_arpeggiate_to_lily_event(mxl_event):
1040     ev = musicexp.ArpeggioEvent()
1041     ev.direction = musicxml_direction_to_indicator(getattr(mxl_event, 'direction', None))
1042     return ev
1043
1044 def musicxml_nonarpeggiate_to_lily_event(mxl_event):
1045     ev = musicexp.ArpeggioEvent()
1046     ev.non_arpeggiate = True
1047     ev.direction = musicxml_direction_to_indicator(getattr(mxl_event, 'direction', None))
1048     return ev
1049
1050 def musicxml_tremolo_to_lily_event(mxl_event):
1051     ev = musicexp.TremoloEvent()
1052     txt = mxl_event.get_text()
1053     if txt:
1054       ev.strokes = txt
1055     else:
1056       # This is supposed to be a default for empty tremolo elements
1057       # TODO: Add empty tremolo element to test cases in tremolo.xml
1058       # TODO: Test empty tremolo element
1059       # TODO: Consideration: Is 3 really a reasonable default?
1060       ev.strokes = "3"
1061     return ev
1062
1063 def musicxml_falloff_to_lily_event(mxl_event):
1064     ev = musicexp.BendEvent()
1065     ev.alter = -4
1066     return ev
1067
1068 def musicxml_doit_to_lily_event(mxl_event):
1069     ev = musicexp.BendEvent()
1070     ev.alter = 4
1071     return ev
1072
1073 def musicxml_bend_to_lily_event(mxl_event):
1074     ev = musicexp.BendEvent()
1075     ev.alter = mxl_event.bend_alter()
1076     return ev
1077
1078 def musicxml_caesura_to_lily_event(mxl_event):
1079     ev = musicexp.MarkupEvent()
1080     # FIXME: default to straight or curved caesura?
1081     ev.contents = "\\musicglyph #\"scripts.caesura.straight\""
1082     ev.force_direction = 1
1083     return ev
1084
1085 def musicxml_fingering_event(mxl_event):
1086     ev = musicexp.ShortArticulationEvent()
1087     ev.type = mxl_event.get_text()
1088     return ev
1089
1090 def musicxml_string_event(mxl_event):
1091     ev = musicexp.NoDirectionArticulationEvent()
1092     ev.type = mxl_event.get_text()
1093     return ev
1094
1095 def musicxml_accidental_mark(mxl_event):
1096     ev = musicexp.MarkupEvent()
1097     contents = { "sharp": "\\sharp",
1098       "natural": "\\natural",
1099       "flat": "\\flat",
1100       "double-sharp": "\\doublesharp",
1101       "sharp-sharp": "\\sharp\\sharp",
1102       "flat-flat": "\\flat\\flat",
1103       "flat-flat": "\\doubleflat",
1104       "natural-sharp": "\\natural\\sharp",
1105       "natural-flat": "\\natural\\flat",
1106       "quarter-flat": "\\semiflat",
1107       "quarter-sharp": "\\semisharp",
1108       "three-quarters-flat": "\\sesquiflat",
1109       "three-quarters-sharp": "\\sesquisharp",
1110     }.get(mxl_event.get_text())
1111     if contents:
1112         ev.contents = contents
1113         return ev
1114     else:
1115         return None
1116
1117 # translate articulations, ornaments and other notations into ArticulationEvents
1118 # possible values:
1119 #   -) string  (ArticulationEvent with that name)
1120 #   -) function (function(mxl_event) needs to return a full ArticulationEvent-derived object
1121 #   -) (class, name)  (like string, only that a different class than ArticulationEvent is used)
1122 # TODO: Some translations are missing!
1123 articulations_dict = {
1124     "accent": (musicexp.ShortArticulationEvent, ">"), # or "accent"
1125     "accidental-mark": musicxml_accidental_mark,
1126     "bend": musicxml_bend_to_lily_event,
1127     "breath-mark": (musicexp.NoDirectionArticulationEvent, "breathe"),
1128     "caesura": musicxml_caesura_to_lily_event,
1129     #"delayed-turn": "?",
1130     "detached-legato": (musicexp.ShortArticulationEvent, "_"), # or "portato"
1131     "doit": musicxml_doit_to_lily_event,
1132     #"double-tongue": "?",
1133     "down-bow": "downbow",
1134     "falloff": musicxml_falloff_to_lily_event,
1135     "fingering": musicxml_fingering_event,
1136     #"fingernails": "?",
1137     #"fret": "?",
1138     #"hammer-on": "?",
1139     "harmonic": "flageolet",
1140     #"heel": "?",
1141     "inverted-mordent": "prall",
1142     "inverted-turn": "reverseturn",
1143     "mordent": "mordent",
1144     "open-string": "open",
1145     #"plop": "?",
1146     #"pluck": "?",
1147     #"pull-off": "?",
1148     #"schleifer": "?",
1149     #"scoop": "?",
1150     #"shake": "?",
1151     "snap-pizzicato": "snappizzicato",
1152     #"spiccato": "?",
1153     "staccatissimo": (musicexp.ShortArticulationEvent, "!"), # or "staccatissimo"
1154     "staccato": (musicexp.ShortArticulationEvent, "."), # or "staccato"
1155     "stopped": (musicexp.ShortArticulationEvent, "+"), # or "stopped"
1156     #"stress": "?",
1157     "string": musicxml_string_event,
1158     "strong-accent": (musicexp.ShortArticulationEvent, "^"), # or "marcato"
1159     #"tap": "?",
1160     "tenuto": (musicexp.ShortArticulationEvent, "-"), # or "tenuto"
1161     "thumb-position": "thumb",
1162     #"toe": "?",
1163     "turn": "turn",
1164     "tremolo": musicxml_tremolo_to_lily_event,
1165     "trill-mark": "trill",
1166     #"triple-tongue": "?",
1167     #"unstress": "?"
1168     "up-bow": "upbow",
1169     #"wavy-line": "?",
1170 }
1171 articulation_spanners = [ "wavy-line" ]
1172
1173 def OrnamenthasWhat(mxl_event):
1174     wavy = trilly = ignore = start = stop = False
1175     for i in mxl_event._parent._children:
1176             if i._name == "wavy-line": wavy = True
1177             elif i._name == "trill-mark": trilly = True
1178             try:
1179                 if i.type == "continue": ignore = True
1180                 elif i.type == "start": start = True
1181                 elif i.type == "stop": stop = True
1182             except:
1183                 pass
1184     if start == True:
1185         if wavy == True and trilly == False: musicexp.whatOrnament = "wave"
1186         else: musicexp.whatOrnament = "trill"
1187     if ignore == True: return "ignore"
1188     elif stop == True: return "stop"
1189     elif wavy == True and trilly == True: return "trill and wave"
1190     elif wavy == True: return "wave"
1191     elif trilly == True: return "trill"
1192
1193 def OrnamenthasWavyline(mxl_event):
1194     for i in mxl_event._parent._children:
1195             if i._name == "wavy-line": return True
1196     return False
1197
1198
1199 def musicxml_articulation_to_lily_event(mxl_event):
1200     # wavy-line elements are treated as trill spanners, not as articulation ornaments
1201     if mxl_event.get_name() in articulation_spanners:
1202         return musicxml_spanner_to_lily_event(mxl_event)
1203
1204     tmp_tp = articulations_dict.get(mxl_event.get_name())
1205     if OrnamenthasWavyline(mxl_event):
1206         return
1207     if not tmp_tp:
1208         return
1209
1210     if isinstance(tmp_tp, str):
1211         ev = musicexp.ArticulationEvent()
1212         ev.type = tmp_tp
1213     elif isinstance(tmp_tp, tuple):
1214         ev = tmp_tp[0]()
1215         ev.type = tmp_tp[1]
1216     else:
1217         ev = tmp_tp(mxl_event)
1218
1219     # Some articulations use the type attribute, other the placement...
1220     dir = None
1221     if hasattr(mxl_event, 'type') and hasattr(options, 'convert_directions') and options.convert_directions:
1222         dir = musicxml_direction_to_indicator(mxl_event.type)
1223     if hasattr(mxl_event, 'placement') and hasattr(options, 'convert_directions') and options.convert_directions:
1224         dir = musicxml_direction_to_indicator(mxl_event.placement)
1225     if dir:
1226         ev.force_direction = dir
1227     return ev
1228
1229
1230
1231 def musicxml_dynamics_to_lily_event(dynentry):
1232     dynamics_available = (
1233         "ppppp", "pppp", "ppp", "pp", "p", "mp", "mf",
1234         "f", "ff", "fff", "ffff", "fp", "sf", "sff", "sp", "spp", "sfz", "rfz")
1235     dynamicsname = dynentry.get_name()
1236     if dynamicsname == "other-dynamics":
1237         dynamicsname = dynentry.get_text()
1238     if not dynamicsname or dynamicsname == "#text":
1239         return None
1240
1241     if not dynamicsname in dynamics_available:
1242         # Get rid of - in tag names (illegal in ly tags!)
1243         dynamicstext = dynamicsname
1244         dynamicsname = string.replace(dynamicsname, "-", "")
1245         additional_definitions[dynamicsname] = dynamicsname + \
1246               " = #(make-dynamic-script \"" + dynamicstext + "\")"
1247         needed_additional_definitions.append(dynamicsname)
1248     event = musicexp.DynamicsEvent()
1249     event.type = dynamicsname
1250     return event
1251
1252 # Convert single-color two-byte strings to numbers 0.0 - 1.0
1253 def hexcolorval_to_nr(hex_val):
1254     try:
1255         v = int(hex_val, 16)
1256         if v == 255:
1257             v = 256
1258         return v / 256.
1259     except ValueError:
1260         return 0.
1261
1262 def hex_to_color(hex_val):
1263     res = re.match(r'#([0-9a-f][0-9a-f]|)([0-9a-f][0-9a-f])([0-9a-f][0-9a-f])([0-9a-f][0-9a-f])$', hex_val, re.IGNORECASE)
1264     if res:
1265         return map(lambda x: hexcolorval_to_nr(x), res.group(2, 3, 4))
1266     else:
1267         return None
1268
1269 def font_size_number_to_lily_command(size):
1270     d = {
1271         (0, 8): r'\teeny',
1272         (8, 10): r'\tiny',
1273         (10, 12): r'\small',
1274         (12, 16): r'',
1275         (16, 24): r'\large',
1276         (24, float('inf')): r'\huge',
1277     }
1278     result = None
1279     for r in d.keys():
1280         if r[0] <= size < r[1]:
1281             result = d[r]
1282             break
1283     return result
1284
1285 def font_size_word_to_lily_command(size):
1286     font_size_dict = {
1287         "xx-small": '\\teeny',
1288         "x-small": '\\tiny',
1289         "small": '\\small',
1290         "medium": '',
1291         "large": '\\large',
1292         "x-large": '\\huge',
1293         "xx-large": '\\larger\\huge'
1294         }
1295     return font_size_dict.get(size, '')
1296
1297 def get_font_size(size):
1298     try:
1299         size = float(size)
1300         return font_size_number_to_lily_command(size)
1301     except ValueError:
1302         return font_size_word_to_lily_command(size)
1303
1304 def musicxml_words_to_lily_event(words):
1305     event = musicexp.TextEvent()
1306     text = words.get_text()
1307     text = re.sub('^ *\n? *', '', text) #remove white spaces and line breaks before text
1308     text = re.sub(' *\n? *$', '', text) #remove white spaces and line breaks before text
1309     event.text = text
1310
1311     if hasattr(words, 'default-y') and hasattr(options, 'convert_directions') and options.convert_directions:
1312         offset = getattr(words, 'default-y')
1313         try:
1314             off = string.atoi(offset)
1315             if off > 0:
1316                 event.force_direction = 1
1317             else:
1318                 event.force_direction = -1
1319         except ValueError:
1320             event.force_direction = 0
1321
1322     if hasattr(words, 'font-weight'):
1323         font_weight = { "normal": '', "bold": '\\bold' }.get(getattr(words, 'font-weight'), '')
1324         if font_weight:
1325             event.markup += font_weight
1326
1327     if hasattr(words, 'font-size'):
1328         size = getattr(words, 'font-size')
1329         # font_size = font_size_dict.get(size, '')
1330         font_size = get_font_size(size)
1331         if font_size:
1332             event.markup += font_size
1333
1334     if hasattr(words, 'color'):
1335         color = getattr(words, 'color')
1336         rgb = hex_to_color(color)
1337         if rgb:
1338             event.markup += "\\with-color #(rgb-color %s %s %s)" % (rgb[0], rgb[1], rgb[2])
1339
1340     if hasattr(words, 'font-style'):
1341         font_style = { "italic": '\\italic' }.get(getattr(words, 'font-style'), '')
1342         if font_style:
1343             event.markup += font_style
1344
1345     # TODO: How should I best convert the font-family attribute?
1346
1347     # TODO: How can I represent the underline, overline and line-through
1348     #       attributes in LilyPond? Values of these attributes indicate
1349     #       the number of lines
1350
1351     return event
1352
1353
1354 # convert accordion-registration to lilypond.
1355 # Since lilypond does not have any built-in commands, we need to create
1356 # the markup commands manually and define our own variables.
1357 # Idea was taken from: http://lsr.dsi.unimi.it/LSR/Item?id=194
1358 def musicxml_accordion_to_markup(mxl_event):
1359     commandname = "accReg"
1360     command = ""
1361
1362     high = mxl_event.get_maybe_exist_named_child('accordion-high')
1363     if high:
1364         commandname += "H"
1365         command += """\\combine
1366           \\raise #2.5 \\musicglyph #\"accordion.dot\"
1367           """
1368     middle = mxl_event.get_maybe_exist_named_child('accordion-middle')
1369     if middle:
1370         # By default, use one dot (when no or invalid content is given). The
1371         # MusicXML spec is quiet about this case...
1372         txt = 1
1373         try:
1374           txt = string.atoi(middle.get_text())
1375         except ValueError:
1376             pass
1377         if txt == 3:
1378             commandname += "MMM"
1379             command += """\\combine
1380           \\raise #1.5 \\musicglyph #\"accordion.dot\"
1381           \\combine
1382           \\raise #1.5 \\translate #(cons 1 0) \\musicglyph #\"accordion.dot\"
1383           \\combine
1384           \\raise #1.5 \\translate #(cons -1 0) \\musicglyph #\"accordion.dot\"
1385           """
1386         elif txt == 2:
1387             commandname += "MM"
1388             command += """\\combine
1389           \\raise #1.5 \\translate #(cons 0.5 0) \\musicglyph #\"accordion.dot\"
1390           \\combine
1391           \\raise #1.5 \\translate #(cons -0.5 0) \\musicglyph #\"accordion.dot\"
1392           """
1393         elif not txt <= 0:
1394             commandname += "M"
1395             command += """\\combine
1396           \\raise #1.5 \\musicglyph #\"accordion.dot\"
1397           """
1398     low = mxl_event.get_maybe_exist_named_child('accordion-low')
1399     if low:
1400         commandname += "L"
1401         command += """\\combine
1402           \\raise #0.5 \musicglyph #\"accordion.dot\"
1403           """
1404
1405     command += "\musicglyph #\"accordion.discant\""
1406     command = "\\markup { \\normalsize %s }" % command
1407     # Define the newly built command \accReg[H][MMM][L]
1408     additional_definitions[commandname] = "%s = %s" % (commandname, command)
1409     needed_additional_definitions.append(commandname)
1410     return "\\%s" % commandname
1411
1412 def musicxml_accordion_to_ly(mxl_event):
1413     txt = musicxml_accordion_to_markup(mxl_event)
1414     if txt:
1415         ev = musicexp.MarkEvent(txt)
1416         return ev
1417     return
1418
1419
1420 def musicxml_rehearsal_to_ly_mark(mxl_event):
1421     text = mxl_event.get_text()
1422     if not text:
1423         return
1424     # default is boxed rehearsal marks!
1425     encl = "box"
1426     if hasattr(mxl_event, 'enclosure'):
1427         encl = {"none": None, "square": "box", "circle": "circle" }.get(mxl_event.enclosure, None)
1428     if encl:
1429         text = "\\%s { %s }" % (encl, text)
1430     ev = musicexp.MarkEvent("\\markup { %s }" % text)
1431     return ev
1432
1433 def musicxml_harp_pedals_to_ly(mxl_event):
1434     count = 0
1435     result = "\\harp-pedal #\""
1436     for t in mxl_event.get_named_children('pedal-tuning'):
1437       alter = t.get_named_child('pedal-alter')
1438       if alter:
1439         val = int(alter.get_text().strip())
1440         result += {1: "v", 0: "-", -1: "^"}.get(val, "")
1441       count += 1
1442       if count == 3:
1443         result += "|"
1444     ev = musicexp.MarkupEvent()
1445     ev.contents = result + "\""
1446     return ev
1447
1448 def musicxml_eyeglasses_to_ly(mxl_event):
1449     needed_additional_definitions.append("eyeglasses")
1450     return musicexp.MarkEvent("\\markup { \\eyeglasses }")
1451
1452 def next_non_hash_index(lst, pos):
1453     pos += 1
1454     while pos < len(lst) and isinstance(lst[pos], musicxml.Hash_text):
1455         pos += 1
1456     return pos
1457
1458 def musicxml_metronome_to_ly(mxl_event):
1459     children = mxl_event.get_all_children()
1460     if not children:
1461         return
1462
1463     index = -1
1464     index = next_non_hash_index(children, index)
1465     if isinstance(children[index], musicxml.BeatUnit):
1466         # first form of metronome-mark, using unit and beats/min or other unit
1467         ev = musicexp.TempoMark()
1468         if hasattr(mxl_event, 'parentheses'):
1469             ev.set_parentheses(mxl_event.parentheses == "yes")
1470
1471         d = musicexp.Duration()
1472         d.duration_log = utilities.musicxml_duration_to_log(
1473             children[index].get_text())
1474         index = next_non_hash_index(children, index)
1475         if isinstance(children[index], musicxml.BeatUnitDot):
1476             d.dots = 1
1477             index = next_non_hash_index(children, index)
1478         ev.set_base_duration(d)
1479         if isinstance(children[index], musicxml.BeatUnit):
1480             # Form "note = newnote"
1481             newd = musicexp.Duration()
1482             newd.duration_log = utilities.musicxml_duration_to_log(
1483                 children[index].get_text())
1484             index = next_non_hash_index(children, index)
1485             if isinstance(children[index], musicxml.BeatUnitDot):
1486                 newd.dots = 1
1487                 index = next_non_hash_index(children, index)
1488             ev.set_new_duration(newd)
1489         elif isinstance(children[index], musicxml.PerMinute):
1490             # Form "note = bpm"
1491             try:
1492                 beats = int(children[index].get_text())
1493                 ev.set_beats_per_minute(beats)
1494             except ValueError:
1495                 pass
1496         else:
1497             ly.warning(_("Unknown metronome mark, ignoring"))
1498             return
1499         return ev
1500     else:
1501         #TODO: Implement the other (more complex) way for tempo marks!
1502         ly.warning(_("Metronome marks with complex relations (<metronome-note> in MusicXML) are not yet implemented."))
1503         return
1504
1505 # translate directions into Events, possible values:
1506 #   -) string  (MarkEvent with that command)
1507 #   -) function (function(mxl_event) needs to return a full Event-derived object
1508 #   -) (class, name)  (like string, only that a different class than MarkEvent is used)
1509 directions_dict = {
1510     'accordion-registration' : musicxml_accordion_to_ly,
1511     'coda' : (musicexp.MusicGlyphMarkEvent, "coda"),
1512 #     'damp' : ???
1513 #     'damp-all' : ???
1514     'eyeglasses': musicxml_eyeglasses_to_ly,
1515     'harp-pedals' : musicxml_harp_pedals_to_ly,
1516 #     'image' : ???
1517     'metronome' : musicxml_metronome_to_ly,
1518     'rehearsal' : musicxml_rehearsal_to_ly_mark,
1519 #     'scordatura' : ???
1520     'segno' : (musicexp.MusicGlyphMarkEvent, "segno"),
1521     'words' : musicxml_words_to_lily_event,
1522 }
1523 directions_spanners = [ 'octave-shift', 'pedal', 'wedge', 'dashes', 'bracket' ]
1524
1525 def musicxml_direction_to_lily(n):
1526     # TODO: Handle the <staff> element!
1527     res = []
1528     # placement applies to all children!
1529     dir = None
1530     if hasattr(n, 'placement') and hasattr(options, 'convert_directions') and options.convert_directions:
1531         dir = musicxml_direction_to_indicator(n.placement)
1532     dirtype_children = []
1533     # TODO: The direction-type is used for grouping (e.g. dynamics with text),
1534     #       so we can't simply flatten them out!
1535     for dt in n.get_typed_children(musicxml.DirType):
1536         dirtype_children += dt.get_all_children()
1537
1538     for entry in dirtype_children:
1539         # brackets, dashes, octave shifts. pedal marks, hairpins etc. are spanners:
1540         if entry.get_name() in directions_spanners:
1541             event = musicxml_spanner_to_lily_event(entry)
1542             if event:
1543                 event.force_direction=dir
1544                 res.append(event)
1545             continue
1546
1547         # now treat all the "simple" ones, that can be translated using the dict
1548         ev = None
1549         tmp_tp = directions_dict.get(entry.get_name(), None)
1550         if isinstance(tmp_tp, str): # string means MarkEvent
1551             ev = musicexp.MarkEvent(tmp_tp)
1552         elif isinstance(tmp_tp, tuple): # tuple means (EventClass, "text")
1553             ev = tmp_tp[0](tmp_tp[1])
1554         elif tmp_tp:
1555             ev = tmp_tp(entry)
1556         if ev:
1557             # TODO: set the correct direction! Unfortunately, \mark in ly does
1558             #       not seem to support directions!
1559             ev.force_direction = dir
1560             res.append(ev)
1561             continue
1562
1563         if entry.get_name() == "dynamics":
1564             for dynentry in entry.get_all_children():
1565                 ev = musicxml_dynamics_to_lily_event(dynentry)
1566                 if ev:
1567                     ev.force_direction = dir
1568                     res.append(ev)
1569
1570     return res
1571
1572 notehead_styles_dict = {
1573     'slash': '\'slash',
1574     'triangle': '\'triangle',
1575     'diamond': '\'diamond',
1576     'square': '\'la', # TODO: Proper squared note head
1577     'cross': None, # TODO: + shaped note head
1578     'x': '\'cross',
1579     'circle-x': '\'xcircle',
1580     'inverted triangle': None, # TODO: Implement
1581     'arrow down': None, # TODO: Implement
1582     'arrow up': None, # TODO: Implement
1583     'slashed': None, # TODO: Implement
1584     'back slashed': None, # TODO: Implement
1585     'normal': None,
1586     'cluster': None, # TODO: Implement
1587     'none': '#f',
1588     'do': '\'do',
1589     're': '\'re',
1590     'mi': '\'mi',
1591     'fa': '\'fa',
1592     'so': None,
1593     'la': '\'la',
1594     'ti': '\'ti',
1595     }
1596
1597 def musicxml_chordpitch_to_lily(mxl_cpitch):
1598     r = musicexp.ChordPitch()
1599     r.alteration = mxl_cpitch.get_alteration()
1600     r.step = musicxml2ly_conversion.musicxml_step_to_lily(mxl_cpitch.get_step())
1601     return r
1602
1603 chordkind_dict = {
1604     'major': r'{}:5',
1605     'minor': r'{}:m5',
1606     'augmented': r'{}:aug5',
1607     'diminished': r'{}:dim5',
1608         # Sevenths:
1609     'dominant': r'{}:7',
1610     'dominant-seventh': r'{}:7',
1611     'major-seventh': r'{}:maj7',
1612     'minor-seventh': r'{}:m7',
1613     'diminished-seventh': r'{}:dim7',
1614     'augmented-seventh': r'{}:aug7',
1615     'half-diminished': r'{}:dim5m7',
1616     'major-minor': r'{}:maj7m5',
1617         # Sixths:
1618     'major-sixth': r'{}:6',
1619     'minor-sixth': r'{}:m6',
1620         # Ninths:
1621     'dominant-ninth': r'{}:9',
1622     'major-ninth': r'{}:maj9',
1623     'minor-ninth': r'{}:m9',
1624         # 11ths (usually as the basis for alteration):
1625     'dominant-11th': r'{}:11',
1626     'major-11th': r'{}:maj11',
1627     'minor-11th': r'{}:m11',
1628         # 13ths (usually as the basis for alteration):
1629     'dominant-13th': r'{}:13.11',
1630     'major-13th': r'{}:maj13.11',
1631     'minor-13th': r'{}:m13',
1632         # Suspended:
1633     'suspended-second': r'{}:sus2',
1634     'suspended-fourth': r'{}:sus4',
1635         # Functional sixths:
1636     # TODO
1637     #'Neapolitan': '???',
1638     #'Italian': '???',
1639     #'French': '???',
1640     #'German': '???',
1641         # Other:
1642     #'pedal': '???',(pedal-point bass)
1643     'power': r'\powerChords {}:1.5',
1644     #'Tristan': '???',
1645     'other': r'{}:1',
1646     'none': None,
1647 }
1648
1649 def musicxml_chordkind_to_lily(kind):
1650     res = chordkind_dict.get(kind, None)
1651     # Check for None, since a major chord is converted to ''
1652     if res == None:
1653         ly.warning(_("Unable to convert chord type %s to lilypond.") % kind)
1654     return res
1655
1656
1657 # Global variable for guitar string tunings
1658 string_tunings = None
1659
1660 def musicxml_get_string_tunings(lines):
1661     global string_tunings
1662     if (string_tunings == None):
1663         if not lines:
1664             lines = 6
1665         string_tunings = [musicexp.Pitch()] * lines
1666         for i in range(0, lines):
1667             p = musicexp.Pitch()
1668             p.step = musicxml2ly_conversion.musicxml_step_to_lily(((("E","A","D","G","B")*(lines/5+1))[0:lines])[i])
1669             p.octave = (([-2+int(x%5>1)+2*(x/5) for x in range(0,lines)][0:lines])[i])
1670             p.alteration = 0
1671             p._force_absolute_pitch = True
1672             string_tunings[i] = p
1673         string_tunings = string_tunings[::-1]
1674     return string_tunings[0:lines]
1675
1676 def musicxml_frame_to_lily_event(frame):
1677     ev = musicexp.FretEvent()
1678     ev.strings = frame.get_strings()
1679     ev.frets = frame.get_frets()
1680     #offset = frame.get_first_fret() - 1
1681     #offset = frame.get_first_fret()
1682     barre = []
1683     open_strings = range(1,ev.strings+1)
1684     for fn in frame.get_named_children('frame-note'):
1685         fret = fn.get_fret()
1686         if fret <= 0:
1687             fret = "o"
1688         el = [ fn.get_string(), fret ]
1689         fingering = fn.get_fingering()
1690         if fingering >= 0:
1691             el.append(fingering)
1692         ev.elements.append(el)
1693         open_strings.remove(fn.get_string())
1694         b = fn.get_barre()
1695         if b == 'start':
1696             barre.append(el[0]) # start string
1697             barre.append(el[1]) # fret
1698         elif b == 'stop':
1699             barre.insert(1, el[0]) # end string
1700     for string in open_strings:
1701         ev.elements.append([string, 'x'])
1702     ev.elements.sort()
1703     ev.elements.reverse()
1704     if barre:
1705         ev.barre = barre
1706     return ev
1707
1708 def musicxml_harmony_to_lily(n):
1709     res = []
1710     for f in n.get_named_children('frame'):
1711         ev = musicxml_frame_to_lily_event(f)
1712         if ev:
1713             res.append(ev)
1714     return res
1715
1716 def musicxml_harmony_to_lily_fretboards(n):
1717     res = []
1718     frame = n.get_maybe_exist_named_child('frame')
1719     if frame:
1720         strings = frame.get_strings()
1721         if not strings:
1722             strings = 6
1723         tunings = musicxml_get_string_tunings(strings)
1724         ev = musicexp.FretBoardEvent()
1725         #barre = []
1726         for fn in frame.get_named_children('frame-note'):
1727             fbn = musicexp.FretBoardNote()
1728             string = fn.get_string()
1729             fbn.string = string
1730             fingering = fn.get_fingering()
1731             if fingering >= 0:
1732                 fbn.fingering = fingering
1733             p = tunings[string-1].copy()
1734             p.add_semitones(fn.get_fret())
1735             fbn.pitch = p
1736             ev.append(fbn)
1737         res.append(ev)
1738     return res
1739
1740 def musicxml_harmony_to_lily_chordname(n):
1741     res = []
1742     root = n.get_maybe_exist_named_child('root')
1743     if root:
1744         ev = musicexp.ChordNameEvent()
1745         ev.root = musicxml_chordpitch_to_lily(root)
1746         kind = n.get_maybe_exist_named_child('kind')
1747         if kind:
1748             ev.kind = musicxml_chordkind_to_lily(kind.get_text())
1749             if not ev.kind:
1750                 return res
1751         bass = n.get_maybe_exist_named_child('bass')
1752         if bass:
1753             ev.bass = musicxml_chordpitch_to_lily(bass)
1754         inversion = n.get_maybe_exist_named_child('inversion')
1755         if inversion:
1756             # TODO: LilyPond does not support inversions, does it?
1757
1758             # Mail from Carl Sorensen on lilypond-devel, June 11, 2008:
1759             # 4. LilyPond supports the first inversion in the form of added
1760             # bass notes.  So the first inversion of C major would be c:/g.
1761             # To get the second inversion of C major, you would need to do
1762             # e:6-3-^5 or e:m6-^5.  However, both of these techniques
1763             # require you to know the chord and calculate either the fifth
1764             # pitch (for the first inversion) or the third pitch (for the
1765             # second inversion) so they may not be helpful for musicxml2ly.
1766             inversion_count = string.atoi(inversion.get_text())
1767             if inversion_count == 1:
1768               # TODO: Calculate the bass note for the inversion...
1769               pass
1770             pass
1771         for deg in n.get_named_children('degree'):
1772             d = musicexp.ChordModification()
1773             d.type = deg.get_type()
1774             d.step = deg.get_value()
1775             d.alteration = deg.get_alter()
1776             ev.add_modification(d)
1777         #TODO: convert the user-symbols attribute:
1778             #major: a triangle, like Unicode 25B3
1779             #minor: -, like Unicode 002D
1780             #augmented: +, like Unicode 002B
1781             #diminished: (degree), like Unicode 00B0
1782             #half-diminished: (o with slash), like Unicode 00F8
1783         if ev and ev.root:
1784             res.append(ev)
1785     return res
1786
1787 def musicxml_figured_bass_note_to_lily(n):
1788     res = musicexp.FiguredBassNote()
1789     suffix_dict = { 'sharp' : "+",
1790                     'flat' : "-",
1791                     'natural' : "!",
1792                     'double-sharp' : "++",
1793                     'flat-flat' : "--",
1794                     'sharp-sharp' : "++",
1795                     'slash' : "/" }
1796     prefix = n.get_maybe_exist_named_child('prefix')
1797     if prefix:
1798         res.set_prefix(suffix_dict.get(prefix.get_text(), ""))
1799     fnumber = n.get_maybe_exist_named_child('figure-number')
1800     if fnumber:
1801         res.set_number(fnumber.get_text())
1802     suffix = n.get_maybe_exist_named_child('suffix')
1803     if suffix:
1804         res.set_suffix(suffix_dict.get(suffix.get_text(), ""))
1805     if n.get_maybe_exist_named_child('extend'):
1806         # TODO: Implement extender lines (unfortunately, in lilypond you have
1807         #       to use \set useBassFigureExtenders = ##t, which turns them on
1808         #       globally, while MusicXML has a property for each note...
1809         #       I'm not sure there is a proper way to implement this cleanly
1810         #n.extend
1811         pass
1812     return res
1813
1814
1815
1816 def musicxml_figured_bass_to_lily(n):
1817     if not isinstance(n, musicxml.FiguredBass):
1818         return
1819     res = musicexp.FiguredBassEvent()
1820     for i in n.get_named_children('figure'):
1821         note = musicxml_figured_bass_note_to_lily(i)
1822         if note:
1823             res.append(note)
1824     dur = n.get_maybe_exist_named_child('duration')
1825     if dur:
1826         # apply the duration to res
1827         length = Rational(int(dur.get_text()), n._divisions) * Rational(1, 4)
1828         res.set_real_duration(length)
1829         duration = musicxml2ly_conversion.rational_to_lily_duration(length)
1830         if duration:
1831             res.set_duration(duration)
1832     if hasattr(n, 'parentheses') and n.parentheses == "yes":
1833         res.set_parentheses(True)
1834     return res
1835
1836 instrument_drumtype_dict = {
1837     'Acoustic Snare Drum': 'acousticsnare',
1838     'Side Stick': 'sidestick',
1839     'Open Triangle': 'opentriangle',
1840     'Mute Triangle': 'mutetriangle',
1841     'Tambourine': 'tambourine',
1842     'Bass Drum': 'bassdrum',
1843 }
1844
1845
1846
1847 def musicxml_lyrics_to_text(lyrics, ignoremelismata):
1848     # TODO: Implement text styles for lyrics syllables
1849     continued = False
1850     extended = False
1851     text = ''
1852     for e in lyrics.get_all_children():
1853         if isinstance(e, musicxml.Syllabic):
1854             continued = e.continued()
1855         elif isinstance(e, musicxml.Text):
1856             # We need to convert soft hyphens to -, otherwise the ascii codec as well
1857             # as lilypond will barf on that character
1858             text += string.replace(e.get_text(), u'\xad', '-')
1859         elif isinstance(e, musicxml.Elision):
1860             if text:
1861                 text += " "
1862             continued = False
1863             extended = False
1864         elif isinstance(e, musicxml.Extend):
1865             if text:
1866                 text += " "
1867             extended = True
1868
1869     if text == "-" and continued:
1870         return "--"
1871     elif text == "_" and extended:
1872         return "__"
1873     elif continued and text:
1874         if hasattr(options, 'convert_beaming') and options.convert_beaming:
1875             if (ignoremelismata == "on"):
1876                 return " \set ignoreMelismata = ##t " + utilities.escape_ly_output_string (text)
1877             elif (ignoremelismata == "off"):
1878                 return " " + utilities.escape_ly_output_string(text) + " -- \unset ignoreMelismata"
1879             else:
1880                 return " " + utilities.escape_ly_output_string(text) + " --"
1881         else:
1882             return " " + utilities.escape_ly_output_string(text) + " -- "
1883     elif continued:
1884         return "--"
1885     elif extended and text:
1886         return " " + utilities.escape_ly_output_string(text) + " __"
1887     elif extended:
1888         return "__"
1889     elif text:
1890         return " " + utilities.escape_ly_output_string(text)
1891     else:
1892         return ""
1893
1894 ## TODO
1895 class NegativeSkip:
1896     def __init__(self, here, dest):
1897         self.here = here
1898         self.dest = dest
1899
1900 class LilyPondVoiceBuilder:
1901     def __init__(self):
1902         self.elements = []
1903         self.pending_dynamics = []
1904         self.end_moment = Rational(0)
1905         self.begin_moment = Rational(0)
1906         self.pending_multibar = Rational(0)
1907         self.ignore_skips = False
1908         self.has_relevant_elements = False
1909         self.measure_length = Rational(4, 4)
1910         self.stay_here = False
1911
1912     def _insert_multibar(self):
1913         layout_information.set_context_item('Score', 'skipBars = ##t')
1914         r = musicexp.MultiMeasureRest()
1915         lenfrac = self.measure_length
1916         r.duration = musicxml2ly_conversion.rational_to_lily_duration(lenfrac)
1917         r.duration.factor *= self.pending_multibar / lenfrac
1918         self.elements.append(r)
1919         self.begin_moment = self.end_moment
1920         self.end_moment = self.begin_moment + self.pending_multibar
1921         self.pending_multibar = Rational(0)
1922
1923     def set_measure_length(self, mlen):
1924         if (mlen != self.measure_length) and self.pending_multibar:
1925             self._insert_multibar()
1926         self.measure_length = mlen
1927
1928     def add_multibar_rest(self, duration):
1929         self.pending_multibar += duration
1930
1931     def set_duration(self, duration):
1932         self.end_moment = self.begin_moment + duration
1933     def current_duration(self):
1934         return self.end_moment - self.begin_moment
1935
1936     def add_pending_dynamics(self):
1937         for d in self.pending_dynamics:
1938             self.elements.append(d)
1939         self.pending_dynamics = []
1940
1941     def add_music(self, music, duration, relevant=True):
1942         assert isinstance(music, musicexp.Music)
1943         if self.pending_multibar > Rational(0):
1944             self._insert_multibar()
1945
1946         self.has_relevant_elements = self.has_relevant_elements or relevant
1947
1948         if isinstance(music, musicexp.BarLine):
1949             if self.pending_dynamics:
1950                 for d in self.pending_dynamics:
1951                     if not isinstance(d, (musicexp.SpanEvent, musicexp.DynamicsEvent)):
1952                         index = self.pending_dynamics.index(d)
1953                         dyn = self.pending_dynamics.pop(index)
1954                         self.elements.append(dyn)
1955
1956         self.elements.append(music)
1957         self.begin_moment = self.end_moment
1958         self.set_duration(duration)
1959
1960         # Insert all pending dynamics right after the note/rest:
1961         if isinstance(music, musicexp.ChordEvent) and self.pending_dynamics:
1962             self.add_pending_dynamics()
1963
1964     # Insert some music command that does not affect the position in the measure
1965     def add_command(self, command, relevant=True):
1966         assert isinstance(command, musicexp.Music)
1967         if self.pending_multibar > Rational(0):
1968             self._insert_multibar()
1969         self.has_relevant_elements = self.has_relevant_elements or relevant
1970         self.elements.append(command)
1971     def add_barline(self, barline, relevant=False):
1972         # Insert only if we don't have a barline already
1973         # TODO: Implement proper merging of default barline and custom bar line
1974         has_relevant = self.has_relevant_elements
1975         if (not (self.elements) or
1976             not (isinstance (self.elements[-1], musicexp.BarLine)) or
1977             (self.pending_multibar > Rational(0))):
1978
1979             self.add_music(barline, Rational(0))
1980
1981         self.has_relevant_elements = has_relevant or relevant
1982
1983     def add_partial(self, command):
1984         self.ignore_skips = True
1985         # insert the partial, but restore relevant_elements (partial is not relevant)
1986         relevant = self.has_relevant_elements
1987         self.add_command(command)
1988         self.has_relevant_elements = relevant
1989
1990     def add_dynamics(self, dynamic):
1991         # store the dynamic item(s) until we encounter the next note/rest:
1992         self.pending_dynamics.append(dynamic)
1993
1994     def add_bar_check(self, number):
1995         # re/store has_relevant_elements, so that a barline alone does not
1996         # trigger output for figured bass, chord names
1997         b = musicexp.BarLine()
1998         b.bar_number = number
1999         self.add_barline(b)
2000
2001     def jumpto(self, moment):
2002         if (not self.stay_here):
2003             current_end = self.end_moment + self.pending_multibar
2004             diff = moment - current_end
2005
2006             if diff < Rational(0):
2007                 ly.warning(_('Negative skip %s (from position %s to %s)') %
2008                             (diff, current_end, moment))
2009                 diff = Rational(0)
2010
2011             if diff > Rational(0) and not(self.ignore_skips and moment == 0):
2012                 skip = musicexp.SkipEvent()
2013                 duration_factor = 1
2014                 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)
2015                 duration_dots = 0
2016                 # TODO: Use the time signature for skips, too. Problem: The skip
2017                 #       might not start at a measure boundary!
2018                 if duration_log > 0: # denominator is a power of 2...
2019                     if diff.numerator() == 3:
2020                         duration_log -= 1
2021                         duration_dots = 1
2022                     else:
2023                         duration_factor = Rational(diff.numerator())
2024                 else:
2025                     # for skips of a whole or more, simply use s1*factor
2026                     duration_log = 0
2027                     duration_factor = diff
2028                 skip.duration.duration_log = duration_log
2029                 skip.duration.factor = duration_factor
2030                 skip.duration.dots = duration_dots
2031
2032                 evc = musicexp.ChordEvent()
2033                 evc.elements.append(skip)
2034                 self.add_music(evc, diff, False)
2035
2036             if diff > Rational(0) and moment == 0:
2037                 self.ignore_skips = False
2038
2039     def last_event_chord(self, starting_at):
2040         value = None
2041
2042         # if the position matches, find the last ChordEvent, do not cross a bar line!
2043         at = len(self.elements) - 1
2044         while (at >= 0 and
2045                not isinstance(self.elements[at], musicexp.ChordEvent) and
2046                not isinstance(self.elements[at], musicexp.BarLine)):
2047             at -= 1
2048
2049         if (self.elements
2050             and at >= 0
2051             and isinstance(self.elements[at], musicexp.ChordEvent)
2052             and self.begin_moment == starting_at):
2053             value = self.elements[at]
2054         else:
2055             self.jumpto(starting_at)
2056             value = None
2057         return value
2058
2059     def correct_negative_skip(self, goto):
2060         self.end_moment = goto
2061         self.begin_moment = goto
2062         evc = musicexp.ChordEvent()
2063         self.elements.append(evc)
2064
2065 class VoiceData:
2066     def __init__(self):
2067         self.voicename = None
2068         self.voicedata = None
2069         self.ly_voice = None
2070         self.figured_bass = None
2071         self.chordnames = None
2072         self.fretboards = None
2073         self.lyrics_dict = {}
2074         self.lyrics_order = []
2075
2076 def measure_length_from_attributes(attr, current_measure_length):
2077     len = attr.get_measure_length()
2078     if not len:
2079         len = current_measure_length
2080     return len
2081
2082 def music_xml_voice_name_to_lily_name(part_id, name):
2083     str = "Part%sVoice%s" % (part_id, name)
2084     return musicxml_id_to_lily(str)
2085
2086 def music_xml_lyrics_name_to_lily_name(part_id, name, lyricsnr):
2087     str = music_xml_voice_name_to_lily_name(part_id, name)+("Lyrics%s" % lyricsnr)
2088     return musicxml_id_to_lily(str)
2089
2090 def music_xml_figuredbass_name_to_lily_name(part_id, voicename):
2091     str = music_xml_voice_name_to_lily_name(part_id, voicename)+"FiguredBass"
2092     return musicxml_id_to_lily(str)
2093
2094 def music_xml_chordnames_name_to_lily_name(part_id, voicename):
2095     str = music_xml_voice_name_to_lily_name(part_id, voicename)+"Chords"
2096     return musicxml_id_to_lily(str)
2097
2098 def music_xml_fretboards_name_to_lily_name(part_id, voicename):
2099     str = music_xml_voice_name_to_lily_name(part_id, voicename)+"FretBoards"
2100     return musicxml_id_to_lily(str)
2101
2102 def get_all_lyric_parts_in_voice(voice):
2103     '''
2104     Collect the indexes of all lyric parts in this voice.
2105     In case not all of the current lyric parts are active (a typical case would be
2106     a refrain/chorus), the current implementation inserts \skip-commands in the
2107     inactive parts to keep them in sync.
2108     '''
2109     all_lyric_parts = []
2110     for elem in voice._elements:
2111         lyrics = elem.get_typed_children(musicxml.Lyric)
2112         if lyrics:
2113             for lyric in lyrics:
2114                 index = lyric.number
2115                 if not index in all_lyric_parts:
2116                     all_lyric_parts.append(index)
2117     return all_lyric_parts
2118
2119
2120 def extract_lyrics(voice, lyric_key, lyrics_dict):
2121     curr_number = None
2122     result = []
2123
2124     def is_note(elem):
2125         return isinstance(elem, musicxml.Note)
2126
2127     def is_rest(elem):
2128         return elem.get_typed_children(musicxml.Rest)
2129
2130     def is_note_and_not_rest(elem):
2131         return is_note(elem) and not is_rest(elem)
2132
2133     def get_lyric_elements(note):
2134         return note.get_typed_children(musicxml.Lyric)
2135
2136     def has_lyric_belonging_to_lyric_part(note, lyric_part_id):
2137         lyric_elements = get_lyric_elements(note)
2138         lyric_numbers = [lyric.number for lyric in lyric_elements]
2139         return any([lyric_number == lyric_part_id for lyric_number in lyric_numbers])
2140
2141     for idx, elem in enumerate(voice._elements):
2142         lyrics = get_lyric_elements(elem)
2143         lyric_keys = [lyric.number for lyric in lyrics]
2144         note_has_lyric_belonging_to_lyric_part = lyric_key in lyric_keys
2145         # Current note has lyric with 'number' matching 'lyric_key'.
2146         if note_has_lyric_belonging_to_lyric_part:
2147             for lyric in lyrics:
2148                 if lyric.number == lyric_key:
2149                     text = musicxml_lyrics_to_text(lyric, None)
2150                     result.append(text)
2151         # Note has any lyric.
2152         elif get_lyric_elements(elem) and \
2153              not note_has_lyric_belonging_to_lyric_part:
2154             result.append('\skip1 ')
2155         # Note does not have any lyric attached to it.
2156         elif is_note_and_not_rest(elem):
2157             result.append('\skip1 ')
2158
2159     lyrics_dict[lyric_key].extend(result)
2160
2161 def musicxml_voice_to_lily_voice(voice):
2162     tuplet_events = []
2163     modes_found = {}
2164     lyrics = {}
2165     return_value = VoiceData()
2166     return_value.voicedata = voice
2167
2168     # First pitch needed for relative mode (if selected in command-line options)
2169     first_pitch = None
2170
2171     # Needed for melismata detection (ignore lyrics on those notes!):
2172     inside_slur = False
2173     is_tied = False
2174     is_chord = False
2175     is_beamed = False
2176     ignore_lyrics = False
2177
2178     current_staff = None
2179
2180     pending_figured_bass = []
2181     pending_chordnames = []
2182     pending_fretboards = []
2183
2184     # Make sure that the keys in the dict don't get reordered, since
2185     # we need the correct ordering of the lyrics stanzas! By default,
2186     # a dict will reorder its keys
2187     return_value.lyrics_order = voice.get_lyrics_numbers()
2188     for k in return_value.lyrics_order:
2189         lyrics[k] = []
2190
2191     voice_builder = LilyPondVoiceBuilder()
2192     figured_bass_builder = LilyPondVoiceBuilder()
2193     chordnames_builder = LilyPondVoiceBuilder()
2194     fretboards_builder = LilyPondVoiceBuilder()
2195     current_measure_length = Rational(4, 4)
2196     voice_builder.set_measure_length(current_measure_length)
2197     in_slur = False
2198
2199     all_lyric_parts = set(get_all_lyric_parts_in_voice(voice))
2200     if lyrics.keys():
2201         for number in lyrics.keys():
2202             extracted_lyrics = extract_lyrics(voice, number, lyrics)
2203
2204     for idx, n in enumerate(voice._elements):
2205         tie_started = False
2206         if n.get_name() == 'forward':
2207             continue
2208         staff = n.get_maybe_exist_named_child('staff')
2209         if staff:
2210             staff = staff.get_text()
2211             if current_staff and staff <> current_staff and not n.get_maybe_exist_named_child('chord'):
2212                 voice_builder.add_command(musicexp.StaffChange(staff))
2213             current_staff = staff
2214
2215         if isinstance(n, musicxml.Partial) and n.partial > 0:
2216             a = musicxml_partial_to_lily(n.partial)
2217             if a:
2218                 voice_builder.add_partial(a)
2219                 figured_bass_builder.add_partial(a)
2220                 chordnames_builder.add_partial(a)
2221                 fretboards_builder.add_partial(a)
2222             continue
2223
2224         is_chord = n.get_maybe_exist_named_child('chord')
2225         is_after_grace =(isinstance(n, musicxml.Note) and n.is_after_grace());
2226         if not is_chord and not is_after_grace:
2227             try:
2228                 voice_builder.jumpto(n._when)
2229                 figured_bass_builder.jumpto(n._when)
2230                 chordnames_builder.jumpto(n._when)
2231                 fretboards_builder.jumpto(n._when)
2232             except NegativeSkip, neg:
2233                 voice_builder.correct_negative_skip(n._when)
2234                 figured_bass_builder.correct_negative_skip(n._when)
2235                 chordnames_builder.correct_negative_skip(n._when)
2236                 fretboards_builder.correct_negative_skip(n._when)
2237                 n.message(_("Negative skip found: from %s to %s, difference is %s") % (neg.here, neg.dest, neg.dest - neg.here))
2238
2239         if isinstance(n, musicxml.Barline):
2240             barlines = n.to_lily_object()
2241             for a in barlines:
2242                 if isinstance(a, musicexp.BarLine):
2243                     voice_builder.add_barline(a)
2244                     figured_bass_builder.add_barline(a, False)
2245                     chordnames_builder.add_barline(a, False)
2246                     fretboards_builder.add_barline(a, False)
2247                 elif isinstance(a, musicxml2ly_conversion.RepeatMarker) or isinstance(a, musicxml2ly_conversion.EndingMarker):
2248                     voice_builder.add_command(a)
2249                     figured_bass_builder.add_barline(a, False)
2250                     chordnames_builder.add_barline(a, False)
2251                     fretboards_builder.add_barline(a, False)
2252             continue
2253
2254         if isinstance(n, musicxml.Print):
2255             for a in musicxml_print_to_lily(n):
2256                 voice_builder.add_command(a, False)
2257             continue
2258
2259         # Continue any multimeasure-rests before trying to add bar checks!
2260         # Don't handle new MM rests yet, because for them we want bar checks!
2261         rest = n.get_maybe_exist_typed_child(musicxml.Rest)
2262         if (rest and rest.is_whole_measure()
2263                  and voice_builder.pending_multibar > Rational(0)):
2264             voice_builder.add_multibar_rest(n._duration)
2265             continue
2266
2267         # print a bar check at the beginning of each measure!
2268         if n.is_first() and n._measure_position == Rational(0) and n != voice._elements[0]:
2269             try:
2270                 num = int(n.get_parent().number)
2271             except ValueError:
2272                 num = 0
2273             if num > 0:
2274                 voice_builder.add_bar_check(num)
2275                 figured_bass_builder.add_bar_check(num)
2276                 chordnames_builder.add_bar_check(num)
2277                 fretboards_builder.add_bar_check(num)
2278
2279         if isinstance(n, musicxml.Direction):
2280             # check if Direction already has been converted in another voice.
2281             if n.converted:
2282                 continue
2283             else:
2284                 n.converted = True
2285                 for direction in musicxml_direction_to_lily(n):
2286                     if direction.wait_for_note():
2287                         voice_builder.add_dynamics(direction)
2288                     else:
2289                         voice_builder.add_command(direction)
2290                 continue
2291
2292         # Start any new multimeasure rests
2293         if (rest and rest.is_whole_measure()):
2294             if pending_chordnames:
2295                 chordnames_builder.jumpto(n._when)
2296                 chordnames_builder.stay_here = True
2297             if pending_figured_bass:
2298                 figured_bass_builder.jumpto(n._when)
2299                 figured_bass_builder.stay_here = True
2300             if pending_fretboards:
2301                 fretboards_builder.jumpto(n._when)
2302                 fretboards_builder.stay_here = True
2303             voice_builder.add_multibar_rest(n._duration)
2304             continue
2305
2306         if isinstance(n, musicxml.Harmony):
2307             if (options.fretboards):
2308                 # Makes fretboard diagrams in a separate FretBoards voice
2309                 for a in musicxml_harmony_to_lily_fretboards(n):
2310                     pending_fretboards.append(a)
2311             else:
2312                 # Makes markup fretboard-diagrams inside the voice
2313                 for a in musicxml_harmony_to_lily(n):
2314                     if a.wait_for_note():
2315                         voice_builder.add_dynamics(a)
2316                     else:
2317                         voice_builder.add_command(a)
2318             for a in musicxml_harmony_to_lily_chordname(n):
2319                 pending_chordnames.append(a)
2320             continue
2321
2322         if isinstance(n, musicxml.FiguredBass):
2323             a = musicxml_figured_bass_to_lily(n)
2324             if a:
2325                 pending_figured_bass.append(a)
2326             continue
2327
2328         if isinstance(n, musicxml.Attributes):
2329             for a in musicxml_attributes_to_lily(n):
2330                 voice_builder.add_command(a)
2331             measure_length = measure_length_from_attributes(n, current_measure_length)
2332             if current_measure_length != measure_length:
2333                 current_measure_length = measure_length
2334                 voice_builder.set_measure_length(current_measure_length)
2335             continue
2336
2337         if not n.__class__.__name__ == 'Note':
2338             n.message(_('unexpected %s; expected %s or %s or %s') % (n, 'Note', 'Attributes', 'Barline'))
2339             continue
2340
2341 #        if not hasattr(conversion_settings, 'convert_rest_positions'):
2342 #            conversion_settings.convert_rest_positions = True
2343
2344         main_event = n.to_lily_object(
2345             convert_stem_directions=conversion_settings.convert_stem_directions,
2346             convert_rest_positions=conversion_settings.convert_rest_positions)
2347
2348         if main_event and not first_pitch:
2349             first_pitch = main_event.pitch
2350         # ignore lyrics for notes inside a slur, tie, chord or beam
2351         ignore_lyrics = is_tied or is_chord #or is_beamed or inside_slur
2352
2353         if main_event and hasattr(main_event, 'drum_type') and main_event.drum_type:
2354             modes_found['drummode'] = True
2355
2356         ev_chord = voice_builder.last_event_chord(n._when)
2357         if not ev_chord:
2358             ev_chord = musicexp.ChordEvent()
2359             voice_builder.add_music(ev_chord, n._duration)
2360
2361         # For grace notes:
2362         grace = n.get_maybe_exist_typed_child(musicxml.Grace)
2363         if n.is_grace():
2364             is_after_grace = ev_chord.has_elements() or n.is_after_grace();
2365             is_chord = n.get_maybe_exist_typed_child(musicxml.Chord)
2366
2367             grace_chord = None
2368
2369             # after-graces and other graces use different lists; Depending on
2370             # whether we have a chord or not, obtain either a new ChordEvent or
2371             # the previous one to create a chord
2372             if is_after_grace:
2373                 if ev_chord.after_grace_elements and n.get_maybe_exist_typed_child(musicxml.Chord):
2374                     grace_chord = ev_chord.after_grace_elements.get_last_event_chord()
2375                 if not grace_chord:
2376                     grace_chord = musicexp.ChordEvent()
2377                     ev_chord.append_after_grace(grace_chord)
2378             elif n.is_grace():
2379                 if ev_chord.grace_elements and n.get_maybe_exist_typed_child(musicxml.Chord):
2380                     grace_chord = ev_chord.grace_elements.get_last_event_chord()
2381                 if not grace_chord:
2382                     grace_chord = musicexp.ChordEvent()
2383                     ev_chord.append_grace(grace_chord)
2384
2385             if hasattr(grace, 'slash') and not is_after_grace:
2386                 # TODO: use grace_type = "appoggiatura" for slurred grace notes
2387                 if grace.slash == "yes":
2388                     ev_chord.grace_type = "acciaccatura"
2389             # now that we have inserted the chord into the grace music, insert
2390             # everything into that chord instead of the ev_chord
2391             ev_chord = grace_chord
2392             ev_chord.append(main_event)
2393             ignore_lyrics = True
2394         else:
2395             ev_chord.append(main_event)
2396             # When a note/chord has grace notes (duration==0), the duration of the
2397             # event chord is not yet known, but the event chord was already added
2398             # with duration 0. The following correct this when we hit the real note!
2399             if voice_builder.current_duration() == 0 and n._duration > 0:
2400                 voice_builder.set_duration(n._duration)
2401
2402         # if we have a figured bass, set its voice builder to the correct position
2403         # and insert the pending figures
2404         if pending_figured_bass:
2405             try:
2406                 figured_bass_builder.jumpto(n._when)
2407                 if (figured_bass_builder.stay_here):
2408                     figured_bass_builder.stay_here = False
2409             except NegativeSkip, neg:
2410                 pass
2411             for fb in pending_figured_bass:
2412                 # if a duration is given, use that, otherwise the one of the note
2413                 dur = fb.real_duration
2414                 if not dur:
2415                     dur = ev_chord.get_length()
2416                 if not fb.duration:
2417                     fb.duration = ev_chord.get_duration()
2418                 figured_bass_builder.add_music(fb, dur)
2419             pending_figured_bass = []
2420
2421         if pending_chordnames:
2422             try:
2423                 chordnames_builder.jumpto(n._when)
2424                 if (chordnames_builder.stay_here):
2425                     chordnames_builder.stay_here = False
2426             except NegativeSkip, neg:
2427                 pass
2428             for cn in pending_chordnames:
2429                 # Assign the duration of the EventChord
2430                 cn.duration = ev_chord.get_duration()
2431                 chordnames_builder.add_music(cn, ev_chord.get_length())
2432             pending_chordnames = []
2433
2434         if pending_fretboards:
2435             try:
2436                 fretboards_builder.jumpto(n._when)
2437                 if (fretboards_builder.stay_here):
2438                     fretboards_builder.stay_here = False
2439             except NegativeSkip, neg:
2440                 pass
2441             for fb in pending_fretboards:
2442                 # Assign the duration of the EventChord
2443                 fb.duration = ev_chord.get_duration()
2444                 fretboards_builder.add_music(fb, ev_chord.get_length())
2445             pending_fretboards = []
2446
2447         notations_children = n.get_typed_children(musicxml.Notations)
2448         tuplet_event = None
2449         span_events = []
2450
2451         # The <notation> element can have the following children (+ means implemented, ~ partially, - not):
2452         # +tied | +slur | +tuplet | glissando | slide |
2453         #    ornaments | technical | articulations | dynamics |
2454         #    +fermata | arpeggiate | non-arpeggiate |
2455         #    accidental-mark | other-notation
2456         for notations in notations_children:
2457             for tuplet_event in notations.get_tuplets():
2458                 time_mod = n.get_maybe_exist_typed_child(musicxml.Time_modification)
2459                 tuplet_events.append((ev_chord, tuplet_event, time_mod))
2460
2461             # First, close all open slurs, only then start any new slur
2462             # TODO: Record the number of the open slur to dtermine the correct
2463             #       closing slur!
2464             endslurs = [s for s in notations.get_named_children('slur')
2465                 if s.get_type() in ('stop')]
2466             if endslurs and not inside_slur:
2467                 endslurs[0].message(_('Encountered closing slur, but no slur is open'))
2468             elif endslurs:
2469                 if len(endslurs) > 1:
2470                     endslurs[0].message(_('Cannot have two simultaneous (closing) slurs'))
2471                 # record the slur status for the next note in the loop
2472                 inside_slur = False
2473                 lily_ev = musicxml_spanner_to_lily_event(endslurs[0])
2474                 ev_chord.append(lily_ev)
2475
2476             startslurs = [s for s in notations.get_named_children('slur')
2477                 if s.get_type() in('start')]
2478             if startslurs and inside_slur:
2479                 startslurs[0].message(_('Cannot have a slur inside another slur'))
2480             elif startslurs:
2481                 if len(startslurs) > 1:
2482                     startslurs[0].message(_('Cannot have two simultaneous slurs'))
2483                 # record the slur status for the next note in the loop
2484                 inside_slur = True
2485                 lily_ev = musicxml_spanner_to_lily_event(startslurs[0])
2486                 ev_chord.append(lily_ev)
2487
2488
2489             if not grace:
2490                 mxl_tie = notations.get_tie()
2491                 if mxl_tie and mxl_tie.type == 'start':
2492                     ev_chord.append(musicexp.TieEvent())
2493                     is_tied = True
2494                     tie_started = True
2495                 else:
2496                     is_tied = False
2497
2498             fermatas = notations.get_named_children('fermata')
2499             for a in fermatas:
2500                 ev = musicxml_fermata_to_lily_event(a)
2501                 if ev:
2502                     ev_chord.append(ev)
2503
2504             arpeggiate = notations.get_named_children('arpeggiate')
2505             for a in arpeggiate:
2506                 ev = musicxml_arpeggiate_to_lily_event(a)
2507                 if ev:
2508                     ev_chord.append(ev)
2509
2510             arpeggiate = notations.get_named_children('non-arpeggiate')
2511             for a in arpeggiate:
2512                 ev = musicxml_nonarpeggiate_to_lily_event(a)
2513                 if ev:
2514                     ev_chord.append(ev)
2515
2516             glissandos = notations.get_named_children('glissando')
2517             glissandos += notations.get_named_children('slide')
2518             for a in glissandos:
2519                 ev = musicxml_spanner_to_lily_event(a)
2520                 if ev:
2521                     ev_chord.append(ev)
2522
2523             # accidental-marks are direct children of <notation>!
2524             for a in notations.get_named_children('accidental-mark'):
2525                 ev = musicxml_articulation_to_lily_event(a)
2526                 if ev:
2527                     ev_chord.append(ev)
2528
2529             # Articulations can contain the following child elements:
2530             #         accent | strong-accent | staccato | tenuto |
2531             #         detached-legato | staccatissimo | spiccato |
2532             #         scoop | plop | doit | falloff | breath-mark |
2533             #         caesura | stress | unstress
2534             # Technical can contain the following child elements:
2535             #         up-bow | down-bow | harmonic | open-string |
2536             #         thumb-position | fingering | pluck | double-tongue |
2537             #         triple-tongue | stopped | snap-pizzicato | fret |
2538             #         string | hammer-on | pull-off | bend | tap | heel |
2539             #         toe | fingernails | other-technical
2540             # Ornaments can contain the following child elements:
2541             #         trill-mark | turn | delayed-turn | inverted-turn |
2542             #         shake | wavy-line | mordent | inverted-mordent |
2543             #         schleifer | tremolo | other-ornament, accidental-mark
2544             ornaments = notations.get_named_children('ornaments')
2545             ornaments += notations.get_named_children('articulations')
2546             ornaments += notations.get_named_children('technical')
2547
2548             for a in ornaments:
2549                 for ch in a.get_all_children():
2550                     ev = musicxml_articulation_to_lily_event(ch)
2551                     if ev:
2552                         ev_chord.append(ev)
2553
2554             dynamics = notations.get_named_children('dynamics')
2555             for a in dynamics:
2556                 for ch in a.get_all_children():
2557                     ev = musicxml_dynamics_to_lily_event(ch)
2558                     if ev:
2559                         ev_chord.append(ev)
2560
2561
2562         mxl_beams = [b for b in n.get_named_children('beam')
2563                      if (b.get_type() in('begin', 'end')
2564                          and b.is_primary())]
2565         if mxl_beams and not conversion_settings.ignore_beaming:
2566             beam_ev = musicxml_spanner_to_lily_event(mxl_beams[0])
2567             if beam_ev:
2568                 ev_chord.append(beam_ev)
2569                 if beam_ev.span_direction == -1: # beam and thus melisma starts here
2570                     is_beamed = True
2571                 elif beam_ev.span_direction == 1: # beam and thus melisma ends here
2572                     is_beamed = False
2573
2574         # Assume that a <tie> element only lasts for one note.
2575         # This might not be correct MusicXML interpretation, but works for
2576         # most cases and fixes broken files, which have the end tag missing
2577         if is_tied and not tie_started:
2578             is_tied = False
2579
2580     # force trailing mm rests to be written out.
2581     # voice_builder.add_music (musicexp.ChordEvent(), Rational(0))
2582     if hasattr(options, 'shift_meter') and options.shift_meter:
2583         for event in voice_builder.elements:
2584             if isinstance(event, musicexp.TimeSignatureChange):
2585                 sd = []
2586                 for i in range(0,5):
2587                     sd.append(musicexp.ShiftDurations())
2588                     sd[i].set_shift_durations_parameters(event)
2589                 break;
2590
2591     ly_voice = group_tuplets(voice_builder.elements, tuplet_events)
2592     ly_voice = group_repeats(ly_voice)
2593
2594     seq_music = musicexp.SequentialMusic()
2595
2596     if 'drummode' in modes_found.keys():
2597         ## \key <pitch> barfs in drummode.
2598         ly_voice = [e for e in ly_voice
2599                     if not isinstance(e, musicexp.KeySignatureChange)]
2600
2601     seq_music.elements = ly_voice
2602     for k in lyrics.keys():
2603         return_value.lyrics_dict[k] = musicexp.Lyrics()
2604         return_value.lyrics_dict[k].lyrics_syllables = lyrics[k]
2605
2606
2607     if len(modes_found) > 1:
2608        ly.warning(_('cannot simultaneously have more than one mode: %s') % modes_found.keys())
2609
2610     if hasattr(options, 'shift_meter') and options.shift_meter:
2611         sd[-1].element = seq_music
2612         seq_music = sd[-1]
2613         sd.pop()
2614
2615     if hasattr(options, 'relative') and options.relative:
2616         v = musicexp.RelativeMusic()
2617         v.element = seq_music
2618         v.basepitch = first_pitch
2619         seq_music = v
2620
2621     return_value.ly_voice = seq_music
2622     for mode in modes_found.keys():
2623         v = musicexp.ModeChangingMusicWrapper()
2624         v.element = seq_music
2625         v.mode = mode
2626         return_value.ly_voice = v
2627
2628     # create \figuremode { figured bass elements }
2629     if figured_bass_builder.has_relevant_elements:
2630         fbass_music = musicexp.SequentialMusic()
2631         fbass_music.elements = group_repeats(figured_bass_builder.elements)
2632         v = musicexp.ModeChangingMusicWrapper()
2633         v.mode = 'figuremode'
2634         v.element = fbass_music
2635         if hasattr(options, 'shift_meter') and options.shift_meter:
2636             sd[-1].element = v
2637             v = sd[-1]
2638             sd.pop()
2639         return_value.figured_bass = v
2640
2641     # create \chordmode { chords }
2642     if chordnames_builder.has_relevant_elements:
2643         cname_music = musicexp.SequentialMusic()
2644         cname_music.elements = group_repeats(chordnames_builder.elements)
2645         v = musicexp.ModeChangingMusicWrapper()
2646         v.mode = 'chordmode'
2647         v.element = cname_music
2648         if hasattr(options, 'shift_meter') and options.shift_meter:
2649             sd[-1].element = v
2650             v = sd[-1]
2651             sd.pop()
2652         return_value.chordnames = v
2653
2654     # create diagrams for FretBoards engraver
2655     if fretboards_builder.has_relevant_elements:
2656         fboard_music = musicexp.SequentialMusic()
2657         fboard_music.elements = group_repeats(fretboards_builder.elements)
2658         v = musicexp.MusicWrapper()
2659         v.element = fboard_music
2660         if hasattr(options, 'shift_meter') and options.shift_meter:
2661             sd[-1].element = v
2662             v = sd[-1]
2663             sd.pop()
2664         return_value.fretboards = v
2665
2666     # coll = []
2667     # pending = []
2668
2669     # for elt in return_value.ly_voice.element.elements:
2670     #     if isinstance(elt, musicexp.TimeScaledMusic):
2671     #         print elt.element.elements
2672     #         pending.append(elt)
2673     #     else:
2674     #         coll.append(elt)
2675
2676     # if pending:
2677     #     coll.extend(pending)
2678
2679     # return_value.ly_voice.element.elements = coll
2680
2681     return return_value
2682
2683 def musicxml_id_to_lily(id):
2684     digits = ['Zero', 'One', 'Two', 'Three', 'Four', 'Five',
2685               'Six', 'Seven', 'Eight', 'Nine', 'Ten']
2686
2687     for digit in digits:
2688         d = digits.index(digit)
2689         id = re.sub('%d' % d, digit, id)
2690
2691     id = re.sub('[^a-zA-Z]', 'X', id)
2692     return id
2693
2694 def voices_in_part(part):
2695     """Return a Name -> Voice dictionary for PART"""
2696     part.interpret()
2697     part.extract_voices()
2698     voices = part.get_voices()
2699     part_info = part.get_staff_attributes()
2700
2701     return (voices, part_info)
2702
2703 def voices_in_part_in_parts(parts):
2704     """return a Part -> Name -> Voice dictionary"""
2705     # don't crash if Part doesn't have an id (that's invalid MusicXML,
2706     # but such files are out in the wild!)
2707     dictionary = {}
2708     for p in parts:
2709         voices = voices_in_part(p)
2710         if (hasattr(p, "id")):
2711              dictionary[p.id] = voices
2712         else:
2713              # TODO: extract correct part id from other sources
2714              dictionary[None] = voices
2715     return dictionary;
2716
2717
2718 def get_all_voices(parts):
2719     all_voices = voices_in_part_in_parts(parts)
2720
2721     all_ly_voices = {}
2722     all_ly_staffinfo = {}
2723     for p, (name_voice, staff_info) in all_voices.items():
2724
2725         part_ly_voices = {}
2726         for n, v in name_voice.items():
2727             ly.progress(_("Converting to LilyPond expressions..."), True)
2728             # musicxml_voice_to_lily_voice returns (lily_voice, {nr->lyrics, nr->lyrics})
2729             voice = musicxml_voice_to_lily_voice(v)
2730             part_ly_voices[n] = voice
2731
2732         all_ly_voices[p] = part_ly_voices
2733         all_ly_staffinfo[p] = staff_info
2734
2735     return (all_ly_voices, all_ly_staffinfo)
2736
2737
2738 def option_parser():
2739     p = ly.get_option_parser(usage=_("musicxml2ly [OPTION]... FILE.xml"),
2740                              description=
2741 _("""Convert MusicXML from FILE.xml to LilyPond input.
2742 If the given filename is -, musicxml2ly reads from the command line.
2743 """), add_help_option=False)
2744
2745     p.add_option("-h", "--help",
2746                  action="help",
2747                  help=_("show this help and exit"))
2748
2749     p.version = ('%prog (LilyPond) ' + lilypond_version + '\n\n'
2750 +
2751 _ ("""Copyright (c) 2005--2016 by
2752     Han-Wen Nienhuys <hanwen@xs4all.nl>,
2753     Jan Nieuwenhuizen <janneke@gnu.org> and
2754     Reinhold Kainhofer <reinhold@kainhofer.com>
2755     Patrick L. Schmidt <pls@philomelos.net>
2756 """
2757 +
2758 """
2759 This program is free software.  It is covered by the GNU General Public
2760 License and you are welcome to change it and/or distribute copies of it
2761 under certain conditions.  Invoke as `%s --warranty' for more
2762 information.""") % 'lilypond')
2763
2764     p.add_option("--version",
2765                  action="version",
2766                  help=_ ("show version number and exit"))
2767
2768     p.add_option('-v', '--verbose',
2769                   action="callback",
2770                   callback=ly.handle_loglevel_option,
2771                   callback_args=("DEBUG",),
2772                   help=_ ("be verbose"))
2773
2774     p.add_option('', '--lxml',
2775                   action="store_true",
2776                   default=False,
2777                   dest="use_lxml",
2778                   help=_ ("use lxml.etree; uses less memory and cpu time"))
2779
2780     p.add_option('-z', '--compressed',
2781                   action="store_true",
2782                   dest='compressed',
2783                   default=False,
2784                   help=_ ("input file is a zip-compressed MusicXML file"))
2785
2786     p.add_option('-r', '--relative',
2787                   action="store_true",
2788                   default=True,
2789                   dest="relative",
2790                   help=_ ("convert pitches in relative mode (default)"))
2791
2792     p.add_option('-a', '--absolute',
2793                   action="store_false",
2794                   dest="relative",
2795                   help=_ ("convert pitches in absolute mode"))
2796
2797     p.add_option('-l', '--language',
2798                   metavar=_ ("LANG"),
2799                   action="store",
2800                   help=_ ("use LANG for pitch names, e.g. 'deutsch' for note names in German"))
2801
2802     p.add_option("--loglevel",
2803                   help=_ ("Print log messages according to LOGLEVEL "
2804                           "(NONE, ERROR, WARNING, PROGRESS (default), DEBUG)"),
2805                   metavar=_ ("LOGLEVEL"),
2806                   action='callback',
2807                   callback=ly.handle_loglevel_option,
2808                   type='string')
2809
2810     p.add_option('--nd', '--no-articulation-directions',
2811                   action="store_false",
2812                   default=True,
2813                   dest="convert_directions",
2814                   help=_ ("do not convert directions (^, _ or -) for articulations, dynamics, etc."))
2815
2816     p.add_option('--nrp', '--no-rest-positions',
2817                   action="store_false",
2818                   default=True,
2819                   dest="convert_rest_positions",
2820                   help=_ ("do not convert exact vertical positions of rests"))
2821
2822     p.add_option('--nsb', '--no-system-breaks',
2823                   action="store_false",
2824                   default=True,
2825                   dest="convert_system_breaks",
2826                   help=_ ("ignore system breaks"))
2827
2828     p.add_option('--npb', '--no-page-breaks',
2829                   action="store_false",
2830                   default=True,
2831                   dest="convert_page_breaks",
2832                   help=_ ("ignore page breaks"))
2833
2834     p.add_option('--npm', '--no-page-margins',
2835                   action="store_false",
2836                   default=True,
2837                   dest="convert_page_margins",
2838                   help=_ ("ignore page margins"))
2839
2840     p.add_option('--npl', '--no-page-layout',
2841                   action="store_false",
2842                   default=True,
2843                   dest="convert_page_layout",
2844                   help=_ ("do not convert the exact page layout and breaks (shortcut for \"--nsb --npb --npm\" options)"))
2845
2846     p.add_option('--nsd', '--no-stem-directions',
2847                   action="store_false",
2848                   default=True,
2849                   dest="convert_stem_directions",
2850                   help=_ ("ignore stem directions from MusicXML, use lilypond's automatic stemming instead"))
2851
2852     p.add_option('--nb', '--no-beaming',
2853                   action="store_false",
2854                   default=True,
2855                   dest="convert_beaming",
2856                   help=_ ("do not convert beaming information, use lilypond's automatic beaming instead"))
2857
2858     p.add_option('-o', '--output',
2859                   metavar=_ ("FILE"),
2860                   action="store",
2861                   default=None,
2862                   type='string',
2863                   dest='output_name',
2864                   help=_ ("set output filename to FILE, stdout if -"))
2865
2866     p.add_option('-m', '--midi',
2867                   action="store_true",
2868                   default=False,
2869                   dest="midi",
2870                   help=_("activate midi-block in .ly file"))
2871
2872     # transpose function
2873     p.add_option('--transpose',
2874                   metavar=_ ("TOPITCH"),
2875                   action="store",
2876                   dest="transpose",
2877                   help=_ ("set pitch to transpose by the interval between pitch 'c' and TOPITCH"))
2878
2879     # time signature changing function
2880     p.add_option('--sm', '--shift-meter',
2881                   metavar=_ ("BEATS/BEATTYPE"),
2882                   action="store",
2883                   dest="shift_meter",
2884                   help=_ ("change the length|duration of notes as a function of a given time signature to make the score look faster or slower, (eg. '4/4' or '2/2')"))
2885
2886     # switch tabstaff clef
2887     p.add_option('--tc', '--tab-clef',
2888                   metavar=_ ("TABCLEFNAME"),
2889                   action="store",
2890                   dest="tab_clef",
2891                   help=_ ("switch between two versions of tab clefs (\"tab\" and \"moderntab\")"))
2892
2893     # StringNumber stencil on/off
2894     p.add_option('--sn', '--string-numbers',
2895                   metavar=_ ("t[rue]/f[alse]"),
2896                   action="store",
2897                   dest="string_numbers",
2898                   help=_ ("deactivate string number stencil with --string-numbers f[alse]. Default is t[rue]"))
2899
2900     # StringNumber stencil on/off
2901     p.add_option('--fb', '--fretboards',
2902                   action="store_true",
2903                   default=False,
2904                   dest="fretboards",
2905                   help=_("converts '<frame>' events to a separate FretBoards voice instead of markups"))
2906
2907     p.add_option_group('',
2908                         description=(
2909             _ ("Report bugs via %s")
2910             % 'http://post.gmane.org/post.php'
2911             '?group=gmane.comp.gnu.lilypond.bugs') + '\n')
2912     return p
2913
2914 def print_voice_definitions (printer, part_list, voices):
2915     for part in part_list:
2916         part_id = part.id
2917         nv_dict = voices.get (part_id, {})
2918         for (name, voice) in nv_dict.items ():
2919             k = music_xml_voice_name_to_lily_name (part_id, name)
2920             printer.dump ('%s = ' % k)
2921             voice.ly_voice.print_ly (printer)
2922             printer.newline()
2923             if voice.chordnames:
2924                 cnname = music_xml_chordnames_name_to_lily_name (part_id, name)
2925                 printer.dump ('%s = ' % cnname)
2926                 voice.chordnames.print_ly (printer)
2927                 printer.newline()
2928             for l in voice.lyrics_order:
2929                 lname = music_xml_lyrics_name_to_lily_name (part_id, name, l)
2930                 printer.dump ('%s = ' % lname)
2931                 voice.lyrics_dict[l].print_ly (printer)
2932                 printer.newline()
2933             if voice.figured_bass:
2934                 fbname = music_xml_figuredbass_name_to_lily_name (part_id, name)
2935                 printer.dump ('%s = ' % fbname)
2936                 voice.figured_bass.print_ly (printer)
2937                 printer.newline()
2938             if voice.fretboards:
2939                 fbdname = music_xml_fretboards_name_to_lily_name (part_id, name)
2940                 printer.dump ('%s = ' % fbdname)
2941                 voice.fretboards.print_ly (printer)
2942                 printer.newline()
2943
2944 def uniq_list(l):
2945     return dict([(elt, 1) for elt in l]).keys()
2946
2947 # format the information about the staff in the form
2948 #     [staffid,
2949 #         [
2950 #            [voiceid1, [lyricsid11, lyricsid12,...], figuredbassid1],
2951 #            [voiceid2, [lyricsid21, lyricsid22,...], figuredbassid2],
2952 #            ...
2953 #         ]
2954 #     ]
2955 # raw_voices is of the form [(voicename, lyricsids, havefiguredbass)*]
2956 def format_staff_info(part_id, staff_id, raw_voices):
2957     voices = []
2958     for (v, lyricsids, figured_bass, chordnames, fretboards) in raw_voices:
2959         voice_name = music_xml_voice_name_to_lily_name(part_id, v)
2960         voice_lyrics = [music_xml_lyrics_name_to_lily_name(part_id, v, l)
2961                    for l in lyricsids]
2962         figured_bass_name = ''
2963         if figured_bass:
2964             figured_bass_name = music_xml_figuredbass_name_to_lily_name(part_id, v)
2965         chordnames_name = ''
2966         if chordnames:
2967             chordnames_name = music_xml_chordnames_name_to_lily_name(part_id, v)
2968         fretboards_name = ''
2969         if fretboards:
2970             fretboards_name = music_xml_fretboards_name_to_lily_name(part_id, v)
2971         voices.append([voice_name, voice_lyrics, figured_bass_name, chordnames_name, fretboards_name])
2972     return [staff_id, voices]
2973
2974 def update_score_setup(score_structure, part_list, voices, parts):
2975     for part_definition in part_list:
2976         part_id = part_definition.id
2977         nv_dict = voices.get(part_id)
2978         if not nv_dict:
2979             ly.warning(_('unknown part in part-list: %s') % part_id)
2980             continue
2981
2982         staves = reduce(lambda x, y: x + y,
2983                 [voice.voicedata._staves.keys()
2984                  for voice in nv_dict.values()],
2985                 [])
2986         staves_info = []
2987         if len(staves) > 1:
2988             staves_info = []
2989             staves = uniq_list(staves)
2990             staves.sort()
2991             for s in staves:
2992                 thisstaff_raw_voices = [(voice_name, voice.lyrics_order, voice.figured_bass, voice.chordnames, voice.fretboards)
2993                     for (voice_name, voice) in nv_dict.items()
2994                     if voice.voicedata._start_staff == s]
2995                 staves_info.append(format_staff_info(part_id, s, thisstaff_raw_voices))
2996         else:
2997             thisstaff_raw_voices = [(voice_name, voice.lyrics_order, voice.figured_bass, voice.chordnames, voice.fretboards)
2998                 for (voice_name, voice) in nv_dict.items()]
2999             staves_info.append(format_staff_info(part_id, None, thisstaff_raw_voices))
3000         score_structure.set_part_information(part_id, staves_info)
3001
3002     sounds = []
3003     for part in parts:
3004         for measure in part.get_typed_children(musicxml.Measure):
3005             for sound in measure.get_typed_children(musicxml.Sound):
3006                 sounds.append(sound)
3007             for direction in measure.get_typed_children(musicxml.Direction):
3008                 for sound in direction.get_typed_children(musicxml.Sound):
3009                     sounds.append(sound)
3010
3011     score_structure.set_tempo('100')
3012     if len(sounds) != 0:
3013         for sound in sounds:
3014             if (sound.get_tempo() != None and sound.get_tempo() != ""):
3015                 score_structure.set_tempo(sound.get_tempo())
3016                 break
3017
3018
3019 # Set global values in the \layout block, like auto-beaming etc.
3020 def update_layout_information():
3021     if not conversion_settings.ignore_beaming and layout_information:
3022         layout_information.set_context_item('Score', 'autoBeaming = ##f')
3023     if musicexp.get_string_numbers() == "f":
3024         layout_information.set_context_item('Score', '\\override StringNumber #\'stencil = ##f')
3025
3026 #  \n\t\t\t\t\\override StringNumber #\'stencil = ##f
3027
3028 def print_ly_preamble(printer, filename):
3029     printer.dump_version(lilypond_version)
3030     printer.print_verbatim('% automatically converted by musicxml2ly from ' + filename)
3031     printer.newline()
3032     printer.dump(r'\pointAndClickOff')
3033     printer.newline()
3034     if options.midi:
3035         printer.newline()
3036         printer.dump(r'\include "articulate.ly"')
3037         printer.newline()
3038
3039 def print_ly_additional_definitions(printer, filename=None):
3040     if needed_additional_definitions:
3041         printer.newline()
3042         printer.print_verbatim('%% additional definitions required by the score:')
3043         printer.newline()
3044     for a in set(needed_additional_definitions):
3045         printer.print_verbatim(additional_definitions.get(a, ''))
3046         printer.newline()
3047     printer.newline()
3048
3049 # Read in the tree from the given I/O object (either file or string) and
3050 # demarshall it using the classes from the musicxml.py file
3051 def read_xml(io_object, use_lxml):
3052     if use_lxml:
3053         import lxml.etree
3054         tree = lxml.etree.parse(io_object)
3055         mxl_tree = musicxml.lxml_demarshal_node(tree.getroot())
3056         return mxl_tree
3057     else:
3058         from xml.dom import minidom, Node
3059         doc = minidom.parse(io_object)
3060         node = doc.documentElement
3061         return musicxml.minidom_demarshal_node(node)
3062     return None
3063
3064
3065 def read_musicxml(filename, compressed, use_lxml):
3066     raw_string = None
3067     if compressed:
3068         if filename == "-":
3069              ly.progress(_("Input is compressed, extracting raw MusicXML data from stdin"), True)
3070              # unfortunately, zipfile.ZipFile can't read directly from
3071              # stdin, so copy everything from stdin to a temp file and read
3072              # that. TemporaryFile() will remove the file when it is closed.
3073              tmp = tempfile.TemporaryFile()
3074              sys.stdin = os.fdopen(sys.stdin.fileno(), 'rb', 0) # Make sys.stdin binary
3075              bytes_read = sys.stdin.read(8192)
3076              while bytes_read:
3077                  for b in bytes_read:
3078                      tmp.write(b)
3079                  bytes_read = sys.stdin.read(8192)
3080              z = zipfile.ZipFile(tmp, "r")
3081         else:
3082             ly.progress(_("Input file %s is compressed, extracting raw MusicXML data") % filename, True)
3083             z = zipfile.ZipFile(filename, "r")
3084         container_xml = z.read("META-INF/container.xml")
3085         if not container_xml:
3086             return None
3087         container = read_xml(StringIO.StringIO(container_xml), use_lxml)
3088         if not container:
3089             return None
3090         rootfiles = container.get_maybe_exist_named_child('rootfiles')
3091         if not rootfiles:
3092             return None
3093         rootfile_list = rootfiles.get_named_children('rootfile')
3094         mxml_file = None
3095         if len(rootfile_list) > 0:
3096             mxml_file = getattr(rootfile_list[0], 'full-path', None)
3097         if mxml_file:
3098             raw_string = z.read(mxml_file)
3099
3100     if raw_string:
3101         io_object = StringIO.StringIO(raw_string)
3102     elif filename == "-":
3103         io_object = sys.stdin
3104     else:
3105         io_object = filename
3106
3107     return read_xml(io_object, use_lxml)
3108
3109
3110 def convert(filename, options):
3111     if filename == "-":
3112         ly.progress(_("Reading MusicXML from Standard input ..."), True)
3113     else:
3114         ly.progress(_("Reading MusicXML from %s ...") % filename, True)
3115
3116     tree = read_musicxml(filename, options.compressed, options.use_lxml)
3117     score_information = extract_score_information(tree)
3118     paper_information = extract_paper_information(tree)
3119
3120     parts = tree.get_typed_children(musicxml.Part)
3121     (voices, staff_info) = get_all_voices(parts)
3122
3123     score = None
3124     mxl_pl = tree.get_maybe_exist_typed_child(musicxml.Part_list)
3125     if mxl_pl:
3126         score = extract_score_structure(mxl_pl, staff_info)
3127         part_list = mxl_pl.get_named_children("score-part")
3128
3129     # score information is contained in the <work>, <identification> or <movement-title> tags
3130     update_score_setup(score, part_list, voices, parts)
3131     # After the conversion, update the list of settings for the \layout block
3132     update_layout_information()
3133
3134     if not options.output_name:
3135         options.output_name = os.path.basename(filename)
3136         options.output_name = os.path.splitext(options.output_name)[0]
3137     elif re.match(".*\.ly", options.output_name):
3138         options.output_name = os.path.splitext(options.output_name)[0]
3139
3140
3141     #defs_ly_name = options.output_name + '-defs.ly'
3142     if (options.output_name == "-"):
3143       output_ly_name = 'Standard output'
3144     else:
3145       output_ly_name = options.output_name + '.ly'
3146     ly.progress(_("Output to `%s'") % output_ly_name, True)
3147     printer = musicexp.Output_printer()
3148     #ly.progress(_("Output to `%s'") % defs_ly_name, True)
3149     if (options.output_name == "-"):
3150       printer.set_file(codecs.getwriter("utf-8")(sys.stdout))
3151     else:
3152       printer.set_file(codecs.open(output_ly_name, 'wb', encoding='utf-8'))
3153     print_ly_preamble(printer, filename)
3154     print_ly_additional_definitions(printer, filename)
3155     if score_information:
3156         score_information.print_ly(printer)
3157     if paper_information and conversion_settings.convert_page_layout:
3158         paper_information.print_ly(printer)
3159     if layout_information:
3160         layout_information.print_ly(printer)
3161     print_voice_definitions(printer, part_list, voices)
3162
3163     printer.newline()
3164     printer.dump("% The score definition")
3165     printer.newline()
3166     score.print_ly(printer)
3167     printer.newline()
3168
3169     # Syntax update to current version
3170     if (options.output_name != "-"):
3171         version = os.popen("lilypond --version | head -1 | cut -d' ' -f3").read().strip()
3172         ly.progress(_("Converting to current version (%s) notations ..." % version), True)
3173         os.system("convert-ly -e %s 2> /dev/null" % utilities.escape_ly_output_string(output_ly_name))
3174
3175     return voices
3176
3177 def get_existing_filename_with_extension(filename, ext):
3178     if os.path.exists(filename):
3179         return filename
3180     newfilename = filename + "." + ext
3181     if os.path.exists(newfilename):
3182         return newfilename;
3183     newfilename = filename + ext
3184     if os.path.exists(newfilename):
3185         return newfilename;
3186     return ''
3187
3188
3189 def main():
3190     opt_parser = option_parser()
3191
3192     global options
3193     (options, args) = opt_parser.parse_args()
3194     if not args:
3195         opt_parser.print_usage()
3196         sys.exit(2)
3197
3198     # midi-block option
3199     if options.midi:
3200         musicexp.set_create_midi(options.midi)
3201
3202     # transpose function
3203     if options.transpose:
3204         musicexp.set_transpose(options.transpose)
3205
3206     # tab clef option
3207     if options.tab_clef:
3208         musicexp.set_tab_clef(options.tab_clef)
3209
3210     # string numbers option
3211     if options.string_numbers:
3212         musicexp.set_string_numbers(options.string_numbers)
3213
3214     if options.language:
3215         musicexp.set_pitch_language(options.language)
3216         needed_additional_definitions.append(options.language)
3217         additional_definitions[options.language] = "\\language \"%s\"\n" % options.language
3218
3219     conversion_settings.ignore_beaming = not options.convert_beaming
3220     conversion_settings.convert_page_layout = options.convert_page_layout
3221     if conversion_settings.convert_page_layout:
3222         conversion_settings.convert_system_breaks = options.convert_system_breaks
3223         conversion_settings.convert_page_breaks = options.convert_page_breaks
3224         conversion_settings.convert_page_margins = options.convert_page_margins
3225     else:
3226         conversion_settings.convert_system_breaks = False
3227         conversion_settings.convert_page_breaks = False
3228         conversion_settings.convert_page_margins = False
3229     conversion_settings.convert_stem_directions = options.convert_stem_directions
3230
3231     # Allow the user to leave out the .xml or xml on the filename
3232     basefilename = args[0].decode('utf-8')
3233     if basefilename == "-": # Read from stdin
3234         filename = "-"
3235     else:
3236         filename = get_existing_filename_with_extension(basefilename, "xml")
3237         if not filename:
3238             filename = get_existing_filename_with_extension(basefilename, "mxl")
3239             options.compressed = True
3240     if filename and filename.endswith("mxl"):
3241         options.compressed = True
3242
3243     if filename and (filename == "-" or os.path.exists(filename)):
3244         voices = convert(filename, options)
3245     else:
3246         ly.error(_("Unable to find input file %s") % basefilename)
3247         sys.exit(1)
3248
3249 if __name__ == '__main__':
3250     main()