1 Windows系统上切换默认音频输出设备
如果电脑上插了多个音频输出设备(比如同时插了音响和耳机),在Windows系统中一般通过在声音设备页面选择输出设备,通过这种方式切换音频的默认输出设备。
也可以在高级声音选项 - 应用音量和设备首选项单独为某个软件设置音频输入设备和音频输出设备。
2 使用C++切换Windows系统默认音频输出设备
最近在开发中有一个这样的需求,一个软件在某种状态下需要把声音输出到一种音频输出设备上,当一段时间后状态发生变化时需要将声音输出到另一种音频设备上。这样的奇葩的需求就需要我们在软件内部使用代码动态切换Windows系统的默认音频输出设备,这里参考的是这个开源项目。
首先需要声明一个PolicyConfig.h
文件,完整代码如下
// ----------------------------------------------------------------------------
// PolicyConfig.h
// Undocumented COM-interface IPolicyConfig.
// Use for set default audio render endpoint
// @author EreTIk
// ----------------------------------------------------------------------------
#pragma once
interface DECLSPEC_UUID("f8679f50-850a-41cf-9c72-430f290290c8")
IPolicyConfig;
class DECLSPEC_UUID("870af99c-171d-4f9e-af0d-e63df40c2bc9")
CPolicyConfigClient;
// ----------------------------------------------------------------------------
// class CPolicyConfigClient
// {870af99c-171d-4f9e-af0d-e63df40c2bc9}
//
// interface IPolicyConfig
// {f8679f50-850a-41cf-9c72-430f290290c8}
//
// Query interface:
// CComPtr<IPolicyConfig> PolicyConfig;
// PolicyConfig.CoCreateInstance(__uuidof(CPolicyConfigClient));
//
// @compatible: Windows 7 and Later
// ----------------------------------------------------------------------------
interface IPolicyConfig : public IUnknown
{
public:
virtual HRESULT GetMixFormat(
PCWSTR,
WAVEFORMATEX **
);
virtual HRESULT STDMETHODCALLTYPE GetDeviceFormat(
PCWSTR,
INT,
WAVEFORMATEX **
);
virtual HRESULT STDMETHODCALLTYPE ResetDeviceFormat(
PCWSTR
);
virtual HRESULT STDMETHODCALLTYPE SetDeviceFormat(
PCWSTR,
WAVEFORMATEX *,
WAVEFORMATEX *
);
virtual HRESULT STDMETHODCALLTYPE GetProcessingPeriod(
PCWSTR,
INT,
PINT64,
PINT64
);
virtual HRESULT STDMETHODCALLTYPE SetProcessingPeriod(
PCWSTR,
PINT64
);
virtual HRESULT STDMETHODCALLTYPE GetShareMode(
PCWSTR,
struct DeviceShareMode *
);
virtual HRESULT STDMETHODCALLTYPE SetShareMode(
PCWSTR,
struct DeviceShareMode *
);
virtual HRESULT STDMETHODCALLTYPE GetPropertyValue(
PCWSTR,
const PROPERTYKEY &,
PROPVARIANT *
);
virtual HRESULT STDMETHODCALLTYPE SetPropertyValue(
PCWSTR,
const PROPERTYKEY &,
PROPVARIANT *
);
virtual HRESULT STDMETHODCALLTYPE SetDefaultEndpoint(
__in PCWSTR wszDeviceId,
__in ERole eRole
);
virtual HRESULT STDMETHODCALLTYPE SetEndpointVisibility(
PCWSTR,
INT
);
};
interface DECLSPEC_UUID("568b9108-44bf-40b4-9006-86afe5b5a620")
IPolicyConfigVista;
class DECLSPEC_UUID("294935CE-F637-4E7C-A41B-AB255460B862")
CPolicyConfigVistaClient;
// ----------------------------------------------------------------------------
// class CPolicyConfigVistaClient
// {294935CE-F637-4E7C-A41B-AB255460B862}
//
// interface IPolicyConfigVista
// {568b9108-44bf-40b4-9006-86afe5b5a620}
//
// Query interface:
// CComPtr<IPolicyConfigVista> PolicyConfig;
// PolicyConfig.CoCreateInstance(__uuidof(CPolicyConfigVistaClient));
//
// @compatible: Windows Vista and Later
// ----------------------------------------------------------------------------
interface IPolicyConfigVista : public IUnknown
{
public:
virtual HRESULT GetMixFormat(
PCWSTR,
WAVEFORMATEX **
); // not available on Windows 7, use method from IPolicyConfig
virtual HRESULT STDMETHODCALLTYPE GetDeviceFormat(
PCWSTR,
INT,
WAVEFORMATEX **
);
virtual HRESULT STDMETHODCALLTYPE SetDeviceFormat(
PCWSTR,
WAVEFORMATEX *,
WAVEFORMATEX *
);
virtual HRESULT STDMETHODCALLTYPE GetProcessingPeriod(
PCWSTR,
INT,
PINT64,
PINT64
); // not available on Windows 7, use method from IPolicyConfig
virtual HRESULT STDMETHODCALLTYPE SetProcessingPeriod(
PCWSTR,
PINT64
); // not available on Windows 7, use method from IPolicyConfig
virtual HRESULT STDMETHODCALLTYPE GetShareMode(
PCWSTR,
struct DeviceShareMode *
); // not available on Windows 7, use method from IPolicyConfig
virtual HRESULT STDMETHODCALLTYPE SetShareMode(
PCWSTR,
struct DeviceShareMode *
); // not available on Windows 7, use method from IPolicyConfig
virtual HRESULT STDMETHODCALLTYPE GetPropertyValue(
PCWSTR,
const PROPERTYKEY &,
PROPVARIANT *
);
virtual HRESULT STDMETHODCALLTYPE SetPropertyValue(
PCWSTR,
const PROPERTYKEY &,
PROPVARIANT *
);
virtual HRESULT STDMETHODCALLTYPE SetDefaultEndpoint(
__in PCWSTR wszDeviceId,
__in ERole eRole
);
virtual HRESULT STDMETHODCALLTYPE SetEndpointVisibility(
PCWSTR,
INT
); // not available on Windows 7, use method from IPolicyConfig
};
然后通过以下代码设置系统的默认音频输出设备
HRESULT SetDefaultAudioPlaybackDevice(LPCWSTR devID)
{
IPolicyConfigVista *pPolicyConfig;
ERole reserved = eConsole;
HRESULT hr = CoCreateInstance(__uuidof(CPolicyConfigVistaClient),
NULL, CLSCTX_ALL, __uuidof(IPolicyConfigVista), (LPVOID *)&pPolicyConfig);
if (SUCCEEDED(hr))
{
hr = pPolicyConfig->SetDefaultEndpoint(devID, reserved);
pPolicyConfig->Release();
}
return hr;
}
不过在设置默认音频输出设备时需要获取系统上所有的音频输出设备Device Id
,然后将获取的音频输出设备Device Id
传参给SetDefaultAudioPlaybackDevice
函数完成默认音频输出设备的切换。
完整的测试代码main.cpp
如下
// EndPointController.cpp : Defines the entry point for the console application.
//
#include <stdio.h>
#include <wchar.h>
#include <tchar.h>
#include "windows.h"
#include "Mmdeviceapi.h"
#include "PolicyConfig.h"
#include "Propidl.h"
#include "Functiondiscoverykeys_devpkey.h"
#include <map>
#include <thread>
#include <chrono>
#include <string>
#include <iostream>
static std::string WCharToMByte(LPCWSTR lpcwszStr)
{
std::string str;
DWORD dwMinSize = 0;
LPSTR lpszStr = NULL;
dwMinSize = WideCharToMultiByte(CP_OEMCP, NULL, lpcwszStr, -1, NULL, 0, NULL, FALSE);
if (0 == dwMinSize)
{
return FALSE;
}
lpszStr = new char[dwMinSize];
WideCharToMultiByte(CP_OEMCP, NULL, lpcwszStr, -1, lpszStr, dwMinSize, NULL, FALSE);
str = lpszStr;
delete[] lpszStr;
return str;
}
static std::wstring string_to_wstring(const std::string& str)
{
std::wstring result;
int len = MultiByteToWideChar(CP_ACP, 0, str.c_str(), -1, NULL, 0);
wchar_t* wstr = new wchar_t[len + 1];
memset(wstr, 0, len + 1);
MultiByteToWideChar(CP_ACP, 0, str.c_str(), -1, wstr, len);
wstr[len] = '\0';
result.append(wstr);
delete[] wstr;
return result;
}
HRESULT SetDefaultAudioPlaybackDevice(LPCWSTR devID)
{
IPolicyConfigVista *pPolicyConfig;
ERole reserved = eConsole;
HRESULT hr = CoCreateInstance(__uuidof(CPolicyConfigVistaClient),
NULL, CLSCTX_ALL, __uuidof(IPolicyConfigVista), (LPVOID *)&pPolicyConfig);
if (SUCCEEDED(hr))
{
hr = pPolicyConfig->SetDefaultEndpoint(devID, reserved);
pPolicyConfig->Release();
}
return hr;
}
std::map<std::string, std::string> GetAudioOutputDevices()
{
std::map<std::string, std::string> all_audio_output_devices_map;
HRESULT hr = CoInitialize(NULL);
if (SUCCEEDED(hr))
{
IMMDeviceEnumerator* pEnum = NULL;
// Create a multimedia device enumerator.
hr = CoCreateInstance(__uuidof(MMDeviceEnumerator), NULL,
CLSCTX_ALL, __uuidof(IMMDeviceEnumerator), (void**)&pEnum);
if (SUCCEEDED(hr))
{
IMMDeviceCollection* pDevices;
// Enumerate the output devices.
hr = pEnum->EnumAudioEndpoints(eRender, DEVICE_STATE_ACTIVE, &pDevices);
if (SUCCEEDED(hr))
{
UINT count;
pDevices->GetCount(&count);
if (SUCCEEDED(hr))
{
for (UINT i = 0; i < count; i++)
{
IMMDevice* pDevice;
hr = pDevices->Item(i, &pDevice);
if (SUCCEEDED(hr))
{
LPWSTR wstrID = NULL;
hr = pDevice->GetId(&wstrID);
if (SUCCEEDED(hr))
{
IPropertyStore* pStore;
hr = pDevice->OpenPropertyStore(STGM_READ, &pStore);
if (SUCCEEDED(hr))
{
PROPVARIANT friendlyName;
PropVariantInit(&friendlyName);
hr = pStore->GetValue(PKEY_Device_FriendlyName, &friendlyName);
if (SUCCEEDED(hr))
{
std::string devices_name = WCharToMByte(friendlyName.pwszVal);
std::string devices_id = WCharToMByte(wstrID);
std::string map_key = std::to_string(i) + " " + devices_name;
all_audio_output_devices_map[map_key] = devices_id;
PropVariantClear(&friendlyName);
}
pStore->Release();
}
}
pDevice->Release();
}
}
}
pDevices->Release();
}
pEnum->Release();
}
}
return all_audio_output_devices_map;
}
int main()
{
std::map<std::string, std::string> all_audio_output_devices_map = GetAudioOutputDevices();
for (auto iter = all_audio_output_devices_map.begin(); iter != all_audio_output_devices_map.end(); iter++)
{
std::wstring w_select_alert_playout_device_id = string_to_wstring(iter->second);
if (w_select_alert_playout_device_id.empty())
continue ;
WCHAR w_buffer_select_alert_playout_device_id[1000] = { 0 };
wmemcpy_s(w_buffer_select_alert_playout_device_id, 1000, w_select_alert_playout_device_id.c_str(), w_select_alert_playout_device_id.size());
LPWSTR temp = w_buffer_select_alert_playout_device_id;
std::cout << "Switch Audio Playout Devices: " << iter->first << std::endl;
SetDefaultAudioPlaybackDevice(temp);
std::this_thread::sleep_for(std::chrono::milliseconds(10000));
}
return 0;
}
在上面的测试程序中,我们想通过GetAudioOutputDevices
函数获取系统上所有的音频输出设备名字和Device ID
的对应表,然后每切换一个设备休眠两秒钟,我们可以在电脑上放一首音乐,然后运行上述代码,就可以听到音乐每隔两秒在各个音频输出设备都进行了输出。
参考
本文作者:StubbornHuang
版权声明:本文为站长原创文章,如果转载请注明原文链接!
原文标题:C++ – Windows系统使用C++切换音频默认输出设备
原文链接:https://www.stubbornhuang.com/2771/
发布于:2023年08月30日 17:17:10
修改于:2023年08月30日 17:17:10
当前分类随机文章推荐
- C++ - 最简单的将文本文件的内容一次性读取到std::string的方法 阅读5663次,点赞4次
- C++ - String literal,字符串关键字R,L,u8,u,U的作用 阅读197次,点赞0次
- C++ - GCC版本与C++标准之间的对应关系 阅读74次,点赞0次
- C++11 - 封装std::thread,增加子线程启动、暂停、唤起、停止功能 阅读5875次,点赞7次
- C++ - 使用Crypto++/CryptoPP加解密库对字符串或者文件进行AES256加密 阅读4589次,点赞1次
- C++11 - 构建一个符合实际应用要求的线程池 阅读1431次,点赞0次
- C++ - 拷贝构造函数与拷贝构造函数调用时机 阅读563次,点赞0次
- C++11 - std::string - stod/stof/stoi/stol/stold/stoll/stoul/stoull,由std::string转换为int/long/float/double等其他类型 阅读4284次,点赞0次
- C++STL容器 - std::vector构造方式与分配值方式总结 阅读1049次,点赞0次
- C++ - std::string与std::wstring相互转换 阅读2454次,点赞0次
全站随机文章推荐
- C++ - vector存储动态指针时正确释放内存 阅读6573次,点赞0次
- 资源分享 - Fundamentals of Computer Graphics, Fourth Edition高清英文PDF下载 阅读13579次,点赞8次
- Pip - 常用命令(安装,卸载,升级第三方库) 阅读3498次,点赞1次
- TensorRT - 使用TensorRT C++ SDK部署模型时推理时间波动不稳定或者推理速度越来越慢的问题 阅读266次,点赞0次
- 简单粗暴:使用pycharm安装对应的Python版本第三方包 阅读3943次,点赞0次
- 深度学习 - 理解LSTM网络[翻译] 阅读569次,点赞0次
- Python3爬虫 - requests的请求响应状态码(requests.status_code) 阅读9654次,点赞4次
- WordPress - 当用户登录失败时在登录界面显示自定义信息 阅读2050次,点赞0次
- Docker - Linux更换国内镜像源 阅读52次,点赞0次
- C++ - 日志库easylogging++初始化时不生成默认日志文件mylog.txt 阅读87次,点赞0次
评论
169