hama-matcha’s blog

思ったことを書き残すモノ

ピアノの音判別をしてみた

はじめに

今回はクラス分類の練習として、ピアノの単音(ドとレ)の判別器を作ったのでいろいろと書き残していきます。

作成したもの

GitHubにおいてます。

必要そうな説明

データセット(zipで固めておいています)

  • ドとレの音源
    生成方法 : Garage Band の Steinway Grand Piano
    高さ: C0~C5
    ベロシティ(音の強弱) : 86, 96, 106
    データ数 : 18個ずつ(36個)
    学習と評価比率 : 学習:評価 = 9:1

前処理

LibROSAを用いてmfcc(メル周波数ケプストラム係数)を抽出
抽出の際設定はデフォルトのままなのでサンプルレートは22050Hz
学習で使用する音声データからmfccとlabel(0がド, 1がレ)を抽出

import scipy.io.wavfile as wav
import librosa
from sklearn.svm import SVC
import numpy

def get_mfcc(fname):
    y, sr = librosa.load(fname)
    return librosa.feature.mfcc(y, sr)

if __name__  ==  '__main__':

    piano_notes = ['do', 're']
    piano_sounds = list(range(16))

    piano_note_training = []
    piano_sound_training = []

    for piano_note in piano_notes:
        print('\nReading data of {}...\n'.format(piano_note))
        for piano_sound in piano_sounds:
            print('{}/{}{}.wav'.format(piano_note, piano_note, piano_sound))
            
            # get mfcc
            mfcc = get_mfcc('{}/{}{}.wav'.format(piano_note, piano_note, piano_sound))
            piano_sound_training.append(mfcc.T)
            
            label = numpy.full((mfcc.shape[1], ), 
                               piano_notes.index(piano_note), dtype=numpy.int)
            piano_note_training.append(label)
    
    piano_sound_training = numpy.concatenate(piano_sound_training)
    piano_note_training = numpy.concatenate(piano_note_training)
    print('done')

学習と評価

SVCを使用し学習をさせる。(今回は線形ですが非線形の学習にも対応したいため)
残りの評価用データを用いて判定
チューニングは

C = 1 # デフォルト
gamma = 1e-3 

となった。

# gammaの値を変更 * 1e-3より正しく分けられている
svc = SVC(gamma = 1e-3)
svc.fit(piano_sound_training, piano_note_training)
print('Learning Done')

piano_sounds_test = list(range(16,18))

for piano_note in piano_notes:
    for piano_sound in piano_sounds_test:
        print('piano test sound {}{}'.format(piano_note, piano_sound))
        mfcc = get_mfcc('{}/{}{}.wav'.format(piano_note, piano_note,piano_sound))
        prediction = svc.predict(mfcc.T)
        counts = numpy.bincount(prediction) 
        result = piano_notes[numpy.argmax(counts)] # 音程の判定
        original_title = '{}'.format(piano_note)
        print('original note is {}. Prediction of note is {}'.format(original_title, result))

評価結果

Learning Done
piano test sound do16
original note is do. Prediction of note is do
piano test sound do17
original note is do. Prediction of note is do
piano test sound re16
original note is re. Prediction of note is re
piano test sound re17
original note is re. Prediction of note is re

ちゃんと分けることが出来ました!

今後の課題

今回はデータセットを学習:評価のみで分けていましたがハイパーパラメータのチューニングも必要なので学習:チューニング:評価で分ける必要がありそうです。
次は[ド、レ、ミ、ファ、ソ、ラ、シ]のクラス分類に挑戦してみます。
データ数も足りないのでデータセットのとり方も調整が必要ですね。
機械学習してるのかな〜っと思いながらまだ出来てないので頑張ります・・・