Opera8 commited on
Commit
f375b6c
·
verified ·
1 Parent(s): 428894f

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +62 -36
app.py CHANGED
@@ -187,8 +187,6 @@ 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
  # --- آماده سازی Reference ---
@@ -204,73 +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_tensor = ref_tensor / (torch.max(torch.abs(ref_tensor)) + 1e-6) * 0.95
209
 
210
- # اگر رفرنس خیلی طولانی باشد، فقط 20 ثانیه اول کافی است (برای استخراج Timbre)
211
- # این کار سرعت را بالا می‌برد و تاثیری در کیفیت ندارد
212
  if ref_tensor.shape[1] > 24000 * 20:
213
  ref_tensor = ref_tensor[:, :24000 * 20]
214
 
215
  save_audio_pcm16(ref_tensor, temp_reference_path, ref_sr)
216
 
217
- # --- منطق Chunking (حل مشکل فایل طولانی) ---
218
  pipeline = get_pipeline()
219
 
220
- # هر تکه 15 ثانیه (360000 سمپل)
221
- CHUNK_SIZE = 15 * 24000
222
- total_samples = content_tensor.shape[1]
 
223
 
224
- print(f"[{session_id}] Audio Duration: {total_samples/24000:.2f}s. Starting Chunking...")
 
225
 
226
- generated_chunks = []
227
 
228
- for start in range(0, total_samples, CHUNK_SIZE):
229
- end = min(start + CHUNK_SIZE, total_samples)
230
- current_chunk = content_tensor[:, start:end]
 
 
231
 
232
- # ذخیره تکه جاری
233
- save_audio_pcm16(current_chunk, temp_content_path, 24000)
234
 
235
- print(f"[{session_id}] Processing chunk {start/24000:.1f}s to {end/24000:.1f}s")
236
 
237
  try:
238
- gen_chunk = pipeline.inference_fm(
239
  src_wav_path=temp_content_path,
240
  timbre_ref_wav_path=temp_reference_path,
241
  flow_matching_steps=32,
242
  )
 
 
 
243
 
244
- # رفع NaN
245
- if torch.isnan(gen_chunk).any() or torch.isinf(gen_chunk).any():
246
- gen_chunk = torch.nan_to_num(gen_chunk, nan=0.0, posinf=0.95, neginf=-0.95)
247
-
248
- # مطمئن شویم تنسور دو بعدی است [1, T]
249
- if gen_chunk.dim() == 1:
250
- gen_chunk = gen_chunk.unsqueeze(0)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
251
 
252
- generated_chunks.append(gen_chunk.cpu())
253
-
 
 
 
 
 
 
 
 
254
  except Exception as e:
255
- print(f"Error in chunk: {e}")
256
- # اگر خطایی رخ داد، سکوت اضافه کن تا فایل قطع نشود
257
- silence = torch.zeros((1, end - start))
258
- generated_chunks.append(silence)
259
 
260
- # چسباندن تکه‌ها
261
- final_audio = torch.cat(generated_chunks, dim=1)
262
 
263
  # ذخیره نهایی
264
- save_audio_pcm16(final_audio, output_path, 24000)
265
  return output_path
266
 
267
  finally:
268
  if os.path.exists(temp_content_path): os.remove(temp_content_path)
269
  if os.path.exists(temp_reference_path): os.remove(temp_reference_path)
270
 
271
- with gr.Blocks(title="Vevo-Timbre (Long Audio)") as demo:
272
  gr.Markdown("## Vevo-Timbre: Zero-Shot Voice Conversion")
273
- gr.Markdown("پشتیبانی کامل از فایل‌های طولانی (بدون نویز و قطعی)")
274
 
275
  with gr.Row():
276
  with gr.Column():
 
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 ---
 
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
+ # --- منطق 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(
238
  src_wav_path=temp_content_path,
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():