Opera8 commited on
Commit
09eb27e
·
verified ·
1 Parent(s): f375b6c

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +90 -55
app.py CHANGED
@@ -204,34 +204,36 @@ def vevo_timbre(content_wav, reference_wav):
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
- # --- منطق Cross-Fade Chunking ---
213
  pipeline = get_pipeline()
214
 
215
  SR = 24000
216
- MAIN_CHUNK = 10 * SR # 10 ثانیه اصلی
217
- OVERLAP = 1 * SR # 1 ثانیه هم‌پوشانی (برای میکس)
218
- STEP = MAIN_CHUNK # قدم حرکت (10 ثانیه)
 
219
 
220
  total_samples = content_tensor.shape[1]
221
- print(f"[{session_id}] Duration: {total_samples/SR:.2f}s. Chunking 10s with Cross-fade...")
222
 
223
- final_output = []
 
224
 
225
- # حلقه روی تکه‌ها با هم‌پوشانی
226
- # ما هر بار 'MAIN_CHUNK + OVERLAP' را پردازش می‌کنیم (یعنی 11 ثانیه)
227
- # مگر اینکه به آخر فایل رسیده باشیم
228
- for start in range(0, total_samples, STEP):
229
- end = min(start + MAIN_CHUNK + OVERLAP, total_samples)
230
 
231
- current_input_chunk = content_tensor[:, start:end]
232
  save_audio_pcm16(current_input_chunk, temp_content_path, SR)
233
 
234
- print(f"[{session_id}] Processing {start/SR:.1f}s to {end/SR:.1f}s")
235
 
236
  try:
237
  gen = pipeline.inference_fm(
@@ -239,64 +241,97 @@ def vevo_timbre(content_wav, reference_wav):
239
  timbre_ref_wav_path=temp_reference_path,
240
  flow_matching_steps=32,
241
  )
 
242
  if torch.isnan(gen).any(): gen = torch.nan_to_num(gen, nan=0.0)
243
  if gen.dim() == 1: gen = gen.unsqueeze(0)
244
- gen = gen.cpu().squeeze(0).numpy() # تبدیل به numpy
245
 
246
- # منطق میکس (Cross-fade)
247
- if start == 0:
248
- # تکه اول: فعلاً نگه می‌داریم (هنوز چیزی برای میکس نیست)
249
- # اگر فایل کوتاه باشد و به هم‌پوشانی نرسد، کلش را اضافه می‌کنیم
250
- if len(gen) <= MAIN_CHUNK:
251
- final_output.append(gen)
252
- else:
253
- # قسمت اصلی را اضافه کن، قسمت اورلپ را برای میکس با بعدی نگه دار
254
- final_output.append(gen[:-OVERLAP])
255
- overlap_buffer = gen[-OVERLAP:]
256
- else:
257
- # تکه‌های بعدی:
258
- # 1. قسمت اورلپ قبلی را با شروع این تکه میکس کن
259
- current_overlap = gen[:OVERLAP]
260
- if len(current_overlap) == len(overlap_buffer):
261
- # ایجاد منحنی فید (Fade Curves)
262
- alpha = np.linspace(0, 1, len(overlap_buffer))
263
- # فرمول: (قبلی * نزولی) + (جدید * صعودی)
264
- blended = (overlap_buffer * (1 - alpha)) + (current_overlap * alpha)
265
- final_output.append(blended)
266
- else:
267
- # اگر سایزها نخواند (خیلی نادر)، فقط قبلی را بچسبان
268
- final_output.append(overlap_buffer)
 
 
 
 
269
 
270
- # 2. بقیه فایل را مدیریت کن
271
- if len(gen) <= OVERLAP + MAIN_CHUNK: # اگر تکه آخر است
272
- final_output.append(gen[OVERLAP:])
273
- overlap_buffer = None # تمام شد
 
 
 
 
274
  else:
275
- # قسمت وسط را اضافه کن
276
- final_output.append(gen[OVERLAP:-OVERLAP])
277
- # اورلپ جدید را ذخیره کن
 
 
 
 
 
 
 
278
  overlap_buffer = gen[-OVERLAP:]
279
-
 
 
 
 
280
  except Exception as e:
281
  print(f"Error: {e}")
282
- silence_len = end - start
283
- final_output.append(np.zeros(silence_len))
284
- overlap_buffer = np.zeros(OVERLAP)
 
 
 
 
 
 
285
 
286
- # چسباندن همه آرایه‌ها
287
- full_audio = np.concatenate(final_output)
288
-
289
- # ذخیره نهایی
290
- sf.write(output_path, full_audio, SR, subtype='PCM_16')
 
 
 
 
 
 
 
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 (Professional)") as demo:
298
  gr.Markdown("## Vevo-Timbre: Zero-Shot Voice Conversion")
299
- gr.Markdown("پشتیبانی از فایل‌های نامحدود با کیفیت بالا (10s Chunking + Smooth Cross-Fade)")
300
 
301
  with gr.Row():
302
  with gr.Column():
 
204
  ref_sr = 24000
205
  ref_tensor = ref_tensor / (torch.max(torch.abs(ref_tensor)) + 1e-6) * 0.95
206
 
207
+ # برش رفرنس به 20 ثانیه برای سرعت
208
  if ref_tensor.shape[1] > 24000 * 20:
209
  ref_tensor = ref_tensor[:, :24000 * 20]
210
 
211
  save_audio_pcm16(ref_tensor, temp_reference_path, ref_sr)
212
 
213
+ # --- منطق دقیق Seamless Chunking ---
214
  pipeline = get_pipeline()
215
 
216
  SR = 24000
217
+ CHUNK_LEN = 10 * SR # 10 ثانیه اصلی
218
+ OVERLAP = 1 * SR # 1 ثانیه همپوشانی
219
+ # مقدار ورودی به مدل = 10 ثانیه + 1 ثانیه اورلپ = 11 ثانیه
220
+ INPUT_SIZE = CHUNK_LEN + OVERLAP
221
 
222
  total_samples = content_tensor.shape[1]
223
+ print(f"[{session_id}] Duration: {total_samples/SR:.2f}s. Seamless Chunking...")
224
 
225
+ final_parts = []
226
+ overlap_buffer = None
227
 
228
+ # حرکت با قدم‌های 10 ثانیه‌ای
229
+ for start in range(0, total_samples, CHUNK_LEN):
230
+ # انتخاب بازه: از شروع تا 11 ثانیه جلوتر (یا تا آخر فایل)
231
+ end_input = min(start + INPUT_SIZE, total_samples)
 
232
 
233
+ current_input_chunk = content_tensor[:, start:end_input]
234
  save_audio_pcm16(current_input_chunk, temp_content_path, SR)
235
 
236
+ print(f"[{session_id}] Processing input {start/SR:.1f}s to {end_input/SR:.1f}s")
237
 
238
  try:
239
  gen = pipeline.inference_fm(
 
241
  timbre_ref_wav_path=temp_reference_path,
242
  flow_matching_steps=32,
243
  )
244
+ # تمیزکاری داده‌ها
245
  if torch.isnan(gen).any(): gen = torch.nan_to_num(gen, nan=0.0)
246
  if gen.dim() == 1: gen = gen.unsqueeze(0)
247
+ gen = gen.cpu().squeeze(0).numpy()
248
 
249
+ # --- الگوریتم میکس دقیق ---
250
+ # gen اکنون شامل [بدنه اصلی (10s)] + [دم (1s)] است (مگر اینکه تکه آخر باشد)
251
+
252
+ current_len = len(gen)
253
+
254
+ # اگر بافر از دور قبلی داریم (یعنی تکه اول نیستیم)
255
+ if overlap_buffer is not None:
256
+ # باید بافر قبلی را با ابتدای این تکه میکس کنیم
257
+ # طول ناحیه میکس = طول بافر
258
+ mix_len = len(overlap_buffer)
259
+
260
+ # اگر تکه جاری کوتاه‌تر از بافر است (خیلی نادر)، برش بزن
261
+ if current_len < mix_len:
262
+ mix_len = current_len
263
+ overlap_buffer = overlap_buffer[:mix_len]
264
+
265
+ # جدا کردن سرِ تکه جاری برای میکس
266
+ head_to_mix = gen[:mix_len]
267
+ body_rest = gen[mix_len:]
268
+
269
+ # ایجاد منحنی فید (Fade In/Out)
270
+ alpha = np.linspace(0, 1, mix_len)
271
+ # فرمول: (دم قبلی * پایین‌رونده) + (سر فعلی * بالا‌رونده)
272
+ blended_segment = (overlap_buffer * (1 - alpha)) + (head_to_mix * alpha)
273
+
274
+ # اضافه کردن بخش میکس شده به خروجی
275
+ final_parts.append(blended_segment)
276
 
277
+ # حالا باید بدنه اصلی را مدیریت کنیم
278
+ # اگر به اندازه کافی دیتا داریم که 1 ثانیه آخر را برای دور بعد نگه داریم
279
+ if len(body_rest) > OVERLAP:
280
+ # بخش خالص وسط
281
+ pure_body = body_rest[:-OVERLAP]
282
+ final_parts.append(pure_body)
283
+ # آپدیت بافر برای دور بعد
284
+ overlap_buffer = body_rest[-OVERLAP:]
285
  else:
286
+ # تکه آخر است و اورلپ ندارد، کلش را اضافه کن
287
+ final_parts.append(body_rest)
288
+ overlap_buffer = None
289
+
290
+ else:
291
+ # تکه اول است (هنوز بافری نداریم)
292
+ if current_len > OVERLAP:
293
+ # بخش اصلی را اضافه کن
294
+ final_parts.append(gen[:-OVERLAP])
295
+ # بخش آخر را بفرست توی بافر
296
  overlap_buffer = gen[-OVERLAP:]
297
+ else:
298
+ # فایل خیلی کوتاه است، کلش را اضافه کن
299
+ final_parts.append(gen)
300
+ overlap_buffer = None
301
+
302
  except Exception as e:
303
  print(f"Error: {e}")
304
+ # در صورت خطا، سکوت جایگزین کن تا تایمینگ به هم نریزد
305
+ missing_len = end_input - start
306
+ # اگر تکه اول نبود، اورلپ را کم کن
307
+ if overlap_buffer is not None:
308
+ missing_len -= len(overlap_buffer)
309
+ final_parts.append(overlap_buffer) # بافر قبلی را خالی کن
310
+ overlap_buffer = None
311
+
312
+ final_parts.append(np.zeros(max(0, missing_len)))
313
 
314
+ # اگر بافری باقی مانده (از تکه آخر)، اضافه‌اش کن
315
+ if overlap_buffer is not None:
316
+ final_parts.append(overlap_buffer)
317
+
318
+ # چسباندن نهایی
319
+ if len(final_parts) > 0:
320
+ full_audio = np.concatenate(final_parts)
321
+ else:
322
+ full_audio = np.zeros(24000) # Fallback
323
+
324
+ # ذخیره
325
+ save_audio_pcm16(full_audio, output_path, SR)
326
  return output_path
327
 
328
  finally:
329
  if os.path.exists(temp_content_path): os.remove(temp_content_path)
330
  if os.path.exists(temp_reference_path): os.remove(temp_reference_path)
331
 
332
+ with gr.Blocks(title="Vevo-Timbre (Perfect Stitch)") as demo:
333
  gr.Markdown("## Vevo-Timbre: Zero-Shot Voice Conversion")
334
+ gr.Markdown("نسخه نهایی: کیفیت ۱۶ بیتی، بدون نویز، زمان‌بندی دقیق، بدون سکوت بین تکه‌ها.")
335
 
336
  with gr.Row():
337
  with gr.Column():