change ui
This commit is contained in:
+3
-3
@@ -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"
|
||||
}
|
||||
|
||||
Binary file not shown.
Binary file not shown.
@@ -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"
|
||||
})
|
||||
},
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
@@ -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
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -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"];
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
@@ -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]);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -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"];
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user