You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.30 02:05:29