[英]How to play a sound (mp3 / wav) on windows using native API on selected output device
我只是希望能夠為我的程序創建選項,以便用戶可以選擇使用哪個輸出設備來播放聲音,例如MS Lync中的一個:
我最初是在Qt中創建程序的,我在這里問了類似(但不完全相同)的問題Qt5 +如何為QMediaPlayer設置默認音頻設備
我發現Qt對此很煩人,這是不可能的,所以我降低了要求,我將使用本機Windows API,因為這些可能只是這里的解決方案。 不幸的是,這需要重寫程序的某些部分,現在我在msdn上遵循此指南: https : //msdn.microsoft.com/zh-cn/library/windows/desktop/dd371455%28v=vs.85%29的.aspx
我基本上希望能夠做到以下幾點:
IMMDeviceEnumerator
的工作代碼 PlaySound(string path)
,如果使用.wav或.mp3文件的PlaySound(string path)
調用該函數,則會使用首選的IMMDevice
並通過它播放文件- 這是我需要的幫助 因為到目前為止我一直在使用Qt,而且我幾乎IMMDevice
MS Windows內部結構,所以我不知道一個人如何才能獲取存儲在磁盤上某個位置的文件並使用Windows API播放它,特別是使用用戶在其首選項中設置的所選IMMDevice
。 我正在使用Google搜索和搜索文檔,但是只能使用極其復雜和奇怪的解決方案,例如https://msdn.microsoft.com/en-us/library/windows/desktop/dd316756%28v=vs.85%29的.aspx
我什至可以找到一些示例,您可以在其中使用MCI
設備播放mp3文件,但這並沒有真正說明如何更改首選輸出設備,因此它對我的使用不是很有用。
我知道低級API可能不會提供一些簡單的“ playmyfile”功能,但是最好至少有一些超簡單解決方案的示例或一些教程,這些教程可以在Windows上使用選定的輸出設備來播放媒體文件以便我可以以此為起點。 我有一個正在運行的活動IMMDevice
,現在我只需要使其可以播放mp3 / wav文件即可。
注意:這不是一些通用的“如何在Windows上播放聲音”問題。 我需要能夠在選定的音頻輸出設備上播放該聲音。 僅對於我的程序(就像MS Lync,VLC媒體播放器或任何其他高級音頻程序一樣)。 我不想更改系統全局首選項(默認設備等)。
我設法做到了這一點,但是令人驚訝的是使用了Windows本機庫“ DirectShow”,該庫主要用於視頻渲染,但也可以處理音頻。
如何:
枚舉輸出設備此功能遍歷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/zh-cn/library/windows/desktop/dd407292%28v=vs.85%29.aspx
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.