tktts_irodori.py ダウンロード/コピー
tktts_irodori.py
tktts_irodori.py
1"""tktts_* ライブラリファミリーのためのIrodori-TTSバックエンド。
2
3このモジュールは ``tktts_voicevox.py`` や ``tktts_qwen3.py`` で使用されるインターフェースに準拠しています。
4Irodori-TTSの公式 ``InferenceRuntime`` API を介して直接呼び出しを行います。
5Gradio UI や HTTPサーバーを実行する必要はありません。
6ロードされたランタイムはIrodori-TTSによってキャッシュされ、以降のすべての発話で再利用されます。
7
8テキストの分割、読み上げ/発音変換は意図的に呼び出し元に任されています。
9``speak_dialogue`` は、他のバックエンドとまったく同様に、tkttsによって渡された置換辞書のみを適用します。
10
11.. seealso::
12 :doc:`tktts_irodori_usage`
13
14"""
15
16from __future__ import annotations
17
18import os
19import re
20from pathlib import Path
21from typing import Any, Iterable
22
23
24missing = []
25for lib, import_name in [
26 ("torch", "torch"),
27 ("Irodori-TTS", "irodori_tts"),
28]:
29 try:
30 __import__(import_name)
31 except ImportError:
32 missing.append(lib)
33
34if missing:
35 raise ImportError(
36 "Error: Missing libraries: "
37 + ", ".join(missing)
38 + "\n Run this module in the Irodori-TTS uv environment."
39 + "\n install: uv sync --extra cu128"
40 )
41
42import torch
43from irodori_tts.inference_runtime import (
44 RuntimeKey,
45 SamplingRequest,
46 clear_cached_runtime,
47 default_runtime_device,
48 download_hf_checkpoint,
49 get_cached_runtime,
50 save_wav,
51)
52
53try:
54 from tktts_base import apply_replacements, normalize_speaker, split_dialogue
55except ImportError as exc:
56 raise ImportError(
57 "tktts_irodori requires tktts_base.py to be importable."
58 ) from exc
59
60
61TTS_ENGINE_NAME = "irodori"
62DEFAULT_IRODORI_MODEL = "Aratako/Irodori-TTS-v4.1-Small"
63DEFAULT_IRODORI_VOICE = "default"
64DEFAULT_CAPTION = "落ち着いた自然な声で、明瞭に読み上げる。"
65DEFAULT_DEVICE = "auto"
66DEFAULT_PRECISION = "auto"
67DEFAULT_NUM_STEPS = 40
68DEFAULT_CFG_SCALE_TEXT = 3.5
69DEFAULT_CFG_SCALE_CAPTION = 3.0
70DEFAULT_CFG_SCALE_SPEAKER = 5.0
71DEFAULT_SEED = 0
72
73
74_AVAILABLE_VOICES = [
75 {
76 "name": DEFAULT_IRODORI_VOICE,
77 "description": (
78 "Irodori-TTS voice. Use irodori_caption and/or reference WAV "
79 "files to define voice identity and speaking style."
80 ),
81 }
82]
83
84
85def _resolve_device(device: str | None = DEFAULT_DEVICE) -> str:
86 """Irodori-TTSが推奨する利用可能なデバイスに ``auto`` を解決します。
87
88 :param device: デバイス名 (例: "cpu", "cuda", "cuda:0", "auto")。デフォルトは "auto"。
89 :type device: str | None
90 :returns: 解決されたデバイス名。 "cuda:0" は "cuda" に正規化されます。
91 :rtype: str
92 """
93 if device is None or str(device).strip().lower() == "auto":
94 return default_runtime_device()
95 value = str(device).strip().lower()
96 if value == "cuda:0":
97 return "cuda"
98 return value
99
100
101def _resolve_precision(device: str, precision: str | None) -> str:
102 """``auto`` をCUDA/XPU上のbf16、それ以外ではfp32に解決します。
103
104 :param device: 解決されたデバイス名 (例: "cpu", "cuda")。
105 :type device: str
106 :param precision: 精度名 (例: "fp32", "bf16", "auto")。
107 :type precision: str | None
108 :returns: 解決された精度名。
109 :rtype: str
110 :raises ValueError: サポートされていない精度が指定された場合、またはbf16が対応していないデバイスで指定された場合。
111 """
112 value = "auto" if precision is None else str(precision).strip().lower()
113 if value == "auto":
114 return "bf16" if device in {"cuda", "xpu"} else "fp32"
115 aliases = {
116 "bfloat16": "bf16",
117 "bf16": "bf16",
118 "float32": "fp32",
119 "fp32": "fp32",
120 }
121 if value not in aliases:
122 raise ValueError(f"Unsupported Irodori-TTS precision: {precision}")
123 resolved = aliases[value]
124 if resolved == "bf16" and device not in {"cuda", "xpu"}:
125 raise ValueError(f"bf16 is not supported for Irodori-TTS device={device}")
126 return resolved
127
128
129def _resolve_checkpoint(model_id: str) -> str:
130 """ローカルチェックポイントを解決するか、Hugging Faceチェックポイントをダウンロードします。
131
132 指定された ``model_id`` がファイルパスであればその絶対パスを返し、
133 そうでなければHugging Faceからダウンロードしてそのパスを返します。
134
135 :param model_id: モデルID (Hugging Faceのリポジトリ名またはローカルファイルパス)。
136 :type model_id: str
137 :returns: 解決されたチェックポイントファイルの絶対パス。
138 :rtype: str
139 :raises FileNotFoundError: ローカルファイルとして指定されたチェックポイントが見つからない場合。
140 """
141 candidate = Path(str(model_id)).expanduser()
142 if candidate.suffix.lower() in {".pt", ".safetensors"}:
143 if not candidate.is_file():
144 raise FileNotFoundError(f"Irodori-TTS checkpoint not found: {candidate}")
145 return str(candidate.resolve())
146 return str(download_hf_checkpoint(str(model_id)))
147
148
149def load_model(
150 model_id: str = DEFAULT_IRODORI_MODEL,
151 device: str = DEFAULT_DEVICE,
152 precision: str = DEFAULT_PRECISION,
153 *,
154 codec_device: str | None = None,
155 codec_precision: str | None = None,
156 force_reload: bool = False,
157):
158 """Irodori-TTS推論ランタイムをロードし、キャッシュします。
159
160 この関数は、指定されたモデル、デバイス、精度に基づいてIrodori-TTSのランタイムを初期化します。
161 ランタイムはIrodori-TTSの内部キャッシュメカニズムによって管理され、
162 同じ設定が再度要求された場合は既存のインスタンスが再利用されます。
163
164 :param model_id: ロードするIrodori-TTSモデルのHugging FaceリポジトリIDまたはローカルパス。デフォルトは ``DEFAULT_IRODORI_MODEL``。
165 :type model_id: str
166 :param device: モデルの推論に使用するデバイス ("cpu", "cuda", "auto" など)。デフォルトは ``DEFAULT_DEVICE``。
167 :type device: str
168 :param precision: モデルの推論に使用する精度 ("fp32", "bf16", "auto" など)。デフォルトは ``DEFAULT_PRECISION``。
169 :type precision: str
170 :param codec_device: コーデックの推論に使用するデバイス。指定されない場合は ``device`` が使用されます。
171 :type codec_device: str | None
172 :param codec_precision: コーデックの推論に使用する精度。指定されない場合は ``precision`` が使用されます。
173 :type codec_precision: str | None
174 :param force_reload: Trueの場合、既存のキャッシュされたランタイムをクリアして強制的に再ロードします。
175 :type force_reload: bool
176 :returns: ロードされたIrodori-TTSの ``InferenceRuntime`` インスタンス。
177 :rtype: irodori_tts.inference_runtime.InferenceRuntime
178 """
179 resolved_device = _resolve_device(device)
180 resolved_codec_device = _resolve_device(codec_device or resolved_device)
181 resolved_precision = _resolve_precision(resolved_device, precision)
182 resolved_codec_precision = _resolve_precision(
183 resolved_codec_device,
184 precision if codec_precision is None else codec_precision,
185 )
186 checkpoint = _resolve_checkpoint(model_id)
187
188 if force_reload:
189 clear_cached_runtime()
190
191 key = RuntimeKey(
192 checkpoint=checkpoint,
193 model_device=resolved_device,
194 codec_repo="Aratako/Semantic-DACVAE-Japanese-32dim",
195 model_precision=resolved_precision,
196 codec_device=resolved_codec_device,
197 codec_precision=resolved_codec_precision,
198 compile_model=False,
199 compile_dynamic=False,
200 )
201 runtime, reloaded = get_cached_runtime(key)
202 status = "loaded" if reloaded else "reused"
203 print(
204 "tktts_irodori.load_model(): "
205 f"{status}, model={model_id}, device={resolved_device}, "
206 f"precision={resolved_precision}"
207 )
208 return runtime
209
210
211def unload_models() -> None:
212 """Irodori-TTSによってキャッシュされたランタイムを解放します。
213
214 これにより、Irodori-TTSの推論ランタイムがキャッシュからクリアされ、
215 GPUメモリを使用している場合はGPUキャッシュも空にされます。
216 """
217 clear_cached_runtime()
218 if torch.cuda.is_available():
219 torch.cuda.empty_cache()
220
221
222def get_available_voices_info(model=None):
223 """Irodori-TTSが使用する仮想的なデフォルト音声の情報を返します。
224
225 Irodori-TTSは固定された話者リストを持っていません。
226 声の同一性は参照オーディオによって制御され、スタイルはキャプションによっても制御できます。
227 ``model`` 引数はtkttsインターフェースの互換性のために受け入れられますが、内部では使用されません。
228
229 :param model: tkttsインターフェース互換性のため。実際には使用されません。
230 :type model: Any
231 :returns: 仮想的なデフォルト音声の詳細情報を含む辞書のリスト。
232 :rtype: list[dict[str, str]]
233 """
234 del model
235 return [voice.copy() for voice in _AVAILABLE_VOICES]
236
237
238def get_available_voices(model=None):
239 """tkttsの音声セレクタと互換性のある音声名を返します。
240
241 :param model: tkttsインターフェース互換性のため。実際には使用されません。
242 :type model: Any
243 :returns: 利用可能な仮想音声名のリスト。
244 :rtype: list[str]
245 """
246 return [voice["name"] for voice in get_available_voices_info(model)]
247
248
249def list_available_voices(model=None) -> bool:
250 """Irodori-TTSの仮想的な音声エントリを表示します。
251
252 :param model: tkttsインターフェース互換性のため。実際には使用されません。
253 :type model: Any
254 :returns: 利用可能な音声がある場合はTrue、ない場合はFalse。
255 :rtype: bool
256 """
257 print(f"=== 利用可能な {TTS_ENGINE_NAME} voices ===")
258 voices = get_available_voices_info(model)
259 for voice in voices:
260 print(f" Name: {voice['name']}, Description: {voice['description']}")
261 return bool(voices)
262
263
264def _speaker_key(name: str) -> str:
265 """話者名を正規化し、比較用のキーを生成します。
266
267 スペース、アンダースコア、ハイフン、括弧を除去し、小文字に変換します。
268
269 :param name: 話者名。
270 :type name: str
271 :returns: 正規化された話者キー。
272 :rtype: str
273 """
274 normalized = normalize_speaker(str(name))
275 return re.sub(r"[\s_\-()()]+", "", normalized).lower()
276
277
278def resolve_speaker_id(speaker_name, voices_info=None, model=None):
279 """``default``/``irodori`` を仮想的なIrodori音声に解決します。
280
281 Irodori-TTSは固定の話者IDを持たないため、指定された話者名が
282 ``default`` またはそのエイリアスであれば、仮想的なIrodori-TTS音声として扱われます。
283
284 :param speaker_name: 解決する話者名。
285 :type speaker_name: str
286 :param voices_info: 利用可能な音声情報のリスト。Noneの場合は ``get_available_voices_info()`` から取得されます。
287 :type voices_info: list[dict[str, str]] | None
288 :param model: tkttsインターフェース互換性のため。実際には使用されません。
289 :type model: Any
290 :returns: (voices_info, 解決された話者名) のタプル。
291 :rtype: tuple[list[dict[str, str]], str]
292 :raises ValueError: 話者名が見つからない場合。
293 """
294 del model
295 if voices_info is None:
296 voices_info = get_available_voices_info()
297
298 query = _speaker_key(speaker_name)
299 aliases = {
300 "default",
301 "irodori",
302 "irodoritts",
303 "標準",
304 "デフォルト",
305 }
306 if query in aliases:
307 return voices_info, DEFAULT_IRODORI_VOICE
308
309 for voice in voices_info:
310 if query == _speaker_key(voice["name"]):
311 return voices_info, voice["name"]
312
313 raise ValueError(
314 "❌ Error in tktts_irodori.resolve_speaker_id(): "
315 f"話者 [{speaker_name}] が見つかりませんでした。"
316 " Irodori-TTSでは voice=default を使用し、"
317 "irodori_captionまたは参照WAVで声を指定してください。"
318 )
319
320
321def _as_optional_int(value: Any, default: int | None) -> int | None:
322 """値を整数に変換し、Noneや特定の文字列をNoneとして扱います。
323
324 :param value: 変換する値。
325 :type value: Any
326 :param default: 値がNoneまたは特定の文字列の場合に返すデフォルト値。
327 :type default: int | None
328 :returns: 整数またはNone。
329 :rtype: int | None
330 """
331 if value is None:
332 return default
333 if isinstance(value, str):
334 text = value.strip().lower()
335 if text in {"", "none", "random"}:
336 return None
337 return int(value)
338
339
340def _as_ref_wavs(value: Any) -> list[str]:
341 """パス、イテラブル、またはセミコロン区切りのパスを正規化します。
342
343 :param value: 参照WAVファイルのパス、またはパスのイテラブル、またはセミコロン区切りのパス文字列。
344 :type value: Any
345 :returns: 解決された参照WAVファイルの絶対パスのリスト。
346 :rtype: list[str]
347 :raises FileNotFoundError: 指定された参照WAVファイルが見つからない場合。
348 """
349 if value is None:
350 return []
351 if isinstance(value, (str, os.PathLike)):
352 text = str(value).strip()
353 if not text:
354 return []
355 values = [part.strip() for part in text.split(";") if part.strip()]
356 elif isinstance(value, Iterable):
357 values = [str(part).strip() for part in value if str(part).strip()]
358 else:
359 values = [str(value).strip()]
360
361 paths = []
362 for item in values:
363 path = Path(item).expanduser()
364 if not path.is_file():
365 raise FileNotFoundError(f"Irodori-TTS reference WAV not found: {path}")
366 paths.append(str(path.resolve()))
367 return paths
368
369
370def speak(
371 outfile,
372 text,
373 voice=DEFAULT_IRODORI_VOICE,
374 speak_rate=None,
375 speak_pitch=None,
376 *,
377 caption: str | None = DEFAULT_CAPTION,
378 ref_wav: str | os.PathLike | None = None,
379 ref_wavs: Iterable[str | os.PathLike] | str | None = None,
380 model=None,
381 model_id: str = DEFAULT_IRODORI_MODEL,
382 device: str = DEFAULT_DEVICE,
383 precision: str = DEFAULT_PRECISION,
384 codec_device: str | None = None,
385 codec_precision: str | None = None,
386 num_steps: int = DEFAULT_NUM_STEPS,
387 cfg_scale_text: float = DEFAULT_CFG_SCALE_TEXT,
388 cfg_scale_caption: float = DEFAULT_CFG_SCALE_CAPTION,
389 cfg_scale_speaker: float = DEFAULT_CFG_SCALE_SPEAKER,
390 duration_scale: float = 1.0,
391 seed: int | str | None = DEFAULT_SEED,
392 lora_adapter: str | None = None,
393):
394 """Irodori-TTS v4.1-Smallを使用して1つのWAVファイルを生成します。
395
396 テキストを音声に変換し、指定された出力ファイルに保存します。
397 Irodori-TTSは固定話者を持たないため、``voice`` 引数は主に情報として扱われます。
398 音声のスタイルや同一性は、``caption`` および/または ``ref_wav``/``ref_wavs`` を使用して制御されます。
399
400 :param outfile: 生成されたWAVファイルを保存するパス。
401 :type outfile: str | os.PathLike
402 :param text: 読み上げるテキスト。
403 :type text: str
404 :param voice: 読み上げに使用する音声名。Irodori-TTSでは仮想的な音声であり、 ``default`` が推奨されます。デフォルトは ``DEFAULT_IRODORI_VOICE``。
405 :type voice: str
406 :param speak_rate: 音声の速さ。Irodori-TTSでは直接制御できないため無視されます。
407 :type speak_rate: float | None
408 :param speak_pitch: 音声のピッチ。Irodori-TTSでは直接制御できないため無視されます。
409 :type speak_pitch: float | None
410 :param caption: 音声のスタイルを制御するためのキャプション。例: "落ち着いた自然な声で、明瞭に読み上げる。"。デフォルトは ``DEFAULT_CAPTION``。
411 :type caption: str | None
412 :param ref_wav: スタイル参照に使用する単一のWAVファイルのパス。 ``ref_wavs`` と同時に指定された場合、結合されます。
413 :type ref_wav: str | os.PathLike | None
414 :param ref_wavs: スタイル参照に使用する複数のWAVファイルのパスのイテラブルまたはセミコロン区切りの文字列。
415 :type ref_wavs: Iterable[str | os.PathLike] | str | None
416 :param model: ロード済みのIrodori-TTSランタイムインスタンス。指定されない場合、 ``load_model`` を使って新規にロードされます。
417 :type model: irodori_tts.inference_runtime.InferenceRuntime | None
418 :param model_id: ロードするIrodori-TTSモデルID (``model`` がNoneの場合にのみ使用されます)。デフォルトは ``DEFAULT_IRODORI_MODEL``。
419 :type model_id: str
420 :param device: モデルの推論に使用するデバイス (``model`` がNoneの場合にのみ使用されます)。デフォルトは ``DEFAULT_DEVICE``。
421 :type device: str
422 :param precision: モデルの推論に使用する精度 (``model`` がNoneの場合にのみ使用されます)。デフォルトは ``DEFAULT_PRECISION``。
423 :type precision: str
424 :param codec_device: コーデックの推論に使用するデバイス (``model`` がNoneの場合にのみ使用されます)。
425 :type codec_device: str | None
426 :param codec_precision: コーデックの推論に使用する精度 (``model`` がNoneの場合にのみ使用されます)。
427 :type codec_precision: str | None
428 :param num_steps: サンプリングステップ数。デフォルトは ``DEFAULT_NUM_STEPS``。
429 :type num_steps: int
430 :param cfg_scale_text: テキストに対するCFGスケール。デフォルトは ``DEFAULT_CFG_SCALE_TEXT``。
431 :type cfg_scale_text: float
432 :param cfg_scale_caption: キャプションに対するCFGスケール。デフォルトは ``DEFAULT_CFG_SCALE_CAPTION``。
433 :type cfg_scale_caption: float
434 :param cfg_scale_speaker: 話者に対するCFGスケール (参照WAVがある場合のみ有効)。デフォルトは ``DEFAULT_CFG_SCALE_SPEAKER``。
435 :type cfg_scale_speaker: float
436 :param duration_scale: 音声の全体的な長さを調整するスケールファクター。デフォルトは 1.0。
437 :type duration_scale: float
438 :param seed: 乱数シード。None, "random" または空文字列の場合、シードはランダムに生成されます。デフォルトは ``DEFAULT_SEED``。
439 :type seed: int | str | None
440 :param lora_adapter: 使用するLoRAアダプターのパス。
441 :type lora_adapter: str | None
442 :returns: 生成されたWAVファイルの絶対パス。生成に失敗した場合はNone。
443 :rtype: str | None
444 """
445 text = str(text).strip()
446 if not text:
447 print("❌ tktts_irodori.speak(): 読み上げテキストが空です")
448 return None
449
450 if _speaker_key(voice) not in {
451 "default",
452 "irodori",
453 "irodoritts",
454 "標準",
455 "デフォルト",
456 }:
457 print(
458 f" ** Warning: Irodori-TTSには固定話者 [{voice}] がないため "
459 "voice=defaultとして扱います"
460 )
461 if speak_rate is not None or speak_pitch is not None:
462 print(
463 " ** Warning: Irodori-TTSでは speak_rate/speak_pitch を"
464 "直接指定できないため無視します"
465 )
466
467 references = _as_ref_wavs(ref_wavs)
468 if ref_wav is not None:
469 references = _as_ref_wavs(ref_wav) + references
470
471 if model is None:
472 model = load_model(
473 model_id=model_id,
474 device=device,
475 precision=precision,
476 codec_device=codec_device,
477 codec_precision=codec_precision,
478 )
479
480 try:
481 result = model.synthesize(
482 SamplingRequest(
483 text=text,
484 caption=str(caption).strip() if caption else None,
485 ref_wav=None,
486 ref_wavs=references or None,
487 no_ref=not references,
488 ref_normalize_db=-16.0,
489 ref_ensure_max=True,
490 num_candidates=1,
491 decode_mode="sequential",
492 seconds=None,
493 duration_scale=float(duration_scale),
494 max_ref_seconds=None,
495 num_steps=int(num_steps),
496 seed=_as_optional_int(seed, DEFAULT_SEED),
497 cfg_guidance_mode="independent",
498 cfg_scale_text=float(cfg_scale_text),
499 cfg_scale_caption=float(cfg_scale_caption),
500 cfg_scale_speaker=(float(cfg_scale_speaker) if references else 0.0),
501 cfg_min_t=0.5,
502 cfg_max_t=1.0,
503 context_kv_cache=True,
504 t_schedule_mode="linear",
505 sway_coeff=-1.0,
506 trim_tail=True,
507 lora_adapter=lora_adapter,
508 ),
509 log_fn=lambda message: print(f" {message}"),
510 )
511 output_path = save_wav(outfile, result.audio, result.sample_rate)
512 except Exception as exc:
513 print(f"❌ Irodori-TTS生成エラー: {exc}")
514 return None
515
516 if Path(output_path).exists():
517 print(
518 f" ** 一時ファイル [{output_path}] を保存しました "
519 f"(seed={result.used_seed})"
520 )
521 return str(output_path)
522
523 print(f" ** Error: ファイル [{output_path}] の出力に失敗しました")
524 return None
525
526
527def _cfg_value(cfg, name: str, default: Any = None) -> Any:
528 """設定オブジェクトから属性値を取得します。
529
530 :param cfg: 設定オブジェクト。Noneの場合、デフォルト値を返します。
531 :type cfg: Any
532 :param name: 取得する属性の名前。
533 :type name: str
534 :param default: 属性が見つからない場合、または ``cfg`` がNoneの場合に返すデフォルト値。
535 :type default: Any
536 :returns: 設定値またはデフォルト値。
537 :rtype: Any
538 """
539 if cfg is None:
540 return default
541 return getattr(cfg, name, default)
542
543
544def speak_dialogue(
545 dialogue,
546 replacements,
547 target_voices,
548 speakers=None,
549 temp_dir=".",
550 outfile=None,
551 ext="wav",
552 cfg=None,
553 *,
554 caption: str | None = None,
555 ref_wav: str | os.PathLike | None = None,
556 ref_wavs: Iterable[str | os.PathLike] | str | None = None,
557 model=None,
558 model_id: str = DEFAULT_IRODORI_MODEL,
559 device: str = DEFAULT_DEVICE,
560 precision: str = DEFAULT_PRECISION,
561):
562 """tkttsダイアログシーケンスのための一時WAVファイルを生成します。
563
564 与えられたダイアログ(会話のリスト)を処理し、各発話に対してIrodori-TTSを使用して
565 一時的なWAVファイルを生成します。
566 ``cfg`` オブジェクトを介してIrodori-TTS固有のパラメータ(例: ``irodori_caption``, ``irodori_seed``)を
567 細かく制御できます。
568
569 :param dialogue: 処理するダイアログアイテムのリスト。各アイテムは ``split_dialogue`` が処理できる形式です。
570 :type dialogue: list
571 :param replacements: テキストに適用する置換辞書。
572 :type replacements: dict[str, str]
573 :param target_voices: ターゲットとする音声名のリストまたは単一の音声名。
574 :type target_voices: list[str] | str
575 :param speakers: 話者IDと音声名のマッピング辞書。デフォルトは空の辞書。
576 :type speakers: dict[str, str] | None
577 :param temp_dir: 一時的なWAVファイルを保存するディレクトリ。デフォルトはカレントディレクトリ。
578 :type temp_dir: str | os.PathLike
579 :param outfile: tkttsインターフェース互換性のため。実際には使用されません。
580 :type outfile: Any
581 :param ext: 生成される音声ファイルの拡張子。デフォルトは "wav"。
582 :type ext: str
583 :param cfg: 設定オブジェクト。このオブジェクトの属性(例: ``cfg.irodori_caption``)を通じて、
584 Irodori-TTSの各種パラメータをオーバーライドできます。
585 :type cfg: Any
586 :param caption: 音声のスタイルを制御するためのキャプション。 ``cfg.irodori_caption`` が設定されている場合はそちらが優先されます。
587 :type caption: str | None
588 :param ref_wav: スタイル参照に使用する単一のWAVファイルのパス。 ``cfg.irodori_ref_wav`` が設定されている場合はそちらが優先されます。
589 :type ref_wav: str | os.PathLike | None
590 :param ref_wavs: スタイル参照に使用する複数のWAVファイルのパス。 ``cfg.irodori_ref_wavs`` が設定されている場合はそちらが優先されます。
591 :type ref_wavs: Iterable[str | os.PathLike] | str | None
592 :param model: ロード済みのIrodori-TTSランタイムインスタンス。指定されない場合、 ``load_model`` を使って新規にロードされます。
593 :type model: irodori_tts.inference_runtime.InferenceRuntime | None
594 :param model_id: ロードするIrodori-TTSモデルID (``model`` がNoneの場合にのみ使用されます)。 ``cfg.irodori_model_id`` が設定されている場合はそちらが優先されます。
595 :type model_id: str
596 :param device: モデルの推論に使用するデバイス (``model`` がNoneの場合にのみ使用されます)。 ``cfg.irodori_device`` が設定されている場合はそちらが優先されます。
597 :type device: str
598 :param precision: モデルの推論に使用する精度 (``model`` がNoneの場合にのみ使用されます)。 ``cfg.irodori_precision`` が設定されている場合はそちらが優先されます。
599 :type precision: str
600 :param codec_device: コーデックの推論に使用するデバイス (``model`` がNoneの場合にのみ使用されます)。 ``cfg.irodori_codec_device`` が設定されている場合はそちらが優先されます。
601 :type codec_device: str | None
602 :param codec_precision: コーデックの推論に使用する精度 (``model`` がNoneの場合にのみ使用されます)。 ``cfg.irodori_codec_precision`` が設定されている場合はそちらが優先されます。
603 :type codec_precision: str | None
604 :returns: 生成が成功したかを示す真偽値と、生成された一時ファイルのパスのリストのタプル。
605 :rtype: tuple[bool, list[str]]
606 """
607 del outfile # Kept in the signature for tktts backend compatibility.
608 speakers = {} if speakers is None else speakers
609
610 print("tktts_irodori.speak_dialogue(): target_voices:", target_voices)
611
612 model_id = _cfg_value(cfg, "irodori_model_id", model_id)
613 device = _cfg_value(cfg, "irodori_device", device)
614 precision = _cfg_value(cfg, "irodori_precision", precision)
615 codec_device = _cfg_value(cfg, "irodori_codec_device", None)
616 codec_precision = _cfg_value(cfg, "irodori_codec_precision", None)
617
618 if model is None:
619 try:
620 model = load_model(
621 model_id=model_id,
622 device=device,
623 precision=precision,
624 codec_device=codec_device,
625 codec_precision=codec_precision,
626 )
627 except Exception as exc:
628 print(f"❌ Irodori-TTSモデル読み込みエラー: {exc}")
629 return False, []
630
631 effective_caption = (
632 caption
633 if caption is not None
634 else _cfg_value(cfg, "irodori_caption", DEFAULT_CAPTION)
635 )
636 effective_ref_wav = (
637 ref_wav
638 if ref_wav is not None
639 else _cfg_value(cfg, "irodori_ref_wav", None)
640 )
641 effective_ref_wavs = (
642 ref_wavs
643 if ref_wavs is not None
644 else _cfg_value(cfg, "irodori_ref_wavs", None)
645 )
646 num_steps = _cfg_value(cfg, "irodori_num_steps", DEFAULT_NUM_STEPS)
647 cfg_scale_text = _cfg_value(
648 cfg, "irodori_cfg_scale_text", DEFAULT_CFG_SCALE_TEXT
649 )
650 cfg_scale_caption = _cfg_value(
651 cfg, "irodori_cfg_scale_caption", DEFAULT_CFG_SCALE_CAPTION
652 )
653 cfg_scale_speaker = _cfg_value(
654 cfg, "irodori_cfg_scale_speaker", DEFAULT_CFG_SCALE_SPEAKER
655 )
656 duration_scale = _cfg_value(cfg, "irodori_duration_scale", 1.0)
657 seed = _cfg_value(cfg, "irodori_seed", DEFAULT_SEED)
658 lora_adapter = _cfg_value(cfg, "irodori_lora_adapter", None)
659
660 tmpfiles = []
661 voices_info = get_available_voices_info(model)
662 idx = 1
663 is_monologue = bool(_cfg_value(cfg, "monologue", False))
664
665 for i, dialogue_item in enumerate(dialogue):
666 print()
667 print(f"Dialogue {i:04d}:")
668 dialogue_list = split_dialogue(
669 dialogue_item,
670 target_voices,
671 speakers=speakers,
672 default_voice=DEFAULT_IRODORI_VOICE,
673 is_monologue=is_monologue,
674 )
675
676 for speaker, text in dialogue_list:
677 tmpfile = os.path.join(temp_dir, f"tmp_{idx:03d}.{ext}")
678 text = apply_replacements(text, replacements)
679 if isinstance(target_voices, str):
680 speaker = target_voices
681
682 try:
683 voices_info, target_voice = resolve_speaker_id(
684 speaker,
685 voices_info=voices_info,
686 model=model,
687 )
688 except ValueError as exc:
689 print(exc)
690 return False, tmpfiles
691
692 print(f" {idx:04d}: voice={speaker} ({target_voice}): {text}")
693
694 generated = speak(
695 outfile=tmpfile,
696 text=text,
697 voice=target_voice,
698 speak_rate=_cfg_value(cfg, "fspeak_rate", None),
699 speak_pitch=_cfg_value(cfg, "fspeak_pitch", None),
700 caption=effective_caption,
701 ref_wav=effective_ref_wav,
702 ref_wavs=effective_ref_wavs,
703 model=model,
704 num_steps=num_steps,
705 cfg_scale_text=cfg_scale_text,
706 cfg_scale_caption=cfg_scale_caption,
707 cfg_scale_speaker=cfg_scale_speaker,
708 duration_scale=duration_scale,
709 seed=seed,
710 lora_adapter=lora_adapter,
711 )
712 if generated is None:
713 return False, tmpfiles
714
715 tmpfiles.append(tmpfile)
716 idx += 1
717
718 return True, tmpfiles
719
720
721if __name__ == "__main__":
722 list_available_voices()