Amazon Pollyを使って耳で本の推敲をする

自分のブログ「[Real World HTTPが出版されます](http://blog.shibu.jp/article/179769139.html)」で紹介したAmazon Pollyを使って本の推敲をする、というやつを軽く紹介します。

ささっと2-3日で作ったSphinx拡張を使っています。本エントリーではツールの使い方も紹介はしますが、Sphinx以外のツール向けに似たような変換機能を作りたい人の参考のために、ツールが何をしているかも紹介します。今まで使う機会もなかったので実は初AWSでした。

リポジトリはこれです。BSDライセンスです。

* https://github.com/shibukawa/sphinxcontrib-ssmlbuilder

SSMLというのは、Amazon Pollyの入力に使う音声合成用のデータフォーマットのことです。W3Cの規格になっており、PanasonicとかTOSHIBAの人も規格制定に加わっています。Amazon Pollyはこのうちのいくつかのサブセットに対応しています。

できあがりはこんな感じです。

* [Real World HTTPのまえがき(11MB/27分/MP3)](https://drive.google.com/file/d/0B8X4zWf2QEfqSVhsWUhRVWxjcWc/view?usp=sharing)

インストール
===============

Sphinxがインストールされているとして次のコマンドでインストールします。

```shell-session
$ pip install sphinxcontrib-ssmlbuilder
```

AWSアクセスのライブラリのboto3と、AWSを使うコマンドラインツールのawscliもインストールされていなければ入ります。あと、ffmpegを使うのでインストールしておきます。

```shell-session
# MacPortsの場合
$ sudo port install ffmpeg
```

AWSのコンソールのIAMでPollyを使う用のアカウントを作ります。僕はpollyという名前にしました。Amazon Pollyが使えるリージョンを選んで（僕はUS-West-Oregon)、Amazon Pollyのアクセス権（Read Onlyでよい)を付けます。ここでいう書き込みというのはオリジナルの発音設定などをアップロードするためのものですが、日本語はサポートしていないようですし、読み込みで十分です。

<img width="879" alt="スクリーンショット 2017-05-25 23.57.04.png" src="https://qiita-image-store.s3.amazonaws.com/0/6105/0983a5e8-9e61-acd9-3a60-01d329f59f59.png">

できたら、aws congiureコマンドで、このアカウントのアクセストークン的なものをローカルに作っておきます。このときの名前をSphinxで使います。

sphinx-quickstartコマンドでドキュメントのプロジェクトフォルダを作ったら設定ファイル(conf.py)に次のssmlbuilder用の設定を追加します。

```py
extensions = ['sphinxcontrib.ssmlbuilder']
# you should register the following user name
# via aws configure.
ssml_polly_aws_profile = "polly"      # AWSのプロファイル
ssml_polly_apply_docnames = "*"       # どのソースファイルを変換するか

# for Japanese
ssml_language = 'ja-JP'               # 言語
ssml_polly_aws_voiceid = 'Mizuki'     # 使いたい音声（現在日本語はMizukiのみ）
```

どのソースファイルを変換するかはここでは全部を指定していますが、実際にはMakefileの中で、書き上がったファイルをオプションで渡せるようにしていました。

```makefile
TARGET        = ""

.PHONY: ssml
ssml:
	$(SPHINXBUILD) -b ssml $(ALLSPHINXOPTS) $(BUILDDIR)/ssml -D ssml_polly_apply_docnames=$(TARGET)
	@echo
	@echo "Build finished. The SSML files are in $(BUILDDIR)/ssml."
```

こんな感じで実行します。

```shell-session
$ make ssml TARGET=preface
sphinx-build -b ssml -d _build/doctrees   . _build/ssml -D ssml_polly_apply_docnames=preface
Running Sphinx v1.6
loading translations [ja]... done
loading pickled environment... done
building [mo]: targets for 0 po files that are out of date
building [ssml]: targets for 0 source files that are out of date
updating environment: 0 added, 0 changed, 0 removed
looking for now-outdated files... none found
no targets are out of date.
build succeeded.
ssml_polly_aws_profile:  polly
ssml_polly_apply_docnames:  preface
audio output folder:  polly
ssml_polly_aws_voiceid:  Mizuki
Found credentials in shared credentials file: ~/.aws/credentials
polly.synthesize_speech for preface-1.ssml (1/18)
polly.synthesize_speech for preface.1-1.ssml (2/18)
polly.synthesize_speech for preface.4-1.ssml (3/18)
polly.synthesize_speech for preface.3-1.ssml (4/18)
:
polly.synthesize_speech for preface.5-1.ssml (18/18)
concatinating MP3 fragments: preface.mp3 (1/1)
:
Build finished. The SSML files are in _build/ssml.
```

推敲の成果
==============

Mac上でも、Android上でも、再生速度が細かく設定できるVLCを使って聞きました。どちらも、間違いを見つけたら一時停止しつつGoogle Keepにメモするようにしてコメントを同期していました。通勤は歩く距離が長く、かつ、歩道が整備されているので、歩きながら聞いたりもしました。自転車に乗りながらはダメですよ。

ブログにも書きましたが、細かい助詞の違いとかは目で見るよりも、耳で聞くほうが間違いが分かりやすいです。それ以外にも、同じ内容を繰り返していたりとか、定義を紹介せずに説明を始めてしまっているところ、何を言いたいのかよくわからない場所とかもよくわかります。

ただし、速度を上げると、時間が節約できる代わりに間違いに気づきにくくなります。速聴の練習をしているのであれば、多少速めても問題はないでしょう。僕は聞くだけなら3.5倍ぐらいで聞く練習をしたのですが、推敲する場合はゆっくり目でないと役に立ちません。僕の場合は次のような感じでした。

* 1.2倍: 助詞の違いにも気づける。中身の間違いにも気づける
* 1.5倍: 中身の違いには気付けるが、助詞の違いは気づけ無い
* 2倍: 間違いに気づけない。

章の内容が書き上がったら、耳で聞きながら推敲するのを2回ぐらい回してからレビューに投げる感じでした。見直しをするときは、少し時間を置いてからとか「新鮮な視点で」やることが大事だとよく言われますが、耳でレビューはまったく別のヒューマンインタフェースを使うので、そういう「ちょっと置いて」とかもあまり考えずにガンガンやって大丈夫です。

あとは、強制的に進むので、時間の見通しが付けやすいのと、集中力がきれて別のことを調べ始めたりとかそういうことが起きにくい点も良かったです。ルールファイルを作ったりとかそういうのも必要ないですしね。

内部の仕組み
=================

処理のフロー
----------------

Pollyの入力では1500文字（SSMLの制御文字を除く）しか受け取れませんので、分割してこの分量で出すようにして個別に音声化して最後にマージします。

初使用から一年間は無料枠がかなりあり、本を何度もエクスポートしても無料枠で収まってしまいましたが、そのうち無料枠がなくなることを考えればサーバーアクセスは最低限にしたいところですよね。もちろん、金額だけじゃなくて、余計な仕事を投げなければ実行時間も節約できます。個人的には上坂すみれオプションがあれば1万円ぐらい払うのもやぶさかでないのですが、残念ながら提供されていません。

本だと、段落を入れ替えたり、新しい段落を追加・削除するのは日常的に起こります。そのため、段落ごとに分割（ついでに1000文字超えても分割）し、本文のコンテンツのハッシュをキーとして、同じキーを持つmp3がある時はサーバーアクセスをスキップするようにしました。あと、読み上げてもしょうがないコメント、テーブル、ソースコードとかもスキップしています。

必要なMP3ファイルがそろったら、それをffmpegを使って1つのMP3に変換します。

テキスト抽出部分
----------------

よくあるビジターパターンです。Sphinxのビルダーを作ったことがある人ならお馴染み。まあ、長いし楽ではないです。参考となるビルダーをコピーしていじる感じです。装飾となるような情報はほぼすっ飛ばしてテキスト、セクションタイトルぐらいをピックアップするぐらいです。

* https://github.com/shibukawa/sphinxcontrib-ssmlbuilder/blob/master/sphinxcontrib/builder.py

変換部分
-----------

一応次のような感じで詳細な設定もできるようにしていましたが、デフォルトのまま使いました。

```py
    ssml_skip_block = {'comment': True, 'table': True, 'codeblock': True}
    ssml_break_around_section_title = [2000, 1600, 1000, 1000, 1000, 1000]
    ssml_break_after_paragraph = 1000
    ssml_emphasis_section_title = ['none', 'none', 'none', 'none', 'none', 'none']
    ssml_paragraph_speed = 'default'
```

SSMLを使うと、再生速度や強調などを設定できます。しかし、実験してみるとどちらも不自然さが目立ってしまったので、声色に関しては何も設定しないようにしました。普通の人が本を音読する時は、表題とか段落の後は空白の溜めだけで区切っているだけで、章、節などのタイトルで強さを変えているわけではありませんからね。セクションタイトルはそれぞれ深くなるにつれて、2秒、1.6秒、1秒と停止時間を減らすようにしています。あと、段落の後にも1秒追加しています。工夫としてはこれだけです。というか、いろいろ実験したけど削った結果がこれです。

AWSのAPIを叩く部分は連続で叩きすぎないように、少し待つようにしています。Goのtime.TickerのAPIを参考に。

* https://github.com/shibukawa/sphinxcontrib-ssmlbuilder/blob/master/sphinxcontrib/throttling.py

あとはffmpegで段落ごとのMP3を結合しておしまいです。管理しやすいようにSphinxの書籍名、著者名、ファイルのタイトル、通し番号のメタデータを付与します。これでVLCでもきちんと整理できます。