クロマ特徴量
クロマは、ハーモニー解析の土台になる表現です。スペクトル全体を C、C♯、D、… B の 12 のピッチクラスビンへ畳み込み、各フレームで「C のエネルギーはどれだけ、C♯ はどれだけ」をオクターブによらず示します。キー検出もコード認識もここから読み取ります。本ページは MIR の全体像 のハーモニーの節を掘り下げます。
ピッチクラス: オクターブを忘れる
ピッチクラスは、ある音名のすべてのオクターブをひとまとめにします。C2、C3、C4 は別々の音高ですが、同じピッチクラス「C」です。西洋音楽には半音ごとに 12 のピッチクラスがあります。クロマは STFT フレーム内の全周波数に広がるエネルギーをその 12 ビンへ合算するので、どのオクターブで弾かれた C も同じビンに入ります。
ただし、すべてのオクターブが同じ重みで足されるわけではありません。libsonare のフィルターバンクは librosa のリファレンスに合わせ、A5(880 Hz)を中心とし、標準偏差 2 オクターブのガウス包絡線で各ビンの寄与に重みを付けます。包絡線の幅は広く、中低域から数 kHz までは強く数えられますが、それより下では減っていきます。中心からほぼ 4 オクターブ下の C2(65 Hz)付近の音は、重みが 5 分の 1 未満になります。はっきり聞こえているベースラインがクロマグラム上では薄く見えるのは、このためです。
この意図的な「忘却」が要点です。ハーモニーはどのオクターブではなくどの音の話です。C メジャーコードは、低く配置されても高く配置されても C メジャーコードです。オクターブを畳み込むと、各時点のハーモニーを表す 12 次元の小さな指紋が得られます。
たとえば低い C、中央の C、高い C が同時に鳴っていても、クロマでは全部 C のビンに足されます。一方で、C と E と G が強ければ C メジャーらしい、A と C と E が強ければ A マイナーらしい、という判断に使えます。つまりクロマは「音名の分布」を見る道具です。
クロマグラム: 時間軸上のクロマ
各フレームのクロマベクトルを積み重ねるとクロマグラムになります。ピッチクラスのエネルギーが曲の中をどう動くかを示す 12 行の画像です。持続するコードは安定した水平の帯として現れ、キーが変わると点灯し続けるビンが移ります。ビジュアライザーはこれを直接描き、アナライザーはここからパターンを読み出します。
フレームは「音声を短く切った 1 コマ」です。動画が静止画の連続でできているように、クロマグラムは短い時間ごとのクロマを横に並べたものです。横方向が時間、縦方向が 12 音名、と考えると読みやすくなります。
NNLS クロマ: ビンを畳むのではなく音を当てはめる
通常の STFT クロマや CQT クロマは、各周波数ビンをそのままピッチクラスへ畳み込みます。そのため音の倍音が別のピッチクラスのビンにも入ります。C の第 3 倍音は G、第 5 倍音は E なので、低い C を 1 音鳴らしただけでも C メジャーの和音が少し点灯します。
NNLS クロマ(非負最小二乗法)は、フレームごとに別の問いを立てます。このスペクトルを最もよく説明する音の組み合わせは何か、という問いです。libsonare はピアノの鍵盤 1 つにつき 1 つ(A0 から C8 までの 88 音)のテンプレートを持つ固定の辞書を作ります。各テンプレートは最初の 6 つの部分音を重みを下げながら並べたもので、1 半音 3 ビンの定 Q スペクトル上に置かれます。CQT 振幅の各フレームをこれらのテンプレートの和として当てはめ、どの音の量も 0 以上に保ちます。得られた音の量を 12 のピッチクラスへ畳むので、当てはめで C に帰属した倍音は G や E ではなく C として数えられます。そのぶんコード用のプロファイルがきれいになり、特に強いベースの上で効きます。
既定では純粋な NNLS の出力ではありません。複数の低い音が 1 つの部分音を同じくらい説明できてしまう、まばらな素材や純音でも安定するように、結果を直接の CQT クロマと混ぜ(当てはめ 60 %、直接 40 %)、さらに通常の STFT クロマと stftBlendWeight 0.55 で混ぜます。当てはめた CQT ベースのプロファイルだけが欲しい場合は enableStftBlend: false を渡します。
nnlsChroma(...)(Python は nnls_chroma)は、これを 12 × フレーム数の行列として返し、各フレームは最大値が 1 になるよう正規化されています。detectChords(...) に chromaMethod: 'nnls' を渡すと、同じフロントエンドの軽量版(5 オクターブ、1 音あたり 4 つの部分音)を使います。
クロマがハーモニーに適し、メロディに不向きな理由
| 問い | クロマを使う? | 理由 |
|---|---|---|
| キーは何か? | はい | 全体のピッチクラス分布が調性の中心を示します |
| 今鳴っているコードは? | はい | フレームのクロマがコードテンプレートと一致します |
| ベースのオクターブは? | いいえ | オクターブは畳み込まれています |
| リードメロディの音は? | いいえ | ピッチ追跡を使います。クロマは旋律線を分離できません |
| どの楽器か? | いいえ | 音色はスペクトル形状が必要なので MFCC を使います |
表現を問いに合わせるのは、MIR(Music Information Retrieval、音楽情報検索)で繰り返し必要になる技能です。クロマはハーモニーを明確にするために、オクターブと音色の詳細をあえて手放します。
libsonare がクロマをどう構築するか
Chroma クラスは STFT のパワースペクトルにクロマフィルターバンクを適用し、各周波数ビンを(上記のオクターブ包絡で重み付けしながら)そのピッチクラスへ対応づけて 12 ビンへ累積し、フレームごとに最大値が 1.0 になるよう正規化します(L-infinity)。振幅ではなくパワーを入力とするため、強い倍音ほど大きく効きます。
| 項目 | 内容 |
|---|---|
| 基本設定 | n_chroma = 12。fmin と n_octaves も設定に含まれますが、STFT クロマのフィルターバンクはこれらを参照しません(CQT クロマ側の概念で、指定しても解析範囲は変わりません) |
| 解析窓 | n_fft = 2048/hop_length = 512、Hann 窓 |
| チューニング | 12 ビン構成では、tuning に A440 からのずれを半音分数で指定します。既定は 0(A440)で、analyze・detectChords・detectKey に tuning: 'auto' を渡したときだけ信号から測定します |
| 算出方式 | STFT ベース、CQT ベース(chromaCqt / chroma_cqt、ChromaCqtConfig。librosa.feature.chroma_cqt の直接の相当)、CENS(ChromaCensConfig) |
| コード向け変種 | NnlsChromaConfig(非負最小二乗)で倍音の影響を抑える |
クロマは KeyAnalyzer(Krumhansl-Schmuckler 系)と ChordAnalyzer(テンプレートマッチング)で使われるため、クロマ段を改善すると下流の 2 機能が同時に良くなります。
チューニングは要求したときだけ測定されます
ChromaConfig.tuning の既定は 0、つまり A440 で、クロマ段が自分でこれを推定することはありません。librosa.feature.chroma_stft は既定の tuning=None でまず estimate_tuning() を実行するので、この点で挙動が異なります。A440 から半音の何分の一かずれた録音では、ピッチクラスの格子が実際の倍音列と合わなくなり、エネルギーが隣のビンへにじみ、キーやコードの結果が何の警告もないまま劣化します。
古い録音、テープ、生演奏、意図的に別の基準ピッチで演奏されたものなど、素材が A440 でない可能性がある場合は、analyze・detectChords・detectKey(Python では analyze・detect_chords・detect_key)に tuning: 'auto'(Python では tuning="auto")を渡してください。エンジンが先にずれを測定し、その値でクロマを作ります。estimateTuning()/estimate_tuning() が返す半音分数の値([-0.5, 0.5))を直接渡すこともでき、C++ では ChromaConfig.tuning にそのまま渡します。どの結果も、実際に使った値を返します(AnalysisResult・ChordAnalysisResult の tuning、detectKey の KeyDetection)。45 セント低く調律した合成のハ長調和音は、A440 のままだとホ短調と判定され、tuning: 'auto' ではハ長調と判定されて、約 -0.43 のチューニングを報告しました。単体の chroma は A440 固定で、チューニング引数を受け取りません。tuningToReferenceHz(tuning) と referenceHzToTuning(hz) で、この単位と Hz 単位の基準ピッチを相互に換算できます。ストリーミングは Hz をそのまま使い、作成時の tuningRefHz または処理中の setTuningRefHz() に A4 の基準周波数を指定します。ビジュアルプレイヤーは、読み込んだファイルごとにずれを推定して適用しています。