Alstears commited on
Commit
5bf3e55
·
verified ·
1 Parent(s): 0ca79ed

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +15 -26
app.py CHANGED
@@ -29,19 +29,21 @@ def _torch_load_cpu(*args, **kwargs):
29
  torch.load = _torch_load_cpu
30
 
31
  # =====================================================================
32
- # CONFIG & PATH MANAGEMENT
33
  # =====================================================================
34
  MODEL_REPO = "grandhigh/Chatterbox-TTS-Indonesian"
35
  CHECKPOINT_FILENAME = "t3_cfg.safetensors"
36
  DEVICE = "cpu"
37
 
38
  MAX_TOTAL_CHARS = int(os.getenv("MAX_TOTAL_CHARS", "2400"))
39
- MAX_CHARS_PER_CHUNK = int(os.getenv("MAX_CHARS_PER_CHUNK", "220"))
40
- 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
- # Sesuai request: Suara default ditaruh langsung di root folder (/)
45
  ROOT_DIR = Path(__file__).parent
46
  DEFAULT_SPEAKER_PATH = ROOT_DIR / "default_speaker.wav"
47
 
@@ -96,7 +98,6 @@ def _resolve_audio_input(audio_file, audio_url: str):
96
  except Exception:
97
  pass
98
 
99
- # Jika parameter audio kosong, otomatis gunakan file suara default langsung dari root (/)
100
  if DEFAULT_SPEAKER_PATH.exists():
101
  return str(DEFAULT_SPEAKER_PATH)
102
 
@@ -145,7 +146,7 @@ def _split_text_safely(text: str, max_chars: int = MAX_CHARS_PER_CHUNK):
145
  return chunks
146
 
147
  # =====================================================================
148
- # ENGINE UTAMA
149
  # =====================================================================
150
  def clone_voice(text: str, audio_file, audio_url: str, progress=gr.Progress(track_tqdm=False)):
151
  global CACHED_EMBEDDINGS
@@ -154,7 +155,6 @@ def clone_voice(text: str, audio_file, audio_url: str, progress=gr.Progress(trac
154
  if not raw_text:
155
  raise gr.Error("Text prompt tidak boleh kosong.")
156
 
157
- # Ambil jalur audio (Custom atau Default Root)
158
  prompt_path = _resolve_audio_input(audio_file, audio_url)
159
  if not prompt_path:
160
  raise gr.Error("Suara acuan default_speaker.wav tidak ditemukan di root server.")
@@ -166,9 +166,8 @@ def clone_voice(text: str, audio_file, audio_url: str, progress=gr.Progress(trac
166
  sr = getattr(model, "sr", 24000)
167
  torch.manual_seed(42)
168
 
169
- # Proses pembuatan cache embedding jika file audio terdeteksi baru
170
  if prompt_path not in CACHED_EMBEDDINGS:
171
- progress(0.0, desc="Mengekstrak karakteristik gelombang audio ke RAM (Hanya sekali)...")
172
 
173
  if hasattr(model, "extract_conditioning") or hasattr(model, "get_speaker_embedding"):
174
  extract_fn = getattr(model, "extract_conditioning", getattr(model, "get_speaker_embedding", None))
@@ -181,10 +180,8 @@ def clone_voice(text: str, audio_file, audio_url: str, progress=gr.Progress(trac
181
  speaker_embedding = CACHED_EMBEDDINGS[prompt_path]
182
 
183
  wav_parts = []
184
- pause = torch.zeros(1, int(sr * PAUSE_SECONDS))
185
  total = len(chunks)
186
 
187
- # Bedah blueprint fungsi model asli untuk memvalidasi parameter masuk
188
  sig = inspect.signature(model.generate)
189
  params = sig.parameters
190
 
@@ -196,9 +193,7 @@ def clone_voice(text: str, audio_file, audio_url: str, progress=gr.Progress(trac
196
  if not re.search(r"[.!?…]$", ch):
197
  ch += "."
198
 
199
- # Saring parameter agar hanya memasukkan key yang dikenal oleh library model
200
  kwargs = {}
201
-
202
  if "audio_prompt_path" in params:
203
  kwargs["audio_prompt_path"] = speaker_embedding
204
  elif "speaker_embedding" in params:
@@ -208,7 +203,6 @@ def clone_voice(text: str, audio_file, audio_url: str, progress=gr.Progress(trac
208
  if len(list_keys) > 1:
209
  kwargs[list_keys[1]] = speaker_embedding
210
 
211
- # Parameter opsional tambahan (Hanya disuntikkan jika disupport oleh versi model)
212
  if "temperature" in params:
213
  kwargs["temperature"] = 0.05
214
  if "top_p" in params:
@@ -218,9 +212,8 @@ def clone_voice(text: str, audio_file, audio_url: str, progress=gr.Progress(trac
218
  if "cfg_weight" in params:
219
  kwargs["cfg_weight"] = 0.3
220
  if "max_new_tokens" in params:
221
- kwargs["max_new_tokens"] = 260
222
 
223
- # Eksekusi aman bebas dari bug TypeError
224
  try:
225
  wav = model.generate(ch, **kwargs)
226
  except TypeError:
@@ -233,19 +226,15 @@ def clone_voice(text: str, audio_file, audio_url: str, progress=gr.Progress(trac
233
  if wav.dim() == 1:
234
  wav = wav.unsqueeze(0)
235
 
 
236
  wav_parts.append(wav.detach().cpu().clone())
237
- wav_parts.append(pause)
238
-
239
- if wav_parts:
240
- wav_parts = wav_parts[:-1]
241
 
242
- progress(0.95, desc="Menggabungkan kompilasi audio...")
243
  full_wav = torch.cat(wav_parts, dim=1)
244
 
245
  out_path = tempfile.NamedTemporaryFile(delete=False, suffix=".wav").name
246
  ta.save(out_path, full_wav, sr)
247
 
248
- # Bersihkan memori RAM kontainer secara agresif
249
  del wav_parts
250
  del full_wav
251
  gc.collect()
@@ -261,9 +250,9 @@ def clone_voice(text: str, audio_file, audio_url: str, progress=gr.Progress(trac
261
  # =====================================================================
262
  # INTERFACE DESIGN
263
  # =====================================================================
264
- with gr.Blocks(title="Chatterbox Flat Root Safe Engine") as demo:
265
- gr.Markdown("## EduScanner AI Voice Backend - Flat Root Configuration")
266
- gr.Markdown("Sistem membaca `default_speaker.wav` langsung dari tingkat root (/) folder Space.")
267
 
268
  text_in = gr.Textbox(label="Teks Rangkuman Materi Kuliah", lines=8, placeholder="Ketik teks di sini...")
269
  wav_in = gr.Audio(label="Opsi Custom Voice (Kosongkan jika ingin pakai suara default Mythia Batford 1:15 di root)", type="filepath")
 
29
  torch.load = _torch_load_cpu
30
 
31
  # =====================================================================
32
+ # CONFIG & PATH MANAGEMENT (DIOPTIMALKAN AGAR SUARA MULUS)
33
  # =====================================================================
34
  MODEL_REPO = "grandhigh/Chatterbox-TTS-Indonesian"
35
  CHECKPOINT_FILENAME = "t3_cfg.safetensors"
36
  DEVICE = "cpu"
37
 
38
  MAX_TOTAL_CHARS = int(os.getenv("MAX_TOTAL_CHARS", "2400"))
39
+ # DIUBAH: Ditingkatkan ke 450 agar model membaca kalimat utuh (intonasi jauh lebih natural)
40
+ MAX_CHARS_PER_CHUNK = int(os.getenv("MAX_CHARS_PER_CHUNK", "450"))
41
+ MAX_CHUNKS = int(os.getenv("MAX_CHUNKS", "8"))
42
+ # DIUBAH: Set ke 0.0 agar tidak ada jeda kosong robotik antar potongan file audio
43
+ PAUSE_SECONDS = float(os.getenv("PAUSE_SECONDS", "0.0"))
44
  DOWNLOAD_TIMEOUT = int(os.getenv("DOWNLOAD_TIMEOUT", "90"))
45
 
46
+ # Jalur untuk suara default bawaan sistem langsung di root (/)
47
  ROOT_DIR = Path(__file__).parent
48
  DEFAULT_SPEAKER_PATH = ROOT_DIR / "default_speaker.wav"
49
 
 
98
  except Exception:
99
  pass
100
 
 
101
  if DEFAULT_SPEAKER_PATH.exists():
102
  return str(DEFAULT_SPEAKER_PATH)
103
 
 
146
  return chunks
147
 
148
  # =====================================================================
149
+ # ENGINE UTAMA (SMOOTH STREAMING AUDIO CONCATENATION)
150
  # =====================================================================
151
  def clone_voice(text: str, audio_file, audio_url: str, progress=gr.Progress(track_tqdm=False)):
152
  global CACHED_EMBEDDINGS
 
155
  if not raw_text:
156
  raise gr.Error("Text prompt tidak boleh kosong.")
157
 
 
158
  prompt_path = _resolve_audio_input(audio_file, audio_url)
159
  if not prompt_path:
160
  raise gr.Error("Suara acuan default_speaker.wav tidak ditemukan di root server.")
 
166
  sr = getattr(model, "sr", 24000)
167
  torch.manual_seed(42)
168
 
 
169
  if prompt_path not in CACHED_EMBEDDINGS:
170
+ progress(0.0, desc="Mengekstrak karakteristik gelombang audio ke RAM...")
171
 
172
  if hasattr(model, "extract_conditioning") or hasattr(model, "get_speaker_embedding"):
173
  extract_fn = getattr(model, "extract_conditioning", getattr(model, "get_speaker_embedding", None))
 
180
  speaker_embedding = CACHED_EMBEDDINGS[prompt_path]
181
 
182
  wav_parts = []
 
183
  total = len(chunks)
184
 
 
185
  sig = inspect.signature(model.generate)
186
  params = sig.parameters
187
 
 
193
  if not re.search(r"[.!?…]$", ch):
194
  ch += "."
195
 
 
196
  kwargs = {}
 
197
  if "audio_prompt_path" in params:
198
  kwargs["audio_prompt_path"] = speaker_embedding
199
  elif "speaker_embedding" in params:
 
203
  if len(list_keys) > 1:
204
  kwargs[list_keys[1]] = speaker_embedding
205
 
 
206
  if "temperature" in params:
207
  kwargs["temperature"] = 0.05
208
  if "top_p" in params:
 
212
  if "cfg_weight" in params:
213
  kwargs["cfg_weight"] = 0.3
214
  if "max_new_tokens" in params:
215
+ kwargs["max_new_tokens"] = 512 # Dinaikkan seiring bertambahnya ukuran panjang karakter chunk
216
 
 
217
  try:
218
  wav = model.generate(ch, **kwargs)
219
  except TypeError:
 
226
  if wav.dim() == 1:
227
  wav = wav.unsqueeze(0)
228
 
229
+ # Masukkan potongan audio murni langsung tanpa diselipkan tensor kosong
230
  wav_parts.append(wav.detach().cpu().clone())
 
 
 
 
231
 
232
+ progress(0.95, desc="Menyambungkan seluruh fragmentasi gelombang secara natural...")
233
  full_wav = torch.cat(wav_parts, dim=1)
234
 
235
  out_path = tempfile.NamedTemporaryFile(delete=False, suffix=".wav").name
236
  ta.save(out_path, full_wav, sr)
237
 
 
238
  del wav_parts
239
  del full_wav
240
  gc.collect()
 
250
  # =====================================================================
251
  # INTERFACE DESIGN
252
  # =====================================================================
253
+ with gr.Blocks(title="Chatterbox Seamless Engine") as demo:
254
+ gr.Markdown("## EduScanner AI Voice Backend - Seamless Continuous Synthesis")
255
+ gr.Markdown("Kode ini menghapus jeda mati buatan antar-chunk agar audio mengalir menyatu secara natural.")
256
 
257
  text_in = gr.Textbox(label="Teks Rangkuman Materi Kuliah", lines=8, placeholder="Ketik teks di sini...")
258
  wav_in = gr.Audio(label="Opsi Custom Voice (Kosongkan jika ingin pakai suara default Mythia Batford 1:15 di root)", type="filepath")