A
Air官方
【Air 系列 · 鸿蒙 PC 适配全记录】04 / 56
■ 应用:轻编曲(from LMMS)(AirBeat)
■ 定位:循环编曲工作站(LMMS)
■ 上游:LMMS | 路线:Qt5 5.15+QPA | 版本:1.0.0
■ 市场状态:开发中,即将上架
■ 官网(免费下载): https://www.shunidc.cn/
————————————————————
开源鸿蒙 PC 社区: https://harmonypc.csdn.net/
PC 社区项目申请: https://atomgit.com/OpenHarmonyPCDeveloper
适配开源地址:(AtomGit 仓库建立后回填)
环境搭建文章:https://blog.csdn.net/weixin_52908342/article/details/161343743
▍一、为什么要适配 LMMS
编曲软件在桌面端是一个口味分化的领域,LMMS 占据的位置很明确:不追求专业 DAW 的录音棚流程,而是把 FL Studio 式的节奏编程做轻——内置合成器、采样器、钢琴窗(Piano Roll)、FX 混音器和 Song Editor,打开就能用鼠标拼出一段完整编曲。上游以 Qt5 为 GUI 基础,find_package(Qt5 5.15 REQUIRED COMPONENTS Core Gui Widgets Xml Svg) 决定了它的移植路线几乎只有一个方向。
鸿蒙工程给这个移植起的自研应用名是"轻编曲"(com.airbeat.app,版本 1.0.0,deviceTypes 覆盖 tablet、2in1、phone)。工程没有为它新交叉编译任何一个第三方库:Qt5 5.15.16 栈与 ohos QPA 插件直拷自 AirDraw,libsndfile、libfftw3f、libsamplerate 直拷自 AirDaw,OHAudio 走系统 NDK 的 libohaudio.so,MIDI 用上游自带的 MidiDummy 兜底。整个工程的重心因此集中在两件事上:把 LMMS 的入口和构建改造成鸿蒙可用的库形态,以及写一个真正出声的 OHAudio 音频设备后端。
验证平台为 MatePad 11.5(主验证机)与 Mate 60 Pro(M3 性能探针回归)。截至 PORTING.md 记载的最新状态(2026-09-22/23),OHAudio 出声后端、中文菜单、全界面汉化、LADSPA 120 插件与 TriggerAction 全链均已在真机实证。
▍二、先划清边界:不把编译通过当成适配完成
LMMS 在桌面 Linux 上依赖一整套音频与 MIDI 服务,插件体系也远比首版放开的多。工程在选型阶段就把每一条后端与插件的去向写成显式决策,而不是留给运行时去失败。其中最关键的一条是版本:master 线(1.3.0-alpha)核心类型贯穿使用 C++20 ranges 与 memory_resource,OHOS NDK clang15 的裁剪版 libc++ 不支持,工程据此回退 1.2.2 stable 并保留 master 源码树作为回退证据。
层次 原项目实现(传统桌面方案) 鸿蒙侧当前方案
版本基线 master 1.3.0-alpha(活跃开发线) 锁定 v1.2.2 stable(libc++ C++20 能力裁决)
音频后端 ALSA/JACK/PulseAudio/SDL/PortAudio 全部 OPTION 关闭,自研 AudioDeviceOHOS(OHAudio FAST 模式)
MIDI 后端 ALSA / WinMM / JACK MIDI MidiDummy 兜底(虚拟键盘与钢琴窗不受影响)
主程序形态 lmms 可执行文件 liblmms.so(SHARED)+ Lmms_Init 导出 + 29 个 C API
界面承载 X11 上的 Qt5 Widgets XComponent(lmms_canvas) + Qt5 ohos QPA(5.15.16)
插件集 全量构建(含 ZynAddSubFX 等) LMMS_MINIMAL 起步,M2 放开 LADSPA 120 项;Zyn 暂缓
采样格式 设备默认位深 OHAudio float interleaved(免 S16 转换损耗)
这份边界表里没有一条是妥协的含糊话:关闭的后端全部是上游 CMake OPTION,打开的能力(OHAudio、LADSPA、汉化)每一条都有真机验收记录。
▍三、鸿蒙版的整体架构
工程复制 AirDraw 骨架做 krita→lmms 映射。ArkTS 壳承担 rawfile 增量部署、环境变量前置、启动覆盖层与就绪轮询;liblmmsbridge.so 是只链系统库的 NAPI 薄桥;Qt 与 LMMS 全部在运行时按硬编码拓扑序 dlopen,QPA 插件用 RegisterQtPluginsStatically 注册,不依赖 QT_PLUGIN_PATH 目录扫描。LMMS 初始化放在 native 专用线程,该线程即 Qt 主线程驻留 exec(),ArkTS 主线程只做轮询,规避 QApplication 首线程绑定与 appfreeze 双看门狗。
text
EntryAbility(rawfile 增量部署 + env 前置:HOME/XDG_*/QT_QPA_FONTDIR)
└── Index.ets(XComponent lmms_canvas + 启动覆盖层 + isLmmsReady 轮询)
├── DialogPage / DialogHost / SubWindowManager(对话框双路径子窗化)
├── ImmersiveManager / FileManager(系统 Picker)/ ShareManager
└── liblmmsbridge.so(薄 NAPI:ace_napi/hilog/native_window/dl)
└── dlopen 拓扑序:libc++_shared → 基础 C 库 → Qt5Core→Gui→Widgets
→Xml→Svg → libsndfile→libsamplerate→libfftw3f → libqohos(QPA)
→ liblmms.so(Lmms_Init → Qt 主线程 exec())
├── AudioDeviceOHOS → libohaudio.so(系统 OHAudio)
└── 插件链:LADSPA 120 项 + AudioFileProcessor/Kicker 等
主要目录如下:
text
AirBeat/
├── AppScope/app.json5 # 包名 com.airbeat.app
├── PORTING.md # 选型、补丁清单、K1-K15 踩坑、风险登记册
├── ai_log/ # v1-v4 逐会话进度实录
├── lmms-1.2.2/ # 锁定的上游源码树(就地鸿蒙化补丁)
├── lmms-master/ # 回退证据:1.3.0-alpha 树
├── native/
│ ├── build_lmms_ohos.sh # 复用 AirDraw qt5 树 + AirDaw 三库的构建脚本
│ └── deps/fftw-3.3.10 # fftw 源码与 .pc 桩
└── entry/src/main/
├── ets/
│ ├── pages/Index.ets # XComponent 宿主、就绪轮询、键/触摸转发
│ ├── managers/ImmersiveManager.ets
│ ├── components/DialogHost.ets
│ └── utils/ # FileManager / ShareManager / SubWindowManager
├── cpp/
│ ├── CMakeLists.txt # 只编 liblmmsbridge.so
│ ├── napi_init.cpp
│ └── qt_platform/ # ohos QPA(libqohos)
├── libs/arm64-v8a/ # Qt5 栈 + LADSPA 插件等 177 个 .so
└── resources/rawfile/lmms/ # data 19MB(六目录)+ 工作区
▍四、核心适配过程
### 1. 入口改造:从 main 到 Lmms_Init
补丁 P1 新增 lmms-1.2.2/src/core/lmms_ohos_entry.cpp,把 main() 流程改写为可导出的 Lmms_Init(dataDir, workDir, fontDir):跳过 CLI 解析与 fileCheck,跳过首启弹窗(配置由 glue 预置),初始化完成后在专用线程驻留 Qt 主循环。对外暴露 29 个 Lmms_* C API,事件注入照 AirDraw 的 QWSI 范式实现。ArkTS 侧的启动链是三条件门:surface 就绪、rawfile 数据部署完成、init 调用发出,三者齐备才撤加载层。
### 2. AudioDeviceOHOS:LMMS 的第一个鸿蒙声音
P2 新增 src/core/audio/AudioDeviceOHOS.{h,cpp},实现 1.2.2 的 push 模型接口(writeBufferToFb),底层挂 OHAudio renderer 回调并启用 FAST 模式,回调范式取自 AirWing 的 ohos_sink。P3 在 Mixer.cpp 的 tryAudioDevices/isAudioDevNameValid 注册 LMMS_HAVE_OHOS 后端并新增 mixer/audiodev 配置键。格式决策是 float interleaved:LMMS 内部本就是 float 管线,直接对齐可免去 S16 转换损耗。M1 的验收不走"能编译",而是 renderer Start 与 usage=11 的 hilog 实证,加上 LED 增量与播放头位移按 BPM 折算的双图互证(播放推进 +5.47 秒对齐 4 小节)。
### 3. 配置预置与向导绕开
LMMS 首启会弹 Setup 向导,且 MainWindow 构造内嵌 SetupDialog::exec() 模态嵌套,界面会永远卡在向导页。工程用 glue 预置 configured=1 与 audiodev=Dummy 绕开;期间发现 rawfile 预置 .lmmsrc.xml 的方案走不通——hvigor 不打包 dot 开头文件,配置只能由 core 在启动时写入沙箱。另一个坑是 LMMS Engine 初始化期会把配置写回文件,"空值才写"的预置策略失效,最终对 laddir/audiodev 改为无条件覆盖。空格键播放的注入则绕开 QShortcut 的焦点竞速,由 glue 直调 Song::playSong(invokeMethod Queued),把"时灵时不灵"变成确定行为。
### 4. 插件与汉化的 M2 放开
首版 LMMS_MINIMAL=True 只带 AudioFileProcessor、Kicker、TripleOscillator;M2 起放开纯 C++ 无外部依赖插件,当前随包 LADSPA 插件 120 项(entry/libs/arm64-v8a/ 下 177 个 .so 的主要增量)。汉化取上游 data/locale 的 zh_CN 与 Qt5 自带中文 qm 双层。文件对话框在系统 Picker 接入前以 TriggerAction/QFileDialog 过渡方案先行,菜单经 ArkTS 化由壳层重建。
### 5. 构建与部署脚本
native/build_lmms_ohos.sh 刻意做薄:Qt5 用 AirDraw 的 install 树与 cmake config,三库直拷 AirDaw,LMMS 源码走 cmake/ninja,产物过 patch_needed.py 去版本号后拷入 entry/libs/arm64-v8a。打包遵循五件套:nativeLib.strip:false、compressNativeLibs:false、NEEDED 去版本号、预编译库走模块级通道、桥内硬编码拓扑序 dlopen 清单。字体经 QT_QPA_FONTDIR=/system/fonts 指向系统字体目录,环境变量在一切初始化之前前置。
▍五、适配中遇到的几个难点
### 难点一:master 线的 C++20 能力墙
初选 master 编译报 no subrange in std::ranges 与 memory_resource not found,两套 SDK 表现一致——NDK clang15 的 libc++ 裁剪版把 subrange gated、头缺失。工程评估后放弃追平上游:回退 1.2.2 stable,master 树保留作对照,并把教训写进流程——新工程选版本前先测 libc++ 的 C++20 能力面,而不是假设 NDK 跟进标准库。
### 难点二:Setup 向导的模态嵌套
界面卡在"config loaded"之后不动,原因是 MainWindow 构造函数里同步弹 SetupDialog(configured/audiodev 为空即触发),模态嵌套在 Qt 主线程初始化路径上。预置配置后二启又弹 AudioSettings 向导,因为 audiodev 空字符串过不了 isAudioDevNameValid。两轮定位才把预置项凑齐(Dummy 后端的全名字符串都要对上),这也是配置预置类方案的代表性格:预置什么值,必须以上游校验函数的判定为准。
### 难点三:插件链接的 undefined symbols
Linux 上 LMMS 插件不链主库,符号靠运行时解析;鸿蒙 lld 默认 --no-undefined,全量插件链接报 Model/Mixer 未定义。解法在 BuildPlugin 的 OHOS 分支显式链 lmms(借 WIN32 平台的既有范式)。同类工具链差异还包括:宿主工具 bin2res 被交叉 toolchain 编成 arm64 无法执行(改 host 编译并以 -host 后缀避开 phony 自环)、fftw 头文件靠系统路径隐式查找(改 INCLUDE_DIRECTORIES(SYSTEM) 显式注入并用 .pc 桩隔离 host 污染)。
### 难点四:诊断通道的三重失明
后端调试先后踩了三个不可见:OHAudio 头的 OH_AudioRenderer 不可前置声明(不透明 tag),回调签名必须精确匹配 NDK typedef;fprintf(stderr) 完全不进应用 hilog 域,诊断一律改 OH_LOG_ERROR;最后 liblmms 内的 OH_LOG 仍然全静默,同进程 A/B 探针定案是 hilog 对 domain ≥ 0x10000 的静默丢弃(0xA04B 可见、0xA0D00 不可见),同时 %{public} 残留字面量会被解析截断。最终 domain 收敛到 0x0A0D,格式串统一 snprintf 加单占位符的 QOHOS_PROBE 形态。
### 难点五:出声之后如何证明在出声
音频后端接通不等于可验收。M1 用双图法定案播放推进:单看 LED 读数置信度不足(低分辨率暗段混辨),改为 LED 增量与播放头位移按 BPM 折算互证,两个独立通道对齐 +5.47 秒。另一个周期性问题是 OHOS 音频服务 idle 约 119 秒杀流,AudioDeviceOHOS 实现流重建自愈,配合欠载统计探针(stats30s 通道)把长会话稳定性纳入 M3 的量化验收,Mate 60 Pro 回归全绿。
▍六、编译、安装与启动
Native 侧构建(脚本内部复用姊妹工程资产,零新交叉编译):
bash
bash native/build_lmms_ohos.sh # cmake/ninja 构建 liblmms.so + 插件,patch_needed 后拷 entry/libs
ArkTS 侧标准 hvigor 构建:
bash
export JAVA_HOME=/Applications/DevEco-Studio.app/Contents/jbr/Contents/Home
export DEVECO_SDK_HOME=/Applications/DevEco-Studio.app/Contents/sdk
hvigorw --mode module -p module=entry assembleHap --no-daemon
签名产物位于 entry/build/default/outputs/default/entry-default-signed.hap。部署验证遵循换进程纪律:覆盖安装后 force-stop 再启动,以新日志行确认跑的是新库,避免 install RC=0 的部署假成功。安装与启动:
bash
hdc install -r entry/build/default/outputs/default/entry-default-signed.hap
hdc shell aa start -a EntryAbility -b com.airbeat.app
▍七、当前功能边界
里程碑验收记录支持的已覆盖能力:
- M0:liblmms.so 编译贯通,LMMS 主窗口真机渲染(Song Editor 四轨道、FX Mixer、工具条),零崩溃;
- M1:AudioDeviceOHOS 出声,renderer Start/usage=11 实证,播放推进双图定案;
- M2:LADSPA 120 插件随包,中文菜单与全界面汉化,TriggerAction/QFileDialog 过渡方案,菜单 ArkTS 化;
- M3:音频欠载统计探针(stats30s 通道打通)、idle 杀流重建自愈、hilog domain 规范定案,Mate 60 Pro 回归全绿;
- 数据面:OGG/FLAC/WAV 导入经 libsndfile 可用,data 资源 19MB 随 HAP 增量部署。
尚未覆盖或规划中的能力(PORTING.md 里程碑与已知决策):
- 系统 Picker 打开/保存与 IME 输入链路为 M2 之后的后续项;
- 对话框触摸与听感两项真人验证待补;
- 长会话压测与播放态 stats 未完成(M3 达标线为 standards.md §9);
- ZynAddSubFX 因 RemotePlugin 的 QProcess 在沙箱内 execve 受禁而暂缓,待评估进程内 local 模式;
- MP3 导入关闭(libsndfile < 1.1.0);MIDI 输入为 dummy,USB MIDI 待后续评估。
因此,当前版本更准确的定位是"轻编曲"的第一阶段:能启动、能出声、插件与汉化可用,交互闭环与稳定性压测仍在 M3 线上推进。
▍八、总结
AirBeat 展示了一条与重资产移植相反的路径:当姊妹工程已经沉淀出 Qt 栈、QPA、音频回调与部署纪律时,新工程可以做到零新交叉编译,把全部预算花在上游入口改造与音频后端这两个真正的增量上。Lmms_Init 导出、配置预置绕向导、插件显式链主库,这三件事办完,LMMS 的主体就在鸿蒙上立住了。
后续工作的清单在里程碑表里很清楚:系统 Picker 与 IME 补齐交互闭环,长会话压测补齐稳定性证据,ZynAddSubFX 的进程内方案与 USB MIDI 做可行性评估。出声已经证明,剩下的都是把它变成一个可以每天打开用的编曲工具。
■ 应用:轻编曲(from LMMS)(AirBeat)
■ 定位:循环编曲工作站(LMMS)
■ 上游:LMMS | 路线:Qt5 5.15+QPA | 版本:1.0.0
■ 市场状态:开发中,即将上架
■ 官网(免费下载): https://www.shunidc.cn/
————————————————————
开源鸿蒙 PC 社区: https://harmonypc.csdn.net/
PC 社区项目申请: https://atomgit.com/OpenHarmonyPCDeveloper
适配开源地址:(AtomGit 仓库建立后回填)
环境搭建文章:https://blog.csdn.net/weixin_52908342/article/details/161343743
▍一、为什么要适配 LMMS
编曲软件在桌面端是一个口味分化的领域,LMMS 占据的位置很明确:不追求专业 DAW 的录音棚流程,而是把 FL Studio 式的节奏编程做轻——内置合成器、采样器、钢琴窗(Piano Roll)、FX 混音器和 Song Editor,打开就能用鼠标拼出一段完整编曲。上游以 Qt5 为 GUI 基础,find_package(Qt5 5.15 REQUIRED COMPONENTS Core Gui Widgets Xml Svg) 决定了它的移植路线几乎只有一个方向。
鸿蒙工程给这个移植起的自研应用名是"轻编曲"(com.airbeat.app,版本 1.0.0,deviceTypes 覆盖 tablet、2in1、phone)。工程没有为它新交叉编译任何一个第三方库:Qt5 5.15.16 栈与 ohos QPA 插件直拷自 AirDraw,libsndfile、libfftw3f、libsamplerate 直拷自 AirDaw,OHAudio 走系统 NDK 的 libohaudio.so,MIDI 用上游自带的 MidiDummy 兜底。整个工程的重心因此集中在两件事上:把 LMMS 的入口和构建改造成鸿蒙可用的库形态,以及写一个真正出声的 OHAudio 音频设备后端。
验证平台为 MatePad 11.5(主验证机)与 Mate 60 Pro(M3 性能探针回归)。截至 PORTING.md 记载的最新状态(2026-09-22/23),OHAudio 出声后端、中文菜单、全界面汉化、LADSPA 120 插件与 TriggerAction 全链均已在真机实证。
▍二、先划清边界:不把编译通过当成适配完成
LMMS 在桌面 Linux 上依赖一整套音频与 MIDI 服务,插件体系也远比首版放开的多。工程在选型阶段就把每一条后端与插件的去向写成显式决策,而不是留给运行时去失败。其中最关键的一条是版本:master 线(1.3.0-alpha)核心类型贯穿使用 C++20 ranges 与 memory_resource,OHOS NDK clang15 的裁剪版 libc++ 不支持,工程据此回退 1.2.2 stable 并保留 master 源码树作为回退证据。
层次 原项目实现(传统桌面方案) 鸿蒙侧当前方案
版本基线 master 1.3.0-alpha(活跃开发线) 锁定 v1.2.2 stable(libc++ C++20 能力裁决)
音频后端 ALSA/JACK/PulseAudio/SDL/PortAudio 全部 OPTION 关闭,自研 AudioDeviceOHOS(OHAudio FAST 模式)
MIDI 后端 ALSA / WinMM / JACK MIDI MidiDummy 兜底(虚拟键盘与钢琴窗不受影响)
主程序形态 lmms 可执行文件 liblmms.so(SHARED)+ Lmms_Init 导出 + 29 个 C API
界面承载 X11 上的 Qt5 Widgets XComponent(lmms_canvas) + Qt5 ohos QPA(5.15.16)
插件集 全量构建(含 ZynAddSubFX 等) LMMS_MINIMAL 起步,M2 放开 LADSPA 120 项;Zyn 暂缓
采样格式 设备默认位深 OHAudio float interleaved(免 S16 转换损耗)
这份边界表里没有一条是妥协的含糊话:关闭的后端全部是上游 CMake OPTION,打开的能力(OHAudio、LADSPA、汉化)每一条都有真机验收记录。
▍三、鸿蒙版的整体架构
工程复制 AirDraw 骨架做 krita→lmms 映射。ArkTS 壳承担 rawfile 增量部署、环境变量前置、启动覆盖层与就绪轮询;liblmmsbridge.so 是只链系统库的 NAPI 薄桥;Qt 与 LMMS 全部在运行时按硬编码拓扑序 dlopen,QPA 插件用 RegisterQtPluginsStatically 注册,不依赖 QT_PLUGIN_PATH 目录扫描。LMMS 初始化放在 native 专用线程,该线程即 Qt 主线程驻留 exec(),ArkTS 主线程只做轮询,规避 QApplication 首线程绑定与 appfreeze 双看门狗。
text
EntryAbility(rawfile 增量部署 + env 前置:HOME/XDG_*/QT_QPA_FONTDIR)
└── Index.ets(XComponent lmms_canvas + 启动覆盖层 + isLmmsReady 轮询)
├── DialogPage / DialogHost / SubWindowManager(对话框双路径子窗化)
├── ImmersiveManager / FileManager(系统 Picker)/ ShareManager
└── liblmmsbridge.so(薄 NAPI:ace_napi/hilog/native_window/dl)
└── dlopen 拓扑序:libc++_shared → 基础 C 库 → Qt5Core→Gui→Widgets
→Xml→Svg → libsndfile→libsamplerate→libfftw3f → libqohos(QPA)
→ liblmms.so(Lmms_Init → Qt 主线程 exec())
├── AudioDeviceOHOS → libohaudio.so(系统 OHAudio)
└── 插件链:LADSPA 120 项 + AudioFileProcessor/Kicker 等
主要目录如下:
text
AirBeat/
├── AppScope/app.json5 # 包名 com.airbeat.app
├── PORTING.md # 选型、补丁清单、K1-K15 踩坑、风险登记册
├── ai_log/ # v1-v4 逐会话进度实录
├── lmms-1.2.2/ # 锁定的上游源码树(就地鸿蒙化补丁)
├── lmms-master/ # 回退证据:1.3.0-alpha 树
├── native/
│ ├── build_lmms_ohos.sh # 复用 AirDraw qt5 树 + AirDaw 三库的构建脚本
│ └── deps/fftw-3.3.10 # fftw 源码与 .pc 桩
└── entry/src/main/
├── ets/
│ ├── pages/Index.ets # XComponent 宿主、就绪轮询、键/触摸转发
│ ├── managers/ImmersiveManager.ets
│ ├── components/DialogHost.ets
│ └── utils/ # FileManager / ShareManager / SubWindowManager
├── cpp/
│ ├── CMakeLists.txt # 只编 liblmmsbridge.so
│ ├── napi_init.cpp
│ └── qt_platform/ # ohos QPA(libqohos)
├── libs/arm64-v8a/ # Qt5 栈 + LADSPA 插件等 177 个 .so
└── resources/rawfile/lmms/ # data 19MB(六目录)+ 工作区
▍四、核心适配过程
### 1. 入口改造:从 main 到 Lmms_Init
补丁 P1 新增 lmms-1.2.2/src/core/lmms_ohos_entry.cpp,把 main() 流程改写为可导出的 Lmms_Init(dataDir, workDir, fontDir):跳过 CLI 解析与 fileCheck,跳过首启弹窗(配置由 glue 预置),初始化完成后在专用线程驻留 Qt 主循环。对外暴露 29 个 Lmms_* C API,事件注入照 AirDraw 的 QWSI 范式实现。ArkTS 侧的启动链是三条件门:surface 就绪、rawfile 数据部署完成、init 调用发出,三者齐备才撤加载层。
### 2. AudioDeviceOHOS:LMMS 的第一个鸿蒙声音
P2 新增 src/core/audio/AudioDeviceOHOS.{h,cpp},实现 1.2.2 的 push 模型接口(writeBufferToFb),底层挂 OHAudio renderer 回调并启用 FAST 模式,回调范式取自 AirWing 的 ohos_sink。P3 在 Mixer.cpp 的 tryAudioDevices/isAudioDevNameValid 注册 LMMS_HAVE_OHOS 后端并新增 mixer/audiodev 配置键。格式决策是 float interleaved:LMMS 内部本就是 float 管线,直接对齐可免去 S16 转换损耗。M1 的验收不走"能编译",而是 renderer Start 与 usage=11 的 hilog 实证,加上 LED 增量与播放头位移按 BPM 折算的双图互证(播放推进 +5.47 秒对齐 4 小节)。
### 3. 配置预置与向导绕开
LMMS 首启会弹 Setup 向导,且 MainWindow 构造内嵌 SetupDialog::exec() 模态嵌套,界面会永远卡在向导页。工程用 glue 预置 configured=1 与 audiodev=Dummy 绕开;期间发现 rawfile 预置 .lmmsrc.xml 的方案走不通——hvigor 不打包 dot 开头文件,配置只能由 core 在启动时写入沙箱。另一个坑是 LMMS Engine 初始化期会把配置写回文件,"空值才写"的预置策略失效,最终对 laddir/audiodev 改为无条件覆盖。空格键播放的注入则绕开 QShortcut 的焦点竞速,由 glue 直调 Song::playSong(invokeMethod Queued),把"时灵时不灵"变成确定行为。
### 4. 插件与汉化的 M2 放开
首版 LMMS_MINIMAL=True 只带 AudioFileProcessor、Kicker、TripleOscillator;M2 起放开纯 C++ 无外部依赖插件,当前随包 LADSPA 插件 120 项(entry/libs/arm64-v8a/ 下 177 个 .so 的主要增量)。汉化取上游 data/locale 的 zh_CN 与 Qt5 自带中文 qm 双层。文件对话框在系统 Picker 接入前以 TriggerAction/QFileDialog 过渡方案先行,菜单经 ArkTS 化由壳层重建。
### 5. 构建与部署脚本
native/build_lmms_ohos.sh 刻意做薄:Qt5 用 AirDraw 的 install 树与 cmake config,三库直拷 AirDaw,LMMS 源码走 cmake/ninja,产物过 patch_needed.py 去版本号后拷入 entry/libs/arm64-v8a。打包遵循五件套:nativeLib.strip:false、compressNativeLibs:false、NEEDED 去版本号、预编译库走模块级通道、桥内硬编码拓扑序 dlopen 清单。字体经 QT_QPA_FONTDIR=/system/fonts 指向系统字体目录,环境变量在一切初始化之前前置。
▍五、适配中遇到的几个难点
### 难点一:master 线的 C++20 能力墙
初选 master 编译报 no subrange in std::ranges 与 memory_resource not found,两套 SDK 表现一致——NDK clang15 的 libc++ 裁剪版把 subrange gated、头缺失。工程评估后放弃追平上游:回退 1.2.2 stable,master 树保留作对照,并把教训写进流程——新工程选版本前先测 libc++ 的 C++20 能力面,而不是假设 NDK 跟进标准库。
### 难点二:Setup 向导的模态嵌套
界面卡在"config loaded"之后不动,原因是 MainWindow 构造函数里同步弹 SetupDialog(configured/audiodev 为空即触发),模态嵌套在 Qt 主线程初始化路径上。预置配置后二启又弹 AudioSettings 向导,因为 audiodev 空字符串过不了 isAudioDevNameValid。两轮定位才把预置项凑齐(Dummy 后端的全名字符串都要对上),这也是配置预置类方案的代表性格:预置什么值,必须以上游校验函数的判定为准。
### 难点三:插件链接的 undefined symbols
Linux 上 LMMS 插件不链主库,符号靠运行时解析;鸿蒙 lld 默认 --no-undefined,全量插件链接报 Model/Mixer 未定义。解法在 BuildPlugin 的 OHOS 分支显式链 lmms(借 WIN32 平台的既有范式)。同类工具链差异还包括:宿主工具 bin2res 被交叉 toolchain 编成 arm64 无法执行(改 host 编译并以 -host 后缀避开 phony 自环)、fftw 头文件靠系统路径隐式查找(改 INCLUDE_DIRECTORIES(SYSTEM) 显式注入并用 .pc 桩隔离 host 污染)。
### 难点四:诊断通道的三重失明
后端调试先后踩了三个不可见:OHAudio 头的 OH_AudioRenderer 不可前置声明(不透明 tag),回调签名必须精确匹配 NDK typedef;fprintf(stderr) 完全不进应用 hilog 域,诊断一律改 OH_LOG_ERROR;最后 liblmms 内的 OH_LOG 仍然全静默,同进程 A/B 探针定案是 hilog 对 domain ≥ 0x10000 的静默丢弃(0xA04B 可见、0xA0D00 不可见),同时 %{public} 残留字面量会被解析截断。最终 domain 收敛到 0x0A0D,格式串统一 snprintf 加单占位符的 QOHOS_PROBE 形态。
### 难点五:出声之后如何证明在出声
音频后端接通不等于可验收。M1 用双图法定案播放推进:单看 LED 读数置信度不足(低分辨率暗段混辨),改为 LED 增量与播放头位移按 BPM 折算互证,两个独立通道对齐 +5.47 秒。另一个周期性问题是 OHOS 音频服务 idle 约 119 秒杀流,AudioDeviceOHOS 实现流重建自愈,配合欠载统计探针(stats30s 通道)把长会话稳定性纳入 M3 的量化验收,Mate 60 Pro 回归全绿。
▍六、编译、安装与启动
Native 侧构建(脚本内部复用姊妹工程资产,零新交叉编译):
bash
bash native/build_lmms_ohos.sh # cmake/ninja 构建 liblmms.so + 插件,patch_needed 后拷 entry/libs
ArkTS 侧标准 hvigor 构建:
bash
export JAVA_HOME=/Applications/DevEco-Studio.app/Contents/jbr/Contents/Home
export DEVECO_SDK_HOME=/Applications/DevEco-Studio.app/Contents/sdk
hvigorw --mode module -p module=entry assembleHap --no-daemon
签名产物位于 entry/build/default/outputs/default/entry-default-signed.hap。部署验证遵循换进程纪律:覆盖安装后 force-stop 再启动,以新日志行确认跑的是新库,避免 install RC=0 的部署假成功。安装与启动:
bash
hdc install -r entry/build/default/outputs/default/entry-default-signed.hap
hdc shell aa start -a EntryAbility -b com.airbeat.app
▍七、当前功能边界
里程碑验收记录支持的已覆盖能力:
- M0:liblmms.so 编译贯通,LMMS 主窗口真机渲染(Song Editor 四轨道、FX Mixer、工具条),零崩溃;
- M1:AudioDeviceOHOS 出声,renderer Start/usage=11 实证,播放推进双图定案;
- M2:LADSPA 120 插件随包,中文菜单与全界面汉化,TriggerAction/QFileDialog 过渡方案,菜单 ArkTS 化;
- M3:音频欠载统计探针(stats30s 通道打通)、idle 杀流重建自愈、hilog domain 规范定案,Mate 60 Pro 回归全绿;
- 数据面:OGG/FLAC/WAV 导入经 libsndfile 可用,data 资源 19MB 随 HAP 增量部署。
尚未覆盖或规划中的能力(PORTING.md 里程碑与已知决策):
- 系统 Picker 打开/保存与 IME 输入链路为 M2 之后的后续项;
- 对话框触摸与听感两项真人验证待补;
- 长会话压测与播放态 stats 未完成(M3 达标线为 standards.md §9);
- ZynAddSubFX 因 RemotePlugin 的 QProcess 在沙箱内 execve 受禁而暂缓,待评估进程内 local 模式;
- MP3 导入关闭(libsndfile < 1.1.0);MIDI 输入为 dummy,USB MIDI 待后续评估。
因此,当前版本更准确的定位是"轻编曲"的第一阶段:能启动、能出声、插件与汉化可用,交互闭环与稳定性压测仍在 M3 线上推进。
▍八、总结
AirBeat 展示了一条与重资产移植相反的路径:当姊妹工程已经沉淀出 Qt 栈、QPA、音频回调与部署纪律时,新工程可以做到零新交叉编译,把全部预算花在上游入口改造与音频后端这两个真正的增量上。Lmms_Init 导出、配置预置绕向导、插件显式链主库,这三件事办完,LMMS 的主体就在鸿蒙上立住了。
后续工作的清单在里程碑表里很清楚:系统 Picker 与 IME 补齐交互闭环,长会话压测补齐稳定性证据,ZynAddSubFX 的进程内方案与 USB MIDI 做可行性评估。出声已经证明,剩下的都是把它变成一个可以每天打开用的编曲工具。
楼主
8 天前