Opera8 commited on
Commit
454cbe7
·
verified ·
1 Parent(s): de91e11

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +60 -66
app.py CHANGED
@@ -174,7 +174,7 @@ def vevo_timbre(content_wav, reference_wav):
174
  raise ValueError("Please upload audio files")
175
 
176
  try:
177
- # --- پردازش ورودی ---
178
  if isinstance(content_wav, tuple):
179
  content_sr, content_data = content_wav if isinstance(content_wav[0], int) else (content_wav[1], content_wav[0])
180
  else:
@@ -187,11 +187,9 @@ def vevo_timbre(content_wav, reference_wav):
187
  if content_sr != 24000:
188
  content_tensor = torchaudio.functional.resample(content_tensor, content_sr, 24000)
189
  content_sr = 24000
190
-
191
- # نرمال‌سازی
192
  content_tensor = content_tensor / (torch.max(torch.abs(content_tensor)) + 1e-6) * 0.95
193
 
194
- # --- پردازش رفرنس ---
195
  if isinstance(reference_wav, tuple):
196
  ref_sr, ref_data = reference_wav if isinstance(reference_wav[0], int) else (reference_wav[1], reference_wav[0])
197
  else:
@@ -204,105 +202,101 @@ def vevo_timbre(content_wav, reference_wav):
204
  if ref_sr != 24000:
205
  ref_tensor = torchaudio.functional.resample(ref_tensor, ref_sr, 24000)
206
  ref_sr = 24000
207
-
208
- ref_max = torch.max(torch.abs(ref_tensor)) + 1e-6
209
- ref_tensor = ref_tensor / ref_max * 0.95
210
 
211
  if ref_tensor.shape[1] > 24000 * 20:
212
  ref_tensor = ref_tensor[:, :24000 * 20]
213
 
214
  save_audio_pcm16(ref_tensor, temp_reference_path, ref_sr)
215
 
216
- # --- منطق Look-back Splicing (حذف قطعی اکو) ---
217
  pipeline = get_pipeline()
218
-
219
  SR = 24000
220
- MAIN_CHUNK_SEC = 10.0
221
- CONTEXT_SEC = 1.0 # مقدار نگاه به عقب
222
 
223
- MAIN_CHUNK = int(MAIN_CHUNK_SEC * SR)
224
- CONTEXT = int(CONTEXT_SEC * SR)
 
 
 
 
225
 
226
  total_samples = content_tensor.shape[1]
227
- print(f"[{session_id}] Processing (High Quality 64 Steps)... Zero Echo Mode.")
228
 
229
- final_output = []
 
 
 
230
 
231
- # اشاره‌گر جاری روی فایل اصلی
232
- cursor = 0
 
 
 
 
233
 
234
- while cursor < total_samples:
235
- if cursor == 0:
236
- # تکه اول: بدون کانتکست
237
- input_start = 0
238
- input_end = min(MAIN_CHUNK, total_samples)
239
- # در تکه اول چیزی را دور نمی‌ریزیم
240
- crop_from = 0
241
- else:
242
- # تکه‌های بعدی: با کانتکست (نگاه به عقب)
243
- input_start = cursor - CONTEXT
244
- input_end = min(cursor + MAIN_CHUNK, total_samples)
245
- # در خروجی، قسمت کانتکست را دور می‌ریزیم (Cut)
246
- crop_from = CONTEXT
247
 
248
- # اگر به انتهای فایل رسیدیم و طول باقی‌مانده خیلی کم است
249
- if input_start >= input_end:
250
- break
251
-
252
- current_input_chunk = content_tensor[:, input_start:input_end]
253
- save_audio_pcm16(current_input_chunk, temp_content_path, SR)
254
 
255
- print(f"[{session_id}] Processing chunk: {cursor/SR:.1f}s -> {(input_end-input_start)/SR:.1f}s len")
 
256
 
257
  try:
258
  gen = pipeline.inference_fm(
259
  src_wav_path=temp_content_path,
260
  timbre_ref_wav_path=temp_reference_path,
261
- flow_matching_steps=64, # کیفیت بالا
262
  )
263
 
264
  if torch.isnan(gen).any(): gen = torch.nan_to_num(gen, nan=0.0)
265
- if gen.dim() == 1: gen = gen.unsqueeze(0)
266
- gen = gen.cpu().squeeze(0).numpy()
 
 
 
 
 
267
 
268
- # *** نکته کلیدی: برش قسمت تکراری ***
269
- # فقط قسمت "جدید" را نگه می‌داریم
270
- if crop_from > 0:
271
- if len(gen) > crop_from:
272
- valid_audio = gen[crop_from:]
273
- else:
274
- valid_audio = np.array([]) # اگر خروجی خیلی کوتاه بود
275
- else:
276
- valid_audio = gen
277
 
278
- final_output.append(valid_audio)
279
 
280
- # حرکت مکان‌نما به اندازه دیتای مفیدی که تولید کردیم
281
- cursor = input_end
 
282
 
283
  except Exception as e:
284
- print(f"Error in chunk: {e}")
285
- # اگر ارور داد، سکوت جایگزین کن که تایمینگ به هم نریزد
286
- needed_len = input_end - (cursor if cursor > 0 else 0)
287
- final_output.append(np.zeros(needed_len))
288
- cursor = input_end
289
-
290
- # چسباندن تکه‌ها (Concatenate)
291
- if len(final_output) > 0:
292
- full_audio = np.concatenate(final_output)
293
- else:
294
- full_audio = np.zeros(24000)
295
 
296
- save_audio_pcm16(full_audio, output_path, SR)
 
 
 
 
 
 
 
 
297
  return output_path
298
 
299
  finally:
300
  if os.path.exists(temp_content_path): os.remove(temp_content_path)
301
  if os.path.exists(temp_reference_path): os.remove(temp_reference_path)
302
 
303
- with gr.Blocks(title="Vevo-Timbre (Zero Echo)") as demo:
304
  gr.Markdown("## Vevo-Timbre: Zero-Shot Voice Conversion")
305
- gr.Markdown("نسخه نهایی: استفاده از روش Look-back Splicing برای حذف کامل اکو و حفظ پیوستگی لحن.")
306
 
307
  with gr.Row():
308
  with gr.Column():
 
174
  raise ValueError("Please upload audio files")
175
 
176
  try:
177
+ # --- آماده سازی Content ---
178
  if isinstance(content_wav, tuple):
179
  content_sr, content_data = content_wav if isinstance(content_wav[0], int) else (content_wav[1], content_wav[0])
180
  else:
 
187
  if content_sr != 24000:
188
  content_tensor = torchaudio.functional.resample(content_tensor, content_sr, 24000)
189
  content_sr = 24000
 
 
190
  content_tensor = content_tensor / (torch.max(torch.abs(content_tensor)) + 1e-6) * 0.95
191
 
192
+ # --- آماده سازی Reference ---
193
  if isinstance(reference_wav, tuple):
194
  ref_sr, ref_data = reference_wav if isinstance(reference_wav[0], int) else (reference_wav[1], reference_wav[0])
195
  else:
 
202
  if ref_sr != 24000:
203
  ref_tensor = torchaudio.functional.resample(ref_tensor, ref_sr, 24000)
204
  ref_sr = 24000
205
+ ref_tensor = ref_tensor / (torch.max(torch.abs(ref_tensor)) + 1e-6) * 0.95
 
 
206
 
207
  if ref_tensor.shape[1] > 24000 * 20:
208
  ref_tensor = ref_tensor[:, :24000 * 20]
209
 
210
  save_audio_pcm16(ref_tensor, temp_reference_path, ref_sr)
211
 
212
+ # --- تنظیمات میکسینگ عمیق (Deep Cross-Fade) ---
213
  pipeline = get_pipeline()
 
214
  SR = 24000
 
 
215
 
216
+ # گام حرکت: ۱۰ ثانیه
217
+ STEP_SIZE = 10 * SR
218
+ # طول پردازش: ۱۲ ثانیه (۲ ثانیه همپوشانی)
219
+ # این ۲ ثانیه اضافه باعث می‌شود هیچوقت لبه تیز نداشته باشیم
220
+ PROCESS_LEN = 12 * SR
221
+ OVERLAP = PROCESS_LEN - STEP_SIZE # 2 seconds
222
 
223
  total_samples = content_tensor.shape[1]
224
+ print(f"[{session_id}] Processing Deep Mix Mode...")
225
 
226
+ # آرایه نهایی را کمی بزرگتر می‌گیریم تا جا کم نیاید
227
+ final_audio = np.zeros(total_samples + OVERLAP)
228
+ # آرایه‌ای برای شمارش وزن‌ها (برای نرمال کردن میکس)
229
+ weight_accumulator = np.zeros(total_samples + OVERLAP)
230
 
231
+ # ایجاد پنجره محو شونده (Trapezoid Window)
232
+ # 1 ثانیه Fade In -- 10 ثانیه ثابت -- 1 ثانیه Fade Out
233
+ fade_samples = SR # 1 second fade
234
+ window = np.ones(PROCESS_LEN)
235
+ window[:fade_samples] = np.linspace(0, 1, fade_samples)
236
+ window[-fade_samples:] = np.linspace(1, 0, fade_samples)
237
 
238
+ for start in range(0, total_samples, STEP_SIZE):
239
+ # انتخاب بازه ورودی (کمی بزرگتر از استپ)
240
+ end = min(start + PROCESS_LEN, total_samples)
241
+ current_len = end - start
 
 
 
 
 
 
 
 
 
242
 
243
+ if current_len <= 0: break
 
 
 
 
 
244
 
245
+ current_input_chunk = content_tensor[:, start:end]
246
+ save_audio_pcm16(current_input_chunk, temp_content_path, SR)
247
 
248
  try:
249
  gen = pipeline.inference_fm(
250
  src_wav_path=temp_content_path,
251
  timbre_ref_wav_path=temp_reference_path,
252
+ flow_matching_steps=64,
253
  )
254
 
255
  if torch.isnan(gen).any(): gen = torch.nan_to_num(gen, nan=0.0)
256
+ gen = gen.cpu().squeeze().numpy()
257
+
258
+ # اگر طول خروجی کمتر از انتظار بود، با سکوت پر کن
259
+ if len(gen) < current_len:
260
+ gen = np.pad(gen, (0, current_len - len(gen)))
261
+ elif len(gen) > current_len:
262
+ gen = gen[:current_len]
263
 
264
+ # اعمال پنجره روی خروجی (اگر تکه آخر است، پنجره را برش بزن)
265
+ current_window = window[:current_len]
266
+ # برای تکه اول، Fade In نیاز نیست (چون شروع فایل است)
267
+ if start == 0:
268
+ current_window[:fade_samples] = 1.0
269
+ # برای تکه آخر، Fade Out نیاز نیست (چون پایان فایل است)
270
+ if end == total_samples:
271
+ current_window[-fade_samples:] = 1.0
 
272
 
273
+ weighted_gen = gen * current_window
274
 
275
+ # اضافه کردن به آرایه اصلی (Overlap-Add)
276
+ final_audio[start:end] += weighted_gen
277
+ weight_accumulator[start:end] += current_window
278
 
279
  except Exception as e:
280
+ print(f"Error: {e}")
 
 
 
 
 
 
 
 
 
 
281
 
282
+ # نرمال‌سازی نهایی (تقسیم بر وزن‌ها برای یکنواخت شدن صدا در نقاط اتصال)
283
+ # جایی که وزن صفر است را ۱ می‌کنیم تا تقسیم بر صفر نشود
284
+ weight_accumulator[weight_accumulator == 0] = 1.0
285
+ final_audio = final_audio / weight_accumulator
286
+
287
+ # برش بخش‌های اضافه انتهای فایل
288
+ final_audio = final_audio[:total_samples]
289
+
290
+ save_audio_pcm16(final_audio, output_path, SR)
291
  return output_path
292
 
293
  finally:
294
  if os.path.exists(temp_content_path): os.remove(temp_content_path)
295
  if os.path.exists(temp_reference_path): os.remove(temp_reference_path)
296
 
297
+ with gr.Blocks(title="Vevo-Timbre (Seamless)") as demo:
298
  gr.Markdown("## Vevo-Timbre: Zero-Shot Voice Conversion")
299
+ gr.Markdown("نسخه Seamless: استفاده از تکنیک Overlap-Add برای حذف کامل پرش و لرزش صدا.")
300
 
301
  with gr.Row():
302
  with gr.Column():