2

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.

dev_null
  • 1,907
  • 1
  • 16
  • 25
  • What's bad about the answer? Do you expect supported capture formats natively supported by a capture device to change depending on the API used to query for them? If so, why? – IInspectable May 17 '18 at 18:15
  • @IInspectable: The problem with `IAudioClient::IsFormatSupported` is that you can't say to the device 'Tell me all the things you can do', like you can on the Mac. Instead, you have to take pot-shots in the dark until you score a hit. This bit me recently when I encountered a device that _only_ supports 4 channels. Since my code was only checking for 1 and 2 channels, it didn't believe the device could do anything at all. I have some alternative code I can post. I will do it when I am at my desk (insomnia). – Paul Sanders May 18 '18 at 04:19
  • Upvoted. It's a perfectly fair question, if a little confusingly worded. – Paul Sanders May 18 '18 at 04:20
  • @PaulSanders: That may be a valid concern, but it's simply not, what this question is asking for, and I'm having a really hard time reading that into the question. But if that is indeed what the OP is after, then this question deserves a down-vote for being unclear. – IInspectable May 18 '18 at 10:45
  • @IInspectable: it was clear enough to me, but I do have experience in the field. And please bear in mind that English appears not to be the OP's first language. You should not downvote just for that - it puts people off. – Paul Sanders May 18 '18 at 10:52

1 Answers1

1

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);
}
Paul Sanders
  • 24,133
  • 4
  • 26
  • 48
  • Thank you, it works! And it looks for me like a revelation. Some hints for those who might read this later: here https://msdn.microsoft.com/en-us/library/windows/desktop/dd370819(v=vs.85).aspx (Device Roles for Legacy Windows Multimedia Applications) is a code sample how to get waveXXX device id from Core Audio IMMDevice. It has one error though, please call cbEndpointId=0 or use DWORD else it won't work in 64 bits. As a second note the function fron the list above require internals of Port Audio, they are publicly accessible and you will need pa_win_wdmks_utils.c to copy paste from there. – dev_null May 19 '18 at 18:00