均衡器与音效调节
均衡器(EQ)与音效调节是 Flutter-JL_Home SDK 中用于控制杰理(Jieli)蓝牙音频设备音质表现的完整能力,涵盖 EQ 模式切换、自定义频段增益、混响(Reverberation)与限幅器(Dynamic Limiter)等高级音效的读取、设置与事件订阅。
Purpose and Scope
本页面向使用 jl_home Flutter SDK 的开发者,完整说明均衡器与音效调节这一能力域的端到端实现:
- Flutter 侧指令入口
BleEqManager(EQ 数据读取、模式设置、高级音效设置、频段增益写入); - 事件侧处理器
BleEqProcessor(混响/限幅器数据流订阅与解析); - 数据模型
EQModel与常量定义(BleMethodConstants、BleEventConstants); - 原生平台层(Android
EQManager.kt/SoundCardEqManager.kt、iOSJL_SystemEQ/JLModel_EQ)的实现位置与作用。
以下内容属于兄弟页面,不在本页展开:音频模式(ble_audio_mode_manager.dart)负责听音模式/场景切换;音乐播放与媒体控制由 ble_music_handler.dart 与 ble_device_music_manager.dart 负责;灯效、闹钟、FM 等能力请参见各自目录页。
Overview
均衡器是音频设备(耳机、音箱)最常用的音质调节手段。SDK 通过 Flutter → 原生(MethodChannel)→ BLE 固件 的命令链路下发 EQ 指令,并通过 BLE 固件 → 原生 → Flutter 事件流 的异步链路接收设备主动上报的音效状态(如混响、限幅器数据)。
整个能力域围绕两条正交的通道组织:
- 指令通道(下行):应用调用
BleEqManager的静态方法,经BleBaseManager.invokeMethod桥接到原生 SDK,再由原生层以 BLE 协议写入设备固件。典型操作包括获取 EQ 数据、设置 EQ 模式、重置 EQ、读取/设置高级音效(混响深度、强度、限幅器)、写入自定义频段增益。 - 事件通道(上行):设备状态变化时原生层解析 BLE 数据并通过事件流派发,Flutter 侧
BleEqProcessor按事件类型过滤并规整为统一的Map<String, dynamic>结构,应用可订阅reverberationAllDataStream实时刷新 UI。
设计上,SDK 将"协议细节"完全隔离在原生层:Flutter 侧只暴露语义化方法名(如 setEQMode、setAdvancedEQ),方法名与参数名均收敛在 BleMethodConstants 常量类中,避免魔法字符串散落各处;事件字段名(keyCapability、keyDepth 等)收敛在 BleEventConstants 中。这种"常量即协议契约"的做法降低了双端联调的出错率。
Architecture
flowchart TD
subgraph sg_App["应用层"]
UI["App UI / 业务代码"]
end
subgraph sg_Flutter["Flutter SDK 层 (jl_home)"]
Mgr["BleEqManager"]
Proc["BleEqProcessor"]
Model["EQModel"]
MC["BleMethodConstants"]
EC["BleEventConstants"]
Base["BleBaseManager.invokeMethod"]
Stream["ble_event_stream"]
end
subgraph sg_Native["原生平台层"]
Android["Android EQManager.kt / SoundCardEqManager.kt / EqInfo / EqPresetInfo"]
iOS["iOS JL_SystemEQ / JLModel_EQ"]
end
subgraph sg_Device["蓝牙设备"]
Dev["耳机/音箱固件"]
end
UI -->|"指令调用"| Mgr
Mgr -->|"fromMap 转换"| Model
Mgr -->|"引用方法名/参数名"| MC
Mgr -->|"MethodChannel"| Base
Base --> Android
Base --> iOS
Android -->|"BLE 协议写入"| Dev
iOS -->|"BLE 协议写入"| Dev
Dev -->|"音效数据上报"| Android
Dev -->|"音效数据上报"| iOS
Android -->|"事件派发"| Stream
iOS -->|"事件派发"| Stream
Stream --> Proc
Proc -->|"过滤 typeReverberation"| EC
Proc -->|"规整后的音效数据"| UI
架构说明:
- BleEqManager:指令通道的唯一门面(facade)。所有 EQ 相关写操作(模式、增益、高级音效)和读操作(获取 EQ 数据)都从这里发起,方法均为
static,无需实例化,适合在无状态的 SDK 场景中直接调用。 - BleBaseManager.invokeMethod:Flutter 与原生之间的统一桥。
BleEqManager的每个方法最终都转换为invokeMethod(methodName, arguments: {...})调用,参数名使用BleMethodConstants.argXxx常量,与原生端注册的 MethodChannel handler 一一对应。 - EQModel:
getEQData()的返回值载体,提供EQModel.fromMap(...)工厂方法,把原生返回的Map转换为强类型模型,避免上层直接操作裸 Map。 - BleEqProcessor:事件通道的规整器。它基于
BleBaseEventProcessor.filterByType订阅特定类型事件,将原始事件负载转换为带默认值、范围明确的Map,并给出能力位(keyCapability)语义注释。 - 原生层:Android 侧由
EQManager.kt与声卡场景的SoundCardEqManager.kt分工,配套EqInfo.java/EqPresetInfo.java数据模型与EqCacheUtil/EqCovertUtil工具;iOS 侧由JL_SystemEQ.h与JLModel_EQ.h提供协议。二者均基于jl_eq原生库(Android 侧以jl_eq_V1.1.0_10101-release.aar形式内置)。
主要实现内容
BleEqManager — 均衡器指令门面
BleEqManager 定义于 ble_eq_manager.dart,是 EQ 能力域的指令入口。全部方法为 static,通过 BleBaseManager.invokeMethod 与原生层通信。其完整源码如下:
import '../ble_base_manager.dart';
import '../constant/ble_method_constants.dart';
import '../model/eq_model.dart';
/// EQ Manager
class BleEqManager {
/// Get EQ data
static Future<EQModel?> getEQData() async {
try {
final result = await BleBaseManager.invokeMethod(
BleMethodConstants.methodGetEQData,
);
if (result != null) {
return EQModel.fromMap(Map<String, dynamic>.from(result));
}
return null;
} on Exception {
return null;
}
}
/// Set EQ mode
static Future<void> setEQMode(int eqIndex) async {
await BleBaseManager.invokeMethod(
BleMethodConstants.methodSetEQMode,
arguments: {BleMethodConstants.argEqIndex: eqIndex},
);
}
/// Reset EQ mode
static Future<void> resetEQMode() async {
await BleBaseManager.invokeMethod(BleMethodConstants.methodResetEQMode);
}
/// Get advanced data
static Future<void> getAdvancedEQ() async {
await BleBaseManager.invokeMethod(BleMethodConstants.methodAdvancedEQ);
}
/// Set advanced data
static Future<void> setAdvancedEQ(int depth, int intensity,
int dynamicLimiter, bool switchState) async {
await BleBaseManager.invokeMethod(
BleMethodConstants.methodAdvancedSetEQ,
arguments: {
BleMethodConstants.argDepth: depth,
BleMethodConstants.argIntensity: intensity,
BleMethodConstants.argDynamicLimiter: dynamicLimiter,
BleMethodConstants.argSwitchState: switchState,
});
}
/// Set EQ data
static Future<void> setEQValues(List<double> eqData) async {
await BleBaseManager.invokeMethod(
BleMethodConstants.methodSetEqValues,
arguments: {BleMethodConstants.argEqValues: eqData},
);
}
}
各方法的设计意图:
getEQData()是唯一带返回值的方法,且内部捕获所有Exception并返回null。这种"吞异常"策略适合 UI 首次加载场景:EQ 数据读取失败不应阻断页面,调用方通过null判断即可走默认 UI。代价是错误原因被隐藏,若需排查需依赖原生侧日志。setEQMode(int eqIndex)通过argEqIndex指定设备 EQ 模式(如流行、摇滚、古典等预设索引),索引的具体含义由固件/原生层定义。resetEQMode()无参数,将 EQ 恢复为设备默认模式,与setEQMode成对使用,常用于"恢复出厂听感"。getAdvancedEQ()为"拉取型"读取:调用后设备当前的高级音效参数(混响/限幅器)会经事件通道异步返回,因此它本身返回Future<void>,结果需通过BleEqProcessor.reverberationAllDataStream订阅获取。setAdvancedEQ(depth, intensity, dynamicLimiter, switchState)一次性写入三个高级音效参数与总开关,参数以Map形式传递,键名取自BleMethodConstants。setEQValues(List<double> eqData)写入自定义频段增益数组(各频段 dB 值),用于用户手动拖拽 EQ 曲线。
BleEqProcessor — 混响与限幅器事件处理
BleEqProcessor 定义于 ble_eq_processor.dart,负责把设备上报的"混响/限幅器"原始事件转换为应用可直接消费的数据:
import '../constant/ble_event_constants.dart';
import 'ble_base_event_processor.dart';
/// Eq processor
class BleEqProcessor {
static Stream<Map<String, dynamic>> get reverberationAllDataStream {
return BleBaseEventProcessor.filterByType(BleEventConstants.typeReverberation)
.map((event) {
final data = BleBaseEventProcessor.getValueFromEvent(event);
return {
BleEventConstants.keyCapability: data[BleEventConstants.keyCapability] as int? ?? 3, // 0:同时支持混响和限幅器 1:只支持混响 2:只支持限幅器 3:都不支持
BleEventConstants.keySwitchState: data[BleEventConstants.keySwitchState] as bool? ?? false,
BleEventConstants.keyDepth: (data[BleEventConstants.keyDepth] as num?)?.toDouble() ?? 0.0, // 深度的范围是0到100
BleEventConstants.keyIntensity: (data[BleEventConstants.keyIntensity] as num?)?.toDouble() ?? 0.0, // 强度的范围是0到100
BleEventConstants.keyDynamicLimiter: (data[BleEventConstants.keyDynamicLimiter] as num?)?.toDouble() ?? 0.0, // 限幅器的范围是-60到0
};
});
}
}
实现要点:
- 通过
BleBaseEventProcessor.filterByType(BleEventConstants.typeReverberation)先按事件类型过滤,只处理混响/限幅器相关事件;ble_event_stream.dart已引入本处理器,确保事件从原生到达后能被正确路由。 .map()阶段对每个字段做类型收窄 + 默认值兜底:as int? ?? 3、as bool? ?? false、(as num?)?.toDouble() ?? 0.0。设备可能漏报某些字段,兜底默认值保证 UI 永不因空值崩溃。keyCapability是能力位,标注设备对高级音效的支持程度:0同时支持混响与限幅器,1仅混响,2仅限幅器,3均不支持。应用应根据该值决定是否展示对应调节控件。keyDepth(0~100)与keyIntensity(0~100)为混响参数;keyDynamicLimiter(-60~0,单位 dB)为限幅器阈值。
EQModel 数据模型
getEQData() 的返回值类型为 EQModel?,定义于 eq_model.dart。它提供 EQModel.fromMap(Map<String, dynamic>) 工厂构造器,将原生返回的 EQ 数据 Map 转换为强类型对象(具体字段由原生返回内容决定,主要包括 EQ 模式索引与各频段增益等)。在 ble_eq_manager.dart 中,结果先经 Map<String, dynamic>.from(result) 显式转换,再交给工厂方法,避免运行时类型错误。
常量契约
指令通道的方法名与参数名统一收拢在 ble_method_constants.dart,事件通道的类型名与字段名收拢在 ble_event_constants.dart。从 BleEqManager/BleEqProcessor 源码中可确认以下契约标识符:
| 类别 | 标识符(常量名) | 用途 |
|---|---|---|
| 方法 | methodGetEQData | 获取 EQ 数据 |
| 方法 | methodSetEQMode | 设置 EQ 模式 |
| 方法 | methodResetEQMode | 重置 EQ 模式 |
| 方法 | methodAdvancedEQ | 拉取高级音效数据 |
| 方法 | methodAdvancedSetEQ | 设置高级音效参数 |
| 方法 | methodSetEqValues | 设置自定义频段增益 |
| 参数 | argEqIndex | EQ 模式索引 |
| 参数 | argDepth / argIntensity / argDynamicLimiter / argSwitchState | 高级音效四项参数 |
| 参数 | argEqValues | 频段增益数组 |
| 事件类型 | typeReverberation | 混响/限幅器事件 |
| 事件字段 | keyCapability / keySwitchState / keyDepth / keyIntensity / keyDynamicLimiter | 能力位、开关、深度、强度、限幅器 |
常量与原生端注册的 MethodChannel handler 一一对应,修改任何一侧时另一侧必须同步,这是双端协议约束的显式化。
Core Flow
指令通道:获取 EQ 数据(下行 + 上行联动)
sequenceDiagram
participant App as 应用/UI
participant Mgr as BleEqManager
participant Base as BleBaseManager
participant Native as 原生 EQManager / JL_SystemEQ
participant Dev as BLE 设备
App->>Mgr: getEQData()
activate Mgr
Mgr->>Base: invokeMethod(methodGetEQData)
Base->>Native: MethodChannel 调用
Native->>Dev: BLE 协议读取 EQ 信息
Dev-->>Native: EQ 数据
Native-->>Base: Map 结果
Base-->>Mgr: result
Mgr->>Mgr: EQModel.fromMap(Map.from(result))
Mgr-->>App: EQModel? (异常返回 null)
deactivate Mgr
App->>Mgr: getAdvancedEQ()
Mgr->>Base: invokeMethod(methodAdvancedEQ)
Base->>Native: MethodChannel 调用
Native->>Dev: BLE 协议拉取高级音效
Dev-->>Native: 混响/限幅器数据
Native-->>Stream: 事件派发 typeReverberation
Stream-->>Proc: BleEqProcessor 过滤 + 规整
Proc-->>App: reverberationAllDataStream 数据
指令通道:设置类操作
setEQMode、resetEQMode、setAdvancedEQ、setEQValues 均遵循同一模式:参数打包 → invokeMethod → 原生写入固件 → 设备本地生效。其中 setAdvancedEQ 携带四个参数(深度、强度、限幅器、开关),一次性完成高级音效的完整配置,减少多次 BLE 写操作带来的延迟与功耗。
事件通道:混响数据流
flowchart LR
Dev["BLE 设备上报"] --> Native["原生解析"]
Native -->|"事件分发"| Stream["ble_event_stream"]
Stream --> Filter{"filterByType<br/>typeReverberation?"}
Filter -->|"是"| Map["map 规整: 类型收窄 + 默认值"]
Filter -->|"否"| Ignore["丢弃/其他处理器"]
Map --> Cap{"keyCapability 判断"}
Cap -->|"0/1"| Reverb["展示混响控件<br/>depth 0~100, intensity 0~100"]
Cap -->|"0/2"| Limiter["展示限幅器控件<br/>dynamicLimiter -60~0"]
Cap -->|"3"| Hide["隐藏高级音效区"]
应用订阅 BleEqProcessor.reverberationAllDataStream 后,每次设备上报或 getAdvancedEQ() 拉取完成,都会收到一份完整规整后的 Map,可直接驱动 setState 刷新滑块与开关。
原生层实现概览
Flutter 侧只是协议外壳,真正的 BLE 通信由原生 SDK 完成。EQ 相关原生代码集中存放于 Android 工程:
Android 侧
| 文件 | 职责 |
|---|---|
| EQManager.kt | 通用设备(耳机/音箱)的 EQ 管理器,实现 MethodChannel 中 EQ 方法的分发、BLE 指令构造与回调 |
| SoundCardEqManager.kt | 声卡类设备的 EQ 管理器,针对声卡协议做独立实现 |
| EqInfo.java | EQ 信息模型(频段数、增益值等),对应 Flutter 侧 EQModel 的数据来源 |
| EqPresetInfo.java | EQ 预设(模式)信息模型,对应 setEQMode 的索引含义 |
| EqCacheUtil.java | EQ 数据缓存工具,减少重复读取设备的次数 |
| EqCovertUtil.java | EQ 数值转换工具(字节序、增益值编码等协议转换) |
| jl_eq_V1.1.0_10101-release.aar | 杰理官方 EQ 原生库(1.1.0 版本),封装底层 BLE 协议 |
EQManager 与 SoundCardEqManager 的分工体现了策略模式:不同设备类型(标准蓝牙设备 vs 声卡)的 EQ 协议细节不同,但对外暴露的 Flutter 方法名一致,上层无需感知差异。EqCacheUtil 用于缓存已读取的 EQ 数据,避免每次 UI 进入都重新走 BLE 读取;EqCovertUtil 负责 Flutter 传入的浮点增益与 BLE 字节协议之间的编解码。
iOS 侧
| 文件 | 职责 |
|---|---|
| JL_SystemEQ.h | iOS 侧系统 EQ 操作接口(JL_BLEKit 框架) |
| JLModel_EQ.h | iOS 侧 EQ 数据模型定义 |
iOS 侧通过 JL_BLEKit.framework 提供与 Android 对等的 EQ 能力,JLModel_EQ 承载 EQ 参数,JL_SystemEQ 提供设置/查询接口。Flutter 方法层(BleEqManager)与平台无关,因此两端共用同一套 Dart 代码。
端到端数据流小结
一次完整的"用户调节高级音效"操作的数据流向为:
- 用户在 UI 拖动混响深度滑块 → 调用
BleEqManager.setAdvancedEQ(depth, intensity, limiter, switch); BleEqManager将参数按BleMethodConstants键名打包为Map,经BleBaseManager.invokeMethod桥接;- 原生侧(Android
EQManager.kt/ iOSJL_SystemEQ)将参数编码为 BLE 协议指令写入设备; - 设备固件应用新参数并(视固件行为)上报最新状态;
- 原生解析上报数据 → 事件流 →
BleEqProcessor规整 → 应用订阅的reverberationAllDataStream发出新值 → UI 同步。
Usage Examples
示例一:读取 EQ 数据并处理空值
// 获取设备当前 EQ 数据;失败时返回 null,页面可走默认 UI
final EQModel? eq = await BleEqManager.getEQData();
if (eq != null) {
// 使用 eq 渲染 EQ 模式与频段增益
} else {
// 读取失败:展示默认 EQ 或引导重试
}
示例二:切换 EQ 模式与重置
// 切换到索引为 2 的预设模式(如"摇滚")
await BleEqManager.setEQMode(2);
// 恢复默认模式
await BleEqManager.resetEQMode();
示例三:写入自定义频段增益(手动 EQ 曲线)
// 以 double 列表表示各频段增益(dB),长度与设备频段数一致
await BleEqManager.setEQValues([0.0, 2.5, -1.0, 3.0, -2.0, 1.5]);
示例四:配置高级音效(混响 + 限幅器)
// depth: 混响深度(0~100) intensity: 混响强度(0~100)
// dynamicLimiter: 限幅器阈值(-60~0 dB) switchState: 高级音效总开关
await BleEqManager.setAdvancedEQ(60, 40, -12, true);
示例五:订阅混响/限幅器数据流
// 先拉取一次最新高级音效数据(结果异步经事件流返回)
await BleEqManager.getAdvancedEQ();
// 订阅数据流,实时刷新 UI
BleEqProcessor.reverberationAllDataStream.listen((data) {
final capability = data[BleEventConstants.keyCapability] as int; // 0/1/2/3
final switchOn = data[BleEventConstants.keySwitchState] as bool;
final depth = data[BleEventConstants.keyDepth] as double;
final intensity = data[BleEventConstants.keyIntensity] as double;
final limiter = data[BleEventConstants.keyDynamicLimiter] as double;
// 根据 capability 决定展示混响/限幅器控件,并更新滑块位置
});
API Reference
BleEqManager
static Future<EQModel?> getEQData()
获取设备当前 EQ 数据。
- 返回:
Future<EQModel?>— 成功返回EQModel;原生返回null或发生任何Exception时返回null。 - 抛出:不向外抛异常(内部
on Exception捕获)。 - 说明:读操作,底层经
methodGetEQData方法通道调用。调用方应基于null判断处理失败场景。
static Future<void> setEQMode(int eqIndex)
设置设备 EQ 模式(预设索引)。
- 参数:
eqIndex(int)— EQ 模式索引,具体模式含义由原生层/固件定义。 - 返回:
Future<void>— 调用完成即返回,实际生效结果取决于设备。 - 抛出:无显式捕获,桥接异常将向上传播。
static Future<void> resetEQMode()
将 EQ 重置为设备默认模式。无参数。
static Future<void> getAdvancedEQ()
拉取设备当前高级音效(混响/限幅器)数据。结果为异步事件,需订阅 BleEqProcessor.reverberationAllDataStream 接收。
static Future<void> setAdvancedEQ(int depth, int intensity, int dynamicLimiter, bool switchState)
一次性设置高级音效四项参数。
- 参数:
depth(int)— 混响深度,范围 0~100;intensity(int)— 混响强度,范围 0~100;dynamicLimiter(int)— 限幅器阈值,范围 -60~0(dB);switchState(bool)— 高级音效总开关。
- 返回:
Future<void>。 - 说明:参数范围与
BleEqProcessor事件字段注释一致,保证"写入范围 = 上报范围"。
static Future<void> setEQValues(List<double> eqData)
写入自定义频段增益。
- 参数:
eqData(List<double>)— 各频段增益值(dB),长度需与设备频段数一致。 - 返回:
Future<void>。
BleEqProcessor
static Stream<Map<String, dynamic>> get reverberationAllDataStream
混响/限幅器数据的实时事件流。
- 返回:
Stream<Map<String, dynamic>>,每个元素包含以下键(键名来自BleEventConstants):keyCapability(int)— 能力位,0/1/2/3;keySwitchState(bool)— 高级音效开关;keyDepth(double)— 混响深度 0~100;keyIntensity(double)— 混响强度 0~100;keyDynamicLimiter(double)— 限幅器 -60~0。
- 说明:所有字段均带默认值兜底,事件缺字段时不会抛异常。
Configuration Options
本能力域无运行时配置文件;行为由事件数据中的能力位与设备固件共同决定。事件流中可用的"配置"语义如下:
| 字段 | 类型 | 默认值 | 说明 |
|---|---|---|---|
keyCapability | int | 3 | 高级音效能力位:0=同时支持混响与限幅器;1=仅混响;2=仅限幅器;3=均不支持 |
keySwitchState | bool | false | 高级音效总开关状态 |
keyDepth | double | 0.0 | 混响深度,范围 0~100 |
keyIntensity | double | 0.0 | 混响强度,范围 0~100 |
keyDynamicLimiter | double | 0.0 | 限幅器阈值,范围 -60~0(dB) |
设计意图:将"设备能力"作为运行时数据(而非编译期配置)下发,使同一套 App 兼容不同能力档位的设备——不支持高级音效的设备(capability=3)自然隐藏相关 UI,无需单独适配。
Failure Modes, Edge Cases & Concurrency
读取失败静默降级
getEQData() 对所有 Exception 捕获并返回 null(ble_eq_manager.dart#L17-L19)。这是有意为之的降级策略:EQ 数据非关键路径,读失败不应让页面崩溃。代价是错误信息被吞掉,建议生产环境配合原生侧日志或后续版本增加错误回调。
事件字段缺失的兜底
BleEqProcessor 对每个字段做 ?? 默认值处理(ble_eq_processor.dart#L11-L17):int 缺省为 3(不支持)、bool 缺省为 false、double 缺省为 0.0。同时用 as num? 兼容原生可能返回的 int/double 数值类型,避免类型转换异常。
设备能力位导致的边界
capability=3(均不支持)时,setAdvancedEQ 仍可调用,但设备可能忽略指令;应用层应在 UI 上依据能力位禁用控件,而非依赖设备侧兜底。
并发与竞态
- 多个
setEQMode/setAdvancedEQ连续调用时,事件流与指令流是异步的:写指令立即返回,设备状态经事件流异步回调。若用户快速连续调节,UI 应以"最新指令值"为准,忽略迟到的事件(或在事件回调中做值比较)。 reverberationAllDataStream基于 Stream 广播语义,多订阅者各自收到同一份数据;getAdvancedEQ()触发的一次拉取可能对应一次事件,应用应避免在监听器中再次调用getAdvancedEQ()造成循环拉取。
数值范围边界
混响深度/强度限定 0~100、限幅器 -60~0。超出范围的数值 SDK 层未显式校验(当前实现直接透传),依赖原生层 EqCovertUtil 的编码约束或固件钳位;上层 UI 应使用 Slider 的 min/max 约束输入。
Performance & Operational Considerations
- BLE 写入合并:
setAdvancedEQ将深度、强度、限幅器、开关打包为一次指令下发,避免逐参数多次 BLE 写操作。BLE 传输是低带宽链路,合并写入可显著降低时延与功耗,UI 调节时应**节流(throttle)**连续滑块事件(如 50~100ms 间隔)后再调用,防止协议层拥塞。 - 读取缓存:Android 侧
EqCacheUtil对 EQ 数据做缓存,应用进入 EQ 页时应优先复用缓存并异步刷新,减少对设备的轮询。Flutter 侧getEQData()每次调用都会走方法通道,频繁调用会引入 bridge 开销。 - 事件订阅生命周期:
reverberationAllDataStream为冷/广播流的订阅关系由BleBaseEventProcessor管理;页面销毁时应取消订阅(StreamSubscription.cancel()),避免页面残留监听器造成的内存泄漏与无效 UI 刷新。 - 平台差异:Android 标准设备走
EQManager.kt、声卡设备走SoundCardEqManager.kt,iOS 走JL_SystemEQ;行为以设备固件为准,跨平台联调时注意两端jl_eq库版本一致(Android 侧为jl_eq_V1.1.0_10101)。
Extension Points
- 新增 EQ 方法:在
BleEqManager中新增静态方法 → 在BleMethodConstants注册方法名与参数名 → 原生侧(EQManager.kt/JL_SystemEQ)注册同名 handler。常量即契约,双端同步是唯一硬约束。 - 新增事件类型:若设备上报新的音效事件(如空间音频),可在
BleEventConstants增加typeXxx与keyXxx,仿照BleEqProcessor.reverberationAllDataStream增加新的 getter 流,复用BleBaseEventProcessor.filterByType的过滤机制。 - 自定义 EQ 曲线:
setEQValues(List<double>)已支持任意频段增益数组,可在此之上实现"专业模式"多频段均衡器 UI,无需改动 SDK。 - 设备能力适配:基于
keyCapability能力位做 UI 分级展示,天然支持不同档位设备,是扩展音效功能(如增加"空间音效"开关)的推荐模式。
Tests
当前仓库中未发现针对 BleEqManager/BleEqProcessor 的独立 Dart 单元测试文件(未在源码探索预算内定位到 test/ 目录中的 EQ 用例)。从实现结构看,该类可测试性较高:BleEqProcessor 的 map 规整逻辑纯函数化、易于 mock 事件流做断言;BleEqManager 依赖静态的 BleBaseManager.invokeMethod,可通过 mock 方法通道返回值来验证参数打包与 EQModel.fromMap 转换。建议在接入时补充针对"字段缺失默认值"与"Exception 返回 null"两条路径的用例。
Related Links
- ble_eq_manager.dart — EQ 指令入口(本文核心 API)
- ble_eq_processor.dart — 混响/限幅器事件处理
- eq_model.dart — EQ 数据模型
- ble_method_constants.dart / ble_event_constants.dart — 方法/事件常量契约
- ble_base_manager.dart — 方法通道桥接基类
- Android 原生:EQManager.kt、SoundCardEqManager.kt、EqInfo.java、EqPresetInfo.java、EqCacheUtil.java、EqCovertUtil.java
- iOS 原生:JL_SystemEQ.h、JLModel_EQ.h
- 相关能力页:音频模式调节(
ble_audio_mode_manager.dart)、音乐播放与媒体控制(ble_music_handler.dart/ble_device_music_manager.dart)、设备设置(ble_device_setting_manager.dart)