audio-analysis について
Decentralandのエンティティでリアルタイム音声分析を行うスキルです。同じエンティティにAudioSource、AudioStream、VideoPlayerのいずれかが必要で、視覚効果に使える振幅と8帯域の値を提供します。クライアントごとの対応状況と無音時の代替動作も説明します。
主な機能
- 01リアルタイムの振幅と8帯域の読み取り
- 02AudioSource、AudioStream、VideoPlayerへの対応
- 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 があるエンティティに付与する、リアルタイムの音声信号分析です。レンダラーが音声フレームバッファーを分析し、各ティックで結果をコンポーネントに書き戻します。シーンはその結果を読み取り、ビジュアライザー、ビートに反応するジオメトリ、音声駆動のライトなどを制御します。
ルール:同じエンティティに音声を出力するコンポーネントが必要です
AudioAnalysis 単体では何も行いません。エンティティには AudioSource、AudioStream、VideoPlayer のいずれかも必要です。レンダラーはそのコンポーネントの音声フレームバッファーから信号を取り込み、振幅/バンドを計算します。AudioAnalysis だけを持つエンティティからはデータが生成されません。
ルール:ゼロ以外の値を出力するには、音声が再生されている必要があります
値はリアルタイムの音声フレームから算出されます。ソースが一時停止中、ミュート中、または未ロードの場合、amplitude とすべての bands[] は 0 のままです。「準備完了」イベントはありません。リアクティブシステムは無条件に開始してください。無音時は単に 0 に向かってアニメーションします。
ルール:すべての環境で実装されているわけではありません。必ず無音時のフォールバックを設計してください
- Unity(デスクトップ): 実装済みです。
- モバイル(Godot):
AudioAnalysis(PBAudioAnalysis)は、音声機能全般の互換性対応とAudioEventにあわせて、モバイル版 v1.14.0(2026年9月) に含まれています(ドキュメントのコミットb4d4c88)。それ以前のモバイルクライアントでは分析結果は書き込まれません。 - Bevy: コンポーネントを無視します(分析結果は書き込まれません)。
未実装の環境では、値は単に 0 のままです。無音時と同じ状態になるため、シーンでエラーは発生せず、動きが止まるだけです。シーンがすべてのクライアントで正常に表示されるよう、amplitude に依存しないフォールバック(基本スケールやループするトゥイーンなど)を必ず設計してください。
ルール:事前に確保したビューに readIntoView で読み取ります
readIntoView/tryReadIntoView は、呼び出し元が所有する AudioAnalysisView = { amplitude: number, bands: number[] } に書き込みます。シーンの初期化時にビューを一度だけ確保し、毎フレーム再利用してください。システム内で new を使って作成しないでください。bands 配列は、あらかじめサイズを 8 にしておく必要があります。
ルール:create ではなく createAudioAnalysis を使います
ヘルパー 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 は TypeScript の型エイリアス(コンポーネントではありません)で、@dcl/sdk/ecs からエクスポートされています。
コンポーネントのフィールド
出力(レンダラーによって設定されます。システム内で読み取ってください):
| フィールド | 型 | 範囲 | 説明 |
|---|---|---|---|
amplitude | number | 0..~1 (mode-dep.) | 現在のオーディオフレームの信号強度の合計です。 |
band0 | number | 0..~1 (mode-dep.) | 最も低い周波数ビン(サブベース)です。 |
band1..6 | number | 0..~1 (mode-dep.) | MODE_LOGARITHMIC では、周波数が高くなる対数間隔の周波数ビンです。 |
band7 | number | 0..~1 (mode-dep.) | 最も高い周波数ビン(高音/空気感)です。 |
入力(作成時に一度設定します):
| フィールド | 型 | デフォルト | 説明 |
|---|---|---|---|
mode | PBAudioAnalysisMode | MODE_LOGARITHMIC | MODE_RAW = 0(生の FFT 振幅)| MODE_LOGARITHMIC = 1(知覚に基づく対数マッピング。ビジュアライザーに推奨)。 |
amplitudeGain | number? (optional) | 省略時は 5.0 | amplitude に適用される倍率です。MODE_LOGARITHMIC でのみ使用されます。 |
bandsGain | number? (optional) | 省略時は 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 バーやアルファチャンネルに渡す場合は、後段でクランプしてください。 - 更新は間引かれます。 レンダラーはフレーム時間の予算内で解析を実行します。通常はフレームごとに値が更新されますが、負荷が高い場合はフレームがスキップされることがあります。固定の更新間隔を前提とせず、
dt補間で滑らかなアニメーションを実現してください。 - 複数の音源があるシーン。 特定の音源に反応させるには、音声を出力する各エンティティに専用の
AudioAnalysisが必要です。全体をミックスダウンする機能はありません。解析するエンティティを指定してください。 VideoPlayerの音声にも対応しています。VideoPlayerがあるエンティティと同じエンティティに対してAudioAnalysis.createAudioAnalysis(videoEntity)を呼び出してください。レンダラーはAudioSourceと同じ方法で、動画のオーディオフレームバッファーを読み取ります。重要:動画は HLS ストリーム(.m3u8)ではなく、プログレッシブファイル(直接 URL で参照する MP4/WebM)である必要があります。 HLS ストリームは音声付きで再生されますが、音声がアナライザーの読み取り対象外の経路でデコードされるため、レンダラーはAudioAnalysisコンポーネントにゼロを書き込みます。AudioSourceとVideoPlayerを並べて実演する88,-10-audio-visualizationで検証済みです。pitchとの連動はありません。AudioSource.pitchは再生速度を変化させます。解析は実際に再生されたオーディオフレームを対象とするため、ピッチを上げると、知覚されるバンドのエネルギーは高い方へ移動します。これは想定された動作であり、バグではありません。createAudioAnalysisの前にreadIntoViewを呼び出さないでください。 コンポーネントがない状態で読み取ると、エラーが発生します。コンポーネントがまだ追加されていない可能性がある場合(例:実行時に解析を追加する場合)は、tryReadIntoViewを使用してください。
権限
基盤となる AudioSource/AudioStream/VideoPlayer が必要とするもの以外に、特別なシーン権限は必要ありません。ストリーミング音声の場合、ホスト名に対する ALLOW_MEDIA_HOSTNAMES を scene.json に設定する必要があります(audio-video スキルを参照してください)。
サンプルシーン
- audio-visualization — エンジンチームのテストシーンです。独立した 2 つのビジュアライザーグループが並んでいます。グループ 0:
AudioSourceとAudioAnalysis.createAudioAnalysis()で 8 本のバーと振幅球を制御します。グループ 1:VideoPlayer(プログレッシブ MP4)とAudioAnalysis.createAudioAnalysis()で、動画のサウンドトラックから独自のバーと球を制御します。各グループは専用に事前確保されたAudioAnalysisViewを持ち、フレームごとに独自のreadIntoView呼び出しを行います。VideoPlayerエンティティでもAudioAnalysisが同様に動作することを実演し、プログレッシブ動画が必要であること(HLS ストリームはゼロを書き込む)を説明しています。
リソース
{baseDir}/references/audio-analysis-example.md— SDK サンプルの完全な音楽ビジュアライザーシーン(音源+振幅パルス+8 バンドのイコライザー)。- ソース PR:protocol #328、js-sdk-toolchain #1256、unity-explorer #6361。
- 参考シーン:
sdk7-goerli-plaza/audio-visualization。