そういうわけで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)
mp3にエンコードする際のLAMEのコマンドが未入力になっています。
未入力のままだと、32kbpsだったと思います。
var lameoptions = '--preset cbr 128' ; //固定ビートレット128kbps
という風に指定すれば、音が良くなります。
便利に使わせて貰っています。本当にありがとうございます。
せっかくですから、音がいい方がいいですね。
あとで修正しておきます。
青空文庫の方(aozoravoice.wsf)なのですが、パラメータ(変数)を1個追加して頂たいのです。
LAMEのコマンド(変数)を最初の方で指定して、それをそのまま
var lameoptions = ' ○○ ' の○○に与えて欲しいのです。
そうすれば本体の方は弄らずに、パラメータを弄れば好みの音に変換出来るという訳です。
(初期値は --preset cbr 128 で良いと思います。)
本体の方を弄って、弄り方を間違えてしまって上手く動かなかったという事があったので。
LAMEのコマンド
http://www.geocities.jp/buritora2004/lame/
http://www.ss.iij4u.or.jp/~somali/extra/_lame.html
こちらとaozoraのほう、書き換えておきました。
文章の中の数字のみ外人がしゃべってるんですが。
何かわかるようでしたら教えてください。
英語の音声が既定の音声になっているんじゃないかと思います。
SAPI5の既定の音声を切り替えるには、次のようにします。
「スタートメニュー」の「設定」にある「コントロールパネル」を開いて、ウィンドウを開きます。
コントロールパネルのウィンドウの中にある「音声認識」を開きます。
そして新しく開いた「音声認識プロパティ」ウィンドウの上部にある「音声合成」タブを選んで、シートを切り替えます。
この画面にある「音声の選択」のところでMisakiを選んで、下にあるOKボタンか適応ボタンを押すと、Misakiが既定の音声になるはずです。またこのとき「音声の速度」を調整すると読みの速度を変えることができます。