Music21基于MIDI通道10移除打击乐时如何修正音符时序问题
问题根因
时序异常是因为你的代码遗漏了三个核心处理逻辑:
- 调用
midiTracksToStreams将MIDI轨道转换为流对象时,没有传入原MIDI文件的ticksPerQuarterNote(每四分音符对应的tick数)参数,导致时间刻度换算错误,直接引发音符时序错位。 - 通道号判断存在偏差:MIDI规范中日常提及的「10号打击乐通道」是1-based计数,而代码层面读取到的通道编号是0-based计数,实际对应的索引值为9,你写的
event.channel == 10匹配不到正确的打击乐通道。 - 轨道过滤逻辑有隐性风险:如果存在单轨道混合多通道的情况,直接删除整轨会误删同轨的非打击乐声部。
修复方案
方案1:基于你现有MidiFile处理逻辑修复
import music21 path = 'lld.midi' m = music21.midi.MidiFile() m.open(path) m.read() m.close() tracks = [] for track in m.tracks: keep_track = True for event in track.events: # 仅对带channel属性的MIDI消息做判断,跳过全局元事件 if hasattr(event, 'channel') and event.channel == 9: keep_track = False break if keep_track: tracks.append(track) # 转换时传入原文件的时间刻度参数,保证时序正确 s = music21.midi.translate.midiTracksToStreams( tracks, ticksPerQuarter=m.ticksPerQuarterNote, quantizePost=False ) s.write('midi', 'no-percussion.midi')
方案2:基于converter.parse的更稳妥实现
你之前用converter.parse读写的逻辑本身没有时序问题,直接在流层级过滤打击乐声部更简单,出错概率更低:
import music21 path = 'lld.midi' score = music21.converter.parse(path, forceSource=False, quantizePost=False, ).stripTies(inPlace=True) # 移除所有绑定打击乐通道的声部 for part in score.parts: if part.getInstrument().midiChannel == 9: score.remove(part) score.write('midi', 'no-percussion.midi')
内容的提问来源于stack exchange,提问作者duhaime
相关产品推荐
相关产品推荐

