You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

在C#中实现向捕获设备(含虚拟捕获驱动)输入音频的可行方案及NAudio报错解决方案咨询

解决方案:用C#(NAudio)向虚拟捕获设备注入音频

咱先把你遇到的错误说透:你代码里用WasapiOut操作捕获设备,但WasapiOut专门给渲染(播放)设备用的,Loopback模式是用来抓取渲染设备输出的,给捕获设备用肯定触发0x88890003错误——系统已经明确提示“Loopback标志设了,但设备是捕获设备不是渲染设备”。

好消息是:完全不用转C++,C#结合NAudio就能搞定你的需求,下面分两种常用场景给你具体方案:

场景1:用主流虚拟音频驱动(比如VB-Cable、Voicemeeter)

这类驱动会同时创建“虚拟渲染设备”和“虚拟捕获设备”——你把音频输出到虚拟渲染设备,对应的虚拟捕获设备就能捕获到这个音频,其他程序直接选择该捕获设备即可,这是最通用、最稳定的方案。

代码示例:

using NAudio.CoreAudioApi;
using NAudio.Wave;
using System.Threading;

var enumerator = new MMDeviceEnumerator();
// 找到你的虚拟渲染设备(比如VB-Cable的输出设备,名字一般带"Cable"关键词)
MMDevice virtualRenderDevice = enumerator.EnumerateAudioEndPoints(DataFlow.Render, DeviceState.Active)
    .FirstOrDefault(d => d.FriendlyName.Contains("Cable"));

if (virtualRenderDevice == null)
{
    Console.WriteLine("未找到虚拟渲染设备,请先安装VB-Cable或同类虚拟音频驱动");
    return;
}

// 加载要注入的音频(这里以本地音频文件为例,也可以替换为实时生成的WaveStream)
using var audioStream = new AudioFileReader("your-target-audio.wav");
using var wasapiOut = new WasapiOut(virtualRenderDevice, AudioClientShareMode.Shared, false, 0);

wasapiOut.Init(audioStream);
wasapiOut.Play();

// 保持程序运行,直到音频播放完成
while (wasapiOut.PlaybackState == PlaybackState.Playing)
{
    Thread.Sleep(100);
}

运行这段代码后,打开微信、Zoom等需要使用音频的程序,选择对应的虚拟捕获设备,就能获取你注入的音频了。

场景2:直接向虚拟捕获设备注入音频(跳过渲染设备)

如果你的虚拟驱动支持直接向捕获端注入音频(大部分主流虚拟驱动都支持),可以用NAudio直接操作捕获设备的AudioClient,获取IAudioRenderClient来推送音频数据:

代码示例:

using NAudio.CoreAudioApi;
using NAudio.CoreAudioApi.Interfaces;
using NAudio.Wave;
using System.Runtime.InteropServices;
using System.Threading;

var enumerator = new MMDeviceEnumerator();
// 获取目标虚拟捕获设备
MMDevice virtualCaptureDevice = enumerator.GetDefaultAudioEndpoint(DataFlow.Capture, Role.Multimedia);

using var audioClient = virtualCaptureDevice.AudioClient;
// 设置音频格式(需与设备兼容,这里以44.1kHz、16位、双声道为例)
var waveFormat = new WaveFormat(44100, 16, 2);

// 初始化AudioClient,注意使用Shared模式,不要设置Loopback标志
audioClient.Initialize(
    AudioClientShareMode.Shared,
    AudioClientStreamFlags.None,
    0, 0,
    waveFormat,
    Guid.Empty);

// 获取RenderClient,用于向捕获设备推送音频
var renderClient = audioClient.GetService<IAudioRenderClient>();
int bufferSize = audioClient.BufferSize;

// 加载要注入的音频
using var audioFile = new AudioFileReader("your-target-audio.wav");
var audioBuffer = new byte[bufferSize * waveFormat.BlockAlign];

audioClient.Start();

while (audioFile.Read(audioBuffer, 0, audioBuffer.Length) > 0)
{
    IntPtr bufferPtr;
    // 申请缓冲区
    renderClient.GetBuffer(bufferSize, out bufferPtr);
    // 将音频数据复制到系统缓冲区
    Marshal.Copy(audioBuffer, 0, bufferPtr, audioBuffer.Length);
    // 释放缓冲区,推送音频数据
    renderClient.ReleaseBuffer(bufferSize, AudioClientBufferFlags.None);
    // 控制推送速度,避免音频播放过快
    Thread.Sleep((int)(bufferSize / (double)waveFormat.SampleRate * 1000));
}

audioClient.Stop();

关键提醒

  • 物理麦克风这类真实捕获设备不支持注入音频,这是硬件层面的限制,和开发语言无关,必须使用虚拟捕获驱动才能实现需求。
  • NAudio已经封装了几乎所有Core Audio API的功能,完全能覆盖你的需求,没必要转用C++。

内容的提问来源于stack exchange,提问作者Saliom

火山引擎 最新活动