COTOHA音声合成を中の人が軽く解説

#COTOHA音声合成を中の人が軽く解説
2019年10月31日にCOTOHA APIのラインナップの一つとして<font color="Tomato">**音声合成**</font>をリリースしました！！

そこで本記事では、音声認識の解説に引き続き実際に<font color="Tomato">音声合成の中の人</font>がpythonで音声合成APIを実行する方法を簡単に説明していきます！！

また、COTOHA API ポータルにて[デモサイト](https://api.ce-cotoha.com/contents/demo/speech-synthesis.html)を掲載しておりますので、実際に遊んでみてください！！

# 今回はテキストからの <font color="Tomato">音声合成</font> ！！

これまでのCOTOHA君はテキストと音声を理解できましたが

音声合成がリリースされたことでCOTOHA君がなんと！！<font color="Tomato">**しゃべれる**</font>ようになりました！！！

出力形式は

・一括音声合成
・逐次音声合成

の２パターンを提供しており、ユースケースに応じて利用可能です。

<font color="DodgerBlue">For Enterprise</font>版の提供のみとなっていますが，<font color="Tomato">3ヶ月のお試し期間</font>があるためそちらを使ってお試しいただければと思います！！

![音声合成の仕組み.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/467283/baeffcd4-1fae-f2d1-621c-e67740d7f103.png)

#音声合成APIってどうなってるの？？
<font color="Tomato">音声合成</font> → テキストを入れたら音声が返ってくるっていうのは分かりました。

では，実際にテキストからどのように音声を生成しているのかをさらっと解説します。

##音声合成APIの構成イメージ図
![音声合成API構成図.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/467283/907481f2-33d8-f90f-780f-a04fa148064d.png)

COTOHA 音声合成APIは上記のような構成をしています。

１．テキスト解析
> 入力されたテキストを解析し、読みやアクセント位置などを決定する部分。
> これを行うことで、正しい読み方やアクセントを実現しています。

２．音声信号生成
> テキスト解析結果をもとに声の高さやスピードなどを決定し、音声信号を生成する部分。
> ここで、流暢で人間らしい肉声感のある音声を実現しています。


#音声合成APIの主な特長
##16種類の豊富な話者！

![16話者.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/467283/99dfd408-1e6c-d6e5-4c48-765d445ec207.png)

<font color="Tomato">16種類の話者</font>を提供しており、ユースケースにあった話者を見つけられます。

詳細は[コチラ](https://api.ce-cotoha.com/contents/reference/apireference.html#speechSynthesis_io_speaker)をご確認ください！

##文脈に応じて読み分けられる！

日本語は１つの表記で複数の読み方を持つ言葉があり、文脈等によって読み方が変わることがあります。

ただ、そういった言葉を以下の例文のように読み分けることができます！


|表記|例文1|例文2|
|---|---|---|
|辛い|スパイスが入っていて、このカレーは辛い（カライ）|この日までに資料を提出するのは辛い（ツライ）|
|羽生|対局室に羽生（ハブ）九段がいます|スケートリンクに羽生（ハニュウ）選手がいます|
|空いている|お腹が空いている（スイテイル）|あそこの席が空いている（アイテイル）


##音声の読み上げ方を調整可能！
生成される音声はユースケースに応じて、<font color="Tomato">話す速さ</font>、<font color="Tomato">音量</font>、<font color="Tomato">声の高さ</font>、<font color="Tomato">抑揚</font>を自在に調整することができます。

また、ここでは詳しく書きませんが、デフォルトでは正しく読み上げない単語や文章も<font color="Tomato">単語辞書API</font>や<font color="Tomato">文辞書API</font>を利用することで正しく読み上げるようになります。

#音声合成APIの使い方解説
それでは，どのように音声合成APIを使えば良いのかを，pythonサンプルコードを使って簡単に解説していきます！
ちなみに、サンプルコードは**Github**に公開しているので、そちらを参考にしてください。

・Githubサンプル
[java](https://github.com/cotoha-api-group/text_to_speech-java-sample)
[Node.js](https://github.com/cotoha-api-group/text_to_speech-nodejs-sample)
[python](https://github.com/cotoha-api-group/text_to_speech-python-sample)
[javascript](https://github.com/cotoha-api-group/text_to_speech-javascript-sample)


・リクエストボディ
<details><summary><font color="DodgerBlue"><font color="DodgerBlue">Source Code | クリックしてください</font></summary>
<div>

``` json
{
    "text": "辛いスパイスを食べるのは辛い",
    "speakerId": "ja_JP-M-S0001-T001-E01-SR0",
    "textType": "normal",
    "speechRate": "1.0",
    "powerRate": "2.0",
    "intonation": 10,
    "pitch": 10
}
```

各パラメータを任意に設定して[jsonファイル]として保存してください。

|キー名|説明|特記|
|---|---|---|
|text|読ませたい文章|textTypeがnormalなら500文字まで,utteranceなら1200文字まで|
|speakerId|話者|[話者ID](https://api.ce-cotoha.com/contents/reference/apireference.html#speechSynthesis_io_speaker)から好みの話者を選択|
|textType|テキストの種別|対象テキストの種別を指定 (normal もしくは utterance)|
|speechRate|音声の話速|値域：0.50~2.00　刻み幅：0.01　値が大きいほど速い|
|powerRate|音声の音量|値域：0.00~5.00　刻み幅：0.01　値が大きいほど大きい|
|intonation|音声の抑揚|値域：1~20　刻み幅：1　値が大きいほど大きい|
|pitch|音声の高低|値域：1~20　刻み幅：1　値が大きいほど高い|

</div></details>


・アクセストークン取得＆一括音声合成
<details><summary><font color="DodgerBlue"><font color="DodgerBlue">Source Code | クリックしてください</font></summary>
<div>

``` python
#coding:utf-8

import sys
import json
import urllib.request

# 認証情報定義
clientId = 'xxxxxxxxxxxxxxxxxxxxxxxx'
clientSecret = 'yyyyyyyyyyyy'

# 接続先定義
oauthUrl = 'https://api.ce-cotoha.com/v1/oauth/accesstokens'
ttsUrl = 'https://api.ce-cotoha.com/api/tts/v1/tts'

# main の処理
# コマンドライン引数1 : 音声合成設定を記入したjsonファイル
# コマンドライン引数2(option) : 出力wavファイル名
# 出力 : 合成音声wavファイル
def main():
    accessToken = getToken(oauthUrl, clientId, clientSecret)
    argv = sys.argv
    if len(argv) <= 1:
        print('usage: python sample_py [input_json_file] [(option)output_wav_file]')
        exit(1)
    inputFilePath = argv[1]
    if len(argv) > 2:
        outputFilePath = argv[2]
    else:
        outputFilePath = 'output.wav'
    postData = inputFromFile(inputFilePath)
    audioData = postAndRecieve(ttsUrl, accessToken, postData)
    outputToFile(audioData,outputFilePath)
    
# アクセストークン取得
def getToken(postUrl, cId, cSecret):
    data = {}
    data['grantType'] = 'client_credentials'
    data['c![undefined]()
lientId'] = cId
    data['clientSecret'] = cSecret
    jsonData = json.dumps(data).encode('utf-8')
    
    http = urllib.request.Request(postUrl)
    http.add_header('Content-Type', 'application/json; charset=UTF-8')
    http.add_header('Content-Length', len(jsonData))

    try:
        with urllib.request.urlopen(http, jsonData) as response:
            responseData = response.read()
            responseJson = json.loads(responseData.decode('utf-8'))
            print('getToken completed successfully.')
            return responseJson['access_token']
    except urllib.error.HTTPError as err:
        errData = err.read()
        errJson = json.loads(errData.decode('utf-8'))
        print('[ERROR!(@getToken)] status: ' + str(errJson['status']) + ', message: ' + errJson['message'])
        exit(1)

# JSON ファイルの読み込み
def inputFromFile(inputFilePath):
    jsonFile = open(inputFilePath, 'r',encoding='utf-8')
    jsonData = json.load(jsonFile)
    print('inputFromFile completed successfully.')
    print('post data: ' + str(jsonData))
    return json.dumps(jsonData).encode('utf-8')

# データをポストし合成音声を取得
def postAndRecieve(postUrl, token, postData):
    http = urllib.request.Request(postUrl)
    http.add_header('Authorization', 'Bearer '+token)
    http.add_header('Accept', 'Audio/wav')
    http.add_header('Content-Length', len(postData))
    http.add_header('Content-Type', 'application/json; charset=UTF-8')

    try:
        with urllib.request.urlopen(http, postData) as response:
            responseData = response.read()
            print('postAndRecieve completed successfully.')
            return responseData
    except urllib.error.HTTPError as err:
        errData = err.read()
        errJson = json.loads(errData.decode('utf-8'))
        print('[ERROR!(@post_and_recieve)] status: ' + str(err.code) + ', code: ' + errJson['code'] + ', detail: ' + errJson['detail'])
        exit(1)

# 音声ファイルを出力
def outputToFile(data, outputFilePath):
    outputFile = open(outputFilePath, 'wb')
    outputFile.write(data)
    outputFile.close
    print('outputToFile completed successfully.')
    print(outputFilePath + ' has been generated.')

main() 
  
```

上記を[pythonファイル]として保存してください。
[pythonファイル]と[jsonファイル]は同じフォルダに置いて、ターミナルで
python [pythonファイル] [jsonファイル] [(任意）音声ファイルを保存するときの名前]を実行してください。
[(任意）音声ファイルを保存するときの名前]がない場合はoutput.wavとして、実行したディレクトリに保存されます


</div></details>



・返ってきた音声ファイルをすぐに再生したいときは・・・
<details><summary><font color="DodgerBlue"><font color="DodgerBlue">Source Code | クリックしてください</font></summary>
<div>

``` python
#coding:utf-8

#2つのライブラリを追加
from pydub import AudioSegment
from pydub.playback import play

def main():
    ・・・
    ・・・
    Audio_playback(outputFilePath) #main() の一番下に追加


# 音声ファイルの再生　適当な場所に追加
def Audio_playback(outputFilePath):   
    sound = AudioSegment.from_file(outputFilePath, "wav")
    print('Audio playback now.')
    play(sound)
    print('Finish.')
```
[pythonファイル]にライブラリの追加とmain()関数に1行追加し、適当な場所に音声ファイル再生の関数を追記してください。音声ファイルの保存後、自動で音声再生されます。

</div></details>

#まとめ
COTOHA音声合成の紹介でした。
音声合成は色んな文章を色んな声で実際に人が話しているように聞こえるので、面白いですね！

最後に、たくさんの文字を一括音声合成で処理すると返ってくるまで時間がかかることがあるので、
そんなときは逐次音声合成を試してみてください。