[b][u][size=150][color=#9900ff]質問:midiファイルから1トラック分のmidiメッセージを取得するにどうしますか。[br][/color][/size][/u][/b][br]トラックによっては、ノートオンのステータスが入っていない場合があります。[br]そのときは、そのあとのデータから1トラックを抜き出しましょう。[br]デルタタイムは可変長なのですが、2バイトくらいは対応できるようにします。[br]また、ランニングステータスとして、ステータスバイトが前のmidiメッセージと同じ場合の対応も[br]必要です。[br]また、デルタタイム[b]intdelta[/b]だけでは、ノートオンオフのデータとして使えないので、[br]それを累積する変数accumTimeを用意しましょう。[br]str_takenが16進データの各バイトを文字列としてくっつけたものです。[br]list_takenが16進データの各バイトをリストとして入れたもので、情報としては同じです。[br]1文字ずつの個数と、2文字で1バイトでの個数では、個数比が2:1なので、ここが注意点ですね。[br][b]文字列[/b]だと、文字列.find(検索文字列)が使えるので便利です。[br]バイト単位にwhile文をまわして、情報をひろっていくには[b]リスト[/b]が便利です。[br][br][IN]===============================================[br]def [b]takeTrk1Msgs[/b](midi_filepath):[br] with open(midi_filepath, 'rb') as f: # バイナリモードでファイルを開く[br] data = f.read()[br] strdata =""[br] for byte in data:[br] strdata +=f"{byte:02x}"[br] fd1 = strdata.find('4d54726b') + 8*2[br] fd2 = strdata.find('00ff2f00')[br] listdata =[f"{byte:02x}" for byte in data][br] str_taken = strdata[fd1:fd2][br][br] [color=#0000ff] #1トラック目に演奏情報が含まれてなければ、次のトラックを取り出す。[br][/color] noteOnCount = str_taken.count('0092')[br] if noteOnCount == 0:[br] strdata = strdata[fd2 + 8:][br] listdata =listdata[(fd2+8)//2:][br] fd1 = strdata.find('4d54726b') + 8*2[br] fd2 = strdata.find('00ff2f00')[br][b][color=#0000ff] str_taken = strdata[fd1:fd2][br] list_taken = listdata[fd1//2:fd2//2][br][/color][/b][br] [color=#0000ff]#エンドオブトラック以外のメタデータはトラックの先頭にかたまると仮定する。[br] #演奏データはノートonとノートoffに限ると仮定する。[br][/color] pos = str_taken.find('0092')//2 [color=#0000ff]#最初のノートONのデータ位置 [/color][br] msgs=[] [color=#0000ff]# msg=[accumulated_deltaTime, status, notenum,velo]を格納する [br] #notenum 0=>C0, 12=>C1,......[br][/color] status = 0[br] size = len(list_taken)[br] accumTime = 0[br] while pos < size:[br] intdelta = int(list_taken[pos],16) [br] if intdelta >= 2**7: [color=#0000ff]#デルタタイムの2バイト対応[/color][br] pos +=1[br] intdelta =(intdelta - 2**7)*(2**7) + int(list_taken[pos],16)[br] accumTime += intdelta[br] if list_taken[pos+1]!='92' and list_taken[pos+1]!='82':[color=#0000ff]#ランニングステータス対応[br][/color] status = msgs[-1][1][br] msg=[accumTime, status, int(list_taken[pos+2],16), int(list_taken[pos+3],16)][br] pos += 3[br] else:[br] status = list_taken[pos+1][br] msg=[accumTime, status, int(list_taken[pos+2],16), int(list_taken[pos+3],16)][br] pos += 4[br] print(msg)[br] msgs.append(msg)[br] [br][b]filepath = "C:/Users/example.mid" #フルパスがおすすめ。[br]takeTrk1Msgs[/b](filepath)[br]#[OUT]=======================================================[br][0, '92', 38, 79][br][1027, '82', 38, 64][br][1038, '92', 38, 79][br][2067, '82', 38, 64][br][2078, '92', 38, 79][br][3588, '92', 79, 136][br][4616, '82', 41, 64][br][4627, '92', 40, 79][br][5136, '82', 40, 64][br][5147, '92', 45, 79][br][5656, '82', 45, 64][br][5668, '82', 64, 0][br][5668, '92', 38, 79][br][6448, '92', 79, 129][br][6696, '82', 48, 64][br][6708, '92', 46, 79][br][7216, '82', 46, 64][br][7227, '92', 48, 79][br]……[br]……
[color=#9900ff][u][b][size=150]質問:midiデータからピアノロールを表示するにはどうしたらよいでしょうか。[br][/size][/b][/u][/color][br]同じ音ごとに、音の辞書を作る方法があります。[br]音べつにノートのオンオフのデータをならべれば、音のスタートとストップのデータができます。[br]データ数が半分になります。[br][(スタート、音の高さ)、(ストップ、音の高さ)]のセグメント情報を作ることで、[br]midiデータをピアノロール風に表示できるようになります。[br][IN]==================================================[br][b]import numpy as np[br]import matplotlib.pyplot as plt[br]import matplotlib.collections as mc[br]import matplotlib.cm as cm[br]def takeTrk1gate(midi_filepath):[br][/b] [br] with open(midi_filepath, 'rb') as f: # バイナリモードでファイルを開く[br] data = f.read()[br] strdata =""[br] for byte in data:[br] strdata +=f"{byte:02x}"[br] fd1 = strdata.find('4d54726b') + 8*2[br] fd2 = strdata.find('00ff2f00')[br] listdata =[f"{byte:02x}" for byte in data][br] str_taken = strdata[fd1:fd2][br][br] #1トラック目に演奏情報が含まれてなければ、次のトラックを取り出す。[br] noteOnCount = str_taken.count('0092')[br] if noteOnCount == 0:[br] strdata = strdata[fd2 + 8:][br] listdata =listdata[(fd2+8)//2:][br] fd1 = strdata.find('4d54726b') + 8*2[br] fd2 = strdata.find('00ff2f00')[br] str_taken = strdata[fd1:fd2][br] list_taken = listdata[fd1//2:fd2//2][br] #演奏データはノートonとノートoffに限ると仮定する。[br] pos = str_taken.find('0092')//2 #最初のノートONのデータ位置 [br] msgs=[] # msg=[accumulated_deltaTime, status, notenum,velo]を格納する [br] #notenum 0=>C0, 12=>C1,......[br] status = 0[br] size = len(list_taken)[br] accumTime = 0[br] while pos < size:[br] intdelta = int(list_taken[pos],16) [br] if intdelta >= 2**7: #デルタタイムの2バイト対応[br] pos +=1[br] intdelta =(intdelta - 2**7)*(2**7) + int(list_taken[pos],16)[br] accumTime += intdelta[br] if list_taken[pos+1]!='92' and list_taken[pos+1]!='82':#ランニングステータス対応[br] status = msgs[-1][1][br] msg=[accumTime, status, int(list_taken[pos+2],16), int(list_taken[pos+3],16)][br] pos += 3[br] else:[br] status = list_taken[pos+1][br] msg=[accumTime, status, int(list_taken[pos+2],16), int(list_taken[pos+3],16)][br] pos += 4[br] #print(msg)[br] msgs.append(msg)[br] lastTime = msgs[-1][0]/320 #横の長さの調整のために時間軸のサイズダウンをする。[br] noteDict={}[br] [b][color=#0000ff] #同じノート別に辞書を作る。[br][/color][/b] [color=#0000ff]for msg in msgs:[br] note = msg[2][br] if note in noteDict:[br] noteDict[note].append(msg)[br] else:[br] noteDict[note]=[msg][br] #print(noteDict)[br] notes = noteDict.keys()[br] miny=min(notes)[br] maxy=max(notes)[br] [b] #ノート別noteにmsgのオンオフのペアからノートオンaccumTimeをstartTime、[br] #ノートオフaccumTimeをstopTImeにして、velocityはノートオンのみ採用。[br] #[(startTime,note), (stopTime,note)]の形式に変換する。[br][/b] noteSegments=[][br] noteVelos=[][br] startTime =0[br] stopTime = 0[br] velocity = 0[br] note = 0[br] for k,v in noteDict.items():[br] note = k [br] for msg in v:[br] if msg[1]=='92':[br] startTime = msg[0]/320[br] noteVelos.append(msg[3])[br] velocity = msg[3][br] elif msg[1]=='82':[br] stopTime = msg[0]/320[br] noteSegments.append([(startTime, note),(stopTime,note)] )[br] print(noteSegments) [br] #print(noteVelos) [br] [b] return noteSegments,noteVelos,lastTime, miny,maxy[br][/b]#=================== 以下でデータ渡しと視覚化をする[br][/color]filepath = "C:/Users/example.mid"[br]noteSegments,noteVelos,lastTime,miny,maxy= takeTrk1gate(filepath)[br][b]xmin = 0[br]xmax = lastTime[br]ymin = miny[br]ymax = maxy[br]lines = noteSegments[br]lc = mc.LineCollection(lines, linewidths=2)[br]fig = plt.figure(figsize=(20,20))[br]ax = fig.add_subplot(aspect='1')[br]ax.add_collection(lc)[br]ax.autoscale()[br]plt.xlim(xmin, xmax); plt.ylim(ymin, ymax)[br]plt.xlabel('x'); plt.ylabel('y')[br]plt.show()[br][/b]#[OUT]==========================================[br][[(0.0, 38), (3.209375, 38)], [(3.24375, 38), (6.459375, 38)], [(50.36875, 38), (51.959375, 38)], [(60.2625, 38), (61.04375, 38)], [(89.628125, 38), (92.025, 38)], [(99.378125, 38), (100.9625, 38)], [(138.634375, 38), (141.034375, 38)], [(148.3875, 38), (149.975, 38)], [(112.934375, 41), (14.425, 41)], [(24.2125, 41), (26.6125, 41)], [(31.96875, 41), (34.075, 41)], [(34.1125, 41), (37.328125, 41)], [(47.11875, 41), (49.51875, 41)], [(79.775, 41), (83.084375, 41)], [(83.125, 41), (86.3375, 41)], [(96.128125, 41), (98.528125, 41)], [(103.884375, 41), (105.984375, 41)], [(115.778125, 41), (118.99375, 41)], [(128.784375, 41), (132.09375, 41)], [(132.1375, 41), (135.35, 41)], [(145.1375, 41), (147.5375, 41)], [(152.89375, 41), (155.234375, 41)], [(155.278125, 41), (157.078125, 41)], [(159.95, 41), (160.503125, 41)], [(160.553125, 41), (166.003125, 41)], [(14.459375, 40), (16.05, 40)], [(26.65, 40), (27.428125, 40)], [(37.3625, 40), (38.953125, 40)], [(49.553125, 40), (50.328125, 40)], [(86.375, 40), (87.9625, 40)], [(98.5625, 40), (99.3375, 40)], [(135.384375, 40), (136.975, 40)], [(147.575, 40), (148.35, 40)], [(16.084375, 45), (17.675, 45)], [(33.25, 45), (33.64375, 45)], [(38.9875, 45), (40.578125, 45)], [(57.01875, 45), (59.05, 45)], [(70.025, 45), (73.234375, 45)], [(88.0, 45), (89.5875, 45)], [(119.034375, 45), (122.24375, 45)], [(137.009375, 45), (138.6, 45)], [(137.009375, 64), (17.7125, 64)], [(137.009375, 64), (27.4625, 64)], [(137.009375, 64), (34.075, 64)], [(137.009375, 64), (43.0125, 64)], [(137.009375, 64), (54.8375, 64)], [(137.009375, 64), (56.978125, 64)], [(137.009375, 64), (60.228125, 64)], [(137.009375, 64), (61.04375, 64)], [(137.009375, 64), (61.853125, 64)], [(137.009375, 64), (63.484375, 64)], [(137.009375, 64), (65.534375, 64)], [(137.009375, 64), (109.2375, 64)], [(137.009375, 64), (110.053125, 64)], [(137.009375, 64), (110.8625, 64)], [(137.009375, 64), (112.4875, 64)], [(137.009375, 64), (115.74375, 64)], [(137.009375, 48), (20.925, 48)], [(22.584375, 48), (24.178125, 48)], [(43.053125, 48), (43.828125, 48)], [(45.4875, 48), (47.078125, 48)], [(61.078125, 48), (61.853125, 48)], [(74.084375, 48), (74.859375, 48)], [(78.15, 48), (79.7375, 48)], [(92.059375, 48), (92.8375, 48)], [(94.503125, 48), (96.0875, 48)], [(110.0875, 48), (110.8625, 48)], [(123.0875, 48), (123.86875, 48)], [(127.153125, 48), (128.74375, 48)], [(141.075, 48), (141.85, 48)], [(143.509375, 48), (145.103125, 48)], [(20.9625, 46), (22.55, 46)], [(30.71875, 46), (31.934375, 46)], [(43.8625, 46), (45.453125, 46)], [(53.625, 46), (54.8375, 46)], [(74.89375, 46), (76.484375, 46)], [(92.875, 46), (94.4625, 46)], [(102.634375, 46), (103.84375, 46)], [(123.903125, 46), (125.49375, 46)], [(141.884375, 46), (143.475, 46)], [(151.64375, 46), (152.859375, 46)], [(158.959375, 46), (159.903125, 46)], [(158.959375, 43), (30.678125, 43)], [(158.959375, 43), (33.2125, 43)], [(51.99375, 43), (53.584375, 43)], [(63.51875, 43), (65.109375, 43)], [(65.55, 43), (65.91875, 43)], [(76.525, 43), (78.1125, 43)], [(101.0, 43), (102.59375, 43)], [(112.528125, 43), (114.11875, 43)], [(125.534375, 43), (127.11875, 43)], [(150.009375, 43), (151.603125, 43)], [(157.11875, 43), (158.925, 43)], [(157.11875, 57), (56.978125, 57)], [(157.11875, 57), (108.428125, 57)], [(157.11875, 47), (59.825, 47)], [(157.11875, 58), (63.484375, 58)], [(64.328125, 58), (65.5125, 58)], [(110.903125, 58), (112.4875, 58)], [(113.3375, 58), (114.525, 58)], [(113.3375, 50), (64.29375, 50)], [(73.26875, 50), (74.05, 50)], [(109.275, 50), (110.053125, 50)], [(109.275, 50), (113.303125, 50)], [(122.278125, 50), (123.059375, 50)], [(66.6, 53), (67.9375, 53)], [(108.4625, 59), (108.834375, 59)], [(114.559375, 55), (114.928125, 55)]][br][br]