如何使用C#的DryWetMidi库从MIDI文件的指定块获取程序/乐器名称?
获取MIDI程序号与乐器名称(DryWetMidi实现)
Hey there! I get what you're looking for—let's break down how to pull program numbers and their corresponding instrument names using DryWetMidi.
First, a quick MIDI 101: Instruments are set via Program Change messages in MIDI tracks, not as a direct property on the track or file itself. So we need to hunt down those messages first.
Step 1: 获取Program Change消息
DryWetMidi provides a dedicated method to extract these events from a track chunk. Here's how to adjust your existing code:
var midiFile = MidiFile.Read("test.mid"); var trackList = midiFile.GetTrackChunks().ToList(); // 遍历第一个轨道的所有Program Change事件 var programChangeEvents = trackList[0].GetProgramChangeEvents().ToList(); foreach (var programChange in programChangeEvents) { // 获取程序号 int programNumber = programChange.ProgramNumber; // 获取对应的MIDI通道(不同通道可以设置不同乐器) int channel = programChange.Channel; Console.WriteLine($"Channel {channel}: Program Number {programNumber}"); }
Step 2: 把程序号转换成乐器名称
DryWetMidi has built-in support for General MIDI (GM), Roland GS, and Yamaha XG instrument names. Use the ProgramName class to map numbers to human-readable names:
using Melanchall.DryWetMidi.MusicTheory; using Melanchall.DryWetMidi.Interaction; // 接上面的循环 foreach (var programChange in programChangeEvents) { int programNumber = programChange.ProgramNumber; // 获取GM1标准的乐器名称 string instrumentName = ProgramName.GetProgramName(programNumber, GeneralMidiLevel.GeneralMidi1); Console.WriteLine($"Channel {programChange.Channel}: {instrumentName} (Program #{programNumber})"); }
注意事项
- 如果你的MIDI文件使用非标准的乐器映射(比如自定义音色库),DryWetMidi的内置方法可能无法识别,这时你需要自己维护一个编号到名称的映射表。
- 有些MIDI文件可能在多个轨道或多个位置发送Program Change消息,记得要处理所有相关事件,而不是只取第一个。
完整示例代码
using Melanchall.DryWetMidi.Core; using Melanchall.DryWetMidi.MusicTheory; using Melanchall.DryWetMidi.Interaction; using System.Collections.Generic; using System.Linq; var midiFile = MidiFile.Read("test.mid"); var trackChunks = midiFile.GetTrackChunks().ToList(); foreach (var track in trackChunks) { var programChanges = track.GetProgramChangeEvents().ToList(); if (!programChanges.Any()) continue; Console.WriteLine($"--- Track {track} ---"); foreach (var pc in programChanges) { var instrumentName = ProgramName.GetProgramName(pc.ProgramNumber, GeneralMidiLevel.GeneralMidi1); Console.WriteLine($"Channel {pc.Channel}: {instrumentName} (Program #{pc.ProgramNumber})"); } }
内容的提问来源于stack exchange,提问作者Luka




