使用NAudio模拟游戏循环时音频卡顿问题求助
NAudio播放卡顿问题的解决方案
问题根源分析
你的代码出现卡顿主要有以下几个原因:
- 线程安全问题:
Queue<short>不是线程安全集合,录制的DataAvailable事件在后台线程触发,和主线程的游戏循环同时操作队列会导致数据错乱、丢失,进而引发卡顿。 - 手动循环精度不足:
Task.Delay(8).Wait()的计时精度极低(实际延迟可能远大于8ms),加上DateTime的计时误差,导致喂数据的时机完全不稳定,WaveOut内部缓冲随时可能耗尽,出现断流卡顿。 - 缓冲配置不合理:
BufferedWaveProvider默认的缓冲大小和读取逻辑没有针对低延迟播放优化,容易出现缓冲空的情况。 - 多余的数据转换:录制数据转
short入队列再转字节数组的过程既消耗性能,又增加了出错概率。
修复方案
1. 替换线程不安全集合
用ConcurrentQueue<short>代替Queue<short>,或者更简单的方式——直接把录制的数据写入BufferedWaveProvider,跳过中间队列环节。
2. 移除手动游戏循环,让NAudio管理播放节奏
WaveOut本身会在后台线程按需从BufferedWaveProvider读取数据,不需要手动定时喂数据,你只需要保证缓冲始终有足够的数据即可。
3. 优化BufferedWaveProvider配置
设置合适的缓冲参数,避免缓冲耗尽或溢出:
bufferedWaveProvider = new BufferedWaveProvider(sourceStream.WaveFormat); bufferedWaveProvider.BufferDuration = TimeSpan.FromMilliseconds(200); // 设置200ms缓冲,平衡延迟和稳定性 bufferedWaveProvider.DiscardOnBufferOverflow = true; // 缓冲溢出时丢弃旧数据,避免卡顿 bufferedWaveProvider.ReadFully = true; // 每次读取足够的数据
4. 简化录制数据处理
直接在DataAvailable事件中把字节数据写入缓冲,跳过不必要的转换:
private static void sourceStream_DataAvailable(object sender, WaveInEventArgs e) { bufferedWaveProvider.AddSamples(e.Buffer, 0, e.BytesRecorded); // 可选:打印缓冲状态用于调试 Console.WriteLine($"缓冲剩余时长: {bufferedWaveProvider.BufferedDuration.TotalMilliseconds:F2}ms"); }
5. 移除多余的手动播放循环
删除PlaybackLoop方法和主线程的while循环,只保留初始化逻辑即可。
修改后的完整代码
using NAudio.Wave; using System; class Program { private static BufferedWaveProvider bufferedWaveProvider; static void Initialize() { WaveInEvent sourceStream = new WaveInEvent(); sourceStream.WaveFormat = new WaveFormat(48000, 16, 1); sourceStream.DataAvailable += sourceStream_DataAvailable; sourceStream.StartRecording(); WaveOutEvent outputStream = new WaveOutEvent(); bufferedWaveProvider = new BufferedWaveProvider(sourceStream.WaveFormat); bufferedWaveProvider.BufferDuration = TimeSpan.FromMilliseconds(200); bufferedWaveProvider.DiscardOnBufferOverflow = true; bufferedWaveProvider.ReadFully = true; outputStream.Init(bufferedWaveProvider); outputStream.Play(); } private static void sourceStream_DataAvailable(object sender, WaveInEventArgs e) { bufferedWaveProvider.AddSamples(e.Buffer, 0, e.BytesRecorded); Console.WriteLine($"缓冲剩余时长: {bufferedWaveProvider.BufferedDuration.TotalMilliseconds:F2}ms"); } static void Main(string[] args) { Initialize(); Console.WriteLine("按任意键退出..."); Console.ReadKey(); } }
额外说明
如果需要低延迟播放(比如游戏场景),可以尝试以下优化:
- 改用
WasapiOut代替WaveOutEvent,Wasapi支持更低的延迟。 - 调整
WaveOutEvent的DesiredLatency属性,比如设置为100ms:outputStream.DesiredLatency = 100; - 确保录制和播放的WaveFormat完全一致(你的代码里已经做到了)。
内容的提问来源于stack exchange,提问作者Mentalrob
相关产品推荐
相关产品推荐

