何かあれば GitHub のリポジトリに issue を作るか ryukau@gmail.com までお気軽にどうぞ。
Update: 2023-06-22
GollyのGenerationsルールから生成した音のクラスタリングが意外とうまく行ったので、1秒以下の音のクラスタリングを試しました。
Python3では次のライブラリを使っています。
データセットに含まれる音は、長さを1秒以下、サンプリング周波数を44100Hzにそろえています。
今回作ったデータセットの大きさは6016サンプルで長さは約81分です。
以前作ったシンセサイザから、それぞれ1000ほどのサンプルをレンダリングしました。
今回使ったシンセサイザです。
ブラウザのデベロッパツールのコンソールから次のコードを実行してレンダリングしました。
var title = document.getElementsByTagName("title")
var button = title[0].innerText === "Singen0.3"
? ui.buttonRandom : buttonRandom
var counter = 0
var id = window.setInterval(
=> {
() if (counter > 1024) clearInterval(id)
.onClick()
button++counter
,
}1000
)
Pluck, KSCymbal, PADcymbal, FDNCymbal, WaveCymbalはステレオでレンダリングした音をチャンネルごとに分離してそれぞれ別のサンプルとして扱っています。
得られた音を次のコードで正規化しました。SoXを使っています。
近所を歩いてフィールドレコーディングした音をデータセットに加えました。
フィールドレコーディングの音はSoXの remix -
でモノラルにしてから1秒間隔で切っています。
SoX man page -
EFFECTSの節に remix
の解説
python_speech_features.mfcc
で取り出したMFCCを
numpy.ravel
で1次元にして
sklearn.cluster.AffinityPropagation
でクラスタリングしました。
今回使った python_speech_features.mfcc
のパラメータです。
import python_speech_features
import soundfile
= soundfile.read("path/to/wav_file")
data, samplerate
= 1024
nfft = python_speech_features.mfcc(
mfcc
data,
samplerate,=nfft / samplerate,
winlen=0.01,
winstep=26,
numcep=52,
nfilt=nfft,
nfft=0,
lowfreq=20000,
highfreq=0.0,
preemph=0,
ceplifter )
今回は1秒以下の音のクラスタリングなので MFCC のフレーム数が
1 / winstep = 100
になるように調整しました。フレーム数が100より少ないときは0で埋めたフレームを付け足しています。フレーム数が100より大きいときは、音の始まりから100フレームだけを取り出して、残りのフレームを切り捨てています。
Practical
Cryptography の MFCC
チュートリアルにMFCCのデルタで結果が改善することがあると書いてあったので
numpy.concatenate
でMFCCと、MFCCのデルタをつないで一つのデータポイントにまとめました。
# mfcc の取得は省略。
= python_speech_features.base.delta(mfcc, 1)
delta = numpy.concatenate((numpy.ravel(mfcc), numpy.ravel(delta))) mfccdelta
ここでは mfccdelta
のことをMFCC+デルタと呼んでいます。
ここでのエンベロープは信号を短い区間に区切って、それぞれの区間で信号の絶対値の最大値を取り出したものです。
def get_envelope(data, samplerate, winstep, n_frame):
"""
:data: 一次元の信号。
:samplerate: サンプリング周波数。
:winstep: 区間の長さ。秒。
:n_frame: 区間の数。
"""
= numpy.abs(data)
data_abs = numpy.zeros(n_frame)
envelope = None
index = int(winstep * samplerate)
step = 0
start = step
end for frame in range(n_frame):
if end >= len(data_abs):
= frame
index break
= numpy.max(data_abs[start:end])
envelope[frame] = end
start += step
end if index is not None:
= numpy.max(data_abs[start:])
envelope[index] += 1
index return envelope
エンベロープの区間の長さと数はMFCCの対応するパラメータと合わせました。区間の長さは
winstep=0.01
、区間の数は n_frame=100
です。
ピッチは python_speech_features.sigproc.framesig
で区切った各フレームから次の手順で取り出しました。
手順 5. のソートがないと似たような音でまとまりにくくなります。推定したピッチは似たような値で順番が入れ替わっていることが多いので、ソートなしだとクラスタリングで計算される距離が大きくなることが予想されます。
CMND type II も試したのですが、テストに使った小さなデータセットでは NSD type II のほうが良い結果が出ました。
係数 k の値ではテストデータでの結果は変わりませんでした。
エンベロープ、MFCC+デルタ、ソートしたピッチは別物なのでクラスタリングを分けることにしました。以下はエンベロープ -> MFCC+デルタ -> ソートしたピッチの順でクラスタリングした結果の一例です。クラスタリングの階層構造がなんとなく見て取れるかと思います。 Outlier と判断されたクラスタは番号が飛んでいます。
cluster/test2
├── envelope1
│ ├── mfccdelta2
│ │ ├── pitch1
│ │ │ ├── fast_vib2.wav
│ │ │ ├── high_vib.wav
│ │ │ ├── klang4.wav
│ │ │ ├── noisy1.wav
│ │ │ └── vib2.wav
│ │ └── pitch_outlier
│ │ └── fast_vib1.wav
│ └── mfccdelta_outlier
│ ├── ping1.wav
│ └── ping2.wav
├── envelope2
│ ├── mfccdelta0
│ │ ├── pitch1
│ │ │ ├── ding.wav
│ │ │ ├── fm.wav
│ │ │ └── noisy4.wav
│ │ └── pitch_outlier
│ │ └── sweep_to_high.wav
│ └── mfccdelta_outlier
│ └── vib1.wav
├── envelope3
│ ├── mfccdelta0
│ │ ├── pitch1
│ │ │ ├── klang1.wav
│ │ │ ├── klang3.wav
│ │ │ └── noisy3.wav
│ │ └── pitch_outlier
│ │ ├── mid_crack.wav
│ │ └── noisy5.wav
│ └── mfccdelta_outlier
│ ├── klang2.wav
│ └── vib3.wav
└── envelope_outlier
└── noisy2.wav
クラスタリング手法は sklearn.cluster.AffinityPropagation
を使いました。エンベロープのクラスタリングでは damping=0.9
、MFCC+デルタのクラスタリングでは damping=0.6
、ソートしたピッチのクラスタリングでは damping=0.5
としました。
次の動画はクラスタリングの結果です。音は0.25秒間隔で再生されます。残りの0.75秒は次の音と重なっています。
多少は似たような音が集まっている気がします。
ラベルのついていないデータセットでのクラスタリングは無謀です。
今回使ったエンベロープとMFCC+デルタはそういう無茶でもそれなりになんとかしてくれました。エンベロープとMFCC+デルタによるクラスタリングではビブラートのような細かいピッチの揺れやカラーノイズはうまく区別できていないように感じたのでピッチ推定に注目したのですがあまり大きく改善したようには感じませんでした。
Google AI Experiments の The Infinite Drum Machine で使われていた手法を参考にしたので紹介します。 The Infinite Drum Machine は Kyle Mcdonald さんの AudioNotebooks の視覚化です。クラスタリングはインターフェイス上で音を表す点の色付けに使っているだけのようです。
skimage.measure.block_reduce
で正規化したSTFTの結果を縮小。block_reduce
は画像の特徴抽出で使われる関数です。STFTの結果は2次元なので、画像とみなして処理できるというのは面白いです。