使用PortAudio 19录制音频时遇Invalid Device错误及设备去重问题
问题分析与解决方案
一、WDM-KS宿主API打开设备失败(错误码-9996)的原因及修复
错误码-9996(paInvalidDevice)说明PortAudio无法识别指定设备索引,或该设备在当前宿主API下不可用。你的兼容性检查逻辑存在以下遗漏:
未绑定目标宿主API
现有Pa_IsFormatSupported调用未指定特定宿主API,PortAudio会默认用首选API(通常是MME)验证格式兼容性,导致MME验证通过的设备切换到WDM-KS时可能不兼容。修复时需明确指定WDM-KS API:bool IsInputDeviceCompatible(int index, PaHostApiTypeId targetApi) { const PaDeviceInfo* device = Pa_GetDeviceInfo((PaDeviceIndex)index); // 先判断设备是否属于目标宿主API if (device->hostApi != Pa_HostApiTypeIdToHostApiIndex(targetApi)) { return false; } PaStreamParameters inputParam; inputParam.device = index; inputParam.channelCount = 1; inputParam.sampleFormat = paInt16; inputParam.hostApiSpecificStreamInfo = 0; // 必须设置对应API的建议延迟,不能传0 inputParam.suggestedLatency = device->defaultLowInputLatency; PaError err = Pa_IsFormatSupported(&inputParam, nullptr, 22050); return err == paNoError; }调用时传入
paWDMKS作为目标API:if (IsInputDeviceCompatible(i, paWDMKS)) { ... }WDM-KS格式限制更严格
WDM-KS对采样格式、通道数要求更高,部分设备可能不支持paInt16或单通道。可尝试:- 通过
PaDeviceInfo的nativeSampleFormats字段获取设备原生采样格式 - 改用2通道进行兼容性检查
- 通过
系统权限或设备占用问题
Windows 10下WDM-KS可能需要更高权限,或设备被其他进程占用。可:- 关闭其他正在使用音频设备的程序
- 以管理员身份运行程序
二、过滤同一物理设备的多API实例(除名称外的方法)
PortAudio为同一物理设备的不同API分配不同索引,可通过以下方式识别同一物理设备:
利用硬件ID(PortAudio Windows扩展)
同一物理设备的不同API实例共享相同硬件ID,通过PaWin_GetDeviceInfo获取:#include <portaudio.h> #include <pa_win_wdmks.h> bool IsSamePhysicalDevice(int dev1, int dev2) { const PaWinDeviceInfo* winDev1 = PaWin_GetDeviceInfo((PaDeviceIndex)dev1); const PaWinDeviceInfo* winDev2 = PaWin_GetDeviceInfo((PaDeviceIndex)dev2); if (!winDev1 || !winDev2) return false; return QString::fromWCharArray(winDev1->deviceID) == QString::fromWCharArray(winDev2->deviceID); }填充列表时维护已添加的硬件ID集合,仅保留每个硬件ID的一个实例。
按API优先级筛选
若无需用户选择API,可优先保留性能更优的API实例(如WDM-KS优先于MME):// 定义API优先级:WDM-KS > WASAPI > MME int GetApiPriority(PaHostApiTypeId apiType) { switch(apiType) { case paWDMKS: return 3; case paWASAPI: return 2; case paMME: return 1; default: return 0; } } // 填充deviceList的过滤逻辑 QMap<QString, DeviceInfo> physicalDeviceMap; // key: 硬件ID,value: 最优设备 for (int i = 0; i < deviceCount; ++i) { const PaDeviceInfo* device = Pa_GetDeviceInfo((PaDeviceIndex)i); if (!device || device->maxInputChannels == 0) continue; const PaWinDeviceInfo* winDev = PaWin_GetDeviceInfo((PaDeviceIndex)i); QString hardwareId = QString::fromWCharArray(winDev->deviceID); int currentPriority = GetApiPriority(Pa_GetHostApiInfo(device->hostApi)->type); if (!physicalDeviceMap.contains(hardwareId) || currentPriority > GetApiPriority(Pa_GetHostApiInfo(physicalDeviceMap[hardwareId].index)->type)) { if (IsInputDeviceCompatible(i)) { DeviceInfo info; info.index = i; info.name = device->name; physicalDeviceMap[hardwareId] = info; } } } deviceList = physicalDeviceMap.values();
内容的提问来源于stack exchange,提问作者user20834974
相关产品推荐
相关产品推荐

