Amazon Pollyでなろう小説を聞く

この記事は[CPS Lab Advent Calendar 2016](http://www.adventar.org/calendars/1536)の18日目の記事です。

---

小説を読んでると続きが気になって、運転中や食事中に続きを読みたくなることってありますよね。
また、朝の通勤、通学電車で読みたいが満員電車で本やKindle、スマホなんて手に持つなんてできない、ということはあると思います。

そんなあなた、[Amazon Polly](https://aws.amazon.com/jp/polly/)を使うととても簡単になろう小説を音声に変換して聞く事が可能になりました。

しかし、Pollyは以下の[制限](https://docs.aws.amazon.com/ja_jp/polly/latest/dg/limits.html)があります。

> 入力テキストのサイズは、最大 1500 課金対象文字 (合計 3000 文字) です。SSML タグは、課金対象文字としてカウントされません。
入力テキストに適用する最大 5 個のレキシコンを指定できます。
出力オーディオストリーム (合成) は 5 分に制限されます。その時間を過ぎると、残りの音声は切り取られます

なのでなろう小説の1話ごとを音声に変換しようと思ったのですが、多分制限に引っかかり、ちょっと1500課金対象文字がわからなかったので、1000文字ごとに分割して変換し、最後に1つのMP3に合成して保存するプログラムを作りました。

# AWS CLI
とりあえずPollyの準備をしましょう。今回はPythonでやるんですが、AWS CLIの準備が必要なのでやっていきます。と言っても簡単で、ここを見てもらえればだいたいわかりますがざっくりと説明しましょう。

まずインストールします。Pythonでやるのでpipでいれます。

```
pip install awscli
```

MaxOS El Capitanのひとは

```
pip install awscli --ignore-installed six
```

これだけです。
そしたらインストールできたと思うので、アクセスキーなどの設定をします。

```
$ aws configure
AWS Access Key ID []: your access key
AWS Secret Access Key []: your secret key
Default region name [us-east-1]: us-east-1
Default output format [None]:
```

これでOKです。regionはPollyが使えるregionならなんでもいんですが、わからなかったら `us-east-1` で良いと思います。

# AWS SDK for Python (Boto3)
pythonで使うためには[Boto3](https://github.com/boto/boto3)というSDKが用意されてるのでそれを使います。

```
pip install boto3
```

以上です。

そしたら、[ここ](http://docs.aws.amazon.com/polly/latest/dg/get-started-what-next.html)にあったサンプルそのままですが、以下のコードを実行すると変換して、デフォルトのmp3プレイヤーが起動して喋り始めます。

```Python
"""Getting Started Example for Python 2.7+/3.3+"""
from boto3 import Session
from botocore.exceptions import BotoCoreError, ClientError
from contextlib import closing
import os
import sys
import subprocess
from tempfile import gettempdir

# Create a client using the credentials and region defined in the [adminuser]
# section of the AWS credentials file (~/.aws/credentials).
session = Session(profile_name="adminuser")
polly = session.client("polly")

try:
    # Request speech synthesis
    response = polly.synthesize_speech(Text="Hello world!", OutputFormat="mp3",
                                        VoiceId="Joanna")
except (BotoCoreError, ClientError) as error:
    # The service returned an error, exit gracefully
    print(error)
    sys.exit(-1)

# Access the audio stream from the response
if "AudioStream" in response:
    # Note: Closing the stream is important as the service throttles on the
    # number of parallel connections. Here we are using contextlib.closing to
    # ensure the close method of the stream object will be called automatically
    # at the end of the with statement's scope.
    with closing(response["AudioStream"]) as stream:
        output = os.path.join(gettempdir(), "speech.mp3")

        try:
            # Open a file for writing the output as a binary stream
            with open(output, "wb") as file:
                file.write(stream.read())
        except IOError as error:
            # Could not write to file, exit gracefully
            print(error)
            sys.exit(-1)

else:
    # The response didn't contain audio data, exit gracefully
    print("Could not stream audio")
    sys.exit(-1)

# Play the audio using the platform's default player
if sys.platform == "win32":
    os.startfile(output)
else:
    # the following works on Mac and Linux. (Darwin = mac, xdg-open = linux).
    opener = "open" if sys.platform == "darwin" else "xdg-open"
subprocess.call([opener, output])
```

# VoiceId
声じたいは結構種類がありますが、日本語は `Mizuki` ひとつしかないのでこれを使います。

# 分割して変換

ここまできたらあとは簡単です。
まず、なろう小説をローカルに保存します。
そしたら保存されているテキストファイルを読み込んで1000文字ずつに分割します。

```Python
def split_str(s, n):
    length = len(s)
    return [s[i:i + n] for i in range(0, length, n)]

f = open(filename)
text = f.read()
f.close()
texts = split_str(text, 1000)
```

また、先程のPollyに投げる処理をてきとうに関数にしといて、戻り値に `stream.read()` を返すようにしたとします。
そしたらあとは、それをMP3に書き込むだけです

```Python
out = open('output.mp3', 'wb')
for item in texts:
    stream = request_speech(item)
    out.write(stream)
out.close()    
```

これで完成です。
まぁ更に詳しいことは[公式リファレンス](https://docs.aws.amazon.com/ja_jp/polly/latest/dg/what-is.html)見てもらえるとだいたい書いてあるのでおねがいします。
日本語になってるしね。

# まとめ
実際に聞いてみた感じですが、以外と頭に入ってきます。
しかし結構集中して聞いてないと全く流れがつかめないので、作業中とかに聞くのは多分無理だと思います。
また、すべて同じ音声で同じトーンで喋るので会話とか結構違和感あります。
まぁ予想どおりですよね。会話の前に、男性か女性の声を選べるようになるとかなり違うと思います。
また、レキシコンで単語のカスタマイズできるのですが、日本語には対応していないみたいです。解散！

以上、ソースコードはGithubにあげてあります。
[nshiba/TextToSpeechFromPolly](https://github.com/nshiba/TextToSpeechFromPolly)