ガイド

LiveKit Agentsの独自TTS:音声エージェントに好きな声を

LiveKit Agentsは、リアルタイム音声エージェント向けの人気のオープンソースフレームワークです。音声認識、言語モデル、音声合成が、LiveKitのルームにつながったセッションの中で動きます。多くの音声プロバイダー向けのプラグインがあり、それ以外のプロバイダーも小さなクラスを1つ書くだけで使えます。

このガイドでは、そのクラスをSuperClonicaの音声合成API向けに書きます。私たちの計測では約300 msでストリーミングが始まり、キャラクターやナレーターを含むカタログの190の声、または自分で学習させた声をエージェントに使わせられます。

TTSプラグインのしくみ

プラグインはtts.TTSを継承し、synthesizeからChunkedStreamを返します。その_runメソッドがリクエストを送り、音声のバイト列をAudioEmitterに渡します。AudioEmitterはOgg、mp3、wavを自分でデコードしてフレームに分けます。LiveKit Agents 1.8で確認したところ、テキストをストリームで受け取らないTTSは、セッションがStreamAdapterで包み、モデルの回答を文ごとに分けます。各文が短いリクエストになり、モデルが書いている間に最初の文が再生されます。

pip install livekit-agents aiohttp

プラグイン

これをsuperclonica_tts.pyとしてエージェントの隣に保存します:

import os
import aiohttp
from livekit.agents import DEFAULT_API_CONNECT_OPTIONS, APIConnectOptions, APIStatusError, tts, utils

URL = "https://superclonica.com/api/v1/speech"


class SuperClonicaTTS(tts.TTS):
    def __init__(self, voice: str, language: str = "en"):
        super().__init__(capabilities=tts.TTSCapabilities(streaming=False), sample_rate=44100, num_channels=1)
        self.voice, self.language = voice, language
        self._session = None

    def synthesize(self, text: str, *, conn_options: APIConnectOptions = DEFAULT_API_CONNECT_OPTIONS):
        return SuperClonicaStream(tts=self, input_text=text, conn_options=conn_options)

    def session(self):
        if self._session is None:
            key = os.environ["SUPERCLONICA_KEY"]
            self._session = aiohttp.ClientSession(headers={"Authorization": "Bearer " + key})
        return self._session

    async def aclose(self):
        if self._session:
            await self._session.close()


class SuperClonicaStream(tts.ChunkedStream):
    async def _run(self, output_emitter: tts.AudioEmitter):
        engine = self._tts
        body = {"text": self.input_text, "voice": engine.voice, "language": engine.language, "stream": True}
        async with engine.session().post(URL, json=body) as answer:
            if answer.status != 200:
                raise APIStatusError(await answer.text(), status_code=answer.status)
            output_emitter.initialize(
                request_id=utils.shortuuid(), sample_rate=44100, num_channels=1, mime_type="audio/ogg"
            )
            async for chunk in answer.content.iter_chunked(4096):
                output_emitter.push(chunk)
            output_emitter.flush()

リクエストは"stream": trueを指定し、APIは合成しながらOggを返します。エミッターはそれをデコードし、クラスが宣言した44.1 kHzにリサンプリングします。それ以外の応答ではAPIStatusErrorを発生させます。フレームワークは429とサーバーエラーを接続オプションに従って再試行し、キーの誤りなどではすぐにあきらめます。

セッションで使う

AgentSessionをつくるときに、すでに使っている音声認識と言語モデルのプラグインと一緒に、このクラスをttsとして渡します:

import os
from livekit.agents import AgentSession
from superclonica_tts import SuperClonicaTTS

session = AgentSession(
    stt=...,  # your speech recognition plugin
    llm=...,  # your language model plugin
    tts=SuperClonicaTTS(voice=os.environ["VOICE_ID"], language="en"),
)

SUPERCLONICA_KEYに音声合成APIのページで作ったキーを、VOICE_IDに選んだ声のページに表示されるIDを設定します。エージェントのほかの部分はそのままです。

知っておきたい制限

APIはWebSocketではなく通常のHTTPSでストリーミング音声を返し、単語ごとのタイムスタンプは返さないため、整列済みの字幕はオフになります。31言語を話しますが、中国語にはまだ対応していません。プランは10分ごとの枠で、すべての通話を合わせた音声の秒数を決めます。無料キーは開発用に1日1分、月5ドルのスタートは枠ごとに最大3分、Proは20分で、にぎやかな通話を同時にいくつか処理できます。