杰理 SDK 文档中心
首页
首页
  • SDK 框架库

    • JL_BLEKit 蓝牙通信核心
    • JL_AdvParse 广播包解析
    • JL_HashPair 加密配对
    • JL_OTALib 固件升级
    • JLDialUnit 彩屏仓与表盘控制
    • JLBmpConvertKit 位图转换
    • JLPackageResKit 资源包处理
    • JLLogHelper 日志工具
  • 核心功能模块

    • 音乐与媒体控制
    • 音效调节与均衡器
    • 设备发现、连接与设置
    • Auracast 广播接收与发射
    • 文件浏览、闹钟、FM 与灯光控制
    • ANC、按键设置与查找设备
    • AI 翻译与自定义命令
  • 应用架构与工程支撑

    • 杰理之家 App 架构与导航
    • 数据存储与缓存
    • Swift 工具与扩展层
    • JLAudioUnitKit 示例工程
    • SDKTestHelper 测试工具
  • 开发文档与资源

    • 文档中心与 JL_OTALib API 说明
    • 自定义蓝牙接入方式
    • 调试技巧与问题排查
    • 版本历史与社区支持

JLAudioUnitKit 示例工程

JLAudioUnitKit 是杰理科技(ZhuHai JieLi Technology)提供的 iOS 音频处理框架(以 XCFramework 二进制形式分发),本页介绍其配套示例工程 JLAudioUnitKitDemo 的结构、框架公共 API 面、播放器双模式工作机制与集成方式。

Purpose and Scope

本页覆盖以下内容:

  • JLAudioUnitKit 框架的二进制分发形态(XCFramework 双架构布局)与公共头文件所暴露的全部模块。
  • 示例工程 code/JLAudioUnitKitDemo 的工程结构、构建产物与 CocoaPods 集成方式。
  • 核心类 JLAudioUnitPlayer 的两种播放模式(文件播放 / PCM 流播放)、委托协议与完整 API。
  • 播放器生命周期、核心调用流程、典型使用代码、配置项与失败模式。

以下内容不在本页范围,请参见应用架构目录下的其他页面:

  • 杰理全功能示例 App JieLi_Home_Demo 的完整应用架构(本仓库另一示例工程)。
  • BLE 蓝牙协议栈与设备交互细节(属于连接与传输层能力)。

概述

JLAudioUnitKit 封装了 iOS 底层音频能力(AudioToolbox / AVAudioPlayer / Audio Queue),为上层业务提供统一的音频播放与格式转换入口。它解决的问题是:在杰理设备的音频场景中(例如从蓝牙设备接收 PCM 语音、播放本地提示音文件、语音编解码),开发者无需直接面对 AudioQueueRef、AudioStreamBasicDescription、编解码器细节,而是通过一个轻量 Objective-C 对象完成播放控制与数据喂入。

框架由 EzioChan 创建(JLAudioUnitKit.h),公开模块分为三类:

  1. 播放器:JLAudioUnitPlayer —— 支持文件播放(MP3/WAV/AAC)与 PCM 流式播放。
  2. 编解码器:JLSpeexDecoder、JLOpusDecoder、JLOpusEncoder 及格式/配置类型。
  3. 格式转换器:JLPcmToWtg、JLPcmToWav、JLOpusToOgg、JLAudioConverter。

示例工程 JLAudioUnitKitDemo 位于 code/JLAudioUnitKitDemo/Code/JLAudioUnitKitDemo/,通过 CocoaPods 接入框架,以 Release-iphoneos 配置构建,展示了如何在真实 App 中链接并使用该框架。

架构

flowchart TD
    subgraph sg_Demo["JLAudioUnitKitDemo 示例工程"]
        App["JLAudioUnitKitDemo App"]
        VC["ViewController(UI 层)"]
        Pods["CocoaPods 集成<br/>Pods-JLAudioUnitKitDemo"]
    end

    subgraph sg_Kit["JLAudioUnitKit(XCFramework)"]
        Player["JLAudioUnitPlayer"]
        Codec["编解码器<br/>Opus / Speex"]
        Convert["格式转换器<br/>PcmToWav / PcmToWtg / OpusToOgg / AudioConverter"]
    end

    subgraph sg_System["系统框架"]
        AVAudioPlayer["AVAudioPlayer(文件模式)"]
        AudioQueue["Audio Queue(PCM 模式)"]
    end

    App --> VC
    VC --> Pods
    Pods --> Player
    VC --> Codec
    VC --> Convert
    Player --> AVAudioPlayer
    Player --> AudioQueue

架构说明:

  • 上层:示例工程以 App 形式存在,ViewController 负责 UI 与用户交互;框架通过 CocoaPods 静态/动态链接进 App。
  • 中间层:JLAudioUnitKit 以 xcframework 二进制分发,公共 API 由 10 个头文件暴露(见 JLAudioUnitKit.h)。JLAudioUnitPlayer 是播放相关能力的唯一入口。
  • 底层:文件模式内部使用 AVAudioPlayer(对应枚举注释 JLAudioUnitPlayerTypeFile),PCM 流模式内部使用 Audio Queue(JLAudioUnitPlayerTypePCM),见 JLAudioUnitPlayer.h。

框架结构与公共模块

XCFramework 二进制布局

框架以 Apple XCFramework 格式分发,位于 code/JieLi_Home_Demo/JLAudioUnitKit.xcframework/,包含两个平台切片:

切片平台说明
ios-arm64/JLAudioUnitKit.framework真机(iOS arm64)真机调试/发布链接
ios-x86_64-simulator/JLAudioUnitKit.framework模拟器(x86_64)Intel Mac 模拟器链接

两个切片暴露相同的一组公共头文件,其中 JLAudioUnitKit.h 是伞头(umbrella header),集中导入全部公共模块:

#import <JLAudioUnitKit/JLSpeexDecoder.h>
#import <JLAudioUnitKit/JLOpusDecoder.h>
#import <JLAudioUnitKit/JLOpusEncoder.h>
#import <JLAudioUnitKit/JLOpusFormat.h>
#import <JLAudioUnitKit/JLOpusEncodeConfig.h>
#import <JLAudioUnitKit/JLPcmToWtg.h>
#import <JLAudioUnitKit/JLPcmToWav.h>
#import <JLAudioUnitKit/JLAudioUnitPlayer.h>
#import <JLAudioUnitKit/JLOpusToOgg.h>
#import <JLAudioUnitKit/JLAudioConverter.h>

Source: JLAudioUnitKit.h

公共模块一览

模块类/类型职责
播放器JLAudioUnitPlayer文件播放(MP3/WAV/AAC)与 PCM 流播放
解码器JLSpeexDecoderSpeex 语音解码
解码器JLOpusDecoderOpus 语音解码
编码器JLOpusEncoderOpus 语音编码
格式定义JLOpusFormatOpus 格式描述类型
编码配置JLOpusEncodeConfigOpus 编码参数配置
转换器JLPcmToWtgPCM → WTG(杰理私有音频格式)
转换器JLPcmToWavPCM → WAV
转换器JLOpusToOggOpus → Ogg 封装
转换器JLAudioConverter通用音频转换入口

设计意图:框架将「播放」与「编解码/转换」拆分为独立模块,业务侧可按需组合——例如先 JLOpusDecoder 解码蓝牙收到的 Opus 数据,再 appendPCMData: 喂给 JLAudioUnitPlayer 的 PCM 模式播放;或先用 JLPcmToWav 把 PCM 转成 WAV 文件再走文件播放。

版本信息

框架在伞头中导出版本常量(JLAudioUnitKitVersionNumber 与 JLAudioUnitKitVersionString),供运行时读取框架版本,便于排障与兼容性判断,见 JLAudioUnitKit.h。

示例工程结构

示例工程位于 code/JLAudioUnitKitDemo/Code/JLAudioUnitKitDemo/,仓库内可见其构建产物目录 build/JLAudioUnitKitDemo.build/Release-iphoneos/,从产物可确认以下集成事实:

  • 构建配置:以 Release-iphoneos 为目标配置,产物为 JLAudioUnitKitDemo.app(含 .xcent 签名授权文件,说明工程启用了代码签名)。
  • CocoaPods 集成:构建产物中出现 Pods-JLAudioUnitKitDemo-frameworks-Release-input-files-*.xcfilelist 与 checkManifestLockResult.txt,证明示例工程使用 CocoaPods 管理依赖(Pod 名为 JLAudioUnitKitDemo),框架通过 Pods 的 frameworks 脚本阶段链接进 App。
  • 头文件映射:*-all-target-headers.hmap、*-project-headers.hmap 等 hmap 文件表明 Xcode 使用头映射加速编译,JLAudioUnitKitDemo 是唯一的 target。

说明:本次文档生成受探索预算限制,未直接读取示例工程的 ViewController/AppDelegate 源码文件;上表结论来自构建产物文件名的可验证证据。示例工程的说明文档位于 code/JLAudioUnitKitDemo/Code/README.md。

JLAudioUnitPlayer 详解

JLAudioUnitPlayer 是整个框架中与「播放」相关的最核心类,定义于 JLAudioUnitPlayer.h。它通过两种初始化方式确定内部引擎:

双模式设计

typedef NS_ENUM(NSInteger, JLAudioUnitPlayerType) {
    JLAudioUnitPlayerTypeFile,   // 支持 MP3/WAV/AAC 等文件格式(AVAudioPlayer)
    JLAudioUnitPlayerTypePCM      // 支持 PCM 流式播放(Audio Queue)
};

Source: JLAudioUnitPlayer.h

  • 文件模式(initWithAudioFile:):传入本地文件路径,支持 MP3/WAV/AAC,内部由 AVAudioPlayer 驱动,天然支持进度查询、跳转(seekToTime:)与时长读取。
  • PCM 模式(initWithPCMFormat:):传入 AudioStreamBasicDescription 描述采样率、位深、声道数等格式,内部由 Audio Queue 驱动,播放数据通过 appendPCMData: 持续喂入,适合实时语音流(如蓝牙传输的 PCM 数据)。

设计意图:两种模式共用同一套播放控制接口(play/pause/stop)与委托回调,业务层无需关心底层引擎差异;而模式相关的差异能力(文件模式的 duration/seekToTime:,PCM 模式的 appendPCMData:/endPCMStream)通过注释明确标注适用模式,避免误用。

委托协议

@protocol JLAudioPlayerDelegate <NSObject>
@optional
// 播放进度更新(秒)
- (void)audioPlayer:(JLAudioUnitPlayer *)player didUpdateProgress:(NSTimeInterval)currentTime duration:(NSTimeInterval)duration;
// 播放完成
- (void)audioPlayerDidFinishPlaying:(JLAudioUnitPlayer *)player;
// 播放错误
- (void)audioPlayer:(JLAudioUnitPlayer *)player didFailWithError:(NSError *)error;
@end

Source: JLAudioUnitPlayer.h

三个回调全部为 @optional,且委托属性声明为 weak(@property (nonatomic, weak) id<JLAudioPlayerDelegate> delegate;),避免循环引用——这是 iOS 委托模式的经典约定:播放器持有委托的弱引用,生命周期由外部(通常是 ViewController)管理。

公开属性

属性类型说明
delegateid<JLAudioPlayerDelegate>(weak)播放回调委托
durationNSTimeInterval(readonly)总时长,仅文件模式有效
currentTimeNSTimeInterval(readonly)当前播放位置(秒)
isPlayingBOOL(readonly)是否正在播放

播放器状态机

stateDiagram-v2
    [*] --> Idle
    Idle --> Playing: play()
    Playing --> Paused: pause()
    Paused --> Playing: play()
    Playing --> Stopped: stop()
    Paused --> Stopped: stop()
    Stopped --> Playing: play()
    Playing --> Finished: 播放完成 / endPCMStream()
    Finished --> [*]
  • Idle:初始化完成、尚未播放;stop() 后回到可重新播放的 Stopped 态。
  • Playing/Paused:由 play/pause 切换,PCM 模式下暂停时 Audio Queue 暂停消费数据。
  • Finished:文件模式播完或 PCM 模式调用 endPCMStream 结束流后,触发 audioPlayerDidFinishPlaying: 回调。

核心流程

文件播放流程

sequenceDiagram
    participant VC as ViewController
    participant P as JLAudioUnitPlayer
    participant AP as AVAudioPlayer

    VC->>P: initWithAudioFile: (MP3/WAV/AAC 路径)
    VC->>P: play()
    P->>AP: 创建并启动 AVAudioPlayer
    loop 播放中
        P-->>VC: audioPlayer:didUpdateProgress:duration:
    end
    AP-->>P: 播放结束
    P-->>VC: audioPlayerDidFinishPlaying:

PCM 流播放流程

sequenceDiagram
    participant VC as ViewController
    participant P as JLAudioUnitPlayer
    participant AQ as Audio Queue
    participant SRC as 数据源(蓝牙/编解码器)

    VC->>P: initWithPCMFormat: (AudioStreamBasicDescription)
    VC->>P: play()
    P->>AQ: 启动 Audio Queue 播放
    loop 持续接收 PCM 数据
        SRC->>VC: PCM 数据包
        VC->>P: appendPCMData:
        P->>AQ: 填充播放缓冲区
    end
    VC->>P: endPCMStream()
    P-->>VC: audioPlayerDidFinishPlaying:

关键点:PCM 模式是「推」模型——播放器本身不产生数据,业务侧(如蓝牙接收回调、JLOpusDecoder 解码回调)必须持续调用 appendPCMData: 喂数;数据不足会出现卡顿/停顿,数据停止后必须调用 endPCMStream 让播放器知道流已结束,否则不会触发完成回调。

使用示例

以下示例均基于框架公共头文件中的真实声明整理,展示典型调用方式。

文件模式播放本地提示音

// 1. 初始化:传入本地音频文件路径(MP3/WAV/AAC)
JLAudioUnitPlayer *player = [[JLAudioUnitPlayer alloc] initWithAudioFile:audioFilePath];

// 2. 设置委托以接收进度/完成/错误回调
player.delegate = self;

// 3. 播放控制
[player play];      // 开始播放
[player pause];     // 暂停
[player seekToTime:30.0]; // 跳转到 30 秒(仅文件模式)
[player stop];      // 停止

Source: JLAudioUnitPlayer.h

PCM 流模式播放实时语音

// 1. 声明 PCM 格式(采样率/位深/声道)
AudioStreamBasicDescription format = {0};
format.mSampleRate = 16000;          // 16kHz 采样率(示例值)
format.mFormatID = kAudioFormatLinearPCM;
format.mChannelsPerFrame = 1;        // 单声道
// ... 按实际音频源填充 mBitsPerChannel / mBytesPerFrame 等字段

// 2. 初始化 PCM 播放器
JLAudioUnitPlayer *player = [[JLAudioUnitPlayer alloc] initWithPCMFormat:format];
player.delegate = self;
[player play];

// 3. 数据到达时持续喂数(例如蓝牙回调 / Opus 解码输出)
[player appendPCMData:pcmData];

// 4. 数据流结束时通知播放器
[player endPCMStream];

Source: JLAudioUnitPlayer.h

实现委托回调

// 进度更新(秒)——可用于更新 UI 进度条
- (void)audioPlayer:(JLAudioUnitPlayer *)player didUpdateProgress:(NSTimeInterval)currentTime duration:(NSTimeInterval)duration {
    // duration 仅文件模式有效
    self.progressView.progress = (duration > 0) ? currentTime / duration : 0;
}

// 播放完成——清理资源、切换到下一首
- (void)audioPlayerDidFinishPlaying:(JLAudioUnitPlayer *)player {
    [self nextTrack];
}

// 播放错误——提示用户并恢复状态
- (void)audioPlayer:(JLAudioUnitPlayer *)player didFailWithError:(NSError *)error {
    NSLog(@"Audio play failed: %@", error.localizedDescription);
}

Source: JLAudioUnitPlayer.h

配置选项

配置项类型默认值说明
delegateid<JLAudioPlayerDelegate>(weak)nil播放回调委托,未设置则无进度/完成/错误通知
PCM 格式AudioStreamBasicDescription调用方指定initWithPCMFormat: 的入参,决定 Audio Queue 的采样率、位深、声道数;需与实际数据严格一致
音频文件路径NSString调用方指定initWithAudioFile: 的入参,需指向本地存在的 MP3/WAV/AAC 文件
seekToTime:NSTimeInterval—跳转目标时间点,仅文件模式有效

配置方式说明:JLAudioUnitPlayer 采用「初始化即定模式」的设计——通过两个不同初始化方法决定播放引擎,之后不可切换;业务侧需要切换文件/流播放时必须重新创建实例。这与 CocoaPods 集成时的 Pod 配置(Pods-JLAudioUnitKitDemo)相互独立,框架本身没有全局配置文件。

API 参考

- (instancetype)initWithAudioFile:(NSString *)filePath

创建文件模式播放器(MP3/WAV/AAC)。

参数:

  • filePath(NSString):本地音频文件路径。

返回: 初始化后的 JLAudioUnitPlayer 实例。

- (instancetype)initWithPCMFormat:(AudioStreamBasicDescription)format

创建 PCM 流模式播放器,内部使用 Audio Queue。

参数:

  • format(AudioStreamBasicDescription):PCM 数据的格式描述(采样率、位深、声道等)。

返回: 初始化后的 JLAudioUnitPlayer 实例。

- (void)play

开始或恢复播放(两种模式通用)。从 Paused/Stopped 状态可再次调用。

- (void)pause

暂停播放,保留播放位置;PCM 模式下暂停 Audio Queue 的数据消费。

- (void)stop

停止播放并释放内部播放资源;文件模式下次 play 从头开始。

- (void)seekToTime:(NSTimeInterval)time

跳转到指定时间点。仅文件模式有效;PCM 模式下调用无意义。

参数:

  • time(NSTimeInterval):目标时间(秒)。

- (void)appendPCMData:(NSData *)pcmData

向 PCM 播放器喂入数据。仅 PCM 模式有效,需在 play 之后持续调用。

参数:

  • pcmData(NSData):PCM 音频数据块,格式须与初始化时声明的 AudioStreamBasicDescription 一致。

- (void)endPCMStream

结束 PCM 流输入,通知播放器数据已全部送达;之后播放器消费完缓冲数据即触发 audioPlayerDidFinishPlaying:。

委托方法(JLAudioPlayerDelegate,全部 @optional)

方法触发时机参数说明
audioPlayer:didUpdateProgress:duration:播放进度周期性更新currentTime 当前秒数;duration 总时长(仅文件模式有意义)
audioPlayerDidFinishPlaying:播放完成(文件播完 / PCM 流结束)player 播放器实例
audioPlayer:didFailWithError:播放过程出错error 具体错误信息

失败模式、边界情况与并发

播放错误

  • 文件不存在/格式不支持:initWithAudioFile: 传入无效路径或非 MP3/WAV/AAC 格式时,播放启动失败,通过 audioPlayer:didFailWithError: 通知调用方。业务侧应在回调中提示用户并复位 UI 状态。
  • PCM 格式不匹配:initWithPCMFormat: 声明的 AudioStreamBasicDescription 与实际喂入数据不一致(如采样率不同)会导致播放噪声或 Audio Queue 报错。这是 PCM 模式最常见的坑,必须在数据源侧保证格式一致。

模式误用边界

框架通过注释明确区分两种模式的专属能力,但 Objective-C 无编译期强制,误用只会产生无效行为而非崩溃:

  • PCM 模式下调用 seekToTime:、读取 duration 无意义(duration 仅文件模式有效)。
  • 文件模式下调用 appendPCMData: / endPCMStream 无效。

并发与线程

  • 委托属性为 weak,若持有播放器的控制器在播放中释放,委托回调不会再触发——业务侧需在 dealloc 或 viewWillDisappear 中 stop 并置空 delegate。
  • PCM 模式是典型的「生产-消费」模型:数据生产(蓝牙回调/解码线程)与音频消费(Audio Queue 实时线程)速率不同步。喂数过快会堆积缓冲(内存与延迟上升),过慢则导致卡顿/断音。框架未在头文件中暴露缓冲水位查询 API,业务侧需自行做流量控制(如按包间隔喂数)。
  • 回调线程:头文件未声明委托回调所在线程,didUpdateProgress: 可能来自音频线程。若需更新 UI,建议在回调内 dispatch_async 到主队列,避免 UIKit 跨线程访问。

生命周期

  • stop 后文件模式重新 play 从头播放;PCM 模式 endPCMStream 之后播放器进入完成态,如需再次播放应重新创建实例(或复用前先 stop)。

性能与运维

  • 底层引擎选择:文件模式使用 AVAudioPlayer(系统级、省电、支持硬件解码 AAC),PCM 模式使用 Audio Queue(低延迟、适合实时流)。对 CPU 敏感的语音场景(如对讲)优先走 PCM 模式并配合 JLOpusDecoder/JLSpeexDecoder 解码,可显著降低蓝牙传输带宽。
  • 缓冲管理:PCM 模式下持续 appendPCMData: 而不消费,会造成内存线性增长;结束会话必须调用 endPCMStream,否则播放器无法触发完成回调且资源不释放。
  • 版本管理:通过 JLAudioUnitKitVersionNumber/JLAudioUnitKitVersionString 可在运行时校验框架版本,升级 XCFramework 后建议回归验证两种播放模式。
  • 构建分发:示例工程以 Release-iphoneos 构建并含 .xcent 签名授权,说明接入方需自行配置签名;xcframework 已包含 arm64 与 x86_64-simulator 双切片,真机与模拟器可直接链接。

扩展点

  • 自定义数据源:PCM 模式天然支持任意音频数据源——只要把数据以 NSData 形式 appendPCMData: 喂入即可,可对接蓝牙串口、UDP 音频、解码器输出等。
  • 编解码组合:JLOpusDecoder、JLSpeexDecoder 的解码输出(PCM)可直接接入 JLAudioUnitPlayer 的 PCM 模式,形成「蓝牙 → 解码 → 播放」完整链路;JLPcmToWav 等转换器可将 PCM 落盘为 WAV,再走文件模式播放。
  • UI 集成:通过 didUpdateProgress:duration: 回调实现进度条/时间显示;通过完成/错误回调实现播放列表自动切换。

测试

本次文档生成受探索预算限制,未在仓库中发现针对 JLAudioUnitKit 或示例工程的可读测试源码(JLAudioUnitKitDemo 目录下可见的仅为构建产物)。框架以二进制形式分发,测试覆盖情况需联系杰理科技获取。建议接入方自行补充冒烟用例:分别用文件模式播放 MP3/WAV/AAC 各一个样本,PCM 模式播放已知采样率数据并验证进度回调与完成回调。

相关链接

  • JLAudioUnitPlayer.h(框架播放器公共头文件)
  • JLAudioUnitKit.h(框架伞头文件)
  • 示例工程 README
  • 示例工程构建目录(Release-iphoneos)
  • 应用架构目录下的其他页面:JieLi_Home_Demo 全功能示例 App 架构(同仓库 code/JieLi_Home_Demo)
Prev
Swift 工具与扩展层
Next
SDKTestHelper 测试工具