diff --git a/ly/musicxml/create_musicxml.py b/ly/musicxml/create_musicxml.py index 3142abc7..b7be8e47 100644 --- a/ly/musicxml/create_musicxml.py +++ b/ly/musicxml/create_musicxml.py @@ -121,6 +121,25 @@ def create_measure(self, pickup = False, **bar_attrs): if bar_attrs: self.new_bar_attr(**bar_attrs) + def get_previous_bar(self): + """ Return the previous bar or False if we're in the first. """ + siblings = self.current_part.getchildren() + curr_index = siblings.index(self.current_bar) + if curr_index == 0: + return False + else: + return siblings[curr_index - 1] + + def get_next_bar(self): + """ Return the next bar or False if we're in the last. """ + siblings = self.current_part.getchildren() + curr_index = siblings.index(self.current_bar) + if curr_index == len(siblings) - 1: + return False + else: + return siblings[curr_index + 1] + + ## # High-level node creation ## @@ -504,6 +523,21 @@ def create_bar_attr(self): """Create node attributes """ self.bar_attr = etree.SubElement(self.current_bar, "attributes") + def create_barline(self, bar_style, ends_bar): + """Create a barline, either at the current position + or at the end of the previous bar. """ + if ends_bar: + # This is necessary because when the user writes \bar as a bar end + # we are already in the next measure. + bar = self.get_previous_bar() + location = 'right' + else: + bar = self.current_bar + location = 'middle' + bl = etree.SubElement(bar, "barline", {'location': location}) + bs = etree.SubElement(bl, 'bar-style') + bs.text = bar_style + def add_divisions(self, div): division = etree.SubElement(self.bar_attr, "divisions") division.text = str(div) diff --git a/ly/musicxml/ly2xml_mediator.py b/ly/musicxml/ly2xml_mediator.py index e8607ea7..ce9830db 100644 --- a/ly/musicxml/ly2xml_mediator.py +++ b/ly/musicxml/ly2xml_mediator.py @@ -335,11 +335,12 @@ def add_to_bar(self, obj): self.new_bar() self.bar.add(obj) - def create_barline(self, bl): - barline = xml_objs.BarAttr() - barline.set_barline(bl) - self.bar.add(barline) - self.new_bar() + def new_barline(self, bl): + if not self.bar: + # \bar at the very beginning is ignored + return + ends_bar = not self.bar.has_music() + self.bar.add(xml_objs.BarLine(bl, ends_bar)) def new_repeat(self, rep): barline = xml_objs.BarAttr() diff --git a/ly/musicxml/lymus2musxml.py b/ly/musicxml/lymus2musxml.py index 46effdb5..f5d52d39 100644 --- a/ly/musicxml/lymus2musxml.py +++ b/ly/musicxml/lymus2musxml.py @@ -94,10 +94,10 @@ def __init__(self): def parse_text(self, ly_text, filename=None): """Parse the LilyPond source specified as text. - + If you specify a filename, it can be used to resolve \\include commands correctly. - + """ doc = ly.document.Document(ly_text) doc.filename = filename @@ -105,11 +105,11 @@ def parse_text(self, ly_text, filename=None): def parse_document(self, ly_doc, relative_first_pitch_absolute=False): """Parse the LilyPond source specified as a ly.document document. - + If relative_first_pitch_absolute is set to True, the first pitch in a \relative expression without startpitch is considered to be absolute (LilyPond 2.18+ behaviour). - + """ # The document is copied and the copy is converted to absolute mode to # facilitate the export. The original document is unchanged. @@ -540,7 +540,7 @@ def MarkupList(self, markuplist): def String(self, string): prev = self.get_previous_node(string) if prev and prev.token == '\\bar': - self.mediator.create_barline(string.value()) + self.mediator.new_barline(string.value()) def LyricsTo(self, lyrics_to): r"""A \lyricsto expression. """ @@ -682,7 +682,7 @@ def End(self, end): def get_previous_node(self, node): """ Returns the nodes previous node - or false if the node is first in its branch. """ + or False if the node is first in its branch. """ parent = node.parent() i = parent.index(node) if i > 0: diff --git a/ly/musicxml/xml_objs.py b/ly/musicxml/xml_objs.py index 8cca5e4a..d932f125 100644 --- a/ly/musicxml/xml_objs.py +++ b/ly/musicxml/xml_objs.py @@ -125,11 +125,13 @@ def iterate_bar(self, bar): elif isinstance(obj, BarBackup): divdur = self.count_duration(obj.duration, self.divisions) self.musxml.add_backup(divdur) + elif isinstance(obj, BarLine): + self.musxml.create_barline(obj.bar_style, obj.ends_bar) def new_xml_bar_attr(self, obj): """Create bar attribute xml-nodes.""" if obj.has_attr(): - self.musxml.new_bar_attr(obj.clef, obj.time, obj.key, obj.mode, + self.musxml.new_bar_attr(obj.clef, obj.time, obj.key, obj.mode, obj.divs, obj.multirest) if obj.new_system: self.musxml.new_system(obj.new_system) @@ -523,6 +525,17 @@ def inject_voice(self, new_voice, override=False): self.add(bl) +class BarLine(): + """ Represents a manual bar line, needed for in-measure barlines + or for special-style barlines.""" + def __init__(self, bar_style, ends_bar): + self.bar_style = convert_barl(bar_style) + self.ends_bar = ends_bar + + def __repr__(self): + return '<{0} {1}>'.format(self.__class__.__name__, self.bar_style) + + class BarMus(): """ Common class for notes and rests. """ def __init__(self, duration, voice=1): diff --git a/tests/test_xml_files/barline.ly b/tests/test_xml_files/barline.ly new file mode 100644 index 00000000..5ee4655f --- /dev/null +++ b/tests/test_xml_files/barline.ly @@ -0,0 +1,13 @@ +\version "2.18.2" + +\relative { + % Ignored: + \bar ":" + a'1 + a4 a + % mid-measure + \bar "||" + a a | + g g g g + \bar "|." +} diff --git a/tests/test_xml_files/barline.xml b/tests/test_xml_files/barline.xml new file mode 100644 index 00000000..a8041140 --- /dev/null +++ b/tests/test_xml_files/barline.xml @@ -0,0 +1,123 @@ + + + + + + python-ly 0.9.5 + 2018-05-07 + + + + + + + + + + + 1 + + + G + 2 + + + + + A + 4 + + 4 + 1 + whole + + + + + + A + 4 + + 1 + 1 + quarter + + + + A + 4 + + 1 + 1 + quarter + + + light-light + + + + A + 4 + + 1 + 1 + quarter + + + + A + 4 + + 1 + 1 + quarter + + + + + + G + 4 + + 1 + 1 + quarter + + + + G + 4 + + 1 + 1 + quarter + + + + G + 4 + + 1 + 1 + quarter + + + + G + 4 + + 1 + 1 + quarter + + + light-heavy + + + + +