Search code examples
winapiwdkwasapi

How to get Audio Formats supported by physical device (WinAPI, Windows)


I have an audio device (a USB microphone) and I want to find out what audio formats it supports natively (bit depth & sample rate), on OS X there is a nice kAudioStreamPropertyAvailablePhysicalFormats Core Audio property, but I fail to find something similar on Windows.

I know there is this question Windows Core Audio Api get all supported formats on capture device but

  • The answer is bad... and I'm not limited with windows Core Audio APIs, I need any way.

  • Windows itself somehow knows it, so most likely there should be a way, via IOCTL for example, DirectShow or WMI or something else.

  • Probably there is a way to Spy on Windows to find out what it uses to enumerate the formats, but I don't know how to do this.


Solution

  • Hokay, here is some sample code for you. The information is out there, scattered around the web, but you have to search for it. Google the functions I call below and some of the weirdo manifest constants to learn more. Code written in Notepad, might not compile.

    The code below queries the default input / output device. To get device_id's for all the devices installed on any particular system, call waveInGetNumDevs or waveOutGetNumDevs and count up from 0.

    #define INITGUID
    
    #include "Ks.h"
    #include "KsMedia.h"
    #include "mmdeviceapi.h"
    
    
    // Open a query handle for the default input or output device
    // Call Closehandle when done.
    static HANDLE QueryOpen (bool input)
    {
        DWORD device_id, device_status;
        DWORD err = (input) ?
            waveInMessage ((HWAVEIN) (INT_PTR) WAVE_MAPPER,
                DRVM_MAPPER_PREFERRED_GET, (DWORD_PTR) &device_id, (DWORD_PTR) &device_status) :
            waveOutMessage ((HWAVEOUT) (INT_PTR) WAVE_MAPPER,
                DRVM_MAPPER_PREFERRED_GET, (DWORD_PTR) &device_id, (DWORD_PTR) &device_status);
        if (err)
            return INVALID_HANDLE_VALUE;
    
        DWORD devicePathSize;
        DWORD mm_result = (input) ? 
            waveInMessage ((HWAVEIN) (INT_PTR) device_id, DRV_QUERYDEVICEINTERFACESIZE,
                (DWORD_PTR) &devicePathSize, 0) :
            waveOutMessage ((HWAVEOUT) (INT_PTR) device_id, DRV_QUERYDEVICEINTERFACESIZE,
                (DWORD_PTR) &devicePathSize, 0);
        if (mm_result != 0)
            return INVALID_HANDLE_VALUE;
    
        /* apparently DRV_QUERYDEVICEINTERFACE returns a unicode interface path, although this is undocumented */
        WCHAR *devicePath = (WCHAR *) malloc (devicePathSize);
    
        mm_result = (input) ?
            waveInMessage ((HWAVEIN) (INT_PTR) device_id, DRV_QUERYDEVICEINTERFACE,
                (DWORD_PTR) devicePath, devicePathSize) :
            waveOutMessage ((HWAVEOUT) (INT_PTR) device_id, DRV_QUERYDEVICEINTERFACE,
                (DWORD_PTR) devicePath, devicePathSize);
    
        HANDLE result = (mm_result == 0) ? CreateFileW (devicePath, FILE_SHARE_READ | FILE_SHARE_WRITE,
            0, NULL, OPEN_EXISTING, 0, NULL) : INVALID_HANDLE_VALUE;
    
        free (devicePath);
        return result;
    }
    
    
    // Interrogate the default input / output device (demo code)
    void InterrogateDefaultDevice (bool input)
    {
        HANDLE hQuery = QueryOpen (input);
        if (hQuery == INVALID_HANDLE_VALUE)
            return;
    
        int pin_count = GetKSFilterPinCount (hQuery);
    
        for (int pinId = 0; pinId < pin_count; ++pinId)
        {
            KSPIN_COMMUNICATION communication = GetKSFilterPinPropertyCommunication (hQuery, pinId);
            KSPIN_DATAFLOW dataflow = GetKSFilterPinPropertyDataflow (h, pinId);
    
            if ((communication == KSPIN_COMMUNICATION_SINK || communication == KSPIN_COMMUNICATION_BOTH) &&
                (KSFilterPinPropertyIdentifiersInclude (hQuery, pinId, KSPROPERTY_PIN_INTERFACES,
                    &KSINTERFACESETID_Standard, KSINTERFACE_STANDARD_STREAMING) ||
                 KSFilterPinPropertyIdentifiersInclude (hQuery, pinId, KSPROPERTY_PIN_INTERFACES,
                    &KSINTERFACESETID_Standard, KSINTERFACE_STANDARD_LOOPED_STREAMING)) &&
                 KSFilterPinPropertyIdentifiersInclude (hQuery, pinId, KSPROPERTY_PIN_MEDIUMS,
                    &KSMEDIUMSETID_Standard, KSMEDIUM_STANDARD_DEVIO))
            {
                KSMULTIPLE_ITEM *item = NULL;
    
                if (WdmGetPinPropertyMulti (hQuery, pinId, KSPROPERTY_PIN_DATARANGES, &item))
                {
                    KSDATARANGE_AUDIO *dr = (KSDATARANGE_AUDIO *) (item + 1);
    
                    for (ULONG i = 0; i < item->Count; ++i )
                    {
                        printf ("%ul - %ul Hz (%ul - %ul bits per sample, upto %ul channels)\n",
                            dr->MinimumSampleFrequency, dr->MaximumSampleFrequency,
                            dr->MinimumBitsPerSample, dr->MaximumBitsPerSample, dr->MaximumChannels);
    
                        dr = (KSDATARANGE_AUDIO *) ((BYTE *) dr + dr->DataRange.FormatSize);
                    }
    
                    free (item);
                }
            }
        }
    
        CloseHandle (hQuery);
    }