Skip to content

Commit 5837f41

Browse files
committed
Fix MusicXML export losing measure boundaries with chords
Chords were not incrementing the bar duration counter, causing measure boundaries to be lost in MusicXML export. A 3-bar piece with chords would incorrectly export as 2 bars. Two functions affected: - new_chordbase(): explicit chord notation <c e g> - copy_prev_chord(): duration shorthand after chords (4 4) Added self.increase_bar_dura(duration) to both functions to match the behavior of regular notes. Fixes #171
1 parent 086e55f commit 5837f41

4 files changed

Lines changed: 275 additions & 0 deletions

File tree

ly/musicxml/ly2xml_mediator.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -564,6 +564,7 @@ def new_chordbase(self, note, duration, rel=False):
564564
self.current_note.set_duration(duration)
565565
self.current_lynote = note
566566
self.check_current_note(rel)
567+
self.increase_bar_dura(duration)
567568

568569
def new_chordnote(self, note, rel):
569570
chord_note = self.create_barnote_from_note(note)
@@ -600,6 +601,7 @@ def copy_prev_chord(self, duration):
600601
cn.set_tie('stop')
601602
self.bar.add(cn)
602603
self.tied = False
604+
self.increase_bar_dura(duration)
603605

604606
def clear_chord(self):
605607
self.q_chord = self.current_chord

tests/test_xml.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,10 @@ def test_no_barcheck():
7676
compare_output('no_barcheck')
7777

7878

79+
def test_chord_duration():
80+
compare_output('chord_duration')
81+
82+
7983
def ly_to_xml(filename):
8084
"""Read Lilypond file and return XML string."""
8185
writer = ly.musicxml.writer()
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
\version "2.24.0"
2+
3+
% Test chord duration counting for bar boundaries (issue #171)
4+
% Both explicit chords and duration shorthand must count toward measure duration
5+
6+
\score {
7+
<<
8+
% Explicit chord notation: 3 measures
9+
\new Staff { c''1 | <d'' f'' a''>1 | e''1 }
10+
11+
% Duration shorthand after chord: 3 measures
12+
\new Staff { \time 3/4 <e' f'>4 4 4 | 4 4 8 r8 | r4 a' g' }
13+
>>
14+
\layout { }
15+
}
Lines changed: 254 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,254 @@
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.9</software>
8+
<encoding-date>2026-01-26</encoding-date>
9+
</encoding>
10+
</identification>
11+
<part-list>
12+
<score-part id="P1">
13+
<part-name />
14+
</score-part>
15+
<score-part id="P2">
16+
<part-name />
17+
</score-part>
18+
</part-list>
19+
<part id="P1">
20+
<measure number="1">
21+
<attributes>
22+
<divisions>2</divisions>
23+
<time>
24+
<beats>3</beats>
25+
<beat-type>4</beat-type>
26+
</time>
27+
<clef>
28+
<sign>G</sign>
29+
<line>2</line>
30+
</clef>
31+
</attributes>
32+
<note>
33+
<pitch>
34+
<step>C</step>
35+
<octave>5</octave>
36+
</pitch>
37+
<duration>8</duration>
38+
<voice>1</voice>
39+
<type>whole</type>
40+
</note>
41+
<backup>
42+
<duration>8</duration>
43+
</backup>
44+
</measure>
45+
<measure number="2">
46+
<note>
47+
<pitch>
48+
<step>D</step>
49+
<octave>5</octave>
50+
</pitch>
51+
<duration>8</duration>
52+
<voice>1</voice>
53+
<type>whole</type>
54+
</note>
55+
</measure>
56+
<measure number="3">
57+
<note>
58+
<chord />
59+
<pitch>
60+
<step>F</step>
61+
<octave>5</octave>
62+
</pitch>
63+
<duration>8</duration>
64+
<voice>1</voice>
65+
<type>whole</type>
66+
</note>
67+
<note>
68+
<chord />
69+
<pitch>
70+
<step>A</step>
71+
<octave>5</octave>
72+
</pitch>
73+
<duration>8</duration>
74+
<voice>1</voice>
75+
<type>whole</type>
76+
</note>
77+
<note>
78+
<pitch>
79+
<step>E</step>
80+
<octave>5</octave>
81+
</pitch>
82+
<duration>8</duration>
83+
<voice>1</voice>
84+
<type>whole</type>
85+
</note>
86+
</measure>
87+
</part>
88+
<part id="P2">
89+
<measure number="1">
90+
<attributes>
91+
<divisions>2</divisions>
92+
<time>
93+
<beats>3</beats>
94+
<beat-type>4</beat-type>
95+
</time>
96+
<clef>
97+
<sign>G</sign>
98+
<line>2</line>
99+
</clef>
100+
</attributes>
101+
<note>
102+
<pitch>
103+
<step>E</step>
104+
<octave>4</octave>
105+
</pitch>
106+
<duration>2</duration>
107+
<voice>1</voice>
108+
<type>quarter</type>
109+
</note>
110+
<note>
111+
<chord />
112+
<pitch>
113+
<step>F</step>
114+
<octave>4</octave>
115+
</pitch>
116+
<duration>2</duration>
117+
<voice>1</voice>
118+
<type>quarter</type>
119+
</note>
120+
<note>
121+
<pitch>
122+
<step>E</step>
123+
<octave>4</octave>
124+
</pitch>
125+
<duration>2</duration>
126+
<voice>1</voice>
127+
<type>quarter</type>
128+
</note>
129+
<note>
130+
<chord />
131+
<pitch>
132+
<step>F</step>
133+
<octave>4</octave>
134+
</pitch>
135+
<duration>2</duration>
136+
<voice>1</voice>
137+
<type>quarter</type>
138+
</note>
139+
<note>
140+
<pitch>
141+
<step>E</step>
142+
<octave>4</octave>
143+
</pitch>
144+
<duration>2</duration>
145+
<voice>1</voice>
146+
<type>quarter</type>
147+
</note>
148+
<note>
149+
<chord />
150+
<pitch>
151+
<step>F</step>
152+
<octave>4</octave>
153+
</pitch>
154+
<duration>2</duration>
155+
<voice>1</voice>
156+
<type>quarter</type>
157+
</note>
158+
<backup>
159+
<duration>6</duration>
160+
</backup>
161+
</measure>
162+
<measure number="2">
163+
<note>
164+
<pitch>
165+
<step>E</step>
166+
<octave>4</octave>
167+
</pitch>
168+
<duration>2</duration>
169+
<voice>1</voice>
170+
<type>quarter</type>
171+
</note>
172+
<note>
173+
<chord />
174+
<pitch>
175+
<step>F</step>
176+
<octave>4</octave>
177+
</pitch>
178+
<duration>2</duration>
179+
<voice>1</voice>
180+
<type>quarter</type>
181+
</note>
182+
<note>
183+
<pitch>
184+
<step>E</step>
185+
<octave>4</octave>
186+
</pitch>
187+
<duration>2</duration>
188+
<voice>1</voice>
189+
<type>quarter</type>
190+
</note>
191+
<note>
192+
<chord />
193+
<pitch>
194+
<step>F</step>
195+
<octave>4</octave>
196+
</pitch>
197+
<duration>2</duration>
198+
<voice>1</voice>
199+
<type>quarter</type>
200+
</note>
201+
<note>
202+
<pitch>
203+
<step>E</step>
204+
<octave>4</octave>
205+
</pitch>
206+
<duration>1</duration>
207+
<voice>1</voice>
208+
<type>eighth</type>
209+
</note>
210+
<note>
211+
<chord />
212+
<pitch>
213+
<step>F</step>
214+
<octave>4</octave>
215+
</pitch>
216+
<duration>1</duration>
217+
<voice>1</voice>
218+
<type>eighth</type>
219+
</note>
220+
<note>
221+
<rest />
222+
<duration>1</duration>
223+
<voice>1</voice>
224+
<type>eighth</type>
225+
</note>
226+
</measure>
227+
<measure number="3">
228+
<note>
229+
<rest />
230+
<duration>2</duration>
231+
<voice>1</voice>
232+
<type>quarter</type>
233+
</note>
234+
<note>
235+
<pitch>
236+
<step>A</step>
237+
<octave>4</octave>
238+
</pitch>
239+
<duration>2</duration>
240+
<voice>1</voice>
241+
<type>quarter</type>
242+
</note>
243+
<note>
244+
<pitch>
245+
<step>G</step>
246+
<octave>4</octave>
247+
</pitch>
248+
<duration>2</duration>
249+
<voice>1</voice>
250+
<type>quarter</type>
251+
</note>
252+
</measure>
253+
</part>
254+
</score-partwise>

0 commit comments

Comments
 (0)