Получить полное имя устройства waveIn
Я использовал waveInGetDevCaps для получения имени устройств waveIn, но структура WAVEINCAPS поддерживает только 31 символ плюс ноль, что означает, что на моем компьютере имена устройств, которые я получаю, усекаются:
Microphone / Line In (SigmaTel
Microphone Array (SigmaTel High,
Я уверен, что должен быть способ получить полное имя устройства, но кто-нибудь знает, что это такое?
6 ответов
Да, есть обходной путь. Я решил эту проблему несколько раз в коде доставки.
Перечислять устройства захвата звука с DirectSoundCapture. API является DirectSoundCaptureEnumerate. Он вернет вам полное имя устройства.
Конечно, вы, вероятно, думаете: "Это здорово, но остальная часть моего кода настроена на использование Wave API, а не DirectSound. Я не хочу переключать все это. Так как я могу сопоставить идентификаторы GUID, возвращаемые DirectSoundCaptureEnumerate к целочисленным идентификаторам, используемым API-интерфейсом WaveIn?"
Решением является CoCreateInstance для объекта DirectSoundPrivate (или вызов GetClassObject непосредственно из dsound.dll) для получения указателя на интерфейс IKsPropertySet. Из этого интерфейса вы можете получить DSound GUID для сопоставления Wave ID. Для более подробной информации смотрите эту веб-страницу:
http://msdn.microsoft.com/en-us/library/bb206182(VS.85).aspx
Вы хотите использовать DSPROPERTY_DIRECTSOUNDDEVICE_WAVEDEVICEMAPPING, как описано на веб-странице, указанной выше.
Улучшенный / полный CF WPF-код на основе ответа @Andrea Bertucelli
using NAudio.CoreAudioApi;
using NAudio.Wave;
using System;
using System.Collections.Generic;
using System.Windows;
namespace WpfApp2
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
foreach (KeyValuePair<string, MMDevice> device in GetInputAudioDevices())
{
Console.WriteLine("Name: {0}, State: {1}", device.Key, device.Value.State);
}
}
public Dictionary<string, MMDevice> GetInputAudioDevices()
{
Dictionary<string, MMDevice> retVal = new Dictionary<string, MMDevice>();
MMDeviceEnumerator enumerator = new MMDeviceEnumerator();
int waveInDevices = WaveIn.DeviceCount;
for (int waveInDevice = 0; waveInDevice < waveInDevices; waveInDevice++)
{
WaveInCapabilities deviceInfo = WaveIn.GetCapabilities(waveInDevice);
foreach (MMDevice device in enumerator.EnumerateAudioEndPoints(DataFlow.Capture, DeviceState.All))
{
if (device.FriendlyName.StartsWith(deviceInfo.ProductName))
{
retVal.Add(device.FriendlyName, device);
break;
}
}
}
return retVal;
}
}
}
Я заполнил имена устройств waveIn, изучая имена, возвращенные из MMDeviceEnumerator. Для каждого устройства waveIn, когда неполное имя является частью полного имени одного из EnumerateAudioEndPoints, я использовал это полное имя для заполнения комбинированного списка в том же порядке устройств waveIn.
VisualBasic.NET:
Dim wain = New WaveIn()
Dim DeviceInfoI As WaveInCapabilities
Dim nomedevice As String
For de = 0 To wain.DeviceCount - 1
DeviceInfoI = wain.GetCapabilities(de)
nomedevice = DeviceInfoI.ProductName
For deg = 0 To devices.Count - 1
If InStr(devices.Item(deg).FriendlyName, nomedevice) Then
nomedevice = devices.Item(deg).FriendlyName
Exit For
End If
Next
cmbMessaggiVocaliMIC.Items.Add(nomedevice)
Next
cmbMessaggiVocaliMIC.SelectedIndex = 0
waveIn.DeviceNumber = cmbMessaggiVocaliMIC.SelectedIndex
Есть способ задействовать реестр, который проще, чем использование DirectSound. Если вы используете структуру WAVEINCAPS2, она имеет имя GUID, которое ссылается на ключ в HKLM\System\CurrentControlSet\Control\MediaCategories. Если ключ не существует, просто используйте имя в структуре. Это документировано на http://msdn.microsoft.com/en-us/library/windows/hardware/ff536382%28v=vs.85%29.aspx. Вот пример:
public static ICollection<AudioInputDevice> GetDevices()
{
RegistryKey namesKey = Registry.LocalMachine.OpenSubKey(@"System\CurrentControlSet\Control\MediaCategories");
List<AudioInputDevice> devices = new List<AudioInputDevice>();
for(int i=0, deviceCount=waveInGetNumDevs(); i<deviceCount; i++)
{
WAVEINCAPS2 caps;
if(waveInGetDevCaps(new IntPtr(i), out caps, Marshal.SizeOf(typeof(WAVEINCAPS2))) == 0 && caps.Formats != 0)
{
string name = null;
if(namesKey != null)
{
RegistryKey nameKey = namesKey.OpenSubKey(caps.NameGuid.ToString("B"));
if(nameKey != null) name = nameKey.GetValue("Name") as string;
}
devices.Add(new AudioInputDevice(name ?? caps.Name, caps.ProductGuid));
}
}
return devices;
}
struct WAVEINCAPS2
{
public short ManufacturerId, ProductId;
public uint DriverVersion;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst=32)] public string Name;
public uint Formats;
public short Channels;
ushort Reserved;
public Guid ManufacturerGuid, ProductGuid, NameGuid;
}
[DllImport("winmm.dll")]
static extern int waveInGetDevCaps(IntPtr deviceId, out WAVEINCAPS2 caps, int capsSize);
[DllImport("winmm.dll", ExactSpelling=true)]
static extern int waveInGetNumDevs();
Похоже, у DirectSoundPrivate есть некоторые проблемы. Я пытаюсь получить доступ к нему из пустого проекта, и он работает нормально. Однако, когда я пытаюсь получить доступ к нему из COM DLL или из потока DLL, он возвращает E_NOTIMPL
ошибка от IKsPropertySet::Get
,
Но я понял еще одну хитрость. Кажется, что DirectSound перечисляет устройства захвата и рендеринга в порядке идентификатора волны (исключая первое значение по умолчанию).
Нам все еще нужно взаимодействовать со старым Wave API, и у него все еще нет надлежащего способа сделать это. DirectShow предоставляет устройства ввода звука на основе WaveIn, и мне нужно получить соответствующий идентификатор WASAPI и наоборот.
Используя NAudio, я использую этот код для получения полного имени устройства...
using NAudio.CoreAudioApi;
using NAudio.Wave;
Для получения всех записывающих устройств:
//create enumerator
var enumerator = new MMDeviceEnumerator();
//cycle through all audio devices
for (int i = 0; i < WaveIn.DeviceCount; i++)
Console.WriteLine(enumerator.EnumerateAudioEndPoints(DataFlow.Capture, DeviceState.Active)[i]);
//clean up
enumerator.Dispose();
Для получения всех устройств захвата:
//create enumerator
var enumerator = new MMDeviceEnumerator();
//cyckle trough all audio devices
for (int i = 0; i < WaveOut.DeviceCount; i++)
Console.WriteLine(enumerator.EnumerateAudioEndPoints(DataFlow.Render, DeviceState.Active)[i]);
//clean up
enumerator.Dispose();
Я нашел другой способ, используя реестр, чтобы найти полное имя аудиоустройства, как вход, так и выход.
Работает на Windows 7 и Windows 10.
Этот метод пробует подход Адама М. в первую очередь. Его метод не работает для меня, но на всякий случай, если он работает для вас, я добавил в качестве предпочтительного метода.
procedure TForm_Config.FormCreate(Sender: TObject);
type
tagWAVEOUTCAPS2A = packed record
wMid: WORD;
wPid: WORD;
vDriverVersion: MMVERSION;
szPname: array[0..MAXPNAMELEN-1] of AnsiChar;
dwFormats: DWORD;
wChannels: WORD;
wReserved1: WORD;
dwSupport: DWORD;
ManufacturerGuid: System.TGUID;
ProductGuid: System.TGUID;
NameGuid: System.TGUID;
end;
var
i,outdevs: Integer;
woCaps: tagWAVEOUTCAPS2A;
RegistryService: TRegistry;
iClasses, iSubClasses, iNames: Integer;
audioDeviceClasses, audioDeviceSubClasses, audioDeviceNames: TStringList;
initialDeviceName, partialDeviceName, fullDeviceName: string;
begin
audioDeviceClasses := TStringList.Create;
audioDeviceSubClasses := TStringList.Create;
audioDeviceNames := TStringList.Create;
try
RegistryService := TRegistry.Create;
try
RegistryService.RootKey := HKEY_LOCAL_MACHINE;
if RegistryService.OpenKeyReadOnly('\SYSTEM\CurrentControlSet\Enum\HDAUDIO\') then begin
RegistryService.GetKeyNames(audioDeviceClasses);
RegistryService.CloseKey();
for iClasses := 0 to audioDeviceClasses.Count - 1 do begin
if RegistryService.OpenKeyReadOnly('\SYSTEM\CurrentControlSet\Enum\HDAUDIO\'+audioDeviceClasses[iClasses]) then begin
RegistryService.GetKeyNames(audioDeviceSubClasses);
RegistryService.CloseKey();
for iSubClasses := 0 to audioDeviceSubClasses.Count - 1 do begin
if RegistryService.OpenKeyReadOnly('\SYSTEM\CurrentControlSet\Enum\HDAUDIO\'+audioDeviceClasses[iClasses]+'\'+audioDeviceSubClasses[iSubClasses]) then begin
if RegistryService.ValueExists('DeviceDesc') then begin
fullDeviceName := Trim(RegistryService.ReadString('DeviceDesc'));
if AnsiPos(';',fullDeviceName) > 0 then begin
fullDeviceName := Trim(AnsiMidStr(fullDeviceName, AnsiPos(';',fullDeviceName)+1, Length(fullDeviceName)));
end;
audioDeviceNames.Add(fullDeviceName);
end;
RegistryService.CloseKey();
end;
end;
end;
end;
end;
finally
FreeAndNil(RegistryService);
end;
// WaveOutDevComboBox is a selection box (combo) placed in the form and will receive the list of output audio devices
WaveOutDevComboBox.Clear;
try
outdevs := waveOutGetNumDevs;
for i := 0 to outdevs - 1 do begin
ZeroMemory(@woCaps, sizeof(woCaps));
if waveOutGetDevCaps(i, @woCaps, sizeof(woCaps)) = MMSYSERR_NOERROR then begin
RegistryService := TRegistry.Create;
try
RegistryService.RootKey := HKEY_LOCAL_MACHINE;
if RegistryService.OpenKeyReadOnly('\System\CurrentControlSet\Control\MediaCategories\' + GUIDToString(woCaps.NameGuid)) then begin
WaveOutDevComboBox.Items.Add(RegistryService.ReadString('Name'));
RegistryService.CloseKey();
end
else begin
initialDeviceName := '';
partialDeviceName := Trim(woCaps.szPname);
if AnsiPos('(',partialDeviceName) > 0 then begin
initialDeviceName := Trim(AnsiLeftStr(partialDeviceName,AnsiPos('(',partialDeviceName)-1));
partialDeviceName := Trim(AnsiMidStr(partialDeviceName,AnsiPos('(',partialDeviceName)+1,Length(partialDeviceName)));
if AnsiPos(')',partialDeviceName) > 0 then begin
partialDeviceName := Trim(AnsiLeftStr(partialDeviceName,AnsiPos(')',partialDeviceName)-1));
end;
end;
for iNames := 0 to audioDeviceNames.Count - 1 do begin
fullDeviceName := audioDeviceNames[iNames];
if AnsiStartsText(partialDeviceName,fullDeviceName) then begin
break;
end
else begin
fullDeviceName := partialDeviceName;
end;
end;
WaveOutDevComboBox.Items.Add(initialDeviceName + IfThen(initialDeviceName<>EmptyStr,' (','') + fullDeviceName + IfThen(initialDeviceName<>EmptyStr,')',''));
end;
finally
FreeAndNil(RegistryService);
end;
end;
end;
except
WaveOutDevComboBox.Enabled := False;
end;
finally
FreeAndNil(audioDeviceClasses);
FreeAndNil(audioDeviceSubClasses);
FreeAndNil(audioDeviceNames);
end;
end;