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