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