naudio录音核心是waveinevent类,需预设devicenumber和匹配硬件的waveformat,必须调用stoprecording()再dispose(),用bufferedwaveprovider实现监听,停录后通过wavefilewriter写入wav文件。

如何用 NAudio 开始录音(WaveInEvent 基础用法)
直接上手录音,核心是 WaveInEvent 类——它基于 Windows 的 WaveIn API,线程安全、事件驱动,比老式的 WaveIn 更适合 WinForms/WPF 场景。
关键点不是“能不能录”,而是“录得稳不稳、停得干不干净”:
-
WaveInEvent必须在调用StartRecording()前设置好DeviceNumber(默认 0 是系统默认录音设备),否则可能静音或报NAudio.MmException: Device not available - 采样率、位深、声道数需与硬件实际支持匹配;常见踩坑是硬设
44100但麦克风只支持16000,导致StartRecording()抛MmException: Invalid parameter - 务必手动调用
StopRecording(),不能只靠Dispose()—— 否则后台线程可能卡住,再启录音会失败
示例片段(仅初始化和启动):
var waveIn = new WaveInEvent
{
DeviceNumber = 0,
WaveFormat = new WaveFormat(16000, 16, 1) // 采样率、位深、声道数
};
waveIn.DataAvailable += (s, e) => { /* 写入缓冲区 */ };
waveIn.StartRecording();
把录音数据存成 WAV 文件(WaveFileWriter 写入时机)
WaveFileWriter 不是流式写入器,它需要完整写入 RIFF 头部信息,所以必须等录音结束才能封包。边录边写 WAV 是伪需求——你真正要的是“可播放的 WAV”,不是“实时生成合法 WAV 文件”。
正确做法是:先缓存 byte[] 或写入临时 MemoryStream,停录后再一次性写入磁盘:
- 用
List<byte></byte>累积e.Buffer数据时,注意每次e.BytesRecorded是真实字节数,别直接拷整个e.Buffer.Length - 写入前确保
WaveFormat和录音时完全一致,否则播放时会变调或报错Invalid wav file header - 不要在
DataAvailable里直接File.WriteAllBytes——IO 阻塞会导致丢帧甚至崩溃
停录后写文件示例:
using (var writer = new WaveFileWriter("out.wav", waveIn.WaveFormat))
{
writer.Write(dataBuffer, 0, dataBuffer.Length);
}
录音时同步播放(DirectSoundOut + WaveInEvent 双流处理)
想实现“监听自己声音”(比如 K 歌软件),不能简单把 DataAvailable 里的数据喂给播放器——WaveInEvent 的回调是后台线程,DirectSoundOut 要求主线程初始化,且播放缓冲区管理必须独立。
更可靠的做法是用 BufferedWaveProvider 作中间桥接:
- 创建
BufferedWaveProvider时传入和录音相同的WaveFormat - 在
DataAvailable中调用bufferedProvider.AddSamples(e.Buffer, 0, e.BytesRecorded) - 单独启一个
DirectSoundOut播放这个 provider,注意Play()必须在 UI 线程或已初始化 COM 的线程调用 - 延迟取决于 buffer 容量,设太小(如 20ms)容易 underrun 报
BufferUnderrunException;建议从 200ms 起调
停止录音后资源没释放?查 Dispose 顺序和事件订阅
最常见的“第二次录音失败”,往往不是代码逻辑问题,而是资源残留:
-
WaveInEvent的DataAvailable事件没取消订阅,旧实例还在往已释放的BufferedWaveProvider写数据,引发ObjectDisposedException -
WaveFileWriter或DirectSoundOut没Dispose(),导致音频设备句柄被占,下次StartRecording()返回成功但实际无声 - WPF/WinForms 中,如果在窗口
Closing事件里才Dispose(),用户快速开关窗口可能触发竞态——建议在明确停止录音后立即释放,别等生命周期钩子
稳妥的清理模式:
waveIn?.DataAvailable -= OnDataAvailable; waveIn?.StopRecording(); waveIn?.Dispose(); waveIn = null; <p>bufferedProvider?.Dispose(); directSoundOut?.Stop(); directSoundOut?.Dispose(); </p>
设备重连、USB 麦克风热插拔这些场景下,DeviceNumber 可能失效,得重新枚举 WaveIn.DeviceCount 并校验 WaveIn.GetCapabilities(i),这部分容易被忽略。










