如何在C# WinForms中用NAudio播放MIDI音符及排障
解决WinForms + NAudio实现MIDI音符播放的问题
需求背景
在.NET Framework 8的WinForms项目中,希望通过NAudio实现类似NAudio.PlayNote(28, 100);的简单MIDI音符播放功能(参数1为MIDI音符编号,参数2为播放时长毫秒),但遇到多个技术问题,以下是针对性解决方向:
遇到的问题及解决方法
1. 引用NAudio.Midi时找不到命名空间
- 核心原因:仅安装了
NAudio.WinForms包,该包仅提供WinForms相关控件,核心MIDI功能在NAudio主包中。 - 解决步骤:
- 卸载已安装的
NAudio.WinForms包; - 在NuGet包管理器中搜索并安装NAudio主包(选择最新稳定版);
- 在代码文件顶部添加引用:
using NAudio.Midi;; - 右键项目→「管理NuGet包」,确认NAudio已被正确引用到项目中。
- 卸载已安装的
2. 安装NAudio主包后,NoteOffEvent找不到、var关键字报错
NoteOffEvent找不到:确保已添加using NAudio.Midi;命名空间,该类型属于NAudio.Midi模块;var报错:检查var声明的上下文是否合法(比如是否在方法内、是否有明确的赋值类型),.NET Framework 8完全支持var关键字,大概率是代码拼写或语法错误(比如未完成的语句、缺少分号等)。
3. .NET Framework 8下NAudio.Midi缺少NoteOffset
- 不存在名为
NoteOffset的类型,你需要的是NoteOffEvent(用于终止音符播放)。若要控制音符播放时长,只需在发送NoteOnEvent后延迟指定时间,再发送NoteOffEvent即可。
4. 运行时抛“already allocated using MidiOutOpen”异常,注释MidiOut初始化后无声音
- 异常原因:重复初始化
MidiOut实例导致设备被重复占用; - 无声音原因:未正确初始化MIDI输出设备,或未发送有效的MIDI事件;
- 解决步骤:
- 全局维护一个
MidiOut实例,避免重复创建; - 使用完设备后必须调用
Close()和Dispose()释放资源; - 确认选择了可用的MIDI输出设备(通过
MidiOut.NumberOfDevices枚举设备,通常索引0为默认合成器); - 按照「发送NoteOn→延迟→发送NoteOff」的流程实现音符播放。
- 全局维护一个
示例实现代码
using NAudio.Midi; using System; using System.Threading.Tasks; using System.Windows.Forms; public class MidiPlayer : IDisposable { private MidiOut _midiOut; public MidiPlayer() { // 初始化默认MIDI输出设备 if (MidiOut.NumberOfDevices > 0) { _midiOut = new MidiOut(0); } else { throw new InvalidOperationException("未检测到可用的MIDI输出设备"); } } // 实现需求中的PlayNote方法 public void PlayNote(int noteNumber, int durationMs) { if (_midiOut == null) return; // 发送NoteOn事件(音量设为100,范围0-127) var noteOnEvent = new NoteOnEvent(0, 0, noteNumber, 100, 0); _midiOut.Send(noteOnEvent.RawData); // 延迟指定时长后发送NoteOff事件终止音符 Task.Run(async () => { await Task.Delay(durationMs); var noteOffEvent = new NoteOffEvent(0, 0, noteNumber, 0, 0); _midiOut.Send(noteOffEvent.RawData); }); } // 释放资源,避免设备占用 public void Dispose() { _midiOut?.Close(); _midiOut?.Dispose(); _midiOut = null; } } // WinForms窗体中的使用示例 public partial class MainForm : Form { private MidiPlayer _midiPlayer; public MainForm() { InitializeComponent(); } private void MainForm_Load(object sender, EventArgs e) { try { _midiPlayer = new MidiPlayer(); } catch (Exception ex) { MessageBox.Show($"初始化失败:{ex.Message}"); } } private void btnPlayNote_Click(object sender, EventArgs e) { _midiPlayer?.PlayNote(28, 100); } private void MainForm_FormClosing(object sender, FormClosingEventArgs e) { _midiPlayer?.Dispose(); } }
额外注意事项
- 清理项目的
bin和obj文件夹,重新生成项目,避免缓存导致的类型引用问题; - 若仍无声音,检查系统的MIDI合成器是否正常工作(可通过系统声音设置测试);
- 确保项目的.NET Framework版本为8.0,NAudio支持.NET Framework 4.6.1及以上版本,二者兼容。
内容的提问来源于stack exchange,提问作者B. Clay Shannon-B. Crow Raven
相关产品推荐
相关产品推荐

