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