A
Air官方
【Air 系列 · 鸿蒙 PC 适配全记录】02 / 56
■ 应用:AirAudio(from Audacity)(AirAudio)
■ 定位:Audacity 移植,录音与音频编辑
■ 上游:Audacity 3.7.8 | 路线:GTK3+wxGTK3 | 版本:1.3.0
■ 开源: https://gitcode.com/Andy88/Harmony-Audacity
■ 市场状态:已上架 AGC 应用页: https://appgallery.huawei.com/app/6917614115723675515
■ 官网(免费下载): https://www.shunidc.cn/
————————————————————
开源鸿蒙 PC 社区: https://harmonypc.csdn.net/
PC 社区项目申请: https://atomgit.com/OpenHarmonyPCDeveloper
适配开源地址:https://gitcode.com/Andy88/Harmony-Audacity
环境搭建文章:https://blog.csdn.net/weixin_52908342/article/details/161343743
▍一、为什么要适配 Audacity
Audacity 是开源世界使用最广泛的数字音频编辑器之一。多轨录音、波形剪辑、效果链处理、多格式导出,这些能力让它长期停留在播客制作、采样整理和语音素材处理的工具链里。它不追求工程化的音乐制作,而是把"打开一段音频、剪好、加效果、导出来"这条路径做得足够直接,这正是桌面工具最难被替代的一类价值。
把 Audacity 带到鸿蒙 PC,测试的是一个比文本编辑沉重得多的问题:当一个上游同时依赖 wxWidgets、GTK、PortAudio 和十几条音频编解码库时,交叉编译如何在鸿蒙 NDK 上全部落地,图形栈又如何在不重写界面的前提下接入系统窗口。工程给出的答案是全栈复用:wxWidgets 3.2.8 走 wxGTK3 后端,压在已有的 AirGimp GTK3 OHOS GDK 栈上,音频 I/O 则不依赖任何桌面音频服务,直接用 OHAudio C API 写了一个 PortAudio 后端。
当前鸿蒙工程包名为 com.airaudio.app,版本 1.0.0,deviceTypes 覆盖 phone、tablet、2in1,目标 Native ABI 为 arm64-v8a。上游基线取 Audacity 3.7.8,即 3.x 系列的最终稳定线;用户最初提供的 audacity-master 属于 4.0.0-prerelease,其 AU4/QML 界面在自研 QPA 上没有先例,工程据此把"正常可交付"锚定在稳定线。上游许可为 GPL-2.0+,移植工程保持同源开源。
▍二、先划清边界:不把编译通过当成适配完成
Audacity 的桌面形态依赖三套在鸿蒙上都不存在的底座:X11/GLib 事件循环、ALSA/JACK 音频服务、以及以可执行文件为中心的构建产物。把源码编译成 .so 只解决了符号问题,不等于得到了能录音、能播放、能出波形的应用。工程在 PORTING.md 中把这些差距逐层登记,并明确了当前产物到达的深度。
层次 原项目实现(传统桌面方案) 鸿蒙侧当前方案
应用入口 main 进入 wxWidgets 初始化 Stage 模型 EntryAbility + native 专用线程启动 wx
窗口承载 X11/XCB 上的 GTK 窗口 ArkTS XComponent(TEXTURE) + GTK3 OHOS GDK 后端
音频 I/O ALSA / JACK / PulseAudio 等 hostapi PortAudio 19.7.0 + 自研 OHAudio hostapi(pa_ohos.c)
本体形态 audacity 可执行文件 libAudacity.so(SHARED)+ lib-*.a 静态并入(P7)
解码与编码链 系统或 conan 提供的编解码库 libsndfile/ogg/vorbis/flac/opus/lame/mpg123 全部交叉编译随包
MIDI 子系统 PortMidi 完整设备枚举 自研 stub,设备枚举恒空(OHOS 无 MIDI)
菜单栏 GTK widget menubar ArkUI 原生菜单栏,菜单树经 NAPI 从引擎导出
这张表的最后一列就是当前可运行产物的真实边界:图形、事件循环与音频输出已经落在鸿蒙系统设施上,而 MIDI 这类系统不提供的能力以显式 stub 存在,不做伪装。
▍三、鸿蒙版的整体架构
工程采用 AirGimp 双层架构。ArkTS 层负责 Ability 生命周期、rawfile 数据部署与系统弹层;NAPI 薄桥 libairaudiobridge.so 只链接系统库,所有重型依赖在运行时按拓扑序 dlopen 进来,最终由 libairaudio_core.so 在宿主线程上驱动 wx 主循环。启动时序遵循 AirGimp 铁律的 wx 版本:先 wxEntryStart 完成 GTK 初始化,再注入 OHNativeWindow,随后 wxTheApp->CallOnInit() 由 AudacityApp::OnInit 建立主窗,最后进入 OnRun() 的 wx 主循环(即 gtk_main)。
text
EntryAbility(rawfile 数据部署、权限、Want 处理)
└── Index.ets / XComponent(TEXTURE) + ArkUI 原生菜单栏
└── libairaudiobridge.so(薄 NAPI:仅系统库)
└── dlopen 拓扑序 47+ 库(RTLD_GLOBAL)→ libairaudio_core.so
├── libAudacity.so(src + 全部 lib-* 静态并入 + nyquist/sqlite/soxr)
├── libwx_gtk3u_*.so(7 库)→ libgtk-3 / libgdk-3(OHOS GDK 后端)
├── libportaudio.so → libohaudio.so(系统 OHAudio)
└── 音频解码链(sndfile/flac/ogg/vorbis/opus/lame/mpg123)
主要目录如下:
text
AirAudio/
├── AppScope/app.json5 # 包名、版本
├── PORTING.md # 选型、依赖矩阵、踩坑实录
├── entry/src/main/
│ ├── ets/
│ │ ├── entryability/EntryAbility.ets # 数据部署与生命周期
│ │ ├── pages/Index.ets # XComponent 宿主 + ArkUI 菜单栏
│ │ └── utils/ # FileManager / SubWindowManager / ImmersiveManager
│ ├── cpp/
│ │ ├── airaudio_core.cpp # 宿主核心:env 前置、dlopen 拓扑、wx 启动
│ │ ├── napi_init.cpp # NAPI 桥
│ │ └── libs/arm64-v8a/ # 预编译库整目录打包通道(107 个 .so)
│ └── resources/rawfile/ # 随 HAP 携带的运行数据
└── native/
├── sources/ # audacity、wxwidgets、portaudio、编解码库源码树
├── patches/ # portaudio-ohos 等鸿蒙补丁
├── scripts/ # env.sh 与七段构建脚本
└── install/ # 交叉编译安装前缀
▍四、核心适配过程
### 1. 版本裁决与 GUI 栈复用
选型从两个决定展开。其一是版本:3.7.8 稳定线的 wx GUI 代码仍参与构建,而 4.0.0-prerelease 的 au3 wx 代码上游已不参与构建,AU4 走 QML,在自研 QPA 上属于无人区。其二是界面栈:上游 find_package(wxWidgets) 决定了不能换 GUI 框架,工程因此把 wxWidgets 3.2.8 编为 wxGTK3 后端,压在 AirGimp 工程现成的 GTK3 OHOS GDK 栈上。裁剪对齐上游 conan 配置:tiff、opengl、ribbon、richtext、stc、webview 等组件关闭,--with-x=no 并以 libX11.a 空桩补链接。GTK3 栈本身(glib/gtk3/gdk/cairo/pango/pixbuf 及 png/jpeg/zlib/expat)经跨工程 PREFIX 直接引用,零重复构建。
### 2. PortAudio 的 OHAudio 后端
音频 I/O 是本工程唯一从零编写的系统接口层。native/patches/portaudio-ohos 为 PortAudio 19.7.0 新增 OHAudio hostapi(pa_ohos.c),走 NDK sysroot 的 libohaudio.so C API。Audacity 的设备枚举、采样率协商与回调式播放都经由 PortAudio 抽象层,因此这个后端一旦注册,上层无需感知平台差异。MIDI 侧则如实登记为 stub:portmidi 与 libuuid 均为自研最小实现,设备枚举恒空。
### 3. 本体库化与树内补丁
可执行文件形态在鸿蒙上不可用,补丁 patch_audacity_ohos.sh 以幂等标记(AIRAUDIO-P*)改造源码树:P1 把 src 的 add_executable 改为 add_library SHARED;P7 进一步把 audacity_library 改为 STATIC 并入 libAudacity.so,规避 musl 上深层 NEEDED 链的 dlopen 崩溃;P3 内联 musl 缺失的 linux/magic.h;P4 对 arm64 强制关闭 libsbsms 的 ENABLE_SSE;P5 跳过依赖 glibc dlinfo 的 findlib;P6 跳过 AppImage 打包。依赖侧还修了上游 mpg123.cmake 引用不存在别名目标的 bug,并处理 opus/opusfile 的安装布局。
### 4. 宿主工具、构建链与日志通道
Audacity 构建期需要 image-compiler 生成主题头。交叉环境下它会被错误地编成 arm64 二进制,工程在 macOS 侧建立独立 CMake 工程(brew wxwidgets + lib 子集源码),经 -DIMAGE_COMPILER_EXECUTABLE 预置给交叉构建,与 Blender makesdna 的处理同款。全流程由 native/scripts/ 七段脚本串起:env.sh 环境前置、wxwidgets/portaudio/audio_deps/fix_audio_libs 依赖链、image-compiler 宿主工具、build_audacity.sh 本体、deploy_data.sh 与 deploy_libs.sh 数据与库部署。日志遵循文件优先纪律:core GLib 日志与启动里程碑写 /data/storage/el2/base/haps/entry/files/gimp_trace.txt,仅 ERROR 级上 hilog,SIGSEGV handler 落盘后链回系统 faultlogger。
▍五、适配中遇到的几个难点
### 难点一:musl 下深层 NEEDED 链导致 dlopen 崩溃
Audacity 原本按上游惯例把内部模块编为几十个 lib-*.so。首轮产物在 dlopen(libAudacity.so) 时稳定 SIGSEGV,落点固定在 dlopen_impl+2056。定位结论是 60 余个深层依赖链触发了 musl 动态链接器在依赖解析上的崩溃路径。工程没有沿着动态链接器继续挖,而是改产物形态:P7 把全部内部库 STATIC 并入本体,libAudacity.so 的 NEEDED 链一下子收窄,问题随之消失。这也是"编译通过不等于可运行"的直接例证。
### 难点二:libtool 交叉链接静默失败
opusfile、lame、mpg123 的构建在本机 libtool 下出现同一种怪象:--mode=link 返回成功,但产物里子目录对象(mpglib、compat)整体丢失,运行时 dlopen 报 ExitMP3、INT123_compat_strdup not found。工程改为 make 只产对象文件,再由 fix_audio_libs.sh 用 clang 手工全对象链接,并加符号自检,把"链接成功"从退出码语义修正为符号存在性语义。
### 难点三:PATH 里混入的 DevEco diff 让 configure 误判
pcre2 的 config.h 在交叉构建中始终不生成。根因相当隐蔽:PATH 里 DevEco toolchains 自带的 diff 对不存在的文件返回 0,autotools 的 config.status 因此误判模板 unchanged,跳过生成。解法落在 env.sh:强制把 /usr/bin:/bin 前置,保证构建期拿到的是系统 diff。工具链环境变量对一个 autotools 工程的影响,在这里体现得比任何编译选项都直接。
### 难点四:hvigor 闭包打包丢弃预编译库
首轮 HAP 里只有 2 个 so:hvigor 依据链接闭包打包,运行时 dlopen 的库不在闭包里。工程启用 entry/libs/arm64-v8a 整目录通道,把 107 个预编译库随 HAP 携带,部署脚本同时用 fix_needed.py 做版本号归一,清理 libvorbisenc.so.2.0.12 这类残留后缀,保证 dlopen 清单可以按裸名拓扑展开。
### 难点五:真机签名授权挡在新机前面
模拟器安装顺利,真机报 9568322:debug profile 需要 AGC 注册设备 UDID,且设备端需授权"USB 安装"。工程的处理是先把验证链落在模拟器(AirSky2in1)上跑通,真机授权作为交付前的人工步骤保留。PORTING.md 对此如实登记为待办,文章同样不做超出记载的宣称。
▍六、编译、安装与启动
Native 侧按 PORTING.md 的构建链执行,顺序如下:
bash
source native/scripts/env.sh
bash native/scripts/build_wxwidgets.sh # wxGTK(一次)
bash native/scripts/build_portaudio.sh # + OHOS 后端
bash native/scripts/build_audio_deps.sh # sndfile 链(CMake 系)
bash native/scripts/fix_audio_libs.sh # lame/mpg123 补链 + id3tag/wavpack/uuid
bash native/scripts/build_image_compiler.sh # 宿主工具(一次)
bash native/scripts/build_audacity.sh # 本体(内部先跑补丁脚本)
bash native/scripts/deploy_data.sh # rawfile 运行数据
bash native/scripts/deploy_libs.sh # 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。PORTING.md 记载当前 HAP 约 230MB,体积主要来自随包的编解码链与运行数据。安装与启动:
bash
hdc install -r entry/build/default/outputs/default/entry-default-signed.hap
hdc shell aa start -a EntryAbility -b com.airaudio.app
▍七、当前功能边界
工程文档与代码支持的已覆盖能力:
- 从源码到 HAP 的全链交叉编译可复现,libAudacity.so 与 107 个预编译库随包部署;
- Stage 模型壳层,XComponent(TEXTURE) 全窗口渲染 GTK 画面(entry/src/main/ets/pages/Index.ets);
- ArkUI 原生菜单栏:菜单树经 bridge.getMenubarJson() 从引擎侧导出,叶子项经 bridge.activateMenuAction() 触发,菜单项 enabled 状态按周期刷新;
- 启动时序 wxEntryStart → 注入 OHNativeWindow → CallOnInit → OnRun;
- PortAudio OHAudio 后端接入系统音频输出,解码链覆盖 ogg/vorbis/flac/opus/mp3/wavpack;
- 文件选择器桥与对话框子窗口桥已按 AirGimp P11 模式接线(utils/FileManager.ets、utils/SubWindowManager.ets);
- 日志双通道:文件优先 + 仅 ERROR 上 hilog,SIGSEGV 落盘并链回 faultlogger。
尚未覆盖或规划中的能力(均有文档依据):
- mod-*.so(import/export 插件、mod-script-pipe)已构建未随包,待 ModuleManager 运行时按需加载(M5 计划);
- MIDI 输入输出为 stub,OHOS 平台当前无 MIDI 设备枚举路径;
- picker 桥、对话框桥在 Audacity 侧的联动尚待真机验证;
- 真机安装待设备端授权或 AGC UDID 注册,当前验证平台为模拟器。
因此,当前版本更准确的定位是 Audacity 鸿蒙移植的可复现构建骨架:编译链、图形承载、音频后端与壳层桥接已经落位,交互级验收还停在模拟器与真机授权之间。
▍八、总结
AirAudio 的价值在于把"重依赖桌面应用"的移植路径走通了一遍:wxWidgets 不换框架压到 GTK3 OHOS 栈,PortAudio 不等系统音频服务自己写后端,本体不守可执行形态改成单库 + 静态并入。这条路径沉淀出的不只是 Audacity 的 HAP,还有 libtool 补链、NEEDED 归一、宿主工具分离等一批可复用的工程手法。
后续工作的重心在 PORTING.md 的待办清单里已经写明:mod 插件的运行时按需加载、菜单栏接管从桥接走向深度联动、picker 与对话框桥在 Audacity 侧完成真机验证,以及补齐真机签名授权后的完整交互验收。每一步的验收口径保持一致:可构建的 HAP,加上设备上可复现的音频编辑行为。
■ 应用:AirAudio(from Audacity)(AirAudio)
■ 定位:Audacity 移植,录音与音频编辑
■ 上游:Audacity 3.7.8 | 路线:GTK3+wxGTK3 | 版本:1.3.0
■ 开源: https://gitcode.com/Andy88/Harmony-Audacity
■ 市场状态:已上架 AGC 应用页: https://appgallery.huawei.com/app/6917614115723675515
■ 官网(免费下载): https://www.shunidc.cn/
————————————————————
开源鸿蒙 PC 社区: https://harmonypc.csdn.net/
PC 社区项目申请: https://atomgit.com/OpenHarmonyPCDeveloper
适配开源地址:https://gitcode.com/Andy88/Harmony-Audacity
环境搭建文章:https://blog.csdn.net/weixin_52908342/article/details/161343743
▍一、为什么要适配 Audacity
Audacity 是开源世界使用最广泛的数字音频编辑器之一。多轨录音、波形剪辑、效果链处理、多格式导出,这些能力让它长期停留在播客制作、采样整理和语音素材处理的工具链里。它不追求工程化的音乐制作,而是把"打开一段音频、剪好、加效果、导出来"这条路径做得足够直接,这正是桌面工具最难被替代的一类价值。
把 Audacity 带到鸿蒙 PC,测试的是一个比文本编辑沉重得多的问题:当一个上游同时依赖 wxWidgets、GTK、PortAudio 和十几条音频编解码库时,交叉编译如何在鸿蒙 NDK 上全部落地,图形栈又如何在不重写界面的前提下接入系统窗口。工程给出的答案是全栈复用:wxWidgets 3.2.8 走 wxGTK3 后端,压在已有的 AirGimp GTK3 OHOS GDK 栈上,音频 I/O 则不依赖任何桌面音频服务,直接用 OHAudio C API 写了一个 PortAudio 后端。
当前鸿蒙工程包名为 com.airaudio.app,版本 1.0.0,deviceTypes 覆盖 phone、tablet、2in1,目标 Native ABI 为 arm64-v8a。上游基线取 Audacity 3.7.8,即 3.x 系列的最终稳定线;用户最初提供的 audacity-master 属于 4.0.0-prerelease,其 AU4/QML 界面在自研 QPA 上没有先例,工程据此把"正常可交付"锚定在稳定线。上游许可为 GPL-2.0+,移植工程保持同源开源。
▍二、先划清边界:不把编译通过当成适配完成
Audacity 的桌面形态依赖三套在鸿蒙上都不存在的底座:X11/GLib 事件循环、ALSA/JACK 音频服务、以及以可执行文件为中心的构建产物。把源码编译成 .so 只解决了符号问题,不等于得到了能录音、能播放、能出波形的应用。工程在 PORTING.md 中把这些差距逐层登记,并明确了当前产物到达的深度。
层次 原项目实现(传统桌面方案) 鸿蒙侧当前方案
应用入口 main 进入 wxWidgets 初始化 Stage 模型 EntryAbility + native 专用线程启动 wx
窗口承载 X11/XCB 上的 GTK 窗口 ArkTS XComponent(TEXTURE) + GTK3 OHOS GDK 后端
音频 I/O ALSA / JACK / PulseAudio 等 hostapi PortAudio 19.7.0 + 自研 OHAudio hostapi(pa_ohos.c)
本体形态 audacity 可执行文件 libAudacity.so(SHARED)+ lib-*.a 静态并入(P7)
解码与编码链 系统或 conan 提供的编解码库 libsndfile/ogg/vorbis/flac/opus/lame/mpg123 全部交叉编译随包
MIDI 子系统 PortMidi 完整设备枚举 自研 stub,设备枚举恒空(OHOS 无 MIDI)
菜单栏 GTK widget menubar ArkUI 原生菜单栏,菜单树经 NAPI 从引擎导出
这张表的最后一列就是当前可运行产物的真实边界:图形、事件循环与音频输出已经落在鸿蒙系统设施上,而 MIDI 这类系统不提供的能力以显式 stub 存在,不做伪装。
▍三、鸿蒙版的整体架构
工程采用 AirGimp 双层架构。ArkTS 层负责 Ability 生命周期、rawfile 数据部署与系统弹层;NAPI 薄桥 libairaudiobridge.so 只链接系统库,所有重型依赖在运行时按拓扑序 dlopen 进来,最终由 libairaudio_core.so 在宿主线程上驱动 wx 主循环。启动时序遵循 AirGimp 铁律的 wx 版本:先 wxEntryStart 完成 GTK 初始化,再注入 OHNativeWindow,随后 wxTheApp->CallOnInit() 由 AudacityApp::OnInit 建立主窗,最后进入 OnRun() 的 wx 主循环(即 gtk_main)。
text
EntryAbility(rawfile 数据部署、权限、Want 处理)
└── Index.ets / XComponent(TEXTURE) + ArkUI 原生菜单栏
└── libairaudiobridge.so(薄 NAPI:仅系统库)
└── dlopen 拓扑序 47+ 库(RTLD_GLOBAL)→ libairaudio_core.so
├── libAudacity.so(src + 全部 lib-* 静态并入 + nyquist/sqlite/soxr)
├── libwx_gtk3u_*.so(7 库)→ libgtk-3 / libgdk-3(OHOS GDK 后端)
├── libportaudio.so → libohaudio.so(系统 OHAudio)
└── 音频解码链(sndfile/flac/ogg/vorbis/opus/lame/mpg123)
主要目录如下:
text
AirAudio/
├── AppScope/app.json5 # 包名、版本
├── PORTING.md # 选型、依赖矩阵、踩坑实录
├── entry/src/main/
│ ├── ets/
│ │ ├── entryability/EntryAbility.ets # 数据部署与生命周期
│ │ ├── pages/Index.ets # XComponent 宿主 + ArkUI 菜单栏
│ │ └── utils/ # FileManager / SubWindowManager / ImmersiveManager
│ ├── cpp/
│ │ ├── airaudio_core.cpp # 宿主核心:env 前置、dlopen 拓扑、wx 启动
│ │ ├── napi_init.cpp # NAPI 桥
│ │ └── libs/arm64-v8a/ # 预编译库整目录打包通道(107 个 .so)
│ └── resources/rawfile/ # 随 HAP 携带的运行数据
└── native/
├── sources/ # audacity、wxwidgets、portaudio、编解码库源码树
├── patches/ # portaudio-ohos 等鸿蒙补丁
├── scripts/ # env.sh 与七段构建脚本
└── install/ # 交叉编译安装前缀
▍四、核心适配过程
### 1. 版本裁决与 GUI 栈复用
选型从两个决定展开。其一是版本:3.7.8 稳定线的 wx GUI 代码仍参与构建,而 4.0.0-prerelease 的 au3 wx 代码上游已不参与构建,AU4 走 QML,在自研 QPA 上属于无人区。其二是界面栈:上游 find_package(wxWidgets) 决定了不能换 GUI 框架,工程因此把 wxWidgets 3.2.8 编为 wxGTK3 后端,压在 AirGimp 工程现成的 GTK3 OHOS GDK 栈上。裁剪对齐上游 conan 配置:tiff、opengl、ribbon、richtext、stc、webview 等组件关闭,--with-x=no 并以 libX11.a 空桩补链接。GTK3 栈本身(glib/gtk3/gdk/cairo/pango/pixbuf 及 png/jpeg/zlib/expat)经跨工程 PREFIX 直接引用,零重复构建。
### 2. PortAudio 的 OHAudio 后端
音频 I/O 是本工程唯一从零编写的系统接口层。native/patches/portaudio-ohos 为 PortAudio 19.7.0 新增 OHAudio hostapi(pa_ohos.c),走 NDK sysroot 的 libohaudio.so C API。Audacity 的设备枚举、采样率协商与回调式播放都经由 PortAudio 抽象层,因此这个后端一旦注册,上层无需感知平台差异。MIDI 侧则如实登记为 stub:portmidi 与 libuuid 均为自研最小实现,设备枚举恒空。
### 3. 本体库化与树内补丁
可执行文件形态在鸿蒙上不可用,补丁 patch_audacity_ohos.sh 以幂等标记(AIRAUDIO-P*)改造源码树:P1 把 src 的 add_executable 改为 add_library SHARED;P7 进一步把 audacity_library 改为 STATIC 并入 libAudacity.so,规避 musl 上深层 NEEDED 链的 dlopen 崩溃;P3 内联 musl 缺失的 linux/magic.h;P4 对 arm64 强制关闭 libsbsms 的 ENABLE_SSE;P5 跳过依赖 glibc dlinfo 的 findlib;P6 跳过 AppImage 打包。依赖侧还修了上游 mpg123.cmake 引用不存在别名目标的 bug,并处理 opus/opusfile 的安装布局。
### 4. 宿主工具、构建链与日志通道
Audacity 构建期需要 image-compiler 生成主题头。交叉环境下它会被错误地编成 arm64 二进制,工程在 macOS 侧建立独立 CMake 工程(brew wxwidgets + lib 子集源码),经 -DIMAGE_COMPILER_EXECUTABLE 预置给交叉构建,与 Blender makesdna 的处理同款。全流程由 native/scripts/ 七段脚本串起:env.sh 环境前置、wxwidgets/portaudio/audio_deps/fix_audio_libs 依赖链、image-compiler 宿主工具、build_audacity.sh 本体、deploy_data.sh 与 deploy_libs.sh 数据与库部署。日志遵循文件优先纪律:core GLib 日志与启动里程碑写 /data/storage/el2/base/haps/entry/files/gimp_trace.txt,仅 ERROR 级上 hilog,SIGSEGV handler 落盘后链回系统 faultlogger。
▍五、适配中遇到的几个难点
### 难点一:musl 下深层 NEEDED 链导致 dlopen 崩溃
Audacity 原本按上游惯例把内部模块编为几十个 lib-*.so。首轮产物在 dlopen(libAudacity.so) 时稳定 SIGSEGV,落点固定在 dlopen_impl+2056。定位结论是 60 余个深层依赖链触发了 musl 动态链接器在依赖解析上的崩溃路径。工程没有沿着动态链接器继续挖,而是改产物形态:P7 把全部内部库 STATIC 并入本体,libAudacity.so 的 NEEDED 链一下子收窄,问题随之消失。这也是"编译通过不等于可运行"的直接例证。
### 难点二:libtool 交叉链接静默失败
opusfile、lame、mpg123 的构建在本机 libtool 下出现同一种怪象:--mode=link 返回成功,但产物里子目录对象(mpglib、compat)整体丢失,运行时 dlopen 报 ExitMP3、INT123_compat_strdup not found。工程改为 make 只产对象文件,再由 fix_audio_libs.sh 用 clang 手工全对象链接,并加符号自检,把"链接成功"从退出码语义修正为符号存在性语义。
### 难点三:PATH 里混入的 DevEco diff 让 configure 误判
pcre2 的 config.h 在交叉构建中始终不生成。根因相当隐蔽:PATH 里 DevEco toolchains 自带的 diff 对不存在的文件返回 0,autotools 的 config.status 因此误判模板 unchanged,跳过生成。解法落在 env.sh:强制把 /usr/bin:/bin 前置,保证构建期拿到的是系统 diff。工具链环境变量对一个 autotools 工程的影响,在这里体现得比任何编译选项都直接。
### 难点四:hvigor 闭包打包丢弃预编译库
首轮 HAP 里只有 2 个 so:hvigor 依据链接闭包打包,运行时 dlopen 的库不在闭包里。工程启用 entry/libs/arm64-v8a 整目录通道,把 107 个预编译库随 HAP 携带,部署脚本同时用 fix_needed.py 做版本号归一,清理 libvorbisenc.so.2.0.12 这类残留后缀,保证 dlopen 清单可以按裸名拓扑展开。
### 难点五:真机签名授权挡在新机前面
模拟器安装顺利,真机报 9568322:debug profile 需要 AGC 注册设备 UDID,且设备端需授权"USB 安装"。工程的处理是先把验证链落在模拟器(AirSky2in1)上跑通,真机授权作为交付前的人工步骤保留。PORTING.md 对此如实登记为待办,文章同样不做超出记载的宣称。
▍六、编译、安装与启动
Native 侧按 PORTING.md 的构建链执行,顺序如下:
bash
source native/scripts/env.sh
bash native/scripts/build_wxwidgets.sh # wxGTK(一次)
bash native/scripts/build_portaudio.sh # + OHOS 后端
bash native/scripts/build_audio_deps.sh # sndfile 链(CMake 系)
bash native/scripts/fix_audio_libs.sh # lame/mpg123 补链 + id3tag/wavpack/uuid
bash native/scripts/build_image_compiler.sh # 宿主工具(一次)
bash native/scripts/build_audacity.sh # 本体(内部先跑补丁脚本)
bash native/scripts/deploy_data.sh # rawfile 运行数据
bash native/scripts/deploy_libs.sh # 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。PORTING.md 记载当前 HAP 约 230MB,体积主要来自随包的编解码链与运行数据。安装与启动:
bash
hdc install -r entry/build/default/outputs/default/entry-default-signed.hap
hdc shell aa start -a EntryAbility -b com.airaudio.app
▍七、当前功能边界
工程文档与代码支持的已覆盖能力:
- 从源码到 HAP 的全链交叉编译可复现,libAudacity.so 与 107 个预编译库随包部署;
- Stage 模型壳层,XComponent(TEXTURE) 全窗口渲染 GTK 画面(entry/src/main/ets/pages/Index.ets);
- ArkUI 原生菜单栏:菜单树经 bridge.getMenubarJson() 从引擎侧导出,叶子项经 bridge.activateMenuAction() 触发,菜单项 enabled 状态按周期刷新;
- 启动时序 wxEntryStart → 注入 OHNativeWindow → CallOnInit → OnRun;
- PortAudio OHAudio 后端接入系统音频输出,解码链覆盖 ogg/vorbis/flac/opus/mp3/wavpack;
- 文件选择器桥与对话框子窗口桥已按 AirGimp P11 模式接线(utils/FileManager.ets、utils/SubWindowManager.ets);
- 日志双通道:文件优先 + 仅 ERROR 上 hilog,SIGSEGV 落盘并链回 faultlogger。
尚未覆盖或规划中的能力(均有文档依据):
- mod-*.so(import/export 插件、mod-script-pipe)已构建未随包,待 ModuleManager 运行时按需加载(M5 计划);
- MIDI 输入输出为 stub,OHOS 平台当前无 MIDI 设备枚举路径;
- picker 桥、对话框桥在 Audacity 侧的联动尚待真机验证;
- 真机安装待设备端授权或 AGC UDID 注册,当前验证平台为模拟器。
因此,当前版本更准确的定位是 Audacity 鸿蒙移植的可复现构建骨架:编译链、图形承载、音频后端与壳层桥接已经落位,交互级验收还停在模拟器与真机授权之间。
▍八、总结
AirAudio 的价值在于把"重依赖桌面应用"的移植路径走通了一遍:wxWidgets 不换框架压到 GTK3 OHOS 栈,PortAudio 不等系统音频服务自己写后端,本体不守可执行形态改成单库 + 静态并入。这条路径沉淀出的不只是 Audacity 的 HAP,还有 libtool 补链、NEEDED 归一、宿主工具分离等一批可复用的工程手法。
后续工作的重心在 PORTING.md 的待办清单里已经写明:mod 插件的运行时按需加载、菜单栏接管从桥接走向深度联动、picker 与对话框桥在 Audacity 侧完成真机验证,以及补齐真机签名授权后的完整交互验收。每一步的验收口径保持一致:可构建的 HAP,加上设备上可复现的音频编辑行为。
楼主
8 天前