Spaces:
Runtime error
Runtime error
import gc | |
import os.path | |
import numpy as np | |
import parselmouth | |
import torch | |
import pyworld | |
import torchcrepe | |
from scipy import signal | |
from torch import Tensor | |
def get_f0_crepe_computation( | |
x, | |
f0_min, | |
f0_max, | |
p_len, | |
sr, | |
hop_length=128, | |
# 512 before. Hop length changes the speed that the voice jumps to a different dramatic pitch. Lower hop lengths means more pitch accuracy but longer inference time. | |
model="full", # Either use crepe-tiny "tiny" or crepe "full". Default is full | |
): | |
x = x.astype(np.float32) # fixes the F.conv2D exception. We needed to convert double to float. | |
x /= np.quantile(np.abs(x), 0.999) | |
torch_device = 'cuda' if torch.cuda.is_available() else 'cpu' | |
audio = torch.from_numpy(x).to(torch_device, copy=True) | |
audio = torch.unsqueeze(audio, dim=0) | |
if audio.ndim == 2 and audio.shape[0] > 1: | |
audio = torch.mean(audio, dim=0, keepdim=True).detach() | |
audio = audio.detach() | |
# print("Initiating prediction with a crepe_hop_length of: " + str(hop_length)) | |
pitch: torch.Tensor = torchcrepe.predict( | |
audio, | |
sr, | |
hop_length, | |
f0_min, | |
f0_max, | |
model, | |
batch_size=hop_length * 2, | |
device=torch_device, | |
pad=True | |
) | |
p_len = p_len or x.shape[0] // hop_length | |
# Resize the pitch for final f0 | |
source = np.array(pitch.squeeze(0).cpu().float().numpy()) | |
source[source < 0.001] = np.nan | |
target = np.interp( | |
np.arange(0, len(source) * p_len, len(source)) / p_len, | |
np.arange(0, len(source)), | |
source | |
) | |
f0 = np.nan_to_num(target) | |
return f0 # Resized f0 | |
def get_mangio_crepe_f0(x, f0_min, f0_max, p_len, sr, crepe_hop_length, model='full'): | |
# print("Performing crepe pitch extraction. (EXPERIMENTAL)") | |
# print("CREPE PITCH EXTRACTION HOP LENGTH: " + str(crepe_hop_length)) | |
x = x.astype(np.float32) | |
x /= np.quantile(np.abs(x), 0.999) | |
torch_device_index = 0 | |
torch_device = None | |
if torch.cuda.is_available(): | |
torch_device = torch.device(f"cuda:{torch_device_index % torch.cuda.device_count()}") | |
elif torch.backends.mps.is_available(): | |
torch_device = torch.device("mps") | |
else: | |
torch_device = torch.device("cpu") | |
audio = torch.from_numpy(x).to(torch_device, copy=True) | |
audio = torch.unsqueeze(audio, dim=0) | |
if audio.ndim == 2 and audio.shape[0] > 1: | |
audio = torch.mean(audio, dim=0, keepdim=True).detach() | |
audio = audio.detach() | |
# print( | |
# "Initiating f0 Crepe Feature Extraction with an extraction_crepe_hop_length of: " + | |
# str(crepe_hop_length) | |
# ) | |
# Pitch prediction for pitch extraction | |
pitch: Tensor = torchcrepe.predict( | |
audio, | |
sr, | |
crepe_hop_length, | |
f0_min, | |
f0_max, | |
model, | |
batch_size=crepe_hop_length * 2, | |
device=torch_device, | |
pad=True | |
) | |
p_len = p_len or x.shape[0] // crepe_hop_length | |
# Resize the pitch | |
source = np.array(pitch.squeeze(0).cpu().float().numpy()) | |
source[source < 0.001] = np.nan | |
target = np.interp( | |
np.arange(0, len(source) * p_len, len(source)) / p_len, | |
np.arange(0, len(source)), | |
source | |
) | |
return np.nan_to_num(target) | |
def pitch_extract(f0_method, x, f0_min, f0_max, p_len, time_step, sr, window, crepe_hop_length, filter_radius=3): | |
f0s = [] | |
f0 = np.zeros(p_len) | |
for method in f0_method if isinstance(f0_method, list) else [f0_method]: | |
if method == "pm": | |
f0 = ( | |
parselmouth.Sound(x, sr) | |
.to_pitch_ac( | |
time_step=time_step / 1000, | |
voicing_threshold=0.6, | |
pitch_floor=f0_min, | |
pitch_ceiling=f0_max, | |
) | |
.selected_array["frequency"] | |
) | |
pad_size = (p_len - len(f0) + 1) // 2 | |
if pad_size > 0 or p_len - len(f0) - pad_size > 0: | |
f0 = np.pad( | |
f0, [[pad_size, p_len - len(f0) - pad_size]], mode="constant" | |
) | |
elif method in ['harvest', 'dio']: | |
if method == 'harvest': | |
f0, t = pyworld.harvest( | |
x.astype(np.double), | |
fs=sr, | |
f0_ceil=f0_max, | |
f0_floor=f0_min, | |
frame_period=10, | |
) | |
elif method == "dio": | |
f0, t = pyworld.dio( | |
x.astype(np.double), | |
fs=sr, | |
f0_ceil=f0_max, | |
f0_floor=f0_min, | |
frame_period=10, | |
) | |
f0 = pyworld.stonemask(x.astype(np.double), f0, t, sr) | |
elif method == "torchcrepe": | |
f0 = get_f0_crepe_computation(x, f0_min, f0_max, p_len, sr, crepe_hop_length) | |
elif method == "torchcrepe tiny": | |
f0 = get_f0_crepe_computation(x, f0_min, f0_max, p_len, sr, crepe_hop_length, "tiny") | |
elif method == "mangio-crepe": | |
f0 = get_mangio_crepe_f0(x, f0_min, f0_max, p_len, sr, crepe_hop_length) | |
elif method == "mangio-crepe tiny": | |
f0 = get_mangio_crepe_f0(x, f0_min, f0_max, p_len, sr, crepe_hop_length, 'tiny') | |
elif method == "rmvpe": | |
rmvpe_model_path = os.path.join('data', 'models', 'rmvpe') | |
rmvpe_model_file = os.path.join(rmvpe_model_path, 'rmvpe.pt') | |
if not os.path.isfile(rmvpe_model_file): | |
import huggingface_hub | |
rmvpe_model_file = huggingface_hub.hf_hub_download('lj1995/VoiceConversionWebUI', 'rmvpe.pt', local_dir=rmvpe_model_path, local_dir_use_symlinks=False) | |
from modules.voice_conversion.rvc.rmvpe import RMVPE | |
print("loading rmvpe model") | |
model_rmvpe = RMVPE(rmvpe_model_file, is_half=True, device=None) | |
f0 = model_rmvpe.infer_from_audio(x, thred=0.03) | |
del model_rmvpe | |
torch.cuda.empty_cache() | |
gc.collect() | |
f0s.append(f0) | |
if not f0s: | |
f0s = [f0] | |
f0s_new = [] | |
for f0_val in f0s: | |
_len = f0_val.shape[0] | |
if _len == p_len: | |
f0s_new.append(f0) | |
continue | |
if _len > p_len: | |
f0 = f0[:p_len] | |
f0s_new.append(f0) | |
continue | |
if _len < p_len: | |
print('WARNING: len < p_len, skipping this f0') | |
f0 = np.nanmedian(np.stack(f0s_new, axis=0), axis=0) | |
if filter_radius >= 2: | |
f0 = signal.medfilt(f0, filter_radius) | |
return f0 | |