AbeShinzo0708 commited on
Commit
cdcae8c
1 Parent(s): 41abb89

Upload app.py

Browse files
Files changed (1) hide show
  1. app.py +421 -0
app.py ADDED
@@ -0,0 +1,421 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import argparse
2
+ import datetime
3
+ import json
4
+ import os
5
+ import sys
6
+ from typing import Optional
7
+
8
+ import gradio as gr
9
+ import torch
10
+ import yaml
11
+
12
+ from common.constants import (
13
+ DEFAULT_ASSIST_TEXT_WEIGHT,
14
+ DEFAULT_LENGTH,
15
+ DEFAULT_LINE_SPLIT,
16
+ DEFAULT_NOISE,
17
+ DEFAULT_NOISEW,
18
+ DEFAULT_SDP_RATIO,
19
+ DEFAULT_SPLIT_INTERVAL,
20
+ DEFAULT_STYLE,
21
+ DEFAULT_STYLE_WEIGHT,
22
+ Languages,
23
+ )
24
+ from common.log import logger
25
+ from common.tts_model import ModelHolder
26
+ from infer import InvalidToneError
27
+ from text.japanese import g2kata_tone, kata_tone2phone_tone, text_normalize
28
+
29
+ is_hf_spaces = os.getenv("SYSTEM") == "spaces"
30
+ limit = 100
31
+
32
+ # Get path settings
33
+ with open(os.path.join("configs", "paths.yml"), "r", encoding="utf-8") as f:
34
+ path_config: dict[str, str] = yaml.safe_load(f.read())
35
+ # dataset_root = path_config["dataset_root"]
36
+ assets_root = path_config["assets_root"]
37
+
38
+ languages = [l.value for l in Languages]
39
+
40
+
41
+ def tts_fn(
42
+ model_name,
43
+ model_path,
44
+ text,
45
+ language,
46
+ reference_audio_path,
47
+ sdp_ratio,
48
+ noise_scale,
49
+ noise_scale_w,
50
+ length_scale,
51
+ line_split,
52
+ split_interval,
53
+ assist_text,
54
+ assist_text_weight,
55
+ use_assist_text,
56
+ style,
57
+ style_weight,
58
+ kata_tone_json_str,
59
+ use_tone,
60
+ speaker,
61
+ ):
62
+ if is_hf_spaces and len(text) > limit:
63
+ logger.error(f"Text is too long: {len(text)}")
64
+ return (
65
+ f"Error: 文字数が多すぎます({limit}文字以下にしてください)",
66
+ None,
67
+ kata_tone_json_str,
68
+ )
69
+ model_holder.load_model_gr(model_name, model_path)
70
+
71
+ wrong_tone_message = ""
72
+ kata_tone: Optional[list[tuple[str, int]]] = None
73
+ if use_tone and kata_tone_json_str != "":
74
+ if language != "JP":
75
+ logger.warning("Only Japanese is supported for tone generation.")
76
+ wrong_tone_message = "アクセント指定は現在日本語のみ対応しています。"
77
+ if line_split:
78
+ logger.warning("Tone generation is not supported for line split.")
79
+ wrong_tone_message = (
80
+ "アクセント指定は改行で分けて生成を使わない場合のみ対応しています。"
81
+ )
82
+ try:
83
+ kata_tone = []
84
+ json_data = json.loads(kata_tone_json_str)
85
+ # tupleを使うように変換
86
+ for kana, tone in json_data:
87
+ assert isinstance(kana, str) and tone in (0, 1), f"{kana}, {tone}"
88
+ kata_tone.append((kana, tone))
89
+ except Exception as e:
90
+ logger.warning(f"Error occurred when parsing kana_tone_json: {e}")
91
+ wrong_tone_message = f"アクセント指定が不正です: {e}"
92
+ kata_tone = None
93
+
94
+ # toneは実際に音声合成に代入される際のみnot Noneになる
95
+ tone: Optional[list[int]] = None
96
+ if kata_tone is not None:
97
+ phone_tone = kata_tone2phone_tone(kata_tone)
98
+ tone = [t for _, t in phone_tone]
99
+
100
+ speaker_id = model_holder.current_model.spk2id[speaker]
101
+
102
+ start_time = datetime.datetime.now()
103
+
104
+ try:
105
+ sr, audio = model_holder.current_model.infer(
106
+ text=text,
107
+ language=language,
108
+ reference_audio_path=reference_audio_path,
109
+ sdp_ratio=sdp_ratio,
110
+ noise=noise_scale,
111
+ noisew=noise_scale_w,
112
+ length=length_scale,
113
+ line_split=line_split,
114
+ split_interval=split_interval,
115
+ assist_text=assist_text,
116
+ assist_text_weight=assist_text_weight,
117
+ use_assist_text=use_assist_text,
118
+ style=style,
119
+ style_weight=style_weight,
120
+ given_tone=tone,
121
+ sid=speaker_id,
122
+ )
123
+ except InvalidToneError as e:
124
+ logger.error(f"Tone error: {e}")
125
+ return f"Error: アクセント指定が不正です:\n{e}", None, kata_tone_json_str
126
+ except ValueError as e:
127
+ logger.error(f"Value error: {e}")
128
+ return f"Error: {e}", None, kata_tone_json_str
129
+
130
+ end_time = datetime.datetime.now()
131
+ duration = (end_time - start_time).total_seconds()
132
+
133
+ if tone is None and language == "JP":
134
+ # アクセント指定に使えるようにアクセント情報を返す
135
+ norm_text = text_normalize(text)
136
+ kata_tone = g2kata_tone(norm_text)
137
+ kata_tone_json_str = json.dumps(kata_tone, ensure_ascii=False)
138
+ elif tone is None:
139
+ kata_tone_json_str = ""
140
+ message = f"Success, time: {duration} seconds."
141
+ if wrong_tone_message != "":
142
+ message = wrong_tone_message + "\n" + message
143
+ return message, (sr, audio), kata_tone_json_str
144
+
145
+
146
+ initial_text = "私や妻が関係していたということになれば総理大臣も国会議員も辞める"
147
+
148
+ example_hf_spaces = [
149
+ [initial_text, "JP"],
150
+ ["えっと、私、あなたのことが好きです!もしよければ付き合ってくれませんか?", "JP"],
151
+ ["吾��は猫である。名前はまだ無い。", "JP"],
152
+ ["桜の樹の下には屍体が埋まっている!これは信じていいことなんだよ。", "JP"],
153
+ ["やったー!テストで満点取れたよ!私とっても嬉しいな!", "JP"],
154
+ [
155
+ "どうして私の意見を無視するの?許せない!ムカつく!あんたなんか死ねばいいのに。",
156
+ "JP",
157
+ ],
158
+ ["あはははっ!この漫画めっちゃ笑える、見てよこれ、ふふふ、あはは。", "JP"],
159
+ [
160
+ "あなたがいなくなって、私は一人になっちゃって、泣いちゃいそうなほど悲しい。",
161
+ "JP",
162
+ ],
163
+ [
164
+ "深層学習の応用により、感情やアクセントを含む声質の微妙な変化も再現されている。",
165
+ "JP",
166
+ ],
167
+ ]
168
+ initial_md = """
169
+ # おしゃべり安倍晋三メーカー
170
+ 安倍晋三にあんなことやこんなことを喋らせよう
171
+ """
172
+
173
+ style_md = f"""
174
+ - プリセットまたは音声ファイルから読み上げの声音・感情・スタイルのようなものを制御できます。
175
+ - デフォルトの{DEFAULT_STYLE}でも、十分に読み上げる文に応じた感情で感情豊かに読み上げられます。このスタイル制御は、それを重み付きで上書きするような感じです。
176
+ - 強さを大きくしすぎると発音が変になったり声にならなかったりと崩壊することがあります。
177
+ - どのくらいに強さがいいかはモデルやスタイルによって異なるようです。
178
+ - 音声ファイルを入力する場合は、学習データと似た声音の話者(特に同じ性別)でないとよい効果が出ないかもしれません。
179
+ """
180
+
181
+
182
+ def make_interactive():
183
+ return gr.update(interactive=True, value="音声合成")
184
+
185
+
186
+ def make_non_interactive():
187
+ return gr.update(interactive=False, value="音声合成(モデルをロードしてください)")
188
+
189
+
190
+ def gr_util(item):
191
+ if item == "プリセットから選ぶ":
192
+ return (gr.update(visible=True), gr.Audio(visible=False, value=None))
193
+ else:
194
+ return (gr.update(visible=False), gr.update(visible=True))
195
+
196
+
197
+ if __name__ == "__main__":
198
+ parser = argparse.ArgumentParser()
199
+ parser.add_argument("--cpu", action="store_true", help="Use CPU instead of GPU")
200
+ parser.add_argument(
201
+ "--dir", "-d", type=str, help="Model directory", default=assets_root
202
+ )
203
+ parser.add_argument(
204
+ "--share", action="store_true", help="Share this app publicly", default=False
205
+ )
206
+ parser.add_argument(
207
+ "--server-name",
208
+ type=str,
209
+ default=None,
210
+ help="Server name for Gradio app",
211
+ )
212
+ parser.add_argument(
213
+ "--no-autolaunch",
214
+ action="store_true",
215
+ default=False,
216
+ help="Do not launch app automatically",
217
+ )
218
+ args = parser.parse_args()
219
+ model_dir = args.dir
220
+
221
+ if args.cpu:
222
+ device = "cpu"
223
+ else:
224
+ device = "cuda" if torch.cuda.is_available() else "cpu"
225
+
226
+ model_holder = ModelHolder(model_dir, device)
227
+
228
+ model_names = model_holder.model_names
229
+ if len(model_names) == 0:
230
+ logger.error(
231
+ f"モデルが見つかりませんでした。{model_dir}にモデルを置いてください。"
232
+ )
233
+ sys.exit(1)
234
+ initial_id = 0
235
+ initial_pth_files = model_holder.model_files_dict[model_names[initial_id]]
236
+
237
+ with gr.Blocks() as app:
238
+ gr.Markdown(initial_md)
239
+ with gr.Row():
240
+ with gr.Column():
241
+ with gr.Row():
242
+ with gr.Column(scale=3):
243
+ model_name = gr.Dropdown(
244
+ label="モデル一覧",
245
+ choices=model_names,
246
+ value=model_names[initial_id],
247
+ )
248
+ model_path = gr.Dropdown(
249
+ label="モデルファイル",
250
+ choices=initial_pth_files,
251
+ value=initial_pth_files[0],
252
+ )
253
+ refresh_button = gr.Button("更新", scale=1, visible=False)
254
+ load_button = gr.Button("ロード", scale=1, variant="primary")
255
+ text_input = gr.TextArea(label="テキスト", value=initial_text)
256
+
257
+ line_split = gr.Checkbox(
258
+ label="改行で分けて生成(分けたほうが感情が乗ります)",
259
+ value=DEFAULT_LINE_SPLIT,
260
+ )
261
+ split_interval = gr.Slider(
262
+ minimum=0.0,
263
+ maximum=2,
264
+ value=DEFAULT_SPLIT_INTERVAL,
265
+ step=0.1,
266
+ label="改行ごとに挟む無音の長さ(秒)",
267
+ )
268
+ line_split.change(
269
+ lambda x: (gr.Slider(visible=x)),
270
+ inputs=[line_split],
271
+ outputs=[split_interval],
272
+ )
273
+ tone = gr.Textbox(
274
+ label="アクセント調整(数値は 0=低 か1=高 のみ)",
275
+ info="改行で分けない場合のみ使えます。万能ではありません。",
276
+ )
277
+ use_tone = gr.Checkbox(label="アクセント調整を使う", value=False)
278
+ use_tone.change(
279
+ lambda x: (gr.Checkbox(value=False) if x else gr.Checkbox()),
280
+ inputs=[use_tone],
281
+ outputs=[line_split],
282
+ )
283
+ language = gr.Dropdown(choices=["JP"], value="JP", label="Language")
284
+ speaker = gr.Dropdown(label="話者")
285
+ with gr.Accordion(label="詳細設定", open=False):
286
+ sdp_ratio = gr.Slider(
287
+ minimum=0,
288
+ maximum=1,
289
+ value=DEFAULT_SDP_RATIO,
290
+ step=0.1,
291
+ label="SDP Ratio",
292
+ )
293
+ noise_scale = gr.Slider(
294
+ minimum=0.1,
295
+ maximum=2,
296
+ value=DEFAULT_NOISE,
297
+ step=0.1,
298
+ label="Noise",
299
+ )
300
+ noise_scale_w = gr.Slider(
301
+ minimum=0.1,
302
+ maximum=2,
303
+ value=DEFAULT_NOISEW,
304
+ step=0.1,
305
+ label="Noise_W",
306
+ )
307
+ length_scale = gr.Slider(
308
+ minimum=0.1,
309
+ maximum=2,
310
+ value=DEFAULT_LENGTH,
311
+ step=0.1,
312
+ label="Length",
313
+ )
314
+ use_assist_text = gr.Checkbox(
315
+ label="Assist textを使う", value=False
316
+ )
317
+ assist_text = gr.Textbox(
318
+ label="Assist text",
319
+ placeholder="どうして私の意見を無視するの?許せない、ムカつく!死ねばいいのに。",
320
+ info="このテキストの読み上げと似た声音・感情になりやすくなります。ただ抑揚やテンポ等が犠牲になる傾向があります。",
321
+ visible=False,
322
+ )
323
+ assist_text_weight = gr.Slider(
324
+ minimum=0,
325
+ maximum=1,
326
+ value=DEFAULT_ASSIST_TEXT_WEIGHT,
327
+ step=0.1,
328
+ label="Assist textの強さ",
329
+ visible=False,
330
+ )
331
+ use_assist_text.change(
332
+ lambda x: (gr.Textbox(visible=x), gr.Slider(visible=x)),
333
+ inputs=[use_assist_text],
334
+ outputs=[assist_text, assist_text_weight],
335
+ )
336
+ with gr.Column():
337
+ with gr.Accordion("スタイルについて詳細", open=False):
338
+ gr.Markdown(style_md)
339
+ style_mode = gr.Radio(
340
+ ["プリセットから選ぶ", "音声ファイルを入力"],
341
+ label="スタイルの指定方法",
342
+ value="プリセットから選ぶ",
343
+ )
344
+ style = gr.Dropdown(
345
+ label=f"スタイル({DEFAULT_STYLE}が平均スタイル)",
346
+ choices=["モデルをロードしてください"],
347
+ value="モデルをロードしてください",
348
+ )
349
+ style_weight = gr.Slider(
350
+ minimum=0,
351
+ maximum=50,
352
+ value=DEFAULT_STYLE_WEIGHT,
353
+ step=0.1,
354
+ label="スタイルの強さ",
355
+ )
356
+ ref_audio_path = gr.Audio(
357
+ label="参照音声", type="filepath", visible=False
358
+ )
359
+ tts_button = gr.Button(
360
+ "音声合成(モデルをロードしてください)",
361
+ variant="primary",
362
+ interactive=False,
363
+ )
364
+ text_output = gr.Textbox(label="情報")
365
+ audio_output = gr.Audio(label="結果")
366
+ Image = gr.Image('./abe.jpg')
367
+
368
+ tts_button.click(
369
+ tts_fn,
370
+ inputs=[
371
+ model_name,
372
+ model_path,
373
+ text_input,
374
+ language,
375
+ ref_audio_path,
376
+ sdp_ratio,
377
+ noise_scale,
378
+ noise_scale_w,
379
+ length_scale,
380
+ line_split,
381
+ split_interval,
382
+ assist_text,
383
+ assist_text_weight,
384
+ use_assist_text,
385
+ style,
386
+ style_weight,
387
+ tone,
388
+ use_tone,
389
+ speaker,
390
+ ],
391
+ outputs=[text_output, audio_output, tone],
392
+ )
393
+
394
+ model_name.change(
395
+ model_holder.update_model_files_gr,
396
+ inputs=[model_name],
397
+ outputs=[model_path],
398
+ )
399
+
400
+ model_path.change(make_non_interactive, outputs=[tts_button])
401
+
402
+ refresh_button.click(
403
+ model_holder.update_model_names_gr,
404
+ outputs=[model_name, model_path, tts_button],
405
+ )
406
+
407
+ load_button.click(
408
+ model_holder.load_model_gr,
409
+ inputs=[model_name, model_path],
410
+ outputs=[style, tts_button, speaker],
411
+ )
412
+
413
+ style_mode.change(
414
+ gr_util,
415
+ inputs=[style_mode],
416
+ outputs=[style, ref_audio_path],
417
+ )
418
+
419
+ app.launch(
420
+ inbrowser=not args.no_autolaunch, share=args.share, server_name=args.server_name
421
+ )