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

如何修复Abjad中休止符的异常分组问题?

问题

使用Abjad生成四分音符、八分音符、十六分音符与休止符的随机组合时,记谱可读性差,核心问题是休止符分组不合理——比如两个连续八分休止符未合并为四分休止符,八分+十六分休止符未合并为附点八分休止符等。现有两种优化思路:

  • 生成过程中跟踪前后休止符并简化
  • 先生成再检查简化休止符分组

以下是示例代码:

import random
import abjad

def random_notes():
    noteDuration = [4, 8, 16]
    beatCount = 0. # Keep track of how much space is left in a beat

    returnArray = []

    while beatCount < 1:
        validNoteValueFound = False
        noteVal = 0
        dur = 0
        while not validNoteValueFound:
            dur = random.randrange(0, 3)
            noteVal = 1. / noteDuration[dur]
            remainingSpace = 1. - beatCount
            if noteVal <= remainingSpace:
                # if note fits
                validNoteValueFound = True

        returnArray.append(noteType() + str(noteDuration[dur]))
        beatCount += noteVal

    return " ".join(returnArray)

def noteType():
    if random.randrange(0, 2) == 1:
        return "c"
    else:
        return "r"
def generateMeasures(numberOfMeasures):
    measures = []
    for i in range(0, numberOfMeasures):
        measures.append(random_notes())

    return "".join(flattenStringArray(measures))


def flattenList(list):
    flattenedList = []
    for i in list:
        for j in i:
            flattenedList.append(j)

    return flattenedList

def flattenStringArray(stringArr):
    return "".join(stringArr)

# Display randomly generated measures
staff = abjad.Staff(generateMeasures(10), lilypond_type="RhythmicStaff")
abjad.show(staff)

示例输出存在休止符分组混乱问题,连续小时值休止符未合并为更简洁的时值。

解决方案

方法一:生成过程中合并休止符

生成时维护当前连续休止符的累计时长,遇到音符时先输出合并后的休止符,再处理音符;遇到休止符则累加时长,直到无法合并或遇到音符/小节结束。

修改后的核心代码示例:

def random_notes():
    noteDuration = [4, 8, 16]
    beatCount = 0.
    returnArray = []
    current_rest_duration = 0.  # 跟踪当前连续休止符总时长

    while beatCount < 1:
        validNoteValueFound = False
        noteVal = 0
        dur = 0
        while not validNoteValueFound:
            dur = random.randrange(0, 3)
            noteVal = 1. / noteDuration[dur]
            remainingSpace = 1. - beatCount
            if noteVal <= remainingSpace:
                validNoteValueFound = True

        is_rest = random.randrange(0, 2) == 0
        if is_rest:
            current_rest_duration += noteVal
            beatCount += noteVal
        else:
            # 先处理累计的休止符
            if current_rest_duration > 0:
                rest_str = get_simplest_rest(current_rest_duration)
                returnArray.append(rest_str)
                current_rest_duration = 0.
            # 添加音符
            returnArray.append(f"c{noteDuration[dur]}")
            beatCount += noteVal

    # 处理小节末尾剩余的休止符
    if current_rest_duration > 0:
        rest_str = get_simplest_rest(current_rest_duration)
        returnArray.append(rest_str)

    return " ".join(returnArray)

def get_simplest_rest(duration):
    # 映射时长到最简休止符表示
    duration_map = {
        1.0: "r4",
        0.5: "r8",
        0.25: "r16",
        0.75: "r8.",  # 附点八分休止符(八分+十六分)
    }
    return duration_map.get(duration, "r16")

方法二:生成后批量优化休止符

先按原有逻辑生成完整节奏序列,再遍历序列合并连续休止符,替换为最简形式。Abjad的Duration对象会自动处理时值简化,比如Duration(3/8)会对应附点八分休止符。

核心代码示例:

def simplify_rests(staff):
    for component in staff[:]:
        if isinstance(component, abjad.Measure):
            elements = component[:]
            new_elements = []
            current_rest_duration = abjad.Duration(0)

            for elem in elements:
                if isinstance(elem, abjad.Rest):
                    current_rest_duration += elem.written_duration
                else:
                    # 合并休止符并添加到新列表
                    if current_rest_duration > 0:
                        simplest_rest = abjad.Rest(current_rest_duration)
                        new_elements.append(simplest_rest)
                        current_rest_duration = abjad.Duration(0)
                    new_elements.append(elem)

            # 处理小节末尾的休止符
            if current_rest_duration > 0:
                simplest_rest = abjad.Rest(current_rest_duration)
                new_elements.append(simplest_rest)

            # 替换小节内的元素
            component[:] = new_elements

# 生成Staff后调用优化函数
staff = abjad.Staff(generateMeasures(10), lilypond_type="RhythmicStaff")
simplify_rests(staff)
abjad.show(staff)

方案对比

  • 方法一效率更高,生成时直接避免冗余休止符,无需后续遍历
  • 方法二更灵活,无需修改原有生成逻辑,适合已有成熟生成代码的场景

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.07 15:17:21