展示HN:完美的Windows蓝牙MIDI
嗨,HN,我是Erwin。我开发了一个小型的免费开源工具,可以将蓝牙低能耗(BLE)MIDI键盘连接到新的Windows MIDI服务堆栈,这样任何数字音频工作站(DAW)或Web MIDI应用程序都可以像使用有线设备一样使用它们。
我购买了一台Roland FP-90X钢琴,部分原因是它支持蓝牙MIDI。在我的Windows 11电脑上,配对成功,但我的DAW无法识别键盘,电脑发送的音符从未让钢琴发声。在经历了令人遗憾的多个晚上后,我将这个问题分解为三个独立的bug,层层叠加。
第一个问题是著名的:Windows仅通过WinRT API原生支持BLE-MIDI,而几乎没有DAW会轮询这个API。因此,即使配对成功,MIDI应用程序仍然无法看到设备。通常的解决方法是使用MIDIberry和loopMIDI,但在我的情况下,这种组合无法可靠工作,我希望找到一个单一应用的解决方案。新的Windows MIDI服务堆栈提供了一种名为回环端点的功能:写入一个端点的任何内容都会从另一个端点输出,任何winmm/WinRT/WMS应用程序都将其视为正常的MIDI端口。因此,该应用程序实现了WinRT BLE-MIDI输入,WMS回环输出。这解决了从钢琴到电脑的方向问题。
然而,从电脑到钢琴的方向仍然无法工作。NoteOn写入遭遇了ATT攻击,但钢琴依然沉默。我尝试了两种写入模式(某些BLE-MIDI固件会默默丢弃其中一种),并探测了专有的ISSC特征。每种变体都遭遇了ATT攻击,每种变体都产生了沉默。因此,字节确实到达了钢琴,但GATT层之上的某些东西将其丢弃了。
在排除了配对、加密、写入模式和专有特征后,唯一明显的杠杆就是MIDI通道本身。FP-90X有一个面板设置叫做“传输通道”,默认是1。然而,FP-90X实际上是在通道4上接收(并且无法更改)。我在通道1上发送的音符被GATT攻击并在合成引擎处静默丢弃,因为它们不在引擎监听的通道上。任何层面都没有反馈。解决方案必须在应用层,因此我添加了一个“检测”按钮,可以在每个通道上从1到16播放N个测试音符:你计算实际听到的音符数量,这个数字就是接收通道。每个BLE MAC保存大约75秒,永久适用于每台钢琴。
技术栈:.NET 10,使用Avalonia作为UI(BLE/MIDI部分仅适用于Windows,但UI层是可移植的),使用Microsoft.Windows.Devices.Midi2包用于WMS,直接使用Windows.Devices.Midi(WinRT)用于BLE,而不是依赖Korg的旧WinMM驱动程序。MIT许可证,单个自包含的约21MB可执行文件,无需安装程序,无遥测,无需账户。
我为自己开发了这个工具,并用它与我的FP-90X通过几个应用程序和Web MIDI网站进行演奏。当我在r/synthesizers上分享时,来自微软Windows MIDI服务团队的Pete对BLE集成给予了积极的评价(链接:[Reddit](https://www.reddit.com/r/synthesizers/comments/1szvuiq/comment/oj5ew9b/))。
网站(带截图):[Perfect Bluetooth MIDI For Windows](https://mayerwin.github.io/Perfect-Bluetooth-MIDI-For-Windows)
源代码:[GitHub](https://github.com/mayerwin/Perfect-Bluetooth-MIDI-For-Windows)
详细的技术文章和完整的调试故事:[Dev.to](https://dev.to/mayerwin/why-your-bluetooth-midi-keyboard-silently-drops-notes-on-windows-2i84)
个人仅在我的FP-90X上进行了测试。BLE部分是通用的,因此其他键盘(如WIDI Master、CME、Yamaha MD-BT01、Korg microKey Air、ROLI Seaboard等)应该也能工作,但我尚未逐一确认。欢迎提供设备测试报告、问题反馈和PR。
查看原文
Hi HN, I'm Erwin. I built a small free open-source utility that bridges Bluetooth LE MIDI keyboards into the new Windows MIDI Services stack so any DAW or Web MIDI app can use them as if they were wired.<p>I bought a Roland FP-90X piano partly because it had Bluetooth MIDI. On my Windows 11 PC, pairing succeeded, but my DAW couldn't see the keyboard, and notes I sent from the PC never made the piano sing. After a regrettable number of evenings, I'd separated this into three independent bugs stacked on top of each other.<p>The first one is the famous one: Windows only natively exposes BLE-MIDI through the WinRT API, which almost no DAW polls. So even when pairing succeeds, MIDI apps still don't see the device. The usual workaround is MIDIberry + loopMIDI, but I couldn't get that combination to work reliably in my case, and I wanted a single-app solution. The new Windows MIDI Services stack ships with a feature called loopback endpoints: anything written to one comes out the other, and any winmm/WinRT/WMS app sees them as normal MIDI ports. So the app does WinRT BLE-MIDI in, WMS loopback out. That solved direction one, piano to PC.<p>Direction two, PC to piano, still didn't work. NoteOn writes were getting ATT-acked, but the piano stayed silent. I tried both write modes (some BLE-MIDI firmware silently drops one or the other), poked the proprietary ISSC characteristic. Every variant ATT-acked, every variant produced silence. So the bytes were reaching the piano. Something above the GATT layer was discarding them.<p>After ruling out pairing, encryption, write-mode, and proprietary characteristics, the only obvious lever left was the MIDI channel itself. The FP-90X has a panel setting called Transmit Channel, default 1. Yet it turns out the FP-90X actually receives on channel 4 (and it can't be changed). Notes I sent on channel 1 were being GATT-acked and silently dropped at the synth engine because they weren't on the channel the engine was listening to. Zero feedback at any layer. The fix had to live up at the application layer, so I added a Detect button that plays N test notes ascending on each channel from 1 to 16: you count the notes you actually hear, and that number is the receive channel. Saved per BLE MAC, about 75 seconds, done forever per piano.<p>Tech stack: .NET 10, Avalonia for the UI (the BLE/MIDI side is Windows-only but the UI layer is portable), Microsoft.Windows.Devices.Midi2 packages for WMS, Windows.Devices.Midi (WinRT) directly for BLE rather than relying on Korg's older WinMM driver. MIT, single self-contained ~21 MB exe, no installer, no telemetry, no account.<p>I built it for myself and use it with my FP-90X to play through a few apps and Web MIDI sites. Pete from the Microsoft Windows MIDI Services team commented positively on the BLE integration when I shared it on r/synthesizers (<a href="https://www.reddit.com/r/synthesizers/comments/1szvuiq/comment/oj5ew9b/" rel="nofollow">https://www.reddit.com/r/synthesizers/comments/1szvuiq/comme...</a>).<p>Site (with screenshots): <a href="https://mayerwin.github.io/Perfect-Bluetooth-MIDI-For-Windows/" rel="nofollow">https://mayerwin.github.io/Perfect-Bluetooth-MIDI-For-Window...</a>
Source: <a href="https://github.com/mayerwin/Perfect-Bluetooth-MIDI-For-Windows" rel="nofollow">https://github.com/mayerwin/Perfect-Bluetooth-MIDI-For-Windo...</a>
Long-form technical writeup with the full debugging story: <a href="https://dev.to/mayerwin/why-your-bluetooth-midi-keyboard-silently-drops-notes-on-windows-2i84" rel="nofollow">https://dev.to/mayerwin/why-your-bluetooth-midi-keyboard-sil...</a><p>Personally tested with my FP-90X only. The BLE side is generic, so other keyboards (WIDI Master, CME, Yamaha MD-BT01, Korg microKey Air, ROLI Seaboard, etc.) should work, but I haven't confirmed individually. Device test reports, issues, and PRs very welcome.