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

如何用Python分离钢琴MIDI轨的左右手音符?

分离MIDI钢琴轨左右手音符的实现方案

MuseScore自动拆分左右手的逻辑

MuseScore并没有从MIDI文件中读取现成的左右手标识,而是通过启发式规则自动判断:

  • 核心依据是音高范围:默认以中央C(MIDI编号60)为分界,低于60的音符归为左手声部,高于或等于的归为右手
  • 同时结合音符的时值、小节位置、声部布局逻辑做调整,比如跨中央C的和弦会根据演奏习惯分配到对应声部,避免谱表混乱

在Python中实现左右手分离的两种方法

方法1:基于音高阈值(快速简便)

直接以中央C为分界拆分音符,适合大多数常规钢琴谱:

import mido
from mido import MidiFile

def split_hands_by_pitch(midi_file_path):
    mid = MidiFile(midi_file_path)
    left_hand_notes = []
    right_hand_notes = []

    # 定位钢琴轨道(program=0对应Acoustic Grand Piano)
    for track in mid.tracks:
        is_piano_track = False
        for msg in track:
            if msg.type == 'program_change' and msg.program == 0:
                is_piano_track = True
                break
        if not is_piano_track:
            continue

        # 遍历轨道提取音符
        current_tick = 0
        for msg in track:
            current_tick += msg.time
            if msg.type == 'note_on' and msg.velocity > 0:
                note_info = {
                    'midi_note': msg.note,
                    'tick_position': current_tick,
                    'velocity': msg.velocity
                }
                if msg.note < 60:
                    left_hand_notes.append(note_info)
                else:
                    right_hand_notes.append(note_info)
    
    return left_hand_notes, right_hand_notes

# 使用示例
left_notes, right_notes = split_hands_by_pitch("your_piano_midi.mid")

方法2:匹配MuseScore的精准拆分(适合复杂谱例)

如果需要和MuseScore的拆分结果完全一致,可以借助MuseScore的命令行工具导出为MusicXML,再解析XML获取分声部信息:

  1. 确保MuseScore已安装并添加到系统环境变量
  2. 调用命令行导出XML:mscore input.mid -o output.xml
  3. 用music21库解析XML提取左右手音符:
from music21 import converter
import subprocess

def split_hands_via_musescore(midi_file_path):
    # 临时XML文件路径
    temp_xml = "temp_piano_score.xml"
    # 调用MuseScore导出XML
    subprocess.run(["mscore", midi_file_path, "-o", temp_xml], check=True)
    
    # 解析XML文件
    score = converter.parse(temp_xml)
    left_notes = []
    right_notes = []

    # 遍历声部提取音符
    for part in score.parts:
        instr_name = part.getInstrument().instrumentName
        if "Left Hand" in instr_name:
            for note in part.flat.notes:
                left_notes.append({
                    'midi_note': note.pitch.midi,
                    'time_offset': note.offset,
                    'duration': note.duration.quarterLength
                })
        elif "Right Hand" in instr_name:
            for note in part.flat.notes:
                right_notes.append({
                    'midi_note': note.pitch.midi,
                    'time_offset': note.offset,
                    'duration': note.duration.quarterLength
                })
    
    return left_notes, right_notes

关键说明

  • MIDI标准本身没有定义左右手字段,所有拆分都是基于规则的启发式判断
  • 部分制谱软件导出的MIDI可能用不同通道区分左右手,但PiJAMA数据集的MIDI是单通道单轨道,无法通过通道识别
  • 交叉手段落等复杂情况,音高阈值法会出错,此时优先选择MuseScore导出XML的方案

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.23 09:13:16