Alstears commited on
Commit
6d0b83b
·
verified ·
1 Parent(s): 6b11b7f

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +43 -30
app.py CHANGED
@@ -15,13 +15,13 @@ import torch
15
  import torchaudio as ta
16
  import gradio as gr
17
 
18
- # Optimasi Core CPU shared Hugging Face agar fokus
19
  torch.set_num_threads(2)
20
  torch.set_num_interop_threads(2)
21
  torch.set_grad_enabled(False)
22
  torch.cuda.is_available = lambda: False # noqa: E731
23
 
24
- # Override global torch load untuk keamanan CPU
25
  _original_torch_load = torch.load
26
  def _torch_load_cpu(*args, **kwargs):
27
  kwargs["map_location"] = torch.device("cpu")
@@ -41,13 +41,13 @@ MAX_CHUNKS = int(os.getenv("MAX_CHUNKS", "12"))
41
  PAUSE_SECONDS = float(os.getenv("PAUSE_SECONDS", "0.15"))
42
  DOWNLOAD_TIMEOUT = int(os.getenv("DOWNLOAD_TIMEOUT", "90"))
43
 
44
- # Jalur untuk suara default bawaan sistem (Taruh file suara 1:15 kamu di folder assets Space ini)
45
  ROOT_DIR = Path(__file__).parent
46
  ASSETS_DIR = ROOT_DIR / "assets"
47
  ASSETS_DIR.mkdir(exist_ok=True)
48
  DEFAULT_SPEAKER_PATH = ASSETS_DIR / "default_speaker.wav"
49
 
50
- # Global Cache untuk mengunci embedding suara agar tidak dihitung ulang setiap request
51
  CACHED_EMBEDDINGS = {}
52
 
53
  from chatterbox.tts import ChatterboxTTS
@@ -98,7 +98,7 @@ def _resolve_audio_input(audio_file, audio_url: str):
98
  except Exception:
99
  pass
100
 
101
- # KUNCI UTAMA: Jika tidak ada file/url yang dikirim, otomatis alihkan ke Default Voice bawaan sistem
102
  if DEFAULT_SPEAKER_PATH.exists():
103
  return str(DEFAULT_SPEAKER_PATH)
104
 
@@ -147,7 +147,7 @@ def _split_text_safely(text: str, max_chars: int = MAX_CHARS_PER_CHUNK):
147
  return chunks
148
 
149
  # =====================================================================
150
- # ENGINE INFERENCE DENGAN DUAL-SPEAKER SELECTION & EMBEDDING CACHE
151
  # =====================================================================
152
  def clone_voice(text: str, audio_file, audio_url: str, progress=gr.Progress(track_tqdm=False)):
153
  global CACHED_EMBEDDINGS
@@ -156,10 +156,10 @@ def clone_voice(text: str, audio_file, audio_url: str, progress=gr.Progress(trac
156
  if not raw_text:
157
  raise gr.Error("Text prompt tidak boleh kosong.")
158
 
159
- # Tentukan file suara yang dipakai (Custom atau Default Lokal)
160
  prompt_path = _resolve_audio_input(audio_file, audio_url)
161
  if not prompt_path:
162
- raise gr.Error("Suara default tidak ditemukan di server. Harap upload sampel audio.")
163
 
164
  chunks = _split_text_safely(raw_text, max_chars=MAX_CHARS_PER_CHUNK)
165
  chunks = [ch for ch in chunks if re.search(r'[a-zA-Z0-9]', ch)]
@@ -168,9 +168,9 @@ def clone_voice(text: str, audio_file, audio_url: str, progress=gr.Progress(trac
168
  sr = getattr(model, "sr", 24000)
169
  torch.manual_seed(42)
170
 
171
- # Caching Dinamis berdasarkan path audio agar multi-user/multi-voice tetap aman
172
  if prompt_path not in CACHED_EMBEDDINGS:
173
- progress(0.0, desc="Mengekstrak dan mengunci embedding karakteristik suara ke RAM...")
174
 
175
  if hasattr(model, "extract_conditioning") or hasattr(model, "get_speaker_embedding"):
176
  extract_fn = getattr(model, "extract_conditioning", getattr(model, "get_speaker_embedding", None))
@@ -178,7 +178,7 @@ def clone_voice(text: str, audio_file, audio_url: str, progress=gr.Progress(trac
178
  else:
179
  CACHED_EMBEDDINGS[prompt_path] = prompt_path
180
 
181
- print(f"[CACHE] Karakteristik untuk {prompt_path} berhasil dikunci.")
182
 
183
  speaker_embedding = CACHED_EMBEDDINGS[prompt_path]
184
 
@@ -186,6 +186,7 @@ def clone_voice(text: str, audio_file, audio_url: str, progress=gr.Progress(trac
186
  pause = torch.zeros(1, int(sr * PAUSE_SECONDS))
187
  total = len(chunks)
188
 
 
189
  sig = inspect.signature(model.generate)
190
  params = sig.parameters
191
 
@@ -197,21 +198,31 @@ def clone_voice(text: str, audio_file, audio_url: str, progress=gr.Progress(trac
197
  if not re.search(r"[.!?…]$", ch):
198
  ch += "."
199
 
200
- kwargs = {
201
- "temperature": 0.05,
202
- "top_p": 0.7,
203
- "exaggeration": 0.25,
204
- "cfg_weight": 0.3,
205
- "max_new_tokens": 260
206
- }
207
 
208
  if "audio_prompt_path" in params:
209
  kwargs["audio_prompt_path"] = speaker_embedding
210
  elif "speaker_embedding" in params:
211
  kwargs["speaker_embedding"] = speaker_embedding
212
- else:
213
- kwargs["audio_prompt_path"] = prompt_path
214
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
215
  try:
216
  wav = model.generate(ch, **kwargs)
217
  except TypeError:
@@ -219,6 +230,7 @@ def clone_voice(text: str, audio_file, audio_url: str, progress=gr.Progress(trac
219
  kwargs["text"] = ch
220
  wav = model.generate(**kwargs)
221
  else:
 
222
  wav = model.generate(ch)
223
 
224
  if wav.dim() == 1:
@@ -230,12 +242,13 @@ def clone_voice(text: str, audio_file, audio_url: str, progress=gr.Progress(trac
230
  if wav_parts:
231
  wav_parts = wav_parts[:-1]
232
 
233
- progress(0.95, desc="Menggabungkan audio akhir...")
234
  full_wav = torch.cat(wav_parts, dim=1)
235
 
236
  out_path = tempfile.NamedTemporaryFile(delete=False, suffix=".wav").name
237
  ta.save(out_path, full_wav, sr)
238
 
 
239
  del wav_parts
240
  del full_wav
241
  gc.collect()
@@ -251,16 +264,16 @@ def clone_voice(text: str, audio_file, audio_url: str, progress=gr.Progress(trac
251
  # =====================================================================
252
  # INTERFACE DESIGN
253
  # =====================================================================
254
- with gr.Blocks(title="Chatterbox Dual Voice Hybrid Engine") as demo:
255
- gr.Markdown("## EduScanner AI - Dual Voice Solution (Default & Custom)")
256
- gr.Markdown("Otomatis menggunakan suara default jika input file dikosongkan.")
257
 
258
- text_in = gr.Textbox(label="Teks Rangkuman", lines=8, placeholder="Masukkan teks di sini...")
259
- wav_in = gr.Audio(label="Opsi Custom Voice (Kosongkan jika ingin pakai suara default Syahid)", type="filepath")
260
- url_in = gr.Textbox(label="Opsi Link URL Audio Custom", placeholder="https://example.com/suara_lain.wav")
261
 
262
- btn = gr.Button("Sintesis Audio", variant="primary")
263
- out_audio = gr.Audio(label="Hasil Audio (WAV)", type="filepath")
264
 
265
  btn.click(
266
  fn=clone_voice,
 
15
  import torchaudio as ta
16
  import gradio as gr
17
 
18
+ # Optimasi Core CPU shared Hugging Face agar fokus dan stabil
19
  torch.set_num_threads(2)
20
  torch.set_num_interop_threads(2)
21
  torch.set_grad_enabled(False)
22
  torch.cuda.is_available = lambda: False # noqa: E731
23
 
24
+ # Override global torch load untuk keamanan jalur CPU
25
  _original_torch_load = torch.load
26
  def _torch_load_cpu(*args, **kwargs):
27
  kwargs["map_location"] = torch.device("cpu")
 
41
  PAUSE_SECONDS = float(os.getenv("PAUSE_SECONDS", "0.15"))
42
  DOWNLOAD_TIMEOUT = int(os.getenv("DOWNLOAD_TIMEOUT", "90"))
43
 
44
+ # Jalur untuk suara default bawaan sistem (Simpan file 1:15 kamu di folder assets)
45
  ROOT_DIR = Path(__file__).parent
46
  ASSETS_DIR = ROOT_DIR / "assets"
47
  ASSETS_DIR.mkdir(exist_ok=True)
48
  DEFAULT_SPEAKER_PATH = ASSETS_DIR / "default_speaker.wav"
49
 
50
+ # Global Cache untuk mengunci embedding karakteristik suara ke dalam RAM
51
  CACHED_EMBEDDINGS = {}
52
 
53
  from chatterbox.tts import ChatterboxTTS
 
98
  except Exception:
99
  pass
100
 
101
+ # Jika parameter audio kosong, otomatis gunakan file suara default 1:15 di folder assets
102
  if DEFAULT_SPEAKER_PATH.exists():
103
  return str(DEFAULT_SPEAKER_PATH)
104
 
 
147
  return chunks
148
 
149
  # =====================================================================
150
+ # ENGINE UTAMA: SUDAH FIX TYPEERROR ARGUMEN & CACHE EMBEDDING
151
  # =====================================================================
152
  def clone_voice(text: str, audio_file, audio_url: str, progress=gr.Progress(track_tqdm=False)):
153
  global CACHED_EMBEDDINGS
 
156
  if not raw_text:
157
  raise gr.Error("Text prompt tidak boleh kosong.")
158
 
159
+ # Ambil jalur audio (Custom atau Default)
160
  prompt_path = _resolve_audio_input(audio_file, audio_url)
161
  if not prompt_path:
162
+ raise gr.Error("Suara acuan tidak ditemukan. Sediakan file audio default atau upload sampel.")
163
 
164
  chunks = _split_text_safely(raw_text, max_chars=MAX_CHARS_PER_CHUNK)
165
  chunks = [ch for ch in chunks if re.search(r'[a-zA-Z0-9]', ch)]
 
168
  sr = getattr(model, "sr", 24000)
169
  torch.manual_seed(42)
170
 
171
+ # Proses pembuatan cache embedding jika file audio terdeteksi baru
172
  if prompt_path not in CACHED_EMBEDDINGS:
173
+ progress(0.0, desc="Mengekstrak karakteristik gelombang audio ke RAM (Hanya sekali)...")
174
 
175
  if hasattr(model, "extract_conditioning") or hasattr(model, "get_speaker_embedding"):
176
  extract_fn = getattr(model, "extract_conditioning", getattr(model, "get_speaker_embedding", None))
 
178
  else:
179
  CACHED_EMBEDDINGS[prompt_path] = prompt_path
180
 
181
+ print(f"[CACHE] Fitur karakteristik untuk {prompt_path} berhasil dikunci di RAM.")
182
 
183
  speaker_embedding = CACHED_EMBEDDINGS[prompt_path]
184
 
 
186
  pause = torch.zeros(1, int(sr * PAUSE_SECONDS))
187
  total = len(chunks)
188
 
189
+ # Bedah blueprint fungsi model asli untuk memvalidasi parameter masuk
190
  sig = inspect.signature(model.generate)
191
  params = sig.parameters
192
 
 
198
  if not re.search(r"[.!?…]$", ch):
199
  ch += "."
200
 
201
+ # Saring parameter agar hanya memasukkan key yang dikenal oleh library model
202
+ kwargs = {}
 
 
 
 
 
203
 
204
  if "audio_prompt_path" in params:
205
  kwargs["audio_prompt_path"] = speaker_embedding
206
  elif "speaker_embedding" in params:
207
  kwargs["speaker_embedding"] = speaker_embedding
208
+ elif "prompt" in params and len(params) > 1:
209
+ list_keys = list(params.keys())
210
+ if len(list_keys) > 1:
211
+ kwargs[list_keys[1]] = speaker_embedding
212
+
213
+ # Parameter opsional tambahan (Hanya disuntikkan jika disupport oleh versi model)
214
+ if "temperature" in params:
215
+ kwargs["temperature"] = 0.05
216
+ if "top_p" in params:
217
+ kwargs["top_p"] = 0.7
218
+ if "exaggeration" in params:
219
+ kwargs["exaggeration"] = 0.25
220
+ if "cfg_weight" in params:
221
+ kwargs["cfg_weight"] = 0.3
222
+ if "max_new_tokens" in params:
223
+ kwargs["max_new_tokens"] = 260
224
+
225
+ # Eksekusi aman bebas dari bug TypeError
226
  try:
227
  wav = model.generate(ch, **kwargs)
228
  except TypeError:
 
230
  kwargs["text"] = ch
231
  wav = model.generate(**kwargs)
232
  else:
233
+ # Jalur teraman jika custom kwargs ditolak total oleh internal model
234
  wav = model.generate(ch)
235
 
236
  if wav.dim() == 1:
 
242
  if wav_parts:
243
  wav_parts = wav_parts[:-1]
244
 
245
+ progress(0.95, desc="Menggabungkan kompilasi audio...")
246
  full_wav = torch.cat(wav_parts, dim=1)
247
 
248
  out_path = tempfile.NamedTemporaryFile(delete=False, suffix=".wav").name
249
  ta.save(out_path, full_wav, sr)
250
 
251
+ # Bersihkan memori RAM kontainer secara agresif
252
  del wav_parts
253
  del full_wav
254
  gc.collect()
 
264
  # =====================================================================
265
  # INTERFACE DESIGN
266
  # =====================================================================
267
+ with gr.Blocks(title="Chatterbox Dual Voice Safe Engine") as demo:
268
+ gr.Markdown("## EduScanner AI Voice Backend - Anti-Error Parameter Generation")
269
+ gr.Markdown("Kode ini menyaring argumen secara dinamis untuk mencegah error crash sistem.")
270
 
271
+ text_in = gr.Textbox(label="Teks Rangkuman Materi Kuliah", lines=8, placeholder="Ketik teks di sini...")
272
+ wav_in = gr.Audio(label="Opsi Custom Voice (Kosongkan jika ingin pakai suara default Syahid 1:15)", type="filepath")
273
+ url_in = gr.Textbox(label="Opsi URL File Audio Custom", placeholder="https://domain-kamu.com/audio.wav")
274
 
275
+ btn = gr.Button("Sintesis Audio Kloning Suara", variant="primary")
276
+ out_audio = gr.Audio(label="Hasil Audio Akhir (WAV)", type="filepath")
277
 
278
  btn.click(
279
  fn=clone_voice,