ぴょこりんブログ

裏垢です。

LibROSAで遊んでみる -少し触ってみる編-

圧倒的令和ッ!!ぴょこりんクラスタ Advent Calendar 2019投稿記事です。

1. はじめに

今年はLibROSAで遊びます。LibROSAとは何なのか、公式曰く、

LibROSA is a python package for music and audio analysis. It provides the building blocks necessary to create music information retrieval systems.

音響信号、それも特に音楽向けだそうです!楽しそう!!!(?)

今年は実際の楽曲からその和音を推定する、簡単な所謂コード認識器をつくってみようかと思います。本稿はそのための前準備の雑多な話をします。

ちなみにpython*1で、LibROSAとmatplotlib、numpyあたりがインストールされていること、コードの先頭に以下のように書いてあることを想定しています。

import librosa, os
import librosa.display
from matplotlib import pyplot as plt
import numpy as np

どれもpipで入るから問題ないよね。

2. なぜコード推定は難しいのか

まず、音の高さが何なのかは高校の物理でやっているかと思います。空気中を伝わる音波の周波数が音の高さを決めると。とはいえ同じ音の高さでも多種多様なものが存在します。オルガンとフルート、同じ音高だから区別がつかないなんてこと無いですよね。その違いの一つは倍音の構成にあります。

以下の画像はスペクトログラムと呼ばれるものです。 f:id:cappsLk:20191203223147p:plain

コードは以下の通りで早速LibROSAを使っています。周波数と時間のラベルを表示させるの地味にめんどくさそうだったので一発で表示してくれるのは嬉しい。このあたりはscipyでもまぁ出来るのだけれど。

x, sr = librosa.load("./oto.wav") # srはsampling rate
spectrogram = np.abs(librosa.stft(x))
librosa.display.specshow(librosa.amplitude_to_db(spectrogram, ref=np.max), y_axis='log', x_axis='time')
plt.show()

縦軸が周波数、横軸が時間を表していて、明るいほど高い数値が入っていることを表しています。要は最近見なくなりましたが、音楽プレイヤーのスペアナを時間ごとに横に並べていったものになります。入力信号は僕が適当に用意したドの音。

このサイトによると、大体523.23Hzあたりがドらしいですが、どの時間においても大体そのあたりにピークが立っていると思います。一方で、それ以外にもピークがあり、縞々模様が形成されています。

これらがいわゆる倍音と呼ばれるもので、例えば1046.5Hzあたりにもピークがあり、これも先ほどのサイトからすると1オクターブ高いドであることがわかります。同じドだけならまだしも、1567.96Hzあたりにもピークがありますよね。こいつはソですね。ドなのにソの成分も入っているということです。めんどくさいですね。

ドとソが仲良しなのは良く知られている性質で、大雑把にいえば、ドをの周波数を2倍すると1オクターブ高いドになりますが、3倍すると1オクターブ高いソになります。2音間の周波数比率がシンプルなほど調和がとれるらしいので、まぁ確かにそうかなって気がしてきますね。

話を戻しますと、例えばこんな倍音の構成比率が、オルガンだったりフルートだったりの音色を特徴づける大きな要素の一つだったりするわけです。一音だけ見ても豊富な倍音が複雑なバランスで含まれているというのに、そんな音が楽曲の中には沢山入り混じっているというわけです。その中からコードを推定するというのは何となく難しそうな気がしてきます。

3. 音階を表す特徴量クロマベクトル

さて、コード推定の話に戻りましょう。先ほど、523.23Hzと1046.5Hzはどちらもドという話をしました。冷静になってみると、コードを推定しようと思ったらどちらのドでも別に構わないわけです。じゃあ同一視できる良い感じの特徴量が無いかなーってなるんですが、ちょうどそういう需要向け(?)にクロマベクトルと呼ばれるものがあるそうです。

こちらのスライドの27ページ目がわかりやすいと思いました。先ほどのスペクトログラムのドだったらド(523.23Hzとか1046.5Hzとか全部)、レだったらレ周辺のバンドパスフィルタを通してそれぞれ足し合わせ、12次元 (鍵盤で言う1オクターブ分の白鍵+黒鍵の数) の特徴量としてあげる。

早速先ほどの信号のクロマベクトルを計算してみましょう。どう見ても音楽を対象としないと使いそうもないこんな特徴量だって実装済み、そうLibROSAならね!

chroma = librosa.feature.chroma_cqt(y=x, sr=sr)
plt.matshow(chroma)
plt.show()

縦軸が音階(0~11でド~シ)、横軸が時間です。黄色に近いほど数値が大きいです。 f:id:cappsLk:20191203230628p:plain

時間的に最初の点だけドになっていませんね。このあたりに注意して先ほどのスペクトログラムを見てみると、出だしの周波数が微妙に低くなっていることに気づくかと思います。クロマベクトルにおいてindexが9の音にピークがあるとのことで、どうやらラの成分が強いみたいです。

ドの音なので当然、全体的にドの音 (index=0) にピークが来ています。ドと仲良しだと先ほど説明したソ (index=7) にもピークが来ていますが、不思議なことにドと隣り合うシとド#にも無視できない黄色。

これは先ほどのバンドパスフィルタ設計から考えるとわかりやすくて、ドのフィルタと隣り合うフィルタはドのフィルタとオーバラップしてるんですね。なので隣り合う成分は高い相関を持つということでしょう。

あんまり嬉しくない性質のような気もするのですが、もしかしたらビブラートみたいな時間的な音高の揺れにロバストになる効果があったりするかもしれません(願望)。

と、書いていて気が付いたんですが、クロマ特徴量らしきもの、LibROSAでは3通り実装されてるみたいなんですよね。

  • librosa.feature.chroma_stft
  • librosa.feature.chroma_cqt
  • librosa.feature.chroma_cens

上二つはstftとcqt、短時間フーリエ変換と定Q変換なので、クロマベクトルを計算する前段である、音響信号を時間 - 周波数領域に変換するところの違いのようです。 後者は周波数に対して対数スケールに一定の解像度が得られるとのことらしいので、周波数が倍になると1オクターブ上がるみたいな音を扱うにあたっては低音も高音もフェアに扱ってくれそうですね。

最後のcensは“Chroma Energy Normalized” の略とのことで良くわからないですが、他と違って2011年の参考文献がついててちょっぴり新しそうなので、これ以降はcensを採用することにします。

4. まとめ

簡単なコード推定器をつくる前準備として、LibROSAを紹介、スペクトログラムの表示や音階を扱うための特徴量クロマベクトルについて紹介しました。

空いた枠を埋めるために複数記事に分けてたのですが、枠が全部埋まって出し惜しみする必要もないので続きます。

*1:僕の環境は3.7.3。