実行環境 |
Microsoft Windows 8.1 (64bit) |
手順1
- 適当なディレクトリに下のmml2smf.jsとmml.txtを用意する
- コマンドプロンプトを起動し先のディレクトリに移動する
- 以下のコマンドを入力する
cscript mml2smf.js mml.txt
手順2
- 適当なディレクトリに下のmml2smf.jsとmml.txtを用意する
- mml2smf.jsのショートカットをデスクトップ等に作成する
- 上のショートカットにmml.txtをDrag&Dropする
mml2smf.js
// OpenTextFile
var ForReading = 1;
// SaveOptionsEnum
var adSaveCreateOverWrite = 2;
var scale = {A:9, B:11, C:0, D:2, E:4, F:5, G:7}; // 音階
// 外部変数
var deflen = 4;
var octave = 4;
var rest = 0;
var datalen; // データ長
var st;
var timebase = 480;
WScript.Quit(main());
//==============================================================================
function main()
{
// 引数処理
var args = WScript.Arguments;
if (args.length != 1) {
WScript.Echo("usage: cscript mml2smf.js mmlfile");
return 1;
}
var path_in = args(0);
// パス名処理
var fso = WScript.CreateObject("Scripting.FileSystemObject");
path_in = fso.GetAbsolutePathName(path_in);
var dir = fso.GetParentFolderName(path_in);
var fnm = fso.GetBaseName(path_in);
var path_out = dir + "\\" + fnm + ".mid";
// 入力ファイルのオープン
var fi = fso.OpenTextFile(path_in, ForReading); // ASCII
// ストリームのオープン
st = WScript.CreateObject("ADODB.Stream");
st.Charset = "iso-8859-1";
st.Open();
// ヘッダチャンク
WriteBE(4, 0x4d546864); // MThd
WriteBE(4, 6); // データ長
WriteBE(2, 0); // フォーマットタイプ
WriteBE(2, 1); // トラック数
WriteBE(2, timebase); // タイムベース
// トラックチャンク
WriteBE(4, 0x4d54726b); // MTrk
WriteBE(4, 0); // データ長(仮)
datalen = 0;
while (! fi.AtEndOfStream) {
var line = fi.ReadLine();
line.match(/[^']*/);
line = RegExp.lastMatch;
var token = line.match(/[<>A-GLORT][#+\-\d\.]*/gi);
if (token == null) continue;
for (var n = 0; n < token.length; n++) {
var cmd = token[n].substr(0, 1).toUpperCase();
var param = token[n].substr(1);
switch (cmd) {
case "<":
octave--;
break;
case ">":
octave++;
break;
case "A": case "B": case "C": case "D": case "E": case "F": case "G":
Note(cmd, param);
break;
case "L":
deflen = parseInt(param);
break;
case "O":
octave = parseInt(param);
break;
case "R":
Rest(param);
break;
case "T":
var tempo = parseInt(param);
WriteData(0, 0xff, 0x51, 3);
WriteBE(3, 60000000 / tempo); // 4分音符のマイクロ秒数
break;
}
}
}
fi.Close();
// トラック終了
WriteData(rest, 0xff, 0x2f, 0x00);
st.Position -= datalen + 4;
WriteBE(4, datalen); // データ長
// ストリームをファイルに保存
st.SaveToFile(path_out, adSaveCreateOverWrite);
st.Close();
// 再生
var WshShell = WScript.CreateObject("WScript.Shell");
WshShell.Run(path_out);
return 0;
}
function WriteBE(count, value)
{
for (var n = count - 1; 0 <= n; n--) {
st.WriteText(String.fromCharCode( (value >> (8 * n)) & 0xff ));
}
datalen += count;
}
function WriteData(t, cmd, d1, d2)
{
// 可変長tick
var v = ((t&0xfe00000)<<3)|((t&0x1fc000)<<2)|((t&0x3f80)<<1)|(t&0x7f)|0x80808000;
for (var n = 3; 0 <= n; n--) {
var b = (v >> (8 * n)) & 0xff;
if (b != 0x80) {
WriteBE(1, b);
}
}
WriteBE(1, cmd);
WriteBE(1, d1);
WriteBE(1, d2);
}
function Note(cmd, param)
{
param.match(/([#+]*)(\-*)(\d*)(\.*)/);
// 臨時記号 accidental
var acci = RegExp.$1.length - RegExp.$2.length;
var note = (octave + 1) * 12 + scale[cmd] + acci;
var value = (RegExp.$3 == "") ? deflen : parseInt(RegExp.$3);
var dot = RegExp.$4.length;
var tick = timebase * 4 / value;
tick += tick * ((1<<dot) - 1) / (1<<dot);
var gate = tick * 7 / 8;
WriteData(rest, 0x90, note, 0x70);
WriteData(gate, 0x80, note, 0x00);
rest = tick - gate;
}
function Rest(param)
{
param.match(/(\d*)(\.*)/);
var value = (RegExp.$1 == "") ? deflen : parseInt(RegExp.$1);
var dot = RegExp.$2.length;
var tick = timebase * 4 / value;
tick += tick * ((1<<dot) - 1) / (1<<dot);
rest += tick;
}
mml.txt
' アニーローリー
' スコットランド民謡
t150
e8.d16 c.c8>c.<b8 ba2a g.e8ed8c8 d2.
e8.d16 c.c8>c.<b8 ba2a g.e8d.c8 c2r
g >c.c8d.d8 e2.<g >c.c8d.d8 e2.
o5 e8.d16 c.<b8a>c8<a8 ge2e8.d16 c8>c<e8d.c8 c2.
最終更新:2014年03月23日 12:08