Device Name Truncation with WaveOut.GetCapabilities

Feb 1, 2013 at 11:50 PM
I'm having a bit of a problem distinguishing device names due to the string truncation. I need to support XP users so I'm limited to WaveOut.GetCapabilities.

This might be a possible fix to the problem for the NAudio source.

It appears that the device name length is determined in the WaveOutCapabilities.cs file.
located in the source at ...\NAudio\Wave\MmeInterop\WaveOutCapabilities.cs
line 48 private const int MaxProductNameLength = 32;

I wonder if changing that value (say triple it = 96) will eliminate most of the device name truncation issues?

Then testing this simple code to see if the returned device names are longer.
for (int deviceId = 0; deviceId < deviceCount; deviceId++)
            {
                var capabilities = WaveOut.GetCapabilities(deviceId);
                string name = capabilities.ProductName;
            }
Sorry that I can't test this myself or I would. The NAudio solution source code won't build for me with VS2010-- lots of missing namespaces.
Coordinator
Feb 2, 2013 at 7:27 AM
unfortunately, this is a limitation of the WaveOut APIs themselves. 32 is the value of MAXPNAMELEN as used by WAVEOUTCAPS. Changing this value in NAudio will not get you a longer product name. There is only one workaround I know of and it involves using DirectSound. I'd imagine there might be a way of searching in the registry too.
Feb 2, 2013 at 9:42 AM
Ok, Thanks Mark.
The Ghost of Bill Gates strikes again. Who knew a device name would ever exceed a massive 32 bytes of memory? ;-)

I'll make do with a 32 character name. I'm trying to get away from DirectSound.
Mar 17 at 11:03 AM
Edited Apr 28 at 9:44 AM
Hi, I want to share my solution in c# to match waveout device and MMDevice, but for Windows 7.
Use GetWaveOutEndpointId() to get Guid of the waveout device.
Use this guid to get MMDevice
mmdevice = (new MMDeviceEnumerator()).GetDevice(guid);
Regards.
int DRV_QUERYFUNCTIONINSTANCEID = 0x0811;
int DRV_QUERYFUNCTIONINSTANCEIDSIZE = 0x0812;

[DllImport("winmm.dll")]
static extern Int32 waveOutGetNumDevs();
[DllImport("winmm.dll")]
static extern Int32 waveOutMessage(IntPtr hWaveOut, int uMsg, out int dwParam1, IntPtr dwParam2);
[DllImport("winmm.dll")]
static extern Int32 waveOutMessage(IntPtr hWaveOut, int uMsg, IntPtr dwParam1, int dwParam2);

string GetWaveOutEndpointId(int devNumber)
{
    int cbEndpointId;
    string result = string.Empty;
    waveOutMessage((IntPtr)devNumber, DRV_QUERYFUNCTIONINSTANCEIDSIZE, out cbEndpointId, IntPtr.Zero);
    IntPtr strPtr = Marshal.AllocHGlobal(cbEndpointId);
    waveOutMessage((IntPtr)devNumber, DRV_QUERYFUNCTIONINSTANCEID, strPtr, cbEndpointId);
    result = Marshal.PtrToStringAuto(strPtr);
    Marshal.FreeHGlobal(strPtr);
    return result;
}


public List<WaveOutDevice> GetWaveOutDevices()
{
    List<WaveOutDevice> retVal = new List<WaveOutDevice>();
    foreach (MMDevice d in (new NAudio.CoreAudioApi.MMDeviceEnumerator()).EnumerateAudioEndPoints(DataFlow.Render, DeviceState.All))
    {
        if (d.State != DeviceState.Active) continue;
        WaveOutDevice di = new WaveOutDevice()
        {
            EndpointGuid = d.ID,
            FullName = d.FriendlyName,
            WaveOutDeviceId = -1
        };

        for (int waveOutIdx = 0; waveOutIdx < waveOutGetNumDevs(); waveOutIdx++)
        {
            string guid = GetWaveOutEndpointId(waveOutIdx);
            if (guid == di.EndpointGuid)
            {
                di.WaveOutDeviceId = waveOutIdx;
                break;
            }
        }
        retVal.Add(di);
    }
    return retVal;
}
Coordinator
Mar 17 at 11:09 AM
nice! thanks for sharing
Mar 17 at 11:42 AM
Edited Mar 17 at 11:45 AM
You are welcome Mark! It would be nice to have GetWaveOutEndpointId in WaveOut class in NAudio :) It should be also similar for WaveIn
Coordinator
Mar 17 at 3:25 PM
Sure, you're welcome to submit a pull request (over at Github of course) to add in GetWaveOutEndpointId. Obviously though we can't require that users have access to any MMDevice functions though, since NAudio still supports Windows XP.
Mar 18 at 12:37 PM
I solved this a different way which does not require the DllImport or Marshalling. (maybe be more efficient, not sure)

First You need to do a simple check that the computer is running Windows Vista Or Higher

I create a collection of MMDeviceCollection objects.
Each object has these properties
  • FriendlyName (which is the full device name, not limited to 32 characters)
  • ID (in this format {0.0.0.00000000}.{73d4795c-5a20-4a6d-80ef-6b86e31ea1f1} , note the GUID is included)
    • so then you can just parse out the GUID and can match it to the GUID from the WaveOut.GetCapabilities snippet above
=======================================
C# Partial Code snippet
  MMDeviceEnumerator DevEnum = new MMDeviceEnumerator();

                MMDeviceCollection devCollection = DevEnum.EnumerateAudioEndPoints(DataFlow.Capture, DeviceState.Active);

        string guidStr;
                string friendName = "";
                int j = 0;
                foreach (var item in devCollection)
                {

                    devId = item.ID;  // format {0.0.0.00000000}.{73d4795c-5a20-4a6d-80ef-6b86e31ea1f1};

                    int dotFour = devId.LastIndexOf(".") + 1;
                    guidStr = devId.Substring(dotFour);
                    friendName = item.FriendlyName;
                    
                    // now you have GUID and full device name, call a method to populate your device list as needed

                }