如何关联 USB 耳机麦克风和 earphone/speaker
How to associate a USB headsets microphone and earphone/speaker
背景
我需要在软电话应用程序中显示连接的耳机列表(麦克风和耳机组合)。为了进行测试,我有以下设备:
- 捷波朗 PRO 9470
- 缤特力 BT300M
- 罗技 G430 游戏耳机麦克风
用户应该能够从 ComboBox
中 select 耳机,而无需 select 单独使用麦克风和耳机。
资料
我知道在哪里可以找到关于麦克风和耳机的信息(在 Windows 中),但是我无法使用 WMI
或 MMDevice API
获得它。
要查找信息,请右键单击任务栏右侧的 Sound
(扬声器图标),然后 select Playback devices
。
- 通过双击或单击
Properties
打开属性 window。
- 单击属性 window 上的
Properties
按钮。
- 单击
Details
选项卡并在 ComboBox
中找到 Children
属性。
这将给我以下信息:
SWD\MMDEVAPI\{0.0.0.00000000}.{f2e09e37-8389-46c4-8b2b-53e08b874399}
SWD\MMDEVAPI\{0.0.1.00000000}.{3402ee6e-d862-47ca-8ab8-bb8254216032}
第一行匹配我的 Headset Earphone (Jabra PRO 9470)
和第二行 Headset Microphone (Jabra PRO 9470)
.
为了在 C# 中获得相同的信息,我循环遍历 Win32_USBControllerDevice class 并输出包含 "MMDEVAPI" 的所有值。在我的电脑上它将 return 6 个值(3 x 麦克风,3 x 耳机)。
foreach (var device in new ManagementObjectSearcher("SELECT * FROM Win32_USBControllerDevice").Get())
{
foreach (var property in device.Properties)
{
// Gets the value of the property on the device.
var value = property.Value == null ? string.Empty : property.Value.ToString();
if (value.IndexOf("mmdevapi", StringComparison.OrdinalIgnoreCase) > -1)
{
// Output connected USB microphones and earphones.
Console.WriteLine(property.Value);
}
}
}
作为参考,上面的代码会输出:
\PC9018\root\cimv2:Win32_PnPEntity.DeviceID="SWD\MMDEVAPI\{0.0.0.00000000}.{F2E09E37-8389-46C4-8B2B-53E08B874399}"
\PC9018\root\cimv2:Win32_PnPEntity.DeviceID="SWD\MMDEVAPI\{0.0.1.00000000}.{3402EE6E-D862-47CA-8AB8-BB8254216032}"
\PC9018\root\cimv2:Win32_PnPEntity.DeviceID="SWD\MMDEVAPI\{0.0.0.00000000}.{985F2B5C-2EE2-4733-BBD6-48BFDE2D5582}"
\PC9018\root\cimv2:Win32_PnPEntity.DeviceID="SWD\MMDEVAPI\{0.0.1.00000000}.{71D824EA-DAE9-4F0D-B673-4425385E3777}"
\PC9018\root\cimv2:Win32_PnPEntity.DeviceID="SWD\MMDEVAPI\{0.0.0.00000000}.{D29C0970-D515-4F91-9924-F0063CF1A196}"
\PC9018\root\cimv2:Win32_PnPEntity.DeviceID="SWD\MMDEVAPI\{0.0.1.00000000}.{C4B331E2-C56B-4D9B-A486-2ED6C11FDB8C}"
问题
我现在的大问题是,如何将正确的耳机麦克风和耳机关联到 Headset
对象中?
尝试
我曾尝试在 Google 和 Whosebug 上搜索答案或提示,但我无法使用 WMI
或 MMDevice API
找到麦克风和耳机之间的任何共同点或关系。
如果有一种方法可以创建 Dictionary<string, List<string>>
,其中 Key
是物理设备或 USB 端口独有的东西,而 Value
是关联的列表 Win32_PnPEntity.DeviceID
,那我就找不到了。
本着 星球大战日 几天后的精神:“帮助我,Whosebug。你是我唯一的希望。”
在 NAudio 和 Windows 注册表的帮助下,我似乎自己找到了答案。问题中的代码仍然是正确的,但 NAudio 使它变得更容易一些。
我在 Windows 注册表中找到了问题的关键:
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\MMDevices\Audio\Capture
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\MMDevices\Audio\Render
每个设备都有一个名为 {b3f8fa53-0004-438e-9003-51a46e139bfc},2
的 属性,其值类似于此 {1}.USB\VID_047F&PID_0416&MI_00&21995D75&0&0000
。
这看起来是一个唯一的硬件ID,由耳机麦克风和耳机共享(与问题中的Children
属性相同)。
解决方法
具有单一定位方法和return耳机列表的界面。
public interface IHeadsetLocator
{
/// <summary>
/// Locate all connected audio devices based on the given state.
/// </summary>
/// <param name="deviceState"></param>
/// <returns></returns>
IReadOnlyCollection<Headset> LocateConnectedAudioDevices(DeviceState deviceState = DeviceState.Active);
}
并完成实施。真正的魔法是在 GetHardwareToken
方法的帮助下发生的。
public class HeadsetLocator : IHeadsetLocator
{
/// <summary>
/// Locate all connected audio devices based on the given state.
/// </summary>
/// <param name="deviceState"></param>
/// <returns></returns>
public IReadOnlyCollection<Headset> LocateConnectedAudioDevices(DeviceState deviceState = DeviceState.Active)
{
var enumerator = new MMDeviceEnumerator();
var relatedAudioDevices = new ConcurrentDictionary<string, List<MMDevice>>();
var headsets = new List<Headset>();
// Locate all connected audio devices.
foreach (var device in enumerator.EnumerateAudioEndPoints(DataFlow.All, deviceState))
{
// Gets the DataFlow and DeviceID from the connected audio device.
var index = device.ID.LastIndexOf('.');
var dataFlow = device.ID.Substring(0, index).Contains("0.0.0") ? DataFlow.Render : DataFlow.Capture;
var deviceId = device.ID.Substring(index + 1);
// Gets the unique hardware token.
var hardwareToken = GetHardwareToken(dataFlow, deviceId);
var audioDevice = relatedAudioDevices.GetOrAdd(hardwareToken, o => new List<MMDevice>());
audioDevice.Add(device);
}
// Combines the related devices into a headset object.
foreach (var devicePair in relatedAudioDevices)
{
var capture = devicePair.Value.FirstOrDefault(o => o.ID.Contains("0.0.1"));
var render = devicePair.Value.FirstOrDefault(o => o.ID.Contains("0.0.0"));
if (capture != null && render != null)
{
headsets.Add(new Headset("Headset", render.DeviceFriendlyName, capture, render));
}
}
return new ReadOnlyCollection<Headset>(headsets);
}
/// <summary>
/// Gets the token of the USB device.
/// </summary>
/// <param name="dataFlow"></param>
/// <param name="audioDeviceId"></param>
/// <returns></returns>
public string GetHardwareToken(DataFlow dataFlow, string audioDeviceId)
{
using (var registryKey = RegistryKey.OpenBaseKey(RegistryHive.LocalMachine, RegistryView.Registry64))
{
var captureKey = registryKey.OpenSubKey(@"SOFTWARE\Microsoft\Windows\CurrentVersion\MMDevices\Audio\" + dataFlow + @"\" + audioDeviceId + @"\Properties");
if (captureKey != null)
{
return captureKey.GetValue("{b3f8fa53-0004-438e-9003-51a46e139bfc},2") as string;
}
}
return null;
}
}
希望这对处于类似情况的其他人有所帮助。
编码愉快.
背景
我需要在软电话应用程序中显示连接的耳机列表(麦克风和耳机组合)。为了进行测试,我有以下设备:
- 捷波朗 PRO 9470
- 缤特力 BT300M
- 罗技 G430 游戏耳机麦克风
用户应该能够从 ComboBox
中 select 耳机,而无需 select 单独使用麦克风和耳机。
资料
我知道在哪里可以找到关于麦克风和耳机的信息(在 Windows 中),但是我无法使用 WMI
或 MMDevice API
获得它。
要查找信息,请右键单击任务栏右侧的 Sound
(扬声器图标),然后 select Playback devices
。
- 通过双击或单击
Properties
打开属性 window。 - 单击属性 window 上的
Properties
按钮。 - 单击
Details
选项卡并在ComboBox
中找到Children
属性。
这将给我以下信息:
SWD\MMDEVAPI\{0.0.0.00000000}.{f2e09e37-8389-46c4-8b2b-53e08b874399}
SWD\MMDEVAPI\{0.0.1.00000000}.{3402ee6e-d862-47ca-8ab8-bb8254216032}
第一行匹配我的 Headset Earphone (Jabra PRO 9470)
和第二行 Headset Microphone (Jabra PRO 9470)
.
为了在 C# 中获得相同的信息,我循环遍历 Win32_USBControllerDevice class 并输出包含 "MMDEVAPI" 的所有值。在我的电脑上它将 return 6 个值(3 x 麦克风,3 x 耳机)。
foreach (var device in new ManagementObjectSearcher("SELECT * FROM Win32_USBControllerDevice").Get())
{
foreach (var property in device.Properties)
{
// Gets the value of the property on the device.
var value = property.Value == null ? string.Empty : property.Value.ToString();
if (value.IndexOf("mmdevapi", StringComparison.OrdinalIgnoreCase) > -1)
{
// Output connected USB microphones and earphones.
Console.WriteLine(property.Value);
}
}
}
作为参考,上面的代码会输出:
\PC9018\root\cimv2:Win32_PnPEntity.DeviceID="SWD\MMDEVAPI\{0.0.0.00000000}.{F2E09E37-8389-46C4-8B2B-53E08B874399}"
\PC9018\root\cimv2:Win32_PnPEntity.DeviceID="SWD\MMDEVAPI\{0.0.1.00000000}.{3402EE6E-D862-47CA-8AB8-BB8254216032}"
\PC9018\root\cimv2:Win32_PnPEntity.DeviceID="SWD\MMDEVAPI\{0.0.0.00000000}.{985F2B5C-2EE2-4733-BBD6-48BFDE2D5582}"
\PC9018\root\cimv2:Win32_PnPEntity.DeviceID="SWD\MMDEVAPI\{0.0.1.00000000}.{71D824EA-DAE9-4F0D-B673-4425385E3777}"
\PC9018\root\cimv2:Win32_PnPEntity.DeviceID="SWD\MMDEVAPI\{0.0.0.00000000}.{D29C0970-D515-4F91-9924-F0063CF1A196}"
\PC9018\root\cimv2:Win32_PnPEntity.DeviceID="SWD\MMDEVAPI\{0.0.1.00000000}.{C4B331E2-C56B-4D9B-A486-2ED6C11FDB8C}"
问题
我现在的大问题是,如何将正确的耳机麦克风和耳机关联到 Headset
对象中?
尝试
我曾尝试在 Google 和 Whosebug 上搜索答案或提示,但我无法使用 WMI
或 MMDevice API
找到麦克风和耳机之间的任何共同点或关系。
如果有一种方法可以创建 Dictionary<string, List<string>>
,其中 Key
是物理设备或 USB 端口独有的东西,而 Value
是关联的列表 Win32_PnPEntity.DeviceID
,那我就找不到了。
本着 星球大战日 几天后的精神:“帮助我,Whosebug。你是我唯一的希望。”
在 NAudio 和 Windows 注册表的帮助下,我似乎自己找到了答案。问题中的代码仍然是正确的,但 NAudio 使它变得更容易一些。
我在 Windows 注册表中找到了问题的关键:
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\MMDevices\Audio\Capture
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\MMDevices\Audio\Render
每个设备都有一个名为 {b3f8fa53-0004-438e-9003-51a46e139bfc},2
的 属性,其值类似于此 {1}.USB\VID_047F&PID_0416&MI_00&21995D75&0&0000
。
这看起来是一个唯一的硬件ID,由耳机麦克风和耳机共享(与问题中的Children
属性相同)。
解决方法
具有单一定位方法和return耳机列表的界面。
public interface IHeadsetLocator
{
/// <summary>
/// Locate all connected audio devices based on the given state.
/// </summary>
/// <param name="deviceState"></param>
/// <returns></returns>
IReadOnlyCollection<Headset> LocateConnectedAudioDevices(DeviceState deviceState = DeviceState.Active);
}
并完成实施。真正的魔法是在 GetHardwareToken
方法的帮助下发生的。
public class HeadsetLocator : IHeadsetLocator
{
/// <summary>
/// Locate all connected audio devices based on the given state.
/// </summary>
/// <param name="deviceState"></param>
/// <returns></returns>
public IReadOnlyCollection<Headset> LocateConnectedAudioDevices(DeviceState deviceState = DeviceState.Active)
{
var enumerator = new MMDeviceEnumerator();
var relatedAudioDevices = new ConcurrentDictionary<string, List<MMDevice>>();
var headsets = new List<Headset>();
// Locate all connected audio devices.
foreach (var device in enumerator.EnumerateAudioEndPoints(DataFlow.All, deviceState))
{
// Gets the DataFlow and DeviceID from the connected audio device.
var index = device.ID.LastIndexOf('.');
var dataFlow = device.ID.Substring(0, index).Contains("0.0.0") ? DataFlow.Render : DataFlow.Capture;
var deviceId = device.ID.Substring(index + 1);
// Gets the unique hardware token.
var hardwareToken = GetHardwareToken(dataFlow, deviceId);
var audioDevice = relatedAudioDevices.GetOrAdd(hardwareToken, o => new List<MMDevice>());
audioDevice.Add(device);
}
// Combines the related devices into a headset object.
foreach (var devicePair in relatedAudioDevices)
{
var capture = devicePair.Value.FirstOrDefault(o => o.ID.Contains("0.0.1"));
var render = devicePair.Value.FirstOrDefault(o => o.ID.Contains("0.0.0"));
if (capture != null && render != null)
{
headsets.Add(new Headset("Headset", render.DeviceFriendlyName, capture, render));
}
}
return new ReadOnlyCollection<Headset>(headsets);
}
/// <summary>
/// Gets the token of the USB device.
/// </summary>
/// <param name="dataFlow"></param>
/// <param name="audioDeviceId"></param>
/// <returns></returns>
public string GetHardwareToken(DataFlow dataFlow, string audioDeviceId)
{
using (var registryKey = RegistryKey.OpenBaseKey(RegistryHive.LocalMachine, RegistryView.Registry64))
{
var captureKey = registryKey.OpenSubKey(@"SOFTWARE\Microsoft\Windows\CurrentVersion\MMDevices\Audio\" + dataFlow + @"\" + audioDeviceId + @"\Properties");
if (captureKey != null)
{
return captureKey.GetValue("{b3f8fa53-0004-438e-9003-51a46e139bfc},2") as string;
}
}
return null;
}
}
希望这对处于类似情况的其他人有所帮助。
编码愉快.