几乎每天我都会在智能手机上听音乐,并使用耳机上的控制按钮。 但是我总是不喜欢一件事。 我回到家,继续聆听,耳机连接到我的家用PC,然后按钮突然停止工作。
当然,我用谷歌搜索了这个问题的解决方案。 不幸的是,在Windows上并没有太支持此功能。 几分钟的搜索仅在Stack Overflow上模糊地提及了声卡和一些人发出的消息,指出在笔记本电脑上一切正常。
这并没有吓到我-我决定接受这个问题,这是一个有趣的挑战:如果根本没有硬件支持,可以创建某种程序来激活控制按钮吗? 答案是可以的。 这是在半小时内完成操作的方法。
Android耳机按钮如何工作
首先要了解的是耳机按钮如何工作。 在互联网上进行的快速搜索从Android文档中找到了
此规范 。 那里有一张图表。

如您所知,当您按下耳机上的按钮时,其中一个电阻器上的电路会闭合。 特别值得注意的是电阻为0欧姆的按钮A(播放/暂停/挂钩),即麦克风短路。 如果我们能够检测到麦克风短路,则可以确定按下“播放/暂停”按钮。
假设检验
在开始编程之前,我想原则上检查我们推理的合理性。 也就是说,可以通过按下“播放/暂停”按钮来确定来自麦克风的信号。 幸运的是,为此只需在计算机上录制声音并查看结果就足够了。 我启动了Audacity,在录制过程中按下了播放/暂停按钮-并收到了这样的信号。
宾果如您所见,按下按钮显然会反映在波形中:突然下降到-1,然后突然过渡到1,然后逐渐下降到0。根据规范,从直觉上讲,我认为信号会跳到1并保持在那里,直到释放按钮为止,但是实际上,它看起来有所不同。 尽管如此,如果您从麦克风捕获音频流,仍然很容易检测到此类图片。
使用Python进行声音捕捉
知道了检测耳机上按钮按下的方式后,您可以想到主要目标:如何使用耳机按钮在桌面上控制播放器。
第一步是检测按钮单击。 为此,您需要从麦克风获取音频流,并发现我们之前看到的独特签名。 为简单起见,我们在Python中实现该解决方案。 在Internet上进行了另一个小型搜索之后,我发现了一个名为sounddevice的程序包,该程序包使您可以从最困难的部分中进行抽象-从麦克风捕获真实的音频。
一点点编码即可为我们提供以下内容:
import sounddevice as sd SAMPLE_RATE = 1000
这样的代码连续产生每批样品的平均值。 我们将采样率设置为1000,这对于声音处理来说是非常小的(通常使用44100),但是我们实际上并不需要太多的准确性。 块大小确定缓冲区中有多少样本触发回调。 同样,我们将值设置得很低。 块大小为100,采样率为1000,实际上意味着每秒触发10次,每次调用仅处理100个采样。
按钮点击检测:可能太容易了
现在,我们捕获音频流,并且可以实现检测按钮按下的真实机制。 回想一下,每按一次,信号会跳至1。 这建议了最简单的检测方法:如果
N个连续的块的信号值大于0.9,即单击。
我们在函数中实现算法:
import sounddevice as sd SAMPLE_RATE = 1000
实际上,我们启动了一个内部计数器,其中有多少个已处理的块满足阈值要求(将其简单地设置为0.9),可提供不可避免的样本噪声。 如果该块不满足要求,则将计数器重置-然后重新开始。
is_held
变量监视触发器,以便在不释放按钮时不重复注册它们。
Windows播放控制
现在只剩下替换注释
“按下了按钮!” 。 在Windows中控制音频播放。 Google再次弄清楚如何做到这一点:事实证明,您可以通过使用相应的
虚拟按键代码模拟击键来控制播放。
事实证明,使用
pywin32软件包来模拟击键非常容易,该软件包只是Windows API的Python外壳。 放在一起,我们可以创建以下函数:
import win32api import win32con VK_MEDIA_PLAY_PAUSE = 0xB3 def toggle_play(): win32api.keybd_event(VK_MEDIA_PLAY_PAUSE, 0, 0, 0)
而我们做到了! 在注释
“按下按钮!”的代码处
toggle_play
函数
。 ,可让您使用Android耳机上的按钮控制Windows中的任何媒体播放器。
测试表明,该代码运行良好。 按下按钮时,Android和Windows功能的唯一区别是稍有延迟,但可以忍受。
所以发生了什么Python脚本由51行组成,这些行可激活Windows上Android耳机上的按钮。 该项目的最终源代码
在Github上 。
等等,还不止这些!
在愉快地使用该程序几个小时后,我注意到了一个严重的问题:

该程序几乎占用了30%的CPU! 显然,在长时间的工作中这是不可接受的,需要做一些事情。 查看代码,我意识到主线程在主循环中处于空闲状态,尽管那里什么也没有发生。 最合乎逻辑的解决方案是永远永远使线程安乐死:由于回调是自动调用的,因此我们仍然不需要循环。
from time import sleep if __name__ == '__main__': controller = HeadsetButtonController() while True: sleep(10)

我也不想在每次计算机启动后手动运行Python脚本。 幸运的是,Windows版Python附带了一个名为pythonw.exe的有用实用程序,该实用程序无需连接终端即可启动守护进程。 我们在
Microsoft \ Windows \ Start Menu \ Programs \ Startup目录中放置此过程的快捷方式,将我们的脚本指定为第一个参数-然后该应用程序自动启动并在后台安静地运行。