![images](../img/imgtop_sub_b.png)
音声合成のサーバ〜クライアントを作ってみます。
関連
- ◯
- サーバ証明書を取得〜更新する(ワイルドカード証明書、ネームサーバ経由):Let's Encrypt
検証
- ・
- クラウド:GCP
- ・
- コンテナ:Docker
- ・
- ゲストOS:Ubuntu 22.04
- ・
- ウェブブラウザ:Chrome
- ・
- 画像生成アプリ:ComfyUI
概要
- ◯
- 背景
いまは音声合成のモデルを、ローカル環境で動かすことができます。
- ◯
- 対応
ここでは、音声合成のサーバ〜クライアントの、かんたんな実装を試みます。
- ◯
- 構成
VOICEVOX は、音声合成のエンジンだけを独立して取得できますーーこれを核に、音声合成のサーバ〜クライアントを実装します:[※1]
- ・
- https://github.com/VOICEVOX/voicevox_core
- ※1
- いまローカル環境では、VITS を使う音声合成に活気がありますよね(Bert-VITS2, GPT-SoVITS, ... )。短い時間で、それなりに自然な感情表現をともなう音声合成を学習できるからですがーーただエンジンとサーバが別ではないので、ここではエンジンを単独で動かせる VOICEVOX を利用します。
設置
リモートのサーバで、コンテナ向けのフォルダ群を作ります:
$ cd ${HOME}/system $ mkdir vir106 $ mkdir vol106
音声合成のコアを設置するための、スクリプトを取得します:
$ cd ${HOME}/system/vol106/prj $ curl -sSfL https://github.com/VOICEVOX/voicevox_core/releases/download/0.14.4/download-linux-x64 -o download $ chmod 755 download
コンテナを作成し、いったん起動します。コンテナの変更は、適宜イメージに反映させます:
$ docker build --no-cache -t ubuntu:vir106 ${HOME}/system/vir106 $ docker commit cnt106 ubuntu:vir106 $ docker run -it --rm --gpus all -v ${HOME}/system:/system --name cnt106 ubuntu:vir106
コンテナ上で、ライブラリ群を設置します:
$ cd /system/vol106/prj $ ./download --device cuda $ pip install https://github.com/VOICEVOX/voicevox_core/releases/download/0.14.4/voicevox_core-0.14.4+cuda-cp38-abi3-linux_x86_64.whl # ウェブサーバ/クライアント向けライブラリ群(非同期) $ pip install aiohttp
実装
- ◯
- バックエンド:サーバ
- ・
- apptts/bin/ttssrv.py
import ssl from pathlib import Path from aiohttp import web from voicevox_core import VoicevoxCore async def res001(datreq): global spkold dicreq = await datreq.json() txtreq = dicreq["txtreq"] numspk = dicreq["numspk"] if spkold != numspk: objvox.load_model(numspk) spkold = numspk datwav = objvox.tts(txtreq, numspk) datres = web.StreamResponse( status=200, reason='OK', headers={ 'Content-Type': 'audio/wav', 'Content-Disposition': 'attachment; filename="output.wav"', 'Access-Control-Allow-Origin': '*', } ) await datres.prepare(datreq) await datres.write(datwav) await datres.write_eof() return datres async def opt001(datreq): return web.Response( headers={ 'Access-Control-Allow-Methods': 'POST, OPTIONS', 'Access-Control-Allow-Headers': 'Content-Type', 'Access-Control-Allow-Origin': '*', } ) prttgt = 8080 adr001 = '/' pthcrt = '/system/vol105/etc_nginx/letsencrypt/<server_remote>/fullchain.pem' pthkey = '/system/vol105/etc_nginx/letsencrypt/<server_remote>/privkey.pem' pthdct = "open_jtalk_dic_utf_8-1.11" spkold = 0 objvox = VoicevoxCore(open_jtalk_dict_dir=Path(pthdct)) appweb = web.Application() appweb.add_routes([web.post(adr001, res001)]) appweb.add_routes([web.options(adr001, opt001)]) sslcon = ssl.create_default_context(ssl.Purpose.CLIENT_AUTH) sslcon.load_cert_chain(pthcrt, pthkey) web.run_app(appweb, port=prttgt, ssl_context=sslcon)
- ◯
- フロントエンド:クライアント
- ・
- apptts/web/ttsclt.htm
<html lang="ja"> <head> <meta charset="UTF-8"> </head> <body> <textarea id="txtreq" style="width: 32em; height: 8em;"></textarea><br/> <input id="numspk" style="width: 32em"></input><br/> <button onclick="req001()">ttsclt</button> <script> const adr001 = 'https://<server_remote>:8081/'; const req001 = async () => { const txtreq = document.getElementById('txtreq').value; const numspk = parseInt(document.getElementById('numspk').value); const dicreq = {txtreq: txtreq, numspk: numspk}; const stsres = await fetch(adr001, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(dicreq) }); if (stsres.ok) { const blbado = await stsres.blob(); const adrado = URL.createObjectURL(blbado); const objado = new Audio(adrado); objado.play(); } else { alert('err: ' + stsres.statusText); } } </script> </body> </html>
利用
コンテナを起動します:
$ docker run -it --rm \ -p 8081:8080 \ -v ${HOME}/system:/system \ -v /exp001:/exp001 \ --name cnt106 ubuntu:vir106
コンテナ上で、サーバを起動します:
$ cd /system/vol106/prj/voicevox_core ; python ttssrv.py &
ローカルのウェブブラウザで、クライアントを実行します:
> apptts/web/ttsclt.htm