Files changed (4) hide show
  1. README.md +1 -1
  2. app.py +102 -404
  3. pre-requirements.txt +0 -2
  4. requirements.txt +4 -12
README.md CHANGED
@@ -4,7 +4,7 @@ emoji: ⚡
4
  colorFrom: gray
5
  colorTo: indigo
6
  sdk: gradio
7
- sdk_version: 5.43.1
8
  app_file: app.py
9
  license: mit
10
  pinned: true
 
4
  colorFrom: gray
5
  colorTo: indigo
6
  sdk: gradio
7
+ sdk_version: 4.28.3
8
  app_file: app.py
9
  license: mit
10
  pinned: true
app.py CHANGED
@@ -1,5 +1,4 @@
1
  import os
2
- import subprocess
3
  import gradio as gr
4
  import spaces
5
  from infer_rvc_python import BaseLoader
@@ -7,7 +6,7 @@ import random
7
  import logging
8
  import time
9
  import soundfile as sf
10
- from infer_rvc_python.main import download_manager, load_hu_bert, Config
11
  import zipfile
12
  import edge_tts
13
  import asyncio
@@ -20,67 +19,16 @@ from pydub import AudioSegment
20
  import noisereduce as nr
21
  import numpy as np
22
  import urllib.request
23
- import urllib.parse
24
- import urllib.error
25
  import shutil
26
  import threading
27
- import argparse
28
- import sys
29
- import torch
30
- import fairseq
31
- from picklescan.scanner import scan_file_path
32
- import hashlib
33
-
34
-
35
- ALLOWED_DOMAINS = {"huggingface.co", "hf.co"}
36
- MODEL_CACHE = {}
37
-
38
- parser = argparse.ArgumentParser(description="Run the app with optional sharing")
39
- parser.add_argument(
40
- '--share',
41
- action='store_true',
42
- help='Enable sharing mode'
43
- )
44
- parser.add_argument(
45
- '--theme',
46
- type=str,
47
- default="aliabid94/new-theme",
48
- help='Set the theme (default: aliabid94/new-theme)'
49
- )
50
- args = parser.parse_args()
51
-
52
- IS_COLAB = True if ('google.colab' in sys.modules or args.share) else False
53
- IS_ZERO_GPU = os.getenv("SPACES_ZERO_GPU")
54
 
55
  logging.getLogger("infer_rvc_python").setLevel(logging.ERROR)
56
 
57
- torch.serialization.add_safe_globals([fairseq.data.dictionary.Dictionary])
58
  converter = BaseLoader(only_cpu=False, hubert_path=None, rmvpe_path=None)
59
- converter.hu_bert_model = load_hu_bert(Config(only_cpu=False), converter.hubert_path)
60
-
61
- test_model = "https://huggingface.co/sail-rvc/Aldeano_Minecraft__RVC_V2_-_500_Epochs_/resolve/main/model.pth?download=true, https://huggingface.co/sail-rvc/Aldeano_Minecraft__RVC_V2_-_500_Epochs_/resolve/main/model.index?download=true"
62
- test_names = ["model.pth", "model.index"]
63
-
64
- for url, filename in zip(test_model.split(", "), test_names):
65
- try:
66
- download_manager(
67
- url=url,
68
- path=".",
69
- extension="",
70
- overwrite=False,
71
- progress=True,
72
- )
73
- if not os.path.isfile(filename):
74
- raise FileNotFoundError
75
- except Exception:
76
- with open(filename, "wb") as f:
77
- pass
78
 
79
  title = "<center><strong><font size='7'>RVC⚡ZERO</font></strong></center>"
80
- description = "This demo is provided for educational and research purposes only. The authors and contributors of this project do not endorse or encourage any misuse or unethical use of this software. Any use of this software for purposes other than those intended is solely at the user's own risk. The authors and contributors shall not be held responsible for any damages or liabilities arising from the use of this demo inappropriately." if IS_ZERO_GPU else ""
81
- RESOURCES = "- You can also try `RVC⚡ZERO` in Colab’s free tier, which provides free GPU [link](https://github.com/R3gm/rvc_zero_ui?tab=readme-ov-file#rvczero)."
82
- theme = args.theme
83
- delete_cache_time = (3200, 3200) if IS_ZERO_GPU else (86400, 86400)
84
 
85
  PITCH_ALGO_OPT = [
86
  "pm",
@@ -91,54 +39,6 @@ PITCH_ALGO_OPT = [
91
  ]
92
 
93
 
94
- def check_model_safety(file_path: str):
95
- """Statically checks model or archive integrity. Raises ValueError if unsafe."""
96
- if not file_path or not os.path.exists(file_path):
97
- print(f"Skip file: {file_path}")
98
- return
99
-
100
- ext = os.path.splitext(file_path)[1].lower()
101
- if ext in (".pth", ".pt", ".bin", ".zip", ".pkl"):
102
- try:
103
- result = scan_file_path(file_path)
104
- if result and result.infected_files > 0:
105
- raise ValueError(
106
- f"Integrity check failed: '{os.path.basename(file_path)}' contains unsupported or unsafe structures."
107
- )
108
- except ValueError:
109
- raise
110
- except Exception as e:
111
- print(f"Integrity check skipped for {file_path}: {e}")
112
-
113
-
114
- def get_file_hash(file_path: str) -> str:
115
- hasher = hashlib.sha256()
116
- with open(file_path, "rb") as f:
117
- while chunk := f.read(65536):
118
- hasher.update(chunk)
119
- return hasher.hexdigest()
120
-
121
-
122
- async def get_voices_list(proxy=None):
123
- """Print all available voices."""
124
- from edge_tts import list_voices
125
- voices = await list_voices(proxy=proxy)
126
- voices = sorted(voices, key=lambda voice: voice["ShortName"])
127
-
128
- table = [
129
- {
130
- "ShortName": voice["ShortName"],
131
- "Gender": voice["Gender"],
132
- "ContentCategories": ", ".join(voice["VoiceTag"]["ContentCategories"]),
133
- "VoicePersonalities": ", ".join(voice["VoiceTag"]["VoicePersonalities"]),
134
- "FriendlyName": voice["FriendlyName"],
135
- }
136
- for voice in voices
137
- ]
138
-
139
- return table
140
-
141
-
142
  def find_files(directory):
143
  file_paths = []
144
  for filename in os.listdir(directory):
@@ -162,30 +62,8 @@ def unzip_in_folder(my_zip, my_dir):
162
  def find_my_model(a_, b_):
163
 
164
  if a_ is None or a_.endswith(".pth"):
165
- if a_ and a_.endswith(".pth"):
166
- check_model_safety(a_)
167
  return a_, b_
168
 
169
- input_hash = None
170
- if a_ and os.path.exists(a_):
171
- input_hash = get_file_hash(a_)
172
- if b_ and os.path.exists(b_):
173
- input_hash += "_" + get_file_hash(b_)
174
-
175
- if input_hash and input_hash in MODEL_CACHE:
176
- cached_model, cached_index = MODEL_CACHE[input_hash]
177
- model_exists = cached_model and os.path.exists(cached_model)
178
- index_exists = (cached_index is None) or os.path.exists(cached_index)
179
-
180
- if model_exists and index_exists:
181
- check_model_safety(cached_model)
182
- gr.Info(f"Model found: {cached_model}")
183
- if cached_index:
184
- gr.Info(f"Index found: {cached_index}")
185
- return cached_model, cached_index
186
- else:
187
- del MODEL_CACHE[input_hash]
188
-
189
  txt_files = []
190
  for base_file in [a_, b_]:
191
  if base_file is not None and base_file.endswith(".txt"):
@@ -197,18 +75,14 @@ def find_my_model(a_, b_):
197
  with open(txt, 'r') as file:
198
  first_line = file.readline()
199
 
200
- url_to_download = first_line.strip()
201
- ensure_valid_file(url_to_download)
202
-
203
  download_manager(
204
- url=url_to_download,
205
  path=directory,
206
  extension="",
207
  )
208
 
209
  for f in find_files(directory):
210
  if f.endswith(".zip"):
211
- check_model_safety(f)
212
  unzip_in_folder(f, directory)
213
 
214
  model = None
@@ -217,11 +91,10 @@ def find_my_model(a_, b_):
217
 
218
  for ff in end_files:
219
  if ff.endswith(".pth"):
220
- check_model_safety(ff)
221
- model = ff
222
  gr.Info(f"Model found: {ff}")
223
  if ff.endswith(".index"):
224
- index = ff
225
  gr.Info(f"Index found: {ff}")
226
 
227
  if not model:
@@ -230,115 +103,40 @@ def find_my_model(a_, b_):
230
  if not index:
231
  gr.Warning("Index not found")
232
 
233
- if model and input_hash:
234
- MODEL_CACHE[input_hash] = (model, index)
235
-
236
  return model, index
237
 
238
 
239
- def validate_url(url: str) -> str:
240
- """Validate URL protocol and host."""
241
- url = url.strip()
242
- if not url:
243
- raise ValueError("URL cannot be empty.")
244
 
245
- parsed = urllib.parse.urlparse(url)
246
- if parsed.scheme != "https":
247
- raise ValueError(f"Invalid protocol '{parsed.scheme}'. Only HTTPS is allowed.")
248
 
249
- hostname = (parsed.hostname or "").lower()
250
- if not any(hostname == d or hostname.endswith("." + d) for d in ALLOWED_DOMAINS):
251
- raise ValueError("Only downloads from Hugging Face are allowed.")
252
-
253
- return url
254
-
255
-
256
- def get_supported_audio_video_extensions():
257
  try:
258
- subtitle_codecs = set()
259
- decoders_res = subprocess.run(
260
- ["ffmpeg", "-decoders"], capture_output=True, text=True, check=True
261
- )
262
- for line in decoders_res.stdout.splitlines():
263
- line_str = line.strip()
264
- if len(line_str) > 7 and line_str[0] in ("V", "A", "S"):
265
- parts = line_str.split()
266
- if len(parts) >= 2:
267
- media_type = line_str[0]
268
- codec_name = parts[1].lower()
269
- if media_type == "S":
270
- subtitle_codecs.add(codec_name)
271
-
272
- demuxers_res = subprocess.run(
273
- ["ffmpeg", "-demuxers"], capture_output=True, text=True, check=True
274
- )
275
- extensions = set()
276
- for line in demuxers_res.stdout.splitlines():
277
- line_str = line.strip()
278
- if line_str.startswith("D"):
279
- parts = line_str.split(maxsplit=2)
280
- if len(parts) >= 2:
281
- demux_names = parts[1].split(",")
282
- description = parts[2].lower() if len(parts) >= 3 else ""
283
-
284
- if any(
285
- sub_kw in description
286
- for sub_kw in ["subtitle", "caption", "teletext", "lyrics"]
287
- ):
288
- continue
289
-
290
- for ext in demux_names:
291
- clean_ext = ext.strip().lower()
292
- if clean_ext in subtitle_codecs:
293
- continue
294
- if clean_ext and clean_ext.isalnum():
295
- extensions.add(f".{clean_ext}")
296
-
297
- return sorted(list(extensions))
298
- except Exception as e:
299
- print(f"Error querying ffmpeg: {e}")
300
- return [".mp3", ".wav", ".flac", ".m4a", ".aac", ".ogg", ".opus", ".wma", ".aiff", ".aif", ".alac", ".caf", ".amr"]
301
-
302
-
303
- def ensure_valid_file(url):
304
- url = validate_url(url)
305
-
306
- try:
307
- request = urllib.request.Request(url, method="HEAD", headers={"User-Agent": "Mozilla/5.0"})
308
- with urllib.request.urlopen(request, timeout=15) as response:
309
- content_length = response.headers.get("Content-Length")
310
-
311
- if content_length is None:
312
- raise ValueError("Unable to read file info from url. The link might be invalid or unreachable.")
313
 
314
  file_size = int(content_length)
315
- # print("debug", url, file_size)
316
- if file_size > 900000000 and IS_ZERO_GPU:
317
- raise ValueError("The file is too large. Max allowed is 900 MB.")
318
-
319
- return file_size
320
-
321
- except urllib.error.HTTPError as err:
322
- raise ValueError(
323
- f"HTTP Error {err.code} ({err.reason}): The file at the provided URL does not exist, is private, or has been deleted from Hugging Face. Please verify the link."
324
- )
325
 
326
  except Exception as e:
327
  raise e
328
 
 
329
  def clear_files(directory):
330
  time.sleep(15)
331
  print(f"Clearing files: {directory}.")
332
  shutil.rmtree(directory)
333
 
334
 
335
- def get_my_model(url_data, progress=gr.Progress(track_tqdm=True)):
336
 
337
  if not url_data:
338
  return None, None
339
 
340
  if "," in url_data:
341
- a_, b_ = url_data.split(",")
342
  a_, b_ = a_.strip().replace("/blob/", "/resolve/"), b_.strip().replace("/blob/", "/resolve/")
343
  else:
344
  a_, b_ = url_data.strip().replace("/blob/", "/resolve/"), None
@@ -349,9 +147,12 @@ def get_my_model(url_data, progress=gr.Progress(track_tqdm=True)):
349
  os.makedirs(directory, exist_ok=True)
350
 
351
  try:
 
 
 
 
352
  valid_url = [a_] if not b_ else [a_, b_]
353
  for link in valid_url:
354
- ensure_valid_file(link)
355
  download_manager(
356
  url=link,
357
  path=directory,
@@ -360,7 +161,6 @@ def get_my_model(url_data, progress=gr.Progress(track_tqdm=True)):
360
 
361
  for f in find_files(directory):
362
  if f.endswith(".zip"):
363
- check_model_safety(f)
364
  unzip_in_folder(f, directory)
365
 
366
  model = None
@@ -369,7 +169,6 @@ def get_my_model(url_data, progress=gr.Progress(track_tqdm=True)):
369
 
370
  for ff in end_files:
371
  if ff.endswith(".pth"):
372
- check_model_safety(ff)
373
  model = ff
374
  gr.Info(f"Model found: {ff}")
375
  if ff.endswith(".index"):
@@ -395,13 +194,13 @@ def get_my_model(url_data, progress=gr.Progress(track_tqdm=True)):
395
  t.start()
396
 
397
 
398
- def add_audio_effects(audio_list, type_output):
399
  print("Audio effects")
400
 
401
  result = []
402
  for audio_path in audio_list:
403
  try:
404
- output_path = f'{os.path.splitext(audio_path)[0]}_effects.{type_output}'
405
 
406
  # Initialize audio effects plugins
407
  board = Pedalboard(
@@ -412,23 +211,13 @@ def add_audio_effects(audio_list, type_output):
412
  ]
413
  )
414
 
415
- # Temporary WAV to hold processed data before exporting
416
- temp_wav = f'{os.path.splitext(audio_path)[0]}_temp.wav'
417
-
418
  with AudioFile(audio_path) as f:
419
- with AudioFile(temp_wav, 'w', f.samplerate, f.num_channels) as o:
 
420
  while f.tell() < f.frames:
421
  chunk = f.read(int(f.samplerate))
422
  effected = board(chunk, f.samplerate, reset=False)
423
  o.write(effected)
424
-
425
- # Convert with pydub to desired output type
426
- audio_seg = AudioSegment.from_file(temp_wav, format=type_output)
427
- audio_seg.export(output_path, format=type_output, bitrate=("320k" if type_output == "mp3" else None))
428
-
429
- # Clean up temp file
430
- os.remove(temp_wav)
431
-
432
  result.append(output_path)
433
  except Exception as e:
434
  traceback.print_exc()
@@ -438,13 +227,13 @@ def add_audio_effects(audio_list, type_output):
438
  return result
439
 
440
 
441
- def apply_noisereduce(audio_list, type_output):
442
  # https://github.com/sa-if/Audio-Denoiser
443
  print("Noice reduce")
444
 
445
  result = []
446
  for audio_path in audio_list:
447
- out_path = f"{os.path.splitext(audio_path)[0]}_noisereduce.{type_output}"
448
 
449
  try:
450
  # Load audio file
@@ -465,7 +254,7 @@ def apply_noisereduce(audio_list, type_output):
465
  )
466
 
467
  # Save reduced audio to file
468
- reduced_audio.export(out_path, format=type_output, bitrate=("320k" if type_output == "mp3" else None))
469
  result.append(out_path)
470
 
471
  except Exception as e:
@@ -477,17 +266,13 @@ def apply_noisereduce(audio_list, type_output):
477
 
478
 
479
  @spaces.GPU()
480
- def convert_now(audio_files, random_tag, converter, type_output, steps):
481
- for step in range(steps):
482
- audio_files = converter(
483
- audio_files,
484
- random_tag,
485
- overwrite=False,
486
- parallel_workers=(2 if IS_COLAB else 8),
487
- type_output=type_output,
488
- )
489
-
490
- return audio_files
491
 
492
 
493
  def run(
@@ -502,8 +287,6 @@ def run(
502
  c_b_p,
503
  active_noise_reduce,
504
  audio_effects,
505
- type_output,
506
- steps,
507
  ):
508
  if not audio_files:
509
  raise ValueError("The audio pls")
@@ -517,9 +300,9 @@ def run(
517
  except Exception as e:
518
  print(e)
519
 
520
- if file_m is not None and (file_m.endswith(".txt") or file_m.endswith(".zip")):
521
  file_m, file_index = find_my_model(file_m, file_index)
522
- # print(file_m, file_index)
523
 
524
  random_tag = "USER_"+str(random.randint(10000000, 99999999))
525
 
@@ -533,58 +316,27 @@ def run(
533
  respiration_median_filtering=r_m_f,
534
  envelope_ratio=e_r,
535
  consonant_breath_protection=c_b_p,
536
- resample_sr=0,
537
  )
538
  time.sleep(0.1)
539
 
540
- result = convert_now(audio_files, random_tag, converter, type_output, steps)
541
 
542
  if active_noise_reduce:
543
- result = apply_noisereduce(result, type_output)
544
 
545
  if audio_effects:
546
- result = add_audio_effects(result, type_output)
547
 
548
  return result
549
 
550
 
551
- def clear_player():
552
- return None
553
-
554
-
555
- def load_first_audio(output_files, play_audio):
556
- if not play_audio or not output_files:
557
- return None
558
- first_file = output_files[0]
559
- if isinstance(first_file, dict):
560
- first_file = first_file.get("name")
561
- return first_file
562
-
563
-
564
- def audio_source_conf():
565
- return gr.Radio(
566
- choices=["📁 Upload", "🗣️ TTS", "🎙️ Record"],
567
- value="📁 Upload",
568
- label="Audio Source",
569
- )
570
-
571
-
572
- def mic_conf():
573
- return gr.Audio(
574
- sources=["microphone"],
575
- type="filepath",
576
- label="Record Audio",
577
- visible=False,
578
- )
579
-
580
-
581
  def audio_conf():
582
  return gr.File(
583
  label="Audio files",
584
  file_count="multiple",
585
  type="filepath",
586
  container=True,
587
- file_types=supported_extensions,
588
  )
589
 
590
 
@@ -593,7 +345,6 @@ def model_conf():
593
  label="Model file",
594
  type="filepath",
595
  height=130,
596
- file_types=[".txt", ".pth", ".zip"],
597
  )
598
 
599
 
@@ -624,7 +375,6 @@ def index_conf():
624
  label="Index file",
625
  type="filepath",
626
  height=130,
627
- file_types=[".index", ".txt"],
628
  )
629
 
630
 
@@ -683,6 +433,15 @@ def output_conf():
683
  )
684
 
685
 
 
 
 
 
 
 
 
 
 
686
  def tts_voice_conf():
687
  return gr.Dropdown(
688
  label="tts voice",
@@ -710,12 +469,13 @@ def tts_button_conf():
710
  )
711
 
712
 
713
- def player_conf():
714
  return gr.Checkbox(
715
  False,
716
- label="Audio Player",
 
717
  container=False,
718
- visible=True,
719
  )
720
 
721
 
@@ -723,31 +483,12 @@ def sound_gui():
723
  return gr.Audio(
724
  value=None,
725
  type="filepath",
 
726
  autoplay=True,
727
  visible=False,
728
- interactive=False,
729
- label="Audio Player",
730
  )
731
 
732
 
733
- def steps_conf():
734
- return gr.Slider(
735
- minimum=1,
736
- maximum=3,
737
- label="Steps",
738
- value=1,
739
- step=1,
740
- interactive=True,
741
- )
742
-
743
-
744
- def format_output_gui():
745
- return gr.Dropdown(
746
- label="Format output:",
747
- choices=["wav", "mp3", "flac"],
748
- value="wav",
749
- )
750
-
751
  def denoise_conf():
752
  return gr.Checkbox(
753
  False,
@@ -768,7 +509,7 @@ def effects_conf():
768
  )
769
 
770
 
771
- def infer_tts_audio(tts_voice, tts_text, play_audio):
772
  out_dir = "output"
773
  folder_tts = "USER_"+str(random.randint(10000, 99999))
774
 
@@ -777,25 +518,23 @@ def infer_tts_audio(tts_voice, tts_text, play_audio):
777
  out_path = os.path.join(out_dir, folder_tts, "tts.mp3")
778
 
779
  asyncio.run(edge_tts.Communicate(tts_text, "-".join(tts_voice.split('-')[:-1])).save(out_path))
780
- player_audio = out_path if play_audio else None
781
- return [out_path], player_audio
 
782
 
783
 
784
- def show_components_source(mode):
785
- return (
786
- gr.update(visible=(mode == "🗣️ TTS")),
787
- gr.update(visible=(mode == "🗣️ TTS")),
788
- gr.update(visible=(mode == "🗣️ TTS")),
789
- gr.update(visible=(mode == "🎙️ Record")),
 
 
 
790
  )
791
 
792
 
793
- def sync_mic_audio(mic_path):
794
- if not mic_path:
795
- return None
796
- return [mic_path]
797
-
798
-
799
  def down_active_conf():
800
  return gr.Checkbox(
801
  False,
@@ -833,17 +572,12 @@ def show_components_down(value_active):
833
  )
834
 
835
 
836
- CSS = ""
837
- supported_extensions = get_supported_audio_video_extensions()
838
- print("Supported extensions found:", supported_extensions)
839
-
840
-
841
  def get_gui(theme):
842
- with gr.Blocks(theme=theme, css=CSS, fill_width=True, fill_height=False, delete_cache=delete_cache_time) as app:
843
  gr.Markdown(title)
844
  gr.Markdown(description)
845
 
846
- audio_source = audio_source_conf()
847
  with gr.Row():
848
  with gr.Column(scale=1):
849
  tts_text = tts_text_conf()
@@ -852,28 +586,29 @@ def get_gui(theme):
852
  with gr.Column():
853
  with gr.Row():
854
  tts_voice = tts_voice_conf()
 
855
 
856
  tts_button = tts_button_conf()
 
 
 
 
 
 
 
857
 
858
- mic_aud = mic_conf()
859
  aud = audio_conf()
860
  # gr.HTML("<hr>")
861
 
862
- audio_source.change(
863
- fn=show_components_source,
864
- inputs=[audio_source],
865
- outputs=[tts_voice, tts_text, tts_button, mic_aud],
866
- )
867
-
868
- mic_aud.change(
869
- fn=sync_mic_audio,
870
- inputs=[mic_aud],
871
- outputs=[aud],
872
  )
873
 
874
  down_active_gui = down_active_conf()
875
  down_info = gr.Markdown(
876
- f"Provide a link to a zip file, like this one: `https://huggingface.co/MrDawg/ToothBrushing/resolve/main/ToothBrushing.zip?download=true`, or separate links with a comma for the .pth and .index files, like this: `{test_model}`",
877
  visible=False
878
  )
879
  with gr.Row():
@@ -899,36 +634,17 @@ def get_gui(theme):
899
  [model, indx]
900
  )
901
 
902
- with gr.Accordion(label="Advanced settings", open=False):
903
- algo = pitch_algo_conf()
904
- algo_lvl = pitch_lvl_conf()
905
- indx_inf = index_inf_conf()
906
- res_fc = respiration_filter_conf()
907
- envel_r = envelope_ratio_conf()
908
- const = consonant_protec_conf()
909
- steps_gui = steps_conf()
910
- format_out = format_output_gui()
911
- with gr.Row():
912
- with gr.Column():
913
- with gr.Row():
914
- denoise_gui = denoise_conf()
915
- effects_gui = effects_conf()
916
- player_gui = player_conf()
917
-
918
- player_audio = sound_gui()
919
-
920
- player_gui.change(
921
- fn=lambda val: gr.update(visible=val),
922
- inputs=[player_gui],
923
- outputs=[player_audio],
924
- )
925
-
926
- tts_button.click(
927
- fn=infer_tts_audio,
928
- inputs=[tts_voice, tts_text, player_gui],
929
- outputs=[aud, player_audio],
930
- )
931
-
932
  button_base = button_conf()
933
  output_base = output_conf()
934
 
@@ -946,19 +662,8 @@ def get_gui(theme):
946
  const,
947
  denoise_gui,
948
  effects_gui,
949
- format_out,
950
- steps_gui,
951
  ],
952
  outputs=[output_base],
953
- ).success(
954
- clear_player,
955
- inputs=None,
956
- outputs=[player_audio],
957
- queue=False,
958
- ).success(
959
- load_first_audio,
960
- inputs=[output_base, player_gui],
961
- outputs=[player_audio],
962
  )
963
 
964
  gr.Examples(
@@ -1013,31 +718,24 @@ def get_gui(theme):
1013
  outputs=[output_base],
1014
  cache_examples=False,
1015
  )
1016
- gr.Markdown(RESOURCES)
1017
 
1018
  return app
1019
 
1020
 
1021
  if __name__ == "__main__":
1022
- try:
1023
- tts_voice_list = asyncio.new_event_loop().run_until_complete(get_voices_list(proxy=None))
1024
- voices = sorted([
1025
- (" - ".join(reversed(v["FriendlyName"].split("-"))).replace("Microsoft ", "").replace("Online (Natural)", f"({v['Gender']})").strip(), f"{v['ShortName']}-{v['Gender']}")
1026
- for v in tts_voice_list
1027
- ])
1028
- except Exception as e:
1029
- print(f"Warning: Could not retrieve online voices ({e}). Using default preset.")
1030
- voices = [("English (United States) - EmmaMultilingual (Female)", "en-US-EmmaMultilingualNeural-Female")]
1031
-
1032
  app = get_gui(theme)
1033
 
1034
  app.queue(default_concurrency_limit=40)
1035
 
1036
  app.launch(
1037
  max_threads=40,
1038
- share=IS_COLAB,
1039
  show_error=True,
1040
  quiet=False,
1041
- debug=IS_COLAB,
1042
- ssr_mode=False,
1043
- )
 
1
  import os
 
2
  import gradio as gr
3
  import spaces
4
  from infer_rvc_python import BaseLoader
 
6
  import logging
7
  import time
8
  import soundfile as sf
9
+ from infer_rvc_python.main import download_manager
10
  import zipfile
11
  import edge_tts
12
  import asyncio
 
19
  import noisereduce as nr
20
  import numpy as np
21
  import urllib.request
 
 
22
  import shutil
23
  import threading
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
24
 
25
  logging.getLogger("infer_rvc_python").setLevel(logging.ERROR)
26
 
 
27
  converter = BaseLoader(only_cpu=False, hubert_path=None, rmvpe_path=None)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
28
 
29
  title = "<center><strong><font size='7'>RVC⚡ZERO</font></strong></center>"
30
+ description = "This demo is provided for educational and research purposes only. The authors and contributors of this project do not endorse or encourage any misuse or unethical use of this software. Any use of this software for purposes other than those intended is solely at the user's own risk. The authors and contributors shall not be held responsible for any damages or liabilities arising from the use of this demo inappropriately."
31
+ theme = "aliabid94/new-theme"
 
 
32
 
33
  PITCH_ALGO_OPT = [
34
  "pm",
 
39
  ]
40
 
41
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
42
  def find_files(directory):
43
  file_paths = []
44
  for filename in os.listdir(directory):
 
62
  def find_my_model(a_, b_):
63
 
64
  if a_ is None or a_.endswith(".pth"):
 
 
65
  return a_, b_
66
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
67
  txt_files = []
68
  for base_file in [a_, b_]:
69
  if base_file is not None and base_file.endswith(".txt"):
 
75
  with open(txt, 'r') as file:
76
  first_line = file.readline()
77
 
 
 
 
78
  download_manager(
79
+ url=first_line.strip(),
80
  path=directory,
81
  extension="",
82
  )
83
 
84
  for f in find_files(directory):
85
  if f.endswith(".zip"):
 
86
  unzip_in_folder(f, directory)
87
 
88
  model = None
 
91
 
92
  for ff in end_files:
93
  if ff.endswith(".pth"):
94
+ model = os.path.join(directory, ff)
 
95
  gr.Info(f"Model found: {ff}")
96
  if ff.endswith(".index"):
97
+ index = os.path.join(directory, ff)
98
  gr.Info(f"Index found: {ff}")
99
 
100
  if not model:
 
103
  if not index:
104
  gr.Warning("Index not found")
105
 
 
 
 
106
  return model, index
107
 
108
 
109
+ def get_file_size(url):
 
 
 
 
110
 
111
+ if "huggingface" not in url:
112
+ raise ValueError("Only downloads from Hugging Face are allowed")
 
113
 
 
 
 
 
 
 
 
 
114
  try:
115
+ with urllib.request.urlopen(url) as response:
116
+ info = response.info()
117
+ content_length = info.get("Content-Length")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
118
 
119
  file_size = int(content_length)
120
+ if file_size > 500000000:
121
+ raise ValueError("The file is too large. You can only download files up to 500 MB in size.")
 
 
 
 
 
 
 
 
122
 
123
  except Exception as e:
124
  raise e
125
 
126
+
127
  def clear_files(directory):
128
  time.sleep(15)
129
  print(f"Clearing files: {directory}.")
130
  shutil.rmtree(directory)
131
 
132
 
133
+ def get_my_model(url_data):
134
 
135
  if not url_data:
136
  return None, None
137
 
138
  if "," in url_data:
139
+ a_, b_ = url_data.split()
140
  a_, b_ = a_.strip().replace("/blob/", "/resolve/"), b_.strip().replace("/blob/", "/resolve/")
141
  else:
142
  a_, b_ = url_data.strip().replace("/blob/", "/resolve/"), None
 
147
  os.makedirs(directory, exist_ok=True)
148
 
149
  try:
150
+ get_file_size(a_)
151
+ if b_:
152
+ get_file_size(b_)
153
+
154
  valid_url = [a_] if not b_ else [a_, b_]
155
  for link in valid_url:
 
156
  download_manager(
157
  url=link,
158
  path=directory,
 
161
 
162
  for f in find_files(directory):
163
  if f.endswith(".zip"):
 
164
  unzip_in_folder(f, directory)
165
 
166
  model = None
 
169
 
170
  for ff in end_files:
171
  if ff.endswith(".pth"):
 
172
  model = ff
173
  gr.Info(f"Model found: {ff}")
174
  if ff.endswith(".index"):
 
194
  t.start()
195
 
196
 
197
+ def add_audio_effects(audio_list):
198
  print("Audio effects")
199
 
200
  result = []
201
  for audio_path in audio_list:
202
  try:
203
+ output_path = f'{os.path.splitext(audio_path)[0]}_effects.wav'
204
 
205
  # Initialize audio effects plugins
206
  board = Pedalboard(
 
211
  ]
212
  )
213
 
 
 
 
214
  with AudioFile(audio_path) as f:
215
+ with AudioFile(output_path, 'w', f.samplerate, f.num_channels) as o:
216
+ # Read one second of audio at a time, until the file is empty:
217
  while f.tell() < f.frames:
218
  chunk = f.read(int(f.samplerate))
219
  effected = board(chunk, f.samplerate, reset=False)
220
  o.write(effected)
 
 
 
 
 
 
 
 
221
  result.append(output_path)
222
  except Exception as e:
223
  traceback.print_exc()
 
227
  return result
228
 
229
 
230
+ def apply_noisereduce(audio_list):
231
  # https://github.com/sa-if/Audio-Denoiser
232
  print("Noice reduce")
233
 
234
  result = []
235
  for audio_path in audio_list:
236
+ out_path = f'{os.path.splitext(audio_path)[0]}_noisereduce.wav'
237
 
238
  try:
239
  # Load audio file
 
254
  )
255
 
256
  # Save reduced audio to file
257
+ reduced_audio.export(out_path, format="wav")
258
  result.append(out_path)
259
 
260
  except Exception as e:
 
266
 
267
 
268
  @spaces.GPU()
269
+ def convert_now(audio_files, random_tag, converter):
270
+ return converter(
271
+ audio_files,
272
+ random_tag,
273
+ overwrite=False,
274
+ parallel_workers=8
275
+ )
 
 
 
 
276
 
277
 
278
  def run(
 
287
  c_b_p,
288
  active_noise_reduce,
289
  audio_effects,
 
 
290
  ):
291
  if not audio_files:
292
  raise ValueError("The audio pls")
 
300
  except Exception as e:
301
  print(e)
302
 
303
+ if file_m is not None and file_m.endswith(".txt"):
304
  file_m, file_index = find_my_model(file_m, file_index)
305
+ print(file_m, file_index)
306
 
307
  random_tag = "USER_"+str(random.randint(10000000, 99999999))
308
 
 
316
  respiration_median_filtering=r_m_f,
317
  envelope_ratio=e_r,
318
  consonant_breath_protection=c_b_p,
319
+ resample_sr=44100 if audio_files[0].endswith('.mp3') else 0,
320
  )
321
  time.sleep(0.1)
322
 
323
+ result = convert_now(audio_files, random_tag, converter)
324
 
325
  if active_noise_reduce:
326
+ result = apply_noisereduce(result)
327
 
328
  if audio_effects:
329
+ result = add_audio_effects(result)
330
 
331
  return result
332
 
333
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
334
  def audio_conf():
335
  return gr.File(
336
  label="Audio files",
337
  file_count="multiple",
338
  type="filepath",
339
  container=True,
 
340
  )
341
 
342
 
 
345
  label="Model file",
346
  type="filepath",
347
  height=130,
 
348
  )
349
 
350
 
 
375
  label="Index file",
376
  type="filepath",
377
  height=130,
 
378
  )
379
 
380
 
 
433
  )
434
 
435
 
436
+ def active_tts_conf():
437
+ return gr.Checkbox(
438
+ False,
439
+ label="TTS",
440
+ # info="",
441
+ container=False,
442
+ )
443
+
444
+
445
  def tts_voice_conf():
446
  return gr.Dropdown(
447
  label="tts voice",
 
469
  )
470
 
471
 
472
+ def tts_play_conf():
473
  return gr.Checkbox(
474
  False,
475
+ label="Play",
476
+ # info="",
477
  container=False,
478
+ visible=False,
479
  )
480
 
481
 
 
483
  return gr.Audio(
484
  value=None,
485
  type="filepath",
486
+ # format="mp3",
487
  autoplay=True,
488
  visible=False,
 
 
489
  )
490
 
491
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
492
  def denoise_conf():
493
  return gr.Checkbox(
494
  False,
 
509
  )
510
 
511
 
512
+ def infer_tts_audio(tts_voice, tts_text, play_tts):
513
  out_dir = "output"
514
  folder_tts = "USER_"+str(random.randint(10000, 99999))
515
 
 
518
  out_path = os.path.join(out_dir, folder_tts, "tts.mp3")
519
 
520
  asyncio.run(edge_tts.Communicate(tts_text, "-".join(tts_voice.split('-')[:-1])).save(out_path))
521
+ if play_tts:
522
+ return [out_path], out_path
523
+ return [out_path], None
524
 
525
 
526
+ def show_components_tts(value_active):
527
+ return gr.update(
528
+ visible=value_active
529
+ ), gr.update(
530
+ visible=value_active
531
+ ), gr.update(
532
+ visible=value_active
533
+ ), gr.update(
534
+ visible=value_active
535
  )
536
 
537
 
 
 
 
 
 
 
538
  def down_active_conf():
539
  return gr.Checkbox(
540
  False,
 
572
  )
573
 
574
 
 
 
 
 
 
575
  def get_gui(theme):
576
+ with gr.Blocks(theme=theme, delete_cache=(3200, 3200)) as app:
577
  gr.Markdown(title)
578
  gr.Markdown(description)
579
 
580
+ active_tts = active_tts_conf()
581
  with gr.Row():
582
  with gr.Column(scale=1):
583
  tts_text = tts_text_conf()
 
586
  with gr.Column():
587
  with gr.Row():
588
  tts_voice = tts_voice_conf()
589
+ tts_active_play = tts_play_conf()
590
 
591
  tts_button = tts_button_conf()
592
+ tts_play = sound_gui()
593
+
594
+ active_tts.change(
595
+ fn=show_components_tts,
596
+ inputs=[active_tts],
597
+ outputs=[tts_voice, tts_text, tts_button, tts_active_play],
598
+ )
599
 
 
600
  aud = audio_conf()
601
  # gr.HTML("<hr>")
602
 
603
+ tts_button.click(
604
+ fn=infer_tts_audio,
605
+ inputs=[tts_voice, tts_text, tts_active_play],
606
+ outputs=[aud, tts_play],
 
 
 
 
 
 
607
  )
608
 
609
  down_active_gui = down_active_conf()
610
  down_info = gr.Markdown(
611
+ "Provide a link to a zip file, like this one: `https://huggingface.co/mrmocciai/Models/resolve/main/Genshin%20Impact/ayaka-v2.zip?download=true`, or separate links with a comma for the .pth and .index files, like this: `https://huggingface.co/sail-rvc/ayaka-jp/resolve/main/model.pth?download=true, https://huggingface.co/sail-rvc/ayaka-jp/resolve/main/model.index?download=true`",
612
  visible=False
613
  )
614
  with gr.Row():
 
634
  [model, indx]
635
  )
636
 
637
+ algo = pitch_algo_conf()
638
+ algo_lvl = pitch_lvl_conf()
639
+ indx_inf = index_inf_conf()
640
+ res_fc = respiration_filter_conf()
641
+ envel_r = envelope_ratio_conf()
642
+ const = consonant_protec_conf()
643
+ with gr.Row():
644
+ with gr.Column():
645
+ with gr.Row():
646
+ denoise_gui = denoise_conf()
647
+ effects_gui = effects_conf()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
648
  button_base = button_conf()
649
  output_base = output_conf()
650
 
 
662
  const,
663
  denoise_gui,
664
  effects_gui,
 
 
665
  ],
666
  outputs=[output_base],
 
 
 
 
 
 
 
 
 
667
  )
668
 
669
  gr.Examples(
 
718
  outputs=[output_base],
719
  cache_examples=False,
720
  )
 
721
 
722
  return app
723
 
724
 
725
  if __name__ == "__main__":
726
+
727
+ tts_voice_list = asyncio.new_event_loop().run_until_complete(edge_tts.list_voices())
728
+ voices = sorted([f"{v['ShortName']}-{v['Gender']}" for v in tts_voice_list])
729
+
 
 
 
 
 
 
730
  app = get_gui(theme)
731
 
732
  app.queue(default_concurrency_limit=40)
733
 
734
  app.launch(
735
  max_threads=40,
736
+ share=False,
737
  show_error=True,
738
  quiet=False,
739
+ debug=False,
740
+ allowed_paths=["./downloads/"],
741
+ )
pre-requirements.txt DELETED
@@ -1,2 +0,0 @@
1
- pip==23.0.1
2
- Setuptools<=80.6.0
 
 
 
requirements.txt CHANGED
@@ -1,14 +1,6 @@
1
- torch==2.9.1
2
- torchvision==0.24.1
3
- torchaudio==2.9.1
4
- infer-rvc-python
5
- edge_tts==7.2.7
6
  pedalboard
7
  noisereduce
8
- numpy==1.23.5
9
- transformers<=4.48.3
10
- # pydantic==2.10.6
11
- gradio==5.43.1
12
- spaces
13
- matplotlib-inline
14
- picklescan
 
1
+ torch==2.2.0
2
+ infer-rvc-python==1.1.0
3
+ edge-tts
 
 
4
  pedalboard
5
  noisereduce
6
+ numpy==1.23.5