2018年1月3日水曜日

DiscordのTTS BotをAITalkで喋らせてみた

先日Discord用のTTS(Text to Speach)ボットをPythonとOpen JTalkで作ってみたんだけど、Docomoが音声合成APIを公開しているっぽいので今度はDocomoの音声合成APIに対応させてみた。

Docomoの音声合成APIは数種類提供されているようだけど、今回はAIのAITalkを使ってみた。VOICEROIDにも使われているエンジンなので聞き馴染みがあるかも。
そして声の種類が11種類なのでユーザーごとに違う声を割り振ることによりゲーム中でも誰が発言しているかがわかる!

今回はDocomoのAPIを利用してインターネット経由で音声合成データを取得するので、docomo Developer supportに登録して、音声合成のAPI_KEYを取得しておく。

なんかDiscordのTTSボット以外にも使えそうだったのでAITalk部分は別ファイルにしてみた。

とりあえずDocomo APIを叩いて音声合成データを取得する部分を作ってみた。
ただしAPIから戻ってくる音声データがリニアPCM,1ch,16000サンプル,16bit(ビッグエンディアン)なのでそのまま再生できない…
ということでWAVEデータに変換する部分も同じPythonプログラム内でやることに。

WAVEデータにするやりかたとしてはビッグエンディアン→リトルエンディアンに変換
PythonのWaveモジュールを使ってWAVEのヘッダを追加
という形にしてみた。Pythonの標準モジュールだけでできたのでよかった。

#coding: utf-8
import array
import subprocess
import requests
import wave
import re
import io

def aitalk(text,speaker):
    API_KEY = 'APIキーを入れる'
    url = "https://api.apigw.smt.docomo.ne.jp/aiTalk/v1/textToSpeech?APIKEY="+API_KEY


    speakerlist = ['nozomi','seiji','akari','anzu','hiroshi','kaho', 'koutarou','maki','nanako','osamu','sumire']
    if 0 <= speaker <= 10:
        print(speakerlist[speaker])
    else:
        speaker = 10

    character = {
    'speaker' : speakerlist[speaker],
    'pitch' : '1',
    'range' : '1',
    'rate' : '1.1',
    'volume' : '2'
    }

    xml = u'<?xml version="1.0" encoding="utf-8" ?>'
    voice = '<voice name="' + character["speaker"] + '">'
    prosody = '<prosody rate="'+ character['rate'] +'" pitch="'+ character['pitch'] +'" range="'+ character['range'] +'">'
    xml += '<speak version="1.1">'+ voice + prosody + text + '</prosody></voice></speak>'
    xml = xml.encode("UTF-8")
    print("Connecting API")
    response = requests.post(
    url,
    data=xml,
    headers={
    'Content-Type': 'application/ssml+xml',
    'Accept' : 'audio/L16',
    'Content-Length' : str(len(xml))
    }
    )

    if response.status_code != 200 :
        print("Error Connecting API : " + response.status_code)
        exit()
    else :
        print("API Connection Success")

    lend = array.array('h',response.content)
    lend.byteswap()
    wav_buffer = io.BytesIO()
    with wave.open(wav_buffer, 'wb') as wavfile:
        wavfile.setparams((1, 2, 16000, 0, 'NONE', 'NONE'))
        wavfile.writeframes(lend)
        wavfile.close()
    return wav_buffer.getvalue()


使い方は上のソースコードをaitalk.pyとして保存して
API_KEYのところに自分で取得したAPIキーを入力して
メインのプログラムにimport aitalkして
wav_buffer = aitalk.aitalk(音声合成したいテキスト,声の種類)
のようにすればwavデータをそのまま渡してくれる。
音声合成したいテキストはDiscordのTTS Botの場合はメッセージをそのまま渡してみたけど問題なく合成されてる感じ。むしろOpen JTalkよりイントネーションも漢字の読み方もいい感じ。声の種類は0から10で指定できる。どの番号がどの声かはソースコード見ればすぐわかるかも。
※Python初心者なので変なところあるかも。

せっかくwavファイル作らないで行けるかなーと思ったのに、結局discord.pyのcreate_ffmpeg_playerではwavデータをそのまま渡すのがよくわからなかったので一旦wavファイルに保存してしまっているという。

とりあえずDiscord TTS Botの方は、
・TeamSpeak3みたいにユーザーが入ってきたら音声でお知らせ
・ユーザーごとに違う声を割り当てて読み上げ
・ggrksとかwktkとか音声合成が読み上げにくいワードは辞書により変換して読み上げ
・AOEみたいに番号で特定の音声を鳴らす
・テキストの流れが早くても順番に音声合成
とかそこそこ使えるレベルになってきた。
AITalkにしたおかげで棒読みちゃんより聞き取りやすいかも。

Pythonで作ってあるのでRaspberry Piで24時間稼働できるんだけどたまーにDiscord鯖との接続が切れたりしているので再接続処理などを入れ無いといけないかも。
いかんせんメインプログラムはいまだにソースコードが汚いので今回はaitalk.py部分の紹介にとどまろうと思う…
動作安定したら公開するかも。(需要ないかな…

0 件のコメント:

コメントを投稿