@set @EXIT_SUCCESS = 0
@set @EXIT_FAILURE = 1
@set @DEFAULT_CH_KBPS = 64
@set @DEFAULT_QUALITY = 75
var InputExtRegExp = /^wav$/i;
var OutputExt = "wma";
var Shell= new ActiveXObject("WScript.Shell");
var Fso = new ActiveXObject("Scripting.FileSystemObject");
var Opt = new struct_options();
main();
exit();
function echo(text)
{
Script.Echo(text);
}
function exit(exit_code)
{
Script.Quit(exit_code);
}
function print_usage()
{
echo(Script.ScriptName);
echo(" [/cbr[:<ビットレート kbps>] |");
echo(" /cbr2[:<ビットレート kbps>] |");
echo(" /vbr[:<音質 %>] |");
echo(" /vbr2[:<ビットレート kbps>]]");
echo(" [/fs:<サンプリング レート kHz>]");
echo(" [/pro | /lossless] [/force] [/suspend]");
echo(" <フォルダ名/ファイル名> [<フォルダ名/ファイル名 2> [...]]");
echo();
echo(" /cbr CBR, 1-pass でエンコードする。");
echo(" /cbr2 CBR, 2-pass でエンコードする。");
echo(" /vbr VBR, 1-pass でエンコードする (音質 1〜99%)。");
echo(" /vbr2 VBR, 2-pass でエンコードする (平均ビットレート指定)。");
echo(" /fs 優先するサンプリング レート (kHz)。");
echo(" /pro Windows Media Audio Professional を使用。");
echo(" /lossless Windows Media Audio Lossless を使用。");
echo(" /export 開かれているファイル群を圧縮する。");
echo(" /force 既存ファイルがあっても上書きする。");
echo(" /suspend 終了後 OS を待機状態にする。");
echo();
echo("注: ビットレートの指定は 2 ch の場合の値 (kbps) を書く。");
echo();
echo("注: ビットレートの既定値は " + (@DEFAULT_CH_KBPS * 2) + " kbps。音質の既定値は " + @DEFAULT_QUALITY + "%。");
echo();
echo("注: フォルダ名やファイル名が一つも指定されていない場合は、指定コー");
echo(" デックのフォーマットが列挙表示される。");
echo();
}
function print_fmts(codec_info, fs, ch, is_vbr, is_2pass)
{
var fmt_list, fmt, i;
codec_info.IsVBR = is_vbr;
codec_info.IsTwoPass = is_2pass;
fmt_list = codec_info.GetCodecFormats(fs, ch);
fmt_list = fmt_list.toArray();
if (fmt_list.length) {
echo("--- " +
(fs / 1000) + " kHz / " +
ch + " ch / " +
(is_vbr ? "V" : "C") + "BR / " +
(is_2pass ? 2 : 1) + "-pass" +
" ---");
for (i in fmt_list)
echo(fmt_list[i].Desc);
echo();
}
return fmt_list.length;
}
function print_formats(codec_info)
{
var fs_list = new Array();
var n;
for (n = 1; n <= 8; n *= 2) {
fs_list[fs_list.length] = 96000 / n;
fs_list[fs_list.length] = 88200 / n;
fs_list[fs_list.length] = 64000 / n;
}
echo("使用可能な圧縮フォーマット:");
var num_fmts = 0;
var fs, ch, i;
for (i in fs_list) {
for (ch = 2; ch; --ch) {
fs = fs_list[i];
for (n = 0; n < 4; ++n) {
num_fmts += print_fmts(codec_info, fs, ch,
((n & 0x02) != 0),
((n & 0x01) != 0));
}
}
}
if (num_fmts == 0)
echo("(なし)");
echo("※全てのフォーマットを列挙していない可能性があります。");
}
function struct_options()
{
this.CodecHint = "";
this.IsVBR = false;
this.IsTwoPass = false;
this.ChKbps = @DEFAULT_CH_KBPS;
this.Quality = 0;
this.SampleRate = 0;
this.ExportMode = false;
this.ForceOverwrite = false;
this.Suspend = false;
this.InputFiles = new Array();
}
function str2chkbps(str)
{
var val = @DEFAULT_CH_KBPS;
if ((typeof(str) == "string") && str.length) {
val = parseInt(str);
if (val >= 1000)
val = Math.round(val / 1000);
val /= 2;
}
return val;
}
function str2quality(str)
{
var val = @DEFAULT_QUALITY;
if ((typeof(str) == "string") && str.length) {
val = parseInt(str);
if (val < 1)
val = 1;
else if (val > 100)
val = 100;
}
return val;
}
function str2fs(str)
{
var val = 0;
if ((typeof(str) == "string") && str.length) {
val = parseFloat(str);
if (val < 1000)
val *= 1000;
if (val < 8000)
val = 8000;
else if (val > 48000*4)
val = 48000*4;
val = Math.round(val);
switch (val) {
case 44000*4:
case 44000*2:
case 44000:
case 22000:
case 11000:
val += val / 440;
break;
}
}
return val;
}
function read_options()
{
var args = Script.Arguments;
if (args.Named.Exists("?")) {
print_usage();
exit(@EXIT_FAILURE);
}
var e, key;
for (e = new Enumerator(args.Named); !e.atEnd(); e.moveNext()) {
key = e.item().toLowerCase();
if (key == "cbr") {
Opt.IsVBR = false;
Opt.IsTwoPass = false;
Opt.ChKbps = str2chkbps(args.Named(key));
Opt.Quality = 0;
}
else if (key == "cbr2") {
Opt.IsVBR = false;
Opt.IsTwoPass = true;
Opt.ChKbps = str2chkbps(args.Named(key));
Opt.Quality = 0;
}
else if (key == "vbr") {
Opt.IsVBR = true;
Opt.IsTwoPass = false;
Opt.ChKbps = 0;
Opt.Quality = str2quality(args.Named(key));
}
else if (key == "vbr2") {
Opt.IsVBR = true;
Opt.IsTwoPass = true;
Opt.ChKbps = str2chkbps(args.Named(key));
Opt.Quality = 0;
}
else if (key == "fs")
Opt.SampleRate = str2fs(args.Named(key));
else if (key == "pro")
Opt.CodecHint = "professional";
else if (key == "lossless")
Opt.CodecHint = key;
else if (key == "export")
Opt.ExportMode = true;
else if (key == "force")
Opt.ForceOverwrite = true;
else if (key == "suspend")
Opt.Suspend = true;
else {
echo("エラー: 不明なオプション " + e.item());
exit(@EXIT_FAILURE);
}
}
var i;
for (i = 0; i < args.Unnamed.length; ++i)
Opt.InputFiles[i] = args.Unnamed(i);
}
function select_codec(writer)
{
var ci_list = writer.CodecInfoList;
var name, i;
if (Opt.CodecHint.length) {
for (i = 0; i < ci_list.Count; ++i) {
name = ci_list(i).Name.toLowerCase();
if (name.indexOf("windows media audio") >= 0)
if (name.indexOf(Opt.CodecHint) >= 0)
return ci_list(i);
}
}
for (i = 0; i < ci_list.Count; ++i) {
name = ci_list(i).Name.toLowerCase();
if (name.indexOf("windows media audio") >= 0) {
if (name.indexOf("professional") < 0)
if (name.indexOf("lossless") < 0)
if (name.indexOf("voice") < 0)
return ci_list(i);
}
}
echo("コーデックが見つかりません。");
exit(@EXIT_FAILURE);
}
function select_lossless_fmt(codec_info, sample_rate, channels, bits_per_sample)
{
var fmt_list, fmt;
var fmt_cand = null;
var i, diff, diff2;
codec_info.IsVBR = true;
codec_info.IsTwoPass = false;
fmt_list = codec_info.GetCodecFormats(sample_rate, channels);
fmt_list = fmt_list.toArray();
for (i in fmt_list) {
fmt = fmt_list[i];
diff = fmt.BitsPerSample - bits_per_sample;
if (diff == 0)
return fmt;
if (fmt_cand == null)
fmt_cand = fmt;
else {
diff2 = fmt_cand.BitsPerSample - bits_per_sample;
if (diff2 * diff >= 0) {
if (Math.abs(diff2) > Math.abs(diff))
fmt_cand = fmt;
}
else {
if (diff >= 0)
fmt_cand = fmt;
}
}
}
return fmt_cand;
}
function select_lossless_format(codec_info, sample_rate, channels, bits_per_sample)
{
var fmt = select_lossless_fmt(codec_info, sample_rate, channels, bits_per_sample);
if ((fmt == null) && (channels == 1))
fmt = select_lossless_fmt(codec_info, sample_rate, 2, bits_per_sample);
return fmt;
}
function select_lossy_fmt(codec_info, sample_rate, channels, bits_per_sample)
{
var fmt_list, fmt_cand, fmt, fmtx;
var i, x, diff, diff2;
bits_per_sample = (bits_per_sample <= 16) ? 16 : 24;
codec_info.IsVBR = Opt.IsVBR;
codec_info.IsTwoPass = Opt.IsTwoPass;
fmt_list = codec_info.GetCodecFormats(sample_rate, channels);
fmt_list = fmt_list.toArray();
fmt_cand = new Array();
fmt_cand[0] = null;
fmt_cand[1] = null;
for (i in fmt_list) {
fmt = fmt_list[i];
if (Opt.Quality && fmt.IsQualityBased) {
diff = fmt.Quality - Opt.Quality;
if ((diff == 0) && (fmt.BitsPerSample == bits_per_sample))
return fmt;
switch (fmt.BitsPerSample) {
case 16:
case 24:
x = fmt.BitsPerSample / 8 - 2;
fmtx = fmt_cand[x];
if ((fmtx == null) || (diff == 0))
fmtx = fmt;
else {
diff2 = fmtx.Quality - Opt.Quality;
if (diff2 * diff >= 0) {
if (Math.abs(diff2) > Math.abs(diff))
fmtx = fmt;
}
else {
if (diff <= 0)
fmtx = fmt;
}
}
fmt_cand[x] = fmtx;
break;
}
}
else if (!Opt.Quality && !fmt.IsQualityBased) {
diff = Math.round(fmt.Bitrate / 1000) - Opt.ChKbps * channels;
if ((diff == 0) && (fmt.BitsPerSample == bits_per_sample))
return fmt;
switch (fmt.BitsPerSample) {
case 16:
case 24:
x = fmt.BitsPerSample / 8 - 2;
fmtx = fmt_cand[x];
if ((fmtx == null) || (diff == 0))
fmtx = fmt;
else {
diff2 = Math.round(fmtx.Bitrate / 1000) - Opt.ChKbps * channels;
if (diff2 * diff >= 0) {
if (Math.abs(diff2) > Math.abs(diff))
fmtx = fmt;
}
else {
if (diff <= 0)
fmtx = fmt;
}
}
fmt_cand[x] = fmtx;
break;
}
}
}
x = bits_per_sample / 8 - 2;
return fmt_cand[x] ? fmt_cand[x] : fmt_cand[x ^ 1];
}
function select_lossy_format(codec_info, sample_rate, channels, bits_per_sample)
{
var fmt = select_lossy_fmt(codec_info, sample_rate, channels, bits_per_sample);
if ((fmt == null) && (channels == 1))
fmt = select_lossy_fmt(codec_info, sample_rate, 2, bits_per_sample);
return fmt;
}
function get_link_target(file_path)
{
if (Fso.GetExtensionName(file_path).toLowerCase() == "lnk")
return Shell.CreateShortcut(file_path).TargetPath;
else
return file_path;
}
function process_file(writer, codec_info, input_file)
{
var input_ext = Fso.GetExtensionName(input_file);
var output_file = input_file.slice(0, -input_ext.length) + OutputExt;
echo(output_file);
if (!Opt.ForceOverwrite)
if (Fso.FileExists(output_file))
if (Fso.GetFile(output_file).DateLastModified > Fso.GetFile(input_file).DateLastModified) {
echo(" ファイルが既に存在します。");
return;
}
var clip = Application.CreateSoundClip(input_file);
var fmt = null;
if (Opt.CodecHint.indexOf("lossless") >= 0) {
if (Opt.SampleRate)
fmt = select_lossless_format(codec_info, Opt.SampleRate, clip.Channels, clip.BitsPerSample);
if ((fmt == null) && (Opt.SampleRate != clip.SampleRate))
fmt = select_lossless_format(codec_info, clip.SampleRate, clip.Channels, clip.BitsPerSample);
if ((fmt == null) && (Opt.SampleRate != 44100) && (clip.SampleRate != 44100))
fmt = select_lossless_format(codec_info, 44100, clip.Channels, 16);
}
else {
if (Opt.SampleRate)
fmt = select_lossy_format(codec_info, Opt.SampleRate, clip.Channels, clip.BitsPerSample);
if ((fmt == null) && (Opt.SampleRate != clip.SampleRate))
fmt = select_lossy_format(codec_info, clip.SampleRate, clip.Channels, clip.BitsPerSample);
if ((fmt == null) && (Opt.SampleRate != 44100) && (clip.SampleRate != 44100))
fmt = select_lossy_format(codec_info, 44100, clip.Channels, clip.BitsPerSample);
}
if (fmt == null) {
echo(" 圧縮フォーマットが見つかりません。");
echo("(途中終了)");
exit(@EXIT_FAILURE);
}
echo(" 圧縮フォーマット: " + fmt.Desc);
var time;
writer.CodecFormat = fmt;
time = Application.System.GetTickCount();
writer.Process(clip, output_file);
time = Application.System.GetTickCount() - time;
var sec, min;
sec = Math.round(time / 1000);
min = Math.floor(sec / 60);
sec -= min * 60;
echo(" 経過時間: " + min + " 分 " + sec + " 秒");
var fi = Fso.GetFile(get_link_target(input_file));
var fo = Fso.GetFile(output_file);
echo(" ファイル サイズ比: " + Math.round(fo.Size / fi.Size * 100) + "%");
}
function process_folder(writer, codec_info, folder_path)
{
var folder = Fso.GetFolder(folder_path);
var fc, input_file, input_ext;
for (fc = new Enumerator(folder.Files); !fc.atEnd(); fc.moveNext()) {
input_file = fc.item().Path;
input_ext = Fso.GetExtensionName(get_link_target(input_file));
if (InputExtRegExp.test(input_ext))
process_file(writer, codec_info, input_file);
}
}
function process_doc(writer, codec_info, output_file, doc)
{
echo(output_file);
if (!Opt.ForceOverwrite)
if (Fso.FileExists(output_file)) {
echo(" ファイルが既に存在します。");
return;
}
doc.SelectionFrom = 0;
doc.SelectionTo = doc.Length;
var clip = doc.CreateSoundClip();
var fmt = null;
if (Opt.CodecHint.indexOf("lossless") >= 0) {
if (Opt.SampleRate)
fmt = select_lossless_format(codec_info, Opt.SampleRate, clip.Channels, clip.BitsPerSample);
if ((fmt == null) && (Opt.SampleRate != clip.SampleRate))
fmt = select_lossless_format(codec_info, clip.SampleRate, clip.Channels, clip.BitsPerSample);
if ((fmt == null) && (Opt.SampleRate != 44100) && (clip.SampleRate != 44100))
fmt = select_lossless_format(codec_info, 44100, clip.Channels, 16);
}
else {
if (Opt.SampleRate)
fmt = select_lossy_format(codec_info, Opt.SampleRate, clip.Channels, clip.BitsPerSample);
if ((fmt == null) && (Opt.SampleRate != clip.SampleRate))
fmt = select_lossy_format(codec_info, clip.SampleRate, clip.Channels, clip.BitsPerSample);
if ((fmt == null) && (Opt.SampleRate != 44100) && (clip.SampleRate != 44100))
fmt = select_lossy_format(codec_info, 44100, clip.Channels, clip.BitsPerSample);
}
if (fmt == null) {
echo(" 圧縮フォーマットが見つかりません。");
echo("(途中終了)");
exit(@EXIT_FAILURE);
}
echo(" 圧縮フォーマット: " + fmt.Desc);
var time;
writer.CodecFormat = fmt;
time = Application.System.GetTickCount();
writer.Process(clip, output_file);
time = Application.System.GetTickCount() - time;
var sec, min;
sec = Math.round(time / 1000);
min = Math.floor(sec / 60);
sec -= min * 60;
echo(" 経過時間: " + min + " 分 " + sec + " 秒");
var fi_size = (clip.Length * clip.SampleRate) * (clip.BitsPerSample / 8) * clip.Channels;
var fo = Fso.GetFile(output_file);
echo(" ファイル サイズ比: " + Math.round(fo.Size / fi_size * 100) + "%");
}
function process_documents(writer, codec_info, output_dir)
{
var names = new Array();
var i, j, s, t, n;
for (i = 0; i < Application.Documents.Count; ++i)
names[i] = Fso.GetBaseName(Application.Documents(i).Title);
for (i in names) {
s = names[i];
for (j = 0; j < i; ++j) {
if (s.toUpperCase() == names[j].toUpperCase())
break;
}
if (j < i) {
for (n = 2; ; ++n) {
t = s + " (" + n + ")";
for (j = 0; j < names.length; ++j) {
if (t.toUpperCase() == names[j].toUpperCase())
break;
}
if (j == names.length)
break;
}
names[i] = t;
}
}
for (i in names) {
s = output_dir + "\\" + names[i] + "." + OutputExt;
process_doc(writer, codec_info, s, Application.Documents(i));
}
}
function main()
{
read_options();
var writer = Application.CreateFileWriter("wma");
var codec_info = select_codec(writer);
echo("コーデック: " + codec_info.Name);
echo();
if (Opt.InputFiles.length == 0) {
print_formats(codec_info);
exit(@EXIT_FAILURE);
}
if (Opt.Suspend)
Application.System.SetSuspendState(false, true, false);
var i, str;
if (Opt.ExportMode) {
if (Opt.InputFiles.length != 1) {
echo(" 出力先フォルダを一つだけ指定してください。");
exit(@EXIT_FAILURE);
}
str = Opt.InputFiles[0];
if ((str.length > 0) && (str.charAt(str.length - 1) == "\\"))
str = str.substr(0, str.length - 1);
if (Fso.FileExists(str)) {
echo(str);
echo(" パスが無効です。");
exit(@EXIT_FAILURE);
}
process_documents(writer, codec_info, str);
}
else {
for (i in Opt.InputFiles) {
str = Opt.InputFiles[i];
if (Fso.FolderExists(str))
process_folder(writer, codec_info, str);
else if (Fso.FileExists(str))
process_file(writer, codec_info, str);
else {
echo(str);
echo(" パスが無効です。");
echo("(途中終了)");
exit(@EXIT_FAILURE);
}
}
}
exit(@EXIT_SUCCESS);
}