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

