Windows 音频设备控制之旅

Kaffa 发布于

分类: 数智 标签: audio-device

我的需求

笔记本通过 TypeC-DP 1.4 口连接了显示器,听音乐时,我使用笔记本连接的音频输出设备,看视频时,我使用显示器的音频输出口,因此,我有了需要在笔记本和显示器之间快速切换音频设备的需求。

解决过程

我拥有 Windows 编程能力,在 AI 时代想到的第一解决途径就是Prompt,AI 帮我编写一段小功能:你是 Windows 编程的高级程序员,我需要实现一个最简的功能,我需要界面上提供系统音频设备列表供选择,计划用 Python Tkinter 实现,可以用一组单选框,竖直排列,选中设备后设置为 Windows 默认音频设备。请得到代码后复查一遍可能的问题,提供最终可运行的代码。

得到代码后,我表示没看出 API 是什么,于是问 Windows 中切换音频设备的 API 什么,我想着实现 Python 后,可转化为一个简单的 C 程序一键在多个音频设备之间切换,我的基本思路是:先列出设备,再在设备间循环激活为默认。

后续过程就不列举了,AI 提供的代码,都是无法运行的,以下列举 AI 的关键代码。

实现方式一:

import comtypes
from comtypes import GUID, COMObject, POINTER
from comtypes.client import CreateObject
from comtypes.gen import PolicyConfigClient

def set_default_audio_endpoint(device_id):
   # 使用comtypes创建PolicyConfigClient对象
   pPolicyConfig = CreateObject(PolicyConfigClient.CLSID_PolicyConfigClient,
                                interface = PolicyConfigClient.IPolicyConfig)
   # 调用SetDefaultEndpoint方法设置默认音频端点
   hr = pPolicyConfig.SetDefaultEndpoint(device_id, 0)
   if hr == 0:
       print("默认音频端点设置成功")
   else:
       print("默认音频端点设置失败")


if __name__ == '__main__':
   # 这里需要替换为实际的设备ID
   device_id = "{0.0.0.0}"
   set_default_audio_endpoint(device_id)

实现方式二::

from pycaw.pycaw import AudioUtilities, IMMDeviceEnumerator

def get_speaker_device_id():
   device_enumerator = IMMDeviceEnumerator()
   speakers = AudioUtilities.GetSpeakers()
   device_id = speakers.GetId()
   return device_id

if __name__ == '__main__':
   device_id = get_speaker_device_id()
   set_default_audio_endpoint(device_id)

我后来使用一段复杂的 Python 代码调用了一个本地 dll 文件作为桥梁实现了切换。然后,为去掉这个不明就里的 dll,把问题弄得更明白一点,我查阅了 Microsoft 的文档,文档中居然没有提!

最后我发现实现切换的是 Microsoft Windows Undocumented API,列举和操纵此设置,居然分属在不同的 COM 接口中,未有文档的是 PolicyConfigClient 接口,它是一个特殊的 COM 接口,脚本无法创建和使用它的属性和方法,需要 Native 代码通过最基本的结构去调用。

然后,我浏览了交友网站 GitHub,发现世界上还有几个和我有一样需求的人,并且有几个 Geek 动手实现了 AudioSwitch 软件,以及还有几个 C# 和 vbs 的实现,经过比较,我还是喜欢 C++ 实现的版本。

AudioSwitch 软件运行后,按 F12 就能自动切换系统默认的音频设备。我于是下载源码,发现它使用一个叫做 SCons-win32 的编译工具进行了编译,我又查阅了 SCons-win32 帮助,成功编译出了 AudioSwitch.exe

当我完成编译,微软的安全软件居然报 Trojan:Win32/Bearfoos.A!ml,我去微软官方查询了 Bearfoos.A!ml 解释带 !ml 的是机器学习判断的木马。

我查看了它的源代码,然后 SCons-win32 作为可能引入病毒的第二个可能,风险也不高,我的编译器也是本地,安全软件扫描过的,所以我认为它大概率是误报,于是向微软提交了该二进制软件,几天后,我发现微软排除了它的威胁。

随后的几天中,我感觉都非常方便。

新的发现

突然有一天,我发现 Windows 托盘区的小喇叭似乎自带切换功能,只是这个功能,我才发现。相比开机自动运行一个小软件然后按 F12,和点击托盘区的小图标再点展开再点设备的三次操作来说,我觉得已经差不多了。

按一次键盘和点击三次鼠标,我感觉还是键盘轻松点,但弊端是 F12 是容易和其它热键冲突的。比如 MadEdit 中 F12 就是进入一个 PostIt 模式,如果从菜单进入了,再 F12 时,就无法生效了。

结论

  1. 虽然 AI 编程已经很厉害,你讲清楚了需求,它也无法写出切换音频设备的小功能。
  2. AI 无法完成的原因是它使用了 Windows 未公开的技术。
  3. 新问题很少,这个问题 Windows 的发展过程的几十年间,已经被很多人用不同方式解决。
  4. 如果要解决电脑问题,通过编程解决只是一种直接的方式,你去问 AI 怎么编程,这时有两个结果,一个是 AI 实现了,二是 AI 未能实现,但无论是搜索还是 AI,最后都可以解决。
  5. 解决问题本来还有更简单的办法,也许是对问题解空间的搜索方向错误了。

但无论如何,现有的 AI 都不算能直接解决此问题,最后还得依靠专家的知识和一个灵感和一个机会。