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

如何用music21实现和弦的平滑声部连接(最小化声部距离)

基于music21实现和弦声部平滑连接的解决方案

要实现和弦间声部路径最短的转位优化,核心思路是为每个和弦生成所有合理的转位+八度移位变体,计算每个变体与前序和弦的声部距离总和,选择距离最小的变体作为最终使用的和弦。

具体实现步骤

  • 先生成初始的原位和弦流,保留每个和弦的罗马数字标识与调式信息,用于后续生成变体
  • 定义声部距离计算函数:将两个和弦的音按MIDI编号排序,对齐声部后计算对应音高差值的绝对值之和,总和越小代表声部移动越平滑
  • 为每个和弦生成所有合理变体:包含原位、第一转位、第二转位,以及每个转位上下1-2个八度的移位版本(控制声部在合理音域内)
  • 遍历和弦序列,从第一个和弦开始,为每个后续和弦筛选出与前一个选定和弦距离最小的变体,构建最终的平滑和弦流

完整代码示例

from music21 import stream, roman, key, chord

def calculate_voice_distance(chord1, chord2):
    # 将两个和弦的音按MIDI编号排序,对齐声部计算距离总和
    notes1 = sorted(n.midi for n in chord1.pitches)
    notes2 = sorted(n.midi for n in chord2.pitches)
    return sum(abs(n1 - n2) for n1, n2 in zip(notes1, notes2))

def generate_chord_variants(roman_numeral, target_key):
    # 生成当前罗马数字和弦的所有合理转位与八度移位变体
    variants = []
    # 遍历所有转位(0=原位,1=第一转位,2=第二转位)
    for inversion in [0, 1, 2]:
        base_chord = roman.RomanNumeral(roman_numeral, target_key).transpose(0)
        inverted_chord = base_chord.invert(inversion)
        # 生成上下1-2个八度的移位版本,控制音域在MIDI 40-80之间
        for octave_shift in [-12, 0, 12]:
            shifted_chord = inverted_chord.transpose(octave_shift)
            # 检查和弦最低音和最高音是否在合理范围
            min_midi = min(n.midi for n in shifted_chord.pitches)
            max_midi = max(n.midi for n in shifted_chord.pitches)
            if 40 <= min_midi and max_midi <= 80:
                variants.append(shifted_chord)
    return variants

# 初始罗马数字序列
numeral_list = ['i', 'vi', 'I', '#iii', '#iii', 'V', 'i', 'vii', 'I', 'I', 'vi', 'vii']
target_key = key.Key('C')

# 初始化最终流,第一个和弦用原位
final_stream = stream.Stream()
prev_chord = roman.RomanNumeral(numeral_list[0], target_key)
final_stream.append(prev_chord)

# 遍历后续和弦,选择最优变体
for numeral in numeral_list[1:]:
    variants = generate_chord_variants(numeral, target_key)
    # 计算每个变体与前一个和弦的距离,选最小的
    min_distance = float('inf')
    best_variant = None
    for variant in variants:
        distance = calculate_voice_distance(prev_chord, variant)
        if distance < min_distance:
            min_distance = distance
            best_variant = variant
    # 将最优变体加入流,并更新前一个和弦
    final_stream.append(best_variant)
    prev_chord = best_variant

# 展示结果
final_stream.show()

代码说明

  • calculate_voice_distance函数:通过对齐两个和弦的声部排序,确保声部对应关系合理,计算移动距离总和
  • generate_chord_variants函数:生成所有可能的转位和八度移位,同时过滤掉音域不合理的和弦,避免声部过高或过低
  • 主逻辑:从第一个原位和弦开始,逐个为后续和弦筛选出移动距离最小的变体,实现平滑的声部连接

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.24 00:47:24