C++ 如何在选定的输出设备上使用本机API在windows上播放声音(mp3/wav)
我只是希望能够为我的程序创建选项,以便用户可以选择将用于播放声音的输出设备,如MS Lync中的以下设备: 我最初在Qt中创建了我的程序,我在这里问了类似(但不完全相同)的问题 我发现Qt上的bug太多了,这是不可能的,所以我降低了要求,我将使用本机windows API,因为这可能是这里唯一的解决方案。不幸的是,这需要重写我程序的某些部分,现在我遵循msdn指南: 我基本上希望能够做到以下几点:C++ 如何在选定的输出设备上使用本机API在windows上播放声音(mp3/wav),c++,windows,C++,Windows,我只是希望能够为我的程序创建选项,以便用户可以选择将用于播放声音的输出设备,如MS Lync中的以下设备: 我最初在Qt中创建了我的程序,我在这里问了类似(但不完全相同)的问题 我发现Qt上的bug太多了,这是不可能的,所以我降低了要求,我将使用本机windows API,因为这可能是这里唯一的解决方案。不幸的是,这需要重写我程序的某些部分,现在我遵循msdn指南: 我基本上希望能够做到以下几点: 列出所有可用的输出设备,并将它们显示在首选项表单上-我已经有了使用IMMDeviceEnume
- 列出所有可用的输出设备,并将它们显示在首选项表单上-我已经有了使用
IMMDeviceEnumerator的工作代码
- 让用户选择一个他们想用于我的程序输出的设备-我已经有了那个部分
- 创建一个函数,我们称之为
,如果使用.wav或.mp3文件的路径调用,将使用首选的PlaySound(stringpath)
并通过它播放文件-这就是我需要的帮助IMMDevice
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));
}
这段代码本身当然不会开箱即用,但它为您提供了一条如何做到这一点的线索,简而言之:
msdn文档:没有低级音频API会为您播放mp3,它们通常只处理原始pcm数据。幸运的是,一个.wav文件就是这样——一个pcm音频流,前面加了一个WAVEFORMAT结构。要播放它,你必须从磁盘上读取,必要时解码(对于mp3等),然后将其输入音频引擎。如果使用WASAPI,您还必须自己处理重采样。总而言之,我会选择一些更易于管理的东西,比如portaudio+一个非pcm格式的解码器库。如果你想走wasapi路线,你给出的例子几乎是最低限度的。