Thorsten-Voice commited on
Commit
9cb8d13
·
1 Parent(s): 413f61c

UI und Textcleaning Anpassung

Browse files
Files changed (2) hide show
  1. app.py +127 -56
  2. requirements.txt +2 -1
app.py CHANGED
@@ -1,6 +1,5 @@
1
  # Thorsten-Voice CosyVoice3 Space
2
  # Basiert auf FunAudioLLM/Fun-CosyVoice3-0.5B (Apache 2.0)
3
- # SFT-Inferenz mit dem Thorsten-Voice Finetune
4
 
5
  import spaces
6
  import os
@@ -8,7 +7,7 @@ import sys
8
  import gradio as gr
9
  import numpy as np
10
  import torch
11
- import random
12
 
13
  ROOT_DIR = os.path.dirname(os.path.abspath(__file__))
14
  sys.path.append(os.path.join(ROOT_DIR, "third_party", "Matcha-TTS"))
@@ -16,122 +15,194 @@ sys.path.append(os.path.join(ROOT_DIR, "third_party", "Matcha-TTS"))
16
  from huggingface_hub import snapshot_download, hf_hub_download
17
 
18
  # ---------------------------------------------------------------------------
19
- # Modell-Download beim Start (einmalig, wird gecacht)
20
  # ---------------------------------------------------------------------------
21
  MODEL_DIR = "pretrained_models/CosyVoice3-0.5B"
22
 
23
- print("Lade Basis-Modell (FunAudioLLM/Fun-CosyVoice3-0.5B-2512) ...")
24
- snapshot_download(
25
- "FunAudioLLM/Fun-CosyVoice3-0.5B-2512",
26
- local_dir=MODEL_DIR,
27
- )
28
 
29
  print("Lade Thorsten-Voice Finetune-Gewichte ...")
30
  for filename in ["llm.pt", "flow.pt", "spk2info.pt"]:
31
- hf_hub_download(
32
- repo_id="Thorsten-Voice/CosyVoice3",
33
- filename=filename,
34
- local_dir=MODEL_DIR,
35
- )
36
 
37
  from cosyvoice.cli.cosyvoice import AutoModel as CosyVoiceAutoModel
38
  from cosyvoice.utils.common import set_all_random_seed
 
39
 
40
  # ---------------------------------------------------------------------------
41
  # Konstanten
42
  # ---------------------------------------------------------------------------
43
- TARGET_SR = 24000
44
- MAX_TEXT_LEN = 300
45
- SPEAKER = "thorsten" # Name aus spk2info.pt
 
 
 
 
 
 
 
 
46
 
 
 
 
47
 
48
- def generate_seed():
49
- return {"__type__": "update", "value": random.randint(1, 100_000_000)}
50
 
 
 
 
 
 
 
51
 
52
  # ---------------------------------------------------------------------------
53
  # Inferenz
54
  # ---------------------------------------------------------------------------
55
 
56
  @spaces.GPU
57
- def generate_audio(tts_text: str, seed: int):
58
  default_audio = (TARGET_SR, np.zeros(TARGET_SR, dtype=np.float32))
59
 
60
  tts_text = tts_text.strip()
61
  if not tts_text:
62
  gr.Warning("Bitte gib einen Text ein.")
63
- return default_audio
 
64
  if len(tts_text) > MAX_TEXT_LEN:
65
- gr.Warning(f"Der Text ist zu lang. Bitte maximal {MAX_TEXT_LEN} Zeichen eingeben.")
66
- return default_audio
 
 
67
 
68
- set_all_random_seed(int(seed))
69
- speech_chunks = []
70
- for chunk in cosyvoice.inference_sft(
71
- tts_text,
72
- SPEAKER,
73
- stream=False,
74
- speed=1.0,
75
- ):
76
- speech_chunks.append(chunk["tts_speech"])
77
 
78
- audio = torch.concat(speech_chunks, dim=1).numpy().flatten()
79
- return (TARGET_SR, audio)
80
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
81
 
82
  # ---------------------------------------------------------------------------
83
  # Gradio UI
84
  # ---------------------------------------------------------------------------
85
 
86
  def build_ui():
87
- with gr.Blocks(title="Thorsten-Voice · CosyVoice3") as demo:
88
- gr.Markdown(
89
- "# 🎙️ Thorsten-Voice · CosyVoice3\n"
90
- "Deutsches Text-to-Speech · Finetune von "
91
- "[FunAudioLLM/Fun-CosyVoice3-0.5B](https://huggingface.co/FunAudioLLM/Fun-CosyVoice3-0.5B-2512) "
92
- "auf dem [Thorsten-Voice Datensatz](https://huggingface.co/thorsten-voice) \n"
93
- "Modell: [Thorsten-Voice/CosyVoice3](https://huggingface.co/Thorsten-Voice/CosyVoice3)"
94
- )
 
 
 
 
 
 
95
 
96
  tts_text = gr.Textbox(
97
- label="Text (max. 300 Zeichen)",
98
  placeholder="Hallo, ich bin Thorsten. Schön, dass du da bist.",
99
- lines=4,
100
  value="Hallo, ich bin Thorsten. Schön, dass du da bist.",
 
101
  )
102
 
103
- with gr.Row():
104
- seed_button = gr.Button("🎲", scale=0)
105
- seed = gr.Number(value=0, label="Zufalls-Seed", scale=1)
106
-
107
  generate_button = gr.Button("🔊 Audio generieren", variant="primary")
 
108
  audio_output = gr.Audio(
109
  label="Synthetisiertes Audio",
110
  autoplay=True,
111
  streaming=False,
 
 
 
 
 
 
 
 
112
  )
113
 
114
- seed_button.click(fn=generate_seed, inputs=[], outputs=[seed])
115
  generate_button.click(
116
  fn=generate_audio,
117
- inputs=[tts_text, seed],
118
- outputs=[audio_output],
119
  )
120
 
121
  return demo
122
 
123
-
124
  # ---------------------------------------------------------------------------
125
  # Hauptprogramm
126
  # ---------------------------------------------------------------------------
127
 
128
  if __name__ == "__main__":
129
  print("Initialisiere CosyVoice3 ...")
130
- cosyvoice = CosyVoiceAutoModel(
131
- model_dir=MODEL_DIR,
132
- load_trt=False,
133
- fp16=False,
134
- )
135
 
136
  demo = build_ui()
137
  demo.queue(default_concurrency_limit=2).launch()
 
1
  # Thorsten-Voice CosyVoice3 Space
2
  # Basiert auf FunAudioLLM/Fun-CosyVoice3-0.5B (Apache 2.0)
 
3
 
4
  import spaces
5
  import os
 
7
  import gradio as gr
8
  import numpy as np
9
  import torch
10
+ import librosa
11
 
12
  ROOT_DIR = os.path.dirname(os.path.abspath(__file__))
13
  sys.path.append(os.path.join(ROOT_DIR, "third_party", "Matcha-TTS"))
 
15
  from huggingface_hub import snapshot_download, hf_hub_download
16
 
17
  # ---------------------------------------------------------------------------
18
+ # Modell-Download beim Start
19
  # ---------------------------------------------------------------------------
20
  MODEL_DIR = "pretrained_models/CosyVoice3-0.5B"
21
 
22
+ print("Lade Basis-Modell ...")
23
+ snapshot_download("FunAudioLLM/Fun-CosyVoice3-0.5B-2512", local_dir=MODEL_DIR)
 
 
 
24
 
25
  print("Lade Thorsten-Voice Finetune-Gewichte ...")
26
  for filename in ["llm.pt", "flow.pt", "spk2info.pt"]:
27
+ hf_hub_download(repo_id="Thorsten-Voice/CosyVoice3", filename=filename, local_dir=MODEL_DIR)
 
 
 
 
28
 
29
  from cosyvoice.cli.cosyvoice import AutoModel as CosyVoiceAutoModel
30
  from cosyvoice.utils.common import set_all_random_seed
31
+ from german_transliterate.core import GermanTransliterate
32
 
33
  # ---------------------------------------------------------------------------
34
  # Konstanten
35
  # ---------------------------------------------------------------------------
36
+ TARGET_SR = 24000
37
+ MAX_TEXT_LEN = 2000
38
+ SPEAKER = "thorsten"
39
+ FIXED_SEED = 42 # Reproduzierbar, für Nutzer unsichtbar
40
+
41
+ # Trimm-Parameter gegen Artefakte am Anfang
42
+ TOP_DB = 40
43
+ HOP_LENGTH = 220
44
+ WIN_LENGTH = 440
45
+
46
+ transliterator = GermanTransliterate(replace={';': ',', ':': ','}, sep_abbreviation=' -- ')
47
 
48
+ # ---------------------------------------------------------------------------
49
+ # Hilfsfunktionen
50
+ # ---------------------------------------------------------------------------
51
 
52
+ def normalize_text(text: str) -> str:
53
+ return transliterator.transliterate(text)
54
 
55
+ def trim_audio(audio: np.ndarray, sr: int) -> np.ndarray:
56
+ """Entfernt stille/artefakt-reiche Bereiche am Anfang und Ende."""
57
+ _, indices = librosa.effects.trim(
58
+ audio, top_db=TOP_DB, frame_length=WIN_LENGTH, hop_length=HOP_LENGTH
59
+ )
60
+ return audio[indices[0]:indices[1]]
61
 
62
  # ---------------------------------------------------------------------------
63
  # Inferenz
64
  # ---------------------------------------------------------------------------
65
 
66
  @spaces.GPU
67
+ def generate_audio(tts_text: str):
68
  default_audio = (TARGET_SR, np.zeros(TARGET_SR, dtype=np.float32))
69
 
70
  tts_text = tts_text.strip()
71
  if not tts_text:
72
  gr.Warning("Bitte gib einen Text ein.")
73
+ return default_audio, ""
74
+
75
  if len(tts_text) > MAX_TEXT_LEN:
76
+ gr.Warning(f"Der Text ist zu lang (max. {MAX_TEXT_LEN} Zeichen).")
77
+ return default_audio, ""
78
+
79
+ normalized = normalize_text(tts_text)
80
 
81
+ set_all_random_seed(FIXED_SEED)
82
+ chunks = []
83
+ for chunk in cosyvoice.inference_sft(normalized, SPEAKER, stream=False, speed=1.0):
84
+ chunks.append(chunk["tts_speech"])
 
 
 
 
 
85
 
86
+ audio = torch.concat(chunks, dim=1).numpy().flatten()
87
+ audio = trim_audio(audio, TARGET_SR)
88
 
89
+ return (TARGET_SR, audio), normalized
90
+
91
+ # ---------------------------------------------------------------------------
92
+ # CSS (Corporate Design)
93
+ # ---------------------------------------------------------------------------
94
+
95
+ CSS = """
96
+ :root {
97
+ --tv-dark: #515f7f;
98
+ --tv-light: #91a0bf;
99
+ --tv-yellow:#ffc038;
100
+ }
101
+
102
+ /* Hintergrund & Schrift */
103
+ body, .gradio-container {
104
+ background-color: #f5f7fa !important;
105
+ font-family: 'Segoe UI', Arial, sans-serif !important;
106
+ }
107
+
108
+ /* Überschrift */
109
+ .tv-header {
110
+ background: linear-gradient(135deg, var(--tv-dark) 0%, var(--tv-light) 100%);
111
+ border-radius: 12px;
112
+ padding: 24px 28px 18px 28px;
113
+ margin-bottom: 8px;
114
+ color: white !important;
115
+ }
116
+ .tv-header h1 { color: white !important; margin: 0 0 6px 0; font-size: 1.6rem; }
117
+ .tv-header p { color: #dde3ef !important; margin: 0; font-size: 0.92rem; }
118
+ .tv-header a { color: var(--tv-yellow) !important; text-decoration: none; }
119
+ .tv-header a:hover { text-decoration: underline; }
120
+
121
+ /* Primär-Button */
122
+ button.primary {
123
+ background: var(--tv-yellow) !important;
124
+ color: #1a1a1a !important;
125
+ border: none !important;
126
+ font-weight: 700 !important;
127
+ border-radius: 8px !important;
128
+ }
129
+ button.primary:hover {
130
+ background: #e6a800 !important;
131
+ }
132
+
133
+ /* Labels */
134
+ label span { color: var(--tv-dark) !important; font-weight: 600; }
135
+
136
+ /* Normalisierter Text Box */
137
+ .normalized-box textarea {
138
+ background: #eef1f7 !important;
139
+ color: #3a4560 !important;
140
+ font-size: 0.88rem !important;
141
+ }
142
+
143
+ /* Audio-Output */
144
+ .audio-out { border-top: 3px solid var(--tv-yellow) !important; border-radius: 8px; }
145
+ """
146
 
147
  # ---------------------------------------------------------------------------
148
  # Gradio UI
149
  # ---------------------------------------------------------------------------
150
 
151
  def build_ui():
152
+ with gr.Blocks(css=CSS, title="Thorsten-Voice · CosyVoice3") as demo:
153
+
154
+ gr.HTML("""
155
+ <div class="tv-header">
156
+ <h1>🎙️ Thorsten-Voice &nbsp;·&nbsp; CosyVoice3</h1>
157
+ <p>
158
+ Deutsches Text-to-Speech &nbsp;·&nbsp;
159
+ Finetune von <a href="https://huggingface.co/FunAudioLLM/Fun-CosyVoice3-0.5B-2512" target="_blank">FunAudioLLM/Fun-CosyVoice3-0.5B</a>
160
+ auf dem <a href="https://huggingface.co/thorsten-voice" target="_blank">Thorsten-Voice Datensatz</a>
161
+ &nbsp;·&nbsp;
162
+ Modell: <a href="https://huggingface.co/Thorsten-Voice/CosyVoice3" target="_blank">Thorsten-Voice/CosyVoice3</a>
163
+ </p>
164
+ </div>
165
+ """)
166
 
167
  tts_text = gr.Textbox(
168
+ label=f"Text eingeben (max. {MAX_TEXT_LEN} Zeichen)",
169
  placeholder="Hallo, ich bin Thorsten. Schön, dass du da bist.",
170
+ lines=5,
171
  value="Hallo, ich bin Thorsten. Schön, dass du da bist.",
172
+ max_lines=12,
173
  )
174
 
 
 
 
 
175
  generate_button = gr.Button("🔊 Audio generieren", variant="primary")
176
+
177
  audio_output = gr.Audio(
178
  label="Synthetisiertes Audio",
179
  autoplay=True,
180
  streaming=False,
181
+ elem_classes=["audio-out"],
182
+ )
183
+
184
+ normalized_text = gr.Textbox(
185
+ label="Normalisierter Text (nach german_transliterate)",
186
+ interactive=False,
187
+ lines=3,
188
+ elem_classes=["normalized-box"],
189
  )
190
 
 
191
  generate_button.click(
192
  fn=generate_audio,
193
+ inputs=[tts_text],
194
+ outputs=[audio_output, normalized_text],
195
  )
196
 
197
  return demo
198
 
 
199
  # ---------------------------------------------------------------------------
200
  # Hauptprogramm
201
  # ---------------------------------------------------------------------------
202
 
203
  if __name__ == "__main__":
204
  print("Initialisiere CosyVoice3 ...")
205
+ cosyvoice = CosyVoiceAutoModel(model_dir=MODEL_DIR, load_trt=False, fp16=False)
 
 
 
 
206
 
207
  demo = build_ui()
208
  demo.queue(default_concurrency_limit=2).launch()
requirements.txt CHANGED
@@ -1,4 +1,4 @@
1
- --extra-index-url https://aiinfra.pkgs.visualstudio.com/PublicPackages/_packaging/onnxruntime-cuda-12/pypi/simple/ # https://github.com/microsoft/onnxruntime/issues/21684
2
  --extra-index-url https://pypi.nvidia.com
3
  --trusted-host download.pytorch.org
4
  --trusted-host aiinfra.pkgs.visualstudio.com
@@ -42,3 +42,4 @@ wget==3.2
42
  funasr==1.2.0
43
  onnxconverter-common
44
  x-transformers==2.3.5
 
 
1
+ --extra-index-url https://aiinfra.pkgs.visualstudio.com/PublicPackages/_packaging/onnxruntime-cuda-12/pypi/simple/
2
  --extra-index-url https://pypi.nvidia.com
3
  --trusted-host download.pytorch.org
4
  --trusted-host aiinfra.pkgs.visualstudio.com
 
42
  funasr==1.2.0
43
  onnxconverter-common
44
  x-transformers==2.3.5
45
+ german-transliterate