RO-Rtechs/Rtechs_voiceClone
2
1import os2import gradio as gr3import spaces4from infer_rvc_python import BaseLoader5import random6import logging7import time8import soundfile as sf9from infer_rvc_python.main import download_manager10import zipfile11import edge_tts12import asyncio13import librosa14import traceback15import numpy as np16from pedalboard import Pedalboard, Reverb, Compressor, HighpassFilter17from pedalboard.io import AudioFile18from pydub import AudioSegment19import noisereduce as nr20 21logging.getLogger("infer_rvc_python").setLevel(logging.ERROR)22 23converter = BaseLoader(only_cpu=False, hubert_path=None, rmvpe_path=None)24 25title = "<center><strong><font size='7'>WELCOME TO RVC⚡RTECHS</font></strong></center>"26description = "This RVC is provided for RTECHS MEDIA PRODUCTIONS AND SOFTWARE DEV'S LOCAL purposes only. The authors (@Robel Adugna) 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."27theme = "aliabid94/new-theme"28 29PITCH_ALGO_OPT = [30 "pm",31 "harvest",32 "crepe",33 "rmvpe",34 "rmvpe+"35]36 37 38def find_files(directory):39 file_paths = []40 for filename in os.listdir(directory):41 # Check if the file has the desired extension42 if filename.endswith('.pth') or filename.endswith('.zip') or filename.endswith('.index'):43 # If yes, add the file path to the list44 file_paths.append(os.path.join(directory, filename))45 46 return file_paths47 48 49def unzip_in_folder(my_zip, my_dir):50 with zipfile.ZipFile(my_zip) as zip:51 for zip_info in zip.infolist():52 if zip_info.is_dir():53 continue54 zip_info.filename = os.path.basename(zip_info.filename)55 zip.extract(zip_info, my_dir)56 57 58def find_my_model(a_, b_):59 60 if a_ is None or a_.endswith(".pth"):61 return a_, b_62 63 txt_files = []64 for base_file in [a_, b_]:65 if base_file is not None and base_file.endswith(".txt"):66 txt_files.append(base_file)67 68 directory = os.path.dirname(a_)69 70 for txt in txt_files:71 with open(txt, 'r') as file:72 first_line = file.readline()73 74 download_manager(75 url=first_line.strip(),76 path=directory,77 extension="",78 )79 80 for f in find_files(directory):81 if f.endswith(".zip"):82 unzip_in_folder(f, directory)83 84 model = None85 index = None86 end_files = find_files(directory)87 88 for ff in end_files:89 if ff.endswith(".pth"):90 model = os.path.join(directory, ff)91 gr.Info(f"Model found: {ff}")92 if ff.endswith(".index"):93 index = os.path.join(directory, ff)94 gr.Info(f"Index found: {ff}")95 96 if not model:97 gr.Error(f"Model not found in: {end_files}")98 99 if not index:100 gr.Warning("Index not found")101 102 return model, index103 104 105def add_audio_effects(audio_list):106 print("Audio effects")107 108 result = []109 for audio_path in audio_list:110 try:111 output_path = f'{os.path.splitext(audio_path)[0]}_effects.wav'112 113 # Initialize audio effects plugins114 board = Pedalboard(115 [116 HighpassFilter(),117 Compressor(ratio=4, threshold_db=-15),118 Reverb(room_size=0.10, dry_level=0.8, wet_level=0.2, damping=0.7)119 ]120 )121 122 with AudioFile(audio_path) as f:123 with AudioFile(output_path, 'w', f.samplerate, f.num_channels) as o:124 # Read one second of audio at a time, until the file is empty:125 while f.tell() < f.frames:126 chunk = f.read(int(f.samplerate))127 effected = board(chunk, f.samplerate, reset=False)128 o.write(effected)129 result.append(output_path)130 except Exception as e:131 traceback.print_exc()132 print(f"Error noisereduce: {str(e)}")133 result.append(audio_path)134 135 return result136 137 138def apply_noisereduce(audio_list):139 # https://github.com/saif/Audio-Denoiser140 print("Noise reduction")141 142 result = []143 for audio_path in audio_list:144 out_path = f'{os.path.splitext(audio_path)[0]}_noisereduce.wav'145 146 try:147 # Load audio file148 audio = AudioSegment.from_file(audio_path)149 150 # Convert audio to numpy array151 samples = np.array(audio.get_array_of_samples())152 153 # Reduce noise154 reduced_noise = nr.reduce_noise(samples, sr=audio.frame_rate, prop_decrease=0.6)155 156 # Convert reduced noise signal back to audio157 reduced_audio = AudioSegment(158 reduced_noise.tobytes(),159 frame_rate=audio.frame_rate,160 sample_width=audio.sample_width,161 channels=audio.channels162 )163 164 # Save reduced audio to file165 reduced_audio.export(out_path, format="wav")166 result.append(out_path)167 168 except Exception as e:169 traceback.print_exc()170 print(f"Error in noise reduction: {str(e)}")171 result.append(audio_path)172 173 return result174 175 176def split_audio_into_chunks(audio_file, chunk_length_ms=30000):177 """178 Splits an audio file into smaller chunks.179 :param audio_file: Path to the input audio file.180 :param chunk_length_ms: Length of each chunk in milliseconds (default is 30 seconds).181 :return: List of chunk file paths.182 """183 try:184 audio = AudioSegment.from_file(audio_file)185 chunks = [audio[i:i + chunk_length_ms] for i in range(0, len(audio), chunk_length_ms)]186 chunk_paths = []187 188 base_name = os.path.splitext(os.path.basename(audio_file))[0]189 output_dir = os.path.join(os.path.dirname(audio_file), f"{base_name}_chunks")190 os.makedirs(output_dir, exist_ok=True)191 192 for index, chunk in enumerate(chunks):193 chunk_path = os.path.join(output_dir, f"{base_name}_chunk_{index + 1}.wav")194 chunk.export(chunk_path, format="wav")195 chunk_paths.append(chunk_path)196 197 return chunk_paths198 except Exception as e:199 traceback.print_exc()200 print(f"Error splitting audio into chunks: {str(e)}")201 return [audio_file]202 203 204@spaces.GPU()205def convert_now(audio_files, random_tag, converter):206 return converter(207 audio_files,208 random_tag,209 overwrite=False,210 parallel_workers=8211 )212 213 214def run(215 audio_files,216 file_m,217 pitch_alg,218 pitch_lvl,219 file_index,220 index_inf,221 r_m_f,222 e_r,223 c_b_p,224 active_noise_reduce,225 audio_effects,226 chunk_length_ms=30000227):228 if not audio_files:229 raise ValueError("Please provide audio files")230 231 if isinstance(audio_files, str):232 audio_files = [audio_files]233 234 if file_m is not None and file_m.endswith(".txt"):235 file_m, file_index = find_my_model(file_m, file_index)236 print(file_m, file_index)237 238 random_tag = "USER_" + str(random.randint(10000000, 99999999))239 240 converter.apply_conf(241 tag=random_tag,242 file_model=file_m,243 pitch_algo=pitch_alg,244 pitch_lvl=pitch_lvl,245 file_index=file_index,246 index_influence=index_inf,247 respiration_median_filtering=r_m_f,248 envelope_ratio=e_r,249 consonant_breath_protection=c_b_p,250 resample_sr=44100 if audio_files[0].endswith('.mp3') else 0,251 )252 time.sleep(0.1)253 254 # Split each audio file into chunks255 chunked_audio_files = []256 for audio_file in audio_files:257 chunked_audio_files.extend(split_audio_into_chunks(audio_file, chunk_length_ms))258 259 result = convert_now(chunked_audio_files, random_tag, converter)260 261 if active_noise_reduce:262 result = apply_noisereduce(result)263 264 if audio_effects:265 result = add_audio_effects(result)266 267 return result268 269 270def audio_conf():271 return gr.File(272 label="Audio files",273 file_count="multiple",274 type="filepath",275 container=True,276 )277 278 279def model_conf():280 return gr.File(281 label="Model file",282 type="filepath",283 height=130,284 )285 286 287def pitch_algo_conf():288 return gr.Dropdown(289 PITCH_ALGO_OPT,290 value=PITCH_ALGO_OPT[4],291 label="Pitch algorithm",292 visible=True,293 interactive=True,294 )295 296 297def pitch_lvl_conf():298 return gr.Slider(299 label="Pitch level",300 minimum=-24,301 maximum=24,302 step=1,303 value=0,304 visible=True,305 interactive=True,306 )307 308 309def index_conf():310 return gr.File(311 label="Index file",312 type="filepath",313 height=130,314 )315 316 317def index_inf_conf():318 return gr.Slider(319 minimum=0,320 maximum=1,321 label="Index influence",322 value=0.75,323 )324 325 326def respiration_filter_conf():327 return gr.Slider(328 minimum=0,329 maximum=7,330 label="Respiration median filtering",331 value=3,332 step=1,333 interactive=True,334 )335 336 337def envelope_ratio_conf():338 return gr.Slider(339 minimum=0,340 maximum=1,341 label="Envelope ratio",342 value=0.25,343 interactive=True,344 )345 346 347def consonant_protec_conf():348 return gr.Slider(349 minimum=0,350 maximum=0.5,351 label="Consonant breath protection",352 value=0.5,353 interactive=True,354 )355 356 357def button_conf():358 return gr.Button(359 "Inference",360 variant="primary",361 )362 363 364def output_conf():365 return gr.File(366 label="Result",367 file_count="multiple",368 interactive=False,369 )370 371 372def active_tts_conf():373 return gr.Checkbox(374 False,375 label="TTS",376 container=False,377 )378 379 380def tts_voice_conf():381 return gr.Dropdown(382 label="TTS Voice",383 choices=[384 "en-US-EmmaMultilingualNeural-Female",385 "en-US-GuyMultilingualNeural-Male",386 "en-GB-SoniaNeural-Female",387 "fr-FR-DeniseNeural-Female"388 ],389 visible=False,390 value="en-US-EmmaMultilingualNeural-Female",391 )392 393 394def tts_text_conf():395 return gr.Textbox(396 value="",397 placeholder="Write the text here...",398 label="Text",399 visible=False,400 lines=3,401 )402 403 404def tts_button_conf():405 return gr.Button(406 "Process TTS",407 variant="secondary",408 visible=False,409 )410 411 412def tts_play_conf():413 return gr.Checkbox(414 False,415 label="Play",416 container=False,417 visible=False,418 )419 420 421def sound_gui():422 return gr.Audio(423 value=None,424 type="filepath",425 autoplay=True,426 visible=False,427 )428 429 430def denoise_conf():431 return gr.Checkbox(432 False,433 label="Denoise",434 container=False,435 visible=True,436 )437 438 439def effects_conf():440 return gr.Checkbox(441 False,442 label="Effects",443 container=False,444 visible=True,445 )446 447 448def infer_tts_audio(tts_voice, tts_text, play_tts):449 out_dir = "output"450 folder_tts = "USER_" + str(random.randint(10000, 99999))451 452 os.makedirs(out_dir, exist_ok=True)453 os.makedirs(os.path.join(out_dir, folder_tts), exist_ok=True)454 out_path = os.path.join(out_dir, folder_tts, "tts.mp3")455 456 asyncio.run(edge_tts.Communicate(tts_text, "-".join(tts_voice.split('-')[:-1])).save(out_path))457 if play_tts:458 return [out_path], out_path459 return [out_path], None460 461 462def show_components_tts(value_active):463 return gr.update(464 visible=value_active465 ), gr.update(466 visible=value_active467 ), gr.update(468 visible=value_active469 ), gr.update(470 visible=value_active471 )472 473 474def get_gui(theme):475 with gr.Blocks(theme=theme) as app:476 gr.Markdown(title)477 gr.Markdown(description)478 479 active_tts = active_tts_conf()480 with gr.Row():481 with gr.Column(scale=1):482 tts_text = tts_text_conf()483 with gr.Column(scale=2):484 with gr.Row():485 with gr.Column():486 with gr.Row():487 tts_voice = tts_voice_conf()488 tts_active_play = tts_play_conf()489 490 tts_button = tts_button_conf()491 tts_play = sound_gui()492 493 active_tts.change(494 fn=show_components_tts,495 inputs=[active_tts],496 outputs=[tts_voice, tts_text, tts_button, tts_active_play],497 )498 499 aud = audio_conf()500 gr.HTML("<hr></h2>")501 502 tts_button.click(503 fn=infer_tts_audio,504 inputs=[tts_voice, tts_text, tts_active_play],505 outputs=[aud, tts_play],506 )507 508 with gr.Column():509 with gr.Row():510 model = model_conf()511 indx = index_conf()512 algo = pitch_algo_conf()513 algo_lvl = pitch_lvl_conf()514 indx_inf = index_inf_conf()515 res_fc = respiration_filter_conf()516 envel_r = envelope_ratio_conf()517 const = consonant_protec_conf()518 denoise = denoise_conf()519 effects = effects_conf()520 inference_button = button_conf()521 output = output_conf()522 523 inference_button.click(524 fn=run,525 inputs=[526 aud,527 model,528 algo,529 algo_lvl,530 indx,531 indx_inf,532 res_fc,533 envel_r,534 const,535 denoise,536 effects,537 ],538 outputs=[output],539 )540 541 app.launch(share=True)542 543get_gui(theme=theme)