Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Get the full name of a waveIn device

Tags:

winapi

audio

I have been using waveInGetDevCaps to get the name of waveIn devices, but the WAVEINCAPS structure only supports 31 characters plus a null, meaning that on my computer, the device names I get back are truncated:

Microphone / Line In (SigmaTel 
Microphone Array (SigmaTel High, 

I am sure that there must be a way of getting the full device name, but does anyone know what that is?

like image 581
Mark Heath Avatar asked Sep 19 '09 18:09

Mark Heath


5 Answers

Yes, there's a workaround. I've solved this problem several times in shipping code.

Enumerate audio capture devices with DirectSoundCapture. The API is DirectSoundCaptureEnumerate. It will return you the full length name of the devices.

Of course, you're probably thinking "That's great, but the rest of my code is setup to use the Wave API, not DirectSound. I don't want to switch it all over. So how can I map the GUID IDs returned by DirectSoundCaptureEnumerate to the integer IDs used by the WaveIn API?"

The solution is to CoCreateInstance for the DirectSoundPrivate object (or call GetClassObject directly from dsound.dll) to get a pointer to an IKsPropertySet interface. From this interface, you can obtain the DSound GUID to Wave ID mapping. For more details see this web page:

http://msdn.microsoft.com/en-us/library/bb206182(VS.85).aspx

You want to use the DSPROPERTY_DIRECTSOUNDDEVICE_WAVEDEVICEMAPPING as described on the web page listed above.

like image 119
selbie Avatar answered Nov 12 '22 09:11

selbie


Improved/full C# WPF code based on @Andrea Bertucelli answer

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;
        }
    }
}
like image 8
Zunair Avatar answered Nov 12 '22 09:11

Zunair


I completed the names of waveIn devices, exploring the names returned from MMDeviceEnumerator. For each waveIn device, when the name incompleted is part of full name of one of EnumerateAudioEndPoints, I used this full name for populate combobox in the same order of waveIn devices.

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
like image 5
Andrea Bertucelli Avatar answered Nov 12 '22 09:11

Andrea Bertucelli


There's a way involving the registry that's simpler than using DirectSound. If you use the WAVEINCAPS2 structure, it has a name GUID that references a key under HKLM\System\CurrentControlSet\Control\MediaCategories. If the key doesn't exist, then just use the name in the structure. This is documented on http://msdn.microsoft.com/en-us/library/windows/hardware/ff536382%28v=vs.85%29.aspx. Here's an example:

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();
like image 4
Adam M. Avatar answered Nov 12 '22 11:11

Adam M.


Looks like DirectSoundPrivate has some issues. I am trying to access it from an empty project and it works fine. However, when I try to access it from COM DLL or from a DLL thread it returns E_NOTIMPL error from IKsPropertySet::Get.

But I figured out another trick. It seems DirectSound enumerates capture and render devices in wave id order (excluding first default).

We still have to interact with old Wave API and it still lacks a proper way to do that. DirectShow provides audio input devices based on WaveIn and I need to get corresponding a WASAPI id and vice-versa.

like image 3
Vladimir Hmelyoff Avatar answered Nov 12 '22 10:11

Vladimir Hmelyoff