ストリーミング解析
オフライン解析はファイル全体を一度に見ます。ストリーミング解析は音声を一塊ずつ見て、進みながら結果を出さねばなりません。
libsonare の StreamAnalyzer は、同じ MIR(Music Information Retrieval、音楽情報検索) のパイプラインを少しずつ実行します。ビジュアライザーやライブ表示向けに、フレーム単位の特徴量と、音声が増えるほど更新されていく音楽推定を出力します。
使うのは、マイク・AudioWorklet・再生グラフなどから音声がライブで届き、メーターやスペクトログラム、進行中の BPM/キー推定をリアルタイムに更新したいときです。すでに全体が手元にある固定ファイルなら、バッチ(オフライン)解析のほうが簡潔で精度も高くなります。
本ページではストリーミングの考え方を説明します。API のレシピは リアルタイムとストリーミング を参照してください。
ブロック・フレーム・ホップ・nFft — 混同しやすい 4 つ
混同しやすい 4 語で、取り違えがストリーミングのバグの多くを生みます。
- ブロック(チャンク) — ホストがコールバックごとに渡すサンプルの塊(AudioWorklet なら 128 や 512 サンプルなど)。サイズは音声システムが決め、あなたは決めません。
- フレーム — STFT(short-time Fourier transform、短時間フーリエ変換) の解析窓 1 つ分の出力。サイズ次第で、アナライザーは 1 ブロックにつき複数フレームを出すことも、0 のこともあります。
- ホップ(
hopLength) — フレーム間で解析窓を進める量。ブロックサイズとは独立にフレームレートを決めます。 nFft— 解析窓のサイズ。大きいほど周波数が細かく、時間は粗くなります。
あなたはブロックを流し込み、フレームを読み出します。2 つのレートは切り離されており、だからこそアナライザーは内部でバッファします。
更新されていく推定: 暫定から安定へ
音楽推定 — BPM、キー、現在のコード、進行、パターン — は、音声が届くにつれて少しずつ精緻化します。ストリームの最初の 1 秒には自信のある BPM に足る根拠がないため、序盤の値は暫定として見せる(または隠す)べきで、後から修正されることを許容します。最初のフレームを確定として扱うのが、ストリーミングの典型的なミスです。
フレームはまとめて読み出す
フレームは process() 呼び出しの間にバッファへたまります。1 ブロックにつき 1 フレームを期待するのではなく、利用可能な分をまとめて読み出して描画します。このまとめ読みの形にすると、音声コールバックは軽く保てます(バッファするだけ)。フレーム数が揺れる処理は、多少の揺れが問題になりにくい UI ループへ移せます。
API 上の「量子化」は、8 ビット/16 ビット出力でデータ量を減らす話です。タイミングをそろえる意味ではありません。
libsonare がストリーミング解析をどう行うか
StreamAnalyzer は sampleRate、nFft、hopLength、nMels、compute* フラグで一度構築し、process() でブロックを与えます。
| 呼び出し | 役割 |
|---|---|
readFrames(availableFrames()) | バッファされたメル/クロマ/オンセット/スペクトルのフレームを排出 |
stats() | BPM/キー/コード/進行/パターンの推定を返す。updated はキーまたは BPM を再計算したフレームで true になり、再計算後の値が前回と変わったフレームだけではないため、変化検出には使えない |
emitEveryNFrames | UI 描画向けにフレーム出力を間引く |
既定サンプルレートは 44100 Hz(バッチの 22050 に対し)です。リアルタイム音声が再生/キャプチャグラフから 44100/48000 で届くため、この値にしています。オフライン解析と同じ STFT 由来の特徴量段を再利用します。