我只是希望能够为我的程序创建选项,以便用户可以选择将用于播放声音的输出设备,例如MS Lync中的这个:
我最初在Qt中创建了我的程序,我在这里询问了类似(但不完全相同)的问题Qt5+ How to set default audio device for QMediaPlayer
我发现Qt对此漏洞过多,这是不可能的,所以我降低了我的要求,我将使用原生的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播放它,特别是使用选择的{{1}哪个用户在他们的首选项中设置。我在谷歌搜索并搜索文档,但我只能处理极其复杂和奇怪的解决方案,例如https://msdn.microsoft.com/en-us/library/windows/desktop/dd316756%28v=vs.85%29.aspx
我甚至可以找到一些可以使用IMMDevice
设备播放mp3文件的示例,但这并没有真正解释如何更改首选输出设备,所以它对我的使用并不是很有用。
据我所知,低级API可能不会提供一些简单的“playmyfile”功能,但至少要有一些超简单解决方案的示例或者使用选定输出播放媒体文件的教程会很好Windows上的设备,以便我可以将其用作起始参考。我有一个活跃的MCI
,现在我只需要通过它播放mp3 / wav文件就可以了。
注意:这不是一些通用的“如何在Windows上播放声音”的问题。我需要能够在选定的音频输出设备上播放该声音。仅适用于我的程序(就像MS Lync,VLC媒体播放器或任何其他高级音频程序一样)。我不想更改系统全局首选项(默认设备等)。
答案 0 :(得分:0)
我设法做到这一点,但令人惊讶的是使用名为“DirectShow”的Windows本机库,它们主要用于视频渲染,但也可以处理音频。
如何:
枚举输出设备 此函数迭代OS检测到的所有音频设备,并将它们存储在列表中。
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));
}
这个代码本身当然不会开箱即用,但它简明扼要地为你提供了如何做到这一点的线索:
msdn上的文档:https://msdn.microsoft.com/en-us/library/windows/desktop/dd407292%28v=vs.85%29.aspx