語弊を承知でいうと、ボーカロイドみたいなツールのUTAU。
.midi
ファイルからUTAUで使う.ust
ファイルへ歌詞付きで雑に変換しダウンロードする方法のおぼえがきです。
前提
- create-react-appベースのReactアプリ
- ES6準拠のJavaScript
tone.jsでMIDIのインポート
だいぶつよいMIDIライブラリのtone.jsを利用します。
インストール
yarn add @tonejs/midi
インポート
import { Midi } from '@tonejs/midi';
ボタンクリックでMIDIを取り込めるようにする
ここは読み飛ばしていただいても。
import React, { useState } from 'react';
import { Midi } from '@tonejs/midi';
const MidiPage = (props) => {
const [midi, setMidi] = useState(false);
// FileReaderでMIDIファイルをArrayBufferで読み込む
const handleChangeFile = (e) => {
const file = e.target.files[0];
const reader = new FileReader();
reader.readAsArrayBuffer(file);
reader.onload = (e) => {
const midiData = e.target.result;
if (midiData) {
// tone.jsで変換
const midi = new Midi(midiData);
// stateに入れておく
setMidi(midi);
}
};
reader.onerror = (e) => {
// ロード失敗
};
};
return (
<div>
<input
type="file"
accept="audio/midi, audio/x-midi"
onChange={handleChangeFile}
/>
</div>
);
USTファイルに変換する
tone.jsでインポートしたMIDIをUSTに変換できるようにする
tone.jsのMIDIからUSTに雑に変換します。
歌詞にも一応対応してみます。
const parseMidiToUST = (midi, lyric, trackIndex, ustOptions = {
// USTファイルのオプション
projectName: '(no title)',
flags: '',
outFile: '',
voiceDir: '${DEFAULT}/',
mode2: true,
}) => {
const json = midi.toJSON();
const header = json.header;
// MIDIヘッダから拍子を取得
const beats = Array.isArray(header.timeSignatures) && header.timeSignatures.length > 0 ? header.timeSignatures[0].timeSignature : [4,4];
// MIDIヘッダからテンポを取得
// テンポ情報がないMIDIファイルの場合、UTAU-Synth側で50000になり操作不能になるので、ない場合は120
const tempo = header.tempos.length ? midi.header.tempos.slice(-1)[0].bpm : '120';
// 使用するトラックを指定
const track = json.tracks[trackIndex];
return `
[#VERSION]
UST Version2.0
Charset=UTF-8
[#SETTING]
TimeSignatures=(${beats[0]}/${beats[1]}/0),
Tempo=${tempo}
ProjectName=${ustOptions.projectName}
OutFile=${ustOptions.outFile}
VoiceDir=${ustOptions.voiceDir}
Flags=${ustOptions.flags}
Mode2=${ustOptions.mode2 ? 'True' : 'False'}
${track.notes.map((note, index, origin) => {
return `
[#${('0000' + index).slice(-4)}]
Delta=${index === 0 ? note.ticks : note.ticks - origin[index - 1].ticks}
Duration=${note.durationTicks}
Length=${origin[index + 1] ? origin[index + 1].ticks - note.ticks : note.durationTicks}
Lyric=${lyric[index] ? lyric[index] : ''}
NoteNum=${note.midi}
Velocity=${Math.floor(note.velocity) * 10000 / 100}
StartPoint=0.00
Intensity=100
Modulation=0
PBS=-40.0,0.0
PBW=80.0
Envelope=5.0,1.0,0.0,100.0,
100.0,100.0,100.0,7.0,80.0,1.0,100.0,0.0,1.0,100.0,1.0,100.0
VBR=0,0,0,0,0,0,0,0,0,0`;
}).join('')}
[#TRACKEND]`.replace(/^\s+/gm, ''); // Stringリテラルのインデントを削除
}
USTファイルをダウンロードできるようにする
ここは読み飛ばしていただいても。
import React, { useState } from 'react';
const parseMidiToUST = ...
const MidiPage = (props) => {
const [midi, setMidi] = useState(false);
// 適当な歌詞
const [lyric, setLyric] = useState(['か','し','で','す','よ']);
const handleDownloadUST = () => {
const text = parseMidiToUST(midi, lyric, 0);
const blob = new Blob([text], {
type: 'text/plain;charset=utf-8',
});
const a = document.createElement('a');
a.download = 'score.ust';
a.href = URL.createObjectURL(blob);
a.click();
};
return (
<div>
<input
type="file"
accept="audio/midi, audio/x-midi"
onChange={handleChangeFile}
/>
<button type="button" onClick={handleDownloadUST}>ダウンロード</button>
</div>
);
間違いなどありましたらご指摘ください、なにかの役に立てば幸いです。
(似たような要領でMusicXMLも一応いけるのですが、雑すぎるので別にします)