基于NAudio的ASIO同时录音与播放实现需求
用ASIO实现低延迟音频录制+实时监听(基于NAudio)
嘿,刚好我之前做过类似的ASIO低延迟实时监听功能,用NAudio就能搞定!核心就是把asio_DataAvailable回调里的浮点音频数据转换成BufferedWaveProvider能识别的PCM字节数据,然后喂给播放设备就行。给你一步步拆解:
1. 准备工作
首先确保你已经通过NuGet安装了NAudio包——它自带完整的ASIO支持,不用额外依赖。
2. 核心代码实现
定义全局变量
先声明需要用到的ASIO设备、缓冲提供者和播放组件:
private AsioOut _asioDevice; private BufferedWaveProvider _audioBuffer; private WaveOutEvent _waveOutPlayer; private WaveFormat _targetAudioFormat;
初始化设备与缓冲区
这里我们先选择ASIO设备,设置匹配的音频格式(尽量和ASIO硬件格式一致,避免额外转换延迟),然后启动捕获和播放:
// 列出所有可用ASIO驱动,也可以做个下拉框让用户选择 var asioDrivers = AsioOut.GetDriverNames(); if (asioDrivers.Length == 0) { throw new InvalidOperationException("未检测到ASIO设备,请安装对应ASIO驱动"); } // 初始化ASIO设备 _asioDevice = new AsioOut(asioDrivers[0]); // 设置目标音频格式:示例用44.1kHz、16位、立体声,根据你的硬件调整 _targetAudioFormat = new WaveFormat(44100, 16, 2); // 初始化缓冲提供者:设置2秒缓冲区平衡延迟与稳定性,溢出时自动丢弃旧数据 _audioBuffer = new BufferedWaveProvider(_targetAudioFormat); _audioBuffer.BufferDuration = TimeSpan.FromSeconds(2); _audioBuffer.DiscardOnBufferOverflow = true; // 初始化播放设备并关联缓冲区 _waveOutPlayer = new WaveOutEvent(); _waveOutPlayer.Init(_audioBuffer); // 绑定ASIO数据回调事件 _asioDevice.AudioAvailable += Asio_DataAvailable; // 启动ASIO捕获和实时播放 _asioDevice.Play(); _waveOutPlayer.Play();
处理ASIO数据回调
这是最关键的一步:ASIO提供的是非交错的浮点采样数据(每个通道的采样连续存储),我们需要转换成PCM标准的交错格式,再写入BufferedWaveProvider:
private void Asio_DataAvailable(object sender, AsioAudioAvailableEventArgs e) { // 计算输出缓冲区大小:采样数 × 通道数 × 单采样字节数(16位是2字节) int bufferSize = e.SamplesPerBuffer * _targetAudioFormat.Channels * (_targetAudioFormat.BitsPerSample / 8); byte[] outputBuffer = new byte[bufferSize]; int outputIndex = 0; for (int sampleIdx = 0; sampleIdx < e.SamplesPerBuffer; sampleIdx++) { // 遍历每个通道,将浮点采样转成16位PCM并交错写入 for (int channelIdx = 0; channelIdx < _targetAudioFormat.Channels; channelIdx++) { // 获取当前通道的浮点采样值(范围[-1,1]) float floatSample = e.WaveBuffer[channelIdx * e.SamplesPerBuffer + sampleIdx]; // 限制值范围,防止溢出失真 floatSample = Math.Clamp(floatSample, -1f, 1f); // 转换成16位整数 short intSample = (short)(floatSample * short.MaxValue); // 按小端模式写入字节数组 outputBuffer[outputIndex++] = (byte)(intSample & 0xFF); outputBuffer[outputIndex++] = (byte)(intSample >> 8); } } // 将转换好的PCM数据写入缓冲提供者,供播放设备读取 _audioBuffer.AddSamples(outputBuffer, 0, outputBuffer.Length); }
资源清理
记得在程序退出或停止功能时释放资源,避免内存泄漏:
private void CleanupAudioResources() { _asioDevice?.Stop(); _asioDevice?.Dispose(); _waveOutPlayer?.Stop(); _waveOutPlayer?.Dispose(); }
3. 关键注意事项
- 格式匹配优先:尽量让ASIO设备的采样率、通道数和你设置的
_targetAudioFormat完全一致,若必须转换可使用NAudio的WaveFormatConversionStream,但会增加额外延迟。 - 调整缓冲区大小:ASIO硬件缓冲区越小,延迟越低,但系统负载越高,容易出现爆音;
BufferedWaveProvider的缓冲区建议设为1-2秒,平衡延迟和播放稳定性。 - ASIO路由设置:部分消费级声卡的ASIO驱动需要手动设置输入输出路由,确保输入信号能被正确捕获并输出到监听设备。
内容的提问来源于stack exchange,提问作者Mnaukal
相关产品推荐
相关产品推荐

