OpenJTalkとNode.jsでブラウザから日本語TTS
ブラウザから日本語TTSと言えばgoogle翻訳の非公式APIが有名(?)ですが、ローカルで堂々と使える仕組みを試してみました。
とは言っても骨格部分はこちら:凹みTips - Node.js 用 Open JTalk アドオンを作ってみた からもらってきたもので、私はただその上にサンプルアプリを書いてみただけなのですけど...
ソースはこの中の drop_n_talk になります。
https://github.com/penkoba/node-openjtalk
あ、とりあえずLinux前提です。
OpenJTalkについては過去にもいくつか書いているので、説明は省略します。
http://d.hatena.ne.jp/penkoba/20121014/1350201613
http://d.hatena.ne.jp/penkoba/20121024/1351091330
またOpenJTalkのNodeアドオンについては上述のhecomiさんのブログを参照してください。
というわけでここではアプリ部分の解説に集中することにします。(手抜き)
Nodeアプリ生成
まずNodeのアプリケーションのスケルトンをexpressで生成します。
% express drop_n_talk % cd drop_n_talk % npm install
またブラウザとNodeの通信にsocket.ioを使います。
% npm install socket.io
サーバ側実装
expressの生成したapp.jsをdrop_n_talk.jsとリネームし、編集します。
まずはrequireにsocket.ioとOpenJTalkを追加。(OpenJTalkのプラグインが ../build/Release/openjtalk.node にあるという前提です)
var sockio = require('socket.io') , OpenJTalk = require('../build/Release/openjtalk').OpenJTalk;
OpenJTalkインスタンス生成と初期化。
var mei = new OpenJTalk(); mei.init("../data/mei_normal", "../openjtalk/open_jtalk_dic_utf_8-1.05");
そしてsocket.ioのイベントハンドラを記述します。'talk'イベントを受け取り、OpenJTalkモジュールのtalk()を発行します。
(注)ちなみにexpressの2.xではexpress()がhttp.Serverのインスタンスを返していたそうで、socket.ioのlisten()にそれを渡している例がweb上にたくさんあるのですが、3.xではそれでは動きません。3.xの場合はhttp.createServer(app)で返ってくるhttp.Serverインスタンスを渡します。
var server = http.createServer(app); server.listen(app.get('port'), function(){ console.log("Express server listening on port " + app.get('port')); }); var io = sockio.listen(server); io.on('connection', function(socket) { socket.on('talk', function(txt) { mei.talk(txt); }); });
クライアント側html
次にviews/index.jadeを編集します。jadeはhtmlのテンプレートエンジンです。必要なスクリプトを読み込み(talk.jsについては後述)、file読み込みボタン、textarea、talkボタンを配置します。
extends layout block content script(src='/socket.io/socket.io.js') script(src='/javascripts/talk.js') input(id="readbtn", type="file") br textarea(id="droparea", rows="10", cols="80") br button(onclick='javascript:talkArea()') talk
クライアント側js
先ほどのjadeで '/javascripts/talk.js' を読み込む指定をしましたが、public/javascripts/talk.js として以下の内容で新規作成します。
を実装します。
var socket = io.connect('/'); function talkArea(){ socket.emit('talk', document.getElementById("droparea").value); } function talkFile(file) { var reader = new FileReader(); reader.onload = function(e) { var txt = e.target.result; document.getElementById("droparea").value = txt; socket.emit('talk', txt); } reader.readAsText(file, "utf-8"); } function onChangeFile(e) { talkFile(e.target.files[0]); } function onDropFile(e) { e.preventDefault(); talkFile(e.dataTransfer.files[0]); }; function onDragOver(e) { if (e.preventDefault) { e.preventDefault(); } return false; }; function onDragEnter(e) { if (e.preventDefault) { e.preventDefault(); } return false; }; window.onload = function() { var btn = document.getElementById("readbtn"); btn.addEventListener("change", onChangeFile, false); var da = document.getElementById("droparea"); da.addEventListener("dragover", onDragOver, false); da.addEventListener("dragenter", onDragEnter, false); da.addEventListener("drop", onDropFile, false); };
実装は以上です。
要するに、socket.emit('talk', txt) を叩けば喋ってくれるということですね。
動作
nodeでdrop_n_talk.jsを起動し、ブラウザでhttp://localhost:3000 にアクセスすると、以下のような味も素っ気もないページが表示されます。(画面はLinuxのchromeの例)
ここに適当な日本語を入力してtalkボタンを押せば、うまく行けば喋ってくれます。また「ファイルを選択」ボタンからテキストファイル(UTF-8)を選択するか、ドラッグ&ドロップでtextareaに落とせばファイルの内容を喋ります。
謝辞
最後になりましたが、OpenJTalkのNodeモジュールを作っていただいた hecomiさん ありがとうございました。
いつも勝手に参考にしたり使わせていただいております。<(__)>
以上です。