From bdb81e318cf78f828b61eb224e6c7ec86eff7fe7 Mon Sep 17 00:00:00 2001 From: Endre Oma Date: Wed, 29 Mar 2017 12:36:43 +0200 Subject: [PATCH 1/6] Add Sustain xml object --- ly/musicxml/xml_objs.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/ly/musicxml/xml_objs.py b/ly/musicxml/xml_objs.py index bad1e057..3040d7cd 100644 --- a/ly/musicxml/xml_objs.py +++ b/ly/musicxml/xml_objs.py @@ -600,6 +600,12 @@ def __init__(self, nr, slurtype): self.nr = nr self.slurtype = slurtype +class Sustain(): + """Stores information about sustain.""" + def __init__(self, type, line='no', sign='yes'): + self.type = type + self.line = line + self.sign = sign ## # Subclasses of BarMus From fb277e16e010de5130c102ed4e8693c916bcfbcc Mon Sep 17 00:00:00 2001 From: Endre Oma Date: Wed, 29 Mar 2017 12:40:57 +0200 Subject: [PATCH 2/6] Add sustain to BarMus --- ly/musicxml/xml_objs.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/ly/musicxml/xml_objs.py b/ly/musicxml/xml_objs.py index 3040d7cd..d28a14a7 100644 --- a/ly/musicxml/xml_objs.py +++ b/ly/musicxml/xml_objs.py @@ -510,6 +510,7 @@ def __init__(self, duration, voice=1): self.other_notation = None self.dynamic = [] self.oct_shift = None + self.sustain = None def __repr__(self): return '<{0} {1}>'.format(self.__class__.__name__, self.duration) @@ -541,6 +542,9 @@ def set_dynamics_dashes(self, sign, before=True): def set_oct_shift(self, plac, octdir, size): self.oct_shift = OctaveShift(plac, octdir, size) + def set_sustain(self, type, line='no', sign='yes'): + self.sustain = Sustain(type, line, sign) + def has_attr(self): return False From 3f57fc4fede5b88391277c44176b02bf83cf434a Mon Sep 17 00:00:00 2001 From: Endre Oma Date: Wed, 29 Mar 2017 12:54:43 +0200 Subject: [PATCH 3/6] Generate a pedal element from Sustain --- ly/musicxml/create_musicxml.py | 5 +++++ ly/musicxml/xml_objs.py | 2 ++ 2 files changed, 7 insertions(+) diff --git a/ly/musicxml/create_musicxml.py b/ly/musicxml/create_musicxml.py index 8fb82b7b..a1879ec5 100644 --- a/ly/musicxml/create_musicxml.py +++ b/ly/musicxml/create_musicxml.py @@ -596,6 +596,11 @@ def add_octave_shift(self, plac, octdir, size): dirtypenode = etree.SubElement(direction, "direction-type") dyn_node = etree.SubElement(dirtypenode, "octave-shift", oct_dict) + def add_sustain(self, type, line, sign): + direction = etree.SubElement(self.current_bar, "direction", placement='below') + dirtypenode = etree.SubElement(direction, "direction-type") + pedal = etree.SubElement(dirtypenode, "pedal", type=type, line=line, sign=sign) + def add_dirwords(self, words): """Add words in direction, e. g. a tempo mark.""" dirtypenode = etree.SubElement(self.direction, "direction-type") diff --git a/ly/musicxml/xml_objs.py b/ly/musicxml/xml_objs.py index d28a14a7..3b95cb78 100644 --- a/ly/musicxml/xml_objs.py +++ b/ly/musicxml/xml_objs.py @@ -140,6 +140,8 @@ def before_note(self, obj): self._add_dynamics([d for d in obj.dynamic if d.before]) if obj.oct_shift and not obj.oct_shift.octdir == 'stop': self.musxml.add_octave_shift(obj.oct_shift.plac, obj.oct_shift.octdir, obj.oct_shift.size) + if obj.sustain: + self.musxml.add_sustain(obj.sustain.type, obj.sustain.line, obj.sustain.sign) def after_note(self, obj): """Xml-nodes after note.""" From d966947708e9f81a6cbe0c0f34760d93c72f7ba0 Mon Sep 17 00:00:00 2001 From: Endre Oma Date: Wed, 29 Mar 2017 12:58:32 +0200 Subject: [PATCH 4/6] Recognize \sustain{On,Off} --- ly/musicxml/ly2xml_mediator.py | 12 ++++++++++++ ly/musicxml/lymus2musxml.py | 4 ++++ 2 files changed, 16 insertions(+) diff --git a/ly/musicxml/ly2xml_mediator.py b/ly/musicxml/ly2xml_mediator.py index a9ec6951..c4b22b53 100644 --- a/ly/musicxml/ly2xml_mediator.py +++ b/ly/musicxml/ly2xml_mediator.py @@ -717,6 +717,18 @@ def new_trill_spanner(self, end=None): end = "start" self.current_note.add_adv_ornament('wavy-line', end) + def new_sustain(self, type=None): + if not type: + type = 'start' + if self.current_note.sustain and \ + self.current_note.sustain.type == 'stop' and type == 'start': + type = 'change' + + line = 'no' + sign = 'yes' + + self.current_note.set_sustain(type, line, sign) + def new_ottava(self, octdiff): octdiff = int(octdiff) if self.octdiff == octdiff: diff --git a/ly/musicxml/lymus2musxml.py b/ly/musicxml/lymus2musxml.py index 13dfb733..dda353a7 100644 --- a/ly/musicxml/lymus2musxml.py +++ b/ly/musicxml/lymus2musxml.py @@ -497,6 +497,10 @@ def Command(self, command): if self.tupl_span: self.mediator.unset_tuplspan_dur() self.tupl_span = False + elif command.token == '\\sustainOn': + self.mediator.new_sustain('start') + elif command.token == '\\sustainOff': + self.mediator.new_sustain('stop') else: if command.token not in excls: print("Unknown command:", command.token) From d78b062bcd7644db329042a56b7af8aa9f168749 Mon Sep 17 00:00:00 2001 From: Endre Oma Date: Wed, 29 Mar 2017 13:05:09 +0200 Subject: [PATCH 5/6] Implement pedalSustainStyle --- ly/musicxml/ly2xml_mediator.py | 12 ++++++++++++ ly/musicxml/lymus2musxml.py | 5 +++++ 2 files changed, 17 insertions(+) diff --git a/ly/musicxml/ly2xml_mediator.py b/ly/musicxml/ly2xml_mediator.py index c4b22b53..c870e8b5 100644 --- a/ly/musicxml/ly2xml_mediator.py +++ b/ly/musicxml/ly2xml_mediator.py @@ -76,6 +76,7 @@ def __init__(self): self.prev_tremolo = 8 self.tupl_dur = 0 self.tupl_sum = 0 + self.sustain_style = 'text' def new_header_assignment(self, name, value): """Distributing header information.""" @@ -724,8 +725,14 @@ def new_sustain(self, type=None): self.current_note.sustain.type == 'stop' and type == 'start': type = 'change' + # implied that self.sustain_style == 'text' line = 'no' sign = 'yes' + if self.sustain_style == 'mixed': + line = 'yes' + elif self.sustain_style == 'bracket': + line = 'yes' + sign = 'no' self.current_note.set_sustain(type, line, sign) @@ -789,6 +796,8 @@ def set_by_property(self, prprty, value, group=False): self.new_lyric_nr(value) elif prprty == 'systemStartDelimiter': self.change_group_bracket(value) + elif prprty == 'pedalSustainStyle': + self.set_sustain_style(value) def set_partname(self, name): if self.score.is_empty(): @@ -813,6 +822,9 @@ def set_partmidi(self, midi): self.new_part() self.part.midi = midi + def set_sustain_style(self, style): + self.sustain_style = style + def new_lyric_nr(self, num): self.lyric_nr = num diff --git a/ly/musicxml/lymus2musxml.py b/ly/musicxml/lymus2musxml.py index dda353a7..d9282dec 100644 --- a/ly/musicxml/lymus2musxml.py +++ b/ly/musicxml/lymus2musxml.py @@ -464,6 +464,11 @@ def Set(self, cont_set): self.mediator.unset_tuplspan_dur() return val = cont_set.value().get_string() + + if cont_set.property() == 'pedalSustainStyle': + # access #'value + # FIXME: find a safer way to get a string + val = cont_set.value()[0][0].token else: val = cont_set.value().value() if cont_set.context() in part_contexts: From a417da27575be639dd3544c5a28d23a2b594df62 Mon Sep 17 00:00:00 2001 From: Endre Oma Date: Wed, 29 Mar 2017 13:21:40 +0200 Subject: [PATCH 6/6] Add tests for sustain --- tests/test_xml.py | 3 + tests/test_xml_files/sustain.ly | 19 + tests/test_xml_files/sustain.xml | 578 +++++++++++++++++++++++++++++++ 3 files changed, 600 insertions(+) create mode 100644 tests/test_xml_files/sustain.ly create mode 100644 tests/test_xml_files/sustain.xml diff --git a/tests/test_xml.py b/tests/test_xml.py index e25f6ae5..9b5bf696 100644 --- a/tests/test_xml.py +++ b/tests/test_xml.py @@ -31,6 +31,9 @@ def test_tuplet(): compare_output('tuplet') +def test_sustain(): + compare_output('sustain') + def ly_to_xml(filename): """Read Lilypond file and return XML string.""" writer = ly.musicxml.writer() diff --git a/tests/test_xml_files/sustain.ly b/tests/test_xml_files/sustain.ly new file mode 100644 index 00000000..4169c6e4 --- /dev/null +++ b/tests/test_xml_files/sustain.ly @@ -0,0 +1,19 @@ +\score { + \relative c' { + c4\sustainOn d e\sustainOff f | + c8\sustainOn c d d\sustainOff\sustainOn e e f f \sustainOff | + + \set Staff.pedalSustainStyle = #'mixed + c4\sustainOn d e\sustainOff f | + c8\sustainOn c d d\sustainOff\sustainOn e e f f\sustainOff | + + \set Staff.pedalSustainStyle = #'bracket + c4\sustainOn d e\sustainOff f | + c8\sustainOn c d d\sustainOff\sustainOn e e f f\sustainOff | + + \set Staff.pedalSustainStyle = #'text + c4\sustainOn d e\sustainOff f | + c8\sustainOn c d d\sustainOff\sustainOn e e f f\sustainOff | + } + \layout{} +} diff --git a/tests/test_xml_files/sustain.xml b/tests/test_xml_files/sustain.xml new file mode 100644 index 00000000..f884bf15 --- /dev/null +++ b/tests/test_xml_files/sustain.xml @@ -0,0 +1,578 @@ + + + + + + python-ly 0.9.5 + 2017-03-29 + + + + + + + + + + + 2 + + + G + 2 + + + + + + + + + + C + 4 + + 2 + 1 + quarter + + + + D + 4 + + 2 + 1 + quarter + + + + + + + + + E + 4 + + 2 + 1 + quarter + + + + F + 4 + + 2 + 1 + quarter + + + + + + + + + + + C + 4 + + 1 + 1 + eighth + + + + C + 4 + + 1 + 1 + eighth + + + + D + 4 + + 1 + 1 + eighth + + + + + + + + + D + 4 + + 1 + 1 + eighth + + + + E + 4 + + 1 + 1 + eighth + + + + E + 4 + + 1 + 1 + eighth + + + + F + 4 + + 1 + 1 + eighth + + + + + + + + + F + 4 + + 1 + 1 + eighth + + + + + + + + + + + C + 4 + + 2 + 1 + quarter + + + + D + 4 + + 2 + 1 + quarter + + + + + + + + + E + 4 + + 2 + 1 + quarter + + + + F + 4 + + 2 + 1 + quarter + + + + + + + + + + + C + 4 + + 1 + 1 + eighth + + + + C + 4 + + 1 + 1 + eighth + + + + D + 4 + + 1 + 1 + eighth + + + + + + + + + D + 4 + + 1 + 1 + eighth + + + + E + 4 + + 1 + 1 + eighth + + + + E + 4 + + 1 + 1 + eighth + + + + F + 4 + + 1 + 1 + eighth + + + + + + + + + F + 4 + + 1 + 1 + eighth + + + + + + + + + + + C + 4 + + 2 + 1 + quarter + + + + D + 4 + + 2 + 1 + quarter + + + + + + + + + E + 4 + + 2 + 1 + quarter + + + + F + 4 + + 2 + 1 + quarter + + + + + + + + + + + C + 4 + + 1 + 1 + eighth + + + + C + 4 + + 1 + 1 + eighth + + + + D + 4 + + 1 + 1 + eighth + + + + + + + + + D + 4 + + 1 + 1 + eighth + + + + E + 4 + + 1 + 1 + eighth + + + + E + 4 + + 1 + 1 + eighth + + + + F + 4 + + 1 + 1 + eighth + + + + + + + + + F + 4 + + 1 + 1 + eighth + + + + + + + + + + + C + 4 + + 2 + 1 + quarter + + + + D + 4 + + 2 + 1 + quarter + + + + + + + + + E + 4 + + 2 + 1 + quarter + + + + F + 4 + + 2 + 1 + quarter + + + + + + + + + + + C + 4 + + 1 + 1 + eighth + + + + C + 4 + + 1 + 1 + eighth + + + + D + 4 + + 1 + 1 + eighth + + + + + + + + + D + 4 + + 1 + 1 + eighth + + + + E + 4 + + 1 + 1 + eighth + + + + E + 4 + + 1 + 1 + eighth + + + + F + 4 + + 1 + 1 + eighth + + + + + + + + + F + 4 + + 1 + 1 + eighth + + + + +