From a20beb49ec1117b276546890a98f6e2290ad4afb Mon Sep 17 00:00:00 2001 From: Urs Liska Date: Fri, 4 May 2018 23:32:53 +0200 Subject: [PATCH 1/3] XML: Start supporting manual Beam (WIP) Export "first" beam. This works only with regular eighth note beams. Anything else won't work. Only beam nr 1 is created and ended. But if more than one beam is involved they have to be encoded all. That means a) we have to check the number of beams at [ and ] b) we have to check at each note if it is beamed, and if so the current number of beams has to be calculated. (seems challenging) --- ly/musicxml/create_musicxml.py | 2 +- ly/musicxml/ly2xml_mediator.py | 3 +++ ly/musicxml/lymus2musxml.py | 18 +++++++++++++----- ly/musicxml/xml_objs.py | 11 +++++++++++ 4 files changed, 28 insertions(+), 6 deletions(-) diff --git a/ly/musicxml/create_musicxml.py b/ly/musicxml/create_musicxml.py index c2b04aec..93eb1773 100644 --- a/ly/musicxml/create_musicxml.py +++ b/ly/musicxml/create_musicxml.py @@ -366,7 +366,7 @@ def add_dot(self): def add_beam(self, nr, beam_type): """Add beam. """ - beam_node = etree.SubElement(self.current_notation, "beam", number=str(nr)) + beam_node = etree.SubElement(self.current_note, "beam", number=str(nr)) beam_node.text = beam_type def add_tie(self, tie_type): diff --git a/ly/musicxml/ly2xml_mediator.py b/ly/musicxml/ly2xml_mediator.py index c0ec835b..a75c0820 100644 --- a/ly/musicxml/ly2xml_mediator.py +++ b/ly/musicxml/ly2xml_mediator.py @@ -707,6 +707,9 @@ def tie_to_next(self): self.tied = True self.current_note.set_tie(tie_type) + def set_beam(self, nr, beam_type): + self.current_note.set_beam(nr, beam_type) + def set_slur(self, nr, slur_type): """ Set the slur start or stop for the current note. """ diff --git a/ly/musicxml/lymus2musxml.py b/ly/musicxml/lymus2musxml.py index 46effdb5..4f12b7e9 100644 --- a/ly/musicxml/lymus2musxml.py +++ b/ly/musicxml/lymus2musxml.py @@ -86,6 +86,8 @@ def __init__(self): self.unset_tuplspan = False self.alt_mode = None self.rel_pitch_isset = False + self.beamcount = 0 + self.beamnr = 0 self.slurcount = 0 self.slurnr = 0 self.phrslurnr = 0 @@ -94,10 +96,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 +107,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. @@ -412,7 +414,13 @@ def Postfix(self, postfix): pass def Beam(self, beam): - pass + if beam.token == '[': + self.beamcount += 1 + self.beamnr = self.beamcount + self.mediator.set_beam(self.beamnr, "begin") + else: + self.mediator.set_beam(self.beamnr, "end") + self.beamcount -= 1 def Slur(self, slur): """ Slur, '(' = start, ')' = stop. """ diff --git a/ly/musicxml/xml_objs.py b/ly/musicxml/xml_objs.py index ae836371..7b8ad403 100644 --- a/ly/musicxml/xml_objs.py +++ b/ly/musicxml/xml_objs.py @@ -195,6 +195,8 @@ def new_xml_note(self, obj): self.musxml.tie_note(t) for s in obj.slur: self.musxml.add_slur(s.nr, s.slurtype) + for b in obj.beam: + self.musxml.add_beam(b.nr, b.beam_type) for a in obj.artic: self.musxml.new_articulation(a) if obj.ornament: @@ -619,6 +621,11 @@ def __init__(self, nr, slurtype): self.nr = nr self.slurtype = slurtype +class Beam(): + def __init__(self, nr, beam_type): + """Stores information about a beam.""" + self.nr = nr + self.beam_type = beam_type ## # Subclasses of BarMus @@ -637,6 +644,7 @@ def __init__(self, pitch_note, alter, accidental, duration, voice=1): self.grace = (0, 0) self.gliss = None self.tremolo = ('', 0) + self.beam = [] self.skip = False self.slur = [] self.artic = [] @@ -646,6 +654,9 @@ def __init__(self, pitch_note, alter, accidental, duration, voice=1): self.lyric = None self.stem_direction = None + def set_beam(self, nr, beam_type): + self.beam.append(Beam(nr, beam_type)) + def set_duration(self, duration, durtype=''): self.duration = duration self.dot = 0 From c7ed5c916b07db0208bbadd50a9c19f1bac327d4 Mon Sep 17 00:00:00 2001 From: Urs Liska Date: Sat, 5 May 2018 09:11:37 +0200 Subject: [PATCH 2/3] XML: Add beam continuation As described in http://lists.gnu.org/archive/html/lilypond-user/2018-05/msg00128.html there is the possibility to simply encode *one* beam and add continue to each enclosed note. This is no correct MusicXML, but it will correctly be loaded by MuseScore and Verovio (test at http://www.verovio.org/musicxml.html). If we agree to stick with that halfway solution (at least for now) I think we can now (after adding a test file) merge this. --- ly/musicxml/lymus2musxml.py | 2 ++ ly/musicxml/xml_objs.py | 11 +++++++++-- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/ly/musicxml/lymus2musxml.py b/ly/musicxml/lymus2musxml.py index 4f12b7e9..9ee3cb1e 100644 --- a/ly/musicxml/lymus2musxml.py +++ b/ly/musicxml/lymus2musxml.py @@ -297,6 +297,8 @@ def Note(self, note): # chord as grace note if self.grace_seq: self.mediator.new_chord_grace() + if self.beamcount > 0: + self.mediator.set_beam(self.beamnr, "continue") def Unpitched(self, unpitched): """A note without pitch, just a standalone duration.""" diff --git a/ly/musicxml/xml_objs.py b/ly/musicxml/xml_objs.py index 7b8ad403..6ab9b433 100644 --- a/ly/musicxml/xml_objs.py +++ b/ly/musicxml/xml_objs.py @@ -125,7 +125,7 @@ def iterate_bar(self, 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) @@ -655,7 +655,14 @@ def __init__(self, pitch_note, alter, accidental, duration, voice=1): self.stem_direction = None def set_beam(self, nr, beam_type): - self.beam.append(Beam(nr, beam_type)) + # NOTE: The following is only function as long as we're + # only dealing with *one* beam instead of a proper beaming pattern. + # If we have a real beaming pattern, a beam end has to end *all* + # beam levels. + if beam_type == 'end': + self.beam = [Beam(nr, beam_type)] + else: + self.beam.append(Beam(nr, beam_type)) def set_duration(self, duration, durtype=''): self.duration = duration From ebdd6a286b7e85995e4d9460330a6527bb30ae9c Mon Sep 17 00:00:00 2001 From: Urs Liska Date: Sat, 5 May 2018 09:13:40 +0200 Subject: [PATCH 3/3] Add Beam XML test file --- tests/test_xml_files/beam.ly | 7 ++ tests/test_xml_files/beam.xml | 153 ++++++++++++++++++++++++++++++++++ 2 files changed, 160 insertions(+) create mode 100644 tests/test_xml_files/beam.ly create mode 100644 tests/test_xml_files/beam.xml diff --git a/tests/test_xml_files/beam.ly b/tests/test_xml_files/beam.ly new file mode 100644 index 00000000..b42874a5 --- /dev/null +++ b/tests/test_xml_files/beam.ly @@ -0,0 +1,7 @@ +\version "2.19.80" + +{ + a'8 [ b'16 a' ] + g'8 [ a'32 b' c''16 ] + b'16.. [ a'64 g'16 f'32 e' ] +} \ No newline at end of file diff --git a/tests/test_xml_files/beam.xml b/tests/test_xml_files/beam.xml new file mode 100644 index 00000000..dd3db747 --- /dev/null +++ b/tests/test_xml_files/beam.xml @@ -0,0 +1,153 @@ + + + + + + python-ly 0.9.5 + 2018-05-05 + + + + + + + + + + + 128 + + + G + 2 + + + + + A + 4 + + 64 + 1 + eighth + begin + + + + B + 4 + + 32 + 1 + 16th + continue + + + + A + 4 + + 32 + 1 + 16th + end + + + + G + 4 + + 64 + 1 + eighth + begin + + + + A + 4 + + 16 + 1 + 32nd + continue + + + + B + 4 + + 16 + 1 + 32nd + continue + + + + C + 5 + + 32 + 1 + 16th + end + + + + B + 4 + + 56 + 1 + 16th + + + begin + + + + A + 4 + + 8 + 1 + 64th + continue + + + + G + 4 + + 32 + 1 + 16th + continue + + + + F + 4 + + 16 + 1 + 32nd + continue + + + + E + 4 + + 16 + 1 + 32nd + end + + + +