如何避免Music21分割跨小节音符?MIDI文件音符提取异常求助
解决Music21跨小节音符被拆分的问题
我完全懂你碰到的这个麻烦——music21默认会把跨小节线的音符拆成两个独立实例,这确实会让小节音符数量统计虚高,太影响结果了。下面给你几个实用的解决办法:
方法1:用noTie=True参数直接阻止拆分
这是最省心的办法,在调用flatten()的时候加上noTie=True参数,它会告诉music21不要把跨小节的音符拆分成带连线的两个部分,而是保留成一个完整的音符实例。
修改你代码里的这一行:
t = mf.parts[0].flatten(noTie=True)
这样处理后,跨小节的音符会以一个整体存在,它的measureNumber会显示为起始的那个小节。如果需要判断它是否跨小节,可以通过计算音符的起始偏移量+时长是否超过该小节的总长度来确认,但至少不会出现拆分导致的统计错误。
方法2:手动合并被拆分的连音音符
如果第一种方法不满足你的需求(比如你需要保留连线信息但又要合并统计),可以手动遍历音符列表,把通过tie连接的拆分音符合并成一个。
示例代码片段:
# 先获取扁平化后的音符列表 notes_list = list(t.flatten().notes) merged_notes = [] i = 0 while i < len(notes_list): current_note = notes_list[i] # 检查当前音符是否是连音的起始 if current_note.tie and current_note.tie.type == 'start': # 寻找对应的连音结束音符 j = i + 1 while j < len(notes_list): next_note = notes_list[j] # 匹配同音高的连音结束音符 if next_note.tie and next_note.tie.type == 'stop' and next_note.pitch == current_note.pitch: # 合并两个音符的时长 current_note.duration.quarterLength += next_note.duration.quarterLength merged_notes.append(current_note) i = j + 1 # 跳过已合并的结束音符 break j += 1 else: merged_notes.append(current_note) i += 1 # 之后用merged_notes来进行统计和输出 for h, n in enumerate(merged_notes): altura_numerica = n.pitches[0].midi altura_literal = n.pitches[0].nameWithOctave momento_inicio = n.offset duracion = n.duration.quarterLength compas = n.measureNumber print(f" {h:<5} {compas:<5} {momento_inicio:<8.4g} {altura_numerica:<3} {altura_literal:<3} {duracion:<6.4g} ")
方法3:按小节逐个处理,跳过跨小节延续的音符
如果你的核心需求是准确统计每个小节实际起始的音符(不包含从前面小节延续过来的),可以直接按小节遍历,而不是提前扁平化整个声部。
示例代码:
notas_por_compas = {} part = mf.parts[0] num_compases = len(part.getElementsByClass('Measure')) tipo_compas = mf.getTimeSignatures()[0].ratioString print("- Tipo de compás:", tipo_compas,"-") for measure_num in range(1, num_compases + 1): measure = part.measure(measure_num) # 收集当前小节中不是跨小节延续的音符 current_notes = [] for n in measure.notes: # 如果是连音的结束(即来自前一小节的延续),就跳过 if n.tie and n.tie.type == 'stop': continue current_notes.append(n) notas_por_compas[measure_num] = current_notes # 输出统计结果 print("Nota compas t_inicio Alt_Num Alt_Lit Duracion") h = 0 for compas, notas in notas_por_compas.items(): for n in notas: altura_numerica = n.pitches[0].midi altura_literal = n.pitches[0].nameWithOctave momento_inicio = n.offset duracion = n.duration.quarterLength print(f" {h:<5} {compas:<5} {momento_inicio:<8.4g} {altura_numerica:<3} {altura_literal:<3} {duracion:<6.4g} ") h += 1
这三种方法可以根据你的具体需求选择:方法1最快捷,适合大多数场景;方法2更灵活,适合需要保留细节的情况;方法3则精准针对小节统计的需求。
内容的提问来源于stack exchange,提问作者abeh
相关产品推荐
相关产品推荐

