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

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +64 -23
app.py CHANGED
@@ -4,6 +4,7 @@ os.environ["CUDA_VISIBLE_DEVICES"] = ""
4
 
5
  import re
6
  import gc
 
7
  import inspect
8
  import tempfile
9
  import traceback
@@ -29,34 +30,67 @@ def _torch_load_cpu(*args, **kwargs):
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
 
50
- # Global Cache untuk mengunci embedding karakteristik suara ke dalam RAM
51
- CACHED_EMBEDDINGS = {}
52
-
53
- from chatterbox.tts import ChatterboxTTS
54
- from huggingface_hub import hf_hub_download
55
- from safetensors.torch import load_file
56
 
 
 
57
  _model = None
58
  _model_lock = Lock()
59
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
60
  def get_model():
61
  global _model
62
  if _model is None:
@@ -77,6 +111,9 @@ def get_model():
77
 
78
  _model = m
79
  print("[INIT] Model ready.")
 
 
 
80
  return _model
81
 
82
 
@@ -146,7 +183,7 @@ def _split_text_safely(text: str, max_chars: int = MAX_CHARS_PER_CHUNK):
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
@@ -166,17 +203,17 @@ 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
  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))
174
  CACHED_EMBEDDINGS[prompt_path] = extract_fn(prompt_path)
175
  else:
176
  CACHED_EMBEDDINGS[prompt_path] = prompt_path
177
-
178
- print(f"[CACHE] Fitur karakteristik untuk {prompt_path} berhasil dikunci di RAM.")
179
 
 
180
  speaker_embedding = CACHED_EMBEDDINGS[prompt_path]
181
 
182
  wav_parts = []
@@ -212,7 +249,7 @@ def clone_voice(text: str, audio_file, audio_url: str, progress=gr.Progress(trac
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)
@@ -226,7 +263,7 @@ def clone_voice(text: str, audio_file, audio_url: str, progress=gr.Progress(trac
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...")
@@ -250,12 +287,12 @@ def clone_voice(text: str, audio_file, audio_url: str, progress=gr.Progress(trac
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")
259
  url_in = gr.Textbox(label="Opsi URL File Audio Custom", placeholder="https://domain-kamu.com/audio.wav")
260
 
261
  btn = gr.Button("Sintesis Audio Kloning Suara", variant="primary")
@@ -268,6 +305,10 @@ with gr.Blocks(title="Chatterbox Seamless Engine") as demo:
268
  api_name="clone_voice"
269
  )
270
 
 
 
 
 
271
  if __name__ == "__main__":
272
  port = int(os.getenv("PORT", "7860"))
273
  demo.queue(default_concurrency_limit=1)
 
4
 
5
  import re
6
  import gc
7
+ import pickle
8
  import inspect
9
  import tempfile
10
  import traceback
 
30
  torch.load = _torch_load_cpu
31
 
32
  # =====================================================================
33
+ # CONFIG & PATH MANAGEMENT (SWEET SPOT PARAMETERS)
34
  # =====================================================================
35
  MODEL_REPO = "grandhigh/Chatterbox-TTS-Indonesian"
36
  CHECKPOINT_FILENAME = "t3_cfg.safetensors"
37
  DEVICE = "cpu"
38
 
39
  MAX_TOTAL_CHARS = int(os.getenv("MAX_TOTAL_CHARS", "2400"))
40
+ # Set ke 280 agar CPU ringan melakukan sampling awal dan tidak stuck di 0%
41
+ MAX_CHARS_PER_CHUNK = int(os.getenv("MAX_CHARS_PER_CHUNK", "280"))
42
+ MAX_CHUNKS = int(os.getenv("MAX_CHUNKS", "10"))
 
43
  PAUSE_SECONDS = float(os.getenv("PAUSE_SECONDS", "0.0"))
44
  DOWNLOAD_TIMEOUT = int(os.getenv("DOWNLOAD_TIMEOUT", "90"))
45
 
 
46
  ROOT_DIR = Path(__file__).parent
47
  DEFAULT_SPEAKER_PATH = ROOT_DIR / "default_speaker.wav"
48
 
49
+ # FILE BOBOT PERMANEN: Lokasi penyimpanan biner karakteristik suara kustom kamu
50
+ EMBEDDING_CACHE_PATH = ROOT_DIR / "speaker_embedding.pth"
 
 
 
 
51
 
52
+ # Global RAM Cache untuk menyimpan koordinat suara siap pakai
53
+ CACHED_EMBEDDINGS = {}
54
  _model = None
55
  _model_lock = Lock()
56
 
57
+
58
+ def inisialisasi_bobot_default_permanen(model_instance):
59
+ """
60
+ Fungsi untuk melatih/mengekstrak karakteristik file default_speaker.wav
61
+ HANYA SEKALI saja saat startup. Hasilnya disimpan permanen ke disk (.pth).
62
+ """
63
+ global CACHED_EMBEDDINGS
64
+ try:
65
+ # Jika file biner .pth sudah ada, langsung muat ke memori RAM secara instan
66
+ if EMBEDDING_CACHE_PATH.exists():
67
+ print("[INIT] Menemukan file bobot permanen speaker_embedding.pth. Memuat ke RAM...")
68
+ with open(EMBEDDING_CACHE_PATH, "rb") as f:
69
+ CACHED_EMBEDDINGS[str(DEFAULT_SPEAKER_PATH)] = pickle.load(f)
70
+ print("[INIT] Bobot kustom bawaan siap digunakan instan!")
71
+ return
72
+
73
+ # Jika belum ada file .pth tapi ada file .wav asli, lakukan ekstraksi awal
74
+ if DEFAULT_SPEAKER_PATH.exists():
75
+ print("[INIT] Membuat file bobot baru. Mengekstrak default_speaker.wav...")
76
+ if hasattr(model_instance, "extract_conditioning"):
77
+ embedding = model_instance.extract_conditioning(str(DEFAULT_SPEAKER_PATH))
78
+ elif hasattr(model_instance, "get_speaker_embedding"):
79
+ embedding = model_instance.get_speaker_embedding(str(DEFAULT_SPEAKER_PATH))
80
+ else:
81
+ embedding = str(DEFAULT_SPEAKER_PATH)
82
+
83
+ # Simpan secara fisik ke disk tingkat root agar permanen
84
+ if not isinstance(embedding, str):
85
+ with open(EMBEDDING_CACHE_PATH, "wb") as f:
86
+ pickle.dump(embedding, f)
87
+ print("[INIT] File speaker_embedding.pth berhasil disimpan secara permanen!")
88
+
89
+ CACHED_EMBEDDINGS[str(DEFAULT_SPEAKER_PATH)] = embedding
90
+ except Exception as e:
91
+ print(f"[WARN] Gagal inisialisasi pembentukan bobot permanen awal: {e}")
92
+
93
+
94
  def get_model():
95
  global _model
96
  if _model is None:
 
111
 
112
  _model = m
113
  print("[INIT] Model ready.")
114
+
115
+ # JALANKAN PROSES TRAINING/EKSTRAKSI SEKALI SAAT STARTUP
116
+ inisialisasi_bobot_default_permanen(_model)
117
  return _model
118
 
119
 
 
183
  return chunks
184
 
185
  # =====================================================================
186
+ # ENGINE UTAMA (FAST INFERENCE - INSTANT EMBEDDING LOADING)
187
  # =====================================================================
188
  def clone_voice(text: str, audio_file, audio_url: str, progress=gr.Progress(track_tqdm=False)):
189
  global CACHED_EMBEDDINGS
 
203
  sr = getattr(model, "sr", 24000)
204
  torch.manual_seed(42)
205
 
206
+ # Caching dinamis sebagai pengaman cadangan untuk Opsi Custom Voice Baru
207
  if prompt_path not in CACHED_EMBEDDINGS:
208
+ progress(0.0, desc="Mengekstrak karakteristik gelombang audio kustom baru ke RAM...")
 
209
  if hasattr(model, "extract_conditioning") or hasattr(model, "get_speaker_embedding"):
210
  extract_fn = getattr(model, "extract_conditioning", getattr(model, "get_speaker_embedding", None))
211
  CACHED_EMBEDDINGS[prompt_path] = extract_fn(prompt_path)
212
  else:
213
  CACHED_EMBEDDINGS[prompt_path] = prompt_path
214
+ print(f"[CACHE] Fitur karakteristik kustom untuk {prompt_path} berhasil dikunci di RAM.")
 
215
 
216
+ # LOAD INSTAN: Mengambil data biner kustom tanpa membedah file .wav lagi
217
  speaker_embedding = CACHED_EMBEDDINGS[prompt_path]
218
 
219
  wav_parts = []
 
249
  if "cfg_weight" in params:
250
  kwargs["cfg_weight"] = 0.3
251
  if "max_new_tokens" in params:
252
+ kwargs["max_new_tokens"] = 350
253
 
254
  try:
255
  wav = model.generate(ch, **kwargs)
 
263
  if wav.dim() == 1:
264
  wav = wav.unsqueeze(0)
265
 
266
+ # Gabungkan potongan audio secara mulus (Seamless Concatenation)
267
  wav_parts.append(wav.detach().cpu().clone())
268
 
269
  progress(0.95, desc="Menyambungkan seluruh fragmentasi gelombang secara natural...")
 
287
  # =====================================================================
288
  # INTERFACE DESIGN
289
  # =====================================================================
290
+ with gr.Blocks(title="Chatterbox Persistent Weight Engine") as demo:
291
+ gr.Markdown("## EduScanner AI Voice Backend - Pre-computed Weights Edition")
292
+ gr.Markdown("Sistem memuat bobot suara bawaan secara permanen untuk memotong durasi penundaan CPU.")
293
 
294
  text_in = gr.Textbox(label="Teks Rangkuman Materi Kuliah", lines=8, placeholder="Ketik teks di sini...")
295
+ wav_in = gr.Audio(label="Opsi Custom Voice (Kosongkan jika ingin pakai bobot default Mythia Batford)", type="filepath")
296
  url_in = gr.Textbox(label="Opsi URL File Audio Custom", placeholder="https://domain-kamu.com/audio.wav")
297
 
298
  btn = gr.Button("Sintesis Audio Kloning Suara", variant="primary")
 
305
  api_name="clone_voice"
306
  )
307
 
308
+ from chatterbox.tts import ChatterboxTTS
309
+ from huggingface_hub import hf_hub_download
310
+ from safetensors.torch import load_file
311
+
312
  if __name__ == "__main__":
313
  port = int(os.getenv("PORT", "7860"))
314
  demo.queue(default_concurrency_limit=1)