/// 基于portaudio的音频设备名称对应,MME模式时名称存在被切断的情况,
/// 此时用WASAPI模式下可以得到完整设备名称,再将序号对齐于MME。
/// 其次WASAPI模式下可以天然过滤"windows下的混响"设备。
///
#include <utils/StringUtils.hpp>
#ifdef _WIN32
#include <MMDeviceAPI.h>
#include <devicetopology.h>
#include <ksmedia.h>
#include <functiondiscoverykeys_devpkey.h>
#endif
#include <string>
#include <vector>
/// <summary>
/// 输入输出设备信息
/// </summary>
struct DeviceInfo {
int id = 0;
std::string deviceId;
int structVersion = 0; /* this is struct version 2 */
std::string name;
PaHostApiIndex hostApi = 0; /**< note this is a host API index, not a type id*/
int maxInputChannels = 0;
int maxOutputChannels = 0;
/** Default latency values for interactive performance. */
PaTime defaultLowInputLatency = 0.0;
PaTime defaultLowOutputLatency = 0.0;
/** Default latency values for robust non-interactive applications (eg. playing sound files). */
PaTime defaultHighInputLatency = 0.0;
PaTime defaultHighOutputLatency = 0.0;
double defaultSampleRate = 0.0;
DeviceInfo() {}
DeviceInfo(int id, const std::string& deviceId, const PaDeviceInfo& devinfo)
{
this->id = id;
this->deviceId = deviceId;
operator=(devinfo);
}
DeviceInfo(int id, const DeviceInfo& devinfo)
{
this->id = id;
this->deviceId = devinfo.deviceId;
operator=(devinfo);
}
DeviceInfo& operator=(const PaDeviceInfo& devinfo)
{
return set(devinfo);
}
DeviceInfo& operator=(const DeviceInfo& devinfo)
{
return set(devinfo);
}
template <typename Info>
DeviceInfo& set(const Info& devinfo)
{
structVersion = devinfo.structVersion;
name = typeid(Info) == typeid(DeviceInfo) ? devinfo.name : mp::utils::Utf82Ascii(devinfo.name);
hostApi = devinfo.hostApi;
maxInputChannels = devinfo.maxInputChannels;
maxOutputChannels = devinfo.maxOutputChannels;
defaultLowInputLatency = devinfo.defaultLowInputLatency;
defaultLowOutputLatency = devinfo.defaultLowOutputLatency;
defaultHighInputLatency = devinfo.defaultHighInputLatency;
defaultHighOutputLatency = devinfo.defaultHighOutputLatency;
defaultSampleRate = devinfo.defaultSampleRate;
return *this;
}
std::string to_string()
{
return (std::string)*this;
}
operator std::string()
{
std::stringstream ss;
ss << "{\"devinfo\":{\"id\":" << id
<< ",\"deviceId\":\"" << deviceId << "\""
<< ",\"structVersion\":" << structVersion
<< ",\"name\":\"" << name << "\""
<< ",\"hostApi\":" << hostApi
<< ",\"maxInputChannels\":" << maxInputChannels
<< ",\"maxOutputChannels\":" << maxOutputChannels
<< ",\"defaultLowInputLatency\":" << defaultLowInputLatency
<< ",\"defaultLowOutputLatency\":" << defaultLowOutputLatency
<< ",\"defaultHighInputLatency\":" << defaultHighInputLatency
<< ",\"defaultHighOutputLatency\":" << defaultHighOutputLatency
<< ",\"defaultSampleRate\":" << defaultSampleRate << "}}";
return ss.str();
}
};
#ifdef _WIN32
/// <summary>
/// 通过COM组件获取输入输出设备编号信息
/// </summary>
/// <param name="isInputDevice"></param>
/// <returns></returns>
static std::vector<std::pair<std::string, std::string>> getDeviceFromComList(bool isInputDevice = true)
{
std::vector<std::pair<std::string, std::string>> deviceIdNames;
IMMDeviceEnumerator* pMMDeviceEnumerator = nullptr;
IMMDeviceCollection* pMMDeviceCollection = nullptr;
HRESULT hr = CoCreateInstance(
__uuidof(MMDeviceEnumerator), nullptr, CLSCTX_ALL,
__uuidof(IMMDeviceEnumerator),
reinterpret_cast<void**>(&pMMDeviceEnumerator));
if (FAILED(hr))
{
printf("create instance failed: error code: %d,%d\n", GetLastError(), hr);
return deviceIdNames;
}
// get all the active render endpoints
hr = pMMDeviceEnumerator->EnumAudioEndpoints(isInputDevice ? eCapture : eRender, DEVICE_STATE_ACTIVE, &pMMDeviceCollection);
if (FAILED(hr))
{
printf("enum audio endpoints failed: error code: %d,%x\n", GetLastError(), hr);
return deviceIdNames;
}
uint32_t devicecount = 0;
pMMDeviceCollection->GetCount(&devicecount);
deviceIdNames.resize(devicecount);
deviceIdNames.clear();
for (int i = 0; i < devicecount; i++)
{
IMMDevice* pMMDevice = nullptr;
IPropertyStore* pPropertyStore = nullptr;
PROPVARIANT pv;
PropVariantInit(&pv);
hr = pMMDeviceCollection->Item(i, &pMMDevice);
if (FAILED(hr))
{
printf("enum item fail: error code: %d, hresult %x\n", GetLastError(), hr);
continue;
}
LPWSTR pwszDeviceId = nullptr;
hr = pMMDevice->GetId(&pwszDeviceId);
if (FAILED(hr))
{
printf("get device id fail: error code: %d\n", GetLastError());
pMMDevice->Release();
continue;
}
std::wstring id(pwszDeviceId);
free(pwszDeviceId);
// open the property store on that device
hr = pMMDevice->OpenPropertyStore(STGM_READ, &pPropertyStore);
if (FAILED(hr))
{
printf("open property store fail: error code: %d", GetLastError());
continue;
}
// get the long name property
hr = pPropertyStore->GetValue(PKEY_Device_FriendlyName, &pv);
if (FAILED(hr))
{
printf("get value fail: error code: %d", GetLastError());
continue;
}
std::wstring name(pv.pwszVal);
PropVariantClear(&pv);
pPropertyStore->Release();
deviceIdNames.push_back(std::make_pair(mp::utils::WideByte2Ascii(id), mp::utils::WideByte2Ascii(name)));
pMMDevice->Release();
}
pMMDeviceCollection->Release();
pMMDeviceEnumerator->Release();
return deviceIdNames;
}
#endif
/// <summary>
/// 获取(输入/输出)设备列表
/// </summary>
/// <param name="isInputDevice">true为输入设备,否则为输出设备</param>
/// <returns>设为列表</returns>
std::vector<DeviceInfo> getDeviceList(bool isInputDevice = true)
{
std::vector<DeviceInfo> devices;
auto devcount = Pa_GetDeviceCount();
#ifdef _WIN32
auto hostapiWasapi = Pa_HostApiTypeIdToHostApiIndex(paWASAPI);
auto hostapiMme = Pa_HostApiTypeIdToHostApiIndex(paMME);
auto deviceIdNames = getDeviceFromComList(isInputDevice);
std::vector<DeviceInfo> wases(devcount);
std::vector<DeviceInfo> mmes(devcount);
wases.clear();
mmes.clear();
// 检索portaudio里的MME和WASAPI
for (int i = 0; i < devcount; i++)
{
auto devinfo = Pa_GetDeviceInfo(i);
if ((!isInputDevice && devinfo->maxOutputChannels > 0)
|| (isInputDevice && devinfo->maxInputChannels > 0))
{
if (devinfo->hostApi == hostapiWasapi) {
auto name = mp::utils::Utf82Ascii(devinfo->name);
for (auto& devidname : deviceIdNames) {
if (devidname.second == name) {
wases.push_back(DeviceInfo(i, devidname.first, *devinfo));
break;
}
}
}
else if (devinfo->hostApi == hostapiMme) {
mmes.push_back(DeviceInfo(i, "", *devinfo));
}
}
}
// 找到 WASAPI 对应的 MME 编号
devices.resize(wases.size());
devices.clear();
for (auto& devinfo : wases)
{
for (int j = 0; j < mmes.size(); j++)
{
auto& devinfotemp = mmes[j];
if (devinfo.name.find(devinfotemp.name) == 0)
{
devices.push_back(DeviceInfo(devinfotemp.id, devinfo));
break;
}
}
}
#else
#endif
return devices;
}
int main()
{
Pa_Initialize();
bool isInputDevice = false;
auto deviceList = getDeviceList(isInputDevice);
for (auto& device : deviceList) {
printf("%s\n", device.to_string().c_str());
}
return 0;
}
01-29
“相关推荐”对你有帮助么?
-
非常没帮助
-
没帮助
-
一般
-
有帮助
-
非常有帮助
提交