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

如何在Windows 10上用Rust/C++创建虚拟音频输入设备模拟麦克风

在Windows 10上创建可写入的虚拟麦克风(Rust/C++实现)

核心思路澄清

WASAPI是正确的技术路径,但Windows Core Audio API本身无法直接创建虚拟音频设备——虚拟设备需要驱动级支持。最实用的方案是:使用现成的虚拟音频驱动(如VB-Cable)作为中转,通过WASAPI将自定义音频流写入该驱动的播放端,驱动会自动将其映射为可被其他应用捕获的麦克风输入。如果需要完全自定义驱动,需使用Windows Driver Kit (WDK) 开发Virtual Audio Device (VAD),但门槛较高。


C++实现方案

步骤1:准备虚拟音频设备

安装VB-Cable(或类似虚拟音频驱动),它会创建一对配对设备:CABLE Output(播放端)和CABLE Input(捕获端,即虚拟麦克风)。

步骤2:用WASAPI写入音频流

以下是核心代码片段,实现从PCM文件读取数据并写入虚拟播放设备:

#include <windows.h>
#include <mmdeviceapi.h>
#include <audioclient.h>
#include <functiondiscoverykeys_devpkey.h>
#include <vector>
#include <fstream>

#pragma comment(lib, "ole32.lib")
#pragma comment(lib, "winmm.lib")

int main() {
    // 初始化COM环境
    CoInitializeEx(nullptr, COINIT_MULTITHREADED);

    // 获取音频设备枚举器
    IMMDeviceEnumerator* pEnumerator = nullptr;
    CoCreateInstance(__uuidof(MMDeviceEnumerator), nullptr, CLSCTX_ALL, __uuidof(IMMDeviceEnumerator), (void**)&pEnumerator);

    // 枚举激活的播放设备,定位VB-Cable Output
    IMMDeviceCollection* pDevices = nullptr;
    pEnumerator->EnumAudioEndpoints(eRender, DEVICE_STATE_ACTIVE, &pDevices);
    UINT deviceCount = 0;
    pDevices->GetCount(&deviceCount);

    IMMDevice* pTargetDevice = nullptr;
    for (UINT i = 0; i < deviceCount; i++) {
        IMMDevice* pDevice = nullptr;
        pDevices->Item(i, &pDevice);

        IPropertyStore* pProps = nullptr;
        pDevice->OpenPropertyStore(STGM_READ, &pProps);

        PROPVARIANT varName;
        PropVariantInit(&varName);
        pProps->GetValue(PKEY_Device_FriendlyName, &varName);

        if (wcscmp(varName.pwszVal, L"CABLE Output") == 0) {
            pTargetDevice = pDevice;
            break;
        }

        PropVariantClear(&varName);
        pProps->Release();
        pDevice->Release();
    }

    // 初始化AudioClient
    IAudioClient* pAudioClient = nullptr;
    pTargetDevice->Activate(__uuidof(IAudioClient), CLSCTX_ALL, nullptr, (void**)&pAudioClient);

    WAVEFORMATEXTENSIBLE* pMixFormat = nullptr;
    pAudioClient->GetMixFormat((WAVEFORMATEX**)&pMixFormat);
    // 调整格式为16位、44100Hz、单声道(可按需修改)
    pMixFormat->Format.wBitsPerSample = 16;
    pMixFormat->Format.nSamplesPerSec = 44100;
    pMixFormat->Format.nChannels = 1;
    pMixFormat->Format.nBlockAlign = pMixFormat->Format.nChannels * pMixFormat->Format.wBitsPerSample / 8;
    pMixFormat->Format.nAvgBytesPerSec = pMixFormat->Format.nSamplesPerSec * pMixFormat->Format.nBlockAlign;

    // 初始化共享模式,设置100ms延迟
    REFERENCE_TIME hnsRequestedDuration = REFTIMES_PER_SEC / 10;
    pAudioClient->Initialize(AUDCLNT_SHAREMODE_SHARED, 0, hnsRequestedDuration, 0, (WAVEFORMATEX*)pMixFormat, nullptr);

    // 获取RenderClient用于写入数据
    IAudioRenderClient* pRenderClient = nullptr;
    pAudioClient->GetService(__uuidof(IAudioRenderClient), (void**)&pRenderClient);

    // 获取缓冲区帧数量
    UINT32 bufferFrameCount = 0;
    pAudioClient->GetBufferSize(&bufferFrameCount);

    // 启动音频流
    pAudioClient->Start();

    // 读取PCM文件并循环写入缓冲区
    std::ifstream pcmFile("audio.pcm", std::ios::binary);
    std::vector<char> buffer(bufferFrameCount * pMixFormat->Format.nBlockAlign);

    while (!pcmFile.eof()) {
        pcmFile.read(buffer.data(), buffer.size());
        UINT32 bytesRead = pcmFile.gcount();
        if (bytesRead == 0) break;

        UINT32 framesToWrite = bytesRead / pMixFormat->Format.nBlockAlign;
        BYTE* pData = nullptr;
        pRenderClient->GetBuffer(framesToWrite, &pData);
        memcpy(pData, buffer.data(), bytesRead);
        pRenderClient->ReleaseBuffer(framesToWrite, 0);
    }

    // 清理资源
    pAudioClient->Stop();
    pRenderClient->Release();
    pAudioClient->Release();
    pTargetDevice->Release();
    pDevices->Release();
    pEnumerator->Release();
    CoUninitialize();

    return 0;
}

注意事项

  • 确保PCM文件格式与代码中设置的采样率、位深、声道数完全匹配,否则会出现杂音或无声。
  • 若需低延迟,可切换为AUDCLNT_SHAREMODE_EXCLUSIVE独占模式,但会占用设备导致其他应用无法使用。

Rust实现方案

步骤1:依赖准备

使用cpal(封装WASAPI的跨平台音频库)和hound(WAV文件读取库),在Cargo.toml中添加:

[dependencies]
cpal = "0.15.0"
hound = "3.5.0"

步骤2:核心代码

以下示例读取WAV文件并写入VB-Cable输出设备:

use cpal::traits::{DeviceTrait, HostTrait, StreamTrait};
use hound::{WavReader, SampleFormat};

fn main() {
    // 获取Windows默认音频主机
    let host = cpal::default_host();

    // 枚举播放设备,定位VB-Cable Output
    let device = host
        .output_devices()
        .unwrap()
        .find(|d| d.name().unwrap_or_default() == "CABLE Output")
        .expect("未找到VB-Cable Output设备");

    // 获取设备默认输出配置(可手动指定采样率、声道数)
    let config = device.default_output_config().unwrap();
    // 手动配置示例:
    // let config = cpal::StreamConfig {
    //     channels: 1,
    //     sample_rate: cpal::SampleRate(44100),
    //     buffer_size: cpal::BufferSize::Default,
    // };

    // 读取WAV文件并验证格式匹配
    let reader = WavReader::open("audio.wav").unwrap();
    let spec = reader.spec();
    assert_eq!(spec.channels, config.channels() as u16);
    assert_eq!(spec.sample_rate, config.sample_rate().0);

    // 生成音频流写入逻辑
    let mut samples = reader.samples::<i16>();
    let write_data = move |data: &mut [i16], _: &cpal::OutputCallbackInfo| {
        for sample in data {
            *sample = samples.next().unwrap().unwrap_or(0);
        }
    };

    // 根据采样格式创建并启动流
    let stream = match config.sample_format() {
        cpal::SampleFormat::I16 => device.build_output_stream(
            &config.into(),
            write_data,
            |err| eprintln!("音频流错误: {}", err),
            None,
        ).unwrap(),
        _ => panic!("不支持的采样格式"),
    };

    stream.play().unwrap();

    // 阻塞等待播放完成(可根据实际需求调整)
    std::thread::sleep(std::time::Duration::from_secs(spec.duration().unwrap().as_secs()));
}

注意事项

  • cpal会自动处理WASAPI的缓冲区管理,无需手动操作底层API。
  • 若需从自定义生成的采样数据写入,可替换hound的读取逻辑为自己的采样生成代码。

自定义驱动的进阶方案

若需完全自主创建虚拟设备(无需依赖第三方驱动),需使用WDK开发Virtual Audio Device (VAD):

  • 参考微软官方的Virtual Audio Device示例(包含在WDK安装包中)。
  • 实现音频捕获设备的驱动逻辑,接收用户模式程序的音频数据并暴露为系统麦克风。
  • 注意:驱动需要数字签名,否则Windows 10会阻止加载(测试时可启用测试签名模式)。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.07 07:31:09