关于 audio-analysis
用于 Decentraland 实体的实时音频分析技能,要求实体同时带有音频源、音频流或视频播放器。它提供振幅和 8 个频段数据,可用于驱动视觉效果,并说明客户端支持差异及静音回退。
主要功能
- 01读取实时振幅与 8 频段数据
- 02支持音频源、音频流和视频播放器
- 03配置对数或原始分析模式
- 04复用预分配的数据视图
- 05说明客户端实现差异与静音回退
适用场景
- 01制作随音乐变化的视觉效果
- 02用音频振幅驱动实体缩放
- 03按频段控制均衡器柱或灯光
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ decentraland/ sdk-skills --skill audio-analysis安装后在 Claude Code 中输入/audio-analysis确认已加载。
附带文件references/audio-analysis-example.md
详细介绍
AudioAnalysis
实时分析附加到已有 AudioSource、AudioStream 或 VideoPlayer 的任意实体的音频信号。渲染器会分析音频帧缓冲区,并在每个 tick 中将结果写回组件。场景可以读取这些结果来驱动可视化效果、随节拍变化的几何体、由音频驱动的灯光等。
规则:同一实体上必须有一个发出音频的组件
AudioAnalysis 本身不起作用。实体还必须拥有以下组件之一:AudioSource、AudioStream 或 VideoPlayer。渲染器会读取该组件的音频帧缓冲区,以计算振幅和频段。只有 AudioAnalysis 的实体不会产生数据。
规则:音频必须正在播放,输出才会非零
这些值来自实时音频帧。如果音源已暂停、静音或尚未加载,amplitude 和所有 bands[] 都会保持为 0。这里没有“就绪”事件——无条件启动响应式系统即可;静音时,它们只会朝 0 动画变化。
规则:并非所有平台都已实现——始终设计静音回退方案
- Unity(桌面端): 已实现。
- 移动端(Godot): 移动端从 v1.14.0(2026 年 9 月) 起提供
AudioAnalysis(PBAudioAnalysis),同时包含更广泛的音频功能对齐工作和AudioEvent(文档提交b4d4c88)。较早的移动端客户端不会写入分析数据。 - Bevy: 忽略该组件(不会写入分析数据)。
在未实现的平台上,这些值只会保持为 0,与静音时相同,因此场景不会报错,只会停止变化。始终设计不依赖 amplitude 的回退方案(例如基础缩放、循环补间),确保场景在所有客户端上都能正常呈现。
规则:通过 readIntoView 读取到预分配的视图中
readIntoView / tryReadIntoView 会将数据写入由调用方拥有的 AudioAnalysisView = { amplitude: number, bands: number[] }。在场景初始化时分配一次视图,并在每一帧重复使用——不要在系统内部调用 new。bands 数组必须预先设为 8 个元素。
规则:使用 createAudioAnalysis,而非 create
使用辅助函数 AudioAnalysis.createAudioAnalysis(entity, mode?, amplitudeGain?, bandsGain?)。它会使用安全默认值预填所有必需的 protobuf 字段(8 个频段 + 振幅 + 模式)。直接调用 AudioAnalysis.create(entity, {...}) 时,必须手动提供每个频段和振幅字段。使用 createOrReplaceAudioAnalysis 可覆盖已有组件,而不会抛出异常。
导入
import {
AudioAnalysis,
AudioAnalysisView,
PBAudioAnalysisMode,
AudioSource, // or AudioStream / VideoPlayer
engine,
Transform,
} from "@dcl/sdk/ecs";
import { Vector3 } from "@dcl/sdk/math";
AudioAnalysisView 是从 @dcl/sdk/ecs 导出的 TypeScript 类型别名(不是组件)。
组件字段
输出(由渲染器填充;在你的系统中读取):
| 字段 | 类型 | 范围 | 说明 |
|---|---|---|---|
amplitude | number | 0..~1(取决于模式) | 当前音频帧的综合信号强度。 |
band0 | number | 0..~1(取决于模式) | 最低频率分箱(超低音)。 |
band1..6 | number | 0..~1(取决于模式) | 频率分箱逐渐升高;在 MODE_LOGARITHMIC 模式下按对数间隔排列。 |
band7 | number | 0..~1(取决于模式) | 最高频率分箱(高音/空气感)。 |
输入(创建时配置一次):
| 字段 | 类型 | 默认值 | 说明 |
|---|---|---|---|
mode | PBAudioAnalysisMode | MODE_LOGARITHMIC | MODE_RAW = 0(原始 FFT 幅值)| MODE_LOGARITHMIC = 1(感知对数映射,推荐用于可视化器)。 |
amplitudeGain | number?(可选) | 省略时为 5.0 | 应用于 amplitude 的乘数。仅在 MODE_LOGARITHMIC 模式下使用。 |
bandsGain | number?(可选) | 省略时为 0.05 | 应用于全部 8 个频段的乘数。仅在 MODE_LOGARITHMIC 模式下使用。 |
数值是无界浮点数——增益可能使其超过
1。如果驱动的视觉效果需要0..1,请在你的系统中进行钳制或缩放。对于使用默认增益的典型音乐,预期峰值大致在0..1,普通内容则在0..0.5。
AudioAnalysisView
readIntoView / tryReadIntoView 使用的只读便捷结构:
type AudioAnalysisView = {
amplitude: number;
bands: number[]; // length 8 — bands[0] = lowest freq, bands[7] = highest
};
读取数据
const view: AudioAnalysisView = { amplitude: 0, bands: new Array<number>(8) };
engine.addSystem(() => {
// Throws if the entity has no AudioAnalysis
AudioAnalysis.readIntoView(audioEntity, view);
// Or, defensive variant — returns false if missing, no throw
// if (!AudioAnalysis.tryReadIntoView(audioEntity, view)) return
// view.amplitude and view.bands[0..7] are now populated
});
readIntoView(entity, out) — 使用最新分析结果填充 out。如果实体没有 AudioAnalysis,则抛出异常。
tryReadIntoView(entity, out): boolean — 功能相同,但当组件缺失时返回 false,而不是抛出异常。
常见模式
// 1. Pulse an entity's scale to overall amplitude
const view: AudioAnalysisView = { amplitude: 0, bands: new Array<number>(8) };
engine.addSystem(() => {
AudioAnalysis.readIntoView(audioEntity, view);
const t = Transform.getMutable(pulseEntity);
const s = 1 + view.amplitude * 10; // base 1, gain to taste
t.scale = Vector3.create(s, s, s);
});
// 2. 8-bar equalizer (one entity per band, scale Y by bands[i])
for (const [entity, _] of engine.getEntitiesWith(VisualBar, Transform)) {
const i = VisualBar.get(entity).index; // 0..7
Transform.getMutable(entity).scale = Vector3.create(
1,
view.bands[i] * BAR_HEIGHT,
1
);
}
// 3. Bass-only kick — react to the lowest band
const bass = view.bands[0] + view.bands[1]; // sub + low
if (bass > 0.7) {
/* trigger flash */
}
// 4. Custom gains (less sensitive amplitude, punchier bands)
AudioAnalysis.createAudioAnalysis(
audioEntity,
PBAudioAnalysisMode.MODE_LOGARITHMIC,
2.0,
0.1
);
完整的音乐可视化器(音频源 + 振幅球体 + 8 段均衡器柱),请参阅 {baseDir}/references/audio-analysis-example.md。
模式选择
MODE_LOGARITHMIC(默认)——频段按对数间隔排列,并经过增益缩放,使典型音乐的数值大致落在0..1。用于可视化器、缩放和颜色响应。amplitudeGain和bandsGain会生效。MODE_RAW——原始 FFT 派生幅值,按线性间隔排列。由于大部分音乐能量集中在低频段,这些频段在视觉上会占主导。amplitudeGain/bandsGain会被忽略。仅当你打算自行进行归一化时使用。
注意事项
- 组件 ID 为
1212。 - 在 MODE_RAW 中,
amplitudeGain和bandsGain不起作用。 设置它们不会报错,但在 MODE_LOGARITHMIC 之外,渲染器会忽略它们。 - 输出值可能超过
1.0,尤其是在增益较高或源素材音量较大时。如果要将值用于期望范围为0..1的 UI 条形图或 alpha 通道,请在下游进行钳制。 - 更新会受到节流。 渲染器会在帧时间预算内运行分析——正常情况下,每帧都会更新数值,但负载较重时可能会跳帧。请使用
dt插值来驱动平滑动画,不要假设更新间隔固定。 - 多源场景。 如果要针对特定音源进行响应,每个发出音频的实体都需要自己的
AudioAnalysis。这里没有全局混音;你需要选择要分析的实体。 - 也适用于
VideoPlayer音频——在拥有VideoPlayer的同一实体上调用AudioAnalysis.createAudioAnalysis(videoEntity)。渲染器会像处理AudioSource一样读取视频的音频帧缓冲区。重要:视频必须是渐进式文件(带有直接 URL 的 MP4/WebM),不能是 HLS 流(.m3u8)。 HLS 流可以正常播放声音,但渲染器会向 AudioAnalysis 组件写入零值(其音频通过分析器无法读取的路径解码)。已在88,-10-audio-visualization中验证,该场景并排演示了 AudioSource 和 VideoPlayer。 - 不会与
pitch交互。AudioSource.pitch会改变播放速度;分析基于实际播放的音频帧运行,因此较高的 pitch 会使感知到的频带能量向上偏移。这是预期行为,不是错误。 - 不要在调用
createAudioAnalysis之前调用readIntoView。 在组件不存在时读取会抛出异常。如果组件可能尚未附加(例如在运行时添加了分析组件),请使用tryReadIntoView。
权限
除了底层 AudioSource / AudioStream / VideoPlayer 本身所需的权限外,无需特殊的场景权限。流式音频的主机名仍需在 scene.json 中配置 ALLOW_MEDIA_HOSTNAMES(请参阅 audio-video 技能)。
示例场景
- audio-visualization — 引擎团队测试场景:两个独立的可视化组并排展示。组 0:使用
AudioSource+AudioAnalysis.createAudioAnalysis()驱动 8 根条形图和一个振幅球体。组 1:使用VideoPlayer(渐进式 MP4)+AudioAnalysis.createAudioAnalysis(),根据视频配乐驱动自身的条形图和球体。每组都有自己的预分配AudioAnalysisView,并在每帧调用各自的readIntoView。该场景演示了 AudioAnalysis 在 VideoPlayer 实体上的工作方式相同,并说明了渐进式视频的要求(HLS 流会写入零值)。
资源
{baseDir}/references/audio-analysis-example.md— SDK 示例中的完整音乐可视化场景(音频源 + 振幅脉冲 + 8 段均衡器)。- 源 PR:protocol #328、js-sdk-toolchain #1256、unity-explorer #6361。
- 参考场景:
sdk7-goerli-plaza/audio-visualization。