如何计算MIDI文件中每对音符的半音音程大小?
使用DryWetMIDI计算MIDI音符相邻音程(半音数)
核心思路
- 读取MIDI文件,提取所有音符事件并按触发时间排序(MIDI文件的音符可能无序存储)
- 遍历排序后的音符,通过前后音符的
NoteNumber差值计算半音数(音程大小)
Unity实现代码
using UnityEngine; using Melanchall.DryWetMidi.Core; using Melanchall.DryWetMidi.Interaction; using System.Collections.Generic; using System.Linq; public class MidiIntervalCalculator : MonoBehaviour { public TextAsset midiFileAsset; // 在Inspector面板拖入MIDI文件(需设置为TextAsset类型) void Start() { if (midiFileAsset == null) { Debug.LogError("未指定MIDI文件,请拖入到Inspector"); return; } // 读取MIDI文件流 using (var midiFile = MidiFile.Read(midiFileAsset.bytes)) { // 提取所有轨道的音符并按触发时间排序 var sortedNotes = midiFile.GetNotes() .OrderBy(note => note.Time) .ToList(); if (sortedNotes.Count < 2) { Debug.LogError("MIDI文件中音符数量不足,无法计算音程"); return; } List<int> intervalList = new List<int>(); Note prevNote = sortedNotes[0]; // 遍历计算相邻音符的半音差 for (int i = 1; i < sortedNotes.Count; i++) { Note currNote = sortedNotes[i]; // 半音数 = 当前音符编号 - 前一个音符编号 int interval = currNote.NoteNumber - prevNote.NoteNumber; // 若需取音程绝对值(忽略上下行),替换为 Mathf.Abs(interval) intervalList.Add(interval); prevNote = currNote; } // 输出结果(如C大调音阶会得到 2,2,1,2,2,2,1) Debug.Log("相邻音程半音列表:" + string.Join(", ", intervalList)); } } }
关键说明
GetNotes():DryWetMIDI的扩展方法,一次性提取MIDI所有轨道的音符事件OrderBy(note => note.Time):必须排序,否则会出现跨时间的错误音程计算NoteNumber:每个音符的半音编号属性(如C4对应60,D4对应62,差值2即为全音)- 关于
CompareTo(Note):该方法用于比较音符的触发时间,而非音高,不能直接用来计算音程
注意事项
- 若MIDI包含和弦(同时触发的多个音符),代码会按时间顺序依次计算所有音符的相邻差,如需仅计算旋律声部,需额外过滤特定轨道或筛选每个时间点的单音(如最高/最低音)
内容的提问来源于stack exchange,提问作者nuance
相关产品推荐
相关产品推荐

