noahbkim
noahbkim

Reputation: 538

Correctly setting up MMDevice in a DirectX project

I am currently trying to piece together a shader-based music visualizer. The plan is to read data from the current MMDevice, which I'm trying to follow the documentation for, but I must be doing something wrong because I had to jump through all sorts of hoops to get even just the MMDeviceEnumerator to compile.

In order for the uuids of MMDeviceEnumerator and IMMDeviceEnumerator to be defined, I had to set #define WINAPI_FAMILY WINAPI_FAMILY_GAMES. This was also required for EDataFlow and ERole enumerations to be defined. My first question is if I've missed some configuration somewhere, or if this is the intended method of enabling these things.

Currently, I have the following code in an AudioStream class:

class AudioStream {
public:
    AudioStream() {
        //SUCCEEDING(CoInitializeEx(nullptr, COINIT_MULTITHREADED));
        SUCCEEDING(CoCreateInstance(
            __uuidof(IMMDeviceEnumerator),
            NULL,
            CLSCTX_ALL, 
            __uuidof(MMDeviceEnumerator),
            (void**)&this->mmDeviceEnumerator));
        SUCCEEDING(this->mmDeviceEnumerator->GetDefaultAudioEndpoint(
            eRender,
            eConsole,
            &this->mmDevice));
    }

private: 
    IAudioClient* audioClient = NULL;
    IAudioCaptureClient* captureClient = NULL;
    IMMDeviceEnumerator* mmDeviceEnumerator = NULL;
    IMMDevice* mmDevice = NULL;
};

If you're familiar with what the DirectX 12 project template looks like, this object is being instantiated in the Sample3DSceneRenderer constructor. The main issue I'm having right now is the following two errors which are immediately raised during startup:

onecore\com\combase\dcomrem\resolver.cxx(2299)\combase.dll!75AA0DFF: (caller: 75B1CF2C) ReturnHr(1) tid(42a8) 80040154 Class not registered
onecore\com\combase\dcomrem\resolver.cxx(2507)\combase.dll!75B1CF4D: (caller: 75AA29E4) ReturnHr(2) tid(42a8) 80040154 Class not registered

This causes the entire app to hang, and the project template visualization to never appear (the succeeding macro exits). Does anyone have any idea why this is failing? It must have to be something with the CoCreateInstance call :(

Upvotes: 2

Views: 598

Answers (1)

Chuck Walbourn
Chuck Walbourn

Reputation: 41127

You are writing a Universal Windows Platform (UWP) app because that's what the "built-in" DirectX 12 App project template creates in Visual Studio. UWPs do not have access to all the same APIs and IMMDevice is not part of the UWP API surface area.

The fact that you defined WINAPI_FAMILY_GAMES means you hacked the API Family Partition macros which will define the API in a UWP context, but it doesn't mean that API actually works from the AppContainer process that all UWPs run in.

You really have two options:

(1) If you want to write a UWP, then you will need to enumerate audio devices via the proper Windows Runtime APIs which are in the Windows::Devices::Enumeration namespace.

Assuming you are using C++/CX language extensions (instead of the more modern C++/WinRT projections), then this code works:

auto operation = DeviceInformation::FindAllAsync(DeviceClass::AudioRender);
while (operation->Status == Windows::Foundation::AsyncStatus::Started)
{
    Sleep(100);
}
if (operation->Status != Windows::Foundation::AsyncStatus::Completed)
{
    throw std::runtime_error("FindAllAsync");
}

DeviceInformationCollection^ devices = operation->GetResults();

for (unsigned i = 0; i < devices->Size; ++i)
{
    using Windows::Devices::Enumeration::DeviceInformation;

    DeviceInformation^ d = devices->GetAt(i);

    // d->Id->Data();
    // d->Name->Data();
}

Also, if you want to get access to the audio capture device from a UWP, you must add a capability to your manifest to request it via <DeviceCapability Name="microphone"/>. See Microsoft Docs.

You should take the time to read the Microsoft Docs on UWPs so you have a better idea of what's supported and what's not.

(2) If you want to write a Win32 desktop app, use the directx-vs-templates instead which include DirectX 12 starting templates for Win32 desktop apps (plus alternative DirectX templates for UWP if that's your thing).

Whichever appmodel you use, you may want to take a look at DirectX Tool Kit for Audio.

BTW, WINAPI_FAMILY_GAMES is used by the Microsoft GDK for Xbox which is for writing titles for Xbox One and Xbox Series X|S. It uses Win32 APIs and doesn't use Windows Runtime APIs, so it has the IMMDevice interface in it's API surface. See Microsoft Docs.

Upvotes: 3

Related Questions