portaudio 列出所有音频输入设备需先调用 pa_initialize() 确保成功,再用 pa_getdevicecount() 获取总数,循环调用 pa_getdeviceinfo(i) 并检查 info->maxinputchannels > 0 才视为有效输入设备,设备名取 info->name。

PortAudio 怎么列出所有音频输入设备
PortAudio 的 Pa_GetDeviceCount() 和 Pa_GetDeviceInfo() 是唯一可靠入口,别想绕过它查系统注册表或调用底层 API —— 那样既跨平台失效,又容易漏掉虚拟设备(比如 VB-Cable、Voicemeeter 虚拟线)。
常见错误是只调用一次 Pa_Initialize() 就开始枚举,但实际必须确保初始化成功且未被提前终止;还有人忽略 PaDeviceInfo::maxInputChannels > 0 这个关键判断,把纯输出设备也当输入设备列出来。
- 先调用
Pa_Initialize(),检查返回值是否为paNoError - 用
Pa_GetDeviceCount()获取总数,再循环调用Pa_GetDeviceInfo(i) - 对每个设备指针,检查
info->maxInputChannels > 0才算有效输入设备 - 设备名取
info->name,别依赖info->hostApi做分类——WASAPI 和 DirectSound 设备可能共享同一 host API ID
int deviceCount = Pa_GetDeviceCount(); for (int i = 0; i < deviceCount; ++i) { const PaDeviceInfo* info = Pa_GetDeviceInfo(i); if (info && info->maxInputChannels > 0) { printf("Input device %d: %sn", i, info->name); } }
WASAPI 枚举输入设备时为什么 GetDefaultAudioEndpoint 失败
IMMDeviceEnumerator::GetDefaultAudioEndpoint() 默认只返回“默认通信设备”或“默认播放/录制设备”,不是全量列表。想拿到全部输入设备,必须用 EnumAudioEndpoints(eCapture, DEVICE_STATE_ACTIVE | DEVICE_STATE_UNPLUGGED),否则插着的麦克风都可能被过滤掉。
另一个高频坑是没正确设置 COM 初始化模式:WASAPI 必须在单线程公寓(STA)下初始化,CoInitializeEx(NULL, COINIT_APARTMENTTHREADED) 缺一不可;用 COINIT_MULTITHREADED 会导致后续接口调用静默失败或返回 E_NOINTERFACE。
立即学习“C++免费学习笔记(深入)”;
- 必须先调用
CoInitializeEx(NULL, COINIT_APARTMENTTHREADED) - 用
enumerator->EnumAudioEndpoints(eCapture, ...),别用GetDefaultAudioEndpoint() - 遍历
IMMDeviceCollection时,对每个IMMDevice调用OpenPropertyStore(STGM_READ)读取PKEY_Device_FriendlyName - 注意释放
IMMDevice和IMMDeviceCollection,否则设备句柄泄漏
PortAudio 和 WASAPI 枚举结果不一致怎么办
PortAudio 在 windows 上默认走 WASAPI 后端,但它的设备索引和 WASAPI 的 IMMDevice GUID 完全无关——PortAudio 内部做了抽象映射,且会跳过某些被标记为“禁用”或“未连接”的设备(哪怕物理上插着),而 WASAPI 的 DEVICE_STATE_UNPLUGGED 可能仍把它列出来。
更麻烦的是:PortAudio 的 Pa_GetDeviceInfo() 返回的采样率范围(defaultSampleRate)是 host 推荐值,不是设备真实能力;WASAPI 则可通过 IAudioClient::IsFormatSupported() 实际探测,但 PortAudio 不暴露这层。
- 不要假设 PortAudio 索引 i 对应 WASAPI 第 i 个设备——它们没有对应关系
- 若需精确匹配,只能靠设备名字符串粗略比对(
info->namevsPKEY_Device_FriendlyName),但注意空格、括号、驱动附加后缀(如 “(VB-Audio Voicemeeter VAIO)”) - PortAudio 不报告设备是否支持独占模式,WASAPI 可通过
IAudioClient::GetSharedModeEnginePeriod()判断,这点常被忽略
为什么枚举出来的设备名中文乱码或显示为英文
PortAudio 的 PaDeviceInfo::name 是 UTF-8 编码的 C 字符串,但 Windows 控制台默认用本地 ANSI(如 GBK)解码,直接 printf 会乱码;WASAPI 的 PKEY_Device_FriendlyName 是 UTF-16,用 wprintf 或转成 UTF-8 才能正常显示。
最容易被忽略的是:即使你用了 SetConsoleOutputCP(CP_UTF8),如果程序启动时控制台编码已被父进程锁定(比如从 VS 调试器启动),该设置可能无效。
- PortAudio 设备名:用
MultiByteToWideChar(CP_UTF8, ...)转宽字符再输出,或确保终端支持 UTF-8 - WASAPI 设备名:用
wprintf(L"%s", friendlyName),别用printf("%S", ...)—— 后者行为不可靠 - 发布时别依赖控制台编码设置,优先写入日志文件并用 UTF-8 bom 标记
设备名本身不含路径、权限或实时状态信息,它只是个静态标识符。真要区分同名设备(比如两个 USB 麦克风),得靠底层硬件 ID(WASAPI 的 PKEY_Device_InstanceId)或 PortAudio 的 hostApi + PaHostApiInfo::type 组合,但这部分 PortAudio 不对外暴露,只能自己 patch 或换 WASAPI。