日本語 / English


はじめてのトーク作成

VoiSona Talkをインストールする

ユーザー認証をする

ボイスライブラリを選択する

セリフを入力する

セリフを編集する

パラメータを調整する

ファイルをインポート・エクスポートする

環境設定を変更する

REST API チュートリアル

よくある質問

VoiSona TalkのREST APIとは

REST API(Representational State Transfer Application Programming Interface)とは、アプリケーションの機能を外部プログラムから利用できるようにするための仕組みです。HTTP通信を通じてデータの送受信を行い、音声合成のリクエストや状態確認、結果の取得などを自動化できます。

Voisona TalkのREST APIを有効にすることで、PythonやC++、JavaScriptなど、さまざまなプログラミング言語から音声合成を制御できるようになります。これにより、独自のアプリケーションやWebサービス、チャットボット、ゲームなどにVoisona Talkの合成音声を組み込むことが可能になります。

以降のチュートリアルでは、実際にVoisona TalkのREST APIを有効化し、Pythonを用いて音声合成を実行する手順を紹介します。

※REST API機能は現在ベータ版となっております。


REST APIの有効化

  1. Voisona Talkを起動してください。
  2. 認証を行っていない場合は認証を行ってください。
    1. 画面右上の3本線 → ヘルプ → 認証 から
      1. Mail: 登録したメールアドレス
      2. パスワード: 登録時に設定したパスワード
  3. 少なくとも1つのボイスライブラリがダウンロードされていることを確認してください。
  4. REST APIを有効にしてください。
    1. 画面右上の3本線 → 編集 → 環境設定 のAPIタブにおいて
      1. 待ち受けポート番号を任意に設定してください。デフォルトは32766です。
      2. ユーザー名が認証時のメールアドレスになっていることを確認してください。この項目は変更できません。
      3. API用のパスワードを任意に設定してください。このパスワードは認証用のパスワードである必要はありません。ただし、空文字ではない必要があります。
      4. 「REST APIを有効にする」のチェックボックスをONにしてください
        1. このとき、待ち受けポート番号とパスワードは変更不可になります。これらを変更したい場合は、「REST APIを有効にする」のチェックボックスをOFFにしてください。
      5. 「REST APIを有効にする」のチェックボックスの隣にある「Talk APIリファレンス」へのリンクが有効になっています。APIのより詳細な使用方法を知りたい場合は、こちらを参照してください。

Pythonを用いたAPIの利用例

サンプルコード

サンプルコードの全体を以下に示します。このチュートリアルで、それぞれの内容を順に解説していきます。

import argparse
import json
import os
import sys
import time
import xml.etree.ElementTree as ET
import requests
parser = argparse.ArgumentParser()
parser.add_argument("--user", type=str, required=True, help="User name")
parser.add_argument("--password", type=str, required=True, help="API password")
parser.add_argument("--port", type=int, default=32766, help="Port number")
parser.add_argument("--output-wav", type=str, default="test.wav", help="WAV filename")
args = parser.parse_args()
auth = (args.user, args.password)
base_url = f"<http://localhost>:{args.port}/api/talk/v1/"
def get_voice_libraries():
    response = requests.get(base_url + "voices", auth=auth)
    response.raise_for_status()
    voice_libraries = response.json()["items"]
    print("利用可能なボイスライブラリの一覧は以下です。")
    print(json.dumps(voice_libraries, indent=2, ensure_ascii=False))
    return voice_libraries
def synthesize_text(voice_library):
    payload = {
        "text": "こんにちは",
        "language": voice_library["languages"][0],
        "voice_name": voice_library["voice_name"],
        "voice_version": voice_library["voice_version"],
        "force_enqueue": True,
    }
    response = requests.post(base_url + "speech-syntheses", auth=auth, json=payload)
    response.raise_for_status()
    uuid = response.json()["uuid"]
    print("リクエストの送信が完了しました。")
    return uuid
def check_status(uuid, synth=True, timeout=30):
    start = time.time()
    endpoint = "speech-syntheses" if synth else "text-analyses"
    while True:
        response = requests.get(base_url + endpoint + "/" + uuid, auth=auth)
        response.raise_for_status()
        state = response.json()["state"]
        if state == "succeeded":
            break
        if time.time() - start > timeout:
            raise TimeoutError("処理に時間がかかり過ぎています。")
        time.sleep(0.1)
    print("リクエストの処理が完了しました。")
    return response
def delete_request(uuid):
    response = requests.delete(base_url + "speech-syntheses/" + uuid, auth=auth)
    response.raise_for_status()
    print("リクエストの削除が完了しました。")
def synthesize_text_and_save(voice_library):
    payload = {
        "text": "こんにちは",
        "language": voice_library["languages"][0],
        "voice_name": voice_library["voice_name"],
        "voice_version": voice_library["voice_version"],
        "can_overwrite_file": True,
        "destination": "file",
        "output_file_path": os.path.abspath(args.output_wav),
    }
    response = requests.post(base_url + "speech-syntheses", auth=auth, json=payload)
    response.raise_for_status()
    uuid = response.json()["uuid"]
    print("リクエストの送信が完了しました。")
    return uuid
def synthesize_text_with_global_parameters(voice_library):
    payload = {
        "text": "こんにちは",
        "language": voice_library["languages"][0],
        "voice_name": voice_library["voice_name"],
        "voice_version": voice_library["voice_version"],
        "global_parameters": {
            "alp": 0.0,
            "huskiness": 0.0,
            "intonation": 1.0,
            "pitch": 0.0,
            "speed": 2.0,
            "style_weights": [],
            "volume": 0.0,
        },
    }
    response = requests.post(base_url + "speech-syntheses", auth=auth, json=payload)
    response.raise_for_status()
    uuid = response.json()["uuid"]
    print("リクエストの送信が完了しました。")
    return uuid
def analyze_text():
    payload = {
        "text": "こんにちは",
        "language": "ja_JP",
    }
    response = requests.post(base_url + "text-analyses", auth=auth, json=payload)
    uuid = response.json()["uuid"]
    response = check_status(uuid, synth=False)
    analyzed_text = response.json()["analyzed_text"]
    print("テキストの解析が完了しました。")
    print(analyzed_text)
    return analyzed_text
def synthesize_text_with_analyzed_text(voice_library, analyzed_text):
    root = ET.fromstring(analyzed_text)
    word = root.find(".//word")
    word.set("hl", "hllll")
    word.set("pronunciation", "コンニチハ")
    modified_analyzed_text = ET.tostring(root, encoding="unicode")
    print(modified_analyzed_text)
    payload = {
        "analyzed_text": modified_analyzed_text,
        "language": voice_library["languages"][0],
        "voice_name": voice_library["voice_name"],
        "voice_version": voice_library["voice_version"],
    }
    response = requests.post(base_url + "speech-syntheses", auth=auth, json=payload)
    response.raise_for_status()
    uuid = response.json()["uuid"]
    print("リクエストの送信が完了しました。")
    return uuid
try:
    voice_libraries = get_voice_libraries()
    if len(voice_libraries) == 0:
        print("ボイスライブラリをダウンロードしてください。")
        sys.exit(1)
    voice_library = voice_libraries[0]
    uuid = synthesize_text(voice_library)
    check_status(uuid)
    delete_request(uuid)
    uuid = synthesize_text_and_save(voice_library)
    check_status(uuid)
    time.sleep(2)# 前の音声の再生の終了を待ちます。
    uuid = synthesize_text_with_global_parameters(voice_library)
    check_status(uuid)
    time.sleep(2)# 前の音声の再生の終了を待ちます。
    analyzed_text = analyze_text()
    uuid = synthesize_text_with_analyzed_text(voice_library, analyzed_text)
    check_status(uuid)
    print("チュートリアルをエラーなく完了しました。")
except requests.exceptions.ConnectionError as e:
    print("接続に失敗しました。サーバの状態と設定を見直してください。")
    print(e)
except requests.exceptions.HTTPError as e:
    print("HTTPエラーが発生しました。")
    print(e)
except Exception as e:
    print("予期しないエラーです。")
    print(e)

サンプルコードの実行のためにはrequestsパッケージが必要です。

pip install requests

以下はサンプルコードの実行例です。

python sample.py --user [email protected] --password 1234

ユーザ名とパスワードはAPIサーバの設定に合わせて変更してください。


使用可能なボイスライブラリの取得

ボイスライブラリの一覧は以下のようにして取得することができます。

auth = (args.user, args.password)
base_url = f"<http://localhost>:{args.port}/api/talk/v1/"
def get_voice_libraries():
    response = requests.get(base_url + "voices", auth=auth)
    response.raise_for_status()
    voice_libraries = response.json()["items"]
    print("利用可能なボイスライブラリの一覧は以下です。")
    print(json.dumps(voice_libraries, indent=2, ensure_ascii=False))
    return voice_libraries

以下は出力の例です。

[
  {
    "display_names": [
      {
        "language": "ja_JP",
        "name": "田中傘"
      },
      {
        "language": "en_US",
        "name": "Tanaka San"
      }
    ],
    "languages": [
      "ja_JP"
    ],
    "voice_name": "tanaka-san_ja_JP",
    "voice_version": "2.0.0"
  }
]

エディタ上でボイスライブラリをダウンロードしていない場合は空となります。


音声の合成

以下では、音声合成のリクエストをサーバに送信します。

def synthesize_text(voice_library):
    payload = {
        "text": "こんにちは",
        "language": voice_library["languages"][0],
        "voice_name": voice_library["voice_name"],
        "voice_version": voice_library["voice_version"],
        "force_enqueue": True,
    }
    response = requests.post(base_url + "speech-syntheses", auth=auth, json=payload)
    response.raise_for_status()
    uuid = response.json()["uuid"]
    print("リクエストの送信が完了しました。")
    return uuid

サンプルコードでは、取得したボイスライブラリの中から最初に検出されたものを、合成に使用するボイスライブラリとして指定しています。

音声の合成が完了すると、デフォルトのオーディオデバイスから「こんにちは」という音声が再生されます。

なお、サーバにはリクエストの上限があり、上限に達するとリクエストの処理に失敗します。リクエスト時にforce_enqueue をTrueに設定しておくと、古いリクエストから自動で削除されるようになります。

synthesize_text関数を実行することで得られたUUIDから、送信したリクエストの状態を次のようにして知ることができます。

def check_status(uuid, timeout=30):
    start = time.time()
    while True:
        response = requests.get(base_url + "speech-syntheses/" + uuid, auth=auth)
        response.raise_for_status()
        state = response.json()["state"]
        if state == "succeeded":
            break
        if time.time() - start > timeout:
            raise TimeoutError("処理に時間がかかり過ぎています。")
        time.sleep(0.1)
    print("リクエストの処理が完了しました。")
    return response

レスポンスのstateの内容が queued であればサーバがまだリクエストを処理してない状態で、succeeded ならリクエストの処理が完了していることを表します。

また、UUIDを指定してリクエストの削除を行うことができます。

def delete_request(uuid):
    response = requests.delete(base_url + "speech-syntheses/" + uuid, auth=auth)
    response.raise_for_status()
    print("リクエストの削除が完了しました。")

合成音声を直接オーディオデバイスに送信せず、ファイルに保存することができます。なお、ファイルパスは絶対パスである必要があります。

def synthesize_text_and_save(voice_library):
    payload = {
        "text": "こんにちは",
        "language": voice_library["languages"][0],
        "voice_name": voice_library["voice_name"],
        "voice_version": voice_library["voice_version"],
        "can_overwrite_file": True,
        "destination": "file",
        "output_file_path": os.path.abspath(args.output_wav),
    }
    response = requests.post(base_url + "speech-syntheses", auth=auth, json=payload)
    response.raise_for_status()
    print("リクエストの送信が完了しました。")

音声表現の制御

音声の表現を変化させたい場合は、global_parametersを入力に含めてください。以下は合成音声の話速を2倍にする例です。

def synthesize_text_with_global_parameters(voice_library):
    payload = {
        "text": "こんにちは",
        "language": voice_library["languages"][0],
        "voice_name": voice_library["voice_name"],
        "voice_version": voice_library["voice_version"],
        "global_parameters": {
            "alp": 0.0,
            "huskiness": 0.0,
            "intonation": 1.0,
            "pitch": 0.0,
            "speed": 2.0,
            "style_weights": [],
            "volume": 0.0,
        },
    }
    response = requests.post(base_url + "speech-syntheses", auth=auth, json=payload)
    response.raise_for_status()
    print("リクエストの送信が完了しました。")

発話情報の詳細な制御

音声合成のリクエストの送信時に、メタ情報を付与したテキストを含めることで、発話情報を制御することができます。そのためには、まずテキスト解析のリクエストを送信し、所望のテキストの解析結果を取得します。

def analyze_text():
    payload = {
        "text": "こんにちは",
        "language": "ja_JP",
    }
    response = requests.post(base_url + "text-analyses", auth=auth, json=payload)
    uuid = response.json()["uuid"]
    response = check_status(uuid, synth=False)
    analyzed_text = response.json()["analyzed_text"]
    print("テキストの解析が完了しました。")
    print(analyzed_text)
    return analyzed_text

以下が「こんにちは」を解析した結果になります。

<tsml><acoustic_phrase><word chain="0" hl="lhhhh" original="こんにちは" phoneme="k,o|N|n,i|ch,i|w,a" pos="感動詞" pronunciation="コ
ンニチワ">こんにちは</word></acoustic_phrase></tsml>

この解析結果を修正してサーバに渡すことで、アクセントや発音を変えることができます。以下はXMLパーサを用いた修正の例です。

def synthesize_text_with_analyzed_text(voice_library, analyzed_text):
    root = ET.fromstring(analyzed_text)
    word = root.find(".//word")
    word.set("hl", "hllll")
    word.set("pronunciation", "コンニチハ")
    modified_analyzed_text = ET.tostring(root, encoding="unicode")
    print(modified_analyzed_text)
    payload = {
        "analyzed_text": modified_analyzed_text,
        "language": voice_library["languages"][0],
        "voice_name": voice_library["voice_name"],
        "voice_version": voice_library["voice_version"],
    }
    response = requests.post(base_url + "speech-syntheses", auth=auth, json=payload)
    response.raise_for_status()
    print("リクエストの送信が完了しました。")

その他詳細に関しては、Voisona Talkエディタの環境設定のAPIタブのリンクから「Talk APIリファレンス」を参照してください。