Spaces:
Sleeping
Sleeping
cockolo terada
commited on
Update gradio_tabs/single.py
Browse files- gradio_tabs/single.py +34 -52
gradio_tabs/single.py
CHANGED
|
@@ -51,10 +51,8 @@ class TTSModelHolder:
|
|
| 51 |
p.mkdir(parents=True, exist_ok=True)
|
| 52 |
# 起動時に一度だけサンプルモデルを作成するロジック
|
| 53 |
if not any(p.iterdir()):
|
| 54 |
-
# ▼▼▼ 変更: printをENABLE_LOGGINGで制御 ▼▼▼
|
| 55 |
if ENABLE_LOGGING:
|
| 56 |
print("No models found in model_assets. Creating sample models...")
|
| 57 |
-
# ▲▲▲ 変更 ▲▲▲
|
| 58 |
# Sample Model 1
|
| 59 |
model1_path = p / "MyModel1"
|
| 60 |
model1_path.mkdir(parents=True, exist_ok=True)
|
|
@@ -82,10 +80,8 @@ class TTSModelHolder:
|
|
| 82 |
json.dump(style_settings_data, f, indent=2, ensure_ascii=False)
|
| 83 |
|
| 84 |
# FNモデル (FN1-10)
|
| 85 |
-
# ▼▼▼ 変更: printをENABLE_LOGGINGで制御 ▼▼▼
|
| 86 |
if ENABLE_LOGGING:
|
| 87 |
print("Creating FN models (FN1-10)...")
|
| 88 |
-
# ▲▲▲ 変更 ▲▲▲
|
| 89 |
for i in range(1, 11):
|
| 90 |
fn_path = p / f"FN{i}"
|
| 91 |
fn_path.mkdir(exist_ok=True)
|
|
@@ -94,10 +90,8 @@ class TTSModelHolder:
|
|
| 94 |
json.dump({"data": {"style2id": {"Neutral": 0}}}, f)
|
| 95 |
|
| 96 |
# whisperモデル (非表示用)
|
| 97 |
-
# ▼▼▼ 変更: printをENABLE_LOGGINGで制御 ▼▼▼
|
| 98 |
if ENABLE_LOGGING:
|
| 99 |
print("Creating 'whisper' model...")
|
| 100 |
-
# ▲▲▲ 変更 ▲▲▲
|
| 101 |
whisper_path = p / "whisper"
|
| 102 |
whisper_path.mkdir(exist_ok=True)
|
| 103 |
(whisper_path / "G_0.safetensors").touch()
|
|
@@ -111,33 +105,25 @@ class TTSModelHolder:
|
|
| 111 |
"""
|
| 112 |
if self.root_dir.is_dir():
|
| 113 |
self.model_names = sorted([d.name for d in self.root_dir.iterdir() if d.is_dir()])
|
| 114 |
-
# ▼▼▼ 変更: printをENABLE_LOGGINGで制御 ▼▼▼
|
| 115 |
if ENABLE_LOGGING:
|
| 116 |
print(f"TTSModelHolder model list refreshed. Known models: {self.model_names}")
|
| 117 |
-
# ▲▲▲ 変更 ▲▲▲
|
| 118 |
else:
|
| 119 |
self.model_names = []
|
| 120 |
-
# ▼▼▼ 変更: printをENABLE_LOGGINGで制御 ▼▼▼
|
| 121 |
if ENABLE_LOGGING:
|
| 122 |
print("TTSModelHolder root directory not found.")
|
| 123 |
-
# ▲▲▲ 変更 ▲▲▲
|
| 124 |
return self.model_names
|
| 125 |
|
| 126 |
def get_model(self, model_name, model_path):
|
| 127 |
-
# ▼▼▼ 変更: printをENABLE_LOGGINGで制御 ▼▼▼
|
| 128 |
if ENABLE_LOGGING:
|
| 129 |
print(f"Loading model: {model_name} (file: {Path(model_path).name})")
|
| 130 |
-
# ▲▲▲ 変更 ▲▲▲
|
| 131 |
if model_name not in self.model_names:
|
| 132 |
error_msg = (
|
| 133 |
f"Model '{model_name}' is not in the known list of TTSModelHolder. "
|
| 134 |
f"Current list: {self.model_names}. "
|
| 135 |
"Please refresh the model list by toggling the symlink checkbox or clicking the refresh button."
|
| 136 |
)
|
| 137 |
-
# ▼▼▼ 変更: printをエラーなので残すか、制御するか検討。ここでは制御対象に含める。▼▼▼
|
| 138 |
if ENABLE_LOGGING:
|
| 139 |
print(f"[ERROR] {error_msg}")
|
| 140 |
-
# ▲▲▲ 変更 ▲▲▲
|
| 141 |
raise ValueError(error_msg)
|
| 142 |
|
| 143 |
self.current_model = MockTTSModel()
|
|
@@ -149,10 +135,8 @@ class MockTTSModel:
|
|
| 149 |
|
| 150 |
def infer(self, text, **kwargs):
|
| 151 |
length_scale = kwargs.get('length', 1.0)
|
| 152 |
-
# ▼▼▼ 変更: printをENABLE_LOGGINGで制御 ▼▼▼
|
| 153 |
if ENABLE_LOGGING:
|
| 154 |
print(f"Inferencing with text '{text}' and style: {kwargs.get('style')} and weight: {kwargs.get('style_weight')}, length_scale: {length_scale}")
|
| 155 |
-
# ▲▲▲ 変更 ▲▲▲
|
| 156 |
sampling_rate = 44100
|
| 157 |
base_duration = max(1, len(text) // 5)
|
| 158 |
duration = base_duration * length_scale
|
|
@@ -237,10 +221,8 @@ def sort_models_by_custom_order(model_list: List[str], custom_order: List[str])
|
|
| 237 |
|
| 238 |
def set_random_seed(seed: int):
|
| 239 |
if seed >= 0:
|
| 240 |
-
# ▼▼▼ 変更: printをENABLE_LOGGINGで制御 ▼▼▼
|
| 241 |
if ENABLE_LOGGING:
|
| 242 |
print(f"Setting random seed to: {seed}")
|
| 243 |
-
# ▲▲▲ 変更 ▲▲▲
|
| 244 |
torch.manual_seed(seed)
|
| 245 |
if torch.cuda.is_available():
|
| 246 |
torch.cuda.manual_seed(seed)
|
|
@@ -423,19 +405,15 @@ def process_single_synthesis_webui(
|
|
| 423 |
current_model_file_path = Path(current_model_file_path_str)
|
| 424 |
log_messages = []
|
| 425 |
set_random_seed(seed_arg)
|
| 426 |
-
# ▼▼▼ 変更: ログ追加をENABLE_LOGGINGで制御 ▼▼▼
|
| 427 |
if seed_arg >= 0 and ENABLE_LOGGING:
|
| 428 |
log_messages.append(f"乱数シードを {seed_arg} に固定しました。")
|
| 429 |
-
# ▲▲▲ 変更 ▲▲▲
|
| 430 |
try:
|
| 431 |
model_holder_ref.get_model(current_model_name, current_model_file_path)
|
| 432 |
if model_holder_ref.current_model is None:
|
| 433 |
msg = f"モデルのロード失敗: {current_model_name} (ファイル: {current_model_file_path.name})"
|
| 434 |
log_messages.append(f"❌ [エラー] {msg}"); return False, log_messages, None
|
| 435 |
-
# ▼▼▼ 変更: ログ追加をENABLE_LOGGINGで制御 ▼▼▼
|
| 436 |
if ENABLE_LOGGING:
|
| 437 |
log_messages.append(f"使用モデル: {current_model_name} (ファイル: {current_model_file_path.name})")
|
| 438 |
-
# ▲▲▲ 変更 ▲▲▲
|
| 439 |
except Exception as e:
|
| 440 |
msg = f"モデルロードエラー '{current_model_name}' (ファイル: {current_model_file_path.name}): {e}"
|
| 441 |
log_messages.append(f"❌ [エラー] {msg}"); return False, log_messages, None
|
|
@@ -446,10 +424,8 @@ def process_single_synthesis_webui(
|
|
| 446 |
speaker_id = model_spk2id[speaker_name_arg]
|
| 447 |
elif model_spk2id:
|
| 448 |
speaker_id = list(model_spk2id.values())[0]
|
| 449 |
-
# ▼▼▼ 変更: ログ追加をENABLE_LOGGINGで制御 ▼▼▼
|
| 450 |
if ENABLE_LOGGING:
|
| 451 |
log_messages.append(f"音声合成中...")
|
| 452 |
-
# ▲▲▲ 変更 ▲▲▲
|
| 453 |
start_time_synth = datetime.datetime.now(JST)
|
| 454 |
try:
|
| 455 |
length_for_model = 1.0 / length_scale_arg if length_scale_arg != 0 else 1.0
|
|
@@ -467,10 +443,8 @@ def process_single_synthesis_webui(
|
|
| 467 |
except Exception as e:
|
| 468 |
msg = f"予期せぬエラー: {e}"; log_messages.append(f"❌ [エラー] {msg}"); return False, log_messages, None
|
| 469 |
duration_synth = (datetime.datetime.now(JST) - start_time_synth).total_seconds()
|
| 470 |
-
# ▼▼▼ 変更: ログ追加をENABLE_LOGGINGで制御 ▼▼▼
|
| 471 |
if ENABLE_LOGGING:
|
| 472 |
log_messages.append(f"音声合成成功。音声長: {len(audio_data)/sr:.2f}s, 処理時間: {duration_synth:.2f}s.")
|
| 473 |
-
# ▲▲▲ 変更 ▲▲▲
|
| 474 |
return True, log_messages, (sr, audio_data)
|
| 475 |
|
| 476 |
|
|
@@ -679,7 +653,7 @@ def create_synthesis_app(model_holder: TTSModelHolder) -> gr.Blocks:
|
|
| 679 |
all_workbench_ui_components.extend([item["container"], item["item_num_display"], item["audio"], item["download"], item["info"]])
|
| 680 |
|
| 681 |
|
| 682 |
-
# --- UIイベントハンドラ関数 (
|
| 683 |
def load_styles_for_ui(selected_model_name: Optional[str]):
|
| 684 |
if not selected_model_name: return gr.update(choices=[], value=None), gr.update(value=DEFAULT_STYLE_WEIGHT), {}
|
| 685 |
model_path = assets_root_path / selected_model_name
|
|
@@ -695,6 +669,7 @@ def create_synthesis_app(model_holder: TTSModelHolder) -> gr.Blocks:
|
|
| 695 |
default_weight = styles_map[first_key].get("weight", DEFAULT_STYLE_WEIGHT)
|
| 696 |
return gr.update(choices=display_names, value=default_display_name), gr.update(value=default_weight), styles_map
|
| 697 |
|
|
|
|
| 698 |
def action_refresh_model_list(use_fn_model_mode: bool, use_symlink_mode: bool):
|
| 699 |
"""モデルリストを再読み込みし、UIとバックエンドの状態を同期させる。"""
|
| 700 |
MERGER_CACHE_PATH = Path("/tmp/sbv2_merger_cache")
|
|
@@ -731,20 +706,16 @@ def create_synthesis_app(model_holder: TTSModelHolder) -> gr.Blocks:
|
|
| 731 |
fn_model_pattern = re.compile(r'^FN([1-9]|10)$')
|
| 732 |
current_available_models = model_holder.model_names
|
| 733 |
|
| 734 |
-
|
| 735 |
-
|
| 736 |
|
| 737 |
if use_fn_model_mode:
|
| 738 |
ui_model_list = [name for name in current_available_models if fn_model_pattern.match(name)]
|
| 739 |
-
|
| 740 |
-
value = sorted_list[0] if sorted_list else None
|
| 741 |
-
model_dropdown_update = gr.update(choices=sorted_list, value=value)
|
| 742 |
|
| 743 |
elif use_symlink_mode:
|
| 744 |
ui_model_list_names = [p.name for p in assets_root_path.iterdir() if p.is_symlink()]
|
| 745 |
-
|
| 746 |
-
value = formatted_choices[0][1] if formatted_choices else None
|
| 747 |
-
model_dropdown_update = gr.update(choices=formatted_choices, value=value)
|
| 748 |
|
| 749 |
else:
|
| 750 |
ui_model_list = [
|
|
@@ -753,13 +724,32 @@ def create_synthesis_app(model_holder: TTSModelHolder) -> gr.Blocks:
|
|
| 753 |
and not fn_model_pattern.match(name)
|
| 754 |
and not (assets_root_path / name).is_symlink()
|
| 755 |
]
|
| 756 |
-
|
| 757 |
-
|
| 758 |
-
|
| 759 |
-
|
| 760 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 761 |
|
| 762 |
return model_dropdown_update, style_dropdown_update, style_weight_update, styles_data_state_update
|
|
|
|
| 763 |
|
| 764 |
def on_model_select_change(selected_model_name: Optional[str]):
|
| 765 |
style_dropdown_update, style_weight_update, styles_data_state_update = load_styles_for_ui(selected_model_name)
|
|
@@ -803,8 +793,8 @@ def create_synthesis_app(model_holder: TTSModelHolder) -> gr.Blocks:
|
|
| 803 |
found_chars = "".join(sorted(list(set(re.findall(INVALID_FILENAME_CHARS_PATTERN, text)))))
|
| 804 |
error_outputs[0] = f"❌ [エラー] テキストに使用できない文字が含まれています: {found_chars}"
|
| 805 |
return tuple(error_outputs)
|
| 806 |
-
if not model_name:
|
| 807 |
-
error_outputs[0] = "❌ [エラー]
|
| 808 |
return tuple(error_outputs)
|
| 809 |
if not text.strip():
|
| 810 |
error_outputs[0] = "❌ [エラー] テキストが入力されていません。"
|
|
@@ -867,9 +857,9 @@ def create_synthesis_app(model_holder: TTSModelHolder) -> gr.Blocks:
|
|
| 867 |
ratio_list = [0.5]
|
| 868 |
all_logs.append("⚠️ [警告] カタカナ化の割合の解析に失敗したため、0.5 を使用します。")
|
| 869 |
|
|
|
|
| 870 |
if ENABLE_LOGGING:
|
| 871 |
all_logs.append(f"--- 発音ガチャ2 モード (pyopenjtalk) ---")
|
| 872 |
-
internal_mode = int(random_text_mode) + 1
|
| 873 |
all_logs.append(f"粒度: {random_text_mode} (内部モード: {internal_mode}), カタカナ化割合候補: {ratio_list}")
|
| 874 |
|
| 875 |
generated_variations: Dict[str, List[str]] = {}
|
|
@@ -939,14 +929,12 @@ def create_synthesis_app(model_holder: TTSModelHolder) -> gr.Blocks:
|
|
| 939 |
|
| 940 |
final_outputs = []
|
| 941 |
|
| 942 |
-
# ▼▼▼ 変更: ログ表示をENABLE_LOGGINGで制御 ▼▼▼
|
| 943 |
if ENABLE_LOGGING:
|
| 944 |
status_message = "\n".join(all_logs)
|
| 945 |
else:
|
| 946 |
essential_logs = [log for log in all_logs if any(prefix in log for prefix in ["✅", "❌", "⚠️", "ℹ️"])]
|
| 947 |
status_message = "\n".join(essential_logs)
|
| 948 |
final_outputs.append(status_message)
|
| 949 |
-
# ▲▲▲ 変更 ▲▲▲
|
| 950 |
|
| 951 |
final_outputs.append(gr.update(visible=num_generated > 0))
|
| 952 |
|
|
@@ -1008,13 +996,11 @@ def create_synthesis_app(model_holder: TTSModelHolder) -> gr.Blocks:
|
|
| 1008 |
|
| 1009 |
ui_updates = update_workbench_ui(updated_list)
|
| 1010 |
log_messages.append("✅ キープに音声を追加しました。")
|
| 1011 |
-
# ▼▼▼ 変更: ログ表示をENABLE_LOGGINGで制御 ▼▼▼
|
| 1012 |
if ENABLE_LOGGING:
|
| 1013 |
final_status = (current_status + "\n" + "\n".join(log_messages)).strip()
|
| 1014 |
else:
|
| 1015 |
essential_logs = [log for log in log_messages if any(prefix in log for prefix in ["✅", "❌", "⚠️", "ℹ️"])]
|
| 1016 |
final_status = "\n".join(essential_logs).strip()
|
| 1017 |
-
# ▲▲▲ 変更 ▲▲▲
|
| 1018 |
return (final_status, updated_list) + ui_updates
|
| 1019 |
|
| 1020 |
def remove_from_workbench(current_status: str, index_to_remove: int, current_workbench_list: List[Dict]) -> Tuple:
|
|
@@ -1042,13 +1028,11 @@ def create_synthesis_app(model_holder: TTSModelHolder) -> gr.Blocks:
|
|
| 1042 |
|
| 1043 |
updated_list = [item for i, item in enumerate(safe_workbench_list) if i != index_to_remove]
|
| 1044 |
ui_updates = update_workbench_ui(updated_list)
|
| 1045 |
-
# ▼▼▼ 変更: ログ表示をENABLE_LOGGINGで制御 ▼▼▼
|
| 1046 |
if ENABLE_LOGGING:
|
| 1047 |
final_status = (current_status + "\n" + "\n".join(log_messages)).strip()
|
| 1048 |
else:
|
| 1049 |
essential_logs = [log for log in log_messages if any(prefix in log for prefix in ["✅", "❌", "⚠️", "ℹ️"])]
|
| 1050 |
final_status = "\n".join(essential_logs).strip()
|
| 1051 |
-
# ▲▲▲ 変更 ▲▲▲
|
| 1052 |
return (final_status, updated_list) + ui_updates
|
| 1053 |
|
| 1054 |
def action_merge_preview(current_status: str, first_audio_num: int, second_audio_num: int, pause_ms: int, workbench_list: List[Dict], progress=gr.Progress(track_tqdm=True)):
|
|
@@ -1187,7 +1171,7 @@ def create_synthesis_app(model_holder: TTSModelHolder) -> gr.Blocks:
|
|
| 1187 |
return (final_status, final_workbench_list) + ui_updates
|
| 1188 |
|
| 1189 |
|
| 1190 |
-
# --- イベントリスナー接続
|
| 1191 |
def on_fn_mode_change(is_fn_mode_on: bool) -> gr.Checkbox:
|
| 1192 |
if is_fn_mode_on: return gr.update(value=False)
|
| 1193 |
return gr.update()
|
|
@@ -1289,10 +1273,8 @@ if __name__ == "__main__":
|
|
| 1289 |
|
| 1290 |
merger_cache_path = Path("/tmp/sbv2_merger_cache")
|
| 1291 |
mock_model_holder = TTSModelHolder()
|
| 1292 |
-
# ▼▼▼ 変更: printをENABLE_LOGGINGで制御 ▼▼▼
|
| 1293 |
if ENABLE_LOGGING:
|
| 1294 |
print(f"Initial models loaded by TTSModelHolder: {mock_model_holder.model_names}")
|
| 1295 |
-
# ▲▲▲ 変更 ▲▲▲
|
| 1296 |
|
| 1297 |
app = create_synthesis_app(mock_model_holder)
|
| 1298 |
|
|
|
|
| 51 |
p.mkdir(parents=True, exist_ok=True)
|
| 52 |
# 起動時に一度だけサンプルモデルを作成するロジック
|
| 53 |
if not any(p.iterdir()):
|
|
|
|
| 54 |
if ENABLE_LOGGING:
|
| 55 |
print("No models found in model_assets. Creating sample models...")
|
|
|
|
| 56 |
# Sample Model 1
|
| 57 |
model1_path = p / "MyModel1"
|
| 58 |
model1_path.mkdir(parents=True, exist_ok=True)
|
|
|
|
| 80 |
json.dump(style_settings_data, f, indent=2, ensure_ascii=False)
|
| 81 |
|
| 82 |
# FNモデル (FN1-10)
|
|
|
|
| 83 |
if ENABLE_LOGGING:
|
| 84 |
print("Creating FN models (FN1-10)...")
|
|
|
|
| 85 |
for i in range(1, 11):
|
| 86 |
fn_path = p / f"FN{i}"
|
| 87 |
fn_path.mkdir(exist_ok=True)
|
|
|
|
| 90 |
json.dump({"data": {"style2id": {"Neutral": 0}}}, f)
|
| 91 |
|
| 92 |
# whisperモデル (非表示用)
|
|
|
|
| 93 |
if ENABLE_LOGGING:
|
| 94 |
print("Creating 'whisper' model...")
|
|
|
|
| 95 |
whisper_path = p / "whisper"
|
| 96 |
whisper_path.mkdir(exist_ok=True)
|
| 97 |
(whisper_path / "G_0.safetensors").touch()
|
|
|
|
| 105 |
"""
|
| 106 |
if self.root_dir.is_dir():
|
| 107 |
self.model_names = sorted([d.name for d in self.root_dir.iterdir() if d.is_dir()])
|
|
|
|
| 108 |
if ENABLE_LOGGING:
|
| 109 |
print(f"TTSModelHolder model list refreshed. Known models: {self.model_names}")
|
|
|
|
| 110 |
else:
|
| 111 |
self.model_names = []
|
|
|
|
| 112 |
if ENABLE_LOGGING:
|
| 113 |
print("TTSModelHolder root directory not found.")
|
|
|
|
| 114 |
return self.model_names
|
| 115 |
|
| 116 |
def get_model(self, model_name, model_path):
|
|
|
|
| 117 |
if ENABLE_LOGGING:
|
| 118 |
print(f"Loading model: {model_name} (file: {Path(model_path).name})")
|
|
|
|
| 119 |
if model_name not in self.model_names:
|
| 120 |
error_msg = (
|
| 121 |
f"Model '{model_name}' is not in the known list of TTSModelHolder. "
|
| 122 |
f"Current list: {self.model_names}. "
|
| 123 |
"Please refresh the model list by toggling the symlink checkbox or clicking the refresh button."
|
| 124 |
)
|
|
|
|
| 125 |
if ENABLE_LOGGING:
|
| 126 |
print(f"[ERROR] {error_msg}")
|
|
|
|
| 127 |
raise ValueError(error_msg)
|
| 128 |
|
| 129 |
self.current_model = MockTTSModel()
|
|
|
|
| 135 |
|
| 136 |
def infer(self, text, **kwargs):
|
| 137 |
length_scale = kwargs.get('length', 1.0)
|
|
|
|
| 138 |
if ENABLE_LOGGING:
|
| 139 |
print(f"Inferencing with text '{text}' and style: {kwargs.get('style')} and weight: {kwargs.get('style_weight')}, length_scale: {length_scale}")
|
|
|
|
| 140 |
sampling_rate = 44100
|
| 141 |
base_duration = max(1, len(text) // 5)
|
| 142 |
duration = base_duration * length_scale
|
|
|
|
| 221 |
|
| 222 |
def set_random_seed(seed: int):
|
| 223 |
if seed >= 0:
|
|
|
|
| 224 |
if ENABLE_LOGGING:
|
| 225 |
print(f"Setting random seed to: {seed}")
|
|
|
|
| 226 |
torch.manual_seed(seed)
|
| 227 |
if torch.cuda.is_available():
|
| 228 |
torch.cuda.manual_seed(seed)
|
|
|
|
| 405 |
current_model_file_path = Path(current_model_file_path_str)
|
| 406 |
log_messages = []
|
| 407 |
set_random_seed(seed_arg)
|
|
|
|
| 408 |
if seed_arg >= 0 and ENABLE_LOGGING:
|
| 409 |
log_messages.append(f"乱数シードを {seed_arg} に固定しました。")
|
|
|
|
| 410 |
try:
|
| 411 |
model_holder_ref.get_model(current_model_name, current_model_file_path)
|
| 412 |
if model_holder_ref.current_model is None:
|
| 413 |
msg = f"モデルのロード失敗: {current_model_name} (ファイル: {current_model_file_path.name})"
|
| 414 |
log_messages.append(f"❌ [エラー] {msg}"); return False, log_messages, None
|
|
|
|
| 415 |
if ENABLE_LOGGING:
|
| 416 |
log_messages.append(f"使用モデル: {current_model_name} (ファイル: {current_model_file_path.name})")
|
|
|
|
| 417 |
except Exception as e:
|
| 418 |
msg = f"モデルロードエラー '{current_model_name}' (ファイル: {current_model_file_path.name}): {e}"
|
| 419 |
log_messages.append(f"❌ [エラー] {msg}"); return False, log_messages, None
|
|
|
|
| 424 |
speaker_id = model_spk2id[speaker_name_arg]
|
| 425 |
elif model_spk2id:
|
| 426 |
speaker_id = list(model_spk2id.values())[0]
|
|
|
|
| 427 |
if ENABLE_LOGGING:
|
| 428 |
log_messages.append(f"音声合成中...")
|
|
|
|
| 429 |
start_time_synth = datetime.datetime.now(JST)
|
| 430 |
try:
|
| 431 |
length_for_model = 1.0 / length_scale_arg if length_scale_arg != 0 else 1.0
|
|
|
|
| 443 |
except Exception as e:
|
| 444 |
msg = f"予期せぬエラー: {e}"; log_messages.append(f"❌ [エラー] {msg}"); return False, log_messages, None
|
| 445 |
duration_synth = (datetime.datetime.now(JST) - start_time_synth).total_seconds()
|
|
|
|
| 446 |
if ENABLE_LOGGING:
|
| 447 |
log_messages.append(f"音声合成成功。音声長: {len(audio_data)/sr:.2f}s, 処理時間: {duration_synth:.2f}s.")
|
|
|
|
| 448 |
return True, log_messages, (sr, audio_data)
|
| 449 |
|
| 450 |
|
|
|
|
| 653 |
all_workbench_ui_components.extend([item["container"], item["item_num_display"], item["audio"], item["download"], item["info"]])
|
| 654 |
|
| 655 |
|
| 656 |
+
# --- UIイベントハンドラ関数 (action_refresh_model_list を修正) ---
|
| 657 |
def load_styles_for_ui(selected_model_name: Optional[str]):
|
| 658 |
if not selected_model_name: return gr.update(choices=[], value=None), gr.update(value=DEFAULT_STYLE_WEIGHT), {}
|
| 659 |
model_path = assets_root_path / selected_model_name
|
|
|
|
| 669 |
default_weight = styles_map[first_key].get("weight", DEFAULT_STYLE_WEIGHT)
|
| 670 |
return gr.update(choices=display_names, value=default_display_name), gr.update(value=default_weight), styles_map
|
| 671 |
|
| 672 |
+
# ▼▼▼ 修正: この関数を堅牢化してエラーを防ぐ ▼▼▼
|
| 673 |
def action_refresh_model_list(use_fn_model_mode: bool, use_symlink_mode: bool):
|
| 674 |
"""モデルリストを再読み込みし、UIとバックエンドの状態を同期させる。"""
|
| 675 |
MERGER_CACHE_PATH = Path("/tmp/sbv2_merger_cache")
|
|
|
|
| 706 |
fn_model_pattern = re.compile(r'^FN([1-9]|10)$')
|
| 707 |
current_available_models = model_holder.model_names
|
| 708 |
|
| 709 |
+
final_choices = []
|
| 710 |
+
final_value_for_style_load = None
|
| 711 |
|
| 712 |
if use_fn_model_mode:
|
| 713 |
ui_model_list = [name for name in current_available_models if fn_model_pattern.match(name)]
|
| 714 |
+
final_choices = sort_models_by_custom_order(ui_model_list, FN_MODE_MODEL_ORDER)
|
|
|
|
|
|
|
| 715 |
|
| 716 |
elif use_symlink_mode:
|
| 717 |
ui_model_list_names = [p.name for p in assets_root_path.iterdir() if p.is_symlink()]
|
| 718 |
+
final_choices = format_and_sort_model_names(ui_model_list_names)
|
|
|
|
|
|
|
| 719 |
|
| 720 |
else:
|
| 721 |
ui_model_list = [
|
|
|
|
| 724 |
and not fn_model_pattern.match(name)
|
| 725 |
and not (assets_root_path / name).is_symlink()
|
| 726 |
]
|
| 727 |
+
final_choices = sort_models_by_custom_order(ui_model_list, NORMAL_MODE_MODEL_ORDER)
|
| 728 |
+
|
| 729 |
+
if not final_choices:
|
| 730 |
+
# 選択肢が空の場���、エラーを防ぐためにダミー項目を設定し、ドロップダウンを無効化
|
| 731 |
+
model_dropdown_update = gr.update(
|
| 732 |
+
choices=["(利用可能なモデルがありません)"],
|
| 733 |
+
value="(利用可能なモデルがありません)",
|
| 734 |
+
interactive=False
|
| 735 |
+
)
|
| 736 |
+
final_value_for_style_load = None
|
| 737 |
+
else:
|
| 738 |
+
# 選択肢がある場合、通常通り設定
|
| 739 |
+
is_tuple_choices = isinstance(final_choices[0], tuple)
|
| 740 |
+
actual_value = final_choices[0][1] if is_tuple_choices else final_choices[0]
|
| 741 |
+
|
| 742 |
+
model_dropdown_update = gr.update(
|
| 743 |
+
choices=final_choices,
|
| 744 |
+
value=actual_value,
|
| 745 |
+
interactive=True
|
| 746 |
+
)
|
| 747 |
+
final_value_for_style_load = actual_value
|
| 748 |
+
|
| 749 |
+
style_dropdown_update, style_weight_update, styles_data_state_update = load_styles_for_ui(final_value_for_style_load)
|
| 750 |
|
| 751 |
return model_dropdown_update, style_dropdown_update, style_weight_update, styles_data_state_update
|
| 752 |
+
# ▲▲▲ 修正 ▲▲▲
|
| 753 |
|
| 754 |
def on_model_select_change(selected_model_name: Optional[str]):
|
| 755 |
style_dropdown_update, style_weight_update, styles_data_state_update = load_styles_for_ui(selected_model_name)
|
|
|
|
| 793 |
found_chars = "".join(sorted(list(set(re.findall(INVALID_FILENAME_CHARS_PATTERN, text)))))
|
| 794 |
error_outputs[0] = f"❌ [エラー] テキストに使用できない文字が含まれています: {found_chars}"
|
| 795 |
return tuple(error_outputs)
|
| 796 |
+
if not model_name or model_name == "(利用可能なモデルがありません)": # ダミー項目もチェック
|
| 797 |
+
error_outputs[0] = "❌ [エラー] モデルが選択されていません。"
|
| 798 |
return tuple(error_outputs)
|
| 799 |
if not text.strip():
|
| 800 |
error_outputs[0] = "❌ [エラー] テキストが入力されていません。"
|
|
|
|
| 857 |
ratio_list = [0.5]
|
| 858 |
all_logs.append("⚠️ [警告] カタカナ化の割合の解析に失敗したため、0.5 を使用します。")
|
| 859 |
|
| 860 |
+
internal_mode = int(random_text_mode) + 1
|
| 861 |
if ENABLE_LOGGING:
|
| 862 |
all_logs.append(f"--- 発音ガチャ2 モード (pyopenjtalk) ---")
|
|
|
|
| 863 |
all_logs.append(f"粒度: {random_text_mode} (内部モード: {internal_mode}), カタカナ化割合候補: {ratio_list}")
|
| 864 |
|
| 865 |
generated_variations: Dict[str, List[str]] = {}
|
|
|
|
| 929 |
|
| 930 |
final_outputs = []
|
| 931 |
|
|
|
|
| 932 |
if ENABLE_LOGGING:
|
| 933 |
status_message = "\n".join(all_logs)
|
| 934 |
else:
|
| 935 |
essential_logs = [log for log in all_logs if any(prefix in log for prefix in ["✅", "❌", "⚠️", "ℹ️"])]
|
| 936 |
status_message = "\n".join(essential_logs)
|
| 937 |
final_outputs.append(status_message)
|
|
|
|
| 938 |
|
| 939 |
final_outputs.append(gr.update(visible=num_generated > 0))
|
| 940 |
|
|
|
|
| 996 |
|
| 997 |
ui_updates = update_workbench_ui(updated_list)
|
| 998 |
log_messages.append("✅ キープに音声を追加しました。")
|
|
|
|
| 999 |
if ENABLE_LOGGING:
|
| 1000 |
final_status = (current_status + "\n" + "\n".join(log_messages)).strip()
|
| 1001 |
else:
|
| 1002 |
essential_logs = [log for log in log_messages if any(prefix in log for prefix in ["✅", "❌", "⚠️", "ℹ️"])]
|
| 1003 |
final_status = "\n".join(essential_logs).strip()
|
|
|
|
| 1004 |
return (final_status, updated_list) + ui_updates
|
| 1005 |
|
| 1006 |
def remove_from_workbench(current_status: str, index_to_remove: int, current_workbench_list: List[Dict]) -> Tuple:
|
|
|
|
| 1028 |
|
| 1029 |
updated_list = [item for i, item in enumerate(safe_workbench_list) if i != index_to_remove]
|
| 1030 |
ui_updates = update_workbench_ui(updated_list)
|
|
|
|
| 1031 |
if ENABLE_LOGGING:
|
| 1032 |
final_status = (current_status + "\n" + "\n".join(log_messages)).strip()
|
| 1033 |
else:
|
| 1034 |
essential_logs = [log for log in log_messages if any(prefix in log for prefix in ["✅", "❌", "⚠️", "ℹ️"])]
|
| 1035 |
final_status = "\n".join(essential_logs).strip()
|
|
|
|
| 1036 |
return (final_status, updated_list) + ui_updates
|
| 1037 |
|
| 1038 |
def action_merge_preview(current_status: str, first_audio_num: int, second_audio_num: int, pause_ms: int, workbench_list: List[Dict], progress=gr.Progress(track_tqdm=True)):
|
|
|
|
| 1171 |
return (final_status, final_workbench_list) + ui_updates
|
| 1172 |
|
| 1173 |
|
| 1174 |
+
# --- イベントリスナー接続 ---
|
| 1175 |
def on_fn_mode_change(is_fn_mode_on: bool) -> gr.Checkbox:
|
| 1176 |
if is_fn_mode_on: return gr.update(value=False)
|
| 1177 |
return gr.update()
|
|
|
|
| 1273 |
|
| 1274 |
merger_cache_path = Path("/tmp/sbv2_merger_cache")
|
| 1275 |
mock_model_holder = TTSModelHolder()
|
|
|
|
| 1276 |
if ENABLE_LOGGING:
|
| 1277 |
print(f"Initial models loaded by TTSModelHolder: {mock_model_holder.model_names}")
|
|
|
|
| 1278 |
|
| 1279 |
app = create_synthesis_app(mock_model_holder)
|
| 1280 |
|