そういうわけでWSHで作ってみた。僕はBASICで考えるのが嫌いで嫌いでたまらないので、JScriptで書いてみた。JS拡張子は環境によってはスクリプトとして動かないので、wsf拡張子にした。なお、このスクリプトは、SAPI5が利用できるWindowsXPパソコンを対象としている。
お約束。私takayanは、このプログラムによって生じる一切の不具合、影響などに関する損害は一切感知しません。 それに同意いただいた方のみご利用ください。改変自由です。
↓↓↓ダウンロード
textvoice.zip
解凍ソフトを使って適当な場所で解凍すると二つのWSHのスクリプトが入っている。textvoice.wsfは文書ファイルを引数にして、それを音声ファイルに変換する。sendto.wsfは、このtextvoice.wsfのショートカットを「送る」メニューに登録する。
これがスクリプトの全文
var startTime = new Date();
var shell = WScript.CreateObject('WScript.Shell');
var fs = WScript.CreateObject('Scripting.FileSystemObject');
var voice = WScript.CreateObject('SAPI.SpVoice');
var stream = WScript.CreateObject('SAPI.SpFileStream');
var args = WScript.Arguments;
if ( args.length == 0 ){
shell.Popup( 'ファイル名を示す引数がないので、終了します'
, 0, '報告', 0 );
WScript.Quit();
}
var path = fs.GetParentFolderName( WScript.ScriptFullName );
for( var i = 0; i<args.length; i++ ) {
var name = args(i);
// ファイルの存在を確認しておく
if ( !fs.FileExists( name ) ) {
if ( fs.FolderExists( name ) ) {
shell.Popup( '"' + name + '"はフォルダなので変換をスキップします'
, 5, '報告', 0 );
} else {
shell.Popup( '"' + name + '"は存在しないので変換をスキップします'
, 5, '報告', 0 );
}
continue;
}
var txtName = name;
var wavName = name + '.wav';
var mp3Name = name + '.mp3';
var extendedName = txtName;
// 必要ならばテキストに変換
var extend = fs.GetExtensionName(txtName);
if ( extend != '' && extend != 'txt' ) {
// xdoc2txtがあれば、まずテキストに変換する
var d2t = fs.BuildPath( path, 'xdoc2txt.exe' );
if ( fs.FileExists( d2t ) ) {
extendedName = txtName + '.' + extend;
fs.CopyFile( txtName, extendedName );
var d2tcommand = '"' + d2t + '" -f -n "' + extendedName + '"';
shell.Run( d2tcommand, 0, true );
fs.DeleteFile( extendedName );
txtName += '.txt';
}
}
// wavファイルに変換
stream.open( wavName, 3 );
voice.AudioOutputStream = stream;
var h = fs.OpenTextFile( txtName );
while (!h.AtEndOfStream) {
var line = h.ReadLine();
voice.Speak( line, 0 );
}
h.Close();
stream.close();
var last = wavName;
// lameがあればさらにmp3に変換
var lame = fs.BuildPath( path, 'lame.exe' );
var lameoptions = '';
if ( fs.FileExists( lame ) ) {
last = mp3Name;
var lamecommand = '"' + lame + '" ' + lameoptions
+ ' "' + wavName + '" "' + mp3Name + '"' ;
shell.Run( lamecommand, 0, true );
fs.DeleteFile( wavName );
}
// 文書一時ファイルの削除
if (extendedName!=txtName) {
fs.DeleteFile( txtName );
}
}
// 経過時間の計算
var finishTime = new Date();
var timeStr = "";
var diff = finishTime.getTime() - startTime.getTime();
var hour = Math.floor(diff/(60*60*1000));
if (hour!=0) {
timeStr += hour + '時間';
}
diff -= hour * (60*60*1000);
var min = Math.floor(diff/(60*1000));
if (min!=0) {
timeStr += min + '分';
}
diff -= min * (60*1000);
var sec = diff / 1000;
if (timeStr) {
timeStr += Math.floor(sec+0.5) + '秒';
} else {
timeStr = sec + '秒';
}
WScript.Echo( '変換完了です\n経過時間:' + timeStr );
プログラムをみるとすぐに分かるが、外部実行ファイルとして、xdoc2txt.exeやlame.exeを使えるようにしてある。無くてもこのスクリプトは使えるが、あるといいことがある。
xdoc2txt.exeがスクリプトと同じフォルダにあると、PDFやワード文書などテキスト以外の文書ファイルでもまともに変換してくれるようになる。xdoc2txt.exeが無いと拡張子がなんであろうとテキストファイルとみなす。もう一つのlame.exeはwavファイルをmp3に変換するプログラム。これがスクリプトと同じフォルダにあると、最終的にできる音声ファイルはmp3形式になる。
これらは同梱していないので、必要ならばそれぞれ下記の場所からダウンロードしてスクリプトと同じフォルダに入れておく。
・xdoc2txt.exeの入手先
EB series support page
・lame.exeの主な入手先
Lame MP3 Encoder Binariesなど
青空文庫などにあるテキストファイルを音声ファイルに変換し、iPodなどで聞いてみるのもいい。ただそれをするにはこのままだと難しい。ルビもしくはルビが振られた漢字を正規表現を使って削除する操作が必要になるだろう。また章やサイズ毎にファイルを切り分ける工夫をしないと、まるのまま聞き続けるのは大変だろう。
ためしに、このスクリプトで「吾輩は猫である」を丸ごとmp3に変換してみた。すると完了するのに一時間以上かかってしまった。そんなものかもしれない。変換時間の見当が付かないときは、最初からあまり大きなファイルは変換しない方がいい。
おまけのスクリプトとして下に示す「送る」メニューに変換コマンドを登録するWSHスクリプトも作ってみた。手作業でやればいいことだけれど、自動化するとどうなるか、やってみた。
これを実行すると、textvoice.wsfを「音声ファイルに変換」という名前で登録する。文書ファイルを選択して、右クリックまたはアプリケーションキーでメニューを開き、送るメニューの「音声ファイルに変換」を選ぶと、選択されているファイルと同じ場所に生成してくれる。複数のファイルも選択できる。
下で配布しているファイルを適当な場所で解凍し、sendto.wsf を実行すると、「送る」メニューに登録をしてくれる。既に登録しているのであれば、今度は削除するかどうかを聞いてくる。そのあとで再度登録するかを聞いている。
var name = '音声ファイルに変換';
var command = 'textvoice.wsf';
var shell = WScript.CreateObject('WScript.Shell');
var fs = WScript.CreateObject('Scripting.FileSystemObject');
var link = fs.BuildPath( shell.SpecialFolders('SendTo'), name + '.lnk' );
if ( fs.FileExists( link ) ) {
shell.Popup( '「' + name + '」は既に送り先に登録されています'
, 0, '報告', 0 );
var button = shell.Popup( '送り先から「' + name + '」を削除しますか?'
, 0, '確認', 4+32 );
if( button == 6 ) {
fs.DeleteFile( link );
shell.Popup( '送り先から「' + name + '」を削除しました'
, 0, '報告', 0 );
} else {
WScript.Quit();
}
}
var button = shell.Popup('送り先に「' + name + '」を登録しますか?'
, 0, '確認', 4+32 );
if( button == 6 ) {
var shortcut = shell.CreateShortcut(link);
var path = fs.GetParentFolderName( WScript.ScriptFullName );
shortcut.TargetPath = fs.BuildPath( path, command );
shortcut.Save();
shell.Popup( '送り先に「' + name + '」を登録しました', 0, '報告', 0 );
}
このスクリプトは僕が考えたものだけど、lame.exeやxdoc2txt.exeを使ったり、送るメニューなどを使うという手法は以前紹介した「ドキュメントトーカ」で使われているもの(関連記事:WindowsXPにSAPI5音声を入れるには)。misakiさんの声にこだわらなければ、「ドキュメントトーカ」は日本語合成音声の入手手段としておすすめである。このスクリプトを利用しなくても標準で音声ファイルに変換できるし、SAPI5だけでなくSAPI4の音声も使うことができる。
最近、「ドキュメントトーカ Plus V2.1」のフリー版が公開された。製品版に入っている日本語合成エンジンが入っていないなど、いくつかの制限がある。最初にアナウンスがはいるが音声ファイルも作成できる。
製品情報 ドキュメントトーカ 日本語音声合成エンジン for Windows - クリエートシステム開発株式会社
僕のプログラムは、基本的なことしかしていない。音質も生で聞くときよりも落ちている感じがする。他にもいろいろ改良の余地があるだろうが、とりあえず、ここまで。
更新:pathの獲得の仕方など、いろいろ気づいたところを修正(2008年4月12日23:50)