Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 15 additions & 3 deletions ly/musicxml/ly2xml_mediator.py
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@ def __init__(self):
self.prev_tremolo = 8
self.tupl_dur = 0
self.tupl_sum = 0
self.slur_stack = []
self.multiple_rest = False
self.multiple_rest_bar = None
self.current_mark = 1
Expand Down Expand Up @@ -707,10 +708,21 @@ def tie_to_next(self):
self.tied = True
self.current_note.set_tie(tie_type)

def set_slur(self, nr, slur_type):
def set_slur(self, nr, slur_type, phrasing=False):
"""
Set the slur start or stop for the current note. """
self.current_note.set_slur(nr, slur_type)
Set the slur start or stop for the current note.
phrasing should be set to True if the slur is meant to be a phrasing mark.
"""

slur_start = None

if slur_type == 'stop':
slur_start = self.slur_stack.pop()

self.current_note.set_slur(nr, slur_type, phrasing, slur_start)

if slur_type == 'start':
self.slur_stack.append(self.current_note.slur[-1])

def new_articulation(self, art_token):
"""
Expand Down
4 changes: 2 additions & 2 deletions ly/musicxml/lymus2musxml.py
Original file line number Diff line number Diff line change
Expand Up @@ -429,9 +429,9 @@ def PhrasingSlur(self, phrslur):
if phrslur.token == '\(':
self.slurcount += 1
self.phrslurnr = self.slurcount
self.mediator.set_slur(self.phrslurnr, "start")
self.mediator.set_slur(self.phrslurnr, "start", phrasing=True)
elif phrslur.token == '\)':
self.mediator.set_slur(self.phrslurnr, "stop")
self.mediator.set_slur(self.phrslurnr, "stop", phrasing=True)
self.slurcount -= 1

def Dynamic(self, dynamic):
Expand Down
57 changes: 50 additions & 7 deletions ly/musicxml/xml_objs.py
Original file line number Diff line number Diff line change
Expand Up @@ -312,20 +312,34 @@ def merge_voice(self, voice, override=False):
part.merge_voice(voice, override)


class SlurCount:
"""Utility class meant for keeping count of started slurs in a section"""
def __init__(self):
self.count = 0

def inc(self):
self.count += 1

def dec(self):
self.count -= 1

class ScoreSection():
""" object to keep track of music section """
def __init__(self, name, glob=False):
self.name = name
self.barlist = []
self.glob = glob

# Keeps track of the number of started slurs in the section
self.active_slur_count = SlurCount()

def __repr__(self):
return '<{0} {1}>'.format(self.__class__.__name__, self.name)

def merge_voice(self, voice, override=False):
"""Merge in other ScoreSection."""
for org_v, add_v in zip(self.barlist, voice.barlist):
org_v.inject_voice(add_v, override)
org_v.inject_voice(add_v, override, self.active_slur_count)
bl_len = len(self.barlist)
if len(voice.barlist) > bl_len:
self.barlist += voice.barlist[bl_len:]
Expand Down Expand Up @@ -500,7 +514,7 @@ def is_skip(self, obj_list=None):
return False
return True

def inject_voice(self, new_voice, override=False):
def inject_voice(self, new_voice, override=False, active_slur_count=None):
""" Adding new voice to bar.
Omitting double or conflicting bar attributes as long as override is false.
Omitting also bars with only skips."""
Expand All @@ -519,9 +533,36 @@ def inject_voice(self, new_voice, override=False):
pass
if not self.is_skip(backup_list):
self.create_backup()

if active_slur_count:
# Update active_slur_count wrt to already existing slur starts
# and slur ends in the bar, before we add backup_list

for n in self.obj_list:
if isinstance(n, BarNote):
for slur in n.slur:
if slur.slurtype == 'start':
active_slur_count.inc()
elif slur.slurtype == 'stop':
active_slur_count.dec()

for bl in backup_list:
self.add(bl)

if active_slur_count and isinstance(bl, BarNote):
# If the slur is starting: increase active_slur_count and set slur number
# to that value.
# If the slur is ending: set slur number to be the same as the origin slur number.

for slur in bl.slur:
if slur.slurtype == 'start':
active_slur_count.inc()
slur.nr = active_slur_count.count
elif slur.slurtype == 'stop':
active_slur_count.dec()

if slur.start_node:
slur.nr = slur.start_node.nr

class BarMus():
""" Common class for notes and rests. """
Expand Down Expand Up @@ -621,11 +662,13 @@ def __init__(self, fraction, ttype, nr, acttype, normtype):
self.normtype = normtype

class Slur():
"""Stores information about slur."""
def __init__(self, nr, slurtype):
"""Stores information about slur. start_node is only interesting if slurtype is 'stop'.
start_node must be None or a Slur instance."""
def __init__(self, nr, slurtype, phrasing=False, start_node=None):
self.nr = nr
self.slurtype = slurtype

self.phrasing = phrasing
self.start_node = start_node

##
# Subclasses of BarMus
Expand Down Expand Up @@ -668,8 +711,8 @@ def set_octave(self, octave):
def set_tie(self, tie_type):
self.tie.append(tie_type)

def set_slur(self, nr, slur_type):
self.slur.append(Slur(nr, slur_type))
def set_slur(self, nr, slur_type, phrasing=False, slur_start_node=None):
self.slur.append(Slur(nr, slur_type, phrasing, slur_start_node))

def add_articulation(self, art_name):
self.artic.append(art_name)
Expand Down
2 changes: 2 additions & 0 deletions tests/test_xml.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ def test_dynamics():
def test_tuplet():
compare_output('tuplet')

def test_merge_voice_slurs():
compare_output('merge_voice_slurs')

def test_break():
compare_output('break')
Expand Down
37 changes: 37 additions & 0 deletions tests/test_xml_files/merge_voice_slurs.ly
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
\version "2.19.55"

\header {
title = "merge voices with slurs"
}

sopranoOne = \relative c'' {
% Music follows here.
c1( | c) |
c( | c) |
c( | c) |

c2( d) |

c4\( r2 d4( | e) r2 c4\) |
}

sopranoTwo = \relative c'' {
% Music follows here.
c1( | c) |
r2 c2( | c2) r2 |
r2 c2( | r2 c2) |

r4 e( f) r4 |

r4 c4\( d( r4 | r4 d) c4\) r4 |
}

\score {
\new ChoirStaff <<
\new Staff <<
\new Voice = "soprano1" { \voiceOne \sopranoOne }
\new Voice = "soprano2" { \voiceTwo \sopranoTwo }
>>
>>
\layout { }
}
Loading