使用music21生成八分音符五连音遇XML导出异常求助
问题
我正在使用music21为指定音符序列添加连音划分,例如在2/4拍号下,针对序列CEGBCAFD创建5对2的五连音(即5个八分音符填充原本2拍的时值)。以下是我编写的代码:
for i, note_name in enumerate(notes): # Create a note n = note.Note(note_name) # Set note duration to eighth and within a tuplet n.duration = duration.Duration('eighth') # Create tuplet if needed and add it to note's duration if tuplet_counter % rhythm == 0: tup = duration.Tuplet(numberNotesActual=rhythm, numberNotesNormal=2, type='quarter') tup.setDurationType('eighth') # Set the tuplet's note type to eighth n.duration.appendTuplet(tup) # Add accent if the note is in the accents list if note_name in accents: n.articulations.append(articulations.Accent()) #measure.makeBeams(inPlace=True) measure.append(n) tuplet_counter += 1 # If we reach the rhythm count or the end of the notes, reset the counter and append the measure if tuplet_counter == rhythm or note_name == notes[-1]: part.append(measure) measure = stream.Measure() # Create a new measure for the next group tuplet_counter = 0 if (i + 1) < len(notes): # If there are more notes, set the attributes for the new measure measure.append(key.Key(key_signature)) measure.append(meter.TimeSignature(time_signature)) # Add the part to the score s.append(part)
代码能生成5音连音,但输出的XML把音符设为十六分音符,导致呈现出10对2的效果,而非预期的5个八分音符组成的单连音。XML片段如下:
<note> <pitch> <step>C</step> <octave>4</octave> </pitch> <duration>2016</duration> <type>16th</type> <time-modification> <actual-notes>5</actual-notes> <normal-notes>4</normal-notes> <normal-type>16th</normal-type> </time-modification> <beam number="1">begin</beam>
解决方案
问题根源在于Tuplet的参数设置和音符关联逻辑错误:单独给第一个音符追加Tuplet会导致music21错误计算时长,且没有让所有连音内的音符共享同一个Tuplet容器。以下是修正方案:
核心修改点
- 将Tuplet作为容器使用,把所有连音内的音符添加到Tuplet中,而非仅给单个音符追加Tuplet
- 明确Tuplet的参数:
numberNotesActual=5(实际音符数),numberNotesNormal=2(替代的标准音符数),type='quarter'(替代的时值类型,2个四分音符对应2/4拍的整小节时值) - 优化小节创建逻辑,确保每个小节仅包含一个完整的五连音
修正后的代码
for i, note_name in enumerate(notes): n = note.Note(note_name) # 初始化新的连音组和小节(仅当开始新组时) if tuplet_counter == 0: # 5个音符替代2个四分音符(2拍),指定连音内音符为八分音符 tup = duration.Tuplet(numberNotesActual=5, numberNotesNormal=2, type='quarter') tup.setDurationType('eighth') # 创建新小节并添加属性 measure = stream.Measure() measure.append(key.Key(key_signature)) measure.append(meter.TimeSignature(time_signature)) # 添加重音标记 if note_name in accents: n.articulations.append(articulations.Accent()) # 将音符加入连音容器 tup.append(n) tuplet_counter += 1 # 当凑够5个音符或到序列末尾时,完成当前小节 if tuplet_counter == 5 or note_name == notes[-1]: measure.append(tup) part.append(measure) tuplet_counter = 0 # 将声部加入乐谱 s.append(part)
修改说明
- Tuplet容器化管理:所有连音内的音符都加入同一个Tuplet对象,music21会自动计算每个音符的实际时长,避免出现十六分音符的错误
- 参数精准匹配需求:
numberNotesNormal=2对应2/4拍的2拍时值,setDurationType('eighth')确保音符显示为八分音符 - 小节逻辑简化:仅在开始新连音组时创建小节,保证每个小节包含完整的五连音结构
修正后生成的XML中,音符<type>会是eighth,<time-modification>参数会匹配预期的5对2连音规则。
内容的提问来源于stack exchange,提问作者Lee Hampton
相关产品推荐
相关产品推荐

