+ return d
+
+def rational_to_lily_duration (rational_len):
+ d = musicexp.Duration ()
+ d.duration_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)
+ d.factor = Rational (rational_len.numerator ())
+ if d.duration_log < 0:
+ error_message ("Encountered rational duration with denominator %s, "
+ "unable to convert to lilypond duration" %
+ rational_len.denominator ())
+ # TODO: Test the above error message
+ return None
+ else:
+ return d
+
+def musicxml_partial_to_lily (partial_len):
+ if partial_len > 0:
+ p = musicexp.Partial ()
+ p.partial = rational_to_lily_duration (partial_len)
+ return p
+ else:
+ return Null
+
+# Detect repeats and alternative endings in the chord event list (music_list)
+# and convert them to the corresponding musicexp objects, containing nested
+# music
+def group_repeats (music_list):
+ repeat_replaced = True
+ music_start = 0
+ i = 0
+ # Walk through the list of expressions, looking for repeat structure
+ # (repeat start/end, corresponding endings). If we find one, try to find the
+ # last event of the repeat, replace the whole structure and start over again.
+ # For nested repeats, as soon as we encounter another starting repeat bar,
+ # treat that one first, and start over for the outer repeat.
+ while repeat_replaced and i < 100:
+ i += 1
+ repeat_start = -1 # position of repeat start / end
+ repeat_end = -1 # position of repeat start / end
+ repeat_times = 0
+ ending_start = -1 # position of current ending start
+ endings = [] # list of already finished endings
+ pos = 0
+ last = len (music_list) - 1
+ repeat_replaced = False
+ final_marker = 0
+ while pos < len (music_list) and not repeat_replaced:
+ e = music_list[pos]
+ repeat_finished = False
+ if isinstance (e, RepeatMarker):
+ if not repeat_times and e.times:
+ repeat_times = e.times
+ if e.direction == -1:
+ if repeat_end >= 0:
+ repeat_finished = True
+ else:
+ repeat_start = pos
+ repeat_end = -1
+ ending_start = -1
+ endings = []
+ elif e.direction == 1:
+ if repeat_start < 0:
+ repeat_start = 0
+ if repeat_end < 0:
+ repeat_end = pos
+ final_marker = pos
+ elif isinstance (e, EndingMarker):
+ if e.direction == -1:
+ if repeat_start < 0:
+ repeat_start = 0
+ if repeat_end < 0:
+ repeat_end = pos
+ ending_start = pos
+ elif e.direction == 1:
+ if ending_start < 0:
+ ending_start = 0
+ endings.append ([ending_start, pos])
+ ending_start = -1
+ final_marker = pos
+ elif not isinstance (e, musicexp.BarLine):
+ # As soon as we encounter an element when repeat start and end
+ # is set and we are not inside an alternative ending,
+ # this whole repeat structure is finished => replace it
+ if repeat_start >= 0 and repeat_end > 0 and ending_start < 0:
+ repeat_finished = True
+
+ # Finish off all repeats without explicit ending bar (e.g. when
+ # we convert only one page of a multi-page score with repeats)
+ if pos == last and repeat_start >= 0:
+ repeat_finished = True
+ final_marker = pos
+ if repeat_end < 0:
+ repeat_end = pos
+ if ending_start >= 0:
+ endings.append ([ending_start, pos])
+ ending_start = -1
+
+ if repeat_finished:
+ # We found the whole structure replace it!
+ r = musicexp.RepeatedMusic ()
+ if repeat_times <= 0:
+ repeat_times = 2
+ r.repeat_count = repeat_times
+ # don't erase the first element for "implicit" repeats (i.e. no
+ # starting repeat bars at the very beginning)
+ start = repeat_start+1
+ if repeat_start == music_start:
+ start = music_start
+ r.set_music (music_list[start:repeat_end])
+ for (start, end) in endings:
+ s = musicexp.SequentialMusic ()
+ s.elements = music_list[start+1:end]
+ r.add_ending (s)
+ del music_list[repeat_start:final_marker+1]
+ music_list.insert (repeat_start, r)
+ repeat_replaced = True
+ pos += 1
+ # TODO: Implement repeats until the end without explicit ending bar
+ return music_list
+
+