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

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +45 -65
app.py CHANGED
@@ -174,22 +174,7 @@ def vevo_timbre(content_wav, reference_wav):
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:
181
- content_sr, content_data = content_wav
182
-
183
- if len(content_data.shape) > 1 and content_data.shape[1] > 1:
184
- content_data = np.mean(content_data, axis=1)
185
-
186
- content_tensor = torch.FloatTensor(content_data).unsqueeze(0)
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,126 +187,114 @@ def vevo_timbre(content_wav, reference_wav):
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
- # برش رفرنس به 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(
240
  src_wav_path=temp_content_path,
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
 
@@ -329,15 +302,22 @@ def vevo_timbre(content_wav, reference_wav):
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():
338
- timbre_content = gr.Audio(label="Source Audio", type="numpy")
339
- timbre_reference = gr.Audio(label="Target Timbre", type="numpy")
340
- timbre_button = gr.Button("Generate", variant="primary")
341
  with gr.Column():
342
  timbre_output = gr.Audio(label="Result")
343
 
 
174
  raise ValueError("Please upload audio files")
175
 
176
  try:
177
+ # --- آماده سازی Reference (اول رفرنس را پردازش می‌کنیم تا سطح صدا را بگیریم) ---
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
178
  if isinstance(reference_wav, tuple):
179
  ref_sr, ref_data = reference_wav if isinstance(reference_wav[0], int) else (reference_wav[1], reference_wav[0])
180
  else:
 
187
  if ref_sr != 24000:
188
  ref_tensor = torchaudio.functional.resample(ref_tensor, ref_sr, 24000)
189
  ref_sr = 24000
 
190
 
191
+ # محاسبه انرژی رفرنس
192
+ ref_max_vol = torch.max(torch.abs(ref_tensor)) + 1e-6
193
+ ref_tensor = ref_tensor / ref_max_vol * 0.95 # نرمال سازی رفرنس
194
+
195
+ # برش رفرنس به 20 ثانیه
196
  if ref_tensor.shape[1] > 24000 * 20:
197
  ref_tensor = ref_tensor[:, :24000 * 20]
198
 
199
  save_audio_pcm16(ref_tensor, temp_reference_path, ref_sr)
200
+
201
+ # --- آماده سازی Content ---
202
+ if isinstance(content_wav, tuple):
203
+ content_sr, content_data = content_wav if isinstance(content_wav[0], int) else (content_wav[1], content_wav[0])
204
+ else:
205
+ content_sr, content_data = content_wav
206
+
207
+ if len(content_data.shape) > 1 and content_data.shape[1] > 1:
208
+ content_data = np.mean(content_data, axis=1)
209
 
210
+ content_tensor = torch.FloatTensor(content_data).unsqueeze(0)
211
+ if content_sr != 24000:
212
+ content_tensor = torchaudio.functional.resample(content_tensor, content_sr, 24000)
213
+ content_sr = 24000
214
+
215
+ # نرمال سازی هوشمند: صدای ورودی را هم‌سطح صدای رفرنس می‌کنیم
216
+ content_tensor = content_tensor / (torch.max(torch.abs(content_tensor)) + 1e-6) * 0.95
217
+
218
+ # --- منطق Chunking ---
219
  pipeline = get_pipeline()
220
 
221
  SR = 24000
222
+ CHUNK_LEN = 10 * SR
223
+ OVERLAP = 1 * SR
 
224
  INPUT_SIZE = CHUNK_LEN + OVERLAP
225
 
226
  total_samples = content_tensor.shape[1]
227
+ print(f"[{session_id}] High Quality Processing (64 Steps)... Duration: {total_samples/SR:.2f}s")
228
 
229
  final_parts = []
230
  overlap_buffer = None
231
 
 
232
  for start in range(0, total_samples, CHUNK_LEN):
 
233
  end_input = min(start + INPUT_SIZE, total_samples)
234
 
235
  current_input_chunk = content_tensor[:, start:end_input]
236
  save_audio_pcm16(current_input_chunk, temp_content_path, SR)
237
 
 
 
238
  try:
239
  gen = pipeline.inference_fm(
240
  src_wav_path=temp_content_path,
241
  timbre_ref_wav_path=temp_reference_path,
242
+ flow_matching_steps=64, # <--- کیفیت بالا (قبلاً 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
  current_len = len(gen)
250
 
 
251
  if overlap_buffer is not None:
 
 
252
  mix_len = len(overlap_buffer)
 
 
253
  if current_len < mix_len:
254
  mix_len = current_len
255
  overlap_buffer = overlap_buffer[:mix_len]
256
 
 
257
  head_to_mix = gen[:mix_len]
258
  body_rest = gen[mix_len:]
259
 
 
260
  alpha = np.linspace(0, 1, mix_len)
 
261
  blended_segment = (overlap_buffer * (1 - alpha)) + (head_to_mix * alpha)
262
 
 
263
  final_parts.append(blended_segment)
264
 
 
 
265
  if len(body_rest) > OVERLAP:
 
266
  pure_body = body_rest[:-OVERLAP]
267
  final_parts.append(pure_body)
 
268
  overlap_buffer = body_rest[-OVERLAP:]
269
  else:
 
270
  final_parts.append(body_rest)
271
  overlap_buffer = None
272
 
273
  else:
 
274
  if current_len > OVERLAP:
 
275
  final_parts.append(gen[:-OVERLAP])
 
276
  overlap_buffer = gen[-OVERLAP:]
277
  else:
 
278
  final_parts.append(gen)
279
  overlap_buffer = None
280
 
281
  except Exception as e:
282
+ print(f"Error in chunk: {e}")
 
283
  missing_len = end_input - start
 
284
  if overlap_buffer is not None:
285
  missing_len -= len(overlap_buffer)
286
+ final_parts.append(overlap_buffer)
287
  overlap_buffer = None
 
288
  final_parts.append(np.zeros(max(0, missing_len)))
289
 
 
290
  if overlap_buffer is not None:
291
  final_parts.append(overlap_buffer)
292
 
 
293
  if len(final_parts) > 0:
294
  full_audio = np.concatenate(final_parts)
295
  else:
296
+ full_audio = np.zeros(24000)
297
 
 
298
  save_audio_pcm16(full_audio, output_path, SR)
299
  return output_path
300
 
 
302
  if os.path.exists(temp_content_path): os.remove(temp_content_path)
303
  if os.path.exists(temp_reference_path): os.remove(temp_reference_path)
304
 
305
+ with gr.Blocks(title="Vevo-Timbre (Ultra Quality)") as demo:
306
+ gr.Markdown("## Vevo-Timbre: Zero-Shot Voice Conversion (Ultra Quality)")
307
+ gr.Markdown("""
308
+ **ویژگی‌ها:**
309
+ - **Steps 64:** کیفیت و دقت بافت صدا دو برابر شده است.
310
+ - **Auto-Leveling:** سطح صدای شما با مدل تنظی�� می‌شود.
311
+ - **Seamless Stitching:** بدون پرش و بدون اضافه شدن زمان.
312
+
313
+ **نکته مهم:** برای بهترین نتیجه، سعی کنید **لحن، سرعت و احساس** صدای خودتان را شبیه فایل هدف کنید. مدل فقط جنس صدا را تغییر می‌دهد، نه بازیگری شما را!
314
+ """)
315
 
316
  with gr.Row():
317
  with gr.Column():
318
+ timbre_content = gr.Audio(label="Source Audio (صدای شما)", type="numpy")
319
+ timbre_reference = gr.Audio(label="Target Timbre (صدای هدف)", type="numpy")
320
+ timbre_button = gr.Button("Generate (Ultra Quality)", variant="primary")
321
  with gr.Column():
322
  timbre_output = gr.Audio(label="Result")
323