Skip to content

Commit 751d1c6

Browse files
authored
Merge pull request #111 from FelippeRoza/stem-direction
MusicXML: stem direction export
2 parents 144dcca + 202be6d commit 751d1c6

7 files changed

Lines changed: 133 additions & 2 deletions

File tree

ly/musicxml/create_musicxml.py

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -126,7 +126,7 @@ def create_measure(self, pickup = False, **bar_attrs):
126126
##
127127

128128
def new_note(self, step, octave, durtype, divdur, alter=0,
129-
acc_token=0, voice=1, dot=0, chord=0, grace=(0, 0)):
129+
acc_token=0, voice=1, dot=0, chord=0, grace=(0, 0), stem_dir=0):
130130
"""Create all nodes needed for a normal note. """
131131
self.create_note()
132132
if grace[0]:
@@ -148,6 +148,8 @@ def new_note(self, step, octave, durtype, divdur, alter=0,
148148
self.add_accidental(alter, parenth=True)
149149
else:
150150
self.add_accidental(alter)
151+
if stem_dir:
152+
self.set_stem_dir(stem_dir)
151153

152154
def new_unpitched_note(self, step, octave, durtype, divdur, voice=1,
153155
dot=0, chord=0, grace=(0, 0)):
@@ -317,6 +319,10 @@ def add_accidental(self, alter, caut=False, parenth=False):
317319
}
318320
acc.text = acc_dict[alter]
319321

322+
def set_stem_dir(self, dir):
323+
stem_dir = etree.SubElement(self.current_note, "stem")
324+
stem_dir.text = dir
325+
320326
def add_rest(self):
321327
"""Create rest."""
322328
etree.SubElement(self.current_note, "rest")

ly/musicxml/ly2xml_mediator.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,7 @@ def __init__(self):
7777
self.tupl_dur = 0
7878
self.tupl_sum = 0
7979
self.bar_is_pickup = False
80+
self.stem_dir = None
8081

8182
def new_header_assignment(self, name, value):
8283
"""Distributing header information."""
@@ -385,6 +386,8 @@ def new_note(self, note, rel=False, is_unpitched=False):
385386
self.current_note = self.create_barnote_from_note(note)
386387
self.current_lynote = note
387388
self.check_current_note(rel)
389+
if self.stem_dir:
390+
self.current_note.set_stem_direction(self.stem_dir)
388391
self.do_action_onnext(self.current_note)
389392
self.action_onnext = []
390393

@@ -457,6 +460,14 @@ def check_current_note(self, rel=False, rest=False, is_unpitched=False):
457460
self.staff_unset_notes[self.staff] = [self.current_note]
458461
self.add_to_bar(self.current_note)
459462

463+
def stem_direction(self, direction):
464+
if direction == '\\stemUp':
465+
self.stem_dir = 'up'
466+
elif direction == '\\stemDown':
467+
self.stem_dir = 'down'
468+
elif direction == '\\stemNeutral':
469+
self.stem_dir = None
470+
460471
def set_octave(self, relative):
461472
"""Set octave by getting the octave of an absolute note + 3."""
462473
p = self.current_lynote.pitch.copy()

ly/musicxml/lymus2musxml.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -500,6 +500,8 @@ def Command(self, command):
500500
self.mediator.new_trill_spanner("stop")
501501
elif command.token == '\\ottava':
502502
self.ottava = True
503+
elif command.token == '\\stemUp' or command.token == '\\stemDown' or command.token == '\\stemNeutral':
504+
self.mediator.stem_direction(command.token)
503505
elif command.token == '\\default':
504506
if self.tupl_span:
505507
self.mediator.unset_tuplspan_dur()

ly/musicxml/xml_objs.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -179,7 +179,7 @@ def new_xml_note(self, obj):
179179
else:
180180
self.musxml.new_note(obj.base_note, obj.octave, obj.type, divdur,
181181
obj.alter, obj.accidental_token, obj.voice, obj.dot, obj.chord,
182-
obj.grace)
182+
obj.grace, obj.stem_direction)
183183
for t in obj.tie:
184184
self.musxml.tie_note(t)
185185
for s in obj.slur:
@@ -626,6 +626,7 @@ def __init__(self, pitch_note, alter, accidental, duration, voice=1):
626626
self.adv_ornament = None
627627
self.fingering = None
628628
self.lyric = None
629+
self.stem_direction = None
629630

630631
def set_duration(self, duration, durtype=''):
631632
self.duration = duration
@@ -668,6 +669,9 @@ def set_tremolo(self, trem_type, duration=False):
668669
else:
669670
self.tremolo = (trem_type, self.tremolo[1])
670671

672+
def set_stem_direction(self, direction):
673+
self.stem_direction = direction
674+
671675
def add_fingering(self, finger_nr):
672676
self.fingering = finger_nr
673677

tests/test_xml.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,10 @@ def test_partial():
3535
compare_output('partial')
3636

3737

38+
def test_stem_direction():
39+
compare_output('stem')
40+
41+
3842
def ly_to_xml(filename):
3943
"""Read Lilypond file and return XML string."""
4044
writer = ly.musicxml.writer()

tests/test_xml_files/stem.ly

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
\version "2.18.2"
2+
3+
\score {
4+
{
5+
\stemUp g4
6+
f4
7+
\stemDown g4
8+
f4 |
9+
\stemNeutral g4
10+
f4
11+
}
12+
\layout {}
13+
}

tests/test_xml_files/stem.xml

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<!DOCTYPE score-partwise PUBLIC "-//Recordare//DTD MusicXML 2.0 Partwise//EN"
3+
"http://www.musicxml.org/dtds/partwise.dtd">
4+
<score-partwise version="3.0">
5+
<identification>
6+
<encoding>
7+
<software>python-ly 0.9.5</software>
8+
<encoding-date>2017-07-16</encoding-date>
9+
</encoding>
10+
</identification>
11+
<part-list>
12+
<score-part id="P1">
13+
<part-name />
14+
</score-part>
15+
</part-list>
16+
<part id="P1">
17+
<measure number="1">
18+
<attributes>
19+
<divisions>1</divisions>
20+
<time symbol="common">
21+
<beats>4</beats>
22+
<beat-type>4</beat-type>
23+
</time>
24+
<clef>
25+
<sign>G</sign>
26+
<line>2</line>
27+
</clef>
28+
</attributes>
29+
<note>
30+
<pitch>
31+
<step>G</step>
32+
<octave>3</octave>
33+
</pitch>
34+
<duration>1</duration>
35+
<voice>1</voice>
36+
<type>quarter</type>
37+
<stem>up</stem>
38+
</note>
39+
<note>
40+
<pitch>
41+
<step>F</step>
42+
<octave>3</octave>
43+
</pitch>
44+
<duration>1</duration>
45+
<voice>1</voice>
46+
<type>quarter</type>
47+
<stem>up</stem>
48+
</note>
49+
<note>
50+
<pitch>
51+
<step>G</step>
52+
<octave>3</octave>
53+
</pitch>
54+
<duration>1</duration>
55+
<voice>1</voice>
56+
<type>quarter</type>
57+
<stem>down</stem>
58+
</note>
59+
<note>
60+
<pitch>
61+
<step>F</step>
62+
<octave>3</octave>
63+
</pitch>
64+
<duration>1</duration>
65+
<voice>1</voice>
66+
<type>quarter</type>
67+
<stem>down</stem>
68+
</note>
69+
</measure>
70+
<measure number="2">
71+
<note>
72+
<pitch>
73+
<step>G</step>
74+
<octave>3</octave>
75+
</pitch>
76+
<duration>1</duration>
77+
<voice>1</voice>
78+
<type>quarter</type>
79+
</note>
80+
<note>
81+
<pitch>
82+
<step>F</step>
83+
<octave>3</octave>
84+
</pitch>
85+
<duration>1</duration>
86+
<voice>1</voice>
87+
<type>quarter</type>
88+
</note>
89+
</measure>
90+
</part>
91+
</score-partwise>

0 commit comments

Comments
 (0)