在 DirectX 项目中正确设置 MMDevice

Correctly setting up MMDevice in a DirectX project

我目前正在尝试拼凑一个基于着色器的音乐可视化工具。计划是从当前的 MMDevice 中读取数据,我正在尝试按照文档进行操作,但我一定是做错了什么,因为我不得不跳过各种障碍才能获得 MMDeviceEnumerator 编译。

为了定义 MMDeviceEnumeratorIMMDeviceEnumeratoruuid,我必须设置 #define WINAPI_FAMILY WINAPI_FAMILY_GAMES。这也是定义 EDataFlowERole 枚举所必需的。 我的第一个问题是我是否在某处遗漏了某些配置,或者这是否是启用这些功能的预期方法

目前,我在 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;
};

如果您熟悉 DirectX 12 项目模板的外观,该对象将在 Sample3DSceneRenderer 构造函数中实例化。 我现在遇到的主要问题是在启动过程中立即出现以下两个错误:

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

这会导致整个应用挂起,项目模板可视化永远不会出现(后续宏退出)。有谁知道为什么会失败?它必须与 CoCreateInstance 调用有关:(

您正在编写通用 Windows 平台 (UWP) 应用程序,因为这是“内置”DirectX 12 应用程序 项目模板在 Visual Studio. UWP 无法访问所有相同的 APIs 并且 IMMDevice 不是 UWP API 表面区域的一部分。

您定义 WINAPI_FAMILY_GAMES 的事实意味着您破解了 API 系列分区宏,它将 定义 UWP 上下文中的 API ,但这并不意味着 API 实际上 从所有 UWP 运行 中的 AppContainer 进程中工作

你真的有两个选择:

(1) 如果您想要 编写 UWP,那么您将需要通过适当的 Windows 运行时 API 枚举音频设备,这在 Windows::Devices::Enumeration 命名空间中。

假设您使用的是 C++/CX language extensions (instead of the more modern C++/WinRT projections),则此代码有效:

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.

您应该花时间阅读有关 UWP 的 Microsoft Docs,以便您更好地了解支持和不支持的内容。

(2) 如果您想要 编写 Win32 桌面应用程序,请改用 directx-vs-templates,其中包括用于 Win32 桌面应用程序的 DirectX 12 起始模板(加上替代的 DirectX UWP 模板,如果你喜欢的话)。

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.