gpt4 book ai didi

c++ - 如何在选定的输出设备上使用 native API 在 Windows 上播放声音(mp3/wav)

转载 作者:行者123 更新时间:2023-12-02 10:25:39 25 4
gpt4 key购买 nike

我只是希望能够为我的程序创建选项,以便用户可以选择将用于播放声音的输出设备,例如 MS Lync 中的这个:

enter image description here

我最初在 Qt 中创建了我的程序,我在这里问了类似(但不相同)的问题 Qt5+ How to set default audio device for QMediaPlayer

我发现 Qt 对此有太多的错误,这是不可能的,所以我降低了我的要求,我将使用 native Windows API,因为这些可能是这里唯一的解决方案。不幸的是,这需要重写我的程序的某些部分,现在我在 msdn 上遵循此指南:https://msdn.microsoft.com/en-us/library/windows/desktop/dd371455%28v=vs.85%29.aspx

我基本上希望能够做到以下几点:

  • 列出所有可用的输出设备并在首选项表单上显示它们 - 我已经有一个使用 IMMDeviceEnumerator 的工作代码
  • 让用户选择他们想要用于输出我的程序的设备 - 我已经有了该部分
  • 创建一个函数,我们称之为PlaySound(string path)如果使用 .wav 或 .mp3 文件的路径调用将使用首选 IMMDevice并通过它播放文件 - 这就是我需要帮助的

  • 因为到目前为止我一直在使用 Qt,而且我几乎不知道 MS Windows 的内部结构,所以我不知道如何获取存储在磁盘上某处的文件并使用 Windows API 播放它,尤其是使用所选的 IMMDevice。哪个用户在他们的偏好中设置。我在谷歌上搜索和搜索文档,但我只能解决极其复杂和奇怪的解决方案,例如 https://msdn.microsoft.com/en-us/library/windows/desktop/dd316756%28v=vs.85%29.aspx

    我什至可以找到一些可以使用 MCI 播放 mp3 文件的示例。设备,但这并没有真正解释如何更改首选输出设备,因此它对我的使用不是很有用。

    我知道低级 API 可能不会提供一些简单的“playmyfile”功能,但是至少有一些 super 简单解决方案的例子或一些教程会很好地使用 Windows 上的选定输出设备播放媒体文件这样我就可以将其用作起始引用。我有一个工作活跃 IMMDevice ,现在我只需要让通过它播放mp3/wav文件成为可能。

    注意:这不是一些通用的“如何在 Windows 上播放声音”问题。我需要能够在 上播放那个声音选择的音频输出设备 .仅适用于我的程序(就像 MS Lync、VLC 媒体播放器或任何其他高级音频程序一样)。我不想更改系统全局首选项(默认设备等)。

    最佳答案

    我设法做到了这一点,但令人惊讶的是,它使用了名为“DirectShow”的 Windows native 库,该库主要用于视频渲染,但也可以处理音频。

    如何:

    枚举输出设备
    此函数遍历操作系统检测到的所有音频设备并将它们存储在列表中。

    void Options::Initialize()
    {
    #ifdef WIN
    HRESULT hr;
    ICreateDevEnum *pSysDevEnum = NULL;
    hr = CoCreateInstance(CLSID_SystemDeviceEnum, NULL, CLSCTX_INPROC_SERVER, IID_ICreateDevEnum, (void **)&pSysDevEnum);
    if (FAILED(hr))
    return;

    IEnumMoniker *pEnumCat = NULL;
    hr = pSysDevEnum->CreateClassEnumerator(CLSID_AudioRendererCategory, &pEnumCat, 0);
    if (hr == S_OK)
    {
    // Enumerate the monikers.
    IMoniker *pMoniker = NULL;
    ULONG cFetched;
    while (pEnumCat->Next(1, &pMoniker, &cFetched) == S_OK)
    {
    IPropertyBag *pPropBag;
    hr = pMoniker->BindToStorage(0, 0, IID_IPropertyBag, (void **)&pPropBag);
    if (SUCCEEDED(hr))
    {
    // To retrieve the filter's friendly name, do the following:
    VARIANT varName;
    VariantInit(&varName);
    hr = pPropBag->Read(L"FriendlyName", &varName, 0);
    if (SUCCEEDED(hr))
    {
    OutputDevice device;
    device.Name = QString((QChar*)varName.bstrVal, wcslen(varName.bstrVal));
    Options::devices.append(device);
    }
    VariantClear(&varName);
    pPropBag->Release();
    }
    pMoniker->Release();
    }
    pEnumCat->Release();
    }
    pSysDevEnum->Release();
    #endif
    }

    为用户选择的设备创建过滤器
    再次遍历所有设备并为用户选择的设备制作过滤器
    HRESULT hr;
    ICreateDevEnum *pSysDevEnum = NULL;
    hr = CoCreateInstance(CLSID_SystemDeviceEnum, NULL, CLSCTX_INPROC_SERVER, IID_ICreateDevEnum, (void **)&pSysDevEnum);
    if (FAILED(hr))
    {
    Error("Failed SystemDeviceEnum");
    return;
    }

    IEnumMoniker *pEnumCat = NULL;
    QSettings s;
    hr = pSysDevEnum->CreateClassEnumerator(CLSID_AudioRendererCategory, &pEnumCat, 0);
    IBaseFilter *pFilter = NULL;
    if (hr == S_OK)
    {
    // Enumerate the monikers.
    IMoniker *pMoniker = NULL;
    ULONG cFetched;
    int i = 0;
    while (pEnumCat->Next(1, &pMoniker, &cFetched) == S_OK)
    {
    IPropertyBag *pPropBag;
    hr = pMoniker->BindToStorage(0, 0, IID_IPropertyBag, (void **)&pPropBag);
    if (SUCCEEDED(hr))
    {
    // retrieve the filter's friendly name now
    VARIANT varName;
    VariantInit(&varName);
    hr = pPropBag->Read(L"FriendlyName", &varName, 0);
    if (SUCCEEDED(hr))
    {
    QString name = QString((QChar*)varName.bstrVal, wcslen(varName.bstrVal));
    if (s.value("d:" + name).toBool())
    {
    hr = pMoniker->BindToObject(NULL, NULL, IID_IBaseFilter, (void**)&pFilter);
    // now we got the filter in pFilter so we can play sound using that filter
    PlayWin(pFilter, path);
    }
    }
    VariantClear(&varName);
    pPropBag->Release();
    }
    pMoniker->Release();
    }
    pEnumCat->Release();
    }
    pSysDevEnum->Release();

    使用我们设备的过滤器播放声音
    在这个函数中 device是来自前一个函数的 pFilter
    HRESULT hr = CoCreateInstance(CLSID_FilterGraph, NULL, CLSCTX_INPROC_SERVER, __uuidof(IGraphBuilder), (void **)&x->pGraph);
    if (FAILED(hr))
    {
    Error("ERROR - Could not create the Filter Graph Manager.");
    return;
    }

    hr = x->pGraph->QueryInterface(IID_IBasicAudio, (void**)&x->pOutput);

    if (FAILED(hr))
    {
    Error("ERROR - Could not create the IBasicAudio.");
    return;
    }

    x->pFlx = device;
    if (device)
    x->pGraph->AddFilter(device, L"fd");
    hr = x->pGraph->QueryInterface(__uuidof(IMediaControl), (void **)&x->pControl);
    hr = x->pGraph->QueryInterface(__uuidof(IMediaEvent), (void **)&x->pEvent);

    // Build the graph.
    hr = x->pGraph->RenderFile(path, NULL);
    if (SUCCEEDED(hr))
    {
    // Run the graph.
    hr = x->pControl->Run();
    }
    else
    {
    Error("Unable to play: " + QString::fromWCharArray(path));
    }

    这段代码本身当然不会开箱即用,但简而言之,它为您提供了如何执行此操作的线索:
  • 检索所有设备的列表并将其存储在某处,以便我们可以为用户
  • 创建对话框
  • 在播放声音之前,我们会检查用户选择了哪个设备并为其创建过滤器
  • 我们将过滤器应用于 DirectShow BasicAudio,它本身能够播放系统编解码器支持的任何媒体文件。

  • msdn 上的文档: https://msdn.microsoft.com/en-us/library/windows/desktop/dd407292%28v=vs.85%29.aspx

    关于c++ - 如何在选定的输出设备上使用 native API 在 Windows 上播放声音(mp3/wav),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/28414231/

    25 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com