AivisSpeech-engineをVPSで立ち上げてみた(2)

以下の記事で、AivisSpeech-engineの立ち上げまでを実行しました。

AivisSpeech-engineをVPSで立ち上げてみた(1)

チャットボットを作っていると、「もう少し感情表現ができないかな?」と思うことがあります。 また、海外の音声合成サービスはどこか”違和感”を感じることがあります。 …

サンプルコードでもわかるように、音声合成のためには、2回のAPIリクエストが必要になります。
少々使い勝手が悪いので、一度で実行できるようにします。

ついでに、テキストだけでなくSpeaker IDも指定できるようにしたいと思います。

音声合成のコード

前回のサンプルプログラムを修正してFlaskのコードにしました。

AivisSpeech-Engineと同一サーバに配置する予定なので、接続先をlocalhostとしています。

from flask import Flask, request, send_file
from flask_cors import CORS
import io
import json
import soundfile
import requests
from werkzeug.exceptions import BadRequest

app = Flask(__name__)
CORS(app) 

class AivisAdapter:
    def __init__(self, default_speaker: int = 888753760):
        # 同じDocker network内のサービス名で接続
        self.URL = "http://localhost:10101"
        self.default_speaker = default_speaker

    def generate_voice(self, text: str, speaker_id: int = None) -> tuple:
        speaker = speaker_id if speaker_id is not None else self.default_speaker
        
        params = {"text": text, "speaker": speaker}
        query_response = requests.post(f"{self.URL}/audio_query", params=params).json()

        audio_response = requests.post(
            f"{self.URL}/synthesis",
            params={"speaker": speaker},
            headers={"accept": "audio/wav", "Content-Type": "application/json"},
            data=json.dumps(query_response),
        )

        with io.BytesIO(audio_response.content) as audio_stream:
            data, rate = soundfile.read(audio_stream)
            return data, rate

@app.route('/generate', methods=['POST'])
def generate_voice():
    try:
        text = request.form.get('text')
        speaker_id = request.form.get('speaker_id')
        
        if not text:
            raise BadRequest("Text is required")

        speaker_id = int(speaker_id) if speaker_id else None
        
        adapter = AivisAdapter()
        data, rate = adapter.generate_voice(text, speaker_id)
        
        # メモリ上で音声ファイルを作成
        audio_buffer = io.BytesIO()
        soundfile.write(audio_buffer, data, rate, format='WAV')
        audio_buffer.seek(0)
        
        return send_file(
            audio_buffer,
            mimetype='audio/wav',
            as_attachment=True,
            download_name='output.wav'
        )

    except Exception as e:
        return {'error': str(e)}, 400

if __name__ == '__main__':
    app.run(host='0.0.0.0', port=80)


dockerで起動するために、以下のファイルを追加します。

flask
flask-cors
requests
soundfile

FROM python:3.9-slim

WORKDIR /app

# システムの依存関係をインストール
RUN apt-get update && apt-get install -y \
    libsndfile1 \
    && rm -rf /var/lib/apt/lists/*

COPY app/requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt

COPY app/ .

CMD ["python", "main.py"]

全体の構造は、以下のとおりです。

project/
├── app/
│   ├── main.py
│   └── requirements.txt
├── Dockerfile

docker コマンドを使って、flaskアプリを立ち上げます。

sudo docker build -t tts-web-app .
sudo docker run -d --restart unless-stopped --name tts-web --network host tts-web-app

うまく動いているか、サーバ内の端末から以下を叩いて確認します。

curl -X POST -F "text=こんにちは" -F "speaker_id=888753760" http://localhost/generate -o output.wav

音声ファイルが生成されれば完成です。

ちなみに、pythonだと、こんな感じです。Google Colabでも実行できます。

import requests

url = 'http://164.70.118.24/generate'
data = {
    'text': 'なんか面白いこと話してよ',
    'speaker_id': '888753760'
}
files = {}

response = requests.post(url, data=data, files=files)

if response.status_code == 200:
    with open('test.wav', 'wb') as f:
        f.write(response.content)
    print('ファイルを保存しました')
else:
    print('エラー:', response.status_code)