You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用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)

修改说明

  1. Tuplet容器化管理:所有连音内的音符都加入同一个Tuplet对象,music21会自动计算每个音符的实际时长,避免出现十六分音符的错误
  2. 参数精准匹配需求:numberNotesNormal=2对应2/4拍的2拍时值,setDurationType('eighth')确保音符显示为八分音符
  3. 小节逻辑简化:仅在开始新连音组时创建小节,保证每个小节包含完整的五连音结构

修正后生成的XML中,音符<type>会是eighth,<time-modification>参数会匹配预期的5对2连音规则。

内容的提问来源于stack exchange,提问作者Lee Hampton

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.01 21:49:50