Skip to content

FAQ ​

最初に触るときによく挙がる質問には、下の表で答えます。そのあとの節では、音楽解析の結果が何を意味していて、どこまで信じてよいのかを扱います。何がどうテストされているかは 実装検証、librosa と一致する範囲については librosa 互換性 を参照してください。

すぐに答えられる質問 ​

質問答えページ
FFmpeg なしで開ける形式は?どの実行環境でも WAV と MP3 です。ブラウザでは Audio.fromMemoryWithBrowserFallback でブラウザ自身のコーデックを使い、AAC、OGG、FLAC もデコードできます。Python と CLI でそれらを読むには FFmpeg 有効のビルドが必要です。このビルドが読むのは音声および音声・映像のコンテナ形式だけで、concat リスト、HLS や DASH のマニフェスト、連番画像は拒否されます。インストール済みのビルドが何をデコードできるかは sonare doctor で一覧できます。インストール、ファイル入力
BPM が想定の半分や倍になる多くの場合、同じ周期性がどちらの読みも支えています。result.bpmCandidates は各候補に primary、half、double、other のラベルを付けて返し、analyzeBpm では bpmMin / bpmMax で探索範囲を狭められます。テンポ、テンポと BPM
ステレオファイルはどう扱われる?Audio.fromMemory と Audio.from_file はモノラルに畳み込みます。解析が想定しているのはモノラル入力です。ミキシング、マスタリング、roomMorphStereo は両チャンネルを保ちます。ステレオからモノラルへの変換
音声はどこかにアップロードされる?されません。どのバインディングも自分のプロセスの中でエンジンを動かし、ブラウザ版はページの中で動かします。このサイトのデモも何もアップロードしません。ブラウザ内ローカル処理
JavaScript で .delete() を呼ぶ必要はある?Audio と、関数が返す素のオブジェクトには不要です。WASM のメモリを持たないからです。StreamAnalyzer、Mixer、Project、RealtimeEngine など状態を持つクラスは、使い終わったら delete() を呼ぶか、using で宣言してください。メモリの問題
本番環境で使える?1.0 を超えてセマンティックバージョニングのタグが付いており、単独の開発者が保守しています。バージョンを固定し、アップグレード前にリリースノートを読み、依存する機能は自分の素材で検証してください。プロジェクトの状況とサポート

音楽解析の精度はどのくらいですか? ​

公開している数値はまだありません。そして librosa との一致は精度の数値ではありません。

librosa と一致するとは、2 つのライブラリが同じ計算をしているという意味です。その計算がキーを正しく言い当てるか、コードを正しく拾うかは別の問題で、答えられるのはアノテーション付きの音源だけです。両者が一致しているクロマベクトルであっても、キー推定がそれを読み違えることはあります。

代わりに用意してあるのは測定の仕組みです。ライブラリ本体のリポジトリの tests/fixtures/music_eval/ に、キー・BPM・ビート・ダウンビート・拍子・コードのマニフェストがあり、tools/eval/summarize_accuracy.py が実行結果をデータセット別の数値(キー正解率と MIREX 加重スコア〔正しいキーなら満点、5 度上や平行調など近い誤りなら部分点〕、コードの加重シンボル再現率〔曲の長さのうち、コードが正しく言い当てられた割合〕、ビート F 値〔注釈のビートから小さな許容幅に入ったビートの割合で、取りこぼしと余分な検出の両方を見る〕)に集計します。マニフェストが参照するコーパス(GiantSteps、Isophonics、Billboard、Ballroom、SMC など)は研究用途のライセンスで再配布できないため、マニフェストは空のまま同梱し、測定は手元のコーパスに対して走らせます。make accuracy-report で一連の手順が動きます。

その測定結果が出るまで、現状を正確に言えば「古典的 DSP としてはまともな実装だが、精度はまだ測定されていない」です。

confidence は何を表していますか? ​

モデル自身の確信であって、精度の推定値ではありません。この 2 つは混同されやすく、そして値で分岐する処理にとってはこの区別が効いてきます。

キー推定の confidence は、24 個の候補キーで 100% を分け合ったときの勝者の取り分と考えてください。技術的には事後確率で、スコアした全候補キーのプロファイル相関(曲のクロマが各キーのテンプレートにどれだけ合うか)に対するソフトマックスなので、対抗馬が近づくと値は下がります。同じクロマを分け合う平行調どうしは、両方が高い値を出すのではなく、それぞれおよそ半分を報告します。これは「推定が拮抗している」ことが分かるという意味で有用ですが、示しているのは候補集合の中でクロマがどれだけ一方的に 1 つを選んだかであって、その選択がどれだけの頻度で当たるかではありません。

ライブラリはどの部分も、アノテーション付きの音源では較正していません。そのため、confidence が高いまま間違えることは十分にありえます。confidence でしきい値を切るなら、しきい値は自分の素材に対して決めてください。高い値は「証拠が一方的だった」であって「たぶん正解」ではありません。

セクションの confidence とコードの confidence はさらに別の量で、それぞれの型のところに説明があります。互いに比較できるものではなく、キーの事後確率とも比較できません。

平行調(メジャー/マイナー)が入れ替わって出るのはなぜ? ​

ある調とその平行調は、音階の 7 音をすべて共有しているからです。ハ長調とイ短調は同じピッチクラスを使うので、すべてのオクターブを 12 個の数値に畳むクロマグラムには、どちらでも同じ音が並びます。両者を分けるのは、主音とその 5 度、3 度にどれだけ重みがかかっているかだけです。detectKey が読むのはまさにそこで、トラック全体のクロマをキープロファイルと比べます。主音に落ち着くことが少ない素材では、2 つの相関が近い値になります。

analyze() はもうひとつの読みを加えます。認識したコードとクロマのキーを照らし合わせ、カデンツ(属和音から主和音への解決)と、最初と最後のコードに重みを置きます。このコードのルートは低域を重み付けしたクロマから求めるので、ベースパートのある素材ほどこの段階の手がかりが増えます。

それでも特定のトラックが平行調で返ってくる場合、効くのは次の 3 つです。フルミックスの素材では detectKey ではなく analyze() を使い、コードの手がかりを生かすこと。ベースパートのある素材を渡すこと(ベースを抜いたステムはこの手がかりを失っています)。そして最上位だけでなく次点の候補を見ること。detectKeyCandidates(Python は detect_key_candidates、C++ は KeyAnalyzer)は 24 候補すべてを、プロファイル相関と key.confidence の事後確率付きで返すので、拮抗した平行調のペアはそこで「ほぼ同じ 2 つのシェア」として見えます。

認識できるコードの種類は? ​

トライアド(メジャー、マイナー、ディミニッシュ、オーギュメント)、セブンス(ドミナント、メジャー、マイナー、ディミニッシュ、ハーフディミニッシュ)です。サスペンデッド(sus2、sus4、sus2add4)、アドナインス、ナインス、シックスス、マイナーメジャーセブンスも含みます。7sus4、オルタード・ドミナント(11th、13th、7♭9、7♯9)も認識できます。

4 種のトライアドより先の語彙は、全テンプレート集合が有効なとき、つまり useTriadsOnly が off のときにだけ探索されます。単体のコード API は既定で off なので、そのままで全集合が使われます。統合 analyze() のパスは既定で on であり、要求しないかぎりトライアドだけを探索します。全集合を使うには useTriadsOnly: false を渡すか、どちらのコマンドラインでも --with-seventh を指定してください。語彙の外にあるコードは棄却されるのではなく、いちばん近いメンバーとして報告されます。

コードにも調と同じ「畳み込み」の問題があります。メジャートライアドと、その短 3 度下のマイナートライアドは 3 音のうち 2 音が共通で(C–E–G と A–C–E)、音域の情報が失われた後にはどの音がルートかを言う手がかりがほとんど残りません。そこでコード認識では低域を別に評価しています。ルートを言い当てる手がかりとしては、畳まれたクロマより低域のほうがはるかに信頼できるので、ルートが低域で鳴っている候補を、鳴っていない候補より優先します。この重み付けは意図的に「同点のときの決め手」であって「上書き」ではありません。低域の音がルートと一致するのは基本形のときだけで、低域を強く優先する設定にすると、すべての転回形がそのベース音の名前に付け替えられてしまうからです。

このうち 3 つは「難しい」のではなく「原理的に曖昧」なので、知っておく価値があります。メジャー・シックススと、その短 3 度下のマイナーセブンスは同じ 4 つのピッチクラスで構成されます(C6 と Am7 はどちらも C–E–G–A)。マイナー・シックススとその短 3 度下のハーフディミニッシュセブンス、7sus4 とその 4 度上の sus2add4 も同様です(C7sus4 と Fsus2add4 はどちらも C–F–G–B♭)。クロマグラムでは分離できません。分離すべきものがクロマの中に存在しないからです。分けるのは低域であり、低域が別の読みを示さないかぎり、認識器はより一般的なほうの読みを採ります。ベースパートのない素材では、一般的なほうの名前が返ると考えてください。

セクションのラベルが間違っている/全部 Unknown になるのはなぜ? ​

構造ラベリングは固定しきい値のヒューリスティックであって、学習済みのセグメンタではありません。そう明記もしてあります。境界の位置はおおむね使えますが、ラベル(Verse、Chorus、Bridge)はベストエフォートで、一般的なポップスの形式に従わない素材ではよく外れます。

Unknown は失敗コードではなく、意図的な答えです。境界がまったく検出されなかったとき、どの肯定的な分岐にも当てはまらなかったとき、そして音楽的機能を主張するには証拠が弱すぎたときに出ます。最後のケースではしきい値未満のスコアがそのまま残るので、どこまで惜しかったかが見えます。

Unknown が増えやすくなるガードが 2 つあります。どちらも意図的なものです。1 つ目は、クロマで区別できない隣り合うセグメントを、ラベル付けの前に 1 つにまとめることです。ひと続きの音楽の途中にノベルティのピークがあっても、それで 2 つの「繰り返しセクション」に分かれることはありません。2 つ目は、ほぼすべてのセクションのペアが互いの繰り返しと判定される場合(均質な素材はまさにこうなります)、繰り返しを Verse/Chorus 交替の証拠ではなく「情報を持たないもの」として扱うことです。この扱いがなければ、均質な素材から、一度も変化していない小節をもとにした完全な楽曲形式ができてしまいます。

後段のアルゴリズムには、ラベルより生の信号を渡すほうが向いています。boundaryTimes とクロマのコサイン自己類似度行列は、呼び出し側が自分のしきい値を当てられるように公開してあります。

録音が A = 440 Hz にチューニングされていないときは? ​

analyze、detectChords、detectKey に tuning: 'auto'(Python は tuning="auto")を渡すと、ピッチクラスを読む前に音声からチューニングを測定します。tuning は半音の端数で [-0.5, 0.5) の範囲、0 が A440 で、どの結果も、指定した値でも測定した値でも、実際に使った値を報告します(KeyDetection.tuning、AnalysisResult.tuning、ChordAnalysisResult.tuning)。

typescript
const key = detectKey({ samples, sampleRate, tuning: 'auto' });
console.log(key.name, key.tuning, tuningToReferenceHz(key.tuning));

40 セント高く生成したテストコードは、C メジャーとして読まれ、測定された tuning は 0.42 でした。tuningToReferenceHz で 450.8 Hz になります。オプションなしでもキーは C メジャーのままで、tuning は 0 のままでした。基準ピッチが分かっているときは、referenceHzToTuning(hz) で逆向きに変換できます。

モーダルのキーはメジャー/マイナーと同じくらい信頼できますか? ​

いいえ。違いはプロファイルの出どころにあります。

メジャーとマイナーのキープロファイルは出版されたもの(Krumhansl–Kessler、Temperley、Sha'ath、Faraldo、Bellman–Budge)で、いずれも聴取実験かコーパスから導かれています。5 つのモーダル・プロファイル(ドリアン、フリジアン、リディアン、ミクソリディアン、ロクリアン)はそうではありません。同じ形をした構成物です。トニックをいちばん高く、その 5 度と 3 度がそれを裏づけ、そのモードを近隣のメジャー/マイナーから分かつ音がほかのスケール構成音より上に立ち、スケール外はすべて抑える、という組み立てです。意味を担っているのは順序であって、個々の数値は各重みを隣り合う重みの間に置くだけの、それ以上の根拠を持たない値です。

この構成は、それとは独立に作った合成スケールヒストグラムに対して検証してあるので循環はしていません。ただし、プローブトーン研究もアノテーション付きコーパスもその背後にはありません。モーダル検出はもっともらしいヒューリスティックであり、モーダル候補がオプトインなのはそのためです。

自分の素材で精度を測るには? ​

ここはコントリビューターと、アノテーション付きの音源を持っている方向けの説明です。ライブラリ自体が何と照合されているかは 実装検証 にあります。

tests/fixtures/music_eval/ 以下のマニフェストに、音源・アノテーション・期待値を並べた行を書いてから、次を実行します。

sh
export SONARE_MUSIC_FIXTURE_ROOT=/path/to/corpus
make accuracy-report

出力を読む前に 2 点だけ。

CI の合否を決める行と、測定値を出す行は別の行です。合否を決める行はアサートするだけで何も出力しないため、観測値には加わりません。測定行には report_only を付けます。これでアサートが出力に変わります。1 つのマニフェストに両方を混在させて構いません。

そして集計は空集合を採点しません。観測値のない次元は unmeasured と報告され、0% にも 100% にもなりません。これは見かけ以上に重要です。使えないデータ点を飛ばして残りを平均する指標は、空集合を満点として採点します。そしてフィクスチャランナーは音源が見つからない行を、失敗ではなくスキップとして扱う設計なので、パスのタイプミスは要約行の上では成功とまったく同じに見えます。unmeasured だらけのページを結果として公開してしまってはいけないパイプラインでは、集計ツールに --require <dimension> を渡してください。

対象コーパスと手順の全体は、ライブラリ本体のリポジトリの tools/eval/README.md にあります。