欢迎加入开源鸿蒙PC社区: https://harmonypc.csdn.net/

欢迎在PC社区平台申请新建项目:https://atomgit.com/OpenHarmonyPCDeveloper/

摘要:本文复盘 SFZ 采样器库 liquidsfz 0.4.1(Conan 包名 libliquidsfz)移植到开源鸿蒙 PC(HarmonyOS,aarch64)的过程:用 32 行补丁让归档捆绑的 2018 年版 GNU config 平台探测脚本识别 HarmonyOS;用两条自包含命令定位发布包 .pc 烘焙外来绝对 prefix 导致的悬空头文件路径;在官方主线前移 965 个文件后,论证 CI 结论不可沿用,以临时索引重放提交并完整重跑全部门禁。上游测试 make check 的 2 项测试(testsynth、testsfzreader)在真机执行通过,消费者测试包完成渲染往返验证(峰值幅度 0.218745)。交付 12 个文件、757 行,MR !9958 已合入社区主线 archives/l/libliquidsfz/0.4.1。

1 背景

liquidsfz 是一个加载并播放 SFZ 音色的开源采样器库(GitHub),定位是"易于集成到其他项目"的库形态。作者 Stefan Westerfeld 长期从事 Linux 音频基础软件开发,其个人主页列出的 aRts 是 KDE 2/3 使用的合成与媒体框架,此后持续维护 BEAST、SpectMorph、audiowmark 等项目[3]。项目 2019 年 10 月创建,122 star(2026-09-26 时点),0.4.1 于 2026 年 4 月 7 日发布,主要内容是重写了更健壮的低级 SFZ 解析器[4]。

SFZ 格式本身的生态足以支撑移植价值。SFZ 是开放的文本格式:一个 .sfz 文件组织一批采样文件的键位映射与演奏行为,由 rgc:audio 定义,现由 sfzformat.com 社区维护[5]。sfzformat.com 的播放器列表中,LinuxSampler、sforzando、sfizz 与 liquidsfz 均在列[6]。音色内容规模有可核实的证据:Pianobook 社区目前托管 1,762 个免费采样包,SFZ 是其分发的格式之一[7]。liquidsfz 的唯一硬依赖是 libsndfile[8]——Linux 音频栈读写音频文件的事实标准库,社区仓已有 1.2.2 配方,依赖闭包完整。另一个现实是 liquidsfz 尚未进入 Debian、Fedora 正式仓库,AUR 只有 git 快照包[9],鸿蒙 PC 的配方先行落地有检索与复用价值。

最终结果:真机交付静态库 libliquidsfz.a、头文件 liquidsfz.hh 与重定位后的 liquidsfz.pc;JACK/LV2 前端按平台能力显式关闭(第 5 节)。上游测试 make check 的 2 项测试真机通过:交付轮(新基线)在 CI 构建阶段执行,此前基线还在构建内、构建产物复跑、独立探索构建三种方式下运行过三次,结果一致。权威 CI 共 3 轮:第 1 轮败于包名契约,第 2 轮全门通过;随后官方主线合入其他交付、基线按流程刷新,第 3 轮在新基线上完整重跑,再次全门通过。交付提交 12 个文件、757 行,全部位于 archives/l/libliquidsfz/**。MR !9958 已于 2026-09-17 合入 OpenHarmonyPCDeveloper/build_in_harmonyos 主线[10]。

本次适配使用的环境:

项值
设备HUAWEI MateBook Pro(开源鸿蒙 PC,HarmonyOS,aarch64)
内核HongMeng Kernel 1.12.0(uname -a 实测,Toybox 用户态)
工具链clang 15.0.4,Conan 2.29.1,git 2.45.2
上游liquidsfz 0.4.1(2026-04-07),MPL-2.0
源码归档liquidsfz-0.4.1.tar.bz2,SHA256 b086b371…e13f02
依赖libsndfile/1.2.2(社区仓在库配方)
构建Autotools(tarball 内预生成 configure),C++17

2 上游假设与鸿蒙PC环境的差异

liquidsfz 的构建是典型的 autotools 发布形态:configure 已预生成(构建期不需要 autoconf/automake),依赖 pkg-config 探测 libsndfile,可选组件 JACK/LV2 默认开启。真机首轮构建暴露的问题集中在四处:

差异点上游假设鸿蒙PC 实际情况
平台探测GNU config 脚本覆盖主流系统归档捆绑 2018-02 版脚本:uname -s 返回 HarmonyOS 无法识别,config.sub 拒绝 *-linux-ohos
依赖注入pkg-config 找到系统安装的 sndfile.pcconan 发布包的 .pc 烘焙打包机绝对 prefix,换机后路径悬空
可选组件jack/lv2 默认 yes无 JACK 服务与 X11 依赖闭包,须显式 --without
构建环境bash、GNU 工具、常规 umask/system/bin/sh、umask 077、toybox 与 HMDFS 差异

其中前两个有普适性:凡是捆绑旧式 GNU config 的 autotools 项目、凡是经 pkg-config 消费发布包的项目都会遇到,第 4 节分别展开。

3 适配流程

流程与之前两篇(MLton、LAPACKE)一致,底子来自东北大学开源鸿蒙技术俱乐部的研究:王莹教授课题组的 CROSS2OH 把跨平台不兼容问题实证归纳为八类、对应八种通用适配策略,成果发表在 ASE 2025[1],课题成果展播报道见[2]。我在这套方法上做了个性化定制:先侦察确认源码地址与校验值(本例为 GitHub Release 资产,SHA256 经 API 元数据与设备实测双重核验一致),再写 Conan 配方与平台补丁,然后真机跑 CI,失败后按增量日志回修。

本例的交付面相对精简:12 个文件构成配方(conanfile.py,244 行)、源元数据(conandata.yml,含 URL 与 SHA256)、平台补丁(1 个,32 行)、test_package(消费者工程加 166 行渲染断言)、manifest/README/notes(图1)。

请添加图片描述

图1 交付提交的 12 个文件与 757 行增量(git 实测输出)

构建防护上沿用社区已验证的 OHOS autotools 组合(libicns 先例的第二实例):全程 SHELL/CONFIG_SHELL=/system/bin/sh;configure 脚本内两处 umask 077 改写为 umask 022,采用 fail-closed 写法(改写计数不为 2 即报错);TMPDIR 绑定到构建目录内;串行 make;autotools 链时间戳统一固定,防止 missing(1) 误触发重生成;补丁在 source() 阶段应用。这组防护对应的失败模式在社区知识库已有条目(E011/E547/E549/E551 一族),本例零回轮通过,不再展开。

4 重点问题深潜

4.1 32 行补丁:让 2018 年的 config.guess/sub 认识 HarmonyOS

现象:configure 启动即停在 unable to guess system type,提示 uname -s 的输出 HarmonyOS 不被识别。根因不在 autotools 本身,而在归档捆绑的 GNU config 脚本版本:liquidsfz 0.4.1 打包的是 2018-02 版 config.guess/config.sub,比 HarmonyOS 公开出现早得多。

判断是否旧式脚本有一个可靠判别式:grep -c '^GUESS=' build-aux/config.guess。现行版本每个平台分支以 GUESS= 赋值收尾,旧式脚本用 echo 加 exit。本例计数为 0,即旧式——判版本号不如判结构,这也是 E564 条目在 libicns 先例中沉淀的方法。

方案取舍:给 configure 显式传 --build/--host=aarch64-unknown-linux-musl 可以绕过 guess,但这是向构建系统谎报三元组——musl 不是 ohos,后续所有平台分支都建立在假前提上,社区仓库规范禁止这种写法。登记在案的正解是给两个脚本打补丁(图2)。

请添加图片描述

图2 config.guess/config.sub 的 OHOS 双锚点补丁(关键 hunk)

补丁分两处。config.guess 在 aarch64:Linux 分支前插入 aarch64:HarmonyOS 分支,输出 aarch64-unknown-linux-ohos。config.sub 需要双锚点:maybe_os 的 kernel 列表加 linux-ohos*,basic os 列表加 -linux-ohos*。为什么缺一不可:config.sub 先按"kernel-os 成对后缀"匹配,aarch64-unknown-linux-ohos 的 os 段是单段的 linux-ohos,不会落入 maybe_os 分支,而是走 basic machine-os 校验——只改其中一处,另一条路径仍然拒绝。这是读控制流得出的判断,也被验证证实:补丁后 sh build-aux/config.guess 输出 aarch64-unknown-linux-ohos,sh config.sub aarch64-unknown-linux-ohos 原样接受;configure 得到 host_os=linux-ohos,命中上游 case $host_os 的 linux* 分支,代码按 Linux 路径处理——这与鸿蒙 musl 环境的实际情况一致,后续构建与测试没有再出现平台分支相关的问题。

4.2 发布包 .pc 烘焙外来 prefix:两条命令定位

消费者侧的现象链:构建注入 PKG_CONFIG_PATH 指向依赖包安装目录后,编译报 fatal error: 'sndfile.h' file not found——而头文件明明随包安装了。

定位只需要两条命令,且自包含、可移植到任何同类场景(图3 以设备上现存的一个发布包为例):

请添加图片描述

图3 .pc 烘焙外来 prefix 的探针与对照(真机复现)

第一条,pkg-config --variable=includedir <包名>:输出指向 .cache/conan2/p/b/<哈希>/p/include,而本机该包的实际安装目录哈希不同——路径是打包机上烘焙的绝对路径,ls 确认不存在。第二条,stdin 编译探针:echo '#include <头文件>' | clang -x c - -fsyntax-only -I$(pkg-config --variable=includedir <包名>),复现 fatal error;对照组把 -I 指向本机真实 include 目录,编译通过。因果链闭合:不是头文件没装,是 pc 把消费者引向了一条不存在的路。

根因:conan 在打包机上生成 .pc 时把 prefix 写成本机绝对路径,包发布到制品仓后,这个路径在任何其他机器上都悬空。这不是孤例。对本机 conan 缓存中的 389 个 .pc 全量分类:prefix=/ 的约 176 个,prefix=/usr/local 的约 20 个,指向外来 .cache/conan2 路径的 7 个包夹;显式做了相对重定位(prefix=${pcfiledir}/../..)的仅 98 个。社区仓全树有 412 个 conanfile.py 显式使用 PKG_CONFIG_PATH,这个暴露面相当宽。

解法分两层。消费侧:本配方用 PkgConfigDeps 重新生成 sndfile 元数据,PKG_CONFIG_PATH 指向 generators_folder,绕开包内烘焙值。生产侧:本包自己安装的 liquidsfz.pc 在打包阶段把 prefix 重写为 ${pcfiledir}/../..(重写计数不为 1 即失败,不允许静默放过),同时剥离烘焙的绝对 -L/-I。${pcfiledir} 相对重定位正是那 98 处既有条目的做法,新交付保持同一惯例。

这里如实记录一个流程插曲。这个问题一度被提名为社区知识库扩展条目(机理与普查数据都成立),路径审计时发现知识库已有同类条目——ccfits 2.7 交付中的 stale-pc-prefix-regen,机理与修复建议一致——整条撤回,降为团队经验记录。教训落在流程上:查重结论必须附检索命令与命中清单原文。本次初判的检索范围口径有偏差(自报 233 个文件,库内实际 1,243 个),漏掉了必命中的同类条目;审计环节独立重跑探针并以命中条目否决了申报,流程按设计工作,但初判环节的检索纪律值得记录。

4.3 主线前移 965 个文件之后:CI 证据为什么不能沿用

时间线:候选推送后,官方主线合入了其他库的交付,旧基线到新基线相差 965 个文件(949 增 16 改,ci/** 零改动,与本包无冲突面)。此时交付提交需要重放到新基线,随之产生一个判断题:delta 没有变化(12 个文件逐字节同 blob),第 2 轮 CI 的全门通过能否沿用?

答案是不能,依据是"CI 证据绑定被验证的树"。重放后候选的根树从 c12e1419 变为 fd218c8b,差异恰好就是这 965 个基线文件;而 CI 的多个门禁以基线状态为输入——G0/KC 知识图谱完整性是全仓扫描,L0i/L0j 的依赖在册与版本冲突检查随 main 演化。第 2 轮的 PASS 证明的是一棵再也不会参与合并的树。这个判断在复审中被独立复核确认,第 3 轮按完整重跑执行。

执行上有一个真实障碍:字面 git rebase 在这台设备上不可行。设备文件系统大小写不敏感,而仓库历史上有两笔交付分别落在 libXext/ 与 libxext/ 两个目录,检出后小写目录的文件覆盖大写目录的同名文件,工作区恒定显示 2 条 modified 伪影——rebase 要求干净工作区,autostash 也无法收敛(stash 掉一处,另一处立即浮现)。绕行方案是临时索引重放:GIT_INDEX_FILE 指向临时索引,read-tree 读入新基线,注入 12 个 blob,write-tree 得到目标树,commit-tree 以新基线为父、保留原作者与原提交信息生成提交,最后以带 CAS 条件的 update-ref 更新分支。产物与 rebase 语义等价,树与 delta 逐字节核验。中间一轮尝试曾产生一棵丢失 delta 的错误树,因 update-ref 带 CAS 条件未能落到分支上,直接废弃——这也是重放类操作必须带 CAS 的原因。

第 3 轮 CI 在新基线上完整重跑,全门通过(图4):G0=540 条知识图谱条目、KC/JC、L0(依赖在册、版本首发布、目录结构)、L1a-c、L1e 破坏性负向验证、L4、L5,L2 因纯库无 bin/ 按预期跳过,Build Summary 1/1。

5 简单问题速览

包名契约:Conan 包名必须等于归档目录名(L0m-4)。首跑 name="liquidsfz" 对目录 libliquidsfz 直接被门禁拒绝,改为 libliquidsfz 后通过;上游产品名、pc 文件名、头文件名保持 liquidsfz 原样,消费者不受影响。

JACK/LV2 的关闭:两者默认开启,分别依赖 JACK 服务与 X11 系闭包,鸿蒙 PC 上不具备。configure 显式 --without-jack --without-lv2。静态库形态对齐上游默认(AC_DISABLE_SHARED 加 pic-only),配方显式传参以保证确定性。

日志告警辨析:CI 日志中 9 处 [fail] … 'curl' 是设备无 curl 时 conan 的下载回退告警(第 2、3 轮数量一致),不是门禁失败。读日志时先分辨告警与门禁,避免误判。

6 真机运行与复现

CI 入口:zsh ci/conan/build_and_test.sh(profile ohos-aarch64,CI_PACKAGES 指向 archives/l/libliquidsfz/0.4.1)。第 3 轮的门禁结果见图4。

请添加图片描述

图4 权威 CI 第 3 轮(新基线)门禁结果与 Build Summary(日志节选)

上游测试入口:make check(automake TESTS = testsynth testsfzreader)。两项测试完全自足:现场用 libsndfile 生成 wav 与 sfz 文件、纯内存解析渲染,不需要数据文件、音频设备和网络,只要求工作目录可写——这是它能在真机 CI 里原样跑通的前提(图5)。

请添加图片描述

图5 make check 在真机的执行结果(CI 日志节选)

消费者验证分两层。test_package 手写 PCM16 wav 与对应 sfz,LiquidSFZ::Synth 加载后渲染 MIDI note 60,断言输出非静音且幅度有界,实测峰值 0.218745;L1e 门禁把包内 lib/、include/ 移走后确认测试真实失败,证明依赖关系为真、测试不是形式化通过(图6)。

请添加图片描述

图6 test_package 渲染断言与 L1e 破坏性负向验证(日志节选)

消费者接入方式:conanfile 中 requires "libliquidsfz/0.4.1",编译链接静态库,包含 liquidsfz.hh 即可使用 LiquidSFZ::Synth。配方、补丁与 test_package 完整源码见文末入口 3,文中每个问题都能对应到具体 diff。

7 小结

可复用的方法有三条。第一,捆绑旧式 GNU config 的 autotools 项目在鸿蒙上的标准解法:先 grep -c '^GUESS=' 判旧式,再按双锚点打补丁(config.guess 认 uname,config.sub 补 kernel 与 basic os 两个列表);不要用 --build/--host 谎报三元组,假前提会在后续平台分支里还回来。第二,.pc 悬空路径的两条命令自包含可复用:--variable=includedir 暴露烘焙值,stdin 编译探针复现失败;自己发包时 pc 一律 ${pcfiledir} 相对重定位并做 fail-closed 校验。第三,CI 证据绑定被验证的树:基线发生实质变化时,即使 delta 逐字节不变也必须重跑;文件系统大小写不敏感导致工作区无法干净时,带 CAS 的临时索引重放是 rebase 的确定性等价物。

最后把这次用到的入口留在这里:

  1. 开源鸿蒙PC社区——适配方向与积分赛动态;
  2. 社区项目平台——新库在这里申请;
  3. libliquidsfz 0.4.1 配方与补丁(已合入主线)——本篇交付的配方、补丁与 test_package。

8 常见问题(FAQ)

Q1:liquidsfz 与 sfizz、LinuxSampler 是什么关系?
都是 SFZ 播放器/库,定位不同:LinuxSampler 是完整的采样器应用,sfizz 是 BSD 许可的 SFZ 库加插件(仓库已于 2026 年 6 月归档为只读),liquidsfz 定位为易于集成的采样器库,带 LV2/JACK 前端。四者均列于 sfzformat.com 的播放器页。

Q2:为什么鸿蒙 PC 版本没有 JACK/LV2 前端?
JACK 前端需要运行中的 JACK 服务,LV2 插件的 GUI 路径依赖 X11 系闭包,当前平台不具备。核心库不依赖这两者,--without 关闭后静态库与全部上游测试完整。上游 configure 本身就提供这些开关,属正常裁剪而非代码删改。

Q3:为什么只交付静态库?
上游默认即静态(AC_DISABLE_SHARED 加 pic-only),配方对齐上游默认并显式传参。配方保留了 shared 开关,但本次只对上游默认形态做了构建与测试验证,shared 变体未交付验证,报告中有如实说明。

Q4:三轮 CI 分别发生了什么?
第 1 轮败于包名契约(conan name 必须等于目录名);第 2 轮在原基线全门通过;官方主线合入 965 个文件后按流程刷新基线,第 3 轮在新基线完整重跑、再次全门通过。三轮日志均有留档。

Q5:conan 场景下消费者报 fatal error: 'sndfile.h' file not found,怎么排查?
两步:pkg-config --variable=includedir sndfile 检查输出的路径是否存在——若指向 .cache/conan2 一类打包机缓存路径即为本篇第 4.2 节所述问题;再用 stdin 编译探针复现。解法是消费侧改用 PkgConfigDeps 重新生成的元数据(PKG_CONFIG_PATH 指向 generators_folder),不要依赖包内烘焙的 pc。

Q6:自己的包也发布 .pc,如何避免发布后悬空?
打包阶段把 prefix 重写为 ${pcfiledir}/../.. 形式的相对路径,并对重写是否发生做计数校验(不发生即失败)。参考第 4.2 节中 liquidsfz.pc 的处理。

Q7:libsndfile 的 CMakeDeps 消费有什么已知问题?
发布的 libsndfile/1.2.2 包 cpp_info.includedirs 为空,CMakeDeps 消费者无法直接包含 sndfile.h(PkgConfigDeps 不受影响)。包内头文件实际存在,根因尚未定位,属于 libsndfile 配方侧问题,已在交付说明中如实披露,待独立修复。

参考文献

[1] Qian Zhang, Tsz On Li, Ying Wang, Li Li, Shing-Chi Cheung. CROSS2OH: Enabling Seamless Porting of C/C++ Software Libraries to OpenHarmony[C]// Proceedings of the 40th IEEE/ACM International Conference on Automated Software Engineering (ASE 2025). Seoul, Republic of Korea: IEEE, 2025: 1744-1755. DOI: 10.1109/ASE63991.2025.00146

[2] 知识驱动、自动修复:东北大学团队为开源鸿蒙 C/C++ 软件库移植打造自动化工具链——开源鸿蒙技术课题成果展播(第 5 期)[EB/OL]. 微信公众平台, 2026-08. https://mp.weixin.qq.com/s/0pddnK1bxTAUNf9PnTuebQ

[3] Stefan Westerfeld 个人主页(aRts/BEAST/SpectMorph/audiowmark). http://space.twc.de/~stefan/

[4] liquidsfz 0.4.1 Release(2026-04-07). https://github.com/swesterfeld/liquidsfz/releases/tag/0.4.1

[5] SFZ Format(格式定义与维护). https://sfzformat.com/

[6] SFZ Format Players(播放器列表). https://sfzformat.com/software/players

[7] Pianobook(免费采样包社区). https://www.pianobook.co.uk/

[8] libsndfile 官方站点. https://libsndfile.github.io/libsndfile/

[9] AUR: liquidsfz-git. https://aur.archlinux.org/packages?K=liquidsfz

[10] OpenHarmonyPCDeveloper/build_in_harmonyos(社区仓库). https://gitcode.com/OpenHarmonyPCDeveloper/build_in_harmonyos

欢迎加入开源鸿蒙PC社区: https://harmonypc.csdn.net/

欢迎在PC社区平台申请新建项目:https://atomgit.com/OpenHarmonyPCDeveloper/

Logo

作为“人工智能6S店”的官方数字引擎,为AI开发者与企业提供一个覆盖软硬件全栈、一站式门户。

更多推荐