音乐律动功能
项目简介:本模块适用于 Wi-Fi + BLE 协议的幻彩串灯(灯串)设备,支持本地音乐律动和 App 音乐律动两种模式,能够根据音频信号实时调整灯光颜色和亮度,实现音乐与灯光的同步效果。
🖼️ 预览:
音乐律动功能通过监听设备麦克风或 App 音频信号,实时分析音频特征(如分贝值、频率等),并将音频特征转换为灯光效果。该功能支持多种音乐模式选择,可调节灵敏度,支持随机颜色和预设颜色两种模式,为用户提供沉浸式的音乐灯光体验。
功能列表
- 本地音乐律动:通过设备麦克风采集音频,实时分析并控制灯光
- App 音乐律动:通过 App 音频接口获取音频数据,控制灯光效果
- 音乐模式选择:支持多种内置音乐模式(摇滚、爵士、古典等)
- 灵敏度调节:可调节音频灵敏度,控制灯光响应程度
- 随机颜色模式:根据音频随机选择颜色,实现动态效果
- 预设颜色模式:使用预设的颜色组合,实现固定配色效果
- 模式切换:支持本地音乐和云端音乐模式切换
- 状态同步:音乐律动状态与设备状态同步
功能实现详解
实现概述
音乐律动功能采用两种不同的实现方式:
- 本地音乐律动:使用
dreamlightmic_music_dataDP 点,设备端通过麦克风采集音频并处理,面板通过 DP 点控制音乐模式和参数 - App 音乐律动:使用
music_dataDP 点,通过 App 的 MediaKit 音频接口获取音频数据,面板端实时计算颜色并下发
核心设计思路:
- 数据分离:本地和云端使用不同的 DP 点和数据结构
- 实时处理:云端模式使用节流处理音频数据,避免频繁下发
- 状态管理:使用云存储保存当前音乐模式和灵敏度设置
- 颜色策略:支持随机颜色和预设颜色两种策略
核心组件
src/components/music/index.tsx: 音乐律动主组件,负责模式切换和整体布局src/components/music/local/index.tsx: 本地音乐律动组件src/components/music/app/index.tsx: App 音乐律动组件src/hooks/useLocalMusicInit.ts: 本地音乐初始化和管理 Hooksrc/standModel/musicModel/LocalMusic/dpParser/localMusic__dreamlightmic_music_data.ts: 本地音乐 DP 解析器src/components/music/app/sdk.ts: App 音乐音频处理 SDKsrc/hooks/useStoreSensitivity.ts: 灵敏度存储管理 Hooksrc/components/random-colors/index.tsx: 随机颜色选择组件
组件职责说明:
- Music 组件:作为容器组件,管理音乐模式切换(本地/云端),协调子组件
- LocalMusic 组件:处理本地音乐模式,显示音乐列表和灵敏度调节
- AppMusic 组件:处理云端音乐模式,注册音频监听和颜色计算
- useLocalMusicInit Hook:封装本地音乐的初始化、播放、灵敏度调节逻辑
- 音频 SDK:封装 MediaKit 音频接口,提供音频数据回调
数据流
sequenceDiagram
participant User as 用户
participant Music as 音乐组件
participant Local as 本地音乐组件
participant App as 云端音乐组件
participant Hook as 初始化Hook
participant SDK as 音频SDK
participant DP as DP协议
participant Device as 设备
User->>Music: 选择音乐模式
Music->>Local: 显示本地音乐
User->>Local: 选择音乐类型
Local->>Hook: 调用onPlay
Hook->>DP: 编码dreamlightmic_music_data
DP->>Device: 下发DP
Device->>Device: 麦克风采集音频
Device->>User: 显示灯光效果
User->>Music: 切换到云端音乐
Music->>App: 显示云端音乐
User->>App: 选择音乐类型
App->>SDK: 注册音频监听
SDK->>SDK: 获取音频数据
SDK->>App: 回调音频数据
App->>App: 计算颜色和亮度
App->>DP: 编码music_data
DP->>Device: 下发DP
Device->>User: 显示灯光效果Mermaid 流程图
功能主流程图:
flowchart TD
A[进入音乐律动页面] --> B{选择音乐模式}
B -->|本地音乐| C[显示本地音乐列表]
B -->|云端音乐| D[显示云端音乐列表]
C --> E[用户选择音乐类型]
D --> F[用户选择音乐类型]
E --> G[设置音乐参数]
F --> H[注册音频监听]
G --> I[编码dreamlightmic_music_data]
H --> J[获取音频数据]
J --> K[计算颜色和亮度]
K --> L[编码music_data]
I --> M[下发DP到设备]
L --> M
M --> N[设备执行灯光效果]
N --> O[用户观察效果]
O --> P{继续调节?}
P -->|是| Q[调节灵敏度/颜色]
P -->|否| R[保持当前状态]
Q --> G
Q --> K数据流转图:
sequenceDiagram
participant User as 用户操作
participant UI as UI组件
participant Hook as 业务Hook
participant Parser as DP解析器
participant Device as 设备/音频
participant Storage as 云存储
User->>UI: 选择音乐模式
UI->>Hook: 初始化音乐数据
Hook->>Storage: 读取保存的音乐ID
Storage-->>Hook: 返回音乐ID
Hook->>Parser: 构建DP数据
Parser->>Device: 下发DP指令
Note over Device: 本地模式:设备处理音频<br/>云端模式:App提供音频
Device->>Device: 分析音频特征
Device->>User: 显示灯光效果
User->>UI: 调节灵敏度
UI->>Hook: 更新灵敏度
Hook->>Parser: 更新DP数据
Parser->>Device: 下发更新
Hook->>Storage: 保存灵敏度组件交互图:
graph TB
A[Music组件] -->|切换模式| B[LocalMusic组件]
A -->|切换模式| C[AppMusic组件]
A -->|使用| D[RandomColors组件]
B -->|调用| E[useLocalMusicInit]
C -->|使用| F[音频SDK]
E -->|使用| G[useStoreSensitivity]
E -->|编码| H[LocalMusicParser]
F -->|回调| C
C -->|编码| I[MusicDataParser]
H -->|下发| J[设备]
I -->|下发| J
G -->|保存| K[云存储]状态变化图:
stateDiagram-v2
[*] --> 初始化: 页面加载
初始化 --> 选择模式: 用户操作
选择模式 --> 本地模式: 选择本地
选择模式 --> 云端模式: 选择云端
本地模式 --> 选择音乐: 显示列表
云端模式 --> 选择音乐: 显示列表
选择音乐 --> 播放中: 开始播放
播放中 --> 调节参数: 用户调节
调节参数 --> 播放中: 更新参数
播放中 --> 停止播放: 用户停止
停止播放 --> 选择模式: 重新选择
停止播放 --> [*]: 退出页面关键代码片段
本地音乐播放:
云端音乐音频监听:
音频数据处理 SDK:
灵敏度调节:
数据结构
本地音乐 DP 数据结构:
云端音乐 DP 数据结构:
音频数据回调结构:
随机颜色数据结构:
API 调用
本地音乐 DP 接口:
- DP 点:
dreamlightmic_music_data - 调用方式:
structuredActions.dreamlightmic_music_data.set(data, options) - 数据格式:经过
MicMusicFormater编码的十六进制字符串 - 调用时机:
- 选择音乐模式时
- 调节灵敏度时
- 切换随机/预设颜色时
- 初始化时恢复上次状态
云端音乐 DP 接口:
- DP 点:
music_data - 调用方式:
structuredActions.music_data.set(data, options) - 数据格式:经过协议编码的数据对象
- 调用时机:
- 音频数据回调时(节流 300ms)
- 选择音乐模式时
- 切换随机/预设颜色时
音频接口:
- 接口:
ty.media.getRGBAudioManager() - 方法:
startRGBRecord(): 开始音频录制stopRGBRecord(): 停止音频录制onAudioRgbChange(callback): 注册音频数据回调offAudioRgbChange(): 取消音频数据回调
- 调用时机:
- 进入云端音乐模式时注册
- 离开云端音乐模式时取消
云存储接口:
- 存储键:
CLOUD_KEY_LOCAL_MUSIC_ID: 本地音乐 IDCLOUD_KEY_APP_MUSIC_ID: 云端音乐 IDCLOUD_RANDOM_COLOR_ID: 随机颜色 ID
- 调用方式:
useCloudStorageKey(key, options) - 数据格式:字符串(音乐 ID 或颜色 ID)
注意事项
实现难点
-
音频数据处理性能:
- 难点:音频数据回调频率很高,频繁计算和下发可能导致性能问题
- 解决方案:使用节流(throttle)限制处理频率(300ms),减少计算和下发次数
-
状态同步问题:
- 难点:音频回调中使用闭包,React state 可能不是最新值
- 解决方案:使用
useRef保存最新状态,在回调中从 ref 读取
-
颜色计算逻辑:
- 难点:需要根据分贝值动态计算亮度,同时支持随机颜色和预设颜色
- 解决方案:使用
calcPosition函数计算分贝值到亮度的映射,根据模式选择颜色策略
-
模式切换清理:
- 难点:切换音乐模式时需要清理之前的监听和状态
- 解决方案:在组件卸载时调用
offMusic2RgbChange,清理音频监听和保持屏幕常亮状态
-
灵敏度持久化:
- 难点:不同音乐模式的灵敏度需要分别保存和恢复
- 解决方案:使用
useStoreSensitivityHook,以音乐 ID 为键存储灵敏度值
常见问题
-
问题:云端音乐模式无效果
- 原因:音频接口未初始化或权限未授予
- 解决方法:检查
getRGBAudioManager是否可用,确保已授予麦克风权限
-
问题:本地音乐模式设备无响应
- 原因:DP 数据格式错误或设备不支持该音乐模式
- 解决方法:检查 DP 数据编码是否正确,确认设备支持的音乐模式列表
-
问题:颜色变化不流畅
- 原因:节流时间设置过长或 DP 下发失败
- 解决方法:调整节流时间(建议 300ms),检查网络连接和设备状态
-
问题:灵敏度调节无效
- 原因:灵敏度值未正确保存或 DP 下发失败
- 解决方法:检查云存储是否正常,确认 DP 下发成功回调
-
问题:切换模式后状态混乱
- 原因:未正确清理之前模式的状态和监听
- 解决方法:在模式切换时调用清理函数,重置相关状态
性能优化
-
节流处理:
- 音频数据回调使用节流(300ms),减少计算和下发频率
- 灵敏度调节使用节流(100ms),避免频繁更新
-
Ref 使用:
- 使用
useRef保存最新状态,避免闭包问题 - 使用
useRef缓存音频管理器实例,避免重复创建
- 使用
-
条件渲染:
- 根据音乐模式条件渲染对应组件,减少不必要的渲染
- 使用
useMemo缓存音乐列表数据
-
数据缓存:
- 灵敏度数据缓存到云存储,避免重复计算
- 音乐列表数据使用
useMemo缓存
-
屏幕常亮管理:
- 仅在音频监听时保持屏幕常亮,退出时恢复
- 使用
setKeepScreenOn管理屏幕状态
边界情况
-
音频接口不可用:
- 处理:显示错误提示,禁用云端音乐模式
- 代码:检查
getRGBAudioManager返回值,提供降级方案
-
设备不支持音乐模式:
- 处理:根据 DP schema 判断支持情况,隐藏不支持的功能
- 代码:使用
useSupport检查 DP 支持情况
-
音频数据异常:
- 处理:校验数据格式,异常数据不处理
- 代码:在回调中添加数据校验逻辑
-
分贝值超出范围:
- 处理:限制分贝值范围,超出范围使用边界值
- 代码:使用
Math.max和Math.min限制范围
-
颜色列表为空:
- 处理:使用默认颜色或从音频 RGB 值计算颜色
- 代码:检查颜色列表长度,提供默认值
-
组件卸载时清理:
- 处理:在
useEffect清理函数中取消监听和恢复状态 - 代码:返回清理函数,调用
offMusic2RgbChange
- 处理:在
依赖关系
-
依赖注意事项:
- MediaKit 需要在 TTT 配置中启用
- 音频权限需要在 App 中授予
-
解耦建议:
- 将音频处理逻辑封装为独立 SDK,便于复用和测试
- 将颜色计算逻辑抽离为工具函数,便于单元测试
- 将音乐模式数据配置化,便于扩展和维护