キー検出
キー検出は、C major や A minor のような曲の調性中心、つまりルートのピッチクラスとモードを推定します。libsonare ではクロマに基づく推定なので、人間の楽理判断そのものではなく「どのピッチクラス分布が音声をもっともよく説明するか」と読むのが安全です。
主音と旋法
キーには 2 つの要素があります。
| 要素 | 意味 |
|---|---|
| 主音 | ホームと感じるピッチクラス。イ短調なら「イ」 |
| 旋法 | 主音の上に作られる音程パターン。一般的には長調/短調 |
長調は明るく解決した響き、短調は暗い響きとして感じられやすいです。
主音 12 種 × 一般的な旋法 2 種で、24 の候補キーになります。キーを命名するとは、曲が強調する音に最もよく合う主音と旋法の組を選ぶことです。
ハ長調とイ短調のような平行調は 7 音すべてを共有するため、クロマプロファイルの照合では取り違えが起きやすくなります。ただしこの取り違えは、旋法だけでなく主音も動かします。旋法だけを間違えるのは同主調(ハ長調とハ短調)のほうで、こちらは 7 音のうち 4 音しか共有しません。結果を評価するときは両者を分けて見ると役に立ちます。平行調の取り違えはプロファイルの回転位置を外したことを、同主調の取り違えは長短の根拠が弱かったことを意味します。
クロマからキーへ
キー検出は平均クロマベクトルから始まります。12 個のピッチクラスビンを素材全体で平均し、必要に応じて HPSS(打撃成分を抑えて和声が読みやすくなります)、ラウドネス重み付け、ハイパス処理などを使います。libsonare はそのベクトルを、各ルートとモードへ回転したキープロファイルと比較します。
キープロファイルとは、あるキーで各ピッチクラスがどれだけ強く現れると期待されるかを表す 12 個の数値テンプレートです。既定プロファイルは Krumhansl-Kessler のもので、プローブトーンによる聴取実験で測られたピッチクラス重みの古典的なセットです。これを Krumhansl-Schmuckler の手法で照合します(オプション名が 'ks' なのはこのためです)。実装には Shaath、Faraldo EDM 系、Bellman-Budge、Temperley など、それぞれ異なる素材に合わせたプロファイルもあり、genreHint で選択を誘導できます。auto は、別プロファイルの根拠が明確に強い場合を除き、既定のプロファイルを使います。
相関をひとことで
クロマベクトルとプロファイルの一致度はピアソン相関で測ります。2 つの 12 個の数値の形が、どれだけそろって上下するかを −1 から 1 で表す値です。1 は同じ形、0 は無関係、負の値はプロファイルが弱いと予想する場所でクロマが強いことを意味します。全体の大きさは無視されるので、同じ音楽を小さく録っても大きく録っても相関は変わりません。24 個(またはそれ以上)の候補キーそれぞれに相関が 1 つずつ計算され、最も高いものがキーになります。
候補集合は既定でメジャー/マイナーの 24 キーです。modes で教会旋法 5 種('modal')、または 7 種すべて('all')まで広げられます。オプションと既定値(profile、modes、genreHint、tuning)は楽曲解析にあります。
WASM と Node バインディングは、勝ったキーの mode を 'major'、'minor'、'dorian' などの名前で返し、root は Node、WASM、Python のいずれでもピッチクラス番号(C = 0)です。キーを受け取る入力(detectChords の keyRoot / keyMode やコード機能ヘルパー)は、ピッチクラス名('C#')または番号、モード名または序数のどちらでも受け付けます。結果にはクロマの生成に使った tuning も含まれます。tuning: 'auto' を渡すと先にチューニングを測定します(クロマ特徴量を参照)。
モーダルのプロファイルはメジャー/マイナーと同じ根拠を持たない
上に挙げたメジャー/マイナーのプロファイルは、いずれも聴取実験やコーパスから導かれた公表済みのものです。5 種のモーダルプロファイル(ドリアン、フリジアン、リディアン、ミクソリディアン、ロクリアン)は違います。同じ形に揃えた構成物で、6 つの名前付き重みから組み立てられています。主音を最も高くし、次にその 5 度と 3 度、次にそのモードをメジャーまたはマイナーと分ける特徴音、次に残りの音階音を置き、音階外はすべて抑えます。意味を担っているのは順序で、具体的な数値は各重みを隣同士の間に置くためのものです。プローブトーン実験も注釈付きコーパスも背後にないため、モーダルの結果は測定値ではなく妥当なヒューリスティックとして読んでください。詳しくは FAQ を参照してください。
信頼度は相対値
Key.confidence は、採点されたすべての候補のプロファイル相関に対するソフトマックスです。ソフトマックスは、スコアの一覧を合計 1 の割合に変える計算です。各スコアを指数関数に通すので、相関のわずかなリードが割合では大きなリードになり、最後に全体の合計で割ります。どの程度のリードを大きいとみなすかは温度で決まります(値の読み方を参照)。経験則ではなくこの形が値の意味そのもので、次の 4 点はそこから直接導かれます。
- 値域は
[0, 1)で、1 回の解析の候補の信頼度は合計 1 になります。 - 24 候補への配分は 1 に届かないため、2 択の判定から出た値とは比較できません。
- 同じ根拠を分け合う 2 つのキー、典型的には平行調どうしは、それぞれおよそ半分を報告します。
- 無音では候補を分けるものがないため、すべての候補が同じ小さな値を報告します。
信頼度はモデルの確信であって、正解率ではない
この値が表すのは、クロマが候補集合の中からどれだけ決定的に 1 つを選んだかです。その選択がどれだけの頻度で当たるかは表しません。注釈付き録音に対する較正は一切行っていないため、自信を持って外すことは十分に起こります。この値で分岐する処理は、自分の素材に対して自分でしきい値を決める必要があります。
値の読み方
ソフトマックスの温度は固定で、kConfidenceTemperature = 相関 0.08 です。この差で勝者は次点の e 倍、2 倍の差で約 7 倍、0.2 の差で約 12 倍の重みを持ちます。ここから 2 つのことが導かれます。まず、意味を持つのは候補間の差だけです。指数を取る前に各候補から最良の相関を引いているので、勝者の相関が 0.9 でも 0.2 でも、後ろに続く候補の形が同じなら同じ信頼度を報告します。次に、24 候補の値は事実上次点で決まります。他がすべて大きく離れていれば信頼度は 1 / (1 + e^(−差 / 0.08)) に近づくので、0.2 のリードは、入力が曲全体でも 4 小節でも、およそ 0.92 と読めます。
短い、あるいは薄い抜粋で信頼度が高く出るのはこのためです。持続する数音はどのプロファイルとも弱くしか相関しません。よく合うものは何もないのですが、それでもどれか 1 つは他より「ましに」合い、ソフトマックスはそのリードを確信として報告します。もう 1 つの指標 evidence_score()(C++ のみ)は重みの半分を勝者自身の相関に置くため、絶対的な当てはまりがよくなければ高く出ません。解析器がクロマのフロントエンドを選ぶときに比較するのはこちらです。事後確率は根拠が変わらなくても候補集合が広がるだけで機械的に小さくなりますが、マージンはそうならないからです。絶対的な当てはまりを見たいなら KeyCandidate.correlation を読んでください。ソフトマックスの元になった生の値がそのまま入っています。
信頼度は、1 回の解析の中で候補を順位付けする、判定がどれだけ割れているかを示す、平行調の曖昧さ(0.5 付近の要素が 2 つ)を検出する、といった用途に使えます。曲がそのキーにどれだけ合っているか(それは相関です)、候補集合の異なる解析どうしの比較(modes は分母を変えます)、正解率としての利用には使えません。較正されたものは何もないからです。
信頼度は、曲が 1 つの明確なキーだけを持つ証明でもありません。モーダルな混在(同主調から和音を借りる書法)、転調、疎なアレンジ、強い打楽器、チューニングのずれは、信頼度を下げたり近いキーを有力にしたりします。
次点候補が重要な場合は detectKeyCandidates を使います。UI では、キーを固定ラベルとして扱うより、最上位候補と信頼度を一緒に見せるほうが実態に合います。また、転調する曲には単一の答えがないため、区間ごとに見たい場合は短い窓で複数回推定し、平行調の取り違えに注意して候補を比べる運用が有効です。
検出したキーをプロジェクトの注釈にする
プロジェクトはキーの区間をタイムライン上の注釈ストリームとして持てます。キーはアレンジと一緒に移動し、保存と読み込みを経ても残ります。detectKey は主音をすでにピッチクラス(root。C = 0 … B = 11)で返すので、注釈の tonicPc にそのまま渡せます。モードは変換が必要です。注釈の番号付けでは 0 が「不明」に予約されているため、keyModeFromAnalysis(mode) は 'major' を 1、'minor' を 2、'dorian' を 3 というように、7 までの値に写します。
const key = detectKey(samples, sampleRate);
project.annotateKeys([
{ startPpq: 0, endPpq: 64, tonicPc: key.root, mode: keyModeFromAnalysis(key.mode) },
]);annotateKeys はキーのストリーム全体を 1 回の取り消し可能な編集で置き換え、保存したプロジェクト JSON では annotation.keys の下に入ります。転調する曲ではキーの区間ごとに 1 つのセグメントを作り、境界は呼び出し側が四分音符単位で決めます。フィールドの符号化と Python・C の名前は プロジェクト MIDI を参照してください。
libsonare がどう計算するか
KeyAnalyzer は既定で n_fft = 4096、hop_length = 512 のクロマを計算し、プロファイル相関でルート/モード候補を採点します。assign_posterior_confidences は各候補から最良の相関を引き、kConfidenceTemperature で割って指数を取り、正規化します。evidence_score() は勝者の相関を [0, 1] に写した値の半分と、kFullDistinctivenessGap(0.2)で頭打ちにした次点との差の半分の和です。オプションでは 倍音成分のみを使う HPSS 入力、RMS ラウドネス重み付け、ハイパスのカットオフ、候補モード、ジャンル/プロファイルヒントを指定できます。ネイティブ層にはコード進行を使う estimate_key_from_chords と refine_key_with_chords もあります。この 2 つは confidence フィールドに、上で説明したソフトマックスの事後確率ではなく進行のダイアトニック率を入れて返します。JS/Python の検出ヘルパーは、クロマプロファイルによるキー推定と候補リストを公開します。