vsqx2midi120.py - VOCALOID3 の vsqx を 歌詞入りMIDI に変換する
『あなたが降る街 vocal パートMIDI - ニコニ・コモンズ』を作るため、前に作った
を改造して MIDI 内に歌詞を入れられるようにしてみました。ニコニ・コモンズが vsqx に直接対応していればこんな手間は要らないのですが…
実行するには Python 2.6 あるいはそれ以降のバージョンの Python 2.x を用意してください。
使い方
- Python をインストール(やり方は適当にぐぐって)
- 下のソースを BOM付き utf-8 で「vsqx2midi.py」という名前で保存 (メモ帳で文字コード「utf-8」にして保存すればOK)
- そのファイルを右クリックして「Edit with IDLE」を選択
- VOCALOID3 Editor で適当な曲を「hoge.vsqx」という名前で保存
- IDLE で [F5] を押して Python スクリプトを実行
- 「hoge.mid」という名前で MIDI の出来上がり
# 2012-02-14 ver.1.2.0 歌詞対応 import xml.dom.minidom import struct FILENAME = 'hoge' # 対象のファイル名 def chrs(s): return ''.join([chr(x) for x in s]) def i2(v): if v > 0xffff: raise ValueError(v) return struct.pack('>H', v) def i4(v): if v > 0xffffffff: raise ValueError(v) return struct.pack('>I', v) def delta(v): if v < 0: raise ValueError(v) a = [] while True: if v <= 0x7f: a.append(v) break else: a.append(v % 0x80) v //= 0x80 a.reverse() return ''.join([chr(x + 0x80) for x in a[0:-1]] + [chr(a[-1])]) def notedata(n, tag): return n.getElementsByTagName(tag)[0].firstChild.data def makenote(n): return dict([(x, notedata(n, x)) for x in ('posTick', 'durTick', 'noteNum', 'velocity', 'lyric', 'phnms')]) def noteon(ch, nn, vel): return chr(0x90 + ch) + chr(nn) + chr(vel) def noteoff(ch, nn): return chr(0x80 + ch) + chr(nn) + chr(0) def lyrics(lyric): s = lyric.encode('shift-jis', 'replace') return '\xff\x05' + chr(len(s)) + s def maketrack(n): track = [] if n.nodeName == 'note': track.append(makenote(n)) if n.childNodes: for i in n.childNodes: track += maketrack(i) return track def makemiditrack(n, ch): ch %= 16 # 16トラックを超えたら 0 に戻る midi = [] lyric = [] tick = 0 for p in n.getElementsByTagName('musicalPart'): postick = int(notedata(p, 'posTick')) for i in maketrack(p): t = postick + int(i['posTick']) notelen = int(i['durTick']) midi.append( delta(t - tick) + noteon(ch, int(i['noteNum']), int(i['velocity'])) + delta(0) + lyrics(i['lyric']) + delta(notelen) + noteoff(ch, int(i['noteNum']))) lyric.append(i['lyric']) tick = t + notelen data = ''.join(midi) + delta(0) + chrs([0xff, 0x2f, 0]) return 'MTrk' + i4(len(data)) + data, ' '.join(lyric) def makeconductor(): data = delta(0) + '\xff\x2f\x00' return 'MTrk' + i4(len(data)) + data def makemidifile(filename): print 'loading:', filename d = xml.dom.minidom.parse(filename) tracks = [makeconductor()] lyrics = [] for n, i in enumerate(d.getElementsByTagName('vsTrack')): print 'track', n track, lyric = makemiditrack(i, n) tracks.append(track) lyrics.append(lyric) header = 'MThd' + i4(6) + i2(1) + i2(len(tracks)) + i2(480) return header + ''.join(tracks), '\n\n'.join(lyrics) midi, lyric = makemidifile(FILENAME + '.vsqx') f = open(FILENAME + '.mid', 'wb') f.write(midi) f.close() f = open(FILENAME + '.txt', 'w') f.write(lyric.encode('utf_8_sig')) f.close() ## パブリックドメイン。好きに流用してください
テンポ等には対応しておらず、単にノートとペロシティと歌詞にのみ対応ですので、ニコニ・コモンズなりで公開する時にはテンポ等の他に必要なデータを適当な MIDI シーケンサで付け加えてください。(VOCALOID3 Editor で読み込むだけなら、どのみちテンポ等は読み込みませんので不要ですが)