ぴょこりんブログ

裏垢です。

LibROSAで遊んでみる -クソザココード認識器編-

圧倒的令和ッ!!ぴょこりんクラスタ Advent Calendar 2019投稿記事であり、LibROSAで遊んでみる -少し触ってみる編-の続きです 。

1. コード認識器をつくってみる

ここで言うコードはchordです。和音です。なんか適当な曲を引っ張ってきてその曲の中のコードの移り変わりを当ててやろうというやつです。簡単のため、以下のような問題設定でいきます。

  • 簡単のためコードは3音で構成されると想定する (例えばC7とかはめんどいからC)
  • モデル構築に使うデータはとりあえず用意した12音の単音信号
    (音色のバリエーションも無し)
  • 上記の条件で既存の楽曲のコード進行を当てにいく

といったことをやってみようと思います。

2. やってみる

2.1. 大まかな学習器の設計

大雑把に言えば、
- 特徴量空間:クロマベクトル
- 分類のクラス:コード名
教師あり学習をやるという感じ。

なんにせよ今回は学習用のデータがありません。なので、1オクターブ分の単音データ(つまりたった12個のwavファイル) を可能な限り組み合わせて合成して、教師データとします。

コード認識したい曲の各時間フレームに対してクロマベクトルを計算したのち、学習したコード認識器にかけて、コードの認識を行うといった立て付けでやっていきたいと思います。

2.2. 単音から和音の合成

作業領域に以下ディレクトリがあることとを想定しています。

  • sounds 00.wav~11.wavまで11個の連番。同じ長さのド~シの単音の信号
  • chordtemplates
    ここに和音を出力します。とりあえず空です。

11個から3つ選ぶ組み合わせを総当たりで合成していくだけです。Librosaのloadとwrite_wavで読み書きも簡単です。

import itertools, os
import librosa
import numpy as np

files=sorted([file for file in os.listdir("../sounds/") if '.wav' in file])
wavs=[(librosa.load("../sounds/"+i),i.replace(".wav","")) for i in files]

for i in itertools.combinations(wavs,3):
    sr=i[0][0][1]
    tmp=i[0][0][0]+i[1][0][0]+i[2][0][0]
    librosa.output.write_wav("../chordtemplates/"+i[0][1]+i[1][1]+i[2][1]+".wav",tmp,sr)
# 例えば000407.wavがドミソ

おぞましい不協和音も混じってますが、12C3個の和音のデータが用意出来ました。こいつらを使って12C3個のクラス分類をする識別器を作ります。

2.3. 識別器の学習

このあたりからコードコピペするのめんどくさくなってきたので、LibROSAというよりscikit-learn感出てくるので、コードを省略します。そのうちgithubにjupyter notebookあげておこうと思いますが、大したことしてないです。

先ほど用意した和音のクロマベクトルを計算、大したサンプル量もないしk近傍法で学習することにしました。

楽曲の各時間フレームごとに、テンプレートのどの和音とクロマベクトルの空間において一番よく似ているかでその該当フレームの和音を決める(当てる)ということをしています。

2.4. 結果

ave;new feat.佐倉紗織のsnow of loveに本手法を適用しました*1。選曲の理由は作者の趣味とアドベントカレンダーなのでクリスマス曲を選びました。原曲は『snow of love ave;new』くらいでググれば聴けるんじゃないでしょうか。昔は公式がmuzieにあげてたりしたんですけどね(老。

以下がサビを識別した結果です。原曲の時間的には1:15~1:45あたりです。コードネーム出すとかめんどくさいので学習データに使った音を切り貼りしただけです。

何となくsnow of loveのサビの和音っぽく聞こえたら成功ってことですね。

f:id:cappsLk:20191208010149p:plain

3. 悪あがき

いやそもそも用意したデータもチープなので厳しい問題設定なんですが、にしたってもう少しマシにならんかねと。

そもそもコード進行のくせに変わりすぎなんですよ。たぶん主旋律とか打楽器とかそういうやつらに引っ張られている。

というわけで以下のような改善策を考えます。

3.1 打楽器とボーカルの除去

なんかチープな学習データと古典的な手法使っておいてあれですが、巨人の肩に乗ることにします。spleeterを使います。

gigazine.net 公式じゃなくてあえてgigazineなのは、この記事で使われているサンプルがFalcomの曲だからです(???? これでボーカルもベースもドラムも抜いて残ったやつだけでクロマベクトルを計算することにします。特に信号中でもかなりの音量を占めるボーカルとドラムの影響が除けるので、クロマベクトルが良い感じになることが期待できそうです。

ディープラーニングで良い感じにしてくれる学習済みモデル、どんどん公開されていて良いですね。ディープラーニング最高!!!!!!!!!(????)

ちなみにディープじゃなさそうですが、LibROSAにも打楽器音を分離するHPSSという手法が実装されています。 librosa.effects.hpss — librosa 0.7.1 documentation

3.2 クロマベクトルを時間的に均す

曲中のクロマベクトルをクラスタリングして、割り当てられたクラスタの平均に置き換えてしまうという雑なことをします。結果として、短時間での音の遷移に鈍感になり、コード進行位の粒度になってくれることを期待しています。

手法はGaussian Hidden Markov Model (Gaussian HMM)を使います。GaussianHMMはGaussian Mixture Model (GMM)に似ているけれど、各サンプルが独立ではなく、その前方のサンプルがどのクラスタに割り当てられているかに依存する手法、みたいな感じです。

Hidden Markov Modelは遠い昔scikit-learnに入ってたのですが、今は独立プロジェクトとして開発されています(hmmlearn)。

3.3 結果

・・・さっきよりマシですかね。これ聴いてsnow of loveのコードの認識結果だと当てられる自信は全くないです。

12C3個のクラスで分類していて、中にはゴミのような音(ドド#レとか)も存在してるわけですが、そういうのを選ばずにまぁ違和感ない音を選んでくれているだけでも素直に喜ぶべきポイントです(そうか?

ちなみに同じコードが長い間続いて、用意したテンプレの音より長くなった場合には無理やり波形くっつけて伸ばしてるので、ピロピロしてますが仕様です(???)。

4. まとめ

LibROSAとその他諸々をつかってコード認識器を使ってみたけどなんかよくわからないものができました。

全く実用性はないですが、クソ少量のデータの割には頑張ったんじゃないかと思います(かなしみ)。

*1:これは完全に蛇足ですが、2003年頃の曲なんで僕自身の人生の半分以上、この曲聞いてクリスマス過ごしてるんですよね。