皆さま、おはようございます。
自称「永遠の Clojure Newbie」toku345です。
2018 年も Clojure Advent Calendar に馳せ参じました。
お願い
本記事はぶっちゃけそんなに深い内容ではありません...
各種アドベントカレンダーで胃もたれした際の箸休め程度にお読みいただければ幸いです。
去年は ClojureScript で Alexa スキルを書いてみた という内容で書かせていただきました。
今年は ClojureScript で Google Actions1 作成ネタでも書くか...と考えていたのですが、それだとあまり面白くなさそうなので、ちょっと趣向を変えて Google Home 版 say
コマンドを作ってみました2。
say コマンドと何?
macOS に備わっているコマンドで任意の文字列を音声にして再生してくれるコマンドです。
使い方はこんな感じで、console を開いて
$ say "こんにちは世界"
と入力すると、mac が 「こんにちは世界」としゃべってくれます。
これを mac ではなく、Google Home がしゃべるようトライしてみました。
実現方法の説明
Text -> 音声ファイルに変換して、Goole Home に喋らせるという部分は google-home-notifier という npm を使用すると、あっという間に解決しました 😆
Google Home Notifier の役割り
- ネットワーク上の Google Home Mini 探索
- mDNS を使って同一ネットワーク上を探索して Google Home を見つけます3。
-
text → 音声変換
- Google の Cloud text-to-speech を使って text → 音声ファイルに変換し、音声ファイル URL を取得する
-
Google Home に喋らせる(=再生させる)
- Chromecast cast v2 プロトコル4を使って Google Home にファイル再生させる
google-home-notifier のシンプルな使用例
var googlehome = require("google-home-notifier");
var language = "ja"; // if not set 'us' language will be used
googlehome.ip("192.168.3.11", language);
googlehome.notify("Hey ClojureScript", function (res) {
console.log(res);
});
これを ClojureScript で書いてみると ↓
(require '["google-home-notifier" :as googlehome])
(defonce device-ip "192.168.3.11")
(defonce language "ja")
(.ip googlehome device-ip language)
(.notify googlehome "Hey ClojureScript" (fn [res] (prn res)))
構成図
Let's Coding in ClojureScript!
それでは、say コマンドとして console 上から使えるようにしていきましょう!
今回は ClojureScript -> JavaScript は shadow-cljs を使用しました。
Leiningen & cljsbuild の組み合わせよりもストレス無く使えてよかったです5。
ディレクトリ構成
※ build 時作成されるディレクトリは一部省略しています
.
├── node_modules/
├── package-lock.json
├── package.json
├── shadow-cljs.edn
└── src/
└── main/
└── app/
└── core.cljs
設定ファイルはこんな感じ。
シンプルですね ☺️
{:source-paths
["src/main"]
:dependencies
[]
:builds
{:app {:target :node-script
:output-to "bin/say"
:main app.core/main!
:devtools {:after-load app.core/reload!}}}
npm を使いたい場合は別途 package.json で管理すれば OK なので、すごくお手軽です。
※ google-tts のバージョンのアップに本家が追従してくれないので、今回は私が fork して手を入れたものを使っています。
{
"dependencies": {
"google-home-notifier": "toku345/google-home-notifier#update-google-tts-api"
},
"devDependencies": {
"shadow-cljs": "^2.7.8",
"source-map-support": "^0.5.9",
"ws": "^6.1.2"
},
"scripts": {
"watch": "shadow-cljs watch app",
"test": "shadow-cljs compile test",
"build": "shadow-cljs compile app"
}
}
npm install すれば、ClojureScript から参照できるようになります。
肝心の ClojureScript のコードはこんな感じ。
(ns app.core
(:require ["google-home-notifier" :as googlehome]))
(defonce device-ip "192.168.3.11")
(defonce language "ja")
(defn say [text]
(.ip googlehome device-ip language)
(.notify googlehome text (fn [res] (prn res))))
(defn reload! []
(println "Code updated."))
(defn main! [& args]
(say (first args)))
build
$ npx shadow-cljs release app
$ chmod +x bin/say
これで bin/say スクリプト(実体は node.js スクリプト)が生成され
$ ./bin/say "こんにちはClojureScript"
と実行と、Google Home が「こんにちは ClojureScript」と say してくれます。
デモ
コードはこちら ※ 一部開発のためのコードを含んでいるため ↑ と完全一致していません...
GoogleHomeを喋らせる say コマンドを ClojureScript で書いてみました!https://t.co/lqMUuklSej pic.twitter.com/TXteXg94cF
— Fumitaka Tokumitsu (@toku345) 2018年12月17日
まとめ
- ClojureScript + shadow-cljs を使えば、すごく簡単に npm と連携するコードを書ける
- google-home-notify を使えば簡単に Google Home おしゃべりさせることができる
- ClojureScript 書くのすっごく楽しいよ
-
Alexa で言うところの Skill のこと ↩
-
というのはタテマエで、ホンネは会社の Advent Calendar や会社ブログ、おまけに前々職の Advent Calendar にも召喚されてしまったので、余裕が無かったのです。ごめんなさい... ↩
-
試してみた所、手元の環境では動かなかったので、今回は Google Home の IP アドレスを直接指定して狙い撃ちしています。 ↩
-
Google は Chromecast / Google Home とのやり取りをするプロトコルを公開していないので、有志が解析したものをそう呼んでいるみたい。詳しくは node-castv2を参照してください ↩
-
去年、Alexa Skill を作った際は Leiningen & cljsbuild を使ったのですが、REPL の立ち上がりや build の待ち時間が長く、設定ファイルもごちゃごちゃしていて苦労しました... ↩