vsqx2midi.py - VOCALOID3 の vsqx を MIDI ファイルに変換する

テンポやら何やら全部無視して、単にノートとペロシティだけを MIDI にするやっつけ Python スクリプトですが、参考までに。

実行するには Python 2.6 あるいはそれ以降のバージョンの Python 2.x を用意してください。*1

使い方

  1. Python をインストール(やり方は適当にぐぐって)
  2. 下のソースを BOM付き utf-8 で「vsqx2midi.py」という名前で保存 (メモ帳で文字コードutf-8」にして保存すればOK)
  3. そのファイルを右クリックして「Edit with IDLE」を選択
  4. VOCALOID3 Editor で適当な曲を「hoge.vsqx」という名前で保存
  5. IDLE で [F5] を押して Python スクリプトを実行
  6. hoge.mid」という名前で MIDI の出来上がり
# 2012-01-03 ver.1.0.1 トラックが16を超える場合への対処を追加

import xml.dom.minidom
import struct

FILENAME = 'hoge.vsqx'  # 対象のファイル名

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):
    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 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 = []
    tick = 0
    for i in maketrack(n):
        t = int(i['posTick'])
        notelen = int(i['durTick'])
        midi.append(
            delta(t - tick) +
            noteon(ch, int(i['noteNum']), int(i['velocity'])) +
            delta(notelen) +
            noteoff(ch,  int(i['noteNum'])))
        tick = t + notelen
    data = ''.join(midi) + delta(0) + chrs([0xff, 0x2f, 0])
    return 'MTrk' + i4(len(data)) + data

def makemidifile(filename):
    print 'loading:', filename
    d = xml.dom.minidom.parse(filename)
                
    tracks = []
    for n, i in enumerate(d.getElementsByTagName('vsTrack')):
        print 'track', n
        tracks.append(makemiditrack(i, n))
    header = 'MThd' + i4(6) + i2(1) + i2(len(tracks)) + i2(480)
    return header + ''.join(tracks)

m = makemidifile(FILENAME)
f = open('hoge.mid', 'wb')
f.write(m)
f.close()

## パブリックドメイン。好きに流用してください

普通は必要ないプログラムかと思いますが、ついうっかり VOCALOID3 Editor で直接打ち込みしてしまい、MIDI ファイルが無い、という場合には使えるかも?

*1:2.6 以前でも OK かもしれませんが、作ったのが 2.6 なので