音楽のコード進行の自動認識(1)

こないだヤマハが「Chord Tracker」というiOSアプリをリリースしました。デバイスの中にある音楽ファイルを分析してコード進行を表示させるアプリです。他に歌声をある程度消しちゃう機能もついており、耳コピ支援アプリという感じですね。

コード認識も歌声分離もMIRのメジャーなテーマ(毎年MIREXのお題)なので、ヤマハみたいな大手がその技術の応用に手を出したことは少し衝撃でした。

これ以外にもちょっと事情があって、コード進行の認識についていろいろ調べることになったんでここに書いていきます。

クロマベクトル

コード認識の際に使う特徴量は、今のところほぼクロマベクトル(Chroma vector)というもので定着してるようです。近年ではいろいろ改良されてるバージョンもあるそうですが、基本的な考え方は固まってます。

Chromaは簡単に言うと、スペクトラム分析で得た周波数域のエネルギーを、一定の周波数範囲内で半音ごと(あるいはもっと細かく)に区切って、さらに1オクターブ内に詰め込んで足し合わせてできたものです。つまりCのすべてのオクターブの周波数のエネルギーを足し合わせて一つの値になり、同じくC#、Dも・・・という感じで、1フレームごとに12次元のベクトルができます(もっと細かく切れば24次元、36次元・・・になる)。

つまるところ、スペクトラム表示(STFTやConstant-Qなど)を簡略化した産物です。これでどの半音のエネルギーが強いのかが分かりやすくようになり、コード分析が捗る、というわけです。ボイシングの情報が省略されるので、転回型とかが識別できなくなりますが、それを除けば、シンプルな割に強力なツールです。

クロマベクトルの本名はPCP(Pitch Class Profile)で、日本人の研究者さんが考案したものなんですが、何がどうなってChromaが定着したのは謎です。Chromaはもともと画像処理界隈の用語らしいんですが意味が全然違うらしいです。

簡単な比較でコード認識

スペクトラム分析で得たChromaが求まったあと、一番簡単なアプローチはそこから直接フレームごとのコードを認識しちゃおうというものです。コードの種類ごとにChromaの「テンプレート」を作って、一つずつ比較して最も近いテンプレートを選ぶ、という方法です。

「テンプレート」はたとえば、Cメジャーコードのテンプレートはこんな感じです。

[1,0,0,0,1,0,0,1,0,0,0,0]

0と1でできた、Chromaと同じサイズのベクトルです。もし12半音それぞれで、メジャー、マイナーコードのみを認識するとしたら、合わせて24個のテンプレートベクトルを用意。あとコードがない区間も識別するとしたら計25個(コードなしはどんなテンプレートを使えばいいのかちょっと不明)。

これらのテンプレートをそれぞれクロマベクトルとの相関(内積)を求めて、内積が最大だったテンプレートのコードをこのフレームの認識結果とします。以上。

簡単なのでぱぱっとPython書いて実験。ビートルズ「I Saw Her Standing There」の楽曲とコード進行の正解ラベルを使って認識率を測ってみました。

下のグラフの横軸は時間で、縦軸は一つの整数に一つのコードが割り当てられてると考えてください。青い線が正解ラベル、赤い線が認識結果です。

クロマベクトルの計算は、オープンソース音声処理ライブラリのlibrosaを使いました。使いやすいし現在も活発にアップデートが進んでるのでおすすめ。論文に従って、Constant-Qを使ったクロマベクトルにしてます。

まずは上の仕組みのまま直接一発。

result1

正解率は51.4%。認識結果にばらつきが多すぎます。

librosaにオーディオの調波成分を強調するエフェクトがあったので(簡単に言うとドラム音を消してくれるエフェクト)これを通してから認識してみます。

result2

正解率は61.3%に上がりました。ビート音の邪魔がなくなった分よくなってますね。

さらに、ギザギザを減らすため、認識結果にメディアンフィルターをかけてみます。

result3

正解率は75.6%。だいぶよくなりました。さらに応用的にするために、音楽をビートで区切って、ビートごと(あるいは小節ごと)に認識する、という方法をとることができます(つまり、コードはビートあるいは小節線の位置でしか変化しない、という仮定を立てることです)。この場合、区切り内のChromaの平均値を使って認識するか、フレームごとに認識して一番多く出たものをこの区切りの認識結果とする手があります。