将亮灯电子琴接入Synthesia

作为一个码农,平时敲键盘深感自己手不够灵活,外加非常羡慕同学、同事中的各位钢琴带师,于是毫无基础的我最近入手了一个带有琴键发光(亮灯)功能的电子琴,每天深夜下班回家也还能学习一个。该琴有配套APP(APP会员月费30元),在配套APP中学习乐曲,对应的琴键会亮灯提示。此外也有正常的MIDI接口可以连接电脑,总体性价比还是不错的。

 

但是教学APP虽好,曲库量还是很有限,毕竟不可能每首曲子都是你喜欢、想学的;即使是你想学的,也有可能某些段落和你熟悉的不一样(简化了);且该APP学习中一旦遇到超出音域的情况,即使只有1个音超出音域,你也会卡在这里无法跳过导致后面的部分无法学习。(咨询客服,客服表示请购买88键的高级版琴。)另外该APP还有一些其他缺陷,比如练习完右手仅接着就是双手(没有单独的左手练习)、蓝牙连接导致有时亮灯不稳定、只能显示下一个音的音高等等,总之其功能和灵活性是不可能超过已经有十几年历史的老牌MIDI播放和练习软件Synthesia的。因此对于我这种MIDI爱好者来说,不能支持Synthesia,这琴的价值就要折半。

 

Synthesia在最近的10.x版本也开始支持亮灯电子琴了,最早的大厂比如YAMAHA和CASIO出的琴,其亮灯控制都是标准的MIDI NOTE ON/OFF消息,只是消息发送的Channel有所不同。另外也有一家国产的同样号称“智能电子琴”的发光琴厂商THE ONE(就是郎朗代言的那个),Synthesia也单独做了支持,其亮灯控制也不过是MIDI AFTERTOUCH(pressure sensitivity)消息。THE ONE本身也应该是走APP付费教学的模式,能让Synthesia兼容确实有风度。而我这款琴连到Synthesia,无论哪个Channel,灯都是不会亮的……既然官方为了赚钱,大概率不会支持,我只能自己动手丰衣足食了。

P.S. 教学APP中确实有几个我喜欢的曲子,所以会员也续上了。至于Synthesia,我使用的是最新的正版。

为什么不买THE ONE?其实是我个人不太喜欢THE ONE那种欧式造型。这款琴没有音响(需外接音响或耳机或电脑发声,我们深夜琴师只要耳机就够了),整体也是方方正正的,非常节省空间。

既然这款琴用常规的MIDI消息不能亮灯,那通常来说肯定是通过特殊的MIDI消息来传输亮灯信息。那么我们就来看一看这个APP。用解压缩软件打开apk看一哈,可以在assets文件夹下看到libjiagu.so。那我毕竟身经百战见得多了,显然这是360加固。所以本文到这里就结束了,我们下期再见。

 

 

……有加固确实是件麻烦事,不过毕竟该APP要通过蓝牙与琴打交道,直接分析一下中间的消息应该是更快的。

在安卓上要获取蓝牙通信信息,只需要在“开发者选项”中勾选“打开蓝牙数据包日志”即可。不过生成的数据包记录文件在哪个位置就各有不同了,我是MIUI系统,费了好久才在MIUI文件夹下找到这个btsnoop_hci.log文件。首先启用日志,然后重新打开蓝牙,进入APP,等到琴键亮灯,收集到足够的蓝牙消息后,关闭蓝牙,找到日志文件,把它传到电脑上,用wireshark打开分析即可。

分析可得,这是一套用BLE(蓝牙低功耗)标准下GATT协议实现的MIDI消息传输。这种奇葩*的实现,稍加搜索就能找出——大体上应该是基于这个开源项目实现的。对照着开源代码分析消息,很快就能将亮灯对应的消息找出来。由于MIDI规范中有预留“系统独占消息”(就是各厂家可以通过该消息制定自己的功能),所以一旦发现抓取的消息中有系统独占消息(0xF0打头),就要格外注意。事实上该亮灯消息确实是系统独占消息,不过除了亮灯消息之外,APP发送的系统独占消息还有查询设备信息和挑战应答两种。仔细对比每条亮灯消息,就能发现其中不同的部分,其中一个字节用来标识是哪个按键,另一个字节标识亮或灭。再结合一些基本逆向和基于常识的推断,就能完整搞明白亮灯消息的结构了。

(*由于MIDI有时也是需要非常低延迟的,所以蓝牙MIDI就显得较为奇葩了。)

明白了消息的构成,事情已经做完了一半。我们可以在电脑上通过MIDI测试工具(推荐MIDI-OX),向琴发送消息测试。测试结果表明,连接琴之后必须先发送挑战应答消息,才能激活亮灯,随后发送亮灯消息即可正常亮灯。

下一个目标就是如何兼容Synthesia。这件事初步一想感觉会很困难——Synthesia又不开源,我怎么让它发送这种特殊的消息呢?但是万幸前人已经造了足够的轮子,我们只需把它们组装起来就能完成这个任务。

 

首先是与MIDI设备打交道的库,选用Melanchall.DryWetMidi。它可以方便地获取MIDI输入、输出设备,并收到MIDI消息时触发事件,所以很方便地就能实现播放MIDI时让对应的琴键亮灯这种效果。这个只需要在收到MIDI NOTE ON/OFF事件时,顺带产生一个亮灯/灭灯消息发送给琴即可。

为了能让Synthesia发送的标准亮灯消息(即NOTE ON/OFF)转换成琴的独占亮灯消息,我们可以采取代理模式。我们可以搞一个虚拟的MIDI输出设备,并让Synthesia发送亮灯消息到这个虚拟设备。这个虚拟设备实际上是我们的程序,在接收到消息后,产生独占亮灯消息,再发送给琴。那么有没有这种虚拟MIDI设备的方案呢?还真有,就是这个virtualMIDI。而DryWetMidi里面正好有一个DeviceConnector类,可以将一个Input(在这里是Synthesia)发来的消息转发给一个Output(在这里是琴),这正是我们需要的。二者一结合,就可以完美地在Synthesia中玩这款琴了。不过由于virtualMIDI的License要求比较严格,源代码中并没有直接附带它的SDK代码。

 

SentivePiano-demo

 

这个工具我把它叫SentivePiano,名字自然是来自Sentive。希望有一天能弹出Narcissu的钢琴曲吧。

 

github项目地址

添加评论

Loading