From 675a1496469450ab5fb59b8c1420faa36a6f55bf Mon Sep 17 00:00:00 2001 From: Endre Oma Date: Sat, 18 Feb 2017 14:18:52 +0100 Subject: [PATCH 01/16] Add prev_bar member to Mediator --- ly/musicxml/ly2xml_mediator.py | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/ly/musicxml/ly2xml_mediator.py b/ly/musicxml/ly2xml_mediator.py index a9ec6951..22858229 100644 --- a/ly/musicxml/ly2xml_mediator.py +++ b/ly/musicxml/ly2xml_mediator.py @@ -41,6 +41,11 @@ class Mediator(): def __init__(self): """ create global lists """ self.score = xml_objs.Score() + + # Previous and active bar, necessary for putting repeat sign at correct position + self.prev_bar = None + self.bar = None + self.sections = [] """ default and initial values """ self.insert_into = None @@ -95,6 +100,7 @@ def new_section(self, name, glob=False): self.insert_into = section self.sections.append(section) self.bar = None + self.prev_bar = None def new_snippet(self, name): name = self.check_name(name) @@ -102,6 +108,7 @@ def new_snippet(self, name): self.insert_into = snippet self.sections.append(snippet) self.bar = None + self.prev_bar = None def new_lyric_section(self, name, voice_id): name = self.check_name(name) @@ -152,6 +159,7 @@ def new_part(self, pid=None, to_part=None, piano=False): self.score.partlist.append(self.part) self.insert_into = self.part self.bar = None + self.prev_bar = None def part_not_empty(self): return self.part and self.part.barlist @@ -312,6 +320,9 @@ def get_first_var(self): def new_bar(self, fill_prev=True): if self.bar and fill_prev: self.bar.list_full = True + + self.prev_bar = self.bar + self.current_attr = xml_objs.BarAttr() self.bar = xml_objs.Bar() self.bar.obj_list = [self.current_attr] From 5fc8171510e8fdb618bc83398f3306555bcf8839 Mon Sep 17 00:00:00 2001 From: Endre Oma Date: Sat, 18 Feb 2017 14:26:53 +0100 Subject: [PATCH 02/16] Add repeat_times to BarAttr --- ly/musicxml/xml_objs.py | 1 + 1 file changed, 1 insertion(+) diff --git a/ly/musicxml/xml_objs.py b/ly/musicxml/xml_objs.py index bad1e057..db3d1dc7 100644 --- a/ly/musicxml/xml_objs.py +++ b/ly/musicxml/xml_objs.py @@ -723,6 +723,7 @@ def __init__(self): self.divs = 0 self.barline = None self.repeat = None + self.repeat_times = None self.staves = 0 self.multiclef = [] self.tempo = None From 2132081904e7e73de6c0b2447e22c704fa9e3e60 Mon Sep 17 00:00:00 2001 From: Endre Oma Date: Sat, 18 Feb 2017 16:17:16 +0100 Subject: [PATCH 03/16] IterateXmlObjs.new_xml_bar_attr(): Add repeat_times when serializing --- ly/musicxml/xml_objs.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ly/musicxml/xml_objs.py b/ly/musicxml/xml_objs.py index db3d1dc7..f290624c 100644 --- a/ly/musicxml/xml_objs.py +++ b/ly/musicxml/xml_objs.py @@ -123,7 +123,7 @@ def new_xml_bar_attr(self, obj): if obj.has_attr(): self.musxml.new_bar_attr(obj.clef, obj.time, obj.key, obj.mode, obj.divs) if obj.repeat: - self.musxml.add_barline(obj.barline, obj.repeat) + self.musxml.add_barline(obj.barline, obj.repeat, obj.repeat_times) elif obj.barline: self.musxml.add_barline(obj.barline) if obj.staves: From c233f6dceb2510929cf02060f94045964b42011a Mon Sep 17 00:00:00 2001 From: Endre Oma Date: Sat, 18 Feb 2017 15:20:07 +0100 Subject: [PATCH 04/16] CreateMusicXML.add_barline() add repeat_times Also, put repeat barline on correct side of bar. --- ly/musicxml/create_musicxml.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/ly/musicxml/create_musicxml.py b/ly/musicxml/create_musicxml.py index 8fb82b7b..c0823d36 100644 --- a/ly/musicxml/create_musicxml.py +++ b/ly/musicxml/create_musicxml.py @@ -528,13 +528,18 @@ def add_clef(self, sign, line, nr=0, oct_ch=0): octchnode = etree.SubElement(clefnode, "clef-octave-change") octchnode.text = str(oct_ch) - def add_barline(self, bl_type, repeat=None): + def add_barline(self, bl_type, repeat=None, repeat_times=None): barnode = etree.SubElement(self.current_bar, "barline", location="right") barstyle = etree.SubElement(barnode, "bar-style") barstyle.text = bl_type if repeat: + if repeat == "forward": + barnode.attrib["location"] = "left" repeatnode = etree.SubElement(barnode, "repeat", direction=repeat) + if repeat_times and repeat_times > 2: + repeatnode.attrib['times'] = str(repeat_times) + def add_backup(self, duration): if duration <= 0: return From 6fc7061f6e0a88664f942b85165f33fe0c5c937e Mon Sep 17 00:00:00 2001 From: Endre Oma Date: Sat, 18 Feb 2017 15:30:01 +0100 Subject: [PATCH 05/16] Mediator.new_repeat(): add repeat_times, use prev bar if curr is empty. Add the end repeat sign to the previous bar if the current has no content. --- ly/musicxml/ly2xml_mediator.py | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/ly/musicxml/ly2xml_mediator.py b/ly/musicxml/ly2xml_mediator.py index 22858229..f3cc3d88 100644 --- a/ly/musicxml/ly2xml_mediator.py +++ b/ly/musicxml/ly2xml_mediator.py @@ -339,13 +339,22 @@ def create_barline(self, bl): self.bar.add(barline) self.new_bar() - def new_repeat(self, rep): + def new_repeat(self, rep, times=None): + barline = xml_objs.BarAttr() barline.set_barline(rep) barline.repeat = rep + barline.repeat_times = times + if self.bar is None: self.new_bar() - self.bar.add(barline) + + if rep == 'backward' and not self.bar.has_music() and self.prev_bar: + # If we are ending a repeat, but the current bar has no music (no rests, too), + # use the previous bar + self.prev_bar.add(barline) + else: + self.bar.add(barline) def new_key(self, key_name, mode): if self.bar is None: From 0226f2dff256355ec2cccf4f9f175e8145bb98b4 Mon Sep 17 00:00:00 2001 From: Endre Oma Date: Sat, 18 Feb 2017 16:10:25 +0100 Subject: [PATCH 06/16] ParseSource: Specify the number of repeats at end of repeat --- ly/musicxml/lymus2musxml.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ly/musicxml/lymus2musxml.py b/ly/musicxml/lymus2musxml.py index 13dfb733..7e979cef 100644 --- a/ly/musicxml/lymus2musxml.py +++ b/ly/musicxml/lymus2musxml.py @@ -591,7 +591,7 @@ def End(self, end): self.grace_seq = False elif end.node.token == '\\repeat': if end.node.specifier() == 'volta': - self.mediator.new_repeat('backward') + self.mediator.new_repeat('backward', end.node.repeat_count()) elif end.node.specifier() == 'tremolo': if self.look_ahead(end.node, ly.music.items.MusicList): self.mediator.set_tremolo(trem_type="stop") From 631d091ab292998163646860ff38733e3e57f6a2 Mon Sep 17 00:00:00 2001 From: Endre Oma Date: Sat, 18 Feb 2017 19:27:42 +0100 Subject: [PATCH 07/16] Implement BarAttr.__eq__(), Bar should not add BarAttr if preexisting --- ly/musicxml/xml_objs.py | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/ly/musicxml/xml_objs.py b/ly/musicxml/xml_objs.py index f290624c..d9922462 100644 --- a/ly/musicxml/xml_objs.py +++ b/ly/musicxml/xml_objs.py @@ -438,7 +438,20 @@ def __repr__(self): return '<{0} {1}>'.format(self.__class__.__name__, self.obj_list) def add(self, obj): - self.obj_list.append(obj) + if isinstance(obj, BarAttr): + # Find first idx of instance which is not a BarAttr + idx = len(self.obj_list) + for (i, x) in enumerate(self.obj_list): + if not isinstance(x, BarAttr): + idx = i + break + + if (obj not in self.obj_list): + # The candidate does not exists in our obj_list, add it at a fitting + # location in the list + self.obj_list.insert(idx, obj) + else: + self.obj_list.append(obj) def has_music(self): """ Check if bar contains music. """ @@ -731,6 +744,11 @@ def __init__(self): def __repr__(self): return '<{0} {1}>'.format(self.__class__.__name__, self.time) + def __eq__(self, other): + if self.__class__ != other.__class__: + return False + return self.__dict__ == other.__dict__ + def set_key(self, muskey, mode): self.key = muskey self.mode = mode From 22ff849d72d83e8c9baf1300f3c900f6a294fa08 Mon Sep 17 00:00:00 2001 From: Endre Oma Date: Sat, 18 Feb 2017 20:19:51 +0100 Subject: [PATCH 08/16] Add tests for repeats. The tests include a choir staff consisting of a simple score with standard 2 repeats and 3 repeats, and a score with two voices. --- tests/test_xml.py | 3 + tests/test_xml_files/repeat.ly | 43 +++++ tests/test_xml_files/repeat.xml | 307 ++++++++++++++++++++++++++++++++ 3 files changed, 353 insertions(+) create mode 100644 tests/test_xml_files/repeat.ly create mode 100644 tests/test_xml_files/repeat.xml diff --git a/tests/test_xml.py b/tests/test_xml.py index e25f6ae5..385c2629 100644 --- a/tests/test_xml.py +++ b/tests/test_xml.py @@ -31,6 +31,9 @@ def test_tuplet(): compare_output('tuplet') +def test_repeat(): + compare_output('repeat') + def ly_to_xml(filename): """Read Lilypond file and return XML string.""" writer = ly.musicxml.writer() diff --git a/tests/test_xml_files/repeat.ly b/tests/test_xml_files/repeat.ly new file mode 100644 index 00000000..a228f8b3 --- /dev/null +++ b/tests/test_xml_files/repeat.ly @@ -0,0 +1,43 @@ +\version "2.19.55" + +\header { + title = "repeat" +} + +\score { + \new ChoirStaff << + \new Staff { + \relative c' { + c1 | + \repeat volta 2{ c1| } + d1 | + \repeat volta 3{ d1| } + } + } + + \new Staff + << + \clef treble + \new Voice { + \voiceOne + \relative c' { + c1 | + \repeat volta 2{ d1| } + e1 | + \repeat volta 3{ d1| } + } + } + \new Voice { + \voiceTwo + \relative c' { + f4 f f f | + \repeat volta 2{ g g g g| } + a a a a | + \repeat volta 3{ g2 g| } + } + } + >> + >> + + \layout {} +} diff --git a/tests/test_xml_files/repeat.xml b/tests/test_xml_files/repeat.xml new file mode 100644 index 00000000..87455aca --- /dev/null +++ b/tests/test_xml_files/repeat.xml @@ -0,0 +1,307 @@ + + + + repeat + + + python-ly 0.9.5 + 2016-03-28 + + + + + bracket + + + + + + + + + + + + + 1 + + + G + 2 + + + + + C + 4 + + 4 + 1 + whole + + + + + heavy-light + + + + light-heavy + + + + + C + 4 + + 4 + 1 + whole + + + + + + D + 4 + + 4 + 1 + whole + + + + + heavy-light + + + + light-heavy + + + + + D + 4 + + 4 + 1 + whole + + + + + + + + 1 + + + G + 2 + + + + + C + 4 + + 4 + 1 + whole + + + + F + 4 + + 1 + 2 + quarter + + + + F + 4 + + 1 + 2 + quarter + + + + F + 4 + + 1 + 2 + quarter + + + + F + 4 + + 1 + 2 + quarter + + + + + heavy-light + + + + light-heavy + + + + + D + 4 + + 4 + 1 + whole + + + 4 + + + + G + 4 + + 1 + 2 + quarter + + + + G + 4 + + 1 + 2 + quarter + + + + G + 4 + + 1 + 2 + quarter + + + + G + 4 + + 1 + 2 + quarter + + + + + + E + 4 + + 4 + 1 + whole + + + 4 + + + + A + 4 + + 1 + 2 + quarter + + + + A + 4 + + 1 + 2 + quarter + + + + A + 4 + + 1 + 2 + quarter + + + + A + 4 + + 1 + 2 + quarter + + + + + heavy-light + + + + light-heavy + + + + + D + 4 + + 4 + 1 + whole + + + 4 + + + + G + 4 + + 2 + 2 + half + + + + G + 4 + + 2 + 2 + half + + + + + From edc65f785464acc5fb83c6f7ee9e17fe59001714 Mon Sep 17 00:00:00 2001 From: Endre Oma Date: Sat, 18 Feb 2017 20:49:27 +0100 Subject: [PATCH 09/16] Add ending to BarAttr, to be used for alternative endings. --- ly/musicxml/xml_objs.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/ly/musicxml/xml_objs.py b/ly/musicxml/xml_objs.py index d9922462..6de9788e 100644 --- a/ly/musicxml/xml_objs.py +++ b/ly/musicxml/xml_objs.py @@ -741,6 +741,11 @@ def __init__(self): self.multiclef = [] self.tempo = None + # Ending type, 'start', 'stop' or 'discontinue' + self.ending = None + # Ending number should either be a number, or a list of numbers + self.ending_number = None + def __repr__(self): return '<{0} {1}>'.format(self.__class__.__name__, self.time) @@ -767,6 +772,10 @@ def set_barline(self, bl): def set_tempo(self, unit=0, unittype='', beats=0, dots=0, text=""): self.tempo = TempoDir(unit, unittype, beats, dots, text) + def set_ending(self, type, number): + self.ending = type + self.ending_number = number + def has_attr(self): check = False if self.key is not None: From b68cdaf9f22d4f2908ff3089dd52a9bdcdb98ad9 Mon Sep 17 00:00:00 2001 From: Endre Oma Date: Sat, 18 Feb 2017 22:12:21 +0100 Subject: [PATCH 10/16] Mediator: Add new_ending() --- ly/musicxml/ly2xml_mediator.py | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/ly/musicxml/ly2xml_mediator.py b/ly/musicxml/ly2xml_mediator.py index f3cc3d88..486a1ec2 100644 --- a/ly/musicxml/ly2xml_mediator.py +++ b/ly/musicxml/ly2xml_mediator.py @@ -356,6 +356,21 @@ def new_repeat(self, rep, times=None): else: self.bar.add(barline) + def new_ending(self, type, number): + # type must be either 'start', 'stop' or 'discontinue' + # number must be ending number or a list of ending numbers + + ending = xml_objs.BarAttr() + ending.set_ending(type, number) + + if self.bar is None: + self.new_bar() + + if type in ['stop', 'discontinue'] and not self.bar.has_music() and self.prev_bar: + self.prev_bar.add(ending) + else: + self.bar.add(ending) + def new_key(self, key_name, mode): if self.bar is None: self.new_bar() From b538251a81ca9ce52efd34a79018fac89c02647f Mon Sep 17 00:00:00 2001 From: Endre Oma Date: Sat, 18 Feb 2017 23:04:00 +0100 Subject: [PATCH 11/16] ParseSource: add Alternative() --- ly/musicxml/lymus2musxml.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/ly/musicxml/lymus2musxml.py b/ly/musicxml/lymus2musxml.py index 7e979cef..4aa2edc9 100644 --- a/ly/musicxml/lymus2musxml.py +++ b/ly/musicxml/lymus2musxml.py @@ -442,6 +442,10 @@ def Repeat(self, repeat): elif repeat.specifier() == 'tremolo': self.trem_rep = repeat.repeat_count() + def Alternative(self, alternative): + """\alternative""" + pass + def Tremolo(self, tremolo): """A tremolo item ":".""" if self.look_ahead(tremolo, ly.music.items.Duration): From c43821480de662955c01c6b65a9b47562d2633f4 Mon Sep 17 00:00:00 2001 From: Endre Oma Date: Sat, 18 Feb 2017 23:20:15 +0100 Subject: [PATCH 12/16] Add default handling of end-of-repeat without alternative endings --- ly/musicxml/lymus2musxml.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/ly/musicxml/lymus2musxml.py b/ly/musicxml/lymus2musxml.py index 4aa2edc9..ff0c12b8 100644 --- a/ly/musicxml/lymus2musxml.py +++ b/ly/musicxml/lymus2musxml.py @@ -595,7 +595,9 @@ def End(self, end): self.grace_seq = False elif end.node.token == '\\repeat': if end.node.specifier() == 'volta': - self.mediator.new_repeat('backward', end.node.repeat_count()) + if not end.node.find_child(ly.music.items.Alternative, 1): + # the repeat does not contain alternative endings, treat as normal repeat + self.mediator.new_repeat('backward', end.node.repeat_count()) elif end.node.specifier() == 'tremolo': if self.look_ahead(end.node, ly.music.items.MusicList): self.mediator.set_tremolo(trem_type="stop") From a8a6f2484862818c3400671b7ea88c04b2659cdd Mon Sep 17 00:00:00 2001 From: Endre Oma Date: Sat, 18 Feb 2017 23:12:49 +0100 Subject: [PATCH 13/16] ParseSource: Add helper method alternative_handler --- ly/musicxml/lymus2musxml.py | 44 +++++++++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/ly/musicxml/lymus2musxml.py b/ly/musicxml/lymus2musxml.py index ff0c12b8..c5bda2e5 100644 --- a/ly/musicxml/lymus2musxml.py +++ b/ly/musicxml/lymus2musxml.py @@ -446,6 +446,50 @@ def Alternative(self, alternative): """\alternative""" pass + def alternative_handler(self, node, type): + """ + Helper method for handling alternative endings. + Generates number lists for the number attribute in MusicXML's ending element. + + It tries to follow the same pattern as the default in Lilypond + (see http://lilypond.org/doc/v2.18/Documentation/notation/long-repeats#normal-repeats) + """ + + # Should contain an array of MusicLists + alternative_container = node.parent() + + # Instance of \alternative + alternative_instance = alternative_container.parent() + + # instance of \repeat + repeat_instance = alternative_instance.parent() + + num_repeats = repeat_instance.repeat_count() + num_alternatives = len(alternative_container._children) + + idx = alternative_container.index(node) + 1 + ending_numbers = [idx] + + if num_alternatives < num_repeats: + # If there are fewer ending alternatives than repeats, generate + # ending numbers following the same order as Lilypond. + if idx == 1: + ending_numbers = list(range(1, num_repeats - num_alternatives + 2)) + else: + ending_numbers = [idx + num_repeats - num_alternatives] + + if type == 'start': + self.mediator.new_ending(type, ending_numbers) + elif type == 'stop': + if idx == 1 and num_alternatives < num_repeats: + self.mediator.new_repeat('backward', len(ending_numbers)+1) + elif idx < num_alternatives: + self.mediator.new_repeat('backward') + if idx == num_alternatives: + type = 'discontinue' + + self.mediator.new_ending(type, ending_numbers) + def Tremolo(self, tremolo): """A tremolo item ":".""" if self.look_ahead(tremolo, ly.music.items.Duration): From dbb117f92baa15ac875ba646930f0d20f5880fbe Mon Sep 17 00:00:00 2001 From: Endre Oma Date: Sat, 18 Feb 2017 23:30:07 +0100 Subject: [PATCH 14/16] Hook up ParseSource.alternative_handler() --- ly/musicxml/lymus2musxml.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/ly/musicxml/lymus2musxml.py b/ly/musicxml/lymus2musxml.py index c5bda2e5..63a4945a 100644 --- a/ly/musicxml/lymus2musxml.py +++ b/ly/musicxml/lymus2musxml.py @@ -195,6 +195,10 @@ def MusicList(self, musicList): self.sims_and_seqs.append('sim') elif musicList.token == '{': self.sims_and_seqs.append('seq') + if isinstance(musicList.parent().parent(), ly.music.items.Alternative): + # The grandparent node is an instance of \alternative, + # this indicates that this musiclist instance is an ending to a \repeat + self.alternative_handler(musicList, 'start') def Chord(self, chord): self.mediator.clear_chord() @@ -675,6 +679,8 @@ def End(self, end): if self.sims_and_seqs: self.sims_and_seqs.pop() elif end.node.token == '{': + if isinstance(end.node.parent().parent(), ly.music.items.Alternative): + self.alternative_handler(end.node, 'stop') if self.sims_and_seqs: self.sims_and_seqs.pop() elif end.node.token == '<': #chord From 1da695d02479ad7892dee306373ba7758807f2b1 Mon Sep 17 00:00:00 2001 From: Endre Oma Date: Sat, 18 Feb 2017 23:40:06 +0100 Subject: [PATCH 15/16] CreateMusicXML: add method add_ending() and hook it up. --- ly/musicxml/create_musicxml.py | 14 ++++++++++++++ ly/musicxml/xml_objs.py | 2 ++ 2 files changed, 16 insertions(+) diff --git a/ly/musicxml/create_musicxml.py b/ly/musicxml/create_musicxml.py index c0823d36..9bcade31 100644 --- a/ly/musicxml/create_musicxml.py +++ b/ly/musicxml/create_musicxml.py @@ -540,6 +540,20 @@ def add_barline(self, bl_type, repeat=None, repeat_times=None): if repeat_times and repeat_times > 2: repeatnode.attrib['times'] = str(repeat_times) + def add_ending(self, ending, number): + barnode = etree.SubElement(self.current_bar, "barline", location="right") + + if not(isinstance(number,list)): + number = [number] + + number_attr = ','.join(str(n) for n in number) + + endingnode = etree.SubElement(barnode, "ending", type=ending, number=number_attr) + + if ending == 'start': + barnode.attrib['location'] = 'left' + endingnode.text = ', '.join('{}.'.format(n) for n in number) + def add_backup(self, duration): if duration <= 0: return diff --git a/ly/musicxml/xml_objs.py b/ly/musicxml/xml_objs.py index 6de9788e..c0625e6c 100644 --- a/ly/musicxml/xml_objs.py +++ b/ly/musicxml/xml_objs.py @@ -126,6 +126,8 @@ def new_xml_bar_attr(self, obj): self.musxml.add_barline(obj.barline, obj.repeat, obj.repeat_times) elif obj.barline: self.musxml.add_barline(obj.barline) + elif obj.ending: + self.musxml.add_ending(obj.ending, obj.ending_number) if obj.staves: self.musxml.add_staves(obj.staves) if obj.multiclef: From 9dea3ba1bffb5bab3c6d3fd39f0fcecf8ed2c112 Mon Sep 17 00:00:00 2001 From: Endre Oma Date: Sat, 18 Feb 2017 23:47:26 +0100 Subject: [PATCH 16/16] Add test for repeats with alternatives --- tests/test_xml.py | 3 + .../test_xml_files/repeat_with_alternative.ly | 35 +++ .../repeat_with_alternative.xml | 288 ++++++++++++++++++ 3 files changed, 326 insertions(+) create mode 100644 tests/test_xml_files/repeat_with_alternative.ly create mode 100644 tests/test_xml_files/repeat_with_alternative.xml diff --git a/tests/test_xml.py b/tests/test_xml.py index 385c2629..4a9b39c8 100644 --- a/tests/test_xml.py +++ b/tests/test_xml.py @@ -34,6 +34,9 @@ def test_tuplet(): def test_repeat(): compare_output('repeat') +def test_repeat_with_alternative(): + compare_output('repeat_with_alternative') + def ly_to_xml(filename): """Read Lilypond file and return XML string.""" writer = ly.musicxml.writer() diff --git a/tests/test_xml_files/repeat_with_alternative.ly b/tests/test_xml_files/repeat_with_alternative.ly new file mode 100644 index 00000000..4c48fc2b --- /dev/null +++ b/tests/test_xml_files/repeat_with_alternative.ly @@ -0,0 +1,35 @@ +\version "2.19.55" + +\header { + title = "repeat with alternative" +} + +\score { + \relative c'{ + % Simple repeat with a simple alternative + c1 | + \repeat volta 2{ c1| } + \alternative { + { d1 | } + { d1 | e1 |} + } + + % Simple repeat with more repeats than alternatives + c1 | + \repeat volta 3{ c1| } + \alternative { + { d1 | } + { d1 | e1 |} + } + + % Simple repeat with many endings + c1 | + \repeat volta 5{ c1| } + \alternative { + { d1 | e1 |} + { d1 | f1 |} + { d1 |} + } + } + \layout {} +} diff --git a/tests/test_xml_files/repeat_with_alternative.xml b/tests/test_xml_files/repeat_with_alternative.xml new file mode 100644 index 00000000..06f69af7 --- /dev/null +++ b/tests/test_xml_files/repeat_with_alternative.xml @@ -0,0 +1,288 @@ + + + + repeat with alternative + + + python-ly 0.9.5 + 2016-03-28 + + + + + + + + + + + 1 + + + G + 2 + + + + + C + 4 + + 4 + 1 + whole + + + + + heavy-light + + + + + C + 4 + + 4 + 1 + whole + + + + + 1. + + + light-heavy + + + + + + + + D + 4 + + 4 + 1 + whole + + + + + 2. + + + + D + 4 + + 4 + 1 + whole + + + + + + + + + E + 4 + + 4 + 1 + whole + + + + + + C + 4 + + 4 + 1 + whole + + + + + heavy-light + + + + + C + 4 + + 4 + 1 + whole + + + + + 1., 2. + + + light-heavy + + + + + + + + D + 4 + + 4 + 1 + whole + + + + + 3. + + + + D + 4 + + 4 + 1 + whole + + + + + + + + + E + 4 + + 4 + 1 + whole + + + + + + C + 4 + + 4 + 1 + whole + + + + + heavy-light + + + + + C + 4 + + 4 + 1 + whole + + + + + 1., 2., 3. + + + + D + 4 + + 4 + 1 + whole + + + + + light-heavy + + + + + + + + E + 4 + + 4 + 1 + whole + + + + + 4. + + + + D + 4 + + 4 + 1 + whole + + + + + light-heavy + + + + + + + + F + 4 + + 4 + 1 + whole + + + + + 5. + + + + + + + D + 4 + + 4 + 1 + whole + + + + +