Files
youle_app_ios_v2/Vendor/OpenCoreAMR
joywayer 5b38456c66 3.B:vendor opencore-amr + AMRCodec Swift wrapper,打通 AMR↔WAV 编解码
为支撑 H5 的 prepareaudio / mediaTypeAudio 录音上传 + 远端语音播放
(docs/H5-Native-Contract.md §3.1 [4][5] + §3.2 [9])接入 AMR-NB
编解码能力。AMR-NB 是跨端(iOS / Android / 旧版本客户端)契约硬约束,
不可换 AAC / Opus。

## 接入路径

直接 vendor daoqi msext 现成的 fat .a(已上线多年),不打 xcframework /
不重编源码:
- Vendor/OpenCoreAMR/v0.1.x/libopencore-amrnb.a:daoqi 同款 5-slice fat
  库(含 2026-06-20 lipo -segalign 8 修过的对齐版本)
- Vendor/OpenCoreAMR/v0.1.x/{interf_dec,interf_enc}.h:amrnb 公开头
- Vendor/OpenCoreAMR/README.md:来源、版本、修复历史、为何不打 xcframework
- ylgamehall/Source/Audio/AMRFileCodec/{amrFileCodec.h,amrFileCodec.mm}:
  daoqi 同款 wrapper(文件级 WAV↔AMR 处理)
- ylgamehall/Source/Audio/AMRCodec.swift:Swift 薄 wrapper(wavToAmr /
  amrToWav 两个纯函数 + AMRCodec.Error)

理由见 Vendor/OpenCoreAMR/README.md「为何不打 xcframework / 不重编源码」
一节,本质是 opencore-amr 整个 iOS 生态没有"现代主流"分发形态,社区无
官方 SPM / xcframework 维护,强行打反而引入新风险。

## 关键技术点:extern "C" 修复 ObjC++ name mangling

amrFileCodec.h 在原项目里没有 extern "C" 包裹函数声明,daoqi 能跑是
因为调用方 VoiceConverter.mm 也是 ObjC++,双方按相同 C++ name mangling
链接能对上。

新外壳从 Swift 通过 bridging-header 调用,Swift 按 C 函数解析,期望符号
_EncodeWAVEFileToAMRFile / _DecodeWAVEFileToWAVEFile(C 链接);但
.mm 编译时默认 C++ name mangling,符号变成 __Z25Encode...,导致链接器
报 Undefined symbol。

修复:在 amrFileCodec.h 的函数声明外加 #ifdef __cplusplus / extern "C"
包裹。这是标准 C/C++ 头文件兼容写法,对 daoqi 老路径无影响(ObjC++
调用方仍能链接 C-linkage 函数)。

## .gitignore:白名单 Vendor 下的 .a

发现历史遗留问题:原 .gitignore *.a 通配规则把 libWeChatSDK.a 也排除
了,意味着别人 clone 此仓库后链接失败。本次顺带修复:

  !Vendor/**/*.a

让 Vendor 下的 .a 显式纳入跟踪。本提交同时把 libWeChatSDK.a(历史
缺失)+ libopencore-amrnb.a(本次新增)一并加入 git 跟踪。

## 工程改动

pbxproj 改动由用户在 Xcode UI 完成(4 项 Build Settings:Add .a 到
Frameworks group / Header Search Paths +2 / Library Search Paths +1;
EXCLUDED_ARCHS arm64-simulator 因真机调试场景暂未配,需要时再加)。

## 验证

BuildProject 通过,0 错误,符号链接正确。XcodeRefreshCodeIssuesInFile
对 AMRCodec.swift 0 诊断。
2026-06-24 01:20:30 +08:00
..

OpenCoreAMR

opencore-amr 是 AMR-NB / AMR-WB 语音编解码的 C 库(OpenCORE 项目,Martin Storsjo 维护)。新外壳沿用 daoqi msext 时期的同一份二进制 + 公开头,不打 xcframework 不重新编译。理由见下文「ADR」一节。

用途

支撑 H5 的语音功能(prepareaudio 录音 → AMR 编码 → 七牛上传; mediaTypeAudio 下载 → AMR 解码 → 播放)。AMR-NB 格式是跨端(iOS / Android / 旧版本客户端)契约硬约束,不可换成 AAC / Opus。详见 docs/H5-Native-Contract.md §3.1 4][5 + §3.2 9]。

来源

目录

v0.1.x/
├── libopencore-amrnb.a    5 slice fati386 + x86_64 + armv7 + armv7s + arm64-device
├── interf_dec.h           AMR-NB 解码 C API
└── interf_enc.h           AMR-NB 编码 C API

不 vendor libopencore-amrwb.a——daoqi amrFileCodec.mm 实际只用 NB(窄带, 8kHz 单声道),WB 路径源码注释掉了,新外壳同样只用 NB。

fat header 对齐补丁

daoqi .a 在 2026-06-20 用 lipo -segalign 8 重新打包过,所有 5 个 slice 的 fat header align = 2^3 (8)。这是为了适配 Xcode 26+ 的新链接器 ld_prime 对 fat 库 architecture slice 对齐字段的硬性要求(≥ 2^3);不修则 会要 -ld_classic 过渡选项(已确认是定时炸弹)。

详细背景与修复命令见 youle_app_ios/CLAUDE.md 「2026-06-20opencore-amr 静 态库 fat header 重新对齐」一节。

已知遗留链接器警告(无害)

  • No platform load command found in ... assuming: iOS(每个 .o 一行,约 154 行)— 2014 年 .o 不带 LC_VERSION_MIN_IPHONEOS / LC_BUILD_VERSION load command;新链接器假定为 iOS,行为正确
  • Reducing alignment of section __DATA,__common from 0x8000 to 0x4000

要彻底消除这两类警告需要重编 opencore-amr 源码,违反「稳定 > 一切」原 则——接受现状

simulator-arm64 缺失

.a 是 2014 年打包,没有 arm64-simulator slice(这是 2020 年 Apple Silicon 上市后才有的概念)。M1+ Mac 上 simulator 编译需要在 Build Settings 设 置:

EXCLUDED_ARCHS[sdk=iphonesimulator*] = arm64

让 simulator 退回 x86_64 切片(通过 Rosetta 跑)。仅影响开发期 simulator release device 是 arm64 完整可用。

为何不打 xcframework / 不重编源码?

ADR:选 vendor 现成 .a 而非现代化 xcframework / 源码重编。

  • 依据opencore-amr 整个 iOS 生态都是"老 vendor 模式"——各家大厂(微 信、QQ、网易云信、声网 IM 等)都内部 vendor .a,没人公开维护 SPM / 官方 xcframework。社区无"现代主流"形态可参照
  • 稳定性daoqi 这份 .a 已上线 N 年稳定运行;现代化"形态"xcframework 本质上仍是 vendor 同一份二进制字节,形态收益边际,引入新打包/编译路径反而 增加风险
  • 跨编辑成本:要完整 xcframework 必须从源码编出 arm64-simulator slice,跨 编 iOSautoconf)容易踩坑,1+ 小时调试 vs vendor 几分钟落地
  • CLAUDE.md 原则一致:项目「稳定 > 一切」+「daoqi 是工作参照实现」精神 要求避免"为现代化而现代化"。本目录的现代化收益体现在组织规范 + 文档完 备(与 WechatSDK Vendor 并列、README 来源/版本/修复历史齐全),而不是打包 形态

未来若 Xcode 大版本彻底干掉对 fat .a 的支持(被动升级),再走 xcframework 化或源码重编路线,按 CLAUDE.md「Xcode 26.5+ 兼容性 Build Settings」一节的 patch 形式记录即可。

替换 / 升级流程(未来用)

  1. 从 SourceForge / GitHub mirror 拉新版本源码(如 0.1.6
  2. xcrun -sdk iphoneos clang + -arch arm64 跨编 device slice
  3. xcrun -sdk iphonesimulator clang + -arch arm64/-arch x86_64 跨编 simulator slice
  4. lipo -create -segalign 8 合并为新 fat .a
  5. 替换 v0.1.x/ 下文件,更新本 README、提交

链接配置

  • Build Phases → Link Binary With Libraries:加 libopencore-amrnb.a
  • Build Settings → Header Search Paths:追加 $(PROJECT_DIR)/Vendor/OpenCoreAMR/v0.1.x
  • Build Settings → Library Search Paths:追加 $(PROJECT_DIR)/Vendor/OpenCoreAMR/v0.1.x
  • Build Settings → Excluded Architecturesiphonesimulator):arm64

wrapper 源码(amrFileCodec.{h,mm} + AMRCodec.swift)放在 ylgamehall/Source/Audio/AMRFileCodec/ylgamehall/Source/Audio/ 由项目同步组(PBXFileSystemSynchronizedRootGroup)自动加入编译,无需在 pbxproj 中显式声明。