見出し画像

style-bert-vits2等のTTSにPOST時、threadingを使って少し時間短縮

import re
import anthropic
from datetime import datetime
import concurrent
import io
import threading
import time
import wave
from concurrent.futures import ThreadPoolExecutor
import pyaudio
import requests

api_key = "**************************************************************"
end_of_playback = 0
messages = []
count = 0
def create_synthesis(text, count):
    # 音声合成用のクエリを作成
    parameters = {"text": text, "model_id": 0, "style": "Neutral",
                  "style_weight": 5} # 最低限のパラメーター。
    # style: Neutral,Angry,Disgust,Fear,Happy,Sad,Surprise。Neutralがデフォルト。
    endpoint = "http://127.0.0.1:5000/voice/"
    headers = {
        "Content-Type": "application/json"}
    response_synth = requests.post(endpoint, params=parameters, headers=headers, timeout=25)
    # レスポンスから音声データを取得
    audio_data = response_synth.content
    # 音声データを保存せずに直接再生
    audio_io = io.BytesIO(audio_data)
    if count == 0:
        time.sleep(1.2)
    time.sleep(0.8)
    return audio_io

def playback(end_of_playback,audio_io):
    wave_file = wave.open(audio_io, 'rb')
    p = pyaudio.PyAudio()
    stream = p.open(format=p.get_format_from_width(wave_file.getsampwidth()),
                    channels=wave_file.getnchannels(),
                    rate=wave_file.getframerate(),
                    output=True)

    data = wave_file.readframes(1024)
    while data != b'':
        stream.write(data)
        data = wave_file.readframes(1024)
    time.sleep(0.3)
    stream.stop_stream()
    stream.close()
    p.terminate()
    end_of_playback += 1
    return end_of_playback


def threading_synthesis(a_dict, end_of_playback, count):
    # 関数の中に小さい関数を作成
    def create_synthesis_and_get_audio_io(text):
        audio_io = create_synthesis(text,count)
        return audio_io
    # 並列処理
    executor = ThreadPoolExecutor(max_workers=len(a_dict))
    futures = []
    for i in range(len(a_dict)):
        future = executor.submit(create_synthesis_and_get_audio_io, str(a_dict['text' + str(i + 1)]))
        futures.append(future)
    audio_ios = [future.result() for future in futures]

    for i in range(len(a_dict)):
        # 並列処理
        thread = threading.Thread(
            target=lambda: create_synthesis_and_get_audio_io(str(a_dict['text' + str(i + 1)])))
        thread.start()
    thread.join(5) # 5秒

    for i, audio_io in enumerate(audio_ios):
        # 並列処理
        executor = concurrent.futures.ThreadPoolExecutor(max_workers=1) # 再生は1つのみ
        executor.submit(end_of_playback)
        end_of_playback = 0
        playback(end_of_playback, audio_io)


system_content = f"""あなたは優秀なAIアシスタントです。質問に答えてください。
現在時刻は{datetime.now()}です。"""

client = anthropic.Anthropic(
    api_key=api_key,
)

print(" anthropic haikuとチャット。")

while True:
    u = input(" You: ")
    if not u:
        break
    messages.append({"role": "user", "content": u})
    resp = client.messages.create(
        model="claude-3-haiku-20240307",
        system=system_content,
        max_tokens=190,
        temperature=0.7,
        messages=messages,

    )
    a = resp.content[0].text
    print("  chatbot: ", a)
    messages.append({"role": "assistant", "content": a})
    # テキストの最後に。!?のいずれかがあった場合削除
    a = re.sub(r'[。!?]$', '', a)
    # テキストを。!?で分割
    a_list = re.split('[。!?]', a)
    a_dict = {}
    for i, split_text in enumerate(a_list):
        a_dict['text' + str(i + 1)] = split_text
    threading_synthesis(a_dict, end_of_playback, count)
    count += 1

全体の構成はざっとこんな感じになりますが、肝心なのは中ほどにあるthreading_synthesis関数です。この構成にするとすごく短い返答の場合はthreadindをしない方が速いのですが、anthropicのclaude3みたいなおしゃべりなllmの場合、長い返答が多くなるので重宝します。

追記:
create_synthesis関数のtime.sleep()の値は環境(PC)に合わせて調節してください

この記事が気に入ったらサポートをしてみませんか?