如何修复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
相关产品推荐
相关产品推荐

