Google Chromeの合成音声を試してみた

Macではsayコマンドでお馴染みの合成音声が、Google Chromeでも使えるようになったらしいので試してみた。Webページ経由ではなく、Google ChromeJavaScriptコンソールを用いた。

サンプルコードにある通り、message と voice を定義する必要がある。

https://gist.github.com/laclefyoshi/8627514

voice一覧の後半に、AlexからZavoxはMacの読み上げ (say)で使われる声と同じものが現れた。おそらく、Macに (日本語用音声である) KyokoさんやOtoyaくんをインストールすれば、ここに現れると思われる。

https://gist.github.com/laclefyoshi/8627542

messageには最低限、テキストとそれに対応するvoice、言語を設定する必要がある。

> msg.text = "JavaScript (JS) is a dynamic computer programming language."
> msg.voice = voices[0]
> msg.lang = voices[0].lang  # "en-US"

最後に、再生。

> window.speechSynthesis.speak(msg)

英語音声で、英語テキスト。

括弧など記号は発音されていない。読み上げさせる際に必要になる状況もありえるだろうが、発音させることはできるのだろうか。

次に、日本語音声で、英語テキスト。

> msg.text = "JavaScript (JS) is a dynamic computer programming language."
> msg.voice = voices[7]
> msg.lang = voices[7].lang  # "ja-JP"

若干悪意すら感じる日本人 (カタカナっぽい) 英語になった。

中国語音声で、英語テキスト。

> msg.text = "JavaScript (JS) is a dynamic computer programming language."
> msg.voice = voices[9]
> msg.lang = voices[9].lang  # "zh-CN"

ピンインとして解釈されたようだ。

最後に、日本語音声で、日本語テキスト。

> msg.text = "JavaScript(ジャヴァスクリプト)とは、プログラミング言語のひとつである。"
> msg.voice = voices[7]
> msg.lang = voices[7].lang

??

「パーセントユー」と言っていることから、escapeされた文字列を読み上げているようだ。しかも、全部読み切れずに途中で切れてしまった。

> escape("JavaScript(ジャヴァスクリプト)とは、プログラミング言語のひとつである。")
"JavaScript%uFF08%u30B8%u30E3%u30F4%u30A1%u30B9%u30AF%u30EA%u30D7%u30C8%uFF09%u3068%u306F%u3001%u30D7%u30ED%u30B0%u30E9%u30DF%u30F3%u30B0%u8A00%u8A9E%u306E%u3072%u3068%u3064%u3067%u3042%u308B%u3002"

ならばと、unescape("日本語")をmsg.textに与えてみたが、ダメだった。コンソールを使っているのが悪いのかと、Webページに書いてやってみたが、やはりescapeされた文字列を読んでしまう。この問題の解決は、今後の課題とする。

ところで、よく聞いてみると、30A1を「さんじゅうえーいち」と読んでいたり、3068を「さんぜんろくじゅうはち」と読んでいたり、連続する数字の読みをちゃんと判別していることが分かる。

音声認識よりは目立たない地味なAPIだが、今後、ボーカロイド並みの実装になれば楽しいだろうと思う (すでにpitchやrateなどそれっぽい属性が付いているが、活用できなかった)。

Google Chrome Canary buildでMathMLサポート

このポストによると、Google Chrome Canary build (現在のバージョンは24.0.1293.0) でMathMLをサポートしたらしい。

試しに、LibreOfficeの数式エディタを使って、適当に数式を書き、MathML 1.01形式で保存した (ファイル名は math.mml)。gistはこれ

思うに、MathMLはLaTeXほど人手で書くことを考えていない書式であるように思う。

まず、これをGoogle ChromeのStable build (現在のバージョンは22.0.1229.94) で開くと以下のように表示された。

XMLファイルとしては正しく認識しているが、MathMLを解釈できていない。

次に、Canary buildで開くと以下のように表示された。

LibreOfficeで書いた時と違い、中央寄せになっているが、おおよそ正しく想定通りに表示されている。

Webブラウザが数式、数字情報を解釈できるようになった時代。我々がリッチな情報を伝えるWebページを開いた時、Webブラウザはどこまで解釈して、どのような提案をしてくれるようになるだろう。とても楽しみだ。

Google Chrome開発版でsocket APIを試す

Google Chromeの開発版で、拡張機能APIにソケット通信が追加されたそうだ。ネットワークプログラミングマニアとして、早速ドキュメントを読んでみた。

しかし、参考ページにあるサンプルコード通りには動かなかったので、ドキュメントとエラーメッセージを見ながら実際に動くものを作ってみた。ちなみに、experimental.socket.htmlはドキュメントのどこからもリンクが張られていないので、まだ本当に実験段階の「使われるレベルにない」APIのようだ。

動作環境としてGoogle Chrome 21.0.1137.1 canary (Windows/Mac)を用いた。

まず、拡張機能でexperiment APIを使えるように、chrome://flagsの設定を変更し、再起動する。

次に、manifest.json"permissions": ["experimental"]を記述した拡張機能を作り、Chrome拡張機能管理画面から読み込む。

拡張機能のソースに同梱したtcpserver.pyudpserver.pyは単純なTCP/UDPソケットサーバの実装になっている。あらかじめ、Google Chromeが起動しているマシンで動かしておく。

拡張機能管理画面に表示されたこの拡張機能background.htmlのInspect views (Developer tools)を開く。JavaScriptコンソールでexperimental.socket.*を使ったコードを書いてテストする。

ドキュメントにはsocket-typeとして'udp'で無ければならないとあるので、UDP通信のみをテストした ('tcp'を指定することも出来るようだが、通信を確認できなかった) 。

また、chrome.experimental.socket.write(..)の第2引数がstringとあるが、文字列を入れるとエラーになったため、APIのテストコードを参照し、文字列をArrayBufferに変換したデータを渡している。


// JavaScriptコンソール上で

> udp_socket_writer("127.0.0.1", 9998, "hello")
undefined
  ▼Object
    bytesWritten: 5

> udp_socket_writer("127.0.0.1", 9998, "こんにちは、そけっと")
undefined
  ▼Object
    bytesWritten: 30
$ python udpserver.py  # ターミナル上でUDPサーバが動作している
('127.0.0.1', 62019) wrote: hello
('127.0.0.1', 51649) wrote: こんにちは、そけっと

UDPサーバにデータが送られていることを確認できた。逆にChrome側をサーバとして動作させることも出来るらしい。Webブラウザ同士のP2P通信実装も夢ではない。

TCP通信が出来るようになったら、IRCクライアントを書きたい。

Google Chrome拡張機能: Amazon to Yahoo! Bookstore

解説

Google Chrome拡張機能Amazon to Yahoo! Bookstore」は、Amazonの書籍情報にYahoo!ブックストアへの検索リンクを追加する拡張機能です。

現在、Amazonのページ上における以下の箇所に、Yahoo!ブックストア検索リンクを表示します。

  • 書籍検索結果リスト/書籍ランキングリスト
    • 表示されている各書籍タイトル横
  • 書籍詳細ページ
    • 表示されている書籍タイトル、著者横
    • 「この商品を買った人はこんな商品も買っています」リストに表示されている書籍タイトル、著者横

背景

昨日からYahoo!ブックストアを使い始め、

というよく理解出来ないカテゴリが並び(少年(少女)コミックと青年(女性)コミックの違いは何だ? BLはまだしもロマンスだけなぜ別?)、紙書籍基準で言えば旧作新作が入り交じって並ぶ「新着作品」リストがあり、検索の絞り込みは値段と出版社しかなく著者絞り込みは出来ない、という、本屋で本を買ったことがない人が作ったとしか思えないような中身にうんざりした。

改めて、本屋で僕がどうやって本を選んでいるのかということを考えると、本屋の棚は出版社順に並んでいるものの、各出版社棚では著者五十音順だし、背表紙が並んだ棚というのは一見した時の情報量が多いので、すぐに目的の本を発見できる。あるいは、目的を持たず本屋に訪れていると、新刊コーナや雑誌コーナに行き、新しい本との出会いを求めることもある。

角川書店が提供するBOOKWALKERは「作家一覧」というリストを提供しているし、メディアミックスされて今人気のある作品をすぐ発見できるようになっていたりして、かなり使い易い。Yahoo!ブックストアは11月に出来たばかりのようなので、これから改善するかもしれないが、利用者としては待っていられない。妙なガラパゴスちっくの独自フォーマットではなくEPUB(DRM付き)を提供し、複数の電子書籍提供サービスをまとめて利用できるYahoo!ブックストアを積極的に使っていこうと思うのに、読みたい本を見つけられなければ意味が無い。

さて、おそらく僕のWeb上の行動をほとんど知っているのはGoogleだと思うが、僕の本の趣味嗜好を知っているのはAmazonだと思う。Amazonほしい物リストは結構詰まっているし、トップページに行けば、あえて買っていなかったり今まで知らなかったが確かに欲しいと思える本が並んでいる。「持っています」をクリックすれば余計なものは表示されず、割と精度がいいオススメ本が現れている。この仕組みを、この趣味嗜好データを、Yahoo!ブックストアで利用したいと思った。

そこで、AmazonYahoo!ブックストアを繋ぐGoogle Chrome拡張機能を作った。僕はMacを使っているので、Amazonで書籍データを参照 → Yahoo!ブックストア検索リンクボタンクリック → (電子書籍があれば)そのYahoo!ブックストアページをChrome to PhoneAndroid端末に転送 → Android端末で書籍データをダウンロード/閲覧 という流れになっている。

実際にAmazonでオススメされている本から辿り、時には検索し、良さそうな本を探し、Yahoo!ブックストアを参照していった。そうして分かったことは、やはり、まだまだ電子書籍化された本は、非常に少ないということだった。星新一で検索すると1件、筒井康隆は0件、ライトノベル講談社ノベルスも無いし、漫画も中途半端な巻までしか出ていない。買って読んでみると、活字本なのに画像ファイル形式であったりして、電子書籍のメリットがほとんど見えないものも多かった。こればかりは、こちらからどうこう出来る問題ではない。ただ、出版社に、電子書籍に対する正しい理解が広まり、積極的な展開が行われるのを待つしかない。

Google Chromeを操作するAppleScriptの中にJavaScriptを書く

前にGoogle ChromeをAppleScriptで操作する - DiaryExceptionという記事を書いたが、1つ見落としていたことがあった。
AppleScriptの中にJavaScriptを書いて、表示中のWebページを加工することが出来る。
Safariを操作するAppleScriptの中にはdo JavaScriptという命令を書くことが出来、この命令でSafariに表示されているWebページをJavaScriptで加工することが出来る。Google Chromeだとこの命令はexecute javascriptという名前で実装されている。


例として、bitbucketのページを開き、ログインをするという動作をAppleScriptで記述した。

bookmarkletでも同様のことは出来るが、AppleScriptで記述することの強みとして、Webブラウザの外側の様々なアプリケーションと連携することが出来る、という点がある。

Google Chromeのデフォルト検索をgoogle.comにする

google.co.jp検索は日本語Webページが優先されてしまう。一部の分野では英語によって記述されたものが一次資料となる場合が多々あり、日本語による記述を優先していると発見が遅れてしまう。また、Google検索の新機能はgoogle.comでまず提供され、google.co.jpでの新機能提供は(ローカライズなどを経て)後回しとなることが多く、google.comに比べ機能的に劣る面がある。

Google Chromeのアドレスバーを用いた検索は、日本語環境だとgoogle.co.jpによる検索になる。これを変更するには「環境設定」-「Default search」の[管理]ボタンを押す。「検索エンジン」ウィンドウ左下の[+]を押して、google.com検索を追加する。

名前とキーワードは任意(僕は名前をGoogle.com、キーワードをgcにした)。URLは以下のものを入力する。

http://www.google.com/search?{google:RLZ}{google:acceptedSuggestion}{google:originalQueryForSuggestion}sourceid=chrome&ie={inputEncoding}&q=%s

[OK]ボタンを押し、「検索エンジン」ウィンドウに戻る。
検索エンジンリストから、今追加したgoogle.com検索を選択し[既定にする]ボタンを押す。
ついでに、これまでデフォルトで使っていたgoogle.co.jp検索(名前はGoogle)にキーワードを追加する(僕はgjにした)。

これで、アドレスバーを用いた検索はデフォルトでgoogle.comが用いられる。google.co.jp検索をする場合は、アドレスバーに先ほど設定したgoogle.co.jp検索のキーワード(gj)を入力し、Tabを押してから検索語を入力する。

参考に、google.co.jpとgoogle.comで「javascript」という検索語を用いて検索した結果を掲載する。


google.co.jpを用いた検索結果にはWikipediaのページが2つも含まれており、好ましくない結果となっていることが分かる。


余談。
google.com検索のURLを以下のようにすると、reading level統計/フィルタ機能が使えるようになる。

http://www.google.com/search?{google:RLZ}{google:acceptedSuggestion}{google:originalQueryForSuggestion}sourceid=chrome&ie={inputEncoding}&tbs=rl:1&q=%s

Google ChromeをAppleScriptで操作する

Google Chromeはver.7以降でAppleScriptに対応している。正確に書くと、Google ChromeのプロセスにAppleEventを送ることが出来るようになった。
AppleScriptAppleScriptエディタを使って書いて実行するか、エディタで書いてosascriptコマンドで実行することが出来る。
つまり、他のアプリケーションやshellからGoogle Chromeを操作する、Google Chromeの情報を取得することが可能になった。
例えば、Google Chrome内で選択したテキストをそのままEmacsのバッファに挿入したい場合、(AppleScriptを使わなければ、Google Chromeでテキストを選択→Command-c→Emacsに移動→(kill-ringとpasteboardが連動していることを前提にして)C-yとする必要があったが)
Google Chromeでテキストを選択して、

Emacs

C-u M-! osascript -e 'tell application "Google Chrome" to get copy selection of active tab of window 1' ; pbpaste


で出来るようになった。

ちなみに、vimで同じことをするには、

:r! osascript -e 'tell application "Google Chrome" to get copy selection of active tab of window 1' ; pbpaste

選択テキストをEmacsのバッファにそのまま挿入するのではなく、加工して挿入したい場合、例えば、前に作ったGoogle翻訳コマンドラインツールを使えば、

C-u M-! osascript -e 'tell application "Google Chrome" to get copy selection of active tab of window 1' ; pbpaste | transrator

で選択したテキストの翻訳文章が挿入される。



AppleScriptからGoogle Chromeに対して何が出来るかは、AppleScriptエディタにGoogle ChromeD&Dすれば(もしくはAppleScriptエディタの[ファイル]-[用語説明を開く]からGoogle Chromeを選択すれば)、コマンド一覧と説明が見える。

幾つかのコマンドをAppleScriptで使ってみる。

-- Google Chromeの1つ目のウィンドウのプロパティを取得
tell application "Google Chrome"
 get properties of window 1
end tell
------------------------------
-- 結果
{zoomed:true, miniaturized:false, name:"\"applescript\" commands - Google 検索", active tab:tab id 132 of window id 4 of application "Google Chrome", mode:"normal", miniaturizable:true, class:window, closeable:true, resizable:true, visible:true, zoomable:true, id:4, bounds:{50, 22, 1213, 796}, index:1, active tab index:13}
-- 1つ目のウィンドウの5つ目のタブのプロパティを取得
tell application "Google Chrome"
 get properties of tab 5 of window 1
end tell
------------------------------
-- 結果
{name:"dojo/regexp - DojoCampus - Docs", URL:"http://docs.dojocampus.org/dojo/regexp", id:13, class:tab, loading:false}
-- タブに対するコマンドの一部
tell application "Google Chrome"
 view source of active tab of window 1  -- ソースを表示
 save active tab of window 1  -- 保存
 print active tab of window 1  -- 印刷
 reload active tab of window 1  -- 再読み込み
 go back active tab of window 1  -- 戻る
 go forward active tab of window 1  -- 進む
 copy selection of active tab of window 1  -- 選択したテキストをpasteboardへコピー
 paste selection active tab of window 1  -- pasteboard内のテキストをタブに(テキストエリアにフォーカスがある場合はそのエリアに)貼りつけ
end tell

コマンドを組み合わせることで、Google Chromeの操作を自動化出来る。

tell application "Google Chrome"
 set aWin to make new window with properties {mode:"incognito"}
 -- シークレットモードでウィンドウを作る; 通常モードは {mode:"normal"}
 tell aWin
  set newTab to make new tab with properties {URL:"http://www.facebook.com/"}
  -- Facebookを新しいタブで開く
  tell active tab
   repeat  -- ページのロードを待つためのループ
    set curStat to loading
    if curStat = false then exit repeat
    delay 0.1
   end repeat
  end tell
 end tell
end tell

AppleScriptのコードはアプリケーションとして保存出来るので、作ったアプリケーションのアイコンをダブルクリックしてGoogle Chromeに自動作業をさせる、ということも出来る。

追記: Google Chromeを操作するAppleScriptの中にJavaScriptを書く - DiaryException