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

如何在Python中读取含非默认弯音范围的MIDI音高?

读取MIDI中非默认弯音范围的音高方法

MIDI弯音轮默认对应±2半音的范围,但可通过RPN(注册参数编号)消息修改:先通过CC101(RPN MSB)和CC100(RPN LSB)选中编号为0的RPN(对应弯音范围),再用CC6(Data Entry MSB)设置新的半音范围值。由于pretty_midi和mido默认不会自动追踪这类RPN消息,需要手动处理来计算实际音高。

针对mido库的处理方式

mido可直接遍历所有MIDI消息,手动维护当前弯音范围状态:

  • 初始化默认弯音范围为2,同时追踪当前选中的RPN编号
  • 遍历消息时,识别RPN相关的CC消息,更新当前选中的RPN
  • 当选中的是弯音范围对应的RPN(MSB=0,LSB=0)时,后续的CC6消息值就是新的弯音范围
  • 处理弯音轮(pitchwheel)消息时,用公式计算实际偏移:
    偏移半音数 = ((弯音值 - 8192) / 8192) * 当前弯音范围

示例代码:

import mido

def parse_midi_with_custom_bend_range(file_path):
    current_bend_range = 2  # MIDI默认弯音范围
    rpn_msb = None
    rpn_lsb = None
    bend_range_rpn = (0, 0)  # 弯音范围对应的RPN编号

    mid = mido.MidiFile(file_path)
    for track in mid.tracks:
        for msg in track:
            if msg.type == 'control_change':
                # 更新当前选中的RPN编号
                if msg.control == 101:
                    rpn_msb = msg.value
                elif msg.control == 100:
                    rpn_lsb = msg.value
                # 处理弯音范围的参数设置
                elif msg.control == 6 and (rpn_msb, rpn_lsb) == bend_range_rpn:
                    current_bend_range = msg.value
            elif msg.type == 'pitchwheel':
                # 计算实际弯音偏移
                bend_offset = ((msg.pitch - 8192) / 8192) * current_bend_range
                print(f"弯音偏移: {bend_offset:.2f} 半音")
            # 可在此添加音符消息的处理逻辑

针对pretty_midi库的处理方式

pretty_midi没有内置的RPN追踪机制,需要遍历乐器的控制消息和弯音消息,手动维护状态:

  • 遍历乐器的control_changes列表,识别RPN相关的CC消息,更新弯音范围
  • 处理pitch_bends消息时,用同样的公式计算偏移;若要匹配音符的实际音高,需要根据时间戳关联对应的弯音值

示例代码:

import pretty_midi

def process_midi_notes_with_custom_bend(file_path):
    pm = pretty_midi.PrettyMIDI(file_path)
    for instrument in pm.instruments:
        current_bend_range = 2
        rpn_msb = None
        rpn_lsb = None
        bend_range_rpn = (0, 0)

        # 先遍历控制消息,确定当前弯音范围
        for cc in instrument.control_changes:
            if cc.number == 101:
                rpn_msb = cc.value
            elif cc.number == 100:
                rpn_lsb = cc.value
            elif cc.number == 6 and (rpn_msb, rpn_lsb) == bend_range_rpn:
                current_bend_range = cc.value

        # 处理音符与对应的弯音偏移
        for note in instrument.notes:
            # 找到音符开始前最后一次的弯音消息
            relevant_bends = [b for b in instrument.pitch_bends if b.time <= note.start]
            if relevant_bends:
                last_bend = relevant_bends[-1]
                bend_offset = ((last_bend.pitch - 8192) / 8192) * current_bend_range
                actual_pitch = note.pitch + bend_offset
                print(f"音符 {pretty_midi.note_number_to_name(note.pitch)} 实际音高: {actual_pitch:.2f}")
            else:
                print(f"音符 {pretty_midi.note_number_to_name(note.pitch)} 无弯音偏移")

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.25 17:37:02