クーの自由研究

マスターの かえるのクーは8年の任期を終え消失しました。クーⅡ世の中の人絶賛募集中です。(特にオリオン座&プレイアデス方向の方は優遇します)助手がメイド喫茶から生還し、リハビリを兼ねて復帰しています。

計算機音楽の自由研究(準備:その2.6)~サウンド学習実験用のデータベースを考える

実験用のデータについて

MNIST*1の楽器版のような機械学習用の(音の)データセットを探しているのですが、ボクの実験に適したモノは見当たりませんでした。MNISTの音版/楽器版データベースは誰もが作ろうと思うはずで、どこかに必ずあるはずですが、見つかりません。

f:id:np2LKoo:20170509020022p:plain


YouTube-8M Dataset でPIANOのカテゴリがありますが、基本ビデオなので、ボクの実験には荷が重すぎます。
そのうち、ビデオから音だけを抽出してデータベース化することもやってみたいですが、時間がかかりそうなので自前で取り繕ってみます。MIDIを使いサンンプラーや各種VSTから音を拾っていきます。(6月末まで反省キャンペーン中です。内容を順次リファインします。)

フォーマット再考

学習する音のサンプルとしては楽器音の立ち上がり300mSec(0.3秒)を考えました。それだけあれば、音の学習には十分だと判断しました。ところが、ジブンで聞く分にはちょっと短いなと思い改めました。従来の計画よりデータが倍になりますが、0.6秒程度にしてみます。
以下のスペックでデータを準備してみます。
・1音32768Sample(48KHzサンプルで約0.68秒)
・Bit数は16Bit(演算時は内部で32Bitにアップサンプリングするつもりなので、入力・出力としては16BitでOKと判断)
・ファイル形式はベタでwav形式にします。多数のこまかな音を扱うには無圧縮でwavでいいのでは?と考えました。(容量が大きいですが。。。)
0.68秒*10000個=>約1.9時間:625MBなので、ネットにアップするには大きいですが、実験するには許容範囲です。

MNISTのデータベースの種類とバリエーションは大変すぐれていると思っています。これにならい(倣う必要もないのですが)10種類の楽器の単音を1万件ずつ学習(Train)用データとし、テスト用の1万件データを含め、6.9GBくらいになる計算です。(ちなみにMNIST 数字データは非圧縮で学習用(Train)が約45MB,テスト用が7.7MBあわせて約53MB程度です。これに比べると大きすぎだとは思いますが、上記のとおりすすめてみて不都合なら調整しようと思います。)

いきなりフルで作成するのはきついので、とりあえずピアノの最低音から最高音までの88音を作って評価してみます。(他2~3種練習で作成してみます)

ひさびさのソース登場です

久しぶりにPythonをさわりましたが、Pythonを完全に忘れていることはさほど驚きませんでした。いつものようにいろいろなページを参考にして作成しました。いつもながらネットにサンプルソースをアップされる方々には感謝します。ジブンのページもかなり参考になりました。記録は大事だと改めて思いました。

#!/usr/bin/env python
# -*- coding: utf-8 -*-
import pyaudio
import wave
import time
import pygame.midi
import numpy as np
# ========================================================================
# PythonMidi音源を鳴らして、オーディオインターフェースから出た音を
# サウンド機械学習用のデータベースとして編集する練習プログラム
# ========================================================================
#
# リアルタイムのMIDI制御なのでPygameを使用します。
pygame.init()
pygame.midi.init()
pygame.fastevent.init()
event_get = pygame.fastevent.get
event_post = pygame.fastevent.post
input_id = pygame.midi.get_default_input_id()
print("input MIDI:%d" % input_id)
midi_in = pygame.midi.Input(input_id)
midi_out = pygame.midi.Output(4, latency = 0) #環境にあわせて調整します。

#各種パラメータを定義します。48KHzサンプリングで32768サンプルに編集します。
INPUT_DEVICE_NO = 2 #Audioの入力です。環境にあわせて調整します。ボクの環境では現在これです。
FORMAT = pyaudio.paInt16
CHANNELS = 1 #モノラルなので1チャンネルです
SAMPLING_RATE = 48000 #48KHzサンプリングにします。
CHUNK = 2 ** 11
RECORD_SECONDS = 1.0 #1秒間録音して、そこからデータを切り出します
TOTAL_SAMPLES = 32768 # 0.6826mSec at 48K sampling
OROGINAL_SAMPLES = 32000 #音の立ち上がりからの原音のサンプル幅
DECAY_SAMPLES = 752 #減衰するサンプル幅
NO_TONE_SAMPLES = TOTAL_SAMPLES - (OROGINAL_SAMPLES + DECAY_SAMPLES) #無音のサンプル幅
MIDI_NOTE_START = 21 #ピアノの一番低い音 A0
MIDI_NOTE_END = 108 #ピアノの一番高い音 C8
MIDI_VOLUME = 127
THRESHOLD_AMPLITUDE = 10 #多少ノイズがのることを想定し、音開始の振幅閾値を設定します。実際に録音した音を使う場合は環境雑音にあわせて調整します。
WAVE_OUTPUT_FILENAME = "piano01.wav" #このファイル名で出力します。
audio = pyaudio.PyAudio() #クラスのショートカットです
recFrames = [] #録音フレーム用 空リストに初期化します。
editFrames =[] #編集フレーム用
totalFrames = [] #最終出力フレーム

# オーディオ録音用のコールバック定義です
def callback(in_data, frame_count, time_info, status):
recFrames.append(in_data) #録音の本体部分です。
return (None, pyaudio.paContinue)

# frameをwavファイルに出力する定義です
def waveWrite(fileName, frames):
waveFile = wave.open(fileName, 'wb')
waveFile.setnchannels(CHANNELS)
waveFile.setsampwidth(audio.get_sample_size(FORMAT))
waveFile.setframerate(SAMPLING_RATE)
waveFile.writeframes(b''.join(frames)) # 配列をバイナリ列に変換します。
waveFile.close()

# フレームにサンプリング録音したデータを所定のフォーマットに編集する定義です
def getEditFrames(frames):
frames = b''.join(frames) #バイナリ列に変換します。
frames = np.frombuffer(frames, dtype="int16") # 16ビットデータの配列に編集します。
# 音のフレーム内で出始めを探します(振幅閾値を超えれば音が開始したと判断します)
for i in range(frames.size):
if (frames[i] < -THRESHOLD_AMPLITUDE or frames[i] > THRESHOLD_AMPLITUDE):
iStart = i
break
print("StartFrameNo=" + "{0:d}".format(iStart)); # 確認用にスタートのフレーム位置を出力します。
returnFrames = frames[iStart - 1:TOTAL_SAMPLES + iStart - 1] # 閾値超の1個前を音開始とし、所定のサンプル幅を切り出します。
returnFrames.flags.writeable = True #編集可能に設定を変えます。
# 減衰部分の設定(DECAY_SAMPLESの幅をかけて、振幅ゼロにまでリニアに減衰させます)
for i in range(DECAY_SAMPLES):
returnFrames[OROGINAL_SAMPLES + i] = returnFrames[OROGINAL_SAMPLES + i] * (DECAY_SAMPLES - i) / DECAY_SAMPLES
# 無音部分の設定(NO_TONE_SAMPLES幅を無音にします)
for i in range(NO_TONE_SAMPLES):
returnFrames[OROGINAL_SAMPLES + DECAY_SAMPLES + i] = 0
#最大振幅が符号付き16bitの最大値となるように振幅を調整(最大化)します。リミッタをかけないマキシマイザです。
nMax = max(max(returnFrames), -min(returnFrames)) #最大振幅を取得します。(プラス値)
for i in range(TOTAL_SAMPLES):
returnFrames[i] = returnFrames[i] * 32767.0 * 0.80 / nMax #全般に対して振幅調整します。ぎりぎりにすると再生ボリュームによっては振幅が飽和するので、お好みで0.80かけています。
return returnFrames

#オーディオをオープンしStreamオブジェクトを獲得します。「音のストリーム」はcallback定義につながります。
stream = audio.open(format=FORMAT, channels=CHANNELS,
rate=SAMPLING_RATE, input=True,
input_device_index=INPUT_DEVICE_NO,
frames_per_buffer=CHUNK,
stream_callback=callback)

print("recording...")
#MIDIを順次鳴らしてフレームを所定のフォーマットに編集します。
#所定のMIDIチャンネルで実音がでるように音源を立ち上げておきます。
#(VSTホスト+VSTi もしくはDTMソフト:排他的にオーディオインターフェースを取得するものは
# Pythonからの割り込みを受け付けないかもしれないので、そのときはVSTホストを使用します。)
for i in range(MIDI_NOTE_END - MIDI_NOTE_START + 1):
recFrames = []
time.sleep(0.5) #ASIOを使わなかったので、充分な時間をあけています。(タイムラグがあるため)
stream.start_stream() #ストリームオブジェクトをスタートさせます(録音開始)
midi_out.write([[[144, MIDI_NOTE_START + i, MIDI_VOLUME, 0], 0]]) #所定の音のMIDI出力
time.sleep(RECORD_SECONDS) #この時間が実際の録音時間に相当します。
midi_out.write([[[144, MIDI_NOTE_START + i, 0, 0], 0]]) #所定の音をOFFします。
time.sleep(1.0) #同じく次の音まで間をあけます。Decay音も(使いませんが)一旦録音します。
print("finished recording:" + "{0:03d}".format(MIDI_NOTE_START + i))
stream.stop_stream() #ストリームオブジェクトを終了します(録音終了)
fileName = "Check" + "{0:03d}".format(MIDI_NOTE_START + i) + ".wav" # for Check(1音ずつの確認用出力です)
time.sleep(0.5)
editFrames = getEditFrames(recFrames) #所定の形式にフレームを編集します。
waveWrite(fileName, editFrames) #編集後の1音を出力します。(確認用)
totalFrames.extend(editFrames) #学習用データはこれです。順番につなげていきます。

waveWrite(WAVE_OUTPUT_FILENAME, totalFrames) #学習用データをファイルに出力します。

#あと片付けはちゃんと行いましょう。
midi_in.close()
midi_out.close()
stream.close()
audio.terminate()

pygame.midi.quit()
pygame.quit()

ソースの貼り方はいまだによくわかりません。見にくくてすみません。((追記)インデントが分かりにかったので少し調整しました)練習用のプログラムで、クラスもなければ、main()もありません。あしからず。


(追記)AudioのポートとMIDIのPortや接続に関して分かりにくかったので追記します。確認は(その5)と(その6)に載っているプログラムで確認ください。ボクのマシンの環境例です。

Audioの一覧の出し方(Python プログラム)は(その5)参照。文字化けはまんま貼ってますがご愛敬ということで;。

0 Microsoft サウンド マッパー - Input
1 ƒ}ƒCƒN (US-2x2)
2 ƒ}ƒCƒN (Mixing Driver 2 for US-
3 MAIN (QUAD-CAPTURE)
4 3-4 (QUAD-CAPTURE)
5 1-2 (QUAD-CAPTURE)
6 ƒ}ƒCƒN (Mixing Driver 1 for US-
7 Microsoft サウンド マッパー - Output
8 スピーカー (Mixing Driver 1 for
9 スピーカー (US-2x2)
10 3-4 (QUAD-CAPTURE)
11 1-2 (QUAD-CAPTURE)
12 デジタル オーディオ (S/PDIF) (H
13 スピーカー (Mixing Driver 2 for
14 デジタル オーディオ (S/PDIF) (H

MIDIの一覧の出し方(Python プログラム)は(その6)参照

get_default_input_id:1
get_default_output_id:0
No:(interf, name, input, output, opened)
0:(b'MMSystem', b'Microsoft MIDI Mapper', 0, 1, 0)
1:(b'MMSystem', b'QUAD-CAPTURE', 1, 0, 0)
2:(b'MMSystem', b'US-2x2', 1, 0, 0)
3:(b'MMSystem', b'Microsoft GS Wavetable Synth', 0, 1, 0)
4:(b'MMSystem', b'QUAD-CAPTURE', 0, 1, 0)
5:(b'MMSystem', b'US-2x2', 0, 1, 0)

Process finished with exit code 0

です。US-2x2が増えたのでいっぱいでてきます。(TASCAMオーディオインターフェースUS-2x2はubuntuで使いたいので買いましたがまだ実験できていません)

(1)PythonプログラムでMIDI情報をMIDI OUT No.4のQUAD-CAPTUREへ出力

(2)QUAD-CAPTUREのMIDI-OUTをQUAD-CAPTUREのMIDI-IN実接続MIDIケーブル接続)している。:内部でソフトディスパッチできるサウンドカードは設定ででつなげられると思いますが、実接続がてっとり早いです。

(3)QUAD-CAPTUREのMIDI-INからVST HOST(またはDTMソフト)につないでいる(ソフトの入力設定)

(4)VSTから出た音は Audio No.2のMixing Driver 2 for US-2x2 から出力する。

(5)PythonプログラムでAudio No.2の音を録音する。

(追記ここまで)


pythonMIDIを制御して音をだして、録音して編集(所定の長さのサンプリング切り出し、減衰・無音区間設定&音量最大化)してファイル保存しています。

このプログラムで出力した音(練習版データベース)はこんな感じです。

すべて32768サンプルにしているので、読み込んで配列変換して範囲指定すれば、特定の音が簡単に取り出せます。

使用した音は以下です。

以前紹介した Keppy-Steinway-Piano *2をTX16WxというサウンドフォントプレーヤーのVSTiに読み込み、SAVIHost(Standalone VSTi Host)というホストプログラムに乗せて動作させています。(さらに追記)KeppyさんのBlack MIDIはたとえばこちら↓。

www.youtube.com(さらに追記ここまで)

(追記)間違ってVSTの音ではなく、Microsoft サウンド マッパーのWindowsの音を録音していました。Steinwayにしては音がしょぼいと思った方さすがです。ボクは間違いに気が付きませんでした。比較も兼ねて追加で貼ります。

音がしょぼいですが、ギターとバイオリンも貼ってみます。

(追記ここまで)

PythonからMIDIやSoundを扱う方法は去年の実験準備も参照ください。


ピアノはこれで88音ですが、がんばってピアノで1万音あつめます。114種類集めれば88音×114種類=10032音で一万超えにできます。幸い音源(ハード、ソフトとも)をもっている人が周りに多くいるのでなんとかなると思います。

最終的には10種類くらいの楽器でデータベースをつくってMNISTみたいなことをやるつもりです。

まとまらない

準備しなければならないことがまだまだあるのですが、のんびり進みます。

プロモーションに協力

プロモーションのかいあって(あるいは努力むなしく)先週から1アクセス増えました。めざせ!20ヒット!! ぜひご覧ください。

f:id:np2LKoo:20170601000756p:plain

視聴キャンペーンは終了しました。視聴いただいたた方、どうも有難うございました。

Koo'sキッチン 今回は飲み物です

「三ツ矢 新搾り ぶどう」「三ツ矢 新搾り もも」はどちらもたいへんおいしく頂いております。最近はおいしい飲み物がたくさんでていますが、個人的な好みとしてはさっぱりして、自然な果実の香りが生きており、着色していないことを好感して、「三ツ矢 新搾り」が見事殿堂入りを果たしました。おめでとうございます。

*1:去年はずっとMNISTやってました。興味のある方は去年の実験を参照ください。よく見る数字の学習です。

*2:Keppyさんは最近ディスクがクラッシュして落ち込んでいました。