为 PC 提供 3.5mm 耳机线控支持

Banner

源码:PaperStrike/HeadsetControlsPC

本人是一名新晋大学生,自从有了电脑,电脑开着 B 站手机刷消息就成为了本人闲时的挚爱。为了不影响舍友 / 家人的学习生活,受制于自己的经济能力,本人一直戴着有线耳机,并在躺着看电影时发现了这样一个恐怖的事情:

没有一家笔记本支持 3.5mm 有线耳机线控!

好吧可能还是有的,反正在本人的搜索中并没有找到。仅仅搜到有几个型号的台式机,支持中间的那个暂停 / 继续键。这有什么用? 不能调音量,不能调下一曲,而且,又不是我的台式机!😡 于是求助于在搜索关键词中添加万能的程序员专用字眼:github ,发现了这篇文章:

Hacking in Android headset button support for Windows | Christian Barth Roligheten

Perfect,同志们!里边详细介绍了帅哥 Christian Barth Roligheten 为自己的 PC 添加 暂停 / 继续 按键支持的过程,结合了详细的 Python 源码并提供了仓库链接(roligheten/AndroidMediaControlsWindows)。本人在 Roligheten 研究声道图的基础上进一步分析, 在即将放弃希望之际,偶然中找到了 分辨三个不同按键的方法。

原理

3.5mm TRRS 接口的有线耳机的麦克风与按钮并联,按钮按下时麦克风被短接,安卓设备通过检测电压差可以知道按下的按钮。

Reference Headset Circuits

图中各按钮常见的对应功能如下:

按键 介绍
A 暂停 / 继续
B 增加音量
C 减少音量
D Pixel 上唤起语音助手

多数 PC 设备无法获知电压差,但短接对麦克风两个声道产生的影响(虽然不知道为什么,但)有规律可循。这里本人从 sounddevice 的例子库中选择并轻修了一个 plot_input.py ,用于实时输出麦克风声道一的波形(橙线 Channel 1)和两声道差值(蓝线 diffChannel 2 - Channel 1)的波形,演示。

横轴表示时间,单位为毫秒(ms),纵轴表示声音的变化。
数据在不同设备上可能有所不同,另外,演示中排除了轻按的情况。

无声音时,两条波均呈现为一条直线。而极端噪声环境下,Channel 1 震动剧烈,diff 保持稳定:

极端噪声波形图

短按按钮时,处于安静环境(0-1000ms)和处于噪声环境(1300-4000ms)的两条波呈现的变化中,有规律的至少有:

#1. diff 在按下瞬间增长至 0.06(±0.05) 并持续 40(±30)ms,在松开瞬间下降。

#2. Channel 1 在按下瞬间,下降并保持在 -0.6 以下 100(±30)ms,经 30(±20)ms 增长至 0.45 以上并保持 230(±60)ms,经 30(±20)ms 下降至 -0.40(±0.05) 以下。

短按波形图

#3. 在另外的试验中注意到,按钮保持按下时,两条波几乎不受环境声音影响(,应该是短接的原因)。

短按(0-800ms)和长按(850-2400ms)时,两条波在上述规律基础上又呈现出了不同变化:

#4. diff 在长按过程中在 -0.17-0.17 范围内随机波动,在长按松开瞬间再次拉升,后下降,详细见图,不再赘述。

#5. Channel 1 在长按过程中保持在 -0.30(±0.05) 以上,在长按松开瞬间再次拉升,后下降至 -0.40(±0.05) 以下,详细见图。

短按和长按波形图

放大到纵轴 0.1 范围内,观察 diff 的变化。注意到不同按钮的按下瞬间 diff 呈的波动幅度有所不同:

#6. 按钮 A 按下瞬间(700-900ms)diff 波动峰值在 0.075 以上,按钮 B(1700-1900ms)为 0.025-0.075,按钮 C(3000-3200ms)为 0.016-0.025。

不同按钮短按波形图

有兴趣的同志可以自己下载编辑 plot_input.py ,试验。

目前,项目使用 #1 && #2 条件判断按下,#5 条件判断松开,#6 条件判断不同按钮。

判断条件已经被本人换了好几次 以扩大适应的设备范围,但因为只有一台笔记本和一条耳机随我调用,不兼容我也没多少办法。你愿意当我的工具人吗?

安装,有兴趣吗?

以下两种方式都需要联网,会自动安装依赖包 keyboardnumpysounddevice

PyPI

通过 PyPI 安装:

pip install trrsheadset

Clone

或者, 从 这里 下载在 GitHub 仓库中的压缩包,解压到一个记得住位置的地方。

这种安装方式目前有一个 bug(PIP doesn’t read setup.cfg in UTF-8, which causes UnicodeDecodeError · Issue #8931 · pypa/pip),安装前需要根据你使用的命令行环境设置环境变量 PYTHONUTF8=1

  • PowerShell: $env:PYTHONUTF8=1
  • Shell: export PYTHONUTF8=1
  • CMD: set PYTHONUTF8=1

定位到解压文件夹中运行:

pip install .

然后,就可以把解压文件夹删掉。

使用

插入耳机后,在命令行中使用 Python 启动 trrsheadset 即可:

python -m trrsheadset [参数]
  • 可以使用两种方式,一种是直接关闭命令行,一种是按下 ctrl+break 快捷键强制退出。
  • 选用 pythonw 启动可在命令行关闭后保持运行,可在任务管理器中找到 Python 3.x (Windowed) 关闭。

重插耳机需要重新启动。

可选参数

-l or --log 将运行日志保存至文件

--use-hotkey 开启键盘快捷键,见后文介绍

-h or --help 输出此列表后退出

按键

耳机按键映射 & 键盘快捷键响应。基于 Python 库 keyboard

耳机

按键 短按 长按 双击
A 继续 / 暂停 继续 / 暂停 静音 / 取消静音
B 音量+ 下一首 /
C 音量- 上一首 /
D / / /

双击操作处于早期开发阶段

键盘

需在启动时使用 --use-hotkey 参数。

基础快捷键 ctrl+shift+h ,在基础快捷键触发后 1s 内按下以下按键可以触发相应操作:

p 暂停或继续

e 退出

!部分设备在开启上述快捷键后,检测不到右 shift、右 ctrl 的释放动作,此时可以按击键盘左边对应按键恢复。

因此,此功能在默认情况下关闭。

注意哦

  • 不要按得太快,招架不住。
  • 有问题欢迎评论,也可以在 GitHub 提 issue 或进入讨论区(Discussions)交流。
  • 如果放久后按钮辨别老错,可能是太久不放音乐了电压不高(?存疑,讨论欢迎)
  • 感谢 Christian Barth Roligheten 大哥 👍

抓取日志

添加参数 -l--log 启动可将运行时日志保存到运行时文件夹debug.log.1debug.log 日志文件中。日志文件对定位 BUG 非常有帮助。

python run.py -l

项目许可

GPL-3.0 License