クーの自由研究

マスターのかえるのクーは、弟子達の召喚術により新たな依り代を得てⅡ世として復活しました。

Atomで夜ごと心から辟易する母なるかえるのために

今回はデバッグに関するお題です

こんにちは、こんばんわ、かえるのクーです。
危険な音」をきいて暗黒面に堕ちましたが、生還してきました。

f:id:np2LKoo:20171015125120p:plain f:id:np2LKoo:20171015125128p:plain

へんなタイトルですみません。(細かすぎて伝わらないネタ系です)

Atomマシンでのデバッグをなんとかする

Atomマシンで機械学習の勉強ををされている方も多いと思いますが、いかんせん非力です。遅いだけなら待っているだけでいいのですが、タイムアウトが多発してデバックになりません。
タイムアウトを伸ばすとクリックするごとに10分以上まつはめになります。
お気楽なボクでもこれには耐えられません。

f:id:np2LKoo:20171015140601p:plain

そこで、デバック用に変数の内容をレポートするロジックをつくりました。
Atom機でデバッグタイムアウトに悩む、同志のかえるさんやおたまじゃくしさんのお役にたてるかもしれないので、恥ずかしいソースながら貼ってみたいと思います。
汎用的に作ったつもりですが、今調整しているプログラムがメインターゲットなので、機能の過不足はあるかもしれません。
用途に適さない方は、いろいろ改変してお使いください。

こんな方におすすめ

Atomデバッグに悩む方
デバッグタイムアウトになり、変数の内容が見れない方
・多量の配列や変数の内容の概要を確認したい方
・配列を多次元に掘り下げ、収集がつかなくなったしまった方

主作用(機能と効用)

機能は見てのとおり、変数を引数に入れて呼ぶだけなシンプルなものです。
工夫したところは、以下の感じです。

・変数をセットしただけで "変数=" の部分を表示する。
・変数の型を表示する。
・配列はPytorch「風」に配列の大きさや内容を示す。
・多量のデータを扱うことがあるので、配列はデータの最初と最後くらいだけを表示する。

副作用

チェック機能やデバッグ機能に頼りすぎると、いきあたりばったりのコーディングをする生活習慣に陥る可能性があります。過剰摂取と長期服用による依存には十分ご注意ください。
日頃より、シンプルな設計とリファクタリングを怠らず、デバッグ機能に頼るのは必要最小限になるよう、心がけましょう。

有効成分

inspect:コードの調査を行うためのライブラリです。
re:正規表現を使えるようにするライブラリです。。
numpy:標準的な各種処方を行うライブラリです。

用法と用量

Atom、(もしくはCeleron,Semron)機以外の高スペックCPUの方はご利用に注意ください。おそらく、標準のデバッガをうまく使用したほうが幸せと思われます。


なお、Pytorchについてはガリガリ (id:s0sem0y)様の記事を読ませていただいております。わかりやすい説明に感謝です。

(あくまで個人の見解です)

ボクはこの機能でかなり(というか超絶)デバッグが捗るようになりました。
Atom機でPythonデバッグに悩んでいるすべての方におすすめします。

ソース

#!/usr/bin/env python
# -*- coding: utf-8 -*-
import numpy as np
import inspect
import re


class KooChecker():
def __init__(self):
pass

def rpt_obj(self, *args):
out = ''

back_frame = inspect.currentframe().f_back
back_code = inspect.getframeinfo(back_frame).code_context[0].replace(" ", "")
pos = back_code.find("#")
if pos != -1:
back_code = back_code[:pos]
arg = re.search(r"\((.+)\)", back_code).group(1)
# 呼び出し元関数の引数の変数名を取得。
arglist = arg.split(',')
ii = 0
for obj in args:
target = arglist[ii]
ii += 1
if isinstance(obj, bool) or \
isinstance(obj, int) or \
isinstance(obj, float) or \
isinstance(obj, complex):
out += '|' + target + ' = ' + str(obj) + ' ' + self.chktype(obj)
elif isinstance(obj, set) or \
isinstance(obj, frozenset):
out += '|' + target + ' = ' + self.rptset(obj) + ' ' + self.chktype(obj)
elif isinstance(obj, str):
out += '|' + target + ' = ' + self.rptstr(obj) + ' ' + self.chktype(obj)
elif isinstance(obj, bytes) or\
isinstance(obj, bytearray):
out += '|' + target + ' = ' + self.rptbytes(obj) + ' ' + self.chktype(obj)
elif isinstance(obj, list) or \
isinstance(obj, tuple) or \
isinstance(obj, np.ndarray):
out += '|' + target + ' = ' + self.rptlist([], obj) + '\n' + self.chktype(obj)
elif isinstance(obj, dict):
out += '|' + target + ' = ' + str(obj) + '\n' + self.chktype(obj)
else:
out += '|' + target + " =(DisplayNotImpliment) " +self.chktype(obj)
print(out)

def rptstr(self, obj):
strlen = len(obj)
# 表示内容を40文字までに抑制する
if strlen <= 40:
rtn = '"' + obj + '"'
else:
rtn = '"' + obj[0:20] + '...' + obj[strlen - 20:strlen] + '"' + "(len=" + str(strlen) + ")"
return rtn

def rptbytes(self, obj):
byteslen = len(obj)
# 表示内容を40byteまでに抑制する
if byteslen <= 40:
rtn = str(obj)
else:
rtn = str(obj[0:20]) + '...' + str(obj[byteslen - 20:byteslen]) + "(len=" + str(byteslen) + ")"
return rtn

def rptset(self, obj):
setlen = len(obj)
# 表示内容を20項目までに抑制する
if setlen <= 20:
rtn = str(obj)
else:
setstr = str(obj)
ss = (setstr.rsplit(",", setlen - 10))[0]
se = (setstr.split(",", setlen - 10))[-1]
rtn = ss + ', ...' + se + "(len=" + str(setlen) + ")"
return rtn

def rptlist(self, arglevel, obj):
out = ''
datalen = len(obj)
if datalen == 0:
out = '[]'
elif datalen <= 8:
for i in range(datalen):
if isinstance(obj[i], list) or \
isinstance(obj[i], tuple) or \
isinstance(obj[i], np.ndarray):
level = arglevel.copy()
level.append(i)
out += self.rptlist(level, obj[i])
else:
out += str(obj[i]) + ' '
else:
for i in (0, 1, 2, 3, -4, -3, -2, -1):
if isinstance(obj[i], list) or \
isinstance(obj[i], tuple) or \
isinstance(obj[i], np.ndarray):
if i in (0, 1, -2, -1):
level = arglevel.copy()
if i >= 0:
level.append(i)
else:
level.append(datalen + i)
out += self.rptlist(level, obj[i])
if i != -1:
out += '\n'
elif i in (2, 3, -4, -3):
pass
if i == 2:
out += '...\n'
else:
if i == 0:
out += str(arglevel) + '= '
out += str(obj[i]) + ' '
if i == 3:
out += ' ... '
elif i == -1:
out += '(len=' + str(datalen) + ')'
return out

def chktype(self, obj):
rtn = ''
if isinstance(obj, list) or \
isinstance(obj, tuple) or \
isinstance(obj, np.ndarray):
if len(obj) == 0:
rtn = self.chktype_name(obj) + ':0'
else:
rtn = self.chktype_name(obj) + ':' + str(len(obj)) + '*' + self.chktype(obj[-1])
else:
rtn = self.chktype_name(obj)
return rtn

def chktype_name(self, obj):
name = str(type(obj)).replace('<class ', '').replace('>', '').replace("'", "")
name = '<' + name + '>'
return name

用法

if __name__ == '__main__':
kc = KooChecker()
a = 1
b = 12.56
c = False
d = 2 + 3j
e1 = ''
e2 = "ABCDE123456" * 100
f1 = b''
f2 = b'A' * 100
g = bytearray(b'ABC' * 100)
h = set(range(100))
i = frozenset(range(100))
j1 = []
j2 = [range(100)]
j3 = [[range(10)],[range(20)],[range(30)]]
j4 = list(np.arange(10 ** 4).reshape((10, 10, 10, 10)))
j5 = [[list(np.arange(10 ** 2).reshape((10, 10)))] * 10] * 10
k1 = ()
k2 = (1,2,3,4,5,6,7,8,9,0)
k3 = (((1,2,3),(1,2,3),(1,2,3),(1,2,3),(1,2,3),(1,2,3),(1,2,3),(1,2,3),(1,2,3)) * 10)
l = {'one': 1, 'two': 2, 'three': 3}
kc.rpt_obj(a,b,c,d,e1,e2)
kc.rpt_obj(f1,f2,g,h)
kc.rpt_obj(i)
kc.rpt_obj(j1)
kc.rpt_obj(j2)
kc.rpt_obj(j3)
kc.rpt_obj(j4)
kc.rpt_obj(j5)
kc.rpt_obj(k1)
kc.rpt_obj(k2)
kc.rpt_obj(k3)
kc.rpt_obj(l)
kc.rpt_obj({'key': 100})

コンソール出力

|a = 1 <int>|b = 12.56 <float>|c = False <bool>|d = (2+3j) <complex>|e1 = "" <str>|e2 = "ABCDE123456ABCDE1234...CDE123456ABCDE123456"(len=1100) <str>
|f1 = b'' <bytes>|f2 = b'AAAAAAAAAAAAAAAAAAAA'...b'AAAAAAAAAAAAAAAAAAAA'(len=100) <bytes>|g = bytearray(b'ABCABCABCABCABCABCAB')...bytearray(b'BCABCABCABCABCABCABC')(len=300) <bytearray>|h = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, ... 90, 91, 92, 93, 94, 95, 96, 97, 98, 99}(len=100) <set>
|i = frozenset({0, 1, 2, 3, 4, 5, 6, 7, 8, 9, ... 90, 91, 92, 93, 94, 95, 96, 97, 98, 99})(len=100) <frozenset>
|j1 = []
<list>:0
|j2 = range(0, 100)
<list>:1*<range>
|j3 = range(0, 10) range(0, 20) range(0, 30)
<list>:3*<list>:1*<range>
|j4 = [0, 0, 0]= 0 1 2 3 ... 6 7 8 9 (len=10)
[0, 0, 1]= 10 11 12 13 ... 16 17 18 19 (len=10)
...
[0, 0, 8]= 80 81 82 83 ... 86 87 88 89 (len=10)
[0, 0, 9]= 90 91 92 93 ... 96 97 98 99 (len=10)
[0, 1, 0]= 100 101 102 103 ... 106 107 108 109 (len=10)
[0, 1, 1]= 110 111 112 113 ... 116 117 118 119 (len=10)
...
[0, 1, 8]= 180 181 182 183 ... 186 187 188 189 (len=10)
[0, 1, 9]= 190 191 192 193 ... 196 197 198 199 (len=10)
...
[0, 8, 0]= 800 801 802 803 ... 806 807 808 809 (len=10)
[0, 8, 1]= 810 811 812 813 ... 816 817 818 819 (len=10)
...
[0, 8, 8]= 880 881 882 883 ... 886 887 888 889 (len=10)
[0, 8, 9]= 890 891 892 893 ... 896 897 898 899 (len=10)
[0, 9, 0]= 900 901 902 903 ... 906 907 908 909 (len=10)
[0, 9, 1]= 910 911 912 913 ... 916 917 918 919 (len=10)
...
[0, 9, 8]= 980 981 982 983 ... 986 987 988 989 (len=10)
[0, 9, 9]= 990 991 992 993 ... 996 997 998 999 (len=10)
[1, 0, 0]= 1000 1001 1002 1003 ... 1006 1007 1008 1009 (len=10)
[1, 0, 1]= 1010 1011 1012 1013 ... 1016 1017 1018 1019 (len=10)
...
**(略します)**
...
[9, 9, 8]= 9980 9981 9982 9983 ... 9986 9987 9988 9989 (len=10)
[9, 9, 9]= 9990 9991 9992 9993 ... 9996 9997 9998 9999 (len=10)
<list>:10*<numpy.ndarray>:10*<numpy.ndarray>:10*<numpy.ndarray>:10*<numpy.int32>
|j5 = [0, 0, 0]= 0 1 2 3 ... 6 7 8 9 (len=10)
[0, 0, 1]= 10 11 12 13 ... 16 17 18 19 (len=10)
...
**(略します)**
[9, 8, 9]= 90 91 92 93 ... 96 97 98 99 (len=10)
[9, 9, 0]= 0 1 2 3 ... 6 7 8 9 (len=10)
[9, 9, 1]= 10 11 12 13 ... 16 17 18 19 (len=10)
...
[9, 9, 8]= 80 81 82 83 ... 86 87 88 89 (len=10)
[9, 9, 9]= 90 91 92 93 ... 96 97 98 99 (len=10)
<list>:10*<list>:10*<list>:10*<numpy.ndarray>:10*<numpy.int32>
|k1 = []
<tuple>:0
|k2 = []= 1 2 3 4 ... 7 8 9 0 (len=10)
<tuple>:10*<int>
|k3 = 1 2 3
1 2 3
...
1 2 3
1 2 3
<tuple>:90*<tuple>:3*<int>
|l = {'one': 1, 'two': 2, 'three': 3}
<dict>
|{'key':100} = {'key': 100}
<dict>
お出かけ先で「夜ごと太るボクのために」

もうすこしダイエットをこころがけます。

おたまじゃくしさんのおーとまとん~インストール編

おたまじゃくしさんのおーとまとん~インストール編

 さらにおたませしました!
全国の「おたまじゃくし」のみなさん。そして、全国の小中学生のみなさん。おとうさん、おかあさんからお許しはでましたか?
プログラムのインストール(プログラムを入れて動くようにすること)をおこなってみましょう。ちょっと長いけどがんばってみましょう。

f:id:np2LKoo:20170810000944j:plain

Windows 10というOS(オペレーティングシステム)にソフトウェアを入れるのを例(れい)にして、説明(せつめい)します。
Mac(まっく)の人やLinux(りなっくす)の人やD-Wave(でぃーうぇーゔ)というマシンやOSを使っている人は申(もう)し訳(わけ)ありませんが他のブログを探(さが)してみてください。

ソフトウェアをダウンロードしたり、インストールをするときのお約束(やくそく)。

はじめて、ネットを冒険(ぼうけん)する人は長いけど必ず読んでね。聞いたことある人は、ここは読まなくてもいいです。 

・ソフトを入れるとき(最初はわからなくても)説明(せつめい)をひとおりよく読もう。
・間違(まちが)ってクリックして「ほしそうな」ところはクリックしない。(そんなふうにつくってあるページもあります)
・英語(えいご)は「言葉(ことば)」だと思うと「読(よ)めないとわからない」感(かん)じがしてしまいますが、「記号(きごう)」だと思ってしまば、あまりこわくなくなるかもしれません。Downloadだとか Install という文字を探したりや、他の日本語のインストールの説明をよく読みましょう。
・なんでも「OK」していけばいいわけではありません。ある程度(ていど)は何を操作(そうさ)しているのか、分かって(または想像(そうぞう)しながら)やりましょう。
・何か「やばいんじゃないかな」と感じたらそれ以上操作(そうさ)せず:閉じることもしない!おうちの人に相談(そうだん)しましょう。

(なにかいっぱい起動(きどう)したとか、

音が鳴(な)りやまないとか、

赤色のやばそうや場面がでたり、

鍵マークや、暗号化を解除(かいじょ)するにはお支払いくださいが出たとか、

何度閉じてもメールアドレスや名前を入れろといわれたり、

「おめでとうございます。1000万円当選しました。下のURLをクリックしてね。」と表示されたり。

「ウィルスに感染しました。下のURLでいますぐ確認できます。」

「不正にコピーされたプロダクトを使用されています。下のURLで詳細確認ができます。」

とにかくやばそうなところはクリックしない。

ウィルス検出(けんしゅつ)ソフトが何か表示した場合もそこで操作をやめて(閉じずに)、かならずおうちの人に相談(そうだん)しよう。

(ウィルス検出ソフトを偽装(ぎそう:いんちきしてまねること)こわがらなくてもいいけど、本当にこんなふうにでるあぶないところもネットの上にはあります。

(ふつうは手順にしたがっていればこんなことになることはないので、あんしんしてね)

もしおかしくても、最初の対応(たいおう)を間違(まちが)えなければ、ほとんどの場合問題ありません)注意(ちゅうい)は必要(ひつよう)だけど怖(こわ)がることはありません。


・(ふつうは)小中学生のひとは「じぶんの名前や生年月日(せいねんがっぴ)、住所(じゅうしょ)、電話番号(でんわばんごう)、メールアドレス」などを入れるところがあっても入れてはいけません。

どうしてもいれないといけないときはお家(うち)の方と相談しましょう。

・ふつうのパソコンにははいっていると思うけど、ネットをいろいろ探検(たんけん)したり便利(べんり)そうなソフトにチャレンジするときは「ウィルス検出(けんしゅつ)ソフト」がはいっている必要があります。なかには「期限(きげん)切(ぎ)れ」のまま使っているひともいるかもなので、注意しよう。

操作手順(てじゅん)を説明しているとおりにやっていればまず、このようになることはありません。

操作に慣(な)れてくると、どこでもクリックしてチャレンジしてみたくなるので、そのころになると改めて注意が必要です。

・おとうさん、おかあさんが同じパソコンで「ビットコイン」などのネット上の通貨(つうか:おかね)を扱っている場合は、基本的に小中学生の方はそのパソコンで操作しないでください。

(その場合、おとうさん、おかあさんが「つかっちゃだめ」というと思います。)

 anaconda(Pythonもはいっています)とPyCharmと作ったプログラムを入れます

お家のみんなで使っているパソコンや、おとうさん、おかあさんのパソコンにいれるとき。
Python(ぱいそん)がはいっているか確認しよう。

スタートボタン(画面(がめん)の右下にある窓マークをクリックしてメニューを表示(ひょうじ)しAではじまるところにanaconda(あなこんだ)とか、pではじまるところにPythonとかPyCharm(ぱいちゃーむ)とかがあるかみてみよう。

Pythonがはいっているときはバージョンを確認しよう!

anacondaがはいっているときは、anacondaメニューの中にIPython(あいぱいそん)というメニューがあるかもしれません。

起動(きどう)してみましょう。

f:id:np2LKoo:20170810002634p:plain

pythonのあとが3ではじまればOKです。2で始まる場合は3を入れる必要(ひつよう)がありますが、おうちの人に聞きましょう。

「Python2を消していれていい」「Python2とPython3を共存(きょうぞん)させたい」「実はもうPython2とPython3を切り替えられるようになっている」などを教えてもらいましょう。

入っていない場合として説明します。(Python2が入っているときはgoogleなどで消し方や共存(きょうぞん)の仕方(しかた)を探(さが)してみてください)

準備(じゅんび)

慌(あわ)てないで、のんびりやろう!

1. はいっているOSを確認しよう

(1)画面左下にあるWindowsマーク(スタートボタン)の上にマウスを合わせて、ボタンをクリックしよう。

f:id:np2LKoo:20170810003211p:plain

(2)メニューがでるので、「システム」をクリックします。Windows 10 の情報が表示されるよ。

f:id:np2LKoo:20170810003242p:plain
(3)システムの種類のところにかいてある内容を確認しましょう。
32ビットオペレーティングシステム
とか
64ビットオペレーティングシステム
という言葉が書いてあると思います。
この 32 とか 64 という数字がとても大切なので、忘れないように覚えておきましょう。

memolのパソコンは64ビットと書いてありました。

 2. anacondaをダウンロードしてインストールしよう

anacondaとはpythonとよく使うライブラリ(部品の図書館ようなもの)をまとめてあるソフトウェアです。検索してダウンロードしよう!

(1) ブラウザを起動(IEでもedgeというのでもOK)

IE(いんたーねっとえくすぷろーら)をメニューや画面の上から探してクリック(またはダブルクリック)。こんなの(IE)や、

f:id:np2LKoo:20170810004003p:plain

 こんなの(Edge:えっじ)のマークを探してクリック(またはダブルクリック)

f:id:np2LKoo:20170810004033p:plain

このページをみているので、ブラウザは起動できているけど、このページをみながらインストールするときはもうひとつ起動した方が便利(べんり)かも。

(2)ブラウザの入力欄に http://www.google.com と入力しよう。

f:id:np2LKoo:20170810004515p:plain 

google とだけいれても検索してくれるはず。)

(3) googleの検索欄に

anaconda download

と入力して虫眼鏡(むしめがね)ボタンをクリックしよう。

anacondaというソフトをゲッドできる場所が表示されるよ。

f:id:np2LKoo:20170810004805p:plain

(4)上の画面でDownload Anaconda Now!の色が付いた少し大きなリンクをクリックすれば 、ダウンロードができるページが表示されるよ。表示はかわることもあるよ。

www.continuum.io/downloads ってなってたらそこです。

f:id:np2LKoo:20170810004930p:plain

Download for: の横の窓枠(まどわく)の形をクリックすれば、Windows用の anaconda がダウンロードできる画面に移(うつ)るよ。

四角い窓枠はWindowsマイクロソフトいう会社のういんどうずというOS:おーえす)

かじったりんごはMac(まっくというパソコン、アップルという会社のOS X(macOS)というなまえのOS:おーえす)

すわったぺんぎんさんはLinux(りなっくすというOS:おーえす)

です。

(5)うえで覚えた32ビットか64ビットかの情報をここで使います。

64ビットだった人は、左側3.6 Versionの Download と書いたすこし大きなボタンをクリックしてください。

32ビットだった人はその下の Download 32-bitリンクをクリックしてください。

f:id:np2LKoo:20170810005145p:plain

(6)なにか入力する画面がでてくるけどここでは「NO THANKS」(いいえ結構です)のリンクをクリックします。

f:id:np2LKoo:20170810005547p:plain

ちなみに「ANACONDAをダウンロードしてくれてありがとう! Anacondaの進め方のシートがほしい人はメールアドレスをいれてね。」みたいなことがかいてあります。

今は不要なので、NO THNKS をクリックしてください。

その下の「操作を選んでください。」の部分の「保存」ボタンをクリックします。

exeファイルが保存されます。途中はこんな感じです。この表示のときはどこもクリックせず、しばらく待っていてね。

f:id:np2LKoo:20170810010045p:plain

5分程度でダウンロードできると思います。

(7)ダウンロードが終わったら下の表示にかわるので、「実行」ボタンを押します。

f:id:np2LKoo:20170810010149p:plain

インストールの画面が表示されます。念のためbit数が合っているか確認してください。

f:id:np2LKoo:20170810010231p:plain

ようこそ Anaconda3セットアップへ

他のアプリケーションは終わらせてください。インストールで再起動はしません。Nextをクリックすると継続します。みたいなことが書いてあります。

(8)Next> ボタンをクリックします。

 

ライセンスの合意(ごうい)画面がでてきます。

f:id:np2LKoo:20170810010629p:plain

ライセンスの説明が書かれています。
ライセンスとは権利(けんり)を持っている人(たとえばつくった人)が、「つかってもいいよ」と認(みと)めてくれることだよ。
(たとえば運転免許証(うんてんめんきょしょう)も「ライセンス」なんだよ。運転してもいいよという証(あかし)です。)
ほかには、使ってもいい条件とか、他のつかっている技術(ぎじゅつ)とか、これはやっちゃだめなこととかの注意(ちゅうい)が書いてあります。
これを読んで合意できればソフトを使ってもいいよということです。
勉強用(=遊び用)に使うのであれば問題(もんだい)ないので、I Agree(合意します)のボタンをクリックしましょう。

(9)使う人を選ぶ画面がでてきます。

f:id:np2LKoo:20170810011124p:plain

自分だけなら上の(Just Me)
パソコンを使う人全部が使えるようにするには(All Users)を選びます。
自分で使うだけならJustMeのままでよいです。

〇印のところをクリックすれば選べるよ。

 Next> ボタンをクリックします。

(10)ソフトを入れる場所をきいてきます。

f:id:np2LKoo:20170810011228p:plain

普通はそのままでよいので Next> をクリックします。

左下にどれだけ必要か書いてあります。 1.9GB 使うのでとても大きいです。

(11)環境設定(かんきょうせってい)に関するインストールオプションがでてきます。

f:id:np2LKoo:20170810011413p:plain

普通はそのままでOKです。Install をクリックします。

 

f:id:np2LKoo:20170810011546p:plain
かなり時間がかかります。30分以上かかるかもです。
anacondaはpythonの他にいろいろなライブラリをインストールします。

completed と表示されたら終わりです。Next> をクリックします。

(12)下の画面がでたら、完了します。チェックボックスを選択すると、説明が表示されますが、今は不要なので、チェックせずにおわります。

f:id:np2LKoo:20170810011613p:plain

Finish(おしまい) をクリックします

ではうごかしてみましょう。

f:id:np2LKoo:20170810011944p:plain

画面右下のスタートボタンをクリックしてメニューを表示し、Aの部分にあるAnacondaのフォルダメニューを開き、IPythonをクリックします。

f:id:np2LKoo:20170810012141p:plain

上のような画面がでました。対話形式(たいわけいしき:1つ命令をいれると答えがすぐでる)でpythonがうごきます。1+1 と入れた後、[Enter]キーを押したら、2 と出ました。([Enter]の部分は、少し右側にあるいちばん大きなキーを押す意味です。)

終わらせるには exit() を入力して[Enter]キーを押します。(かっこの部分は右か左の 白上矢印Shift のキーを押しながら 上の8と9を順番に押せば入力できるよ)

(ウィンドウの「×」で終わってもいいです。)

上の例では Python 3.6.1 が動きました

ここまでで、anacondaのインストールは完了です。

 3. 次にPycharmをインストールします。

PyCharmは開発をするための統合開発環境(とうごうかいはつかんきょう。IDEともいうよ)です。

(1) anacondaと同じように

PyCharm download

で検索してみよう。f:id:np2LKoo:20170810012512p:plain

(2)上のほうのリンクをクリックすると下の画面が現れました。

f:id:np2LKoo:20170810012543p:plain

左側の青いボタンがProfessional(プロフェッショナル)版の無料お試し版、右側がCommunity(コミュニティ)版です。コミュニティ版は無料(Free)です。右側の黒色Downloadボタンをクリックします。

(3)Pycharmのダウンロードが開始されます。

f:id:np2LKoo:20170810012834p:plain

保存ボタンをクリックしてください。

(4)ダウンロードが進みます。状況によっては20分以上かかるかもしれません。

下の画面がでたら、そのままどこもクリックせずに待っていてください。

f:id:np2LKoo:20170810012952p:plain

(5)ダウンロードが完了したら「実行」ボタンをクリックします。

もし「このアプリがデバイスに変更を加えることを許可しますか
PycharmPC-...」
のような小窓がでたら、「はい」をクリックします。

(6)インストール用の画面が表示されます。

f:id:np2LKoo:20170810013151p:plain

ようこそ。他のアプリケーションは終了してください。インストールで再起動することはありません。(anacondaと同じことが書いてあります)

Next> ボタンをクリックします。

f:id:np2LKoo:20170810013357p:plain

インストールする場所は普通そのままで良いです。408.9MBくらい使うようです。

Next> をクリックします。

f:id:np2LKoo:20170810013454p:plain

画面に起動用のアイコンを作ります。64bit版か32bit版にあわせて選択して Next> ボタンをクリックしてください。

f:id:np2LKoo:20170810013553p:plain

メニューを聞いてきます。JetBrainsのメニューの下にできます。

ちなみにJetBrainsは会社名で「チェコ」(首都はプラハ)の国の会社です。

そのまま、Install ボタンをクリックします。

f:id:np2LKoo:20170810013823p:plain

完了すると下の画面が表示されます。

f:id:np2LKoo:20170810013855p:plain

インストールは5分くらいでできました。

Finishボタン(おしまいボタン)をクリックして完了です。

4.  音の実験でよく使うpyaudioというライブラリを入れます。

anacondaはいろいろなライブラリがはいっていますが、残念ならがpyaudio(ぱいおーでぃお)は入っていません。自分でいれてやる必要があります。

(1)画面左下のスタートメニューから「Anaconda Promot」(あなこんだ ぷろんぷと)を起動します。

f:id:np2LKoo:20170810014205p:plain

(2)Anaconda Promptが起動します。

f:id:np2LKoo:20170810014302p:plain

(3) pip  install  pyaudio

と入力  して、[Enter]キーおします。

f:id:np2LKoo:20170810014330p:plain

ネットワークから pyaudio というライブラリをゲットしてインストールします。

Successfully の文字がでれば成功です。

おわるときは右上の「×」をクリックしてください。

5.  PyCharmを起動します。

(1)画面の上の下のようなPyCharmアイコンをダブルクリックします。(もともとたくさん画面にアイコンがある人はどこかにありますので、探してください。)

f:id:np2LKoo:20170810014603p:plain

(2)最初だけ、情報を引(ひ)き継(つ)ぐかどうかの確認を聞いてきます。

最初なので、下の引き継がない下側の選択のまま OK をクリックします。

f:id:np2LKoo:20170810014632p:plain

(3)プライバシーに関する説明が表示されます。

f:id:np2LKoo:20170810014743p:plain

入力された個人の情報をどのように扱うかを説明して、それでいいか聞いてきています。ここまで、個人情報を入れていませんし、これからも入れないと思いますが、一般的な問題ない内容が書かれているので、Accept(受け入れ:OK)します。これは一度だけ表示されます。

(4)ソフトウェアの起動の画面が出ます。(こういう画面を、スプラッシュ画面といいます)

f:id:np2LKoo:20170810015337p:plain

(5)起動画面がでます。(他にいろいろ確認してくる場合があります)

下の場面はそのままOKクリックしてください。画面の見え方やキー操作の種類を選びますが、こだわらない人はそのままOKでいいです。

f:id:np2LKoo:20170811161654p:plain

下の画面はいろいろな操作の紹介をする画面です。Closeをクリックして閉じましょう。

f:id:np2LKoo:20170811161832p:plain

下の画面が最初の画面です。

1つのソフトウェアは、何本ものプログラムが動くものもあります。そのプログラムをまとめたものを「プロジェクト」といいます。(呼び方はいろいろあります)

「新しいプロジェクトをつくる(Create New Project)」をクリックします。

f:id:np2LKoo:20170810015613p:plain

プロジェクトの名前を聞いてきます。

f:id:np2LKoo:20170810015951p:plain

はじめは名無し(untitled)なので、プロジェクトの名前を入力します。

オートマトン(Automaton)にします。(ここは日本語にしない方がよいです)

f:id:np2LKoo:20170810020121p:plain

新しいプロジェクトが起動します。

f:id:np2LKoo:20170810020224p:plain

最初の起動時にはいろいろ設定を行うので、すぐにはプログラムを動かせない場合があります。(20分くらい、いろいろ初期設定することがあります)

しばらく設定中になるので、その間に今回作ったプログラムのをダウンロードしましょう。

6. 音のでるソフトウェアとデータをダウンロードします。

今回のプログラム(Automatonをダウンロードします。)

ダウンロードはこちらからお願いします。

f:id:np2LKoo:20170810020557p:plain

が表示されますので、「保存」をクリックします。その後は、完了になるまでしばらく待ってください。

f:id:np2LKoo:20170810020734p:plain

次に「フォルダを開く」をクリックします。

ダウンロードできた Automation_日付.zipのデータを解凍(かいとう)します。

 Automation_日付.zipファイルの上で右ボタンクリックして「すべて展開」を選択します。(はいっている解凍ソフトによっては表示もメニューも違う場合があります。また、.zip の部分はパソコンによっては表示されません。)

f:id:np2LKoo:20170811220746p:plain

次にどこに解凍するかきいてみます。(きいてこないソフトもあります)

f:id:np2LKoo:20170811221022p:plain

(ファイル名等は今後かわることがあります。)解凍すると下のようなファイルができます。

f:id:np2LKoo:20170811221101p:plain

次にコピー先のPythonのプロジェクトのフォルダを開きます。

〇ここに入力して検索 の欄(らん)で exp と入力すれば「エクスプローラ」が検索にあらわれますのでクリックします。(他の起動方法を知っているひとはもちろんそちらでOKです)

f:id:np2LKoo:20170811221205p:plain

左側の一覧から「ローカルディスク(C:)」を選択(クリック)します。

右にでてくる ユーザ を選択(クリック)します。

f:id:np2LKoo:20170811221403p:plain

次にでてくる自分のログインIDの名前をクリックします。例ではmemolです。(ログインIDはmemolです。)

f:id:np2LKoo:20170811221407p:plain

f:id:np2LKoo:20170811221940p:plain

いろいろ表示されますが、「PycharmProjects」を選択(クリック)します。

似たようなフォルダで「.Python」というのがありますが、そちらではありませんので注意しましょう。

f:id:np2LKoo:20170811221933p:plain

さっき作った Automatonというプロジェクトがありました!

クリックします。

f:id:np2LKoo:20170811222109p:plain

まだ中身はからっぽぽです。

ここにダウンロードしたソフトを移動します。

先ほどのダウンロードしたウインドウを探します。

そして、ホームメニューから「すべて選択」をクリックします。

f:id:np2LKoo:20170811222148p:plain

表示されたファイルがすべて選ばれるので、ドラッグ アンド ドロップ((元画面の)左ボタンクリックして左側画面の上で離します。

f:id:np2LKoo:20170811223008p:plain

下のようになりました。( _pycache_ はできていないこともあります)

これでOKです。f:id:np2LKoo:20170810021646p:plain

7.PyCharmでプログラムを動作させてみます。

PyCharmインストール直後(ちょくご:すぐに)は実行(じっこう)しようとしても実行できないことがあります。
自動的(じどうてき)にpythonやモジュールのアップデートをする場合があります。
終わるまでプログラムの実行はできません。待ちましょう。
かなり時間がかかる場合があります。

 (1)プロジェクトのフォルダにコピーするとファイルが表示されます。
Automaton001.pyのファイル名の上で、右ボタンクリックでメニューを表示し、RUN 'Automaton001'をクリックします。

f:id:np2LKoo:20170810022941p:plain

※設定中の場合はRun のメニューが出てこないことがあります。

f:id:np2LKoo:20170811162230p:plain

このようにRunが出てこない場合はもうしばらく待っていてください。画面の右下で処理実行中みたいな表示が出ているときはまだ設定中です。

f:id:np2LKoo:20170811162315p:plain

問題なければ、プログラムが起動するはずです。使われているパソコンの環境(かんきょう)によってはエラーでてして動作(どうさ)しない可能性(かのうせい)があります。まわりの人に聞いてもわからないときは来年のなつやすみにまたチャレンジしてください。

f:id:np2LKoo:20170810023404p:plain

ヘッドフォンやスピーカの接続を確認して、音量を絞(しぼ)って(ちいさくして)、拍子とサブ拍子、スピードを選択してから、スタートボタンを押してください。

音量は小さめで実行してください。(必ずすごくちいさな音からだんだん大きくしていこう。大きな音をずっと聞くと耳を傷(いた)めるよ。気をつけよう。

どうでしょう?うまく動きましたか?

16個の音がすべて白い範囲(はんい)になるようにチャレンジしてみてください。

パソコンの種類によっては音が途切(とぎ)れたり、タイミングと違った場所に点が描(えが)かれる場合があります。
ご了承(りょうしょう)ください。

また、ゲームとしては作り込みがまったく足りません(ゲームにはなっていません)。音で遊ぶことがメインなので、ゲーム性はご容赦(ようしゃ)ください。

このプログラムをベースにいろいろな音(列)の実験(というよび遊び)をしようと思います。

だそく(蛇足:むだなあとがき、よけいなひとこと、でも「ほんね」がでることも多い)

 さて、このソフトで「おーとまとん」の部分(ぶぶん)はどこでしょう?

こたえはリズムを入力した「あなた」です。 

(半分冗談(じょうだん)ではんぶん本気(ほんき)です)

 「そんなわけないでしょ!」と「だから、オートマトンてなんなの?」という声がきこえてきそうです。おいおい解説(かいせつ)していきますね。

じっさいに、小学生の方に操作(そうさ)してもらい感想(かんそう)を聞きました。解凍(かいとう)の部分の説明不足(せつめいぶそく)が分かったので書き加(くわ)えます。どうにか説明だけで、最後(さいご)までたどり着(つ)けました。

感想(かんそう)を聞(き)いたら、

「つまらん。」「途中(とちゅう)で眠(ねむ)い」

だそうです。もっと面白(おもしろ)くなるよう、努力(どりょく)が必要(ひつよう)だと痛感(つうかん)しました。また、10分以上待つのはたいへん困難(こんなん)であると理解(りかい)しました。

クーへ:ほらね、ゲームとして完成度が高くないとだれも面白いと思わないって。

 ほんとうにどうでもいいはなし(よまなくてもいいよ)

 「次はどうすればいいの?」

「ここまでできた方(かた)は、もうかなり、自分(じぶん)でできます」

googleで検索(けんさく)できます。(どんなにゅりょくをすれば、自分のほしい情報(じょうほう)が1ページ目にでてくるのか研究(けんきゅう)しよう。)

(キーボードの練習(れんしゅう)をしよう。Cortana(こるたな:Windows10の音声認識(おんせいにんしき))はありますが、それにたよらず、まずはキーボードで、キーボードをキーを見ないで、入力できるように練習しましょう。1年くらい(まじめに)練習すれば、書くよりはやくなります。3年くらい練習すればしゃべるよりはやく打てるようになります。5年くらい練習すれば、普通(ふつう)のキーボードではタイミングが間に合わなくなります。そんなときはNキーロールオーバーというキーボードを使えば正しく認識してくれます。キーボード入力はちゃと練習しないといつまでたってもうまくなりません。ちゃんと練習しましょう。)

・解凍(かいとう)できます。(いろいろな解凍をできるようになろう)

pythonのプログラムを動かせます。(動かすだけでなく、そのうちちょっと作ってみたくなるかも。)

・英語をすこしわかったかも

Install とか Download とか Run の言葉を覚えました。

英語ちょっと苦手な人多いですよね。memolも苦手です(けど、嫌(きら)いではなくなりました。)世(よ)の中の人と反対(はんたい)のことを言いますが、まずは読めればいいんです。読めるようになると、英語のサイトがなんとなく(全部わかるわけでなくても)普通(ふつう)に見れるようになります。実際(じっさい)普通に勉強していると、英語を話せる前に読み書きできるようになります。(実はふつうの大学生(だいがくせい)は英語を(ほんとうに)話せません。(びっくりするかもしれませんが、6年間や10年間英語を勉強しても「だいたいの人は本当に英語をしゃべれません。」)でも、「6年間や10年普通に勉強すると、簡単(かんたん)な(または専門(せんもん)分野(ぶんや)の)英語なら読み書きできる」ようになります。まぁ、10年間やっててればあたりまえですが。

でも社会人になって3年くらい英語を全くやらないと、ほぼ完全に忘れるみたいです。お家でCNN(しーえぬえぬ:外国のニュースのチャンネル)ニュースとかが見れる人はわかんなくていいから、毎日10分みよう!(内容わかんないのに10分みるのは苦痛だけど)

英語でコメントといっても3語から3行程度の中学校の英語です。
「言葉(ことば)は文法(ぶんぽう)ではなく、気持(きも)ちです」たくさん読(よ)んでいると少し書(か)いてみたくなります。書いて相手から返事(へんじ)があるととってもうれしいです。

そう考えると、明日から英語を勉強するのが好きになるかも?。

おたまじゃくしさんのおーとまとん~拍子編(プログラム)

おたませしました

おたまじゃくしのみなさん。全国の小中学生のおともだち。おまたせしました!

ゲームとしてもうすこしちゃんと作るつもりだったのですが、かえるのクー(このブログの主(ぬし))と相談(そうだん)して、実験レベルのものを公開(こうかい)することになりました。このページには2つのプログラムをはります。(インストールの仕方(しかた)と、ダウンロードの準備はもう少し待っていてね。→できました。)

まだ調整(ちょうせい)していないプログラムなので、何回か入れ替(か)えするかもしれません。

f:id:np2LKoo:20170807233625p:plain

バージョンというところをみて、かわっているかどうか確認(かくにん)してください。プログラムをインストールするときは、かならず家の人のお許(ゆる)しをもらってから入れてください。

プログラムとデータはまとめて他のところからもダウンロードできるようにしておきます。(データはここにはれないので、ダウンロードしてください)

どんなのかソースだけみたい人のために貼(は)ります。遊びたい人はダウンロードからお願いします。どのようにインストールするかは「インストール編」をみてください。

np2lkoo.hatenablog.com

本体のプログラム(現在 V 0.2017.08.07 です)

Automaton001.py


import sys
from PyQt4.QtGui import *
import PyQt4.QtGui as QtGui
from VWave import VWave
from sklearn.datasets.base import Bunch
import pyaudio
import threading
import time
from matplotlib.backends.backend_qt4agg import FigureCanvasQTAgg as FigureCanvas
from matplotlib.figure import Figure

'''
-------------------------------------------------------------------------
このプログラムは「おたまじゃくしさんのおーとまとん」の(拍子編)プログラムです。
Memol V.0.2017.08.07
-------------------------------------------------------------------------
'''
WAVE_DB_FILE = "H:\\KooSoundDB\\soundbox.wav"
SUBWAVE_SIZE = 12800 * 2
SAMPLING_RATE = 16000
STRONG_BEAT_NO = 34
SUB_STRONG_BEAT_NO = 29
WEAK_BEAT_NO = 22
CLECK_BEAT_NO= 10
BEAT_OFFSET = 0.056
DISP_VERSION = "V.0.2017.08.07"

class BarPlot():
def __init__(self, parent=None):
# Create the mpl Figure and FigCanvas objects.
# 5x4 inches, 100 dots-per-inch
self.dpi = 100
self.fig = Figure((5, 4), dpi=self.dpi)
self.canvas = FigureCanvas(self.fig) # pass a figure to the canvas
self.canvas.setParent(parent)
self.parent = parent

self.axes = self.fig.add_subplot(111)
self.axes.axis([-0.25, 0.3, 0, 16])
self.spanplot = False

def on_draw(self):
"""
redraw the figure
"""
self.axes.clear()
self.axes.grid()

x = range(len(self.data))
self.axes.bar(left=x, height=self.data, width=0.3, align='center',
alpha=0.44, picker=5)
self.canvas.draw()

def plotData(self, xData, yData):
self.axes.plot(xData,yData, 'o')
if self.spanplot == False:
time.sleep(0.02)
self.axes.axvspan(-0.25,0, facecolor='g', alpha=0.5)
self.axes.axvspan(0.05,0.3, facecolor='g', alpha=0.5)
self.axes.axvspan(-0.25,-0.05, facecolor='g', alpha=0.5)
self.axes.axvspan(0.1,0.3, facecolor='g', alpha=0.5)
self.spanplot = True
time.sleep(0.04)
self.canvas.draw()

def plotDraw(self):
self.canvas.draw()
self.canvas.flush_events()

def plotClear(self):
self.axes = self.fig.add_subplot(111)
self.axes.axis([-0.25, 0.3, 0, 16])
self.canvas = FigureCanvas(self.fig)
self.canvas.setParent(self.parent)
self.spanplot == False
self.canvas.show()


class soundtool():
def __init__(self, aStr):
pass
def fetch_soundData(self):
f = open(WAVE_DB_FILE, 'rb')
fData = f.read()
waveData = VWave(fData)
rtnBunch = Bunch()
rtnBunch.COL_NAMES = {'SoundName', 'label', 'data'}
rtnBunch.DESCR = 'sounddata.koo dataset:ksssr-original'
rtnBunch.SoundName = waveData.getChunkData(waveData.INSTR_TAG)
rtnBunch.label = waveData.getChunkData(waveData.NOTE_TAG)
rtnBunch.alldata = waveData.iff[waveData.IFF_DATA]
rtnBunch.data = []
for i in range (int(len(rtnBunch.alldata) / SUBWAVE_SIZE)):
rtnBunch.data.append(rtnBunch.alldata[i * SUBWAVE_SIZE:(i + 1) * SUBWAVE_SIZE])
return rtnBunch

def play(self, data):
self.stream.write(data)

def openAudio(self):
self.p = pyaudio.PyAudio()
self.stream = self.p.open(format=pyaudio.paInt16,
channels=1,
rate=int(SAMPLING_RATE),
output=True)

def closeAudio(self):
self.stream.close()
self.p.terminate()

class toneThread(threading.Thread):

def __init__(self, aTool, aDb, aMode, arhythm, aVariation, aSpeed, aValiationList, aCallback):
super(toneThread, self).__init__()
self.st = aTool
self.ksssr = aDb
self.mode = aMode
self.rhythm = arhythm
self.variation = aVariation
self.callback = aCallback
self.speed = aSpeed
self.variation_list = aValiationList
if aMode == 1:
print("speed=%d" % aSpeed)
self.sDB = aDb.data.copy()
addAdj = int((32000 * 60 / aSpeed - 25600) / 2) * 2
subAdj = int(32000 * 60 / aSpeed / 2) * 2
for i in range(len(self.sDB)):
if aSpeed <= 75:
s = self.sDB[i] + b'\x00' * addAdj
self.sDB[i] = s
else:
s = self.sDB[i]
self.sDB[i] = s[0:subAdj]
self.stop_event = False

def run(self):
if self.mode != 1:
self.st.play(self.ksssr.data[CLECK_BEAT_NO])
else:
self.play()
self.callback(1)

def play(self):
for i in range(17):
if self.stop_event == True:
print('Stop')
return
self.callback(2)
if self.rhythm == 1:
self.st.play(self.sDB[STRONG_BEAT_NO])
elif self.variation == 0:
self.st.play(self.sDB[STRONG_BEAT_NO])
for i in range(self.rhythm - 1):
self.st.play(self.sDB[WEAK_BEAT_NO])
else:
self.st.play(self.sDB[STRONG_BEAT_NO])
for i in range(len(self.variation_list[self.variation])):
if i == 0:
for j in range(self.variation_list[self.variation][i])[1:]:
self.st.play(self.sDB[WEAK_BEAT_NO])
else:
self.st.play(self.sDB[SUB_STRONG_BEAT_NO])
for j in range(self.variation_list[self.variation][i])[1:]:
self.st.play(self.sDB[WEAK_BEAT_NO])
pass
print('End')

def stop(self):
self.stop_event = True
self.join()


class stackedExample(QWidget):
def __init__(self):
super(stackedExample, self).__init__()
self.FORM_COMBO_ITEM = {
1: ["1拍子", QWidget(), QButtonGroup(), ("1拍子",)],
2: ["2拍子", QWidget(), QButtonGroup(),("2拍子",)],
3: ["3拍子", QWidget(), QButtonGroup(),("3拍子",)],
4: ["4拍子", QWidget(), QButtonGroup(),("4拍子","2拍子+2拍子",)],
5: ["5拍子", QWidget(), QButtonGroup(),("5拍子","2拍子+3拍子","3拍子+2拍子",)],
6: ["6拍子", QWidget(), QButtonGroup(),("6拍子","2拍子+2拍子+2拍子","2拍子+4拍子","4拍子+2拍子",)],
7: ["7拍子", QWidget(), QButtonGroup(),("7拍子","2拍子+2拍子+3拍子","2拍子+3拍子+2拍子","2拍子+5拍子",
"3拍子+2拍子+2拍子","3拍子+4拍子",
"4拍子+3拍子", "5拍子+2拍子")],
8: ["8拍子", QWidget(), QButtonGroup(),("8拍子","2拍子+3拍子+3拍子","3拍子+2拍子+3拍子", "3拍子+3拍子+2拍子",
"3拍子+5拍子", "5拍子+3拍子" )],
9: ["9拍子", QWidget(), QButtonGroup(),("9拍子","2拍子+3拍子+4拍子","2拍子+4拍子+3拍子", "3拍子+2拍子+4拍子",
"3拍子+3拍子+3拍子", "3拍子+4拍子+2拍子",
"4拍子+2拍子+3拍子","4拍子+3拍子+2拍子")],
10: ["10拍子", QWidget(), QButtonGroup(),("10拍子","3拍子+3拍子+4拍子", "3拍子+4拍子+3拍子", "4拍子+3拍子+3拍子",
"4拍子+2拍子+4拍子", "4拍子+4拍子+2拍子",
"2拍子+5拍子+3拍子","3拍子+5拍子+2拍子")],
11: ["11拍子", QWidget(), QButtonGroup(),("11拍子","3拍子+3拍子+5拍子", "3拍子+5拍子+3拍子",
"4拍子+4拍子+3拍子","4拍子+3拍子+4拍子","3拍子+4拍子+4拍子",
"5拍子+3拍子+3拍子","5拍子+4拍子+2拍子",)],
12: ["ここまで", QWidget(), QButtonGroup(),("ここまで",)]
}
self.SPEED_ITEM = {
1: ("60",60,),
2: ("100",100,),
3: ("140",140,),
4: ("180",180,),
5: ("220",220,),
6: ("260",260,),
7: ("300", 300,),
8: ("340", 340,),
9: ("380", 380,),
10: ("420", 420,),
11: ("460", 460,),
12: ("dummy", 999,),
}
self.SUB_BEAT = {
1: ((1,),),
2: ((2,),),
3: ((3,),),
4: ((4,),(2,2,)),
5: ((5,),(2,3,),(3,2,),),
6: ((6,),(2,2,2,),(2,4,),(4,2,),),
7: ((7,),(2,2,3,),(2,3,2,),(2,5,),(3,2,2,),(3,4,),(4,3,),(5,2,),),
8: ((2,3,3,),(3,2,3,),(3,3,2,),(3,5,),(5,3,),),
9: ((9,),(2,3,4,),(2,4,3,),(3,2,4,),(3,3,3,),(3,4,2,),(4,2,3,),(4,3,2,),),
10: ((10,),(3,3,4,),(3,4,3,),(4,3,3,),(4,2,4,),(4,4,2,),(2,5,3,),(3,5,2,),),
11: ((11,),(3,3,5,),(3,5,3,),(4,4,3,),(4,3,4,),(3,4,4,),(5,3,3,),(5,4,2,),),
}
self.resize(400, 768)
self.setWindowTitle('おとであそぼう!')
self.mainLabel = QLabel("<h2 style='color: white; background-color: royalblue;'>おたまじゃくし♪のおーとまとん(拍子編) " + DISP_VERSION +"</ h2>");
self.combo = QComboBox(self)
for i in range(len(self.FORM_COMBO_ITEM))[1:]:
s = self.FORM_COMBO_ITEM[i][0]
self.combo.addItem(s)
self.combo.setGeometry(0, 10, 100, 35)
self.combo.activated[int].connect(self.change01)
self.speed = QWidget()
self.Stack = QStackedWidget(self)
for i in range(len(self.FORM_COMBO_ITEM))[1:]:
self.stackUI(i, self.Stack)

for i in range(len(self.FORM_COMBO_ITEM))[1:]:
self.Stack.addWidget(self.FORM_COMBO_ITEM[i][1])

self.stackGraph(self.Stack)
layout = QFormLayout()
self.speedNo = QHBoxLayout()
self.bgspeed = QButtonGroup()
for i in range(len(self.SPEED_ITEM))[1:]:
btn= QRadioButton(self.SPEED_ITEM[i][0])
if i == 1:
btn.setChecked(True)
self.speedNo.addWidget(btn)
self.bgspeed.addButton(btn)
self.bgspeed.buttonClicked[int].connect(self.setSpeed)

layout.addRow(QLabel("スピード♪+♪="), self.speedNo)
self.speed.setLayout(layout)

self.button = QtGui.QPushButton('スタート', self)
#self.button.setGeometry(100, 10, 100, 35)
self.button.clicked.connect(self.execute01)

self.button2 = QtGui.QPushButton('クリック', self)
self.button2.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Expanding)
self.button2.clicked.connect(self.click01)

self.button3 = QtGui.QPushButton('ストップ', self)
#self.button.setGeometry(100, 10, 100, 35)
self.button3.clicked.connect(self.stop01)

hbox = QVBoxLayout(self)
hbox.addWidget(self.mainLabel)
hbox.addWidget(self.combo)
hbox.addWidget(self.Stack)
hbox.addWidget(self.speed)
hbox.addWidget(self.button)
hbox.addWidget(self.button2)
hbox.addWidget(self.button3)

self.setLayout(hbox)
self.st = soundtool('dummy')
self.ksssr = self.st.fetch_soundData()
self.bonb = []
self.playing = 0
self.grhythm = 1
self.bonbCount = 0
self.gSpeed = 60
self.gValiation = 0
self.BeatPoint = 0
self.BeatCount = 0
self.show()

def setSpeed(self,aSpeed):
self.gSpeed = 60 - (aSpeed + 2) * 40

def stackUI(self, aInt, aStack):
layout = QFormLayout()
countNo = QVBoxLayout()
for i in range(len(self.FORM_COMBO_ITEM[aInt][3])):
strBtn = self.FORM_COMBO_ITEM[aInt][3][i]
btn = QRadioButton(strBtn)
if i == 0:
btn.setChecked(True)
countNo.addWidget(btn)
self.FORM_COMBO_ITEM[aInt][2].addButton(btn)
layout.addRow(QLabel("拍子"), countNo)
self.FORM_COMBO_ITEM[aInt][2].buttonClicked[int].connect(self.setValiation)
self.FORM_COMBO_ITEM[aInt][1].setLayout(layout)
aStack.addWidget(self.FORM_COMBO_ITEM[aInt][1])

def stackGraph(self, aStack):
self.graphstack = QWidget()
layout = QFormLayout()
vBox = QVBoxLayout()
self.barplot = BarPlot(self.graphstack)
vBox.addWidget(self.barplot.canvas)
self.graphstack.setLayout(vBox)
aStack.addWidget(self.graphstack)

def restackGraph(self, aStack):
self.graphstack = QWidget()
layout = QFormLayout()
vBox = QVBoxLayout()
self.barplot = BarPlot(self.graphstack)
vBox.addWidget(self.barplot.canvas)
self.graphstack.setLayout(vBox)
aStack.addWidget(self.graphstack)
self.barplot.canvas.show()

def setValiation(self, aInt):
self.gValiation = - 2 - aInt
print("valiation=%d" % self.gValiation)

def change01(self, aInt):
self.Stack.setCurrentIndex(aInt)
self.grhythm = aInt + 1

def changeGraph(self):
aLen = len(self.Stack)
self.Stack.setCurrentIndex(aLen - 1)

def execute01(self):
if self.playing != 0:
return
print('Start')
self.barplot.plotClear()
self.restackGraph(self.Stack)

self.playing = 1
self.bonb = []
self.bonbCount = 0
self.BeatCount = 0
a = self.bgspeed.buttonClicked['int']
self.changeGraph()
for i in range(16):
ast = soundtool('dummy')
ast.openAudio()
th_cl = toneThread(ast, self.ksssr, 0, 0, 0, 0, [], self.callback)
self.bonb.append(th_cl)
ast = soundtool('dummy')
#ダミープロット(領域表示)
self.barplot.plotData(0, -1)
ast.openAudio()
th_cl = toneThread(ast, self.ksssr, 1, self.grhythm, self.gValiation, self.gSpeed, self.SUB_BEAT[self.grhythm], self.callback)
self.startTime = time.clock()
th_cl.start()
self.currentPlay = th_cl

def stop01(self):
self.currentPlay.stop()

def callback(self,aInt):
if aInt == 1:
self.playing = 0
self.barplot.plotDraw()

if aInt == 2:
atime = time.clock()
self.aTime = atime - self.BeatPoint
if self.BeatCount != 0:
self.aAveTime = (atime - self.startTime) / self.BeatCount
else:
self.aAveTime = 0
self.BeatPoint = atime
print ("AveTime=%.5f" % self.aAveTime)
#print ("time=%.5f" % self.aTime)
self.BeatCount += 1




def click01(self):
if self.playing != 1:
return
self.CleackPoint = time.clock()
ast = soundtool('dummy')
ast.openAudio()
if self.bonbCount >= 16:
print('no bomb')
return
th_cl = self.bonb[self.bonbCount]
th_cl.start()
self.bonbCount += 1
aDiff = (self.CleackPoint - self.BeatPoint - BEAT_OFFSET)
if aDiff > (self.aAveTime - 0.25):
aDiff = aDiff - self.aAveTime
print ("diff=%0.5f" % aDiff)
time.sleep(0.1)
self.barplot.plotData(aDiff ,self.bonbCount)



def main():
app = QApplication(sys.argv)
ex = stackedExample()
sys.exit(app.exec_())


if __name__ == '__main__':
main()


VWave.py (かえるのクー作成のサウンドデータベースアクセス用プログラム)


import numpy as np

STRCODE = 'utf-8'
'''
=====================================================================================
Wave ファイル汎用アクセスクラス
Koo Wells V 0.2017.07.31
=====================================================================================
'''

class VWave:
# 読み込んだwaveファイルの情報を格納するクラスです。
SAMPLING_COUNT = 32768
INSTR_TAG = 'ISFT'
NOTE_TAG = 'ICMT'
# 地道にwavフォーマット情報を定義していきます。
#
IFF_ALL = 0
IFF_ID, IFF_SIZE, IFF_FMT, IFF_SUBID, IFF_SUBSIZE = 1, 2, 3, 4, 5
IFF_WAVFOMT, IFF_CHANNEL, IFF_SAMPLING, IFF_BYTEPS, IFF_BLKSIZE = 6, 7, 8, 9, 10
IFF_BIT, IFF_DATAID, IFF_DATASIZE, IFF_DATA, IFF_LIST = 11, 12, 13, 14, 15
IFF_LISTID, IFF_LISTSIZE, IFF_INFOID = 16, 17, 18
TYPE_BYTE = "byte"
TYPE_STR = "string"
TYPE_LITTLE = "little"
DEF_METAID, DEF_TITLE, DEF_TYPE, DEF_START, DEF_BYTE, DEF_ENC = 0, 1, 2, 3, 4, 5
IFF_DEF = [
[IFF_ALL, "ALL DATA ", TYPE_BYTE, None, None, None ],
[IFF_ID, "ID ", TYPE_STR, 0, 4, STRCODE],
[IFF_SIZE, "Chunk Size", TYPE_LITTLE, 4, 4, None],
[IFF_FMT, "FormatName", TYPE_STR, 8, 4, STRCODE],
[IFF_SUBID, "Sub ID ", TYPE_STR, 12, 4, STRCODE],
[IFF_SUBSIZE, "Sub Size ", TYPE_LITTLE, 16, 4, None],
[IFF_WAVFOMT, "WavFomat ", TYPE_LITTLE, 20, 2, None],
[IFF_CHANNEL, "Channel ", TYPE_LITTLE, 22, 2, None],
[IFF_SAMPLING,"Sampling ", TYPE_LITTLE, 24, 4, None],
[IFF_BYTEPS, "BytePS ", TYPE_LITTLE, 28, 4, None],
[IFF_BLKSIZE, "BlockSize ", TYPE_LITTLE, 32, 2, None],
[IFF_BIT, "Bit ", TYPE_LITTLE, 34, 2, None],
[IFF_DATAID, "DataID ", TYPE_STR, 36, 4, STRCODE],
[IFF_DATASIZE,"DataSize ", TYPE_LITTLE, 40, 4, None],
[IFF_DATA, "IFF Data ", TYPE_BYTE, 44, None, None],
[IFF_LIST, "List ", TYPE_BYTE, 0, None, None ],
[IFF_LISTID, "List ID ", TYPE_STR, 0, 4, STRCODE ],
[IFF_LISTSIZE,"List Size ", TYPE_LITTLE, 4, 4, None],
[IFF_INFOID, "INFO ID ", TYPE_STR, 8, 4, STRCODE]
]
#読み込んだ情報を格納する配列です。最初はクラス変数を使っていましたが、多すぎてわかりにくくなったため、
#すべて配列で持つことにしました。
iff = []
iffSubChunkID = []
iffSubChunkSize = []
iffSubChunkData = []
alldata = b''

def __init__(self, data):
alldata = data
self.iff =[b'', "", 0, "", "", 0, 0, 0, 0, 0, 0, 0, "", 0, b'', b'', "", 0, ""]
siff = self.iff
sALL = self.IFF_ALL
dMetaID, dTitle, dType, dStart, dByte, dEnc = self.DEF_METAID, self.DEF_TITLE, self.DEF_TYPE, self.DEF_START, self.DEF_BYTE, self.DEF_ENC
tByte, tStr, tLittle = self.TYPE_BYTE, self.TYPE_STR, self.TYPE_LITTLE
# 以降全体から特定部分へアクセスするためにバイト配列にして格納します。
siff[sALL] = np.frombuffer(data, dtype=self.IFF_DEF[sALL][dType])
#順次定義の内容に基づき、data内容を解釈して配列に格納します。
for i in range(14)[1:]:
iDEF = self.IFF_DEF[i]
if iDEF[dType] == tStr:
siff[i] = (b''.join(siff[sALL][iDEF[dStart]:iDEF[dStart] + iDEF[dByte]])).decode(iDEF[dEnc])
elif iDEF[dType] == tLittle:
siff[i] = int.from_bytes(siff[sALL][iDEF[dStart]:iDEF[dStart] + iDEF[dByte]], tLittle)

#Waveデータ部分を格納します。この部分が可変長なんです。
iDEF = self.IFF_DEF[self.IFF_DATA]
#siff[self.IFF_DATA] = b''.join(siff[sALL][iDEF[dStart]:iDEF[dStart] + siff[self.IFF_DATASIZE]])
siff[self.IFF_DATA] = alldata[iDEF[dStart]:iDEF[dStart] + siff[self.IFF_DATASIZE]]
#タグの部分はIFF_LISTで示す配列に格納します。
siff[self.IFF_LIST] = siff[sALL][44 + siff[self.IFF_DATASIZE]:]

#タグの部分の範囲を解釈して配列「XXXiffSubChunk」に格納します。
if (siff[self.IFF_LIST].size != 0):
sList = siff[self.IFF_LIST]
iDEF = self.IFF_DEF[self.IFF_LISTID]
siff[self.IFF_LISTID] = (b''.join(sList[iDEF[dStart]:iDEF[dStart] + iDEF[dByte]])).decode(iDEF[dEnc])
iDEF = self.IFF_DEF[self.IFF_LISTSIZE]
siff[self.IFF_LISTSIZE] = int.from_bytes(sList[iDEF[dStart]:iDEF[dStart] + iDEF[dByte]], tLittle)
iDEF = self.IFF_DEF[self.IFF_INFOID]
siff[self.IFF_INFOID] = (b''.join(sList[iDEF[dStart]:iDEF[dStart] + iDEF[dByte]])).decode(iDEF[dEnc])
offset = 12
for i in range(999):
chunkID = (b''.join(sList[offset:offset + 4])).decode(STRCODE)
subChunkSize = int.from_bytes((b''.join(sList[offset + 4:offset + 8])), tLittle)
chunkData = (b''.join(sList[offset + 8:offset + 8 + subChunkSize]).decode('SJIS'))
offset += (8 + subChunkSize + (subChunkSize % 2))
self.iffSubChunkID.append(chunkID)
self.iffSubChunkSize.append(subChunkSize)
self.iffSubChunkData.append(chunkData)
if siff[self.IFF_LISTSIZE] <= offset + 12:
break

def getBytesData(self, aAry, aStart, anEnd, aSize):
rtnBytes = b''
for i in range(int((anEnd - aStart) / aSize)):
if anEnd < aStart + (i + 1) * aSize:
break
rtnBytes += b''.join(aAry[aStart + (i * aSize):aStart + ((i + 1) * aSize)])
print(i)
if anEnd > (aStart + int((anEnd - aStart) / aSize) * aSize):
rtnBytes += b''.join(aAry[aStart + int((anEnd - aStart) / aSize) * aSize:])
return rtnBytes

pass

def print(self):
#読み込んだwavファイルの内容をレポートします。
siff = self.iff
sALL = self.IFF_ALL
dMetaID, dTitle, dType, dStart, dByte, dEnc = self.DEF_METAID, self.DEF_TITLE, self.DEF_TYPE, self.DEF_START, self.DEF_BYTE, self.DEF_ENC
tByte, tStr, tLittle = self.TYPE_BYTE, self.TYPE_STR, self.TYPE_LITTLE
print('Toral size = ' + "{0:,d}".format(siff[sALL].size))
for i in range(19)[1:]:
if (i == 14):
if (siff[self.IFF_LIST].size != 0):
print("-------------------------------------------")
continue
else:
#このブロックは調整中です
break
iDEF = self.IFF_DEF[i]
if iDEF[dType] == tStr:
print(iDEF[dTitle] + '=' + siff[i])
elif iDEF[dType] == tLittle:
print(iDEF[dTitle] + '=' + "{0:,d}".format(siff[i]))
print("<<This wave file contains [ " + "{0:,d}".format(siff[self.IFF_DATASIZE] // (self.SAMPLING_COUNT * siff[self.IFF_BLKSIZE])) + "] sound>>")
print("-------------------------------------------")

def printSize(self):
siff = self.iff
print("Chunk Size=" + siff[self.IFF_SIZE])

def getList(self):
return self.iffSubChunkID, self.iffSubChunkSize, self.iffSubChunkData

def cat(self, data):
#結合用のメソッドです。元のソースとチャンネル数、サンプリングレート、ビット数が異なればエラーにします。
#エラーがあれば結合しません。
if (self.iff[self.IFF_CHANNEL] != data.iff[data.IFF_CHANNEL]):
print("Channel is different base=" + "0:d".format(self.iff[self.IFF_CHANNEL]) +
"source=" + "0:d".format(data.iff[self.IFF_CHANNEL]))
return -1
if (self.iff[self.IFF_SAMPLING] != data.iff[data.IFF_SAMPLING]):
print("Samplint is different base=" + "0:d".format(self.iff[self.IFF_SAMPLING]) +
":source=" + "0:d".format(data.iff[self.IFF_SAMPLING]))
return -1
if (self.iff[self.IFF_BIT] != data.iff[data.IFF_BIT]):
print("Bit is different base=" + "0:d".format(self.iff[self.IFF_BIT]) +
":source=" + "0:d".format(data.iff[self.IFF_BIT]))
return -1
#結合するサンプリング数がSAMPLING_COUNTの整数倍でないと、エラーとします。
if (data.iff[data.IFF_DATASIZE] % self.SAMPLING_COUNT != 0):
print("Sampling count is not base on :" + "0:d".format(self.SAMPLING_COUNT) )

#結合は単純に足しているだけです。
self.iff[self.IFF_DATA] += data.iff[data.IFF_DATA]
self.iff[self.IFF_DATASIZE] += data.iff[data.IFF_DATASIZE]
self.iff[self.IFF_SIZE] += data.iff[data.IFF_DATASIZE]

def catInstrumentInfo(self, dir_name, info):
#ファイルから読み込んだデータについて、楽器情報を結合します。
tagInstr = self.INSTR_TAG
CategolyNo = dir_name[0:2]
InstNo = dir_name[2:]
if tagInstr in self.iffSubChunkID:
itagNo = self.iffSubChunkID.index(tagInstr)
else:
self.iffSubChunkID.append(tagInstr)
self.iffSubChunkData.append("")
self.iffSubChunkSize.append(0)
itagNo = self.iffSubChunkID.index(tagInstr)
sInfo = info.split('\n')
self.iffSubChunkData[itagNo] += '[' + CategolyNo + "," + InstNo + "," + sInfo[0] + "],"
self.iffSubChunkSize[itagNo] = len(self.iffSubChunkData[itagNo])

def catSoundInfo(self, dir_name, info):
# ファイルから読み込んだデータについて、各音の情報を結合します。
tagNote = self.NOTE_TAG
CategolyNo = dir_name[0:2]
InstNo = dir_name[2:]
if tagNote in self.iffSubChunkID:
itagNo = self.iffSubChunkID.index(tagNote)
else:
self.iffSubChunkID.append(tagNote)
self.iffSubChunkData.append("")
self.iffSubChunkSize.append(0)
itagNo = self.iffSubChunkID.index(tagNote)
sInfo = info.split('\n')
sInstr = sInfo[0]
sInstrInfo = sInstr.split(',')

for i in range(len(sInfo))[1:]:
sNoteInfo = sInfo[i].split(',')
if len(sInfo[i].strip(' ')) == 0:
#空の行があればそこで終了
break
self.iffSubChunkData[itagNo] += '[' + CategolyNo + ',' + \
InstNo + ',' +\
sNoteInfo[1] + ',' +\
sNoteInfo[2] + "],"
self.iffSubChunkSize[itagNo] = len(self.iffSubChunkData[itagNo])

def getWave(self):
#Waveデータのフォーマットでバイナリで取り出すメソッドです。
dMetaID, dTitle, dType, dStart, dByte, dEnc = self.DEF_METAID, self.DEF_TITLE, self.DEF_TYPE, self.DEF_START, self.DEF_BYTE, self.DEF_ENC
work = b''
workInfo = b'INFO'
for i in range(len(self.iffSubChunkID)):
workInfo += self.iffSubChunkID[i].encode(STRCODE)
workInfo += (self.iffSubChunkSize[i]+1).to_bytes(4, 'little')
workInfo += self.iffSubChunkData[i].encode(STRCODE) + b'\x00'
if ((len(self.iffSubChunkData[i].encode(STRCODE)) + 1) % 2 == 1):
workInfo += b'\x00'
workList = b'LIST' + (len(workInfo)).to_bytes(4, 'little') + workInfo
self.iff[self.IFF_SIZE] = self.iff[self.IFF_SUBSIZE] + self.iff[self.IFF_DATASIZE] + len(workInfo) + 28
for i in range(14):
siffData = self.iff[i]
iDEF = self.IFF_DEF[i]
if (iDEF[dType] == self.TYPE_STR):
work = work + siffData.encode(iDEF[dEnc])
elif (iDEF[dType] == self.TYPE_LITTLE and iDEF[dByte] == 2 ):
work = work + siffData.to_bytes(2, 'little')
elif (iDEF[dType] == self.TYPE_LITTLE and iDEF[dByte] == 4):
work = work + siffData.to_bytes(4, 'little')
work = work + self.iff[self.IFF_DATA]
work += workList
return work

def getChunkData(self, aChunkID):
for i in range(len(self.iffSubChunkID)):
chunkName = self.iffSubChunkID[i]
if chunkName == aChunkID:
return self.iffSubChunkData[i]
return []


条件 

 ・このプログラムは音をだすためのデータが必要です。小さなサウンドデータベースフォーマットのデータを準備しています(Wavファイル)。動作させるために一部環境調整が必要な場合があります。

・anaconda が導入済(はいっていること)であることを想定(そうてい)しています。はいっていない最初の状態からのいれかたは「インストール編」をみてね。

f:id:np2LKoo:20170807234000p:plain

おわび

・無駄に長くてすみません。
・8/5(土曜日)にQtとかマルチスレッドとか全然しらなくてつくりはじめました。勉強中です。うまく動かなかったらなおしますので、怒(おこ)らないでね。

・遅いマシンだと、音が途切(とぎ)れたり、タイミングの判定(はんてい)が正確(せいかく)でなかったりします。

・実験レベルのプログラムなので、環境によってはプログラムの調整が必要な場合があります。

オートマトンという言葉を早くしりたい人はこんな本を読んでみてもいいかもです。おとうさんか、おかあさんが持っているかもしれないので、聞いてみよう!

計算機音楽の自由研究(準備:その4)機械学習に適した音・サウンドのデータセット「あるよ」

寸劇


かえるのクー:「マスター。ひさしぶり、お疲れっす。ジンジャエールがいいかな。リーズ・ジンジャー・ブリューなんておいてないよね」

f:id:np2LKoo:20170727231050p:plain

マスター:「...あるよ。」

マスターはコースターの上にグラスをおいて、ビンからリーズ・ジンジャー・ブリューを注いだ。
かえるのクー:「えーすげー。キンキンに冷えてる~。ボクがきてこれを注文するのわかってた?」
マスター:「...」
かえるのクー:「ところで最近、音楽関連に興味があって、音のデータセットをつくっているんだ。この店、通販やネットの取り扱いもしているみたいだけど、そんなのないよね?」
マスター:「...あるよ。」
かえるのクー:「え~マジっすか。でも、申請もなく無料で使えて全部で60,000音以上そろっているのなんてそうそうないよね。」
マスター:「...あるよ。」
かえるのクー:「ヤバイ。機械学習用につかいたいんだけど、各楽器いろんな音の高さと強さで、種類は10種類くらいはないと。いくらなんでも、そこまでは無理だよね。」
マスター:「...あるよ。」
かえるのクー:「マジやばくないっすか~。音がそろっているだけじゃなくて、JSONとかで、その音の情報がそろったものがいいんすけど」
マスター:「...あるよ。」
かえるのクー:「ちょ、ちょっとまてよ~、それ、本当すか。ボクはずっとネットで探していたんだけど、ぶっちゃけ、みつかんなかったすけど。」
マスター:「...あるよ。」
マスターはジンジャエールグラスの下に敷いたコースタを指さした。そのコースターはGoogleのロゴ下にmagentaとかかれたプロモーション用と思われるものだった。URLもかいてある。
かえるのクー:「ああ、magentaNsynthだったら知ってる。それ、音を生成するやつだよね。生成されるやつじゃなくて、元ネタがほしいんだよね~。」マスターはコースターを裏返して、上目づかいににこりともせずぼくをみた。
コースターの裏には「The NSynth Dataset Apr 5, 2017」とかかれていた。マスタはさらに言った。
マスター:「...あるよ。」


言い訳

 寸劇をやらずにいられないボクの複雑な心境をお察しください。

NSynthの存在も知っていました。サウンドデータベース、データセット検索もしましたとも。でもNSynthの一環でデータセットが公開されているのは知りませんでした。
googleでサウンド 機械学習 データセット もしくはデータベース で検索してもサウンドに線が引かれ、mnistやcifar-10が検索されるばかり。いくつかの大学や研究機関からは有償の媒体提供が可能などはありました。そうです。現時点で検索してもほとんどNSynthのデータセットは検索にかかりません。Youtube-8Mは知ってます。けど、すぐに機械学習にかけられるMNISTと同じようなサウンドデータベース・サウンドデータセットが欲しかったんです。
ないので作っていましたが、ありました
日本でも外国でも紹介されるのはNSynthやA.I DuetばかりでNSynth Dataset を取りあげてくれてないようです。声を大にしていいます。

機械学習そのまま使えるサウンドデータセット ありますgoogle magentaチームの NSynth Datasetです。

f:id:np2LKoo:20170727212831p:plain

ボクが考えていたのとほぼ同じ(あるいはあきらめていた)すべてのフルスペックを装備しています。

ボクの「サウンドデータベース作成」はあえなく中止・終了となることを潔く宣言します。丸いタイヤが無料で手に入るのにごつごつの車輪を開発するようなことは無意味とおもわれます。(自由研究のコンセプトからすれば、「なしよりのあり」ではありますが)

オリジナルサイトは以下です。(ダウンロードはこちらから)

magenta.tensorflow.org

それではデータセットのスペックをみていみましょう。

おもいっきり意訳

画像機械学習、生成モデリングのためのMNIST、CIFAR、およびImageNetと同様の扱い易いオーディオデータセットが必要だ。ないのでつくっちゃいました。どんどん使ってね。

主なスペック

全部で 305,979 (30万音超え!)の楽音からなり、訓練用に289,205、検証用に12,678、テスト(本番)用に4,096 準備しています。それぞれに重複はありません。以下は「json/wav」のファイル容量例です。

train / nsynth-train.jsonwav.tar.gz / 23.2GB /解凍後 34.6GB

valid / nsynth-valid.jsonwav.tar.gz / 1.04GB /解凍後 1.51GB

test / nsynth-test.jsonwav.tar.gz / 341MB /解凍後 502MB

テンソルフロー用にすぐ使用できるシリアライズされた「TFRecord files」と一般的な「json/wav」がそれぞれ提供されており、どちらかを使用します。

各音は16bit、4秒、モノラル16kHzサンプリングで、MIDIノートで21~88(ピアノ88鍵に相当)で、MIDI Volume=(25,50,75,100,127)のベロシティで個別に録音されています。

楽器の音のでる範囲で録音されていますので、楽器によってノート範囲が異なります。

以下の種類があります。(11種類)

Family Acoustic Electronic Synthetic Total
Bass 200 8,387 60,368 68,955
Brass 13,760 70 0 13,830
Flute 6,572 35 2,816 9,423
Guitar 13,343 16,805 5,275 35,423
Keyboard 8,508 42,645 3,838 54,991
Mallet 27,722 5,581 1,763 35,066
Organ 176 36,401 0 36,577
Reed 14,262 76 528 14,866
String 20,510 84 0 20,594
Synth Lead 0 0 5,501 5,501
Vocal 3,925 140 6,688 10,753
Total 108,978 110,224 86,777 305,979

 

音質はそれぞれ以下のような配分になっています。

Quality Bright Dark Distortion Fast Decay Long Release Multiphonic Nonlinear Envelope Percussive Reverb Tempo-Synced
Dark 0.0                  
Distortion 25.9 2.5                
Fast Decay 10.0 7.5 8.1              
Long Release 9.0 5.2 9.8 0.0            
Multiphonic 6.0 1.5 5.4 2.8 6.9          
Nonlinear Envelope 8.5 1.4 6.6 2.1 6.7 8.6        
Percussive 6.2 5.1 3.0 52.0 0.8 2.4 0.9      
Reverb 6.6 8.9 0.3 13.0 13.7 0.7 3.5 12.4    
Tempo-Synced 2.4 1.8 5.2 0.4 6.4 9.3 2.3 1.5 0.0  
Frequency 13.5 11.0 17.0 14.7 8.5 3.4 3.2 10.2 16.8 1.8

 

ライセンス

The dataset is made available by Google Inc. under a Creative Commons Attribution 4.0 International (CC BY 4.0) license.

クリエイティブ・コモンズ・ライセンスです。CC BY なので、任意の媒体や形式でコピーし、再配布可能です。リミックスや改変してつかったり、素材として使用し作品をつくるなど、利用目的によらず、商用でも使用可能です。

使用した作品などを公開する場合は、原作者クレジット表記は必要です。

f:id:np2LKoo:20170727220731p:plain

 

ほぼすべてにおいてボクの考えていたことを凌駕していますが、1点だけ不満があります。

各音4秒にするなら1.5とか2秒にしてもいいから「48KHzサンプリング」にしてほしかったです。ぜいたくなのは分かっていますが、ここまでするならそこも欲しかった。(ボクのDB案では時間を犠牲にして、サンプリングレートを優先させていました)

ダウンサンプリングしたあと、アップサンプリングするときに、欠落した「周波数」を学習させ、それ以上の周波数構成を予測する「疑似ハイレゾ化学習」の実験がやりたかったためです。とはいえ、ほぼ120点満点の構成で大満足です。*1音はきいているところですが、各種奏法での音もあったりしてバリエーションに富み、シンプルですっきりした音です。(16kHzサンプルなのでいささかすっきりしすぎですが、音に不満は(いまのところ)ありません。

ライセンス順守のもと2次配布OKなので、NSynth Datasetを元にして、実験に適した形に前処理したものを作成して実験していく方向にしたいと考えています。

以上、報告と簡単な紹介でした。

さぁ。みんなでNSynth Datasetを使って、音の実験をしよう!!!今年の夏は暑いぜ!!!

 

 

*1:その後確認してつっこみどろが結構でてきたので、20点減点して100点満点にします。音の好みが偏りすぎな点(SytheBassとMalletが異常に好きなんですね。)と、AcousticのPianoとStringがバランス的に少ない点です。

計算機音楽の自由研究(準備:その2.9)~機械学習用サウンドデータベースをつくる(発動編)

はじめに

ファイルの結合ができたのでタグ情報を付加してみます。前回「結合編」と付けましたが、今回も続きのためXX編にしようと思いました。いいのが思いつかないので助手に意見を求めたところ「発動編がいいのでは?」「最初のは接触編にしたほうがよかったね。」と意味不明なことを言われました。他に思いつかないので、発動編にしました。内容と合っていない気がしますが、何かを発動します。

f:id:np2LKoo:20170521090240p:plain

(助手はコレジャナイとさらに意味不明。 井出の力って何?)

f:id:np2LKoo:20170521200508p:plain

これでもなさそうだし。

 

f:id:np2LKoo:20170521233059j:plain

見つけました!これですね。これ発動していいやつ?

なお、助手の井戸中を井出上に改名する案は却下します。

(6月末まで反省キャンペーン中です。内容を順次リファインします。)

 機械学習用に付加する情報について

各音の情報についてJSONにしようと思っていましたが、いざ書いてみると(1行表示なので)逆にとても読みにくかったです。JSONはあっさりヤメます。フツーの配列(リスト)表記にします。
以下はまだ案で、試行テストで不都合があれば変ます。(個人的な要件のメモなので読み飛ばしてください)
大きくわけて


(1)楽器の音の情報:ソース情報
(2)(1)の楽器の音の音階などの情報:各サンプル情報


のように2段階の情報にします。

(1)ソース情報は以下の案です。


[カテゴリ番号,ソース番号, [メーカー,地域,...],音源説明,補足],...


カテゴリ番号:

準備その1で決めた分類を使います。

当面99以下しか使わない前提で「今は」2桁にしてみます。

ソース番号:

カテゴリ番号での単なる順番です。

基本的に1から始めますが、分類上番号が連続していなくても可とします。

[メーカー,地域,...]:

別途きめます。

(ない場合は不明な場合は[]だけです。[ ]内の要素数は不定です。

(ex.[Steinway & Son's, Germany,Model A-188,NHK Hall]))

音源説明: 任意の文字(ただし全角、半角カナは不可)
補足: 任意の文字(〃)

 

(2)各サンプル情報は以下の案です。


[カテゴリ番号, ソース番号, 音階, 識別, [拡張情報]],...


とします。

カテゴリ番号:

準備その1で決めた分類を使います。(1)と同じ

当面99以下しか使わない前提で「今は」2桁にしてみます。

ソース番号:

カテゴリ番号での単なる順番です。(1)と同じ

基本的に1から始めますが、分類上番号が連続していなくても可とします。

音階:

1~127はMIDIノート番号,平均律(12クロマ)におしこめられるもののみ

0は音階なし(もしくは不明)

識別:

※音階、識別のどちらか一方は必須

音階でない音の識別1桁目が識別で2桁以降が識別詳細データ。

純正率などを調ごとに厳密にやりたい場合はこちらも使うことになると思います。(複数の調の音を識別する?)民族音楽系もおおくはこちらでしょうか。。。(サンプリング済のものは音階でよいと思います)

(識別の補足) 0は識別なし。
 言語音声の場合: VX(Xには発音記号半角アルファベットがはいるボカロ発音記号準拠。桁数任意)
 代表周波数の場合: Fnnnnn(nnnnには代表周波数(Hz)がはいります)
 そのた諸々 必要の都度、識別記号と識別内容を発案してくわえます(笑い声とか鳴き声とかetc)
[拡張情報]:

今はリザーブ、案として

サブカテゴリ:DBごとに任意
感情(その音で呼び出される感情の種類:主観でよい)
状態(機械動作音であれば、故障中(故障パターン識別)とか故障直前とか、初期起動音とか、エージング終了後の動作音とかの種類)
いずれも1桁目は分類のIDがはいり、2桁目以降は内容を記載します。
拡張情報は任意に複数持てる。ない場合は空の [ ] を書きます。

(1),(2)共通で以下のようにします。

なお、

■情報はすべてアルファベットと記号、数字とする。(漢字、カナは不可)
■文字にセミコロンは使用しない。
■, [ ] は配列の構造に使用するので、データとしては使用しない
■文字列は"や'で囲まない
情報の保存場所とファイル名
■データ作成の効率を考え、該当サウンドDB作成に仕様するフォルダをXXYYYX,Yは数字)の5桁とする。XXはカテゴリ番号、YYYはソース番号とする。
■情報とwaveファイルはは各フォルダ(0000199999)の中にフォルダ名と同じファイル名 (02001.txt,02001.wavなど)で保持する。

XX,YYYのペアは作成するサウンドDB内でのみ一意とする。(他のサウンドDBではまた別の音を入れてももちろんよい)

■登録用のファイルは、作成がしやすいように若干フォーマット調整する。
■ソース情報、サンプル情報は登録用はまとめて1ファイルのテキストにする。
■■1行目は(1)ソース情報とする。(カテゴリ番号,ソース番号, [メーカー,地域,...],音源説明, 補足)
■■2行目以降は(2)各サンプル情報とする。各行に(サンプル番号(1から連番),音階, 識別, [拡張情報])をかく
■■これら情報はwave登録用のフォーマットに整形のうえ、wavファイルの所定のタグに登録する。

----------------------------------------------
02002.txt
----------------------------------------------
2,2, [ ] ,Keppy Steinway Piano V6.x,SoundFont
1,21,0, [ ]
2,22,0, [ ]
3,23,0, [ ]
...
88,108,0, [ ]
----------------------------------------------
のようにします。サンプル番号を書くのは、登録ミスチェック用です。音階を全部書くのは、特定12半音がでない(近似該当がない)楽器があるためです。

プログラムソース

 すこし長いですが、切り出してもわかりにくいので、そのまま貼ります。

import numpy as np
import os.path
import re

STRCODE = 'utf-8'
'''
=====================================================================================
機械学習用waveファイル結合プログラム
(複数のファイルを結合するとともに、機械学習用の付加情報をwaveファイルに取り込みます)
=====================================================================================
'''

class VWave:
# 読み込んだwaveファイルの情報を格納するクラスです。
SAMPLING_COUNT = 32768
INSTR_TAG = 'ISFT'
NOTE_TAG = 'ICMT'
# 地道にwavフォーマット情報を定義していきます。
#
IFF_ALL = 0
IFF_ID, IFF_SIZE, IFF_FMT, IFF_SUBID, IFF_SUBSIZE = 1, 2, 3, 4, 5
IFF_WAVFOMT, IFF_CHANNEL, IFF_SAMPLING, IFF_BYTEPS, IFF_BLKSIZE = 6, 7, 8, 9, 10
IFF_BIT, IFF_DATAID, IFF_DATASIZE, IFF_DATA, IFF_LIST = 11, 12, 13, 14, 15
IFF_LISTID, IFF_LISTSIZE, IFF_INFOID = 16, 17, 18
TYPE_BYTE = "byte"
TYPE_STR = "string"
TYPE_LITTLE = "little"
DEF_METAID, DEF_TITLE, DEF_TYPE, DEF_START, DEF_BYTE, DEF_ENC = 0, 1, 2, 3, 4, 5
IFF_DEF = [
[IFF_ALL, "ALL DATA ", TYPE_BYTE, None, None, None ],
[IFF_ID, "ID ", TYPE_STR, 0, 4, STRCODE],
[IFF_SIZE, "Chunk Size", TYPE_LITTLE, 4, 4, None],
[IFF_FMT, "FormatName", TYPE_STR, 8, 4, STRCODE],
[IFF_SUBID, "Sub ID ", TYPE_STR, 12, 4, STRCODE],
[IFF_SUBSIZE, "Sub Size ", TYPE_LITTLE, 16, 4, None],
[IFF_WAVFOMT, "WavFomat ", TYPE_LITTLE, 20, 2, None],
[IFF_CHANNEL, "Channel ", TYPE_LITTLE, 22, 2, None],
[IFF_SAMPLING,"Sampling ", TYPE_LITTLE, 24, 4, None],
[IFF_BYTEPS, "BytePS ", TYPE_LITTLE, 28, 4, None],
[IFF_BLKSIZE, "BlockSize ", TYPE_LITTLE, 32, 2, None],
[IFF_BIT, "Bit ", TYPE_LITTLE, 34, 2, None],
[IFF_DATAID, "DataID ", TYPE_STR, 36, 4, STRCODE],
[IFF_DATASIZE,"DataSize ", TYPE_LITTLE, 40, 4, None],
[IFF_DATA, "IFF Data ", TYPE_BYTE, 44, None, None],
[IFF_LIST, "List ", TYPE_BYTE, 0, None, None ],
[IFF_LISTID, "List ID ", TYPE_STR, 0, 4, STRCODE ],
[IFF_LISTSIZE,"List Size ", TYPE_LITTLE, 4, 4, None],
[IFF_INFOID, "INFO ID ", TYPE_STR, 8, 4, STRCODE]
]
#読み込んだ情報を格納する配列です。最初はクラス変数を使っていましたが、多すぎてわかりにくくなったため、
#すべて配列で持つことにしました。
iff = []
iffSubChunkID = []
iffSubChunkSize = []
iffSubChunkData = []

def __init__(self, data):
self.iff =[b'', "", 0, "", "", 0, 0, 0, 0, 0, 0, 0, "", 0, b'', b'', "", 0, ""]
siff = self.iff
sALL = self.IFF_ALL
dMetaID, dTitle, dType, dStart, dByte, dEnc = self.DEF_METAID, self.DEF_TITLE, self.DEF_TYPE, self.DEF_START, self.DEF_BYTE, self.DEF_ENC
tByte, tStr, tLittle = self.TYPE_BYTE, self.TYPE_STR, self.TYPE_LITTLE
# 以降全体から特定部分へアクセスするためにバイト配列にして格納します。
siff[sALL] = np.frombuffer(data, dtype=self.IFF_DEF[sALL][dType])
#順次定義の内容に基づき、data内容を解釈して配列に格納します。
for i in range(14)[1:]:
iDEF = self.IFF_DEF[i]
if iDEF[dType] == tStr:
siff[i] = (b''.join(siff[sALL][iDEF[dStart]:iDEF[dStart] + iDEF[dByte]])).decode(iDEF[dEnc])
elif iDEF[dType] == tLittle:
siff[i] = int.from_bytes(siff[sALL][iDEF[dStart]:iDEF[dStart] + iDEF[dByte]], tLittle)

#Waveデータ部分を格納します。この部分が可変長なんです。
iDEF = self.IFF_DEF[self.IFF_DATA]
siff[self.IFF_DATA] = b''.join(siff[sALL][iDEF[dStart]:iDEF[dStart] + siff[self.IFF_DATASIZE]])
#タグの部分はIFF_LISTで示す配列に格納します。
siff[self.IFF_LIST] = siff[sALL][44 + siff[self.IFF_DATASIZE]:]

#タグの部分の範囲を解釈して配列「XXXiffSubChunk」に格納します。
if (siff[self.IFF_LIST].size != 0):
sList = siff[self.IFF_LIST]
iDEF = self.IFF_DEF[self.IFF_LISTID]
siff[self.IFF_LISTID] = (b''.join(sList[iDEF[dStart]:iDEF[dStart] + iDEF[dByte]])).decode(iDEF[dEnc])
iDEF = self.IFF_DEF[self.IFF_LISTSIZE]
siff[self.IFF_LISTSIZE] = int.from_bytes(sList[iDEF[dStart]:iDEF[dStart] + iDEF[dByte]], tLittle)
iDEF = self.IFF_DEF[self.IFF_INFOID]
siff[self.IFF_INFOID] = (b''.join(sList[iDEF[dStart]:iDEF[dStart] + iDEF[dByte]])).decode(iDEF[dEnc])
offset = 12
for i in range(999):
chunkID = (b''.join(sList[offset:offset + 4])).decode(STRCODE)
subChunkSize = int.from_bytes((b''.join(sList[offset + 4:offset + 8])), tLittle)
chunkData = (b''.join(sList[offset + 8:offset + 8 + subChunkSize]).decode('SJIS'))
offset += (8 + subChunkSize + (subChunkSize % 2))
self.iffSubChunkID.append(chunkID)
self.iffSubChunkSize.append(subChunkSize)
self.iffSubChunkData.append(chunkData)
if siff[self.IFF_LISTSIZE] <= offset + 12:
break

def print(self):
#読み込んだwavファイルの内容をレポートします。
siff = self.iff
sALL = self.IFF_ALL
dMetaID, dTitle, dType, dStart, dByte, dEnc = self.DEF_METAID, self.DEF_TITLE, self.DEF_TYPE, self.DEF_START, self.DEF_BYTE, self.DEF_ENC
tByte, tStr, tLittle = self.TYPE_BYTE, self.TYPE_STR, self.TYPE_LITTLE
print('Toral size = ' + "{0:,d}".format(siff[sALL].size))
for i in range(19)[1:]:
if (i == 14):
if (siff[self.IFF_LIST].size != 0):
print("-------------------------------------------")
continue
else:
#このブロックは調整中です
break
iDEF = self.IFF_DEF[i]
if iDEF[dType] == tStr:
print(iDEF[dTitle] + '=' + siff[i])
elif iDEF[dType] == tLittle:
print(iDEF[dTitle] + '=' + "{0:,d}".format(siff[i]))
print("<<This wave file contains [ " + "{0:,d}".format(siff[self.IFF_DATASIZE] // (self.SAMPLING_COUNT * siff[self.IFF_BLKSIZE])) + "] sound>>")
print("-------------------------------------------")

def printSize(self):
siff = self.iff
print("Chunk Size=" + siff[self.IFF_SIZE])

def getList(self):
return self.iffSubChunkID, self.iffSubChunkSize, self.iffSubChunkData

def cat(self, data):
#結合用のメソッドです。元のソースとチャンネル数、サンプリングレート、ビット数が異なればエラーにします。
#エラーがあれば結合しません。
if (self.iff[self.IFF_CHANNEL] != data.iff[data.IFF_CHANNEL]):
print("Channel is different base=" + "0:d".format(self.iff[self.IFF_CHANNEL]) +
"source=" + "0:d".format(data.iff[self.IFF_CHANNEL]))
return -1
if (self.iff[self.IFF_SAMPLING] != data.iff[data.IFF_SAMPLING]):
print("Samplint is different base=" + "0:d".format(self.iff[self.IFF_SAMPLING]) +
":source=" + "0:d".format(data.iff[self.IFF_SAMPLING]))
return -1
if (self.iff[self.IFF_BIT] != data.iff[data.IFF_BIT]):
print("Bit is different base=" + "0:d".format(self.iff[self.IFF_BIT]) +
":source=" + "0:d".format(data.iff[self.IFF_BIT]))
return -1
#結合するサンプリング数がSAMPLING_COUNTの整数倍でないと、エラーとします。
if (data.iff[data.IFF_DATASIZE] % self.SAMPLING_COUNT != 0):
print("Sampling count is not base on :" + "0:d".format(self.SAMPLING_COUNT) )

#結合は単純に足しているだけです。
self.iff[self.IFF_DATA] += data.iff[data.IFF_DATA]
self.iff[self.IFF_DATASIZE] += data.iff[data.IFF_DATASIZE]
self.iff[self.IFF_SIZE] += data.iff[data.IFF_DATASIZE]

def catInstrumentInfo(self, info):
#ファイルから読み込んだデータについて、楽器情報を結合します。
tagInstr = self.INSTR_TAG
if tagInstr in self.iffSubChunkID:
itagNo = self.iffSubChunkID.index(tagInstr)
else:
self.iffSubChunkID.append(tagInstr)
self.iffSubChunkData.append("")
self.iffSubChunkSize.append(0)
itagNo = self.iffSubChunkID.index(tagInstr)
sInfo = info.split('\n')
self.iffSubChunkData[itagNo] += '[' + sInfo[0] + "],"
self.iffSubChunkSize[itagNo] = len(self.iffSubChunkData[itagNo])

def catSoundInfo(self, info):
# ファイルから読み込んだデータについて、各音の情報を結合します。
tagNote = self.NOTE_TAG
if tagNote in self.iffSubChunkID:
itagNo = self.iffSubChunkID.index(tagNote)
else:
self.iffSubChunkID.append(tagNote)
self.iffSubChunkData.append("")
self.iffSubChunkSize.append(0)
itagNo = self.iffSubChunkID.index(tagNote)
sInfo = info.split('\n')
sInstr = sInfo[0]
sInstrInfo = sInstr.split(',')

for i in range(len(sInfo))[1:]:
sNoteInfo = sInfo[i].split(',')
if len(sInfo[i].strip(' ')) == 0:
#空の行があればそこで終了
break
self.iffSubChunkData[itagNo] += '[' + sInstrInfo[0] + ',' +\
sInstrInfo[1] + ',' +\
sNoteInfo[1] + ',' +\
sNoteInfo[2] + ',' +\
sNoteInfo[3] + "],"
self.iffSubChunkSize[itagNo] = len(self.iffSubChunkData[itagNo])

def getWave(self):
#Waveデータのフォーマットでバイナリで取り出すメソッドです。
dMetaID, dTitle, dType, dStart, dByte, dEnc = self.DEF_METAID, self.DEF_TITLE, self.DEF_TYPE, self.DEF_START, self.DEF_BYTE, self.DEF_ENC
work = b''
workInfo = b'INFO'
for i in range(len(self.iffSubChunkID)):
workInfo += self.iffSubChunkID[i].encode(STRCODE)
workInfo += (self.iffSubChunkSize[i]+1).to_bytes(4, 'little')
workInfo += self.iffSubChunkData[i].encode(STRCODE) + b'\x00'
if ((len(self.iffSubChunkData[i].encode(STRCODE)) + 1) % 2 == 1):
workInfo += b'\x00'
workList = b'LIST' + (len(workInfo)).to_bytes(4, 'little') + workInfo
self.iff[self.IFF_SIZE] = self.iff[self.IFF_SUBSIZE] + self.iff[self.IFF_DATASIZE] + len(workInfo) + 28
for i in range(14):
siffData = self.iff[i]
iDEF = self.IFF_DEF[i]
if (iDEF[dType] == self.TYPE_STR):
work = work + siffData.encode(iDEF[dEnc])
elif (iDEF[dType] == self.TYPE_LITTLE and iDEF[dByte] == 2 ):
work = work + siffData.to_bytes(2, 'little')
elif (iDEF[dType] == self.TYPE_LITTLE and iDEF[dByte] == 4):
work = work + siffData.to_bytes(4, 'little')
work = work + self.iff[self.IFF_DATA]
work += workList
return work

#出力するDBの名前です。
CAT_WAVE_FILE='.\\KooSoundDB.wav'
sCount = 0
#カレントディレクトリで、"00001"から"99999"(5桁)のフォルダがあれば名前を取得してソートします
output_path = "."
files = os.listdir(output_path)
list_dir = [f for f in files if os.path.isdir(os.path.join(output_path, f))]
sound_dir = [i for i in list_dir if re.search(r'[0-9][0-9][0-9][0-9][0-9]', i)]
sound_dir.sort()
print(sound_dir)
#フォルダ名と同じ番号のwavファイルがあるか調べます。
for dir_name in sound_dir:
fileName = dir_name + "\\" + dir_name + ".wav"
infoFileName = dir_name + "\\" + dir_name + ".txt"
if os.path.isfile(fileName):
if not os.path.isfile(infoFileName):
print("Not exist:" + infoFileName)
continue
sCount += 1
print ("exist:" + fileName)
f = open(fileName, 'rb')
fData = f.read()
t = open(infoFileName, 'r')
tData = t.read()
waveData = VWave(fData)
if (sCount == 1):
# 1個目は元データそのものです。
catWave = waveData
else:
# 2個目以降は結合していきます。
catWave.cat(waveData)
waveData.print()
waveData.catInstrumentInfo(tData)
waveData.catSoundInfo(tData)
#タグリストはget/setするので確認用です。setはまだ作ってません。
subChunkID, subChunkSize, subChunkData = waveData.getList()
for i in range(len(subChunkID)):
print(">" + subChunkID[i] + ":{0:4d}:".format(subChunkSize[i]) + subChunkData[i])
f.close()
t.close()
print("===========================================")
print("Output file name:" + CAT_WAVE_FILE)
print("===========================================")
catWave.print()
#出力用のファイルをオープンし、結合演算済の内容をwavファイルに出力します。
fw = open(CAT_WAVE_FILE, 'wb')
fw.write(catWave.getWave())
fw.close()

 前のプログラムからタグ関連の編集を追加しました。差分だけのせてもわかりにくいので、全部はりました。

 まとめ 

簡単なテストではOKでした。

このプログラムでファイルを2000個以上、容量で6GB(10万音でそれくらい)まで結合できるかテストが必要です。一度全部メモリに乗せる単純なつくりなので、問題があればPGを改変します。(テストはたいへんなわりにおもしろくないので、結果だけいずれ報告します。)最終的なファイルは1つあたり、アップロードの扱いやすさの感覚的な上限の1GB以下がいいと感じていますが、10万音だと6GBくらいになります。圧縮して1GB以下になればよしとしますが、それでも超えるようだとどこかを調整して「圧縮して1GB以下にします」(1ファイル2GBまでならOKなフリーのネットストレージサービスもあるようなので、2GBを上限にするかもしれません。。。)

他のタグの情報も若干追加してプログラムの完成となる予定です。 

祈りをいま君のもとへ。

もしもボクニューロンの1細胞ならばのです

1人称研究というのがあります。人類のいままでの科学や実験では「客観性」や「再現性」を重視してきました。ある特定の視点を欠落させている反省から、「1人称主観」による情報処理結果、判断を重要視する考え方がでてきたようです。(内容はあまり理解していないので説明できません。すみません)
1人称研究の本は読んだことがないのですが、是非読んでみたいです。*1それとは別ですが、ボクのブログで推奨している(といっても助手が推奨していて、ボクが同感しているだけですが)「自在主観」があります。*2自己符号化器の自由研究にあたっては「ジブンがもし1つのニューロン細胞であったら、どう成長し、何を日々の糧とし、何を喜びとし、何を目的に生き、どう死ぬのか」を考え続けています。

1人称といえば

ブログにつかう1人称を何にするかも話題です。
このブログは、複数によって運営されています。(人工知能はもちろん含まれていませんのでご安心ください。ただし開発できたらこっそりメンバーが増えるかもしれません。)このブログにはすこしだけ規定(レギュレーション)があり、「1人称については固有のものを使う」ことを決め事にしています。
性別や年齢、リアルの立場、(現在の)プライベートに関することは公開してはいけないことにしています。(そのほうがおもしろそうなので、という単純にして最強の理由です。)それ以外は各自の判断で自由です。このブログでは1人称はそれぞれ各者固有としています。*3


このおはなしの登場人物 / いきもの


ボク:かえるのクー (井戸中 空)/ Koo Wells
わたくし:井戸中 聖(イトナカ アキラ)
吾(あ):井戸中 芽守(イトナカ メモル)
未発言:井戸中 瑠璃(イトナカ ルリ)(るぅりぃ)
未発言:井戸中 明日良(イトナカ アスラ)(アッシュ、あーくん)


「君の名は」でジブンの1人称に関する(外国の人には絶対伝わらないと思う)「ギャグ」があり、気に入っています。1人称がこれだけ多岐にわたり、発言する人の社会的地位、性別、シュチュエーションでバリエーションがある言語は、日本語がトップクラスであるとききました。ということで、1人称で遊んでいます。なおこのブログでは「わたし」は大好きな「人類は衰退しました」の「わたし」ちゃんの固有名詞である扱いなのでだれも使えません。(ああ、Voltaよりもこのえんばんがほしい。ねたばれ、みるです?*4) 

*1:一般的に考えると統計的に十分な数の「1人称主観」の判断を、主観者についてグループ化すれば、そのグループごとの客観視点(おおまかな総意)とバリエーション(個々の主観のブレ)があきらかになります。もっと大数でサンプリングすれば、人類の思考パターン(の流行)が明確になります。そのうち「人工知能」にこれらの情報がinputされると考えています。

*2:助手の造語と思われます。「執筆依頼があれば書くよ」と助手たちの言動は本当に訳がわかりません。自在主観とは対象物そのものに「本当になったつもり」になって思考する発想テクニックです。(本気度がないと単なる思考の遊びにおわります)ジブンが孫〇義や、イー論マ〇クになったつもりで思考し、そのビジネス思考パタンをなぞる練習にも使えます。絶対絶命の状態や社内的窮地を想像できると、臨場感が増し効果があると考えます。ヒトではなく「モノ」になりきる主観のほうが、実験や研究では違った発想を思いつけることがあるので有用だと力説してました。

*3:1人称の決定権は皆には不評ですがボクが持っています。

*4:妖精さんのちからでもいいから、このブログ「せんきゃくばんらいですからー」と言ってみたい。妖精さん密度100fくらいになれば、きっと人工知能なんて1晩で完成するんでしょうが。

計算機音楽の自由研究(準備:その2.8)~機械学習用サウンドデータベースをつくる(結合編)

はじめに

今年は春のけだるさ病*1にはかからなかったようです。熱中できることを持つことは素晴らしいと感じました。

(準備:その2.6)で、一連の音の作成(たとえばあるVSTi音源のMIDIノート21~108の音)までは、設定して実行すれば簡単に行えるようになりました。

f:id:np2LKoo:20170519231630p:plain

パソコンの処理能力が低いので音を出すと(録音すると)よくノイズが乗ることがあります。外部ノイズではなく、明らかにCPU側能力不足のノイズです。そのため、もっと多数の音を一度に作成することはぜず、一旦作成単位のwavファイル(30音~100音くらい)ごとに、ノイズが乗っていないかチェックします。そのあとで、まとめて1つのDBに結合することを考えます。(6月末まで反省キャンペーン中です。内容を順次リファインします。)

機械学習用ファイル作成プログラムの要件

・複数の所定のフォルダに格納されたwavファイルを結合する。
・同じ条件(サンプリングレートやビット数など)の録音でないとチェックしてエラーとする。
所定のフォルダは数字5桁(00001,00002...)などに決め、その中にフォルダと同名のwavファイル(名前は任意、拡張子は.wav)を置く。(わざわざ00001などのフォルダをつくるのは、その中に複数の候補や作成補助用の(DBには直接関係しない)ファイルをもたせたいからです。)

以下の実装はまた今度しますが、定義だけしておきます。
・準備:その2.7で確認したタグのフォーマットを参考にコメント(ICMT)欄に音(0.68秒のサンプル単位)に関する基本情報を書く。(内容は別途きめる)
・ソース(ISRC)欄に音に関する情報を書く(音源の単位)
・DBに関するサマリ情報をプロダクト(アルバム)欄(IPRD)に書く
・いろいろな情報を乗せても、wavファイル単独で音が聞けるようにする。
・データベースが大きくなると、音の頭出しが難しくなるので、情報を指定すると特定の音を簡単に再生できるしくみを考える。(これは結合用プログラムとは別に考える)

結合用のプログラムを作成してみます。

準備:その2.7のプログラムを元にもうすこし汎用化して結合機能をもたせてみます。

汎用化すると逆に「読みにくい」ソースとなってしまいました。*2小出しで本当に申し訳ないですが、今回は結合する部分だけです。作成中ながら貼っちゃいます。結合する部分と出力する部分だけならあわせて10~20行程度なのですが、内容確認用の情報表示を汎用的にできるように作成したので長くなってしまいました。Python標準的な命名規約には全く従っていません。わかりやすいソースが書けるように勉強中です。

waveファイルのフォーマットについては前にも貼りましたが、こちらを参照してください。実験用でテストも十分行っていないので、間違いがあっらこっそり張り替えます。


import numpy as np
import os.path
import re

STRCODE = 'utf-8'
'''
=====================================================================================
Waveファイルに関するフォーマット情報を地道に定義してみます。
とりあえず、今回はwavファイルを結合するところまでやります。(クラスは未完成です)
=====================================================================================
'''

class VWave:
# 読み込んだwaveファイルの情報を格納するクラスです。
SAMPLING_COUNT = 32768
# 地道にwavフォーマット情報を定義していきます。
IFF_ALL = 0
IFF_ID, IFF_SIZE, IFF_FMT, IFF_SUBID, IFF_SUBSIZE = 1, 2, 3, 4, 5
IFF_WAVFOMT, IFF_CHANNEL, IFF_SAMPLING, IFF_BYTEPS, IFF_BLKSIZE = 6, 7, 8, 9, 10
IFF_BIT, IFF_DATAID, IFF_DATASIZE, IFF_DATA, IFF_LIST = 11, 12, 13, 14, 15
IFF_LISTID, IFF_LISTSIZE, IFF_INFOID = 16, 17, 18
TYPE_BYTE = "byte"
TYPE_STR = "string"
TYPE_LITTLE = "little"
DEF_METAID, DEF_TITLE, DEF_TYPE, DEF_START, DEF_BYTE, DEF_ENC = 0, 1, 2, 3, 4, 5
IFF_DEF = [
[IFF_ALL, "ALL DATA ", TYPE_BYTE, None, None, None ],
[IFF_ID, "ID ", TYPE_STR, 0, 4, STRCODE],
[IFF_SIZE, "Chunk Size", TYPE_LITTLE, 4, 4, None],
[IFF_FMT, "FormatName", TYPE_STR, 8, 4, STRCODE],
[IFF_SUBID, "Sub ID ", TYPE_STR, 12, 4, STRCODE],
[IFF_SUBSIZE, "Sub Size ", TYPE_LITTLE, 16, 4, None],
[IFF_WAVFOMT, "WavFomat ", TYPE_LITTLE, 20, 2, None],
[IFF_CHANNEL, "Channel ", TYPE_LITTLE, 22, 2, None],
[IFF_SAMPLING,"Sampling ", TYPE_LITTLE, 24, 4, None],
[IFF_BYTEPS, "BytePS ", TYPE_LITTLE, 28, 4, None],
[IFF_BLKSIZE, "BlockSize ", TYPE_LITTLE, 32, 2, None],
[IFF_BIT, "Bit ", TYPE_LITTLE, 34, 2, None],
[IFF_DATAID, "DataID ", TYPE_STR, 36, 4, STRCODE],
[IFF_DATASIZE,"DataSize ", TYPE_LITTLE, 40, 4, None],
[IFF_DATA, "IFF Data ", TYPE_BYTE, 44, None, None],
[IFF_LIST, "List ", TYPE_BYTE, 0, None, None ],
[IFF_LISTID, "List ID ", TYPE_STR, 0, 4, STRCODE ],
[IFF_LISTSIZE,"List Size ", TYPE_LITTLE, 4, 4, None],
[IFF_INFOID, "INFO ID ", TYPE_STR, 8, 4, STRCODE]
]
#読み込んだ情報を格納する配列です。最初はクラス変数を使っていましたが、多すぎてわかりにくくなったため、
#すべて配列で持つことにしました。
iff = []
iffSubChunkID = []
iffSubChunkSize = []
iffSubChunkData = []

def __init__(self, data):
self.iff =[b'', "", 0, "", "", 0, 0, 0, 0, 0, 0, 0, "", 0, b'', b'', "", 0, ""]
siff = self.iff
sALL = self.IFF_ALL
dMetaID, dTitle, dType, dStart, dByte, dEnc = self.DEF_METAID, self.DEF_TITLE, self.DEF_TYPE, self.DEF_START, self.DEF_BYTE, self.DEF_ENC
tByte, tStr, tLittle = self.TYPE_BYTE, self.TYPE_STR, self.TYPE_LITTLE
# 以降全体から特定部分へアクセスするためにバイト配列にして格納します。
siff[sALL] = np.frombuffer(data, dtype=self.IFF_DEF[sALL][dType])
#順次定義の内容に基づき、data内容を解釈して配列に格納します。
for i in range(14)[1:]:
iDEF = self.IFF_DEF[i]
if iDEF[dType] == tStr:
siff[i] = (b''.join(siff[sALL][iDEF[dStart]:iDEF[dStart] + iDEF[dByte]])).decode(iDEF[dEnc])
elif iDEF[dType] == tLittle:
siff[i] = int.from_bytes(siff[sALL][iDEF[dStart]:iDEF[dStart] + iDEF[dByte]], tLittle)

#Waveデータ部分を格納します。この部分が可変長なんです。
iDEF = self.IFF_DEF[self.IFF_DATA]
siff[self.IFF_DATA] = b''.join(siff[sALL][iDEF[dStart]:iDEF[dStart] + siff[self.IFF_DATASIZE]])
#タグの部分はIFF_LISTで示す配列に格納します。
siff[self.IFF_LIST] = siff[sALL][44 + siff[self.IFF_DATASIZE]:]

#タグの部分の範囲を解釈して配列「XXXiffSubChunk」に格納します。
if (siff[self.IFF_LIST].size != 0):
sList = siff[self.IFF_LIST]
iDEF = self.IFF_DEF[self.IFF_LISTID]
siff[self.IFF_LISTID] = (b''.join(sList[iDEF[dStart]:iDEF[dStart] + iDEF[dByte]])).decode(iDEF[dEnc])
iDEF = self.IFF_DEF[self.IFF_LISTSIZE]
siff[self.IFF_LISTSIZE] = int.from_bytes(sList[iDEF[dStart]:iDEF[dStart] + iDEF[dByte]], tLittle)
iDEF = self.IFF_DEF[self.IFF_INFOID]
siff[self.IFF_INFOID] = (b''.join(sList[iDEF[dStart]:iDEF[dStart] + iDEF[dByte]])).decode(iDEF[dEnc])
offset = 12
for i in range(999):
chunkID = (b''.join(sList[offset:offset + 4])).decode(STRCODE)
subChunkSize = int.from_bytes((b''.join(sList[offset + 4:offset + 8])), tLittle)
chunkData = (b''.join(sList[offset + 8:offset + 8 + subChunkSize]).decode('SJIS'))
offset += (8 + subChunkSize + (subChunkSize % 2))
self.iffSubChunkID.append(chunkID)
self.iffSubChunkSize.append(subChunkSize)
self.iffSubChunkData.append(chunkData)
if siff[self.IFF_LISTSIZE] <= offset + 12:
break

def print(self):
#読み込んだwavファイルの内容をレポートします。
siff = self.iff
sALL = self.IFF_ALL
dMetaID, dTitle, dType, dStart, dByte, dEnc = self.DEF_METAID, self.DEF_TITLE, self.DEF_TYPE, self.DEF_START, self.DEF_BYTE, self.DEF_ENC
tByte, tStr, tLittle = self.TYPE_BYTE, self.TYPE_STR, self.TYPE_LITTLE
print('Toral size = ' + "{0:,d}".format(siff[sALL].size))
for i in range(19)[1:]:
if (i == 14):
if (siff[self.IFF_LIST].size != 0):
print("-------------------------------------------")
continue
else:
#このブロックは調整中です
break
iDEF = self.IFF_DEF[i]
if iDEF[dType] == tStr:
print(iDEF[dTitle] + '=' + siff[i])
elif iDEF[dType] == tLittle:
print(iDEF[dTitle] + '=' + "{0:,d}".format(siff[i]))
print("<<This wave file contains [ " + "{0:,d}".format(siff[self.IFF_DATASIZE] // (self.SAMPLING_COUNT * siff[self.IFF_BLKSIZE])) + "] sound>>")
print("-------------------------------------------")

def printSize(self):
siff = self.iff
print("Chunk Size=" + siff[self.IFF_SIZE])

def getList(self):
return self.iffSubChunkID, self.iffSubChunkSize, self.iffSubChunkData

def cat(self, data):
#結合用のメソッドです。元のソースとチャンネル数、サンプリングレート、ビット数が異なればエラーにします。
#エラーがあれば結合しません。
if (self.iff[self.IFF_CHANNEL] != data.iff[data.IFF_CHANNEL]):
print("Channel is different base=" + "0:d".format(self.iff[self.IFF_CHANNEL]) +
"source=" + "0:d".format(data.iff[self.IFF_CHANNEL]))
return -1
if (self.iff[self.IFF_SAMPLING] != data.iff[data.IFF_SAMPLING]):
print("Samplint is different base=" + "0:d".format(self.iff[self.IFF_SAMPLING]) +
":source=" + "0:d".format(data.iff[self.IFF_SAMPLING]))
return -1
if (self.iff[self.IFF_BIT] != data.iff[data.IFF_BIT]):
print("Bit is different base=" + "0:d".format(self.iff[self.IFF_BIT]) +
":source=" + "0:d".format(data.iff[self.IFF_BIT]))
return -1
#結合するサンプリング数がSAMPLING_COUNTの整数倍でないと、エラーとします。
if (data.iff[data.IFF_DATASIZE] % self.SAMPLING_COUNT != 0):
print("Sampling count is not base on :" + self.SAMPLING_COUNT )

#結合は単純に足しているだけです。
self.iff[self.IFF_DATA] += data.iff[data.IFF_DATA]
self.iff[self.IFF_DATASIZE] += data.iff[data.IFF_DATASIZE]
self.iff[self.IFF_SIZE] += data.iff[data.IFF_DATASIZE]


def getWave(self):
#Waveデータのフォーマットでバイナリで取り出すメソッドです。
dMetaID, dTitle, dType, dStart, dByte, dEnc = self.DEF_METAID, self.DEF_TITLE, self.DEF_TYPE, self.DEF_START, self.DEF_BYTE, self.DEF_ENC
work = b''
for i in range(14):
siffData = self.iff[i]
iDEF = self.IFF_DEF[i]
if (iDEF[dType] == self.TYPE_STR):
work = work + siffData.encode(iDEF[dEnc])
elif (iDEF[dType] == self.TYPE_LITTLE and iDEF[dByte] == 2 ):
work = work + siffData.to_bytes(2, 'little')
elif (iDEF[dType] == self.TYPE_LITTLE and iDEF[dByte] == 4):
work = work + siffData.to_bytes(4, 'little')
work = work + self.iff[self.IFF_DATA]
return work

#出力するDBの名前です。
CAT_WAVE_FILE='KooSoundDB.wav'
sCount = 0
#カレントディレクトリで、"00001"から"99999"(5桁)のフォルダがあれば名前を取得してソートします
output_path = "."
files = os.listdir(output_path)
list_dir = [f for f in files if os.path.isdir(os.path.join(output_path, f))]
sound_dir = [i for i in list_dir if re.search(r'[0-9][0-9][0-9][0-9][0-9]', i)]
sound_dir.sort()
print(sound_dir)
#フォルダ名と同じ番号のwavファイルがあるか調べます。
for dir_name in sound_dir:
fileName = dir_name + "\\" + dir_name + ".wav"
if os.path.isfile(fileName):
sCount += 1
print ("exist:" + fileName)
f = open(fileName, 'rb')
aData = f.read()
waveData = VWave(aData)
if (sCount == 1):
# 1個目は元データそのものです。
catWave = waveData
else:
# 2個目以降は結合していきます。
catWave.cat(waveData)
waveData.print()
#タグリストはget/setするので確認用です。setはまだ作ってません。
subChunkID, subChunkSize, subChunkData = waveData.getList()
for i in range(len(subChunkID)):
print(">" + subChunkID[i] + ":{0:4d}:".format(subChunkSize[i]) + subChunkData[i])
f.close()
print("===========================================")
print("Output file name:" + CAT_WAVE_FILE)
print("===========================================")
catWave.print()
#出力用のファイルをオープンし、結合演算済の内容をwavファイルに出力します。
fw = open(CAT_WAVE_FILE, 'wb')
fw.write(catWave.getWave())
fw.close()

wavフォーマットの定義を配列(リスト)で行いました。本当はPythonstruct(バイナリ構造の定義、解釈)を使いたかったのですが、可変長のbyteデータの扱いがうまくできなかったので、このようになりました。
RIFF形式のファイルであれば少し改造するだけでなんでも読めるようになるはずです。(作ったボクでもわかりにくいソースになっており反省です)

実行結果例です。(テスト用)

['02001', '02002', '04001', '07001']
exist:02001\02001.wav
Toral size = 5,767,212
ID        =RIFF
Chunk Size=5,767,204
FormatName=WAVE
Sub ID    =fmt 
Sub Size  =16
WavFomat  =1
Channel   =1
Sampling  =48,000
BytePS    =96,000
BlockSize =2
Bit       =16
DataID    =data
DataSize  =5,767,168
<<This wave file contains [ 88] sound>>
-------------------------------------------
exist:02002\02002.wav
...
===========================================
Output file name:KooSoundDB.wav
===========================================
Toral size = 5,767,212
ID        =RIFF
Chunk Size=14,614,564
FormatName=WAVE
Sub ID    =fmt 
Sub Size  =16
WavFomat  =1
Channel   =1
Sampling  =48,000
BytePS    =96,000
BlockSize =2
Bit       =16
DataID    =data
DataSize  =14,614,528
<<This wave file contains [ 223] sound>>
-------------------------------------------

あとは、機械学習用のタグ情報を付加させる機能をつければ、機械学習用サウンドDB作成プログラムはほぼ完成です。

お元気で(2.7)!こんにちは(3.6)!!

 余談ですが、世の中の流れに従って完全にPython 3.xにしています。昨年はじめたときは2.xでしか動かない太古のソースをたくさん参照していたので、おのずと2.xを多用していたのですが、今年は完全に3.xベースにします。今年作成のソースは2.xでは動作しません*3のであしからず。

f:id:np2LKoo:20170520124131p:plain

2.7おくえんのパガーニ ウアイラロードスター

f:id:np2LKoo:20170520123120p:plain

おいしい3.6牛乳

次回は、この「機械学習用サウンドDB」(だけどwavファイルそのもの)に、機械学習に必要なタグ情報を付加する方法を考えます。

プログラムが完成すれば、あとは「ねこ音あつめ」です!めざせ!10万音!!

 

プロモーションキャンペーン終了のお知らせ

プロモーション空しく全く再生されません。2週間くらいカウントアップなしです。15HITからいっこうにアップしません!そこで、プロモーションは今月末までの期間限定となりました。(ジョーンズ調査員より:地球人は「限定」に弱い。)

f:id:np2LKoo:20170601000756p:plain

月末までなら再生すると「小さな幸せ音ずれ訪れる」という特典付きです。(秘密結社「柔らか銀行」マーケット報告書より:日本人は「お得」と「特典」に弱い。

地域限定性をさりげなくアピールしSNSをよいスパイラルで巻き込むと相当の効果がある(大正製菓販売戦略部:(社外秘)一発勝負の列島分断飢餓作戦資料より)
よろしくお願いいたします!めざせ20HIT!!!

→カール便乗キャンペーンはあと1ポ及ばず、不成功におわりました。(19Hitでした)

ご視聴いただきありがとうございました。

*1:蛙界に存在する、生きることの意義に疑問を感じる青春期/思春期特有の病気。「蛙はなぜ生きるのか」をつきつめ、崇高な自己実現目的意識と自己潜在能力とのギャップに失望し、なにもヤルキが起こらなくなる、モチベーション低下スパイラルパターン。失望が大きいと食欲もなくなり死に至る病でもある。:おたまじゃくしから蛙になった年と翌年(感受性の強い蛙は毎年春期に)かかりやすいといわれている。

*2:余談ですが、クラスを使ったので「self.」がやまほどでてきました。あまりに多いところは、一旦別の変数におきかえることをしています。「self.」を1文字であらわす略記方法を採用してくれないかなぁ。。。1行の半分がself.である行が何行も続くと誰でもうんざりすると思います。慣れれば気にならないのかもしれませんが、初心者には辛いです。

*3:「今時、『機械学習するならPython2.xだよ!』なんて言っていると小学生に笑われる。」と、どなたかが書いていましたが、ドキッとしました

計算機音楽の自由研究(準備:その2.7)~wavファイルのフォーマットを勉強する

はじめに

今回は(機械学習向けの音のデータベースとして使用する予定の)、wavファイルへの波形情報以外の情報の持たせ方と、Pythonでの波形情報以外の取得方法について確認します。準備が進まず、実験にまだまだはいれません。準備ブログについて、亀の歩みで番号を0.1ずつアップさせています。

f:id:np2LKoo:20170514104105j:plain

よく使われるwav/waveフォーマットを実験用の標準データベースフォーマットにする方向で考えているのは前回まで書いたとおりです。
このwavフォーマットについて、実験用の情報をいろいろ持たせるための確認をしますです。wav(の中のRIFF情報)のタグ情報をどう扱うかという、超マイナーな話題です。

Pythonの標準のwaveライブラリなどではタグ情報にアクセスできない*1ことによるお題です。手抜きで英語のリンクを貼っていますが、訳す力がありません。あしからず。(6月末まで反省キャンペーン中です。内容を順次リファインします。)

なんのあしあと?かえるのあしあと!(雪の上ならそれは違いますよ)

 最初に蛇足をかくのもなんですが、助手たちが書いているのは「聖なる館」以下の部分だけで、それ以外の部分はボクが書いてます。

どのような情報を持たせるのか

手書き数字画像学習用のMNISTデータベースがとても優れており、現在も確認用やチュートリアルとしてよくつかわれています。今後も当ブログではよく使うと思います。データベースのフォーマットは扱いやすければなんでもいいと思っていますが、以下の要件が必要かと思っています。


学習用(実験用)データベースの要件

・一度リリースしたら初志貫徹。内容を変えない。

  改善したくてもしない。してしまうと以前の実験との比較が困難になる。

・扱いやすい容量にまとまっている。

  1つ1つの情報の粒度がちいさすぎず、大きすぎない

  全体の情報の「個数」は情報の内容に対して、統計的に考えても十分大きい

・学習用とテスト用の境界がはっきりしている。

  利用する人がまちがいようがないようにする。(ここがまちまちになると比較にならない)

・ダウンロードが容易

  特定コードを書けば無条件で使用できるなど。

  アクセス・使用するためのメソッドが標準化されている。

・一部のみの改変がしにくい

  どうしても応用したものをつくりたくなるが、データ差し替えや書き換えがある程度しにくいとやろうと思わない。


手書きの数字を学習させる場合は、その正解がなんであるかは0~9までの数字を持てばよく、ほかにあまり情報が要りません。
当ブログでの音の実験では、音の種類だけでなく、いろいろな情報を持たせたいと考えています。
バイオリンかピアノかを聞き分けるだけでなく、バイオリンなら1億円超えのストラディバリウスと10万円の練習用を聞き分けたり、ピアノならスタンウェイかヤマハかなどを聞き分けるようなこともやりたいと考えています。(目指せ一流!)
そのため「ピアノの中央ドの音」ではなく、「KeppyがサンプリングしたスタンウェイのサウンドフォントV6.xのMIDIノート60の音」のような情報を持たせたいと考えています。
できれば、wavファイルの中にそれらの情報を持たせたいと考えています。*2
wavファイルのフォーマットを調べて、データをどのように持てるのか、(または持てないのか)を確認します。

おさらい
pythonでwaveファイルを扱う場合はimport wave とすれば簡単に行えます。ところが、pythonのwave機能ではタイトルやアーチスト、コメントの情報を取得できません。

マニュアルを読んでもコメント情報にアクセスできる手段は書いてありません。

flacは大好きですが、今回はあえてPASSしています。*3

機械学習向けの正解や補助情報をwavファイルの「タグ」情報として入れたいと考えていますので、pythonで扱う方法を確認します。

ポイントを列記します
・wavファイルのフォーマットはRIFFというフォーマット規格を基本にしている。

・RCFとかの標準規格ではない。「AS IS」(あるがまま)な規格で悪い意味いい加減。いい意味で柔軟性あり。不運なことに悪い面が目立ってしまっている。

・wavファイルのRIFF内タグはRFC 4180を解説してるページの参照としてはあるが、「定義」ではない模様。MS社ソースのaafile.cの内容そのものが仕様定義のようです。
Pythonにはいろいろなwavファイルへのアクセスメソッドやライブラリがあるが、タグアクセスについてはほとんど機能がない。
・タグにアクセス(読むだけ)したいなら通常バイナリファイルとして読み込んで、ジブンで直接アクセスしたほうが手っ取り早い。(Pythonなので、どこかにwavファイルのタグを簡単に扱えるライブラリが「ある」とおもいますが、少し探して見つからないものは探すのがとってもたいへん。)

フォーマットについての説明は初心者につき、他のサイトを参考にさせていただきます。フォーマットの説明は(力が及ばないので)しませんのであしからず。

こちらの説明がわかりやすかったです。どうも有難うございます。

wavファイルの「タグ」情報について

 wavファイルのタグは参照ページのとおり、RIFFの仕様策定はMS社とIBM社の提案によるものですが、厳密な定義をしていない部分やもともと自由度が高いことを想定して策定されているので、かなり混乱したようです。
とくに各タグの意味やフォーマットの例示や定義が薄く、策定時には多言語環境がまとまっていなかったこともあり、多言語及び複数表記(複数情報をデリミタ;で区切るかどうかなど)の不統一により、多くのソフトで非互換部分が発生している模様です。flacのタグなどは後で作成されたのでそのあたりの状況もふまえて設計されており、このあたりの非互換はない模様です。

プログラム

wavフォーマットのページを参考にして、またジブンで作成したwavファイルをバイナリエディタで確認してプログラムしました。仕様にでているフォーマット内容を参考に各情報をそのまま表示しているプログラムです。
ベタベタなコードですが、実験用ということでご容赦ください。(あとでもうすこし汎用的なものに差し替えるかもしれません)


import wave #使ってません
import numpy as np

STRCODE ='utf-8'

class vWave:
iffAll = None
iffID = None
iffSize = 0
iffFormatName = None
iffSubID = None
iffSubSize = 0
iffWavFomat = 0
iffChannel = 0
iffSampling = 0
iffBytePS = 0
iffBlockSize = 0
iffBit = 0
iffEXParaSize = 0
# self.iffEXParam = 0
iffDataID = None
iffDataSize = 0
iffData = None
iffList = None
iffListID = None
iffSubChunkID = []
iffSubChunkSize = []
iffSubChunkData = []

def __init__(self, data):
self.iffAll = np.frombuffer(data, dtype="byte")
self.iffID = (b''.join(self.iffAll[0:4])).decode(STRCODE)
self.iffSize = int.from_bytes(self.iffAll[4:8], 'little')
self.iffFormatName = (b''.join(self.iffAll[8:12])).decode(STRCODE)
self.iffSubID = (b''.join(self.iffAll[12:16])).decode(STRCODE)
self.iffSubSize = int.from_bytes(b''.join(self.iffAll[16:20]), 'little')
self.iffWavFomat = int.from_bytes(self.iffAll[20:22], 'little')
self.iffChannel = int.from_bytes(self.iffAll[22:24], 'little')
self.iffSampling = int.from_bytes(self.iffAll[24:28], 'little')
self.iffBytePS = int.from_bytes(self.iffAll[28:32], 'little')
self.iffBlockSize = int.from_bytes(self.iffAll[32:34], 'little')
self.iffBit = int.from_bytes(self.iffAll[34:36], 'little')
self.iffDataID = (b''.join(self.iffAll[36:40])).decode(STRCODE)
self.iffDataSize = int.from_bytes(self.iffAll[40:44], 'little')
self.iffData = b''.join(self.iffAll[44:44 + self.iffDataSize])
self.iffList = self.iffAll[44 + self.iffDataSize:]
if (self.iffList.size != 0):
self.iffListID = (b''.join(self.iffList[0:4])).decode(STRCODE)
self.iffListSize = int.from_bytes(self.iffList[4:8], 'little')
self.iffInfoID = (b''.join(self.iffList[8:12])).decode(STRCODE)
offset = 12
for i in range(999):
chunkID = (b''.join(self.iffList[offset:offset + 4])).decode(STRCODE)
subChunkSize = int.from_bytes((b''.join(self.iffList[offset + 4:offset + 8])), 'little')
chunkData = (b''.join(self.iffList[offset + 8:offset + 8 + subChunkSize]).decode('SJIS'))
offset += (8 + subChunkSize + (subChunkSize % 2))
self.iffSubChunkID.append(chunkID)
self.iffSubChunkSize.append(subChunkSize)
self.iffSubChunkData.append(chunkData)
if self.iffListSize <= offset + 12:
break

def print(self):
print('Toral size = ' + "{0:d}".format(self.iffAll.size))
print('ID = ' + self.iffID)
print('Chunk Size = ' + "{0:d}".format(self.iffSize))
print('FormatName = ' + self.iffFormatName)
print('Sub ID = ' + self.iffSubID)
print('Sub Size = ' + "{0:d}".format(self.iffSubSize))
print('WavFomat = ' + "{0:d}".format(self.iffWavFomat))
print('Channel = ' + "{0:d}".format(self.iffChannel))
print('Sampling = ' + "{0:d}".format(self.iffSampling))
print('BytePS = ' + "{0:d}".format(self.iffBytePS))
print('BlockSize = ' + "{0:d}".format(self.iffBlockSize))
print('Bit = ' + "{0:d}".format(self.iffBit))
print('DataID = ' + self.iffDataID)
print('DataSize = ' + "{0:d}".format(self.iffDataSize))
print('--------------------------------')
print('List ID = ' + self.iffListID)
print('List Size = ' + "{0:d}".format(self.iffListSize))
print('INFO ID = ' + self.iffInfoID)

def getData(self):
return self.iffData

def getList(self):
return self.iffSubChunkID, self.iffSubChunkSize, self.iffSubChunkData

WAVE_INPUT_FILENAME = "test\Check055B.wav"

f = open(WAVE_INPUT_FILENAME, 'rb')
aData = f.read()
waveData = vWave(aData)
waveData.print()
subChunkID, subChunkSize, subChunkData = waveData.getList()
for i in range(len(subChunkID)):
print(">" + subChunkID[i] + ":{0:4d}:".format(subChunkSize[i]) + subChunkData[i])



 実行結果(例)

例ではモノラル16Bit48KHzサンプル数=32の音声データに対し、フリーソフトmp3infpでタグ情報を付加してその内容を表示したものです。

実行結果


Toral size = 322
ID         = RIFF
Chunk Size = 314
FormatName = WAVE
Sub ID     = fmt
Sub Size   = 16
WavFomat   = 1
Channel    = 1
Sampling   = 48000
BytePS     = 96000
BlockSize  = 2
Bit        = 16
DataID     = data
DataSize   = 64
--------------------------------
List ID    = LIST
List Size  = 206
INFO ID    = INFO
>ISRC:   7:ソース
>ICRD:  11:2017/05/13
>IPRD:   9:アルバム
>IENG:   7:製作者
>ITRK:   5:9999
>INAM:  12:Title Name 
>ICOP:   7:著作権
>IGNR:  11:A Cappella
>ISFT:  13:ソフトウェア
>ICMT:   9:コメント
>IART:  13:アーティスト 

元にしたファイルの情報

f:id:np2LKoo:20170514143304p:plain

f:id:np2LKoo:20170514143103p:plain

まとめ 

勢いでタグ全般を確認しましたが、コメント(ICMT)タグに実験用サウンドの各種情報を入れて登録をすればよいと考えます。(4バイトあるので、4GBまで情報をいれられるはずです。全体でも4バイト容量(4GB)が限界です)機械学習向けコメント情報のフォーマットは別途考えようと思いますが、[JSON形式]でよいと思います。

補足

 Windowsの標準機能では、タグ情報を自由に入れることはできません。専用のソフトを使って入れることができます。フリーでいろいろあります。

ボクはmp3infpというソフトを使っています。

フォーマットがわかれば自作プログラムから書いてもいいと思っています。

何もタグ情報を入れていないwav(上側ファイル)とタグ情報をいれたwav(下側ファイル)でバイナリエディタでの確認。下側ファイルの黒枠部分が上側ファイルに相当します。

f:id:np2LKoo:20170514211645p:plain

Python足 (もしくは Silly Walk)

 (参考)以下の表はMS社のRIFF定義をしたaafile.cの内容からの抜粋です。各種関連情報の紹介とともに訳しておられる方がいらっしゃるのでそちらでどうぞ(苦笑)

ID 内容 内容(英語) 説明(英語)
IARL アーカイブされた所在 Archival Location Indicates where the subject of the file is archived.
IART 参加アーチスト(複数は;でデリミト:しないソフトもある) Artist Lists the artist of the original subject of the file. For example, \"Michaelangelo.\"
ICMS 著作権代理人 Commissioned Lists the name of the person or organization that commissioned the subject of the file. For example, \"Pope Julian II.\"
ICMT コメント Comments Provides general comments about the file or the subject of the file. If the comment is several sentences long, end each sentence with a period. Do not include newline characters.
ICOP 著作権情報(複数は;でデリミト:しないソフトもある模様) Copyright Records the copyright information for the file. For example, \"Copyright Encyclopedia International 1991.\" If there are multiple copyrights, separate them by a semicolon followed by a space.
ICRD 作成日→年のみ表示するソフトもある Creation date Specifies the date the subject of the file was created. List dates in year-month-day format, padding one-digit months and days with a zero on the left. For example, \"1553-05-03\" for May 3, 1553.*4
ICRP

(AVI)

一部切り取り

Cropped Describes whether an image has been cropped and, if so, how it was cropped. For example, \"lower right corner.\"
IDIM

(AVI)縦横サイズ

Dimensions Specifies the size of the original subject of the file. For example, \"8.5 in h, 11 in w.\"
IDPI (AVI)解像度 Dots Per Inch Stores dots per inch setting of the digitizer used to produce the file, such as \"300.\"
IENG (録音/録画)エンジニア Engineer Stores the name of the engineer who worked on the file. If there are multiple engineers, separate the names by a semicolon and a blank. For example, \"Smith, John; Adams, Joe.\"
IGNR ジャンル Genre Describes the original work, such as, \"landscape,\" \"portrait,\" \"still life,\" etc.
IKEY キーワード Keywords Provides a list of keywords that refer to the file or subject of the file. Separate multiple keywords with a semicolon and a blank. For example, \"Seattle; aerial view; scenery.\"
ILGT

(AVI)

明度

Lightness Describes the changes in lightness settings on the digitizer required to produce the file. Note that the format of this information depends on hardware used.
IMED メディア形式 Medium Describes the original subject of the file, such as, \"computer image,\" \"drawing,\" \"lithograph,\" and so forth.
INAM タイトル Name Stores the title of the subject of the file, such as, \"Seattle From Above.\"
IPLT (AVI)色数 Palette Setting Specifies the number of colors requested when digitizing an image, such as \"256.\"
IPRD アルバム Product Specifies the name of the title the file was originally intended for, such as \"Encyclopedia of Pacific Northwest Geography.\"
ISBJ サブジェクト/サブタイトル/内容説明 ※ソフトにより扱いがまちまち Subject Describes the contents of the file, such as \"Aerial view of Seattle.\"
ISFT ソフトウェア Software Identifies the name of the software package used to create the file, such as \"Microsoft WaveEdit.\"
ISHP (AVI)鮮明度 Sharpness Identifies the changes in sharpness for the digitizer required to produce the file (the format depends on the hardware used).
ISRC ソース Source Identifies the name of the person or organization who supplied the original subject of the file. For example, \"Trey Research.\"
ISRF ソースの形態 Source Form Identifies the original form of the material that was digitized, such as \"slide,\" \"paper,\" \"map,\" and so forth. This is not necessarily the same as IMED.
ITCH (デジタル編集加工した)技術者 Technician Identifies the technician who digitized the subject file. For example, \"Smith, John.\"
邪の道 にねそべるカエル(ボク)

wavのアクセスを確認する過程で、Python for .NET というのを使ってみました。(リンクは英語です。ごめんなさい)すんなり動き、何でもできて「にんまり」と笑いました。ボクはピュアなPythonには別にこだわっておらず、(実験なので動けばいいので、)「これもあり」だと思いました。Pythonから.NETのライブラリや.NET経由でシステムCALLが読み放題です。


*1:chunkライブラリではできますが、AIFFなどを対象にしているので、wavファイルに対して使うには逆に手間がかかる感じがします。wavのLISTタグは入れ子のchunkをもつなどがありますが、wavフォーマットそのものには対応していないため、どう使えばいいかがわかりません。。。

*2:MNISTの手書き文字データベースは、「数少ない(集約された)ファイルから構成されており、手軽に改変できない」ことが重要だと思っています。wavファイルの中に各種情報を持たせることにより、改変しにくく、ファイルも少ない状態となると考えます。

*3:各種の情報をいろいろな再生方法で確実に共有したい場合はflacなどを使うべきだ。との見解がほとんどでした。flacは大好きなのですが、実験用の再加工することもあるデータとしては、wavファイルのほうが扱いやすいと考えています。

*4:アスカニオ・コンディヴィの「ミケランジェロの生涯」の著作をイメージした日付かも