diff --git a/run.py b/run.py index eea5fad11..1510a81b5 100644 --- a/run.py +++ b/run.py @@ -1286,26 +1286,44 @@ def main() -> None: if args.output_log_utf8: set_output_log_utf8() - cpu_num_threads: Optional[int] = args.cpu_num_threads + # Synthesis Engine + use_gpu: bool = args.use_gpu + voicevox_dir: Path | None = args.voicevox_dir + voicelib_dirs: list[Path] | None = args.voicelib_dir + runtime_dirs: list[Path] | None = args.runtime_dir + enable_mock: bool = args.enable_mock + cpu_num_threads: int | None = args.cpu_num_threads + load_all_models: bool = args.load_all_models synthesis_engines = make_synthesis_engines( - use_gpu=args.use_gpu, - voicelib_dirs=args.voicelib_dir, - voicevox_dir=args.voicevox_dir, - runtime_dirs=args.runtime_dir, + use_gpu=use_gpu, + voicelib_dirs=voicelib_dirs, + voicevox_dir=voicevox_dir, + runtime_dirs=runtime_dirs, cpu_num_threads=cpu_num_threads, - enable_mock=args.enable_mock, - load_all_models=args.load_all_models, + enable_mock=enable_mock, + load_all_models=load_all_models, ) assert len(synthesis_engines) != 0, "音声合成エンジンがありません。" latest_core_version = get_latest_core_version(versions=synthesis_engines.keys()) + # Cancellable Engine enable_cancellable_synthesis: bool = args.enable_cancellable_synthesis + init_processes: int = args.init_processes + cancellable_engine: CancellableEngine | None = None if enable_cancellable_synthesis: - cancellable_engine = CancellableEngine(args) + cancellable_engine = CancellableEngine( + init_processes=init_processes, + use_gpu=use_gpu, + voicelib_dirs=voicelib_dirs, + voicevox_dir=voicevox_dir, + runtime_dirs=runtime_dirs, + cpu_num_threads=cpu_num_threads, + enable_mock=enable_mock, + ) - root_dir: Path | None = args.voicevox_dir + root_dir: Path | None = voicevox_dir if root_dir is None: root_dir = engine_root() diff --git a/voicevox_engine/cancellable_engine.py b/voicevox_engine/cancellable_engine.py index 1bedb3ff3..174549b40 100644 --- a/voicevox_engine/cancellable_engine.py +++ b/voicevox_engine/cancellable_engine.py @@ -1,10 +1,15 @@ -import argparse import asyncio import queue +import sys from multiprocessing import Pipe, Process -from multiprocessing.connection import Connection + +if sys.platform == "win32": + from multiprocessing.connection import PipeConnection as ConnectionType +else: + from multiprocessing.connection import Connection as ConnectionType + +from pathlib import Path from tempfile import NamedTemporaryFile -from typing import List, Optional, Tuple import soundfile @@ -22,37 +27,52 @@ class CancellableEngine: 初期化後は、synthesis関数で音声合成できる (オリジナルと比べ引数が増えているので注意) + パラメータ use_gpu, voicelib_dirs, voicevox_dir, + runtime_dirs, cpu_num_threads, enable_mock は、 make_synthesis_engines を参照 + Attributes ---------- - watch_con_list: List[Tuple[Request, Process]] + watch_con_list: list[tuple[Request, Process]] Requestは接続の監視に使用され、Processは通信切断時のプロセスキルに使用される クライアントから接続があるとListにTupleが追加される 接続が切断、もしくは音声合成が終了すると削除される - procs_and_cons: queue.Queue[Tuple[Process, Connection]] + procs_and_cons: queue.Queue[tuple[Process, ConnectionType]] 音声合成の準備が終わっているプロセスのList (音声合成中のプロセスは入っていない) """ - def __init__(self, args: argparse.Namespace) -> None: + def __init__( + self, + init_processes: int, + use_gpu: bool, + voicelib_dirs: list[Path] | None, + voicevox_dir: Path | None, + runtime_dirs: list[Path] | None, + cpu_num_threads: int | None, + enable_mock: bool, + ) -> None: """ 変数の初期化を行う - また、args.init_processesの数だけプロセスを起動し、procs_and_consに格納する + また、init_processesの数だけプロセスを起動し、procs_and_consに格納する """ - self.args = args - if not self.args.enable_cancellable_synthesis: - raise HTTPException( - status_code=404, - detail="実験的機能はデフォルトで無効になっています。使用するには引数を指定してください。", - ) - - self.watch_con_list: List[Tuple[Request, Process]] = [] - self.procs_and_cons: queue.Queue[Tuple[Process, Connection]] = queue.Queue() - for _ in range(self.args.init_processes): - self.procs_and_cons.put(self.start_new_proc()) + + self.use_gpu = use_gpu + self.voicelib_dirs = voicelib_dirs + self.voicevox_dir = voicevox_dir + self.runtime_dirs = runtime_dirs + self.cpu_num_threads = cpu_num_threads + self.enable_mock = enable_mock + + self.watch_con_list: list[tuple[Request, Process]] = [] + + procs_and_cons: queue.Queue[tuple[Process, ConnectionType]] = queue.Queue() + for _ in range(init_processes): + procs_and_cons.put(self.start_new_proc()) + self.procs_and_cons = procs_and_cons def start_new_proc( self, - ) -> Tuple[Process, Connection]: + ) -> tuple[Process, ConnectionType]: """ 新しく開始したプロセスを返す関数 @@ -60,14 +80,19 @@ def start_new_proc( ------- ret_proc: Process 新規のプロセス - sub_proc_con1: Connection + sub_proc_con1: ConnectionType ret_procのプロセスと通信するためのPipe """ sub_proc_con1, sub_proc_con2 = Pipe(True) ret_proc = Process( target=start_synthesis_subprocess, kwargs={ - "args": self.args, + "use_gpu": self.use_gpu, + "voicelib_dirs": self.voicelib_dirs, + "voicevox_dir": self.voicevox_dir, + "runtime_dirs": self.runtime_dirs, + "cpu_num_threads": self.cpu_num_threads, + "enable_mock": self.enable_mock, "sub_proc_con": sub_proc_con2, }, daemon=True, @@ -79,7 +104,7 @@ def finalize_con( self, req: Request, proc: Process, - sub_proc_con: Optional[Connection], + sub_proc_con: ConnectionType | None, ) -> None: """ 接続が切断された時の処理を行う関数 @@ -94,7 +119,7 @@ def finalize_con( https://fastapi.tiangolo.com/advanced/using-request-directly/ proc: Process 音声合成を行っていたプロセス - sub_proc_con: Connection, optional + sub_proc_con: ConnectionType, optional 音声合成を行っていたプロセスとのPipe 指定されていない場合、プロセスは再利用されず終了される """ @@ -117,7 +142,7 @@ def _synthesis_impl( query: AudioQuery, speaker_id: int, request: Request, - core_version: Optional[str], + core_version: str | None, ) -> str: """ 音声合成を行う関数 @@ -173,28 +198,34 @@ async def catch_disconnection(self): def start_synthesis_subprocess( - args: argparse.Namespace, - sub_proc_con: Connection, -): + use_gpu: bool, + voicelib_dirs: list[Path] | None, + voicevox_dir: Path | None, + runtime_dirs: list[Path] | None, + cpu_num_threads: int | None, + enable_mock: bool, + sub_proc_con: ConnectionType, +) -> None: """ 音声合成を行うサブプロセスで行うための関数 pickle化の関係でグローバルに書いている + 引数 use_gpu, voicelib_dirs, voicevox_dir, + runtime_dirs, cpu_num_threads, enable_mock は、 make_synthesis_engines を参照 + Parameters ---------- - args: argparse.Namespace - 起動時に作られたものをそのまま渡す - sub_proc_con: Connection + sub_proc_con: ConnectionType メインプロセスと通信するためのPipe """ synthesis_engines = make_synthesis_engines( - use_gpu=args.use_gpu, - voicelib_dirs=args.voicelib_dir, - voicevox_dir=args.voicevox_dir, - runtime_dirs=args.runtime_dir, - cpu_num_threads=args.cpu_num_threads, - enable_mock=args.enable_mock, + use_gpu=use_gpu, + voicelib_dirs=voicelib_dirs, + voicevox_dir=voicevox_dir, + runtime_dirs=runtime_dirs, + cpu_num_threads=cpu_num_threads, + enable_mock=enable_mock, ) assert len(synthesis_engines) != 0, "音声合成エンジンがありません。" latest_core_version = get_latest_core_version(versions=synthesis_engines.keys())