在一块 240 MHz 双核的 ESP32-S3 上实时跑 Neural Amp Modeler 的音箱建模,是可行且已经有人做出来的。CoyoPedal 这个开源项目把 Waveshare 的 2.06 英寸 AMOLED 触摸板变成一个吉他效果器,用 8 MB PSRAM 存模型,双核流水线跑 23 层膨胀卷积,实测在无屏开发板上两个核分别吃掉 1.333 毫秒音频块预算的 91% 和 94%,且没有丢截止时间。它绕过了原板载的音频编解码芯片,走 USB 主机接一个 class-compliant 的 USB 声卡做 48 kHz 输入输出。整个工程的难点不在模型,而在把一块 512 KB SRAM 的单片机塞进实时音频的死线里。
硬件选型的三个约束
选板子先看三件事能不能同时满足。
内存是第一道门槛。NAM 的 A2 模型是 WaveNet 结构,23 层膨胀卷积加一个 16-tap head,12,146 个权重,加上每层的历史状态和激活值,512 KB 的片上 SRAM 放不下。这块板有 8 MB PSRAM,模型放那儿,运行时的热点数据放片内 SRAM,按 32 KiB bank 分组摆放。
USB 是第二道。ESP32-S3 的 USB 是 1.1 Full Speed,跑不了 High Speed,这决定了包大小和延迟的下限。好处是它有 USB 主机能力,能直接枚举 USB 声卡,省掉自己写 I2S 和 ADC。
屏幕和触摸是第三道。410 × 502 的 AMOLED 走 QSPI,FT3168 触摸芯片走 I2C。这部分是 UI,和音频通路解耦,音频丢块和界面卡顿互不影响。
实时处理怎么排布
音频块定 64 帧,48 kHz 采样率下正好 1.333 毫秒,在 240 MHz 上是 320,000 个周期。这个数字就是每个核每级的死线,超时丢块,表现为咔哒声。
双核按层切开,切点在第 8 层,写死在构建参数 COYOPEDAL_PEDAL_S3_SPLIT_LAYER 里。
- 核 0 跑 USB isochronous 完成回调,优先级 20,再跑 Stage A,优先级 19,负责输入环、调音表和前级效果、音箱层的 0 到 7 层、回程的混响和 PCM 打包。
- 核 1 跑 Stage B,优先级 22,负责音箱层的 8 到 22 层、head、第二个调制、延时。
块之间用无锁单生产者队列传递,三个流水线槽加两个暂存缓冲。音频模式下空闲任务会从任务看门狗里摘掉,避免被误判成卡死。
模型是怎么塞进定点运算的
NAM A2 在 ESP32-S3 上以块浮点运行。数值用 int16,部分层用 int8 limb,每一层的历史、权重和激活共享一个独立的指数。卷积、激活、残差、量化和 head 全部是手写的 LX7 汇编,用了 S3 的乘加指令,热点函数放 IRAM。23 层历史环和两张系数表放片内 SRAM。
精度有两条路径,加载时做一次确定性校准扫描,或者模型自带一段 49,748 字节的调优尾段,目标误差标称负 60 dBFS。代价是首次加载一份新捕获要暂停音频几秒到几十秒,解析、校验和调优都在设备上做完,之后 SD 卡会生成 .s3cache 缓存。
模型文件放在 microSD 卡 FAT 格式的 nam 目录下,最多 6 层子目录,支持 .nam 和 .namb,文件名不超过 127 字节,文件不超过 2 MiB。固件只接受 48 kHz 的 A2 模型,采样率不符、架构不同、层形状不一致都会直接报错拒绝。
效果链与音色处理顺序
整条链顺序固定。噪声门、压缩、调制、过载、NAM 音箱带三段音色栈、第二个调制、延时最长 2 秒用 PSRAM 里的浮点延迟线、双罐弹簧混响用 Q15 延迟线并输出立体声,最后回 USB 播放。还有一个调音表,调音时输出静音。
出厂带两个捕获,来自 VoLum 库的 Diezel Herbert 第一通道和 Ampete One 第四通道,都配 V30 箱体,MIT 许可证分发,默认预设叫 Silver Lining。
延迟方面,Full Speed 接口往返实测轻效果时是高个位数毫秒,整条链约 20 毫秒以内。
自己编译一份的完整步骤
前置依赖要齐。Node.js 22.13 以上,Python 3 带 Pillow 和 fontTools,C++20 编译器和 CMake 用于宿主测试,Emscripten 用于 Web 构建。
npm i -g @geastack/cli
gea setup --esp-idf
git clone <仓库地址> && cd <仓库目录>
npm ci
gea setup
gea doctor
npm run remote:configure -- --ssid 你的WIFI名称
gea build --board amoled
gea flash --board amoled
gea setup --esp-idf 把 ESP-IDF 6.0.2 装到主目录的 esp/esp-idf,gea setup 注册板卡。remote:configure 可选,生成 gitignored 的 remote_config.h,放 Wi-Fi 凭证和认证 token,不配就编一个不含 radio 的桩,固件小约 400 KB。产物是 build/pedalboard.bin,首次必须走 USB 烧录。
固件跑起来之后,BOOT 键长按 1.5 秒切换模式。音频模式卸载音频图,开 Wi-Fi、BLE、HTTP API 和 OTA。维护模式更新完会先以音频模式启动一次做验证,再回维护模式。崩溃、看门狗或欠压重启也会进维护模式,防止设备变砖。
哪些坑要提前知道
- 只支持 48 kHz 的 UAC2 声卡。采样率是 44.1 kHz 的设备直接不认。
- 裸 S3R8 模块没有 PMIC,没有电池路径,USB 声卡要自备电源。
- 首次加载新模型会停音频,别在演出中途换模型。
- Wi-Fi 凭证编进固件,
remote_config.h必须确认在 gitignore 里。 - 浏览器版跑在 WebAssembly 上,Safari 和 Firefox 无法回写文件夹,只能读。
常见问题
没有 USB 声卡能用吗? 这块板没走模拟音频通路,需要外接 class-compliant USB 声卡,项目实测兼容 XTONE Pro 和 IK Multimedia iRig HD X。
ESP32-S3-DevKitC-1 能跑吗? 能,但要 N16R8 版本,8 MB PSRAM 是硬条件,BOOT 键此时充当脚踏开关。
模型必须自己录吗? 可以用公开的 NAM 捕获库,也可以自己录音箱后导出 A2 模型。