クーの自由研究

素朴な疑問を実験します。ボクと一緒に螺旋階段を頂上まで登ってみませんか?貴方の影についていきます。

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

おたませしました

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

ゲームとしてもうすこしちゃんと作るつもりだったのですが、かえるのクー(このブログの主(ぬし))と相談(そうだん)して、実験レベルのものを公開(こうかい)することになりました。このページには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とかマルチスレッドとか全然しらなくてつくりはじめました。勉強中です。うまく動かなかったらなおしますので、怒(おこ)らないでね。

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

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

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