Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/cplusplus/151.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ 如何在选定的输出设备上使用本机API在windows上播放声音(mp3/wav)_C++_Windows - Fatal编程技术网

C++ 如何在选定的输出设备上使用本机API在windows上播放声音(mp3/wav)

C++ 如何在选定的输出设备上使用本机API在windows上播放声音(mp3/wav),c++,windows,C++,Windows,我只是希望能够为我的程序创建选项,以便用户可以选择将用于播放声音的输出设备,如MS Lync中的以下设备: 我最初在Qt中创建了我的程序,我在这里问了类似(但不完全相同)的问题 我发现Qt上的bug太多了,这是不可能的,所以我降低了要求,我将使用本机windows API,因为这可能是这里唯一的解决方案。不幸的是,这需要重写我程序的某些部分,现在我遵循msdn指南: 我基本上希望能够做到以下几点: 列出所有可用的输出设备,并将它们显示在首选项表单上-我已经有了使用IMMDeviceEnume

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

我最初在Qt中创建了我的程序,我在这里问了类似(但不完全相同)的问题

我发现Qt上的bug太多了,这是不可能的,所以我降低了要求,我将使用本机windows API,因为这可能是这里唯一的解决方案。不幸的是,这需要重写我程序的某些部分,现在我遵循msdn指南:

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

  • 列出所有可用的输出设备,并将它们显示在首选项表单上-我已经有了使用
    IMMDeviceEnumerator的工作代码
  • 让用户选择一个他们想用于我的程序输出的设备-我已经有了那个部分
  • 创建一个函数,我们称之为
    PlaySound(stringpath)
    ,如果使用.wav或.mp3文件的路径调用,将使用首选的
    IMMDevice
    并通过它播放文件-这就是我需要的帮助
因为到目前为止我一直在使用Qt,我对MS windows的内部结构几乎一无所知,所以我不知道如何使用windows API播放存储在磁盘某处的文件,尤其是使用用户在其首选项中设置的选中的
IMMDevice
。我在谷歌上搜索文档,但我只能使用极其复杂和怪异的解决方案,比如

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

我知道低级API可能不会提供一些简单的“playmyfile”函数,但最好至少有一些超级简单解决方案的示例或一些教程,可以在windows上使用选定的输出设备播放媒体文件,以便我可以将其作为开始参考。我有一个活动的
imm设备
,现在我只需要通过它来播放mp3/wav文件


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

我成功地做到了这一点,但令人惊讶的是,我使用了名为“DirectShow”的windows本机库,它主要用于视频渲染,但也可以处理音频

如何:

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

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();
使用我们设备的过滤器播放声音 在此功能中,设备是前一功能的过滤器

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文档:

    没有低级音频API会为您播放mp3,它们通常只处理原始pcm数据。幸运的是,一个.wav文件就是这样——一个pcm音频流,前面加了一个WAVEFORMAT结构。要播放它,你必须从磁盘上读取,必要时解码(对于mp3等),然后将其输入音频引擎。如果使用WASAPI,您还必须自己处理重采样。总而言之,我会选择一些更易于管理的东西,比如portaudio+一个非pcm格式的解码器库。如果你想走wasapi路线,你给出的例子几乎是最低限度的。