杰理 SDK 文档中心
首页
首页
  • 概述与快速开始

    • 项目简介与功能总览
    • 运行环境与快速开始
    • 工程结构与文档布局
  • 架构与核心机制

    • 插件架构与原生平台桥接
    • 基类管理器与常量体系
    • 事件流与接收通知机制
  • 蓝牙连接与设备管理

    • 蓝牙连接与状态管理
    • 设备信息、配置与按键设置
    • 双设备连接与多链路管理
    • 数据传输与自定义命令
  • 音乐与媒体控制

    • 设备音乐与手机音乐播放控制
    • 音量与音频输出管理
  • 音效与音频模式

    • 均衡器与音效调节
    • 音频模式与降噪(ANC)设置
    • Auracast 音频广播
  • 设备功能控制

    • 闹钟管理
    • FM 收音机控制
    • 灯光控制
    • 充电仓与彩屏仓管理
  • 示例应用:杰理之家 Demo

    • 应用框架与交互组件
    • 设置、多语言与调试
  • 接口参考与文档中心

    • 发送接口参考
    • 接收接口与事件参考
    • 官方文档与集成指南

均衡器与音效调节

均衡器(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、iOS JL_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 事件流 的异步链路接收设备主动上报的音效状态(如混响、限幅器数据)。

整个能力域围绕两条正交的通道组织:

  1. 指令通道(下行):应用调用 BleEqManager 的静态方法,经 BleBaseManager.invokeMethod 桥接到原生 SDK,再由原生层以 BLE 协议写入设备固件。典型操作包括获取 EQ 数据、设置 EQ 模式、重置 EQ、读取/设置高级音效(混响深度、强度、限幅器)、写入自定义频段增益。
  2. 事件通道(上行):设备状态变化时原生层解析 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},
    );
  }
}

来源:ble_eq_manager.dart

各方法的设计意图:

  • 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
      };
    });
  }
}

来源:ble_eq_processor.dart

实现要点:

  • 通过 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设置自定义频段增益
参数argEqIndexEQ 模式索引
参数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.javaEQ 信息模型(频段数、增益值等),对应 Flutter 侧 EQModel 的数据来源
EqPresetInfo.javaEQ 预设(模式)信息模型,对应 setEQMode 的索引含义
EqCacheUtil.javaEQ 数据缓存工具,减少重复读取设备的次数
EqCovertUtil.javaEQ 数值转换工具(字节序、增益值编码等协议转换)
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.hiOS 侧系统 EQ 操作接口(JL_BLEKit 框架)
JLModel_EQ.hiOS 侧 EQ 数据模型定义

iOS 侧通过 JL_BLEKit.framework 提供与 Android 对等的 EQ 能力,JLModel_EQ 承载 EQ 参数,JL_SystemEQ 提供设置/查询接口。Flutter 方法层(BleEqManager)与平台无关,因此两端共用同一套 Dart 代码。

端到端数据流小结

一次完整的"用户调节高级音效"操作的数据流向为:

  1. 用户在 UI 拖动混响深度滑块 → 调用 BleEqManager.setAdvancedEQ(depth, intensity, limiter, switch);
  2. BleEqManager 将参数按 BleMethodConstants 键名打包为 Map,经 BleBaseManager.invokeMethod 桥接;
  3. 原生侧(Android EQManager.kt / iOS JL_SystemEQ)将参数编码为 BLE 协议指令写入设备;
  4. 设备固件应用新参数并(视固件行为)上报最新状态;
  5. 原生解析上报数据 → 事件流 → BleEqProcessor 规整 → 应用订阅的 reverberationAllDataStream 发出新值 → UI 同步。

Usage Examples

示例一:读取 EQ 数据并处理空值

// 获取设备当前 EQ 数据;失败时返回 null,页面可走默认 UI
final EQModel? eq = await BleEqManager.getEQData();
if (eq != null) {
  // 使用 eq 渲染 EQ 模式与频段增益
} else {
  // 读取失败:展示默认 EQ 或引导重试
}

来源:ble_eq_manager.dart

示例二:切换 EQ 模式与重置

// 切换到索引为 2 的预设模式(如"摇滚")
await BleEqManager.setEQMode(2);

// 恢复默认模式
await BleEqManager.resetEQMode();

来源:ble_eq_manager.dart

示例三:写入自定义频段增益(手动 EQ 曲线)

// 以 double 列表表示各频段增益(dB),长度与设备频段数一致
await BleEqManager.setEQValues([0.0, 2.5, -1.0, 3.0, -2.0, 1.5]);

来源:ble_eq_manager.dart

示例四:配置高级音效(混响 + 限幅器)

// depth: 混响深度(0~100)  intensity: 混响强度(0~100)
// dynamicLimiter: 限幅器阈值(-60~0 dB)  switchState: 高级音效总开关
await BleEqManager.setAdvancedEQ(60, 40, -12, true);

来源:ble_eq_manager.dart

示例五:订阅混响/限幅器数据流

// 先拉取一次最新高级音效数据(结果异步经事件流返回)
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 决定展示混响/限幅器控件,并更新滑块位置
});

来源:ble_eq_processor.dart

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

本能力域无运行时配置文件;行为由事件数据中的能力位与设备固件共同决定。事件流中可用的"配置"语义如下:

字段类型默认值说明
keyCapabilityint3高级音效能力位:0=同时支持混响与限幅器;1=仅混响;2=仅限幅器;3=均不支持
keySwitchStateboolfalse高级音效总开关状态
keyDepthdouble0.0混响深度,范围 0~100
keyIntensitydouble0.0混响强度,范围 0~100
keyDynamicLimiterdouble0.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

  1. 新增 EQ 方法:在 BleEqManager 中新增静态方法 → 在 BleMethodConstants 注册方法名与参数名 → 原生侧(EQManager.kt / JL_SystemEQ)注册同名 handler。常量即契约,双端同步是唯一硬约束。
  2. 新增事件类型:若设备上报新的音效事件(如空间音频),可在 BleEventConstants 增加 typeXxx 与 keyXxx,仿照 BleEqProcessor.reverberationAllDataStream 增加新的 getter 流,复用 BleBaseEventProcessor.filterByType 的过滤机制。
  3. 自定义 EQ 曲线:setEQValues(List<double>) 已支持任意频段增益数组,可在此之上实现"专业模式"多频段均衡器 UI,无需改动 SDK。
  4. 设备能力适配:基于 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)
Next
音频模式与降噪(ANC)设置