infer_rvc_python 1.3.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,967 @@
1
+ from infer_rvc_python.lib.log_config import logger
2
+ import torch
3
+ import torch.nn as nn
4
+ import gc
5
+ import numpy as np
6
+ import os
7
+ import warnings
8
+ import threading
9
+ from tqdm import tqdm
10
+ from transformers import HubertConfig, HubertModel
11
+ from infer_rvc_python.lib.infer_pack.models import (
12
+ SynthesizerTrnMs256NSFsid,
13
+ SynthesizerTrnMs256NSFsid_nono,
14
+ SynthesizerTrnMs768NSFsid,
15
+ SynthesizerTrnMs768NSFsid_nono,
16
+ )
17
+ from infer_rvc_python.lib.audio import load_audio
18
+ import soundfile as sf
19
+ from scipy import signal
20
+ from time import time as ttime
21
+ import faiss
22
+ from infer_rvc_python.root_pipe import VC, change_rms, bh, ah
23
+ import librosa
24
+ from urllib.parse import urlparse
25
+ import copy
26
+
27
+ warnings.filterwarnings("ignore")
28
+ SUPPORTED_SAMPLE_RATES = [8000, 12000, 16000, 22050, 24000, 32000, 44100, 48000] # 96000
29
+
30
+
31
+ class Config:
32
+ def __init__(self, only_cpu=False):
33
+ self.device = "cuda:0"
34
+ self.is_half = True
35
+ self.n_cpu = 0
36
+ self.gpu_name = None
37
+ self.gpu_mem = None
38
+ (
39
+ self.x_pad,
40
+ self.x_query,
41
+ self.x_center,
42
+ self.x_max
43
+ ) = self.device_config(only_cpu)
44
+
45
+ def device_config(self, only_cpu) -> tuple:
46
+ if torch.cuda.is_available() and not only_cpu:
47
+ i_device = int(self.device.split(":")[-1])
48
+ self.gpu_name = torch.cuda.get_device_name(i_device)
49
+ if (
50
+ ("16" in self.gpu_name and "V100" not in self.gpu_name.upper())
51
+ or "P40" in self.gpu_name.upper()
52
+ or "1060" in self.gpu_name
53
+ or "1070" in self.gpu_name
54
+ or "1080" in self.gpu_name
55
+ ):
56
+ logger.info(
57
+ "16/10 Series GPUs and P40 excel "
58
+ "in single-precision tasks."
59
+ )
60
+ self.is_half = False
61
+ else:
62
+ self.gpu_name = None
63
+ self.gpu_mem = int(
64
+ torch.cuda.get_device_properties(i_device).total_memory
65
+ / 1024
66
+ / 1024
67
+ / 1024
68
+ + 0.4
69
+ )
70
+ elif torch.backends.mps.is_available() and not only_cpu:
71
+ logger.info("Supported N-card not found, using MPS for inference")
72
+ self.device = "mps"
73
+ else:
74
+ logger.info("No supported N-card found, using CPU for inference")
75
+ self.device = "cpu"
76
+ self.is_half = False
77
+
78
+ if self.n_cpu == 0:
79
+ self.n_cpu = os.cpu_count()
80
+
81
+ if self.is_half:
82
+ # 6GB VRAM configuration
83
+ x_pad = 3
84
+ x_query = 10
85
+ x_center = 60
86
+ x_max = 65
87
+ else:
88
+ # 5GB VRAM configuration
89
+ x_pad = 1
90
+ x_query = 6
91
+ x_center = 38
92
+ x_max = 41
93
+
94
+ if self.gpu_mem is not None and self.gpu_mem <= 4:
95
+ x_pad = 1
96
+ x_query = 5
97
+ x_center = 30
98
+ x_max = 32
99
+
100
+ logger.info(
101
+ f"Config: Device is {self.device}, "
102
+ f"half precision is {self.is_half}"
103
+ )
104
+
105
+ return x_pad, x_query, x_center, x_max
106
+
107
+
108
+ BASE_DOWNLOAD_LINK = "https://huggingface.co/r3gm/sonitranslate_voice_models/resolve/main/"
109
+ BASE_MODELS = [
110
+ "rmvpe.pt"
111
+ ]
112
+ BASE_DIR = "."
113
+
114
+
115
+ def load_file_from_url(
116
+ url: str,
117
+ model_dir: str,
118
+ file_name: str | None = None,
119
+ overwrite: bool = False,
120
+ progress: bool = True,
121
+ ) -> str:
122
+ """Download a file from `url` into `model_dir`,
123
+ using the file present if possible.
124
+
125
+ Returns the path to the downloaded file.
126
+ """
127
+ os.makedirs(model_dir, exist_ok=True)
128
+ if not file_name:
129
+ parts = urlparse(url)
130
+ file_name = os.path.basename(parts.path)
131
+ cached_file = os.path.abspath(os.path.join(model_dir, file_name))
132
+
133
+ # Overwrite
134
+ if os.path.exists(cached_file):
135
+ if overwrite or os.path.getsize(cached_file) == 0:
136
+ os.remove(cached_file)
137
+
138
+ # Download
139
+ if not os.path.exists(cached_file):
140
+ logger.info(f'Downloading: "{url}" to {cached_file}\n')
141
+ from torch.hub import download_url_to_file
142
+
143
+ download_url_to_file(url, cached_file, progress=progress)
144
+ else:
145
+ logger.debug(cached_file)
146
+
147
+ return cached_file
148
+
149
+
150
+ def friendly_name(file: str):
151
+ if file.startswith("http"):
152
+ file = urlparse(file).path
153
+
154
+ file = os.path.basename(file)
155
+ model_name, extension = os.path.splitext(file)
156
+ return model_name, extension
157
+
158
+
159
+ def download_manager(
160
+ url: str,
161
+ path: str,
162
+ extension: str = "",
163
+ overwrite: bool = False,
164
+ progress: bool = True,
165
+ ):
166
+ url = url.strip()
167
+
168
+ name, ext = friendly_name(url)
169
+ name += ext if not extension else f".{extension}"
170
+
171
+ if url.startswith("http"):
172
+ filename = load_file_from_url(
173
+ url=url,
174
+ model_dir=path,
175
+ file_name=name,
176
+ overwrite=overwrite,
177
+ progress=progress,
178
+ )
179
+ else:
180
+ filename = path
181
+
182
+ return filename
183
+
184
+
185
+ class HubertModelWithFinalProj(HubertModel):
186
+ def __init__(self, config):
187
+ super().__init__(config)
188
+ self.final_proj = nn.Linear(config.hidden_size, config.classifier_proj_size)
189
+
190
+
191
+ class FairseqHubertWrapper(nn.Module):
192
+ def __init__(self, model_path_or_name="r3gm/hubert_base"):
193
+ super().__init__()
194
+ try:
195
+ self.model = HubertModelWithFinalProj.from_pretrained(model_path_or_name)
196
+ except Exception:
197
+ self.model = HubertModel.from_pretrained(model_path_or_name)
198
+
199
+ def extract_features(self, source, padding_mask=None, output_layer=12, **kwargs):
200
+ param_dtype = next(self.model.parameters()).dtype
201
+ if source.dtype != param_dtype:
202
+ source = source.to(param_dtype)
203
+
204
+ if source.dim() == 1:
205
+ source = source.unsqueeze(0)
206
+
207
+ with torch.no_grad():
208
+ outputs = self.model(source, output_hidden_states=True)
209
+
210
+ # 9 for v1, 12 for v2/ContentVec
211
+ if output_layer is None or output_layer >= len(outputs.hidden_states):
212
+ hidden_state = outputs.hidden_states[-1]
213
+ else:
214
+ hidden_state = outputs.hidden_states[output_layer]
215
+
216
+ return (hidden_state, None)
217
+
218
+ def final_proj(self, x):
219
+ if hasattr(self.model, "final_proj"):
220
+ return self.model.final_proj(x)
221
+ return x
222
+
223
+
224
+ def load_hu_bert(config, hubert_path=None):
225
+ if hubert_path and (os.path.exists(hubert_path) or os.path.isdir(hubert_path)):
226
+ target_path = hubert_path
227
+ else:
228
+ target_path = "r3gm/hubert_base"
229
+
230
+ hubert_model = FairseqHubertWrapper(target_path)
231
+ hubert_model = hubert_model.to(config.device)
232
+
233
+ if config.is_half and torch.device(config.device).type != "cpu":
234
+ hubert_model = hubert_model.half()
235
+ else:
236
+ hubert_model = hubert_model.float()
237
+
238
+ hubert_model.eval()
239
+ return hubert_model
240
+
241
+
242
+ def load_trained_model(model_path, config):
243
+
244
+ if not model_path:
245
+ raise ValueError("No model found")
246
+
247
+ logger.info("Loading %s" % model_path)
248
+ cpt = torch.load(model_path, map_location="cpu")
249
+ tgt_sr = cpt["config"][-1]
250
+ cpt["config"][-3] = cpt["weight"]["emb_g.weight"].shape[0] # n_spk
251
+ if_f0 = cpt.get("f0", 1)
252
+ if if_f0 == 0:
253
+ # protect to 0.5 need?
254
+ pass
255
+
256
+ version = cpt.get("version", "v1")
257
+ if version == "v1":
258
+ if if_f0 == 1:
259
+ net_g = SynthesizerTrnMs256NSFsid(
260
+ *cpt["config"], is_half=config.is_half
261
+ )
262
+ else:
263
+ net_g = SynthesizerTrnMs256NSFsid_nono(*cpt["config"])
264
+ elif version == "v2":
265
+ if if_f0 == 1:
266
+ net_g = SynthesizerTrnMs768NSFsid(
267
+ *cpt["config"], is_half=config.is_half
268
+ )
269
+ else:
270
+ net_g = SynthesizerTrnMs768NSFsid_nono(*cpt["config"])
271
+ del net_g.enc_q
272
+
273
+ net_g.load_state_dict(cpt["weight"], strict=False)
274
+ net_g.eval().to(config.device)
275
+
276
+ if config.is_half:
277
+ net_g = net_g.half()
278
+ else:
279
+ net_g = net_g.float()
280
+
281
+ vc = VC(tgt_sr, config)
282
+ n_spk = cpt["config"][-3]
283
+
284
+ return n_spk, tgt_sr, net_g, vc, cpt, version
285
+
286
+
287
+ class BaseLoader:
288
+ def __init__(self, only_cpu=False, hubert_path=None, rmvpe_path=None):
289
+ self.model_config = {}
290
+ self.config = None
291
+ self.cache_model = {}
292
+ self.only_cpu = only_cpu
293
+ self.hubert_path = hubert_path
294
+ self.rmvpe_path = rmvpe_path
295
+
296
+ def apply_conf(
297
+ self,
298
+ tag="base_model",
299
+ file_model="",
300
+ pitch_algo="pm",
301
+ pitch_lvl=0,
302
+ file_index="",
303
+ index_influence=0.66,
304
+ respiration_median_filtering=3,
305
+ envelope_ratio=0.25,
306
+ consonant_breath_protection=0.33,
307
+ resample_sr=0,
308
+ file_pitch_algo="",
309
+ ):
310
+
311
+ if not file_model:
312
+ raise ValueError("Model not found")
313
+
314
+ if file_index is None:
315
+ file_index = ""
316
+
317
+ if file_pitch_algo is None:
318
+ file_pitch_algo = ""
319
+
320
+ if not self.config:
321
+ self.config = Config(self.only_cpu)
322
+ self.hu_bert_model = None
323
+ self.model_pitch_estimator = None
324
+
325
+ self.model_config[tag] = {
326
+ "file_model": file_model,
327
+ "pitch_algo": pitch_algo,
328
+ "pitch_lvl": pitch_lvl, # no decimal
329
+ "file_index": file_index,
330
+ "index_influence": index_influence,
331
+ "respiration_median_filtering": respiration_median_filtering,
332
+ "envelope_ratio": envelope_ratio,
333
+ "consonant_breath_protection": consonant_breath_protection,
334
+ "resample_sr": resample_sr,
335
+ "file_pitch_algo": file_pitch_algo,
336
+ }
337
+ return f"CONFIGURATION APPLIED FOR {tag}: {file_model}"
338
+
339
+ def infer(
340
+ self,
341
+ task_id,
342
+ params,
343
+ # load model
344
+ n_spk,
345
+ tgt_sr,
346
+ net_g,
347
+ pipe,
348
+ cpt,
349
+ version,
350
+ if_f0,
351
+ # load index
352
+ index_rate,
353
+ index,
354
+ big_npy,
355
+ # load f0 file
356
+ inp_f0,
357
+ # audio file
358
+ input_audio_path,
359
+ overwrite,
360
+ type_output,
361
+ ):
362
+
363
+ f0_method = params["pitch_algo"]
364
+ f0_up_key = params["pitch_lvl"]
365
+ filter_radius = params["respiration_median_filtering"]
366
+ resample_sr = params["resample_sr"]
367
+ rms_mix_rate = params["envelope_ratio"]
368
+ protect = params["consonant_breath_protection"]
369
+ base_sr = 16000
370
+
371
+ if isinstance(input_audio_path, tuple):
372
+ if f0_method == "harvest":
373
+ raise ValueError("Harvest not support from array")
374
+ audio = input_audio_path[0]
375
+ source_sr = input_audio_path[1]
376
+ if source_sr != base_sr:
377
+ audio = librosa.resample(
378
+ audio.astype(np.float32),
379
+ orig_sr=source_sr,
380
+ target_sr=base_sr
381
+ )
382
+ audio = audio.astype(np.float32).flatten()
383
+ elif not os.path.exists(input_audio_path):
384
+ raise ValueError(
385
+ "The audio file was not found or is not "
386
+ f"a valid file: {input_audio_path}"
387
+ )
388
+ else:
389
+ audio = load_audio(input_audio_path, base_sr)
390
+
391
+ f0_up_key = int(f0_up_key)
392
+
393
+ # Normalize audio
394
+ audio_max = np.abs(audio).max() / 0.95
395
+ if audio_max > 1:
396
+ audio /= audio_max
397
+
398
+ times = [0, 0, 0]
399
+
400
+ # filters audio signal, pads it, computes sliding window sums,
401
+ # and extracts optimized time indices
402
+ audio = signal.filtfilt(bh, ah, audio)
403
+ audio_pad = np.pad(
404
+ audio, (pipe.window // 2, pipe.window // 2), mode="reflect"
405
+ )
406
+ opt_ts = []
407
+ if audio_pad.shape[0] > pipe.t_max:
408
+ audio_sum = np.zeros_like(audio)
409
+ for i in range(pipe.window):
410
+ audio_sum += audio_pad[i:i - pipe.window]
411
+ for t in range(pipe.t_center, audio.shape[0], pipe.t_center):
412
+ opt_ts.append(
413
+ t
414
+ - pipe.t_query
415
+ + np.where(
416
+ np.abs(audio_sum[t - pipe.t_query: t + pipe.t_query])
417
+ == np.abs(audio_sum[t - pipe.t_query: t + pipe.t_query]).min()
418
+ )[0][0]
419
+ )
420
+
421
+ s = 0
422
+ audio_opt = []
423
+ t = None
424
+ t1 = ttime()
425
+
426
+ sid_value = 0
427
+ sid = torch.tensor(sid_value, device=pipe.device).unsqueeze(0).long()
428
+
429
+ # Pads audio symmetrically, calculates length divided by window size.
430
+ audio_pad = np.pad(audio, (pipe.t_pad, pipe.t_pad), mode="reflect")
431
+ p_len = audio_pad.shape[0] // pipe.window
432
+
433
+ # Estimates pitch from audio signal
434
+ pitch, pitchf = None, None
435
+ if if_f0 == 1:
436
+ pitch, pitchf = pipe.get_f0(
437
+ input_audio_path,
438
+ audio_pad,
439
+ p_len,
440
+ f0_up_key,
441
+ f0_method,
442
+ filter_radius,
443
+ inp_f0,
444
+ )
445
+ pitch = pitch[:p_len]
446
+ pitchf = pitchf[:p_len]
447
+ if pipe.device == "mps":
448
+ pitchf = pitchf.astype(np.float32)
449
+ pitch = torch.tensor(
450
+ pitch, device=pipe.device
451
+ ).unsqueeze(0).long()
452
+ pitchf = torch.tensor(
453
+ pitchf, device=pipe.device
454
+ ).unsqueeze(0).float()
455
+
456
+ t2 = ttime()
457
+ times[1] += t2 - t1
458
+ for t in opt_ts:
459
+ t = t // pipe.window * pipe.window
460
+ if if_f0 == 1:
461
+ pitch_slice = pitch[
462
+ :, s // pipe.window: (t + pipe.t_pad2) // pipe.window
463
+ ]
464
+ pitchf_slice = pitchf[
465
+ :, s // pipe.window: (t + pipe.t_pad2) // pipe.window
466
+ ]
467
+ else:
468
+ pitch_slice = None
469
+ pitchf_slice = None
470
+
471
+ audio_slice = audio_pad[s:t + pipe.t_pad2 + pipe.window]
472
+ audio_opt.append(
473
+ pipe.vc(
474
+ self.hu_bert_model,
475
+ net_g,
476
+ sid,
477
+ audio_slice,
478
+ pitch_slice,
479
+ pitchf_slice,
480
+ times,
481
+ index,
482
+ big_npy,
483
+ index_rate,
484
+ version,
485
+ protect,
486
+ )[pipe.t_pad_tgt:-pipe.t_pad_tgt]
487
+ )
488
+ s = t
489
+
490
+ pitch_end_slice = pitch[
491
+ :, t // pipe.window:
492
+ ] if t is not None else pitch
493
+ pitchf_end_slice = pitchf[
494
+ :, t // pipe.window:
495
+ ] if t is not None else pitchf
496
+
497
+ audio_opt.append(
498
+ pipe.vc(
499
+ self.hu_bert_model,
500
+ net_g,
501
+ sid,
502
+ audio_pad[t:],
503
+ pitch_end_slice,
504
+ pitchf_end_slice,
505
+ times,
506
+ index,
507
+ big_npy,
508
+ index_rate,
509
+ version,
510
+ protect,
511
+ )[pipe.t_pad_tgt:-pipe.t_pad_tgt]
512
+ )
513
+
514
+ audio_opt = np.concatenate(audio_opt)
515
+ if rms_mix_rate != 1:
516
+ audio_opt = change_rms(
517
+ audio, 16000, audio_opt, tgt_sr, rms_mix_rate
518
+ )
519
+ if resample_sr >= 16000 and tgt_sr != resample_sr:
520
+ audio_opt = librosa.resample(
521
+ audio_opt, orig_sr=tgt_sr, target_sr=resample_sr
522
+ )
523
+ audio_max = np.abs(audio_opt).max() / 0.99
524
+ max_int16 = 32768
525
+ if audio_max > 1:
526
+ max_int16 /= audio_max
527
+ audio_opt = (audio_opt * max_int16).astype(np.int16)
528
+ del pitch, pitchf, sid
529
+ if torch.cuda.is_available():
530
+ torch.cuda.empty_cache()
531
+
532
+ if tgt_sr != resample_sr >= 16000:
533
+ final_sr = resample_sr
534
+ else:
535
+ final_sr = tgt_sr
536
+
537
+ """
538
+ "Success.\n %s\nTime:\n npy:%ss, f0:%ss, infer:%ss" % (
539
+ times[0],
540
+ times[1],
541
+ times[2],
542
+ ), (final_sr, audio_opt)
543
+
544
+ """
545
+
546
+ if type_output == "array":
547
+ return audio_opt, final_sr
548
+
549
+ if overwrite:
550
+ output_audio_path = input_audio_path # Overwrite
551
+ type_output = os.path.splitext(output_audio_path)[1].lstrip(".")
552
+ else:
553
+ basename = os.path.basename(input_audio_path)
554
+ dirname = os.path.dirname(input_audio_path)
555
+
556
+ name_, ext_ = os.path.splitext(basename)
557
+ new_basename = f"{name_}_edited{ext_}"
558
+ new_path = os.path.join(dirname, new_basename)
559
+
560
+ output_audio_path = new_path
561
+
562
+ # Save file
563
+ if not type_output:
564
+ type_output = "wav"
565
+ output_audio_path = os.path.splitext(
566
+ output_audio_path
567
+ )[0]+f".{type_output}"
568
+
569
+ try:
570
+ if type_output.lower() == "wav":
571
+ sf.write(file=output_audio_path, samplerate=final_sr, data=audio_opt)
572
+ else:
573
+ target_sr = min(SUPPORTED_SAMPLE_RATES, key=lambda altsr: abs(altsr - final_sr))
574
+ if target_sr != final_sr:
575
+ logger.debug(f"Resampling from {final_sr} -> {target_sr} for {type_output}")
576
+ audio_opt = signal.resample_poly(audio_opt, target_sr, final_sr, axis=0).astype(np.int16)
577
+ sf.write(file=output_audio_path, samplerate=target_sr, data=audio_opt, format=type_output)
578
+ except Exception as e:
579
+ logger.error(e)
580
+ logger.error("Error saving file, trying with WAV format")
581
+ output_audio_path = os.path.splitext(output_audio_path)[0]+".wav"
582
+ sf.write(
583
+ file=output_audio_path,
584
+ samplerate=final_sr,
585
+ data=audio_opt
586
+ )
587
+
588
+ logger.info(str(output_audio_path))
589
+
590
+ self.model_config[task_id]["result"].append(output_audio_path)
591
+ self.output_list.append(output_audio_path)
592
+
593
+ def run_threads(self, threads):
594
+ # Start threads
595
+ for thread in threads:
596
+ thread.start()
597
+
598
+ # Wait for all threads to finish
599
+ for thread in threads:
600
+ thread.join()
601
+
602
+ gc.collect()
603
+ torch.cuda.empty_cache()
604
+
605
+ def unload_models(self):
606
+ self.hu_bert_model = None
607
+ self.model_pitch_estimator = None
608
+ self.model_vc = {}
609
+ self.cache_model = {}
610
+ gc.collect()
611
+ torch.cuda.empty_cache()
612
+
613
+ def __call__(
614
+ self,
615
+ audio_files=[],
616
+ tag_list=[],
617
+ overwrite=False,
618
+ parallel_workers=1,
619
+ type_output=None, # ["mp3", "wav", "flac"]
620
+ ):
621
+ logger.info(f"Parallel workers: {str(parallel_workers)}")
622
+
623
+ self.output_list = []
624
+
625
+ if not self.model_config:
626
+ raise ValueError("No model has been configured for inference")
627
+
628
+ if isinstance(audio_files, str):
629
+ audio_files = [audio_files]
630
+ if isinstance(tag_list, str):
631
+ tag_list = [tag_list]
632
+
633
+ if not audio_files:
634
+ raise ValueError("No audio found to convert")
635
+ if not tag_list:
636
+ tag_list = [list(self.model_config.keys())[-1]] * len(audio_files)
637
+
638
+ if len(audio_files) > len(tag_list):
639
+ logger.info("Extend tag list to match audio files")
640
+ extend_number = len(audio_files) - len(tag_list)
641
+ tag_list.extend([tag_list[0]] * extend_number)
642
+
643
+ if len(audio_files) < len(tag_list):
644
+ logger.info("Cut list tags")
645
+ tag_list = tag_list[:len(audio_files)]
646
+
647
+ tag_file_pairs = list(zip(tag_list, audio_files))
648
+ sorted_tag_file = sorted(tag_file_pairs, key=lambda x: x[0])
649
+
650
+ # Base params
651
+ if not self.hu_bert_model:
652
+ self.hu_bert_model = load_hu_bert(self.config, self.hubert_path)
653
+
654
+ cache_params = None
655
+ threads = []
656
+ progress_bar = tqdm(total=len(tag_list), desc="Progress")
657
+ for i, (id_tag, input_audio_path) in enumerate(sorted_tag_file):
658
+
659
+ if id_tag not in self.model_config.keys():
660
+ logger.info(
661
+ f"No configured model for {id_tag} with {input_audio_path}"
662
+ )
663
+ continue
664
+
665
+ if (
666
+ len(threads) >= parallel_workers
667
+ or cache_params != id_tag
668
+ and cache_params is not None
669
+ ):
670
+
671
+ self.run_threads(threads)
672
+ progress_bar.update(len(threads))
673
+
674
+ threads = []
675
+
676
+ if cache_params != id_tag:
677
+
678
+ self.model_config[id_tag]["result"] = []
679
+
680
+ # Unload previous
681
+ (
682
+ n_spk,
683
+ tgt_sr,
684
+ net_g,
685
+ pipe,
686
+ cpt,
687
+ version,
688
+ if_f0,
689
+ index_rate,
690
+ index,
691
+ big_npy,
692
+ inp_f0,
693
+ ) = [None] * 11
694
+ gc.collect()
695
+ torch.cuda.empty_cache()
696
+
697
+ # Model params
698
+ params = self.model_config[id_tag]
699
+
700
+ model_path = params["file_model"]
701
+ f0_method = params["pitch_algo"]
702
+ file_index = params["file_index"]
703
+ index_rate = params["index_influence"]
704
+ f0_file = params["file_pitch_algo"]
705
+
706
+ # Load model
707
+ (
708
+ n_spk,
709
+ tgt_sr,
710
+ net_g,
711
+ pipe,
712
+ cpt,
713
+ version
714
+ ) = load_trained_model(model_path, self.config)
715
+ if_f0 = cpt.get("f0", 1) # pitch data
716
+
717
+ # Load index
718
+ if os.path.exists(file_index) and index_rate != 0:
719
+ try:
720
+ index = faiss.read_index(file_index)
721
+ big_npy = index.reconstruct_n(0, index.ntotal)
722
+ except Exception as error:
723
+ logger.error(f"Index: {str(error)}")
724
+ index_rate = 0
725
+ index = big_npy = None
726
+ else:
727
+ logger.warning("File index not found")
728
+ index_rate = 0
729
+ index = big_npy = None
730
+
731
+ # Load f0 file
732
+ inp_f0 = None
733
+ if os.path.exists(f0_file):
734
+ try:
735
+ with open(f0_file, "r") as f:
736
+ lines = f.read().strip("\n").split("\n")
737
+ inp_f0 = []
738
+ for line in lines:
739
+ inp_f0.append([float(i) for i in line.split(",")])
740
+ inp_f0 = np.array(inp_f0, dtype="float32")
741
+ except Exception as error:
742
+ logger.error(f"f0 file: {str(error)}")
743
+
744
+ if "rmvpe" in f0_method:
745
+ if not self.model_pitch_estimator:
746
+ from infer_rvc_python.lib.rmvpe import RMVPE
747
+
748
+ logger.info("Loading vocal pitch estimator model")
749
+ if self.rmvpe_path is None:
750
+ self.rmvpe_path = ""
751
+ rm_local_path = "rmvpe.pt"
752
+ if os.path.exists(self.rmvpe_path):
753
+ rm_local_path = self.rmvpe_path
754
+ else:
755
+ download_manager(
756
+ os.path.join(BASE_DOWNLOAD_LINK, "rmvpe.pt"), BASE_DIR
757
+ )
758
+ self.model_pitch_estimator = RMVPE(
759
+ rm_local_path,
760
+ is_half=self.config.is_half,
761
+ device=self.config.device
762
+ )
763
+
764
+ pipe.model_rmvpe = self.model_pitch_estimator
765
+
766
+ cache_params = id_tag
767
+
768
+ # self.infer(
769
+ # id_tag,
770
+ # params,
771
+ # # load model
772
+ # n_spk,
773
+ # tgt_sr,
774
+ # net_g,
775
+ # pipe,
776
+ # cpt,
777
+ # version,
778
+ # if_f0,
779
+ # # load index
780
+ # index_rate,
781
+ # index,
782
+ # big_npy,
783
+ # # load f0 file
784
+ # inp_f0,
785
+ # # output file
786
+ # input_audio_path,
787
+ # overwrite,
788
+ # type_output,
789
+ # )
790
+
791
+ thread = threading.Thread(
792
+ target=self.infer,
793
+ args=(
794
+ id_tag,
795
+ params,
796
+ # loaded model
797
+ n_spk,
798
+ tgt_sr,
799
+ net_g,
800
+ pipe,
801
+ cpt,
802
+ version,
803
+ if_f0,
804
+ # loaded index
805
+ index_rate,
806
+ index,
807
+ big_npy,
808
+ # loaded f0 file
809
+ inp_f0,
810
+ # audio file
811
+ input_audio_path,
812
+ overwrite,
813
+ type_output,
814
+ )
815
+ )
816
+
817
+ threads.append(thread)
818
+
819
+ # Run last
820
+ if threads:
821
+ self.run_threads(threads)
822
+
823
+ progress_bar.update(len(threads))
824
+ progress_bar.close()
825
+
826
+ final_result = []
827
+ valid_tags = set(tag_list)
828
+ for tag in valid_tags:
829
+ if (
830
+ tag in self.model_config.keys()
831
+ and "result" in self.model_config[tag].keys()
832
+ ):
833
+ final_result.extend(self.model_config[tag]["result"])
834
+
835
+ return final_result
836
+
837
+ def generate_from_cache(
838
+ self,
839
+ audio_data=None, # str or tuple (<array data>,<int sampling rate>)
840
+ tag=None,
841
+ reload=False,
842
+ ):
843
+
844
+ if not self.model_config:
845
+ raise ValueError("No model has been configured for inference")
846
+
847
+ if not audio_data:
848
+ raise ValueError(
849
+ "An audio file or tuple with "
850
+ "(<numpy data audio>,<sampling rate>) is needed"
851
+ )
852
+
853
+ # Base params
854
+ if not self.hu_bert_model:
855
+ self.hu_bert_model = load_hu_bert(self.config, self.hubert_path)
856
+
857
+ if tag not in self.model_config.keys():
858
+ raise ValueError(
859
+ f"No configured model for {tag}"
860
+ )
861
+
862
+ now_data = self.model_config[tag]
863
+ now_data["tag"] = tag
864
+
865
+ if self.cache_model != now_data and not reload:
866
+
867
+ # Unload previous
868
+ self.model_vc = {}
869
+ gc.collect()
870
+ torch.cuda.empty_cache()
871
+
872
+ model_path = now_data["file_model"]
873
+ f0_method = now_data["pitch_algo"]
874
+ file_index = now_data["file_index"]
875
+ index_rate = now_data["index_influence"]
876
+ f0_file = now_data["file_pitch_algo"]
877
+
878
+ # Load model
879
+ (
880
+ self.model_vc["n_spk"],
881
+ self.model_vc["tgt_sr"],
882
+ self.model_vc["net_g"],
883
+ self.model_vc["pipe"],
884
+ self.model_vc["cpt"],
885
+ self.model_vc["version"]
886
+ ) = load_trained_model(model_path, self.config)
887
+ self.model_vc["if_f0"] = self.model_vc["cpt"].get("f0", 1)
888
+
889
+ # Load index
890
+ if os.path.exists(file_index) and index_rate != 0:
891
+ try:
892
+ index = faiss.read_index(file_index)
893
+ big_npy = index.reconstruct_n(0, index.ntotal)
894
+ except Exception as error:
895
+ logger.error(f"Index: {str(error)}")
896
+ index_rate = 0
897
+ index = big_npy = None
898
+ else:
899
+ logger.warning("File index not found")
900
+ index_rate = 0
901
+ index = big_npy = None
902
+
903
+ self.model_vc["index_rate"] = index_rate
904
+ self.model_vc["index"] = index
905
+ self.model_vc["big_npy"] = big_npy
906
+
907
+ # Load f0 file
908
+ inp_f0 = None
909
+ if os.path.exists(f0_file):
910
+ try:
911
+ with open(f0_file, "r") as f:
912
+ lines = f.read().strip("\n").split("\n")
913
+ inp_f0 = []
914
+ for line in lines:
915
+ inp_f0.append([float(i) for i in line.split(",")])
916
+ inp_f0 = np.array(inp_f0, dtype="float32")
917
+ except Exception as error:
918
+ logger.error(f"f0 file: {str(error)}")
919
+
920
+ self.model_vc["inp_f0"] = inp_f0
921
+
922
+ if "rmvpe" in f0_method:
923
+ if not self.model_pitch_estimator:
924
+ from infer_rvc_python.lib.rmvpe import RMVPE
925
+
926
+ logger.info("Loading vocal pitch estimator model")
927
+ if self.rmvpe_path is None:
928
+ self.rmvpe_path = ""
929
+ rm_local_path = "rmvpe.pt"
930
+ if os.path.exists(self.rmvpe_path):
931
+ rm_local_path = self.rmvpe_path
932
+ else:
933
+ download_manager(
934
+ os.path.join(BASE_DOWNLOAD_LINK, "rmvpe.pt"), BASE_DIR
935
+ )
936
+ self.model_pitch_estimator = RMVPE(
937
+ rm_local_path,
938
+ is_half=self.config.is_half,
939
+ device=self.config.device
940
+ )
941
+
942
+ self.model_vc["pipe"].model_rmvpe = self.model_pitch_estimator
943
+
944
+ self.cache_model = copy.deepcopy(now_data)
945
+
946
+ return self.infer(
947
+ tag,
948
+ now_data,
949
+ # load model
950
+ self.model_vc["n_spk"],
951
+ self.model_vc["tgt_sr"],
952
+ self.model_vc["net_g"],
953
+ self.model_vc["pipe"],
954
+ self.model_vc["cpt"],
955
+ self.model_vc["version"],
956
+ self.model_vc["if_f0"],
957
+ # load index
958
+ self.model_vc["index_rate"],
959
+ self.model_vc["index"],
960
+ self.model_vc["big_npy"],
961
+ # load f0 file
962
+ self.model_vc["inp_f0"],
963
+ # output file
964
+ audio_data,
965
+ False,
966
+ "array",
967
+ )