diff --git a/__init__.py b/__init__.py index 274a442..b6efde6 100644 --- a/__init__.py +++ b/__init__.py @@ -1,5 +1,5 @@ WEB_DIRECTORY = "./web" -from .nodes import LoadAudio, UVR5,PlayAudio +from .nodes import LoadAudio, UVR5,PreViewAudio # Set the web directory, any .js file in that directory will be loaded by the frontend as a frontend extension # WEB_DIRECTORY = "./somejs" @@ -9,12 +9,12 @@ from .nodes import LoadAudio, UVR5,PlayAudio NODE_CLASS_MAPPINGS = { "UVR5_Node": UVR5, "LoadAudio": LoadAudio, - "PlayAudio": PlayAudio + "PreViewAudio": PreViewAudio } # A dictionary that contains the friendly/humanly readable titles for the nodes NODE_DISPLAY_NAME_MAPPINGS = { "UVR5_Node": "UVR5 Node", "LoadAudio": "AudioLoader", - "PlayAudio": "PlayAudio" + "PreViewAudio": "PreView Audio" } diff --git a/__pycache__/__init__.cpython-310.pyc b/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000..733e795 Binary files /dev/null and b/__pycache__/__init__.cpython-310.pyc differ diff --git a/__pycache__/nodes.cpython-310.pyc b/__pycache__/nodes.cpython-310.pyc new file mode 100644 index 0000000..c9e0644 Binary files /dev/null and b/__pycache__/nodes.cpython-310.pyc differ diff --git a/nodes.py b/nodes.py index 46a748c..a5fb422 100644 --- a/nodes.py +++ b/nodes.py @@ -17,7 +17,8 @@ weights_path = os.path.join(node_path, "uvr5") device= "cuda" if cuda_malloc_supported() else "cpu" is_half=True -class PlayAudio: + +class PreViewAudio: @classmethod def INPUT_TYPES(s): return {"required": @@ -53,7 +54,7 @@ class LoadAudio: @classmethod def INPUT_TYPES(s): input_dir = input_path - files = [f for f in os.listdir(input_dir) if os.path.isfile(os.path.join(input_dir, f)) and f.split('.')[-1] in ["wav", "mp3"]] + files = [f for f in os.listdir(input_dir) if os.path.isfile(os.path.join(input_dir, f)) and f.split('.')[-1] in ["wav", "mp3","WAV","flac","m4a"]] return {"required": {"audio": (sorted(files),)}, } @@ -142,7 +143,7 @@ class UVR5: "display": "slider" }), "format0":(["wav", "flac", "mp3", "m4a"],{ - "default": "flac" + "default": "wav" }) }, } diff --git a/uvr5/.ipynb_checkpoints/mdxnet-checkpoint.py b/uvr5/.ipynb_checkpoints/mdxnet-checkpoint.py deleted file mode 100644 index e0ff8b7..0000000 --- a/uvr5/.ipynb_checkpoints/mdxnet-checkpoint.py +++ /dev/null @@ -1,261 +0,0 @@ -import os -import logging - -logger = logging.getLogger(__name__) - -import librosa -import numpy as np -import soundfile as sf -import torch -from tqdm import tqdm -from cuda_malloc import cuda_malloc_supported - -cpu = torch.device("cpu" if not cuda_malloc_supported() else "cuda") - - -class ConvTDFNetTrim: - def __init__( - self, device, model_name, target_name, L, dim_f, dim_t, n_fft, hop=1024 - ): - super(ConvTDFNetTrim, self).__init__() - - self.dim_f = dim_f - self.dim_t = 2**dim_t - self.n_fft = n_fft - self.hop = hop - self.n_bins = self.n_fft // 2 + 1 - self.chunk_size = hop * (self.dim_t - 1) - self.window = torch.hann_window(window_length=self.n_fft, periodic=True).to( - device - ) - self.target_name = target_name - self.blender = "blender" in model_name - - self.dim_c = 4 - out_c = self.dim_c * 4 if target_name == "*" else self.dim_c - self.freq_pad = torch.zeros( - [1, out_c, self.n_bins - self.dim_f, self.dim_t] - ).to(device) - - self.n = L // 2 - - def stft(self, x): - x = x.reshape([-1, self.chunk_size]) - x = torch.stft( - x, - n_fft=self.n_fft, - hop_length=self.hop, - window=self.window, - center=True, - return_complex=True, - ) - x = torch.view_as_real(x) - x = x.permute([0, 3, 1, 2]) - x = x.reshape([-1, 2, 2, self.n_bins, self.dim_t]).reshape( - [-1, self.dim_c, self.n_bins, self.dim_t] - ) - return x[:, :, : self.dim_f] - - def istft(self, x, freq_pad=None): - freq_pad = ( - self.freq_pad.repeat([x.shape[0], 1, 1, 1]) - if freq_pad is None - else freq_pad - ) - x = torch.cat([x, freq_pad], -2) - c = 4 * 2 if self.target_name == "*" else 2 - x = x.reshape([-1, c, 2, self.n_bins, self.dim_t]).reshape( - [-1, 2, self.n_bins, self.dim_t] - ) - x = x.permute([0, 2, 3, 1]) - x = x.contiguous() - x = torch.view_as_complex(x) - x = torch.istft( - x, n_fft=self.n_fft, hop_length=self.hop, window=self.window, center=True - ) - return x.reshape([-1, c, self.chunk_size]) - - -def get_models(device, dim_f, dim_t, n_fft): - return ConvTDFNetTrim( - device=device, - model_name="Conv-TDF", - target_name="vocals", - L=11, - dim_f=dim_f, - dim_t=dim_t, - n_fft=n_fft, - ) - - -class Predictor: - def __init__(self, args): - import onnxruntime as ort - - logger.info(ort.get_available_providers()) - self.args = args - self.model_ = get_models( - device=cpu, dim_f=args.dim_f, dim_t=args.dim_t, n_fft=args.n_fft - ) - self.model = ort.InferenceSession( - os.path.join(args.onnx, self.model_.target_name + ".onnx"), - providers=[ - "CUDAExecutionProvider", - "DmlExecutionProvider", - "CPUExecutionProvider", - ], - ) - logger.info("ONNX load done") - - def demix(self, mix): - samples = mix.shape[-1] - margin = self.args.margin - chunk_size = self.args.chunks * 44100 - assert not margin == 0, "margin cannot be zero!" - if margin > chunk_size: - margin = chunk_size - - segmented_mix = {} - - if self.args.chunks == 0 or samples < chunk_size: - chunk_size = samples - - counter = -1 - for skip in range(0, samples, chunk_size): - counter += 1 - - s_margin = 0 if counter == 0 else margin - end = min(skip + chunk_size + margin, samples) - - start = skip - s_margin - - segmented_mix[skip] = mix[:, start:end].copy() - if end == samples: - break - - sources = self.demix_base(segmented_mix, margin_size=margin) - """ - mix:(2,big_sample) - segmented_mix:offset->(2,small_sample) - sources:(1,2,big_sample) - """ - return sources - - def demix_base(self, mixes, margin_size): - chunked_sources = [] - progress_bar = tqdm(total=len(mixes)) - progress_bar.set_description("Processing") - for mix in mixes: - cmix = mixes[mix] - sources = [] - n_sample = cmix.shape[1] - model = self.model_ - trim = model.n_fft // 2 - gen_size = model.chunk_size - 2 * trim - pad = gen_size - n_sample % gen_size - mix_p = np.concatenate( - (np.zeros((2, trim)), cmix, np.zeros((2, pad)), np.zeros((2, trim))), 1 - ) - mix_waves = [] - i = 0 - while i < n_sample + pad: - waves = np.array(mix_p[:, i : i + model.chunk_size]) - mix_waves.append(waves) - i += gen_size - mix_waves = torch.tensor(mix_waves, dtype=torch.float32).to(cpu) - with torch.no_grad(): - _ort = self.model - spek = model.stft(mix_waves) - if self.args.denoise: - spec_pred = ( - -_ort.run(None, {"input": -spek.cpu().numpy()})[0] * 0.5 - + _ort.run(None, {"input": spek.cpu().numpy()})[0] * 0.5 - ) - tar_waves = model.istft(torch.tensor(spec_pred).to(cpu)) - else: - tar_waves = model.istft( - torch.tensor(_ort.run(None, {"input": spek.cpu().numpy()})[0]).to(cpu) - ) - tar_signal = ( - tar_waves[:, :, trim:-trim] - .transpose(0, 1) - .reshape(2, -1) - .cpu() - .numpy()[:, :-pad] - ) - - start = 0 if mix == 0 else margin_size - end = None if mix == list(mixes.keys())[::-1][0] else -margin_size - if margin_size == 0: - end = None - sources.append(tar_signal[:, start:end]) - - progress_bar.update(1) - - chunked_sources.append(sources) - _sources = np.concatenate(chunked_sources, axis=-1) - # del self.model - progress_bar.close() - return _sources - - def prediction(self, m, vocal_root, others_root, format): - os.makedirs(vocal_root, exist_ok=True) - os.makedirs(others_root, exist_ok=True) - basename = os.path.basename(m) - mix, rate = librosa.load(m, mono=False, sr=44100) - if mix.ndim == 1: - mix = np.asfortranarray([mix, mix]) - mix = mix.T - sources = self.demix(mix.T) - opt = sources[0].T - if format in ["wav", "flac"]: - vocal_AUDIO = "%s/%s_main_vocal.%s" % (vocal_root, basename, format) - sf.write( - vocal_AUDIO, mix - opt, rate - ) - bgm_AUDIO = "%s/%s_others.%s" % (others_root, basename, format) - sf.write(bgm_AUDIO, opt, rate) - else: - vocal_AUDIO = "%s/%s_main_vocal.wav" % (vocal_root, basename) - bgm_AUDIO = "%s/%s_others.wav" % (others_root, basename) - sf.write(vocal_AUDIO, mix - opt, rate) - sf.write(bgm_AUDIO, opt, rate) - opt_path_vocal = vocal_AUDIO[:-4] + ".%s" % format - opt_path_other = bgm_AUDIO[:-4] + ".%s" % format - if os.path.exists(vocal_AUDIO): - os.system( - "ffmpeg -i %s -vn %s -q:a 2 -y" % (vocal_AUDIO, opt_path_vocal) - ) - if os.path.exists(opt_path_vocal): - try: - os.remove(vocal_AUDIO) - except: - pass - if os.path.exists(bgm_AUDIO): - os.system( - "ffmpeg -i %s -vn %s -q:a 2 -y" % (bgm_AUDIO, opt_path_other) - ) - if os.path.exists(opt_path_other): - try: - os.remove(bgm_AUDIO) - except: - pass - return vocal_AUDIO,bgm_AUDIO - - -class MDXNetDereverb: - def __init__(self, chunks): - self.onnx = "%s/uvr5_weights/onnx_dereverb_By_FoxJoy"%os.path.dirname(os.path.abspath(__file__)) - self.shifts = 10 # 'Predict with randomised equivariant stabilisation' - self.mixing = "min_mag" # ['default','min_mag','max_mag'] - self.chunks = chunks - self.margin = 44100 - self.dim_t = 9 - self.dim_f = 3072 - self.n_fft = 6144 - self.denoise = True - self.pred = Predictor(self) - self.device = cpu - - def _path_audio_(self, input, others_root, vocal_root, format, is_hp3=False): - return self.pred.prediction(input, vocal_root, others_root, format) diff --git a/uvr5/.ipynb_checkpoints/vr-checkpoint.py b/uvr5/.ipynb_checkpoints/vr-checkpoint.py deleted file mode 100644 index 32d70b8..0000000 --- a/uvr5/.ipynb_checkpoints/vr-checkpoint.py +++ /dev/null @@ -1,378 +0,0 @@ -import os,sys -parent_directory = os.path.dirname(os.path.abspath(__file__)) -import logging,pdb -logger = logging.getLogger(__name__) - -import librosa -import numpy as np -import soundfile as sf -import torch -from .lib.lib_v5 import nets_61968KB as Nets -from .lib.lib_v5 import spec_utils -from .lib.lib_v5.model_param_init import ModelParameters -from .lib.lib_v5.nets_new import CascadedNet -from .lib.utils import inference - - -class AudioPre: - def __init__(self, agg, model_path, device, is_half, tta=False): - self.model_path = model_path - self.device = device - self.data = { - # Processing Options - "postprocess": False, - "tta": tta, - # Constants - "window_size": 512, - "agg": agg, - "high_end_process": "mirroring", - } - mp = ModelParameters("%s/lib/lib_v5/modelparams/4band_v2.json"%parent_directory) - model = Nets.CascadedASPPNet(mp.param["bins"] * 2) - cpk = torch.load(model_path, map_location="cpu") - model.load_state_dict(cpk) - model.eval() - if is_half: - model = model.half().to(device) - else: - model = model.to(device) - - self.mp = mp - self.model = model - - def _path_audio_( - self, music_file, ins_root=None, vocal_root=None, format="flac", is_hp3=False - ): - if ins_root is None and vocal_root is None: - return "No save root." - name = os.path.basename(music_file) - if ins_root is not None: - os.makedirs(ins_root, exist_ok=True) - if vocal_root is not None: - os.makedirs(vocal_root, exist_ok=True) - X_wave, y_wave, X_spec_s, y_spec_s = {}, {}, {}, {} - bands_n = len(self.mp.param["band"]) - # print(bands_n) - for d in range(bands_n, 0, -1): - bp = self.mp.param["band"][d] - if d == bands_n: # high-end band - ( - X_wave[d], - _, - ) = librosa.core.load( # 理论上librosa读取可能对某些音频有bug,应该上ffmpeg读取,但是太麻烦了弃坑 - music_file, - sr = bp["sr"], - mono = False, - dtype = np.float32, - res_type = bp["res_type"], - ) - if X_wave[d].ndim == 1: - X_wave[d] = np.asfortranarray([X_wave[d], X_wave[d]]) - else: # lower bands - X_wave[d] = librosa.core.resample( - X_wave[d + 1], - orig_sr = self.mp.param["band"][d + 1]["sr"], - target_sr = bp["sr"], - res_type = bp["res_type"], - ) - # Stft of wave source - X_spec_s[d] = spec_utils.wave_to_spectrogram_mt( - X_wave[d], - bp["hl"], - bp["n_fft"], - self.mp.param["mid_side"], - self.mp.param["mid_side_b2"], - self.mp.param["reverse"], - ) - # pdb.set_trace() - if d == bands_n and self.data["high_end_process"] != "none": - input_high_end_h = (bp["n_fft"] // 2 - bp["crop_stop"]) + ( - self.mp.param["pre_filter_stop"] - self.mp.param["pre_filter_start"] - ) - input_high_end = X_spec_s[d][ - :, bp["n_fft"] // 2 - input_high_end_h : bp["n_fft"] // 2, : - ] - - X_spec_m = spec_utils.combine_spectrograms(X_spec_s, self.mp) - aggresive_set = float(self.data["agg"] / 100) - aggressiveness = { - "value": aggresive_set, - "split_bin": self.mp.param["band"][1]["crop_stop"], - } - with torch.no_grad(): - pred, X_mag, X_phase = inference( - X_spec_m, self.device, self.model, aggressiveness, self.data - ) - # Postprocess - if self.data["postprocess"]: - pred_inv = np.clip(X_mag - pred, 0, np.inf) - pred = spec_utils.mask_silence(pred, pred_inv) - y_spec_m = pred * X_phase - v_spec_m = X_spec_m - y_spec_m - - if is_hp3 == True: - ins_root,vocal_root = vocal_root,ins_root - - if ins_root is not None: - if self.data["high_end_process"].startswith("mirroring"): - input_high_end_ = spec_utils.mirroring( - self.data["high_end_process"], y_spec_m, input_high_end, self.mp - ) - wav_instrument = spec_utils.cmb_spectrogram_to_wave( - y_spec_m, self.mp, input_high_end_h, input_high_end_ - ) - else: - wav_instrument = spec_utils.cmb_spectrogram_to_wave(y_spec_m, self.mp) - logger.info("%s instruments done" % name) - if is_hp3 == True: - head = "vocal_" - else: - head = "instrument_" - if format in ["wav", "flac"]: - bgm_AUDIO = os.path.join( - ins_root, - head + "{}_{}.{}".format(name, self.data["agg"], format), - ) - sf.write( - bgm_AUDIO, - (np.array(wav_instrument) * 32768).astype("int16"), - self.mp.param["sr"], - ) # - else: - bgm_AUDIO = os.path.join( - ins_root, head + "{}_{}.wav".format(name, self.data["agg"]) - ) - sf.write( - bgm_AUDIO, - (np.array(wav_instrument) * 32768).astype("int16"), - self.mp.param["sr"], - ) - if os.path.exists(bgm_AUDIO): - opt_format_path = bgm_AUDIO[:-4] + ".%s" % format - os.system("ffmpeg -i %s -vn %s -q:a 2 -y" % (bgm_AUDIO, opt_format_path)) - if os.path.exists(opt_format_path): - try: - os.remove(bgm_AUDIO) - except: - pass - if vocal_root is not None: - if is_hp3 == True: - head = "instrument_" - else: - head = "vocal_" - if self.data["high_end_process"].startswith("mirroring"): - input_high_end_ = spec_utils.mirroring( - self.data["high_end_process"], v_spec_m, input_high_end, self.mp - ) - wav_vocals = spec_utils.cmb_spectrogram_to_wave( - v_spec_m, self.mp, input_high_end_h, input_high_end_ - ) - else: - wav_vocals = spec_utils.cmb_spectrogram_to_wave(v_spec_m, self.mp) - logger.info("%s vocals done" % name) - if format in ["wav", "flac"]: - vocal_AUDIO = os.path.join( - vocal_root, - head + "{}_{}.{}".format(name, self.data["agg"], format), - ) - sf.write( - vocal_AUDIO, - (np.array(wav_vocals) * 32768).astype("int16"), - self.mp.param["sr"], - ) - else: - vocal_AUDIO = os.path.join( - vocal_root, head + "{}_{}.wav".format(name, self.data["agg"]) - ) - sf.write( - vocal_AUDIO, - (np.array(wav_vocals) * 32768).astype("int16"), - self.mp.param["sr"], - ) - if os.path.exists(vocal_AUDIO): - opt_format_path = vocal_AUDIO[:-4] + ".%s" % format - os.system("ffmpeg -i %s -vn %s -q:a 2 -y" % (vocal_AUDIO, opt_format_path)) - if os.path.exists(opt_format_path): - try: - os.remove(vocal_AUDIO) - except: - pass - if is_hp3 == True: - return bgm_AUDIO,vocal_AUDIO - return vocal_AUDIO, bgm_AUDIO - - -class AudioPreDeEcho: - def __init__(self, agg, model_path, device, is_half, tta=False): - self.model_path = model_path - self.device = device - self.data = { - # Processing Options - "postprocess": False, - "tta": tta, - # Constants - "window_size": 512, - "agg": agg, - "high_end_process": "mirroring", - } - mp = ModelParameters("%s/lib/lib_v5/modelparams/4band_v3.json"%parent_directory) - nout = 64 if "DeReverb" in model_path else 48 - model = CascadedNet(mp.param["bins"] * 2, nout) - cpk = torch.load(model_path, map_location="cpu") - model.load_state_dict(cpk) - model.eval() - if is_half: - model = model.half().to(device) - else: - model = model.to(device) - - self.mp = mp - self.model = model - - def _path_audio_( - self, music_file, vocal_root=None, ins_root=None, format="flac", is_hp3=False - ): # 3个VR模型vocal和ins是反的 - if ins_root is None and vocal_root is None: - return "No save root." - name = os.path.basename(music_file) - if ins_root is not None: - os.makedirs(ins_root, exist_ok=True) - if vocal_root is not None: - os.makedirs(vocal_root, exist_ok=True) - X_wave, y_wave, X_spec_s, y_spec_s = {}, {}, {}, {} - bands_n = len(self.mp.param["band"]) - # print(bands_n) - for d in range(bands_n, 0, -1): - bp = self.mp.param["band"][d] - if d == bands_n: # high-end band - ( - X_wave[d], - _, - ) = librosa.core.load( # 理论上librosa读取可能对某些音频有bug,应该上ffmpeg读取,但是太麻烦了弃坑 - music_file, - sr = bp["sr"], - mono = False, - dtype = np.float32, - res_type = bp["res_type"], - ) - if X_wave[d].ndim == 1: - X_wave[d] = np.asfortranarray([X_wave[d], X_wave[d]]) - else: # lower bands - X_wave[d] = librosa.core.resample( - X_wave[d + 1], - orig_sr = self.mp.param["band"][d + 1]["sr"], - target_sr = bp["sr"], - res_type = bp["res_type"], - ) - # Stft of wave source - X_spec_s[d] = spec_utils.wave_to_spectrogram_mt( - X_wave[d], - bp["hl"], - bp["n_fft"], - self.mp.param["mid_side"], - self.mp.param["mid_side_b2"], - self.mp.param["reverse"], - ) - # pdb.set_trace() - if d == bands_n and self.data["high_end_process"] != "none": - input_high_end_h = (bp["n_fft"] // 2 - bp["crop_stop"]) + ( - self.mp.param["pre_filter_stop"] - self.mp.param["pre_filter_start"] - ) - input_high_end = X_spec_s[d][ - :, bp["n_fft"] // 2 - input_high_end_h : bp["n_fft"] // 2, : - ] - - X_spec_m = spec_utils.combine_spectrograms(X_spec_s, self.mp) - aggresive_set = float(self.data["agg"] / 100) - aggressiveness = { - "value": aggresive_set, - "split_bin": self.mp.param["band"][1]["crop_stop"], - } - with torch.no_grad(): - pred, X_mag, X_phase = inference( - X_spec_m, self.device, self.model, aggressiveness, self.data - ) - # Postprocess - if self.data["postprocess"]: - pred_inv = np.clip(X_mag - pred, 0, np.inf) - pred = spec_utils.mask_silence(pred, pred_inv) - y_spec_m = pred * X_phase - v_spec_m = X_spec_m - y_spec_m - - if ins_root is not None: - if self.data["high_end_process"].startswith("mirroring"): - input_high_end_ = spec_utils.mirroring( - self.data["high_end_process"], y_spec_m, input_high_end, self.mp - ) - wav_instrument = spec_utils.cmb_spectrogram_to_wave( - y_spec_m, self.mp, input_high_end_h, input_high_end_ - ) - else: - wav_instrument = spec_utils.cmb_spectrogram_to_wave(y_spec_m, self.mp) - logger.info("%s instruments done" % name) - if format in ["wav", "flac"]: - bgm_AUDIO = os.path.join( - ins_root, - "vocal_{}_{}.{}".format(name, self.data["agg"], format), - ) - sf.write( - bgm_AUDIO, - (np.array(wav_instrument) * 32768).astype("int16"), - self.mp.param["sr"], - ) # - else: - bgm_AUDIO = os.path.join( - ins_root, "vocal_{}_{}.wav".format(name, self.data["agg"]) - ) - sf.write( - bgm_AUDIO, - (np.array(wav_instrument) * 32768).astype("int16"), - self.mp.param["sr"], - ) - if os.path.exists(bgm_AUDIO): - opt_format_path = bgm_AUDIO[:-4] + ".%s" % format - os.system("ffmpeg -i %s -vn %s -q:a 2 -y" % (bgm_AUDIO, opt_format_path)) - if os.path.exists(opt_format_path): - try: - os.remove(bgm_AUDIO) - except: - pass - if vocal_root is not None: - if self.data["high_end_process"].startswith("mirroring"): - input_high_end_ = spec_utils.mirroring( - self.data["high_end_process"], v_spec_m, input_high_end, self.mp - ) - wav_vocals = spec_utils.cmb_spectrogram_to_wave( - v_spec_m, self.mp, input_high_end_h, input_high_end_ - ) - else: - wav_vocals = spec_utils.cmb_spectrogram_to_wave(v_spec_m, self.mp) - logger.info("%s vocals done" % name) - if format in ["wav", "flac"]: - vocal_AUDIO = os.path.join( - vocal_root, - "instrument_{}_{}.{}".format(name, self.data["agg"], format), - ) - sf.write( - vocal_AUDIO, - (np.array(wav_vocals) * 32768).astype("int16"), - self.mp.param["sr"], - ) - else: - vocal_AUDIO = os.path.join( - vocal_root, "instrument_{}_{}.wav".format(name, self.data["agg"]) - ) - sf.write( - vocal_AUDIO, - (np.array(wav_vocals) * 32768).astype("int16"), - self.mp.param["sr"], - ) - if os.path.exists(vocal_AUDIO): - opt_format_path = vocal_AUDIO[:-4] + ".%s" % format - os.system("ffmpeg -i %s -vn %s -q:a 2 -y" % (vocal_AUDIO, opt_format_path)) - if os.path.exists(opt_format_path): - try: - os.remove(vocal_AUDIO) - except: - pass - return bgm_AUDIO,vocal_AUDIO diff --git a/uvr5/__pycache__/mdxnet.cpython-310.pyc b/uvr5/__pycache__/mdxnet.cpython-310.pyc deleted file mode 100644 index 1e21ea1..0000000 Binary files a/uvr5/__pycache__/mdxnet.cpython-310.pyc and /dev/null differ diff --git a/uvr5/__pycache__/vr.cpython-310.pyc b/uvr5/__pycache__/vr.cpython-310.pyc deleted file mode 100644 index 9009abf..0000000 Binary files a/uvr5/__pycache__/vr.cpython-310.pyc and /dev/null differ diff --git a/uvr5/lib/__pycache__/utils.cpython-310.pyc b/uvr5/lib/__pycache__/utils.cpython-310.pyc index c796326..b8983be 100644 Binary files a/uvr5/lib/__pycache__/utils.cpython-310.pyc and b/uvr5/lib/__pycache__/utils.cpython-310.pyc differ diff --git a/uvr5/lib/lib_v5/__pycache__/layers_123821KB.cpython-310.pyc b/uvr5/lib/lib_v5/__pycache__/layers_123821KB.cpython-310.pyc index 0b8aed5..d43f200 100644 Binary files a/uvr5/lib/lib_v5/__pycache__/layers_123821KB.cpython-310.pyc and b/uvr5/lib/lib_v5/__pycache__/layers_123821KB.cpython-310.pyc differ diff --git a/uvr5/lib/lib_v5/__pycache__/layers_new.cpython-310.pyc b/uvr5/lib/lib_v5/__pycache__/layers_new.cpython-310.pyc index 80cc99a..1aedb6a 100644 Binary files a/uvr5/lib/lib_v5/__pycache__/layers_new.cpython-310.pyc and b/uvr5/lib/lib_v5/__pycache__/layers_new.cpython-310.pyc differ diff --git a/uvr5/lib/lib_v5/__pycache__/model_param_init.cpython-310.pyc b/uvr5/lib/lib_v5/__pycache__/model_param_init.cpython-310.pyc index faf0b65..ad8e915 100644 Binary files a/uvr5/lib/lib_v5/__pycache__/model_param_init.cpython-310.pyc and b/uvr5/lib/lib_v5/__pycache__/model_param_init.cpython-310.pyc differ diff --git a/uvr5/lib/lib_v5/__pycache__/nets_61968KB.cpython-310.pyc b/uvr5/lib/lib_v5/__pycache__/nets_61968KB.cpython-310.pyc index fd8e700..3a2402f 100644 Binary files a/uvr5/lib/lib_v5/__pycache__/nets_61968KB.cpython-310.pyc and b/uvr5/lib/lib_v5/__pycache__/nets_61968KB.cpython-310.pyc differ diff --git a/uvr5/lib/lib_v5/__pycache__/nets_new.cpython-310.pyc b/uvr5/lib/lib_v5/__pycache__/nets_new.cpython-310.pyc index 4a62ad3..4e47979 100644 Binary files a/uvr5/lib/lib_v5/__pycache__/nets_new.cpython-310.pyc and b/uvr5/lib/lib_v5/__pycache__/nets_new.cpython-310.pyc differ diff --git a/uvr5/lib/lib_v5/__pycache__/spec_utils.cpython-310.pyc b/uvr5/lib/lib_v5/__pycache__/spec_utils.cpython-310.pyc index 131d668..7b54bd3 100644 Binary files a/uvr5/lib/lib_v5/__pycache__/spec_utils.cpython-310.pyc and b/uvr5/lib/lib_v5/__pycache__/spec_utils.cpython-310.pyc differ diff --git a/web/js/audioUpload.js b/web/js/audioUpload.js deleted file mode 100644 index f390053..0000000 --- a/web/js/audioUpload.js +++ /dev/null @@ -1,171 +0,0 @@ -import { app } from "../../../scripts/app.js"; -import { api } from '../../../scripts/api.js' -import { ComfyWidgets } from "../../../scripts/widgets.js" - -function audioUpload(node, inputName, inputData, app) { - const audioWidget = node.widgets.find((w) => w.name === "audio"); - let uploadWidget; - /* - A method that returns the required style for the html - */ - function playAudio(node, name) { - let url = `/view?filename=${encodeURIComponent(name)}&type=input&subfolder=${app.getPreviewFormatParam()}${app.getRandParam()}` - - while (node.widgets.length > 2){ - node.widgets.pop() - } - - let isTick = true; - const audio = new Audio(url); - const slider = node.addWidget( - "slider", - "loading", - 0, - (v) => { - if (!isTick) { - audio.currentTime = v; - } - isTick = false; - }, - { - min: 0, - max: 0, - } - ); - - const button = node.addWidget("button", `Play ${name}`, "play", () => { - try { - if (audio.paused) { - audio.play(); - button.name = `Pause ${name}`; - } else { - audio.pause(); - button.name = `Play ${name}`; - } - } catch (error) { - alert(error); - } - app.canvas.setDirty(true); - }); - audio.addEventListener("timeupdate", () => { - isTick = true; - slider.value = audio.currentTime; - app.canvas.setDirty(true); - }); - audio.addEventListener("ended", () => { - button.name = `Play ${name}`; - app.canvas.setDirty(true); - }); - audio.addEventListener("loadedmetadata", () => { - slider.options.max = audio.duration; - slider.name = `(${audio.duration})`; - app.canvas.setDirty(true); - }); - } - - var default_value = audioWidget.value; - Object.defineProperty(audioWidget, "value", { - set : function(value) { - this._real_value = value; - }, - - get : function() { - let value = ""; - if (this._real_value) { - value = this._real_value; - } else { - return default_value; - } - - if (value.filename) { - let real_value = value; - value = ""; - if (real_value.subfolder) { - value = real_value.subfolder + "/"; - } - - value += real_value.filename; - - if(real_value.type && real_value.type !== "input") - value += ` [${real_value.type}]`; - } - return value; - } - }); - async function uploadFile(file, updateNode, pasted = false) { - try { - // Wrap file in formdata so it includes filename - const body = new FormData(); - body.append("image", file); - if (pasted) body.append("subfolder", "pasted"); - const resp = await api.fetchApi("/upload/image", { - method: "POST", - body, - }); - - if (resp.status === 200) { - const data = await resp.json(); - // Add the file to the dropdown list and update the widget value - let path = data.name; - if (data.subfolder) path = data.subfolder + "/" + path; - - if (!audioWidget.options.values.includes(path)) { - audioWidget.options.values.push(path); - } - - if (updateNode) { - audioWidget.value = path; - // showAudio(path) - playAudio(node, path); - } - } else { - alert(resp.status + " - " + resp.statusText); - } - } catch (error) { - alert(error); - } - } - - const fileInput = document.createElement("input"); - Object.assign(fileInput, { - type: "file", - accept: "audio/mp3,audio/wav,audio/flac,audio/m4a", - style: "display: none", - onchange: async () => { - if (fileInput.files.length) { - await uploadFile(fileInput.files[0], true); - } - }, - }); - document.body.append(fileInput); - - // Create the button widget for selecting the files - uploadWidget = node.addWidget("button", "choose audio file to upload", "Audio", () => { - fileInput.click(); - }); - - uploadWidget.serialize = false; - playAudio(node, audioWidget.value); - - const cb = node.callback; - audioWidget.callback = function () { - playAudio(node, audioWidget.value); - if (cb) { - return cb.apply(this, arguments); - } - }; - - return { widget: uploadWidget }; -} - -ComfyWidgets.AUDIOUPLOAD = audioUpload; - -app.registerExtension({ - name: "UVR5.UploadAudio", - async beforeRegisterNodeDef(nodeType, nodeData, app) { - if (nodeData?.name == "LoadAudio") { - nodeData.input.required.upload = ["AUDIOUPLOAD"]; - } - }, -}); - diff --git a/web/js/playAudio.js b/web/js/playAudio.js deleted file mode 100644 index d3585c1..0000000 --- a/web/js/playAudio.js +++ /dev/null @@ -1,85 +0,0 @@ -/** - * File: playAudio.js - * Project: comfyui_jags_audiotools - * Author: jags111 - * - * Copyright (c) 2023 jags111 - * - */ -import { app } from "../../../scripts/app.js"; -import { api } from '../../../scripts/api.js' -import { ComfyWidgets } from "../../../scripts/widgets.js" - - -/* -A method that returns the required style for the html -*/ -function addPlaybackWidget(node, name, root) { - - try { - while (node.widgets.length > 0){ - node.widgets.pop() - } - } catch (error) { - //console.log(error); - } - let url = `/view?filename=${encodeURIComponent(name)}&type=${root}&subfolder=${app.getPreviewFormatParam()}${app.getRandParam()}`; - let isTick = true; - const audio = new Audio(url); - const slider = node.addWidget( - "slider", - "loading", - 0, - (v) => { - if (!isTick) { - audio.currentTime = v; - } - isTick = false; - }, - { - min: 0, - max: 0, - } - ); - - const button = node.addWidget("button", `Play ${name}`, "play", () => { - try { - if (audio.paused) { - audio.play(); - button.name = `Pause ${name}`; - } else { - audio.pause(); - button.name = `Play ${name}`; - } - } catch (error) { - alert(error); - } - app.canvas.setDirty(true); - }); - - audio.addEventListener("timeupdate", () => { - isTick = true; - slider.value = audio.currentTime; - app.canvas.setDirty(true); - }); - audio.addEventListener("ended", () => { - button.name = `Play ${name}`; - app.canvas.setDirty(true); - }); - audio.addEventListener("loadedmetadata", () => { - slider.options.max = audio.duration; - slider.name = `(${audio.duration})`; - app.canvas.setDirty(true); - }); -} - -app.registerExtension({ - name: "UVR5.AudioPlayer", - async beforeRegisterNodeDef(nodeType, nodeData, app) { - if (nodeData?.name == "PlayAudio") { - nodeType.prototype.onExecuted = function (data) { - addPlaybackWidget(this, data.audio[0], data.audio[1]); - } - } - } -}); diff --git a/web/js/previewAudio.js b/web/js/previewAudio.js new file mode 100644 index 0000000..86ab47c --- /dev/null +++ b/web/js/previewAudio.js @@ -0,0 +1,155 @@ +import { app } from "../../../scripts/app.js"; +import { api } from '../../../scripts/api.js' + +function fitHeight(node) { + node.setSize([node.size[0], node.computeSize([node.size[0], node.size[1]])[1]]) + node?.graph?.setDirtyCanvas(true); +} +function chainCallback(object, property, callback) { + if (object == undefined) { + //This should not happen. + console.error("Tried to add callback to non-existant object") + return; + } + if (property in object) { + const callback_orig = object[property] + object[property] = function () { + const r = callback_orig.apply(this, arguments); + callback.apply(this, arguments); + return r + }; + } else { + object[property] = callback; + } +} + +function addPreviewOptions(nodeType) { + chainCallback(nodeType.prototype, "getExtraMenuOptions", function(_, options) { + // The intended way of appending options is returning a list of extra options, + // but this isn't used in widgetInputs.js and would require + // less generalization of chainCallback + let optNew = [] + try { + const previewWidget = this.widgets.find((w) => w.name === "audiopreview"); + + let url = null + if (previewWidget.audioEl?.hidden == false && previewWidget.audioEl.src) { + //Use full quality audio + //url = api.apiURL('/view?' + new URLSearchParams(previewWidget.value.params)); + url = previewWidget.audioEl.src + } + if (url) { + optNew.push( + { + content: "Open preview", + callback: () => { + window.open(url, "_blank") + }, + }, + { + content: "Save preview", + callback: () => { + const a = document.createElement("a"); + a.href = url; + a.setAttribute("download", new URLSearchParams(previewWidget.value.params).get("filename")); + document.body.append(a); + a.click(); + requestAnimationFrame(() => a.remove()); + }, + } + ); + } + if(options.length > 0 && options[0] != null && optNew.length > 0) { + optNew.push(null); + } + options.unshift(...optNew); + + } catch (error) { + console.log(error); + } + + }); +} +function previewAudio(node,file,type){ + var element = document.createElement("div"); + const previewNode = node; + var previewWidget = node.addDOMWidget("audiopreview", "preview", element, { + serialize: false, + hideOnZoom: false, + getValue() { + return element.value; + }, + setValue(v) { + element.value = v; + }, + }); + previewWidget.computeSize = function(width) { + if (this.aspectRatio && !this.parentEl.hidden) { + let height = (previewNode.size[0]-20)/ this.aspectRatio + 10; + if (!(height > 0)) { + height = 0; + } + this.computedHeight = height + 10; + return [width, height]; + } + return [width, -4];//no loaded src, widget should not display + } + // element.style['pointer-events'] = "none" + previewWidget.value = {hidden: false, paused: false, params: {}} + previewWidget.parentEl = document.createElement("div"); + previewWidget.parentEl.className = "audio_preview"; + previewWidget.parentEl.style['width'] = "100%" + element.appendChild(previewWidget.parentEl); + previewWidget.audioEl = document.createElement("audio"); + previewWidget.audioEl.controls = true; + previewWidget.audioEl.loop = false; + previewWidget.audioEl.muted = false; + previewWidget.audioEl.style['width'] = "100%" + previewWidget.audioEl.addEventListener("loadedmetadata", () => { + + previewWidget.aspectRatio = previewWidget.audioEl.audioWidth / previewWidget.audioEl.audioHeight; + fitHeight(this); + }); + previewWidget.audioEl.addEventListener("error", () => { + //TODO: consider a way to properly notify the user why a preview isn't shown. + previewWidget.parentEl.hidden = true; + fitHeight(this); + }); + + let params = { + "filename": file, + "type": type, + } + + previewWidget.parentEl.hidden = previewWidget.value.hidden; + previewWidget.audioEl.autoplay = !previewWidget.value.paused && !previewWidget.value.hidden; + let target_width = 256 + if (element.style?.width) { + //overscale to allow scrolling. Endpoint won't return higher than native + target_width = element.style.width.slice(0,-2)*2; + } + if (!params.force_size || params.force_size.includes("?") || params.force_size == "Disabled") { + params.force_size = target_width+"x?" + } else { + let size = params.force_size.split("x") + let ar = parseInt(size[0])/parseInt(size[1]) + params.force_size = target_width+"x"+(target_width/ar) + } + + previewWidget.audioEl.src = api.apiURL('/view?' + new URLSearchParams(params)); + + previewWidget.audioEl.hidden = false; + previewWidget.parentEl.appendChild(previewWidget.audioEl) +} + +app.registerExtension({ + name: "UVR5.AudioPreviewer", + async beforeRegisterNodeDef(nodeType, nodeData, app) { + if (nodeData?.name == "PreViewAudio") { + nodeType.prototype.onExecuted = function (data) { + previewAudio(this, data.audio[0], data.audio[1]); + } + addPreviewOptions(nodeType) + } + } +}); \ No newline at end of file diff --git a/web/js/uploadAudio.js b/web/js/uploadAudio.js new file mode 100644 index 0000000..b1acb67 --- /dev/null +++ b/web/js/uploadAudio.js @@ -0,0 +1,203 @@ +import { app } from "../../../scripts/app.js"; +import { api } from '../../../scripts/api.js' +import { ComfyWidgets } from "../../../scripts/widgets.js" + +function fitHeight(node) { + node.setSize([node.size[0], node.computeSize([node.size[0], node.size[1]])[1]]) + node?.graph?.setDirtyCanvas(true); +} + +function previewAudio(node,file){ + while (node.widgets.length > 2){ + node.widgets.pop(); + } + try { + var el = document.getElementById("uploadAudio"); + el.remove(); + } catch (error) { + console.log(error); + } + var element = document.createElement("div"); + element.id = "uploadAudio"; + const previewNode = node; + var previewWidget = node.addDOMWidget("audiopreview", "preview", element, { + serialize: false, + hideOnZoom: false, + getValue() { + return element.value; + }, + setValue(v) { + element.value = v; + }, + }); + previewWidget.computeSize = function(width) { + if (this.aspectRatio && !this.parentEl.hidden) { + let height = (previewNode.size[0]-20)/ this.aspectRatio + 10; + if (!(height > 0)) { + height = 0; + } + this.computedHeight = height + 10; + return [width, height]; + } + return [width, -4];//no loaded src, widget should not display + } + // element.style['pointer-events'] = "none" + previewWidget.value = {hidden: false, paused: false, params: {}} + previewWidget.parentEl = document.createElement("div"); + previewWidget.parentEl.className = "audio_preview"; + previewWidget.parentEl.style['width'] = "100%" + element.appendChild(previewWidget.parentEl); + previewWidget.audioEl = document.createElement("audio"); + previewWidget.audioEl.controls = true; + previewWidget.audioEl.loop = false; + previewWidget.audioEl.muted = false; + previewWidget.audioEl.style['width'] = "100%" + previewWidget.audioEl.addEventListener("loadedmetadata", () => { + + previewWidget.aspectRatio = previewWidget.audioEl.audioWidth / previewWidget.audioEl.audioHeight; + fitHeight(this); + }); + previewWidget.audioEl.addEventListener("error", () => { + //TODO: consider a way to properly notify the user why a preview isn't shown. + previewWidget.parentEl.hidden = true; + fitHeight(this); + }); + + let params = { + "filename": file, + "type": "input", + } + + previewWidget.parentEl.hidden = previewWidget.value.hidden; + previewWidget.audioEl.autoplay = !previewWidget.value.paused && !previewWidget.value.hidden; + let target_width = 256 + if (element.style?.width) { + //overscale to allow scrolling. Endpoint won't return higher than native + target_width = element.style.width.slice(0,-2)*2; + } + if (!params.force_size || params.force_size.includes("?") || params.force_size == "Disabled") { + params.force_size = target_width+"x?" + } else { + let size = params.force_size.split("x") + let ar = parseInt(size[0])/parseInt(size[1]) + params.force_size = target_width+"x"+(target_width/ar) + } + + previewWidget.audioEl.src = api.apiURL('/view?' + new URLSearchParams(params)); + + previewWidget.audioEl.hidden = false; + previewWidget.parentEl.appendChild(previewWidget.audioEl) +} + +function audioUpload(node, inputName, inputData, app) { + const audioWidget = node.widgets.find((w) => w.name === "audio"); + let uploadWidget; + /* + A method that returns the required style for the html + */ + var default_value = audioWidget.value; + Object.defineProperty(audioWidget, "value", { + set : function(value) { + this._real_value = value; + }, + + get : function() { + let value = ""; + if (this._real_value) { + value = this._real_value; + } else { + return default_value; + } + + if (value.filename) { + let real_value = value; + value = ""; + if (real_value.subfolder) { + value = real_value.subfolder + "/"; + } + + value += real_value.filename; + + if(real_value.type && real_value.type !== "input") + value += ` [${real_value.type}]`; + } + return value; + } + }); + async function uploadFile(file, updateNode, pasted = false) { + try { + // Wrap file in formdata so it includes filename + const body = new FormData(); + body.append("image", file); + if (pasted) body.append("subfolder", "pasted"); + const resp = await api.fetchApi("/upload/image", { + method: "POST", + body, + }); + + if (resp.status === 200) { + const data = await resp.json(); + // Add the file to the dropdown list and update the widget value + let path = data.name; + if (data.subfolder) path = data.subfolder + "/" + path; + + if (!audioWidget.options.values.includes(path)) { + audioWidget.options.values.push(path); + } + + if (updateNode) { + audioWidget.value = path; + previewAudio(node,path) + + } + } else { + alert(resp.status + " - " + resp.statusText); + } + } catch (error) { + alert(error); + } + } + + const fileInput = document.createElement("input"); + Object.assign(fileInput, { + type: "file", + accept: "audio/mp3,audio/wav,audio/flac,audio/m4a", + style: "display: none", + onchange: async () => { + if (fileInput.files.length) { + await uploadFile(fileInput.files[0], true); + } + }, + }); + document.body.append(fileInput); + + // Create the button widget for selecting the files + uploadWidget = node.addWidget("button", "choose audio file to upload", "Audio", () => { + fileInput.click(); + }); + + uploadWidget.serialize = false; + + previewAudio(node, audioWidget.value); + const cb = node.callback; + audioWidget.callback = function () { + previewAudio(node,audioWidget.value); + if (cb) { + return cb.apply(this, arguments); + } + }; + + return { widget: uploadWidget }; +} + +ComfyWidgets.AUDIOPLOAD = audioUpload; + +app.registerExtension({ + name: "UVR5.UploadAudio", + async beforeRegisterNodeDef(nodeType, nodeData, app) { + if (nodeData?.name == "LoadAudio") { + nodeData.input.required.upload = ["AUDIOPLOAD"]; + } + }, +}); +