跳到正文
GameBusiness.jp · 日本游戏产业· 来源·· 20 小时前AI 评分65

把吉他应用和无限音乐机关集成到 RISC-V 平板电脑中,使其随时都能进行演示(CloseBox)

ギターアプリと無限音楽機関をRISC-Vタブレットに組み込んで、いつでもデモできるようにした(CloseBox)

正文 · AI 翻译

我正在推进一个项目,把用 Claude Code 开发的应用也集成到 PC 以外的设备中。这次的目标是搭载 RISC-V 芯片的 5 英寸平板电脑。

我已经回到故乡长崎约 10 天。目的是在初中同学会上表演乐队。因为只有笔者住在远处,所以打算稍微提前回去,和剩下的成员先练习一下。

已经练习了 2 次,每次 3 小时;为此还在老家添置了一台电钢琴(以前买的折叠式 88 键),以便也能进行个人练习。

正式演出是在 10 月 25 日,所以还有点时间,不过笔者有一个会让时间无限流逝的爱好。

主要是使用 Claude Code 进行的 Vibe Coding。

主力机器是家里搭载 RTX 5090/4090/4080 的 Windows 机器(其中 2 台在 WSL2 的 Ubuntu 上运行)、DGX Spark 兼容机、M4 Max MacBook Pro(128GB)、M4 Mac mini,这些都可以通过 VPN 的 Tailscale 从任何地方访问。

这次带回老家的是平时放在浅草桥办公室里的M2 MacBook Air。我琢磨着要用它做点什么。

最近不仅限于视频生成和音乐生成,还把对象扩展到了嵌入式设备,比如用Raspberry Pi 4做了兼容AirDrop的模块,还做了室内自行车游戏,让人可以随时随地骑行,与硬件联动的事情越来越多了。

前几天,我久违地去了技研酒吧,AI研究者清水亮先生给我看了一个有趣的东西。

他正在一台陌生的小型计算机上运行独自开发的操作系统。

・为M5Stack制作了OS“MeOS”/自制OS

没见过的主机外形。看起来也像Cardputer,但显示屏更大,键盘的按键数量也更多。

M5Stack的大型显示屏。总觉得见过。啊,是Tab5。我家里有。而且还在上面连了键盘。

Tab5只要1万2000日元出头,很便宜就买了,但开机后出现的密集画面让我害怕,就一直搁置着。我把它充电后带到了长崎。

Tab5是M5Stack在2025年推出的5英寸平板型开发板。

SoC是Espressif的ESP32-P4。RISC-V双核,最高400MHz,PSRAM为32MB,闪存为16MB。配备1280×720的IPS触摸屏、ES8388音频编解码器和1W扬声器、3.5mm耳机插孔、USB-A主机接口和USB-C、6轴IMU BMI270,甚至还搭载了负责Wi-Fi 6和蓝牙的ESP32-C6。

Linux和Android都跑不了。这是用Arduino IDE或ESP-IDF编写C++并烧录进去的纯粹单片机世界。

笔者有一个在触摸屏上运行的、最合适的项目。就是吉他应用FretCaster。

把 FretCaster 移植到微控制器上

在 iPad 上运行的自制吉他应用 FretCaster,其设计是:触摸琴弦和品丝即可发声,上下移动手指进行推弦,晃动机身产生颤音,用和弦条(长方形按钮)来扫弦。

声音并非使用 Tone.js 之类的库,而是用 Swift 逐个采样计算波形的自研引擎。

这个自研引擎起了很大作用。叠加泛音、加上拨片噪声、使其失真。只要从 Swift 改写成 C++,应该就能直接在微控制器上运行。

于是,我在长崎的老家,在 M2 MacBook Air 上启动了 Claude Code,告诉它“想把 M5Stack 的 Tab5 当作电子乐器来用”,并给它看了 FretCaster 的 GitHub 私有仓库。

Claude Code 首先查了 Tab5 的规格,在准备安装 Arduino CLI 时,Homebrew 因 Xcode 的许可协议确认而卡住,于是它直接下载了二进制文件,安装了 M5Stack 的板级包和库(M5Unified),确认了 FQBN 并编写了构建脚本。在此期间,笔者用 USB 连接 Tab5。我就是那个“插线君”。

到发出声音为止

移植引擎时改变的是精度。Swift 版对每个泛音都调用双精度的 sin(),但 ESP32-P4 的 FPU 是单精度,因此改为查 2048 项正弦波表的方式。采样率为 48kHz,每次计算 256 个采样后交给 M5Unified 的扬声器功能。

将计算分配到核心 0,将触摸和绘制分配到核心 1,测量 256 个采样(5.3ms)的计算耗时,结果为:无音时 0.55ms,6 音同时 2.5ms,10 音 3.8ms。这对于叠加吉他独奏与和弦来说已经足够。

到这里为止都很顺利,但实际一操作,就出现了有时响、有时不响,按下几秒后才发声之类的症状。

根据笔者观察到只有在 Claude Code 连接串口监视器期间反应才良好,判明了原因是 USB 串口。ESP32-P4 的 USB 串口在 Mac 侧不读取端口时发送会被阻塞,每次输出日志都会让 UI 侧的循环停下来。将发送超时设为零,并改为只在调试模式下输出日志后,延迟就不再令人在意了。

触摸面板以 100Hz 返回坐标。虽然比不上 iPad 的 120Hz,但对于推弦来说能获得足够的响应。

作为吉他手提出的要求

接下来,我作为吉他演奏者提出了一些细部要求。

“推弦应该只能从按住的品位音向上方变化”。我把它做成无论是把手指向上移还是向下移,音高都只朝升高的方向变化。在真实吉他上,无论向上推还是向下拉,弦都会被拉紧,所以音高会升高。这一点在iPad版中是正确实现的,但似乎出现了功能退化。

“半音推弦很难做”。最初设定为60像素升高2个半音,但后来放宽到移动相当于两根弦的宽度才升高约1个半音。这一项也是一样。

“同一根弦上的滑弦会被误认为推弦”。横向滑动时,手指难免会有纵向的偏移。因此,对于不超过弦宽三成的纵向偏移予以忽略,并且每当品位改变时就重置推弦的基准点。反过来,如果弯音超过80音分,则在回到原位之前品位不会改变。

品位在一屏上显示10个,采用0~9、8~17、16~24三档切换的形式。要在5英寸的屏幕上确保品位宽度,这大概就是极限了。

这部分不实际用真机试用一下是体会不到的。

和弦条是把 iPad/iPhone 版 GarageBand 的功能加以改良的两段式 16 个打击垫:敲击打击垫中央为下扫弦,敲击右端为上扫弦,从左端滑动则变为琶音。

设置面板可以隐藏,隐藏后会在 1 弦上方多出一根空弦为 B4 的「0弦」。这是把 7 弦吉他向高音方向延伸的思路,让独奏的高音区变得轻松。

为了能完整弹下《Let It Be》专辑版的吉他独奏,0 弦设为 B4 空弦。若是 A4,最高音就有点不够。

摇动机身

Tab5 搭载了名为 BMI270 的六轴传感器。我们移植了 iPad 版上也有的、摇动机身就会加上颤音的功能。

这是iPhone应用Pocket Guitar从一开始就具备的功能。正因为有这个功能,才能轻松地做出表现力非常丰富的颤音。

从加速度中去除重力分量,并将其投影到自动追踪摇晃方向的轴上,再把该值直接加到音高上。摇晃的快慢就直接成为颤音的快慢。为了不拾取敲击屏幕的冲击,设置为只有摇晃持续80ms以上时才生效。

此外,将机身向右猛地推出时音品范围会升高,向左推出时会降低。最初是用触摸的轻扫(flick)来实现的,但演奏过程中的轻扫很难与快速滑弦区分开来,所以改用了传感器。

这样一来,就可以像吉他手在琴颈上移动手的位置那样进行把位变换。

麻烦的是,没有关于传感器安装朝向的资料。Claude Code采用的方法是:根据倾斜手持机身时的重力方向推定屏幕的上下,再将与其正交的方向视为左右,以此回避了问题。笔者确认左右后,碰巧一次就对了。

支持音阶

如果调是A,蓝调音在哪里——能看到音阶会更好。

选定根音和音阶类型(大调五声音阶、小调五声音阶、蓝调、大调、小调、多利亚、混合利底亚、和声小调)后,音阶上的音会被涂成青绿色,根音涂成琥珀色,每个格子里显示音名。如果设为A的蓝调音阶,只要按住颜色变化的位置,就不会弹出错音。

此外,开启SNAP模式后,即使按到偏离的格子,也会向最近的音阶音靠拢发声。推弦、揉弦和滑音不做修正,会从正在响的音连续变化。

即使不看品丝,大致上也能弹出沿音阶的、富有表现力的乐句。

还加入了无限音乐机关

会弹吉他之后,就会生出更多欲望。笔者面向浏览器,把自动演奏应用的无限音乐系列

我做了这些:能够无限生成 Keith Emerson 风格 Moog 独奏的「Emerson Moog Infinity」、以巴赫风格无限延续的「Bach Perpetuum Mobile」,以及一边拆解萨蒂的《裸体歌舞》一边编织新曲的「Satie Infinity」。把这三款装进 Tab5,能否通过 APPS 菜单切换呢。

这些应用都用 JavaScript 编写,声音使用 Tone.js 和 Web Audio 的合成器与采样器,作曲则是用代码写成的规则。Claude Code 读取了三个代码仓库,首先用 C++ 准备了通用的 DSP 部件。PolyBLEP 振荡器、Moog 梯形滤波器、biquad、ADSR、延迟、混响、失真、压缩器。在此基础上,把各应用的作曲逻辑和音色原样迁移了过来。

Emerson 中搭载了 16 种乐句生成器、5 种 Moog 和 2 种 Hammond 音色、贝斯、Palmer 风格鼓组,甚至还加入了当紧张度升高时会让乐曲愈发激烈的「FURY」周期。画面上会显示旋钮的动作、Hammond 的拉杆、旋转扬声器的转动以及键盘。CPU 负载约为单核的四成。

Bach 拥有 7 种体裁。托卡塔与赋格、众赞歌前奏曲、帕萨卡利亚、咏叹调、前奏曲、创意曲,以及鲁特琴的布列舞曲。采用 Werckmeister III 律制和 a′=415Hz 的巴洛克音高。羽管键琴和鲁特琴使用 Karplus-Strong,管风琴使用泛音加法合成,画面上会用日语滚动显示乐理的实时解说。

Satie 是一个钢琴采样器。它把浏览器版所使用的 Salamander 钢琴采样缩减到 30 个音、22kHz 单声道、每个截取 2.6 秒,压缩至 3.4MB,并嵌入固件中。16MB 的 flash 中,应用区域已占用 83%。启动时展开到 PSRAM,音符在五线谱上流动,并落到键盘上。

没有声音,音质很差

虽然能动是动起来了,但接下来才是漫长的过程。

首先,让 Emerson 发声几分钟后就没有声音了,切回 FretCaster 也没有声音。数据在流动,却没有声音。因为让 1W 的放大器以接近满量程的状态持续响了数分钟,所以推测是放大器的保护机制启动了,于是加入了抑制输出长期平均值的「governor」。重启后声音恢复了。

其次是「噼噼啪啪」的噪声。这是画面绘制造成的影响。Tab5 的面板实际上是竖向(720×1280),横向显示是由 M5GFX 逐像素旋转绘制的。Satie 的五线谱(1280×422)传输需要 66~160ms,在此期间 PSRAM 的带宽被占满,运行在另一个核心上的音频计算就来不及了。

Claude Code 找出了 ESP32-P4 上搭载的 2D 加速器(PPA)。这是一个用硬件完成旋转和缩放的模块;把五线谱交给 PPA,让它直接旋转传输到竖向的帧缓冲区后,耗时降到了 27ms。颜色错乱的字节序问题,是 Claude Code 通过串口抓取截图、自己一边确认一边修好的,这一点看着很有意思。

有人指出「巴赫低了半音」,这是因为初始值是巴洛克音高的415Hz,通过按钮可以切换到440Hz。原来的浏览器版也是这样,但从扬声器里听确实有违和感。

「Emerson的音像模糊,乐器分不开」。换另一只扬声器也一样,所以是信号的问题。原因是混响。浏览器版的Tone.Reverb会按能量对残响的脉冲响应做归一化,而移植过来的Freeverb型没有做。把7.5秒的残响加在持续音的风琴上,残响会大幅超过干声,把一切都冲淡了。

加入归一化、并让200Hz以下不进入残响之后,已经达到了可以说「声音变好了」的程度。那之后又靠耳朵降了两档,残响稳定在4秒,湿声降到原来的四分之一。

Satie的残响也换掉了。按照「混响不必强到这个程度」的要求减少了量,算法也从Freeverb换成了Dattorro的板式混响。钢琴的残响这样更自然,而且延迟线收进了内置RAM,所以不会和音频计算争抢PSRAM的带宽。

用测量追查噪声

最后剩下的是Satie的噪声。有人指出「一开始很清晰,但几分钟后开始混进来」「随音量成比例增加」。

Claude Code在此不再依靠耳朵,而是改用测量。将本体中计算完成的音频在PSRAM中积攒4秒,再通过USB导出,以检测咔哒声。即使连续10分钟、每2分钟分析一次,咔哒声也为零。本体的温度传感器稳定在40℃。于是,在APPS中增加一个只播放纯音的测试应用,让笔者试听,结果“纯音很干净”。扬声器和放大器都是无辜的。

决定性的证据是计算出的块累计数。48kHz、每次256个采样的话,每秒本应是187.5个块,但实际只输出了166~183个。也就是说,有百分之几的声音缺失了,而那正是噼啪声的真面目。“几分钟后开始”是因为曲子变密、同时发声增多,“与音量成正比”也是同样的原因。

原因在于,钢琴的播放位置是用 double 推进的。ESP32-P4 的 FPU 是单精度的,因此 double 运算会由软件处理,每个采样要消耗数百个周期。这正是我在移植 FretCaster 时最先修正过的同一个陷阱,却又在另一个地方再次踩了进去。改成整数与单精度的组合后,计算时间降到了每个 5.3ms 块预算中平均 2ms,在 90 秒的测量中丢失为零。

实际试听后,噪音也消失了。

接下来要移植到哪个设备?

大致跑通之后,我试着问了 Claude Code:“如果要进一步移植,哪个设备比较合适?”

本来最中意的是同样搭载 ESP32-P4 的 Waveshare 10.1 英寸版(ESP32-P4-WIFI6-Touch-LCD-10.1)。它有 800×1280 触摸屏,附带编解码器与扬声器、microSD,电池为可选配置,价格 80~90 美元。只要芯片相同,PPA 的旋转传输、以单精度为前提的 DSP,以及 PSRAM 的布局都能直接运行。M5Unified 不认识这块板子,所以显示器和编解码器的初始化需要重写,但音源、作曲和 UI 的逻辑预计可以不做改动。

对吉他来说,真正起作用的是面积。在 5 英寸上排 10 品时,每品约 1.3cm,而 10.1 英寸时约 2.5cm,更接近实物的 12 品附近。5 英寸上放弃的 12 品显示也能重新实现。

另一条路线是给 Raspberry Pi 5 接上触摸屏,三个自动演奏的浏览器版可以照常运行。不过启动要花几十秒,电源只能靠移动电源。“开机即乐器”这种 Tab5 的手感会变淡。像 Teensy 这样的音频专用板子音质是一流的,但没有大的触摸屏,不适合这次这种以屏幕为主体的构成。

一个担心是,从边框到显示屏的距离相当大。这样的话,在 FretCaster 上手指可能够不到第 5、6 品,操作性也会变差。我想查一查有没有更好的设备。

最后,我下单了骰子型的 M5Stack“AtomS3R”产品。往里面装入无限音乐机关,然后掷骰子,我在想,要是能做些像掷骰子之类的事情就好了。

現状

到这里是抵达长崎后大约3天的成果。APPS菜单里排列着FretCaster、Emerson、Bach、Satie,以及纯音测试。弹吉他,弹腻了就放着巴赫,这样的用法已经可以只用Tab5一台设备实现了。

在Tab5上学到了4件事。ESP32-P4的FPU是单精度,如果每个采样都使用double,声音会断掉。面板是纵向的,横向的大块传输要交给PPA。1W的放大器持续发声会掉下来,所以要压低输出的长期平均值。还有,耳朵追不上的噪声要在本体内部测量。这些都能带到下一台设备上。

吉他音色还没追上iPad版,不过,只要从耳机插孔接到调音台,它的尺寸和重量倒是可以只靠Tab5带上舞台。

这个大小也正好适合演示无限音乐机关系列。

接下来大概会是按曲子编辑和弦条,以及通过USB输出MIDI之类的吧。Tab5的开发先到这里,我打算为同学会上的演奏,把披头士和Julie的曲子好好练习一下。

来源:GameBusiness.jp · 日本游戏产业 · gamebusiness.jp