一对airpods引发的程序员脑细胞大死亡事件

本文最后更新于:2022年10月10日 下午

前情提要

众所周知,当你独自宅于自己的小房间时,可能会播放一些**的内容

此时,要是有一只名为Human的生物进入,You则会立刻社会性死亡

为了避免这种情况,我们需要:快速切换音频输出设备(between 扬声器 & 耳机)

But How to make it programmatically?

需求分析

  1. 获取所有音频输出设备
  2. 切换音频输出设备

针对第一点Qt提供了快捷方案(QAudioDeviceInfo):

1
2
3
4
5
6
7
8
9
10
11
QStringList Win::validAudioOutputDevices() //第一个元素就是当前设备
{
QStringList devList;
QList<QAudioDeviceInfo> audioDeviceList = QAudioDeviceInfo::availableDevices(QAudio::AudioOutput);
for (const auto& dev : qAsConst(audioDeviceList)) {
const QString& name = dev.deviceName();
int index = name.lastIndexOf(" ("); //"耳机 (Realtek(R) Audio)"->"耳机"
devList << (index == -1 ? name : name.left(index));
}
return devList;
}

这里的"耳机 (Realtek(R) Audio)"->"耳机"是为了下一步,切换音频输出设备做准备(同时也为一切埋下了隐患)

第二点,切换音频设备,说实话,Qt没有什么办法(不合理啊喂,能获取为什么不能切换),Windows API里也没有什么便捷的办法,寄

但是,找到了一个第三方命令行工具,可以提供切换功能:nircmd.exe

用法如下:

1
nircmd setdefaultsounddevice "耳机" 			#不包含括号内的额外信息(Realtek(R) Audio)

Qt开启外部进程并传参也不是什么难事:

1
2
3
4
5
6
7
8
9
void Win::setActiveAudioOutputDevice(const QString& name)
{
QProcess pro;
pro.setProgram("nircmd");
pro.setArguments(QStringList() << "setdefaultsounddevice" << name);
pro.start();
pro.waitForFinished(2000);
qDebug() << "#Changed Audio Output Device:" << name;
}

之后,切换的逻辑不用我说吧

Okay,已经结束了,就这么简单,散了散了

和平捍卫者

就这样,我快乐地用着这个程序,快乐地在扬声器 & 耳机间反复横跳,好不快活

一切都是那么美好

直到有一天,我向Darli借了一对airpods,库克便告诉了我,什么叫年轻

东窗事发

当我快乐地将airpods连上PC(Windows),便发现airpods的完整设备名叫做:耳机 (Airpods)

好的,我们现在有了三个音频输出设备:

  • 扬声器 (Realtek(R) Audio)
  • 耳机 (Realtek(R) Audio)
  • 耳机 (Airpods)

nircmd在切换设备时,不需要括号中的额外内容,只需要设备名:”耳机”

这不就蛋糕了嘛,这有俩耳机呀,二义性!

根据我多年程序员的直觉来看,我们需要一个唯一标识符,但是显然QAudioDeviceInfo并没有提供相关API

好的,两个parts,获取和切换,都寄了

正片(叠底

好的,开始面向浏览器编程

打开Edge,打开Bing,搜索:Windows C++ 切换音频输出设备

あった

windows代码设置默认音频输出设备_kevin–你不知道的事的博客-CSDN博客

可以看到有亿点点复杂呀,而且用到了COM组件

不过问题不大,talk is cheap,这代码都有了,还能出啥幺蛾子

插电 - 开机!

1
2
3
4
virtual HRESULT STDMETHODCALLTYPE SetDefaultEndpoint( //com接口声明
__in PCWSTR wszDeviceId,
__in ERole eRole
);
1
Warning: '__in' has not been declared

看起来没啥用 不如删了 __in

1
HRESULT hr = CoCreateInstance(__uuidof(CPolicyConfigVistaClient), NULL, CLSCTX_ALL, __uuidof(IPolicyConfigVista), (LPVOID *)&pPolicyConfig);
1
Warning: undefined reference to _GUID const& __mingw_uuidof()

aaaaa o(╥﹏╥)o

打开Edge,打开Bing,搜索:undefined reference to _GUID const& __mingw_uuidof()

Qt5+mingw 在windows系统上调用COM控件时,用到__uuidof()报undefined reference to _GUID const& __mingw_uuidof()_dyj095的博客-CSDN博客

嗯,看起来很有道理,试试:Warning Warning

真是山穷水复疑无路,柳暗花明又一坑啊

蛋糕

1
hr = pStore->GetValue(PKEY_Device_FriendlyName, &friendlyName);
1
Warning: undefined reference to `PKEY_Device_FriendlyName'

HowTime Flies

时间就像海绵里的水,只要你愿意挤,总会被你挤完的

不知道过了多久

岁月不堪数,故人不如初

后来,南山的风吹散了谷堆,北海的水淹没了墓碑

我还是TMD通不过编译

我终于明白了,所谓MinGW编译器和Windows相性差是怎么一回事了

CDSN网友有云:如果是在QT中进行COM编程,则QT的编译套件必须为 MSVC

关于MinGW和MSVC编译器的区别,可以看我上一篇文章:Qt编译器迁移:从MinGW到MSVC - MrBeanC-Blog (mrbeancpp.github.io)

我终于明白了,是时候该换编译器了

大清还未亡呢

等,一下

我是否可以用VS将代码封装为dll,供Qt链接使用呢

VS 2017 导出dll(注意使用.h时,要去掉__declspec(dllexport) 或 改为__declspec(dllimport)

VS是微软的亲儿子,用的也是MSVC编译器,编译以上代码那是如鱼得水

三下五除二,dlllibh新鲜出炉

导入Qt

叮,链接失败

原来,C++dll在不同编译器中命名方式不同,不能跨编译器使用,只能加上extern"C"标志,以C方式导出,但是限制了只能使用C语言

这….

noooooooooooooooooooooooooooooooooooo

改写为C,还是另寻出入,蔡先生陷入了迷茫

躲得过初一,躲不过十五啊,今朝Clang,后朝(chao)可怎么办呢

不能妥协!

小小小补充

  • 封装为exe则不存在以上问题,参考nircmd.exe,但是开启外部进程效率较低
  • 注册为COM组件,供Qt调用(等一下 Qt不是编译不过嘛(但是貌似有什么ActiveX类,貌似,貌似,再说))

大明还坚挺呢

难道,真的要换MSVC编译器了吗,www

Qt + VS联合开发吗,哎

  • 下载VS(2022)的Qt VS Tool
  • Qt中添加MSVC套件

VS中新建Qt项目,熟悉,而又陌生

VS中的Qt项目没有.pro文件,怎么看都不像是亲生的

一看文件夹,好家伙,一个空项目足足204MB,不愧是VS(里面全是文件索引之类的数据库)

成功了吗,但为什么我却一点也开心不起来呢

不,这不是我熟悉的Qt,又大又不完整,noooooooooooo

我要,我要,我就要旺旺碎冰冰

China!

不,我不甘心,不就是个MSVC编译器吗,跟我Qt Creator编辑器有什么关系

我不相信!

插电 - 开机!

切换MSVC编译套件!

切换套件

诶,成了?成了!

WTF

小丑竟是我自己

啊,焯,原来根本不需要VS

Qt + MSVC in Windows yyds

编译 - 运行!爽

Peace

具体切换步骤参考上一篇blog

Ref

有哪些描述时间过得快的好句子? - 知乎 (zhihu.com)

C++获取Windows音频设备列表_xhubobo的博客-CSDN博客

QT 调用 DLL 方法(三种方法) - findumars - 博客园

QT总结第3篇:如何在QT中添加.lib,.dll还有.h文件 - Sankye - 博客园

Qt 加载VS生成的C/C++ dll_Dorthyn的博客-CSDN博客

QT调用VS生成的DLL(无头文件)_qq_40285839的博客-CSDN博客

VS 2017 DLL文件的导出到调用步骤_嘎嘣脆341的博客-CSDN博客_vs导出dll

QT5+如何为QMediaPlayer设置默认音频设备 - IT宝库 (itbaoku.cn)

QT应用编程: windows下QT调用COM组件并集成到QT界面_DS小龙哥的博客-CSDN博客_qt 调用com

Qt5+mingw 在windows系统上调用COM控件时,用到__uuidof()报undefined reference to _GUID const& __mingw_uuidof()_dyj095的博客-CSDN博客

QT - QT中的COM编程(exe进程外组件形式)_bailang_zhizun的博客-CSDN博客

Belphemur/AudioEndPointLibrary: A library based on DefSound to provides access to Default Sound Devices. (github.com)

frgnca/AudioDeviceCmdlets: AudioDeviceCmdlets is a suite of PowerShell Cmdlets to control audio devices on Windows (github.com)

C++设置默认声音输出设备(SetDefaultAudioPlaybackDevice) - 这种人 - 博客园

C++修改默认音频输出设备_c切换音频输出设备-C++代码类资源-CSDN文库

windows代码设置默认音频输出设备_kevin–你不知道的事的博客-CSDN博客

NirCmd - Windows command line tool (nirsoft.net)

View / change sound volume on Windows from command line or GUI (nirsoft.net)


一对airpods引发的程序员脑细胞大死亡事件
https://mrbeancpp.github.io/2022/10/10/一对airpods引发的程序员脑细胞大死亡事件/
作者
MrBeanC
发布于
2022年10月10日
许可协议