streamlined node
This commit is contained in:
+349
-132
@@ -23,6 +23,7 @@ import py3langid
|
||||
import folder_paths
|
||||
import logging
|
||||
import re
|
||||
from tqdm import tqdm
|
||||
|
||||
# logfile_dir = os.path.join(folder_paths.base_path, "comfyui.log")
|
||||
device_list = get_device_list()
|
||||
@@ -65,10 +66,48 @@ def obtain_language(text: str, supported_languages=None):
|
||||
return (language, supported)
|
||||
|
||||
|
||||
# Slice text into fragments, input text and maximum length, return fragment list and combined fragment list.
|
||||
# 切分文本为片段,输入文本和最大长度,返回片段列表和组合的片段列表
|
||||
def slice_combination(text, length=100, language=None):
|
||||
character_ratio = {"en": 1, "zh": 0.6,
|
||||
"ja": 1.3, "fr": 1.5, "ko": 1, "de": 1.75}
|
||||
if language is None:
|
||||
language = py3langid.classify(text)[0]
|
||||
if language not in character_ratio.keys():
|
||||
language = "en"
|
||||
logging.warning(
|
||||
"Warning-MaskGCT-Slice_Combination: language not supported, use default language 'en' instead")
|
||||
length = int(length * character_ratio[language])
|
||||
if len(text) <= length:
|
||||
return ([text], [text])
|
||||
else:
|
||||
pattern = r'(?<=[,,。.!!\n])'
|
||||
fragments = re.split(pattern, text)
|
||||
# remove empty fragments
|
||||
fragments = [frag for frag in fragments if frag]
|
||||
n = len((fragments))
|
||||
if n == 1:
|
||||
return ([text], [text])
|
||||
elif n == 2:
|
||||
return (fragments, fragments)
|
||||
else:
|
||||
result = []
|
||||
combined = ""
|
||||
for i in range(n):
|
||||
combined += fragments[i]
|
||||
if len(combined) >= length:
|
||||
result.append(combined)
|
||||
combined = ""
|
||||
elif i == n - 1:
|
||||
result.append(combined)
|
||||
return (fragments, result)
|
||||
|
||||
|
||||
CATEGORY_NAME = "MaskGCT/MaskGCT_Load"
|
||||
|
||||
|
||||
class load_maskgct_model:
|
||||
DESCRIPTION = """Load or automatically download maskgct_model"""
|
||||
@classmethod
|
||||
def INPUT_TYPES(s):
|
||||
return {
|
||||
@@ -141,6 +180,7 @@ class load_maskgct_model:
|
||||
|
||||
|
||||
class load_w2vbert_model:
|
||||
DESCRIPTION = """Load or automatically download w2vbert_model"""
|
||||
@classmethod
|
||||
def INPUT_TYPES(s):
|
||||
return {
|
||||
@@ -165,28 +205,44 @@ class load_w2vbert_model:
|
||||
|
||||
|
||||
class maskgct_pipeline:
|
||||
DESCRIPTION = """
|
||||
MaskGCT Preprocessing Pipeline
|
||||
Parameters:
|
||||
-maskgct_model: MaskGCT model.
|
||||
-w2vbert_model: W2V-BERT model.
|
||||
-sample_audio: Audio sample.
|
||||
-sample_prompt_text: Text corresponding to the audio sample.
|
||||
-sample_language: Language of the audio sample.
|
||||
-device: Device to run on.
|
||||
"""
|
||||
@classmethod
|
||||
def INPUT_TYPES(s):
|
||||
return {
|
||||
"required": {
|
||||
"maskgct_model": ("maskgct_model",),
|
||||
"w2vbert_model": ("w2vbert_model",),
|
||||
"sample_audio": ("AUDIO",),
|
||||
"sample_prompt_text": ("STRING", {"default": "", }),
|
||||
"sample_language": (["Auto", "en", "zh", "ja", "fr", "ko", "de",], {"default": "Auto"}),
|
||||
"device": (list(device_list[0].keys()), {"default": str(device_list[1])}),
|
||||
},
|
||||
"optional": {
|
||||
}
|
||||
}
|
||||
CATEGORY = CATEGORY_NAME
|
||||
RETURN_TYPES = ("maskgct_pipeline",)
|
||||
RETURN_NAMES = ("maskgct_pipeline",)
|
||||
FUNCTION = "pipeline"
|
||||
FUNCTION = "mc_pipeline"
|
||||
|
||||
def pipeline(self, maskgct_model, w2vbert_model, device):
|
||||
def mc_pipeline(self, maskgct_model, w2vbert_model,
|
||||
sample_audio, sample_prompt_text, sample_language,
|
||||
device):
|
||||
# Unified equipment 统一设备
|
||||
device = device_list[0][device]
|
||||
for i in maskgct_model:
|
||||
i.to(device)
|
||||
for i in w2vbert_model:
|
||||
i.to(device)
|
||||
|
||||
# MaskGCT_Pipeline
|
||||
pipeline = MaskGCT_Inference_Pipeline(
|
||||
w2vbert_model[0],
|
||||
*maskgct_model,
|
||||
@@ -194,27 +250,10 @@ class maskgct_pipeline:
|
||||
w2vbert_model[2],
|
||||
device,
|
||||
)
|
||||
return (pipeline,)
|
||||
|
||||
|
||||
class sample_audio:
|
||||
@classmethod
|
||||
def INPUT_TYPES(s):
|
||||
return {
|
||||
"required": {
|
||||
"audio": ("AUDIO",),
|
||||
"prompt_text": ("STRING", {"default": "", }),
|
||||
"language": (["Auto", "en", "zh", "ja", "fr", "ko", "de",], {"default": "Auto"}),
|
||||
},
|
||||
}
|
||||
CATEGORY = CATEGORY_NAME
|
||||
RETURN_TYPES = ("audio_pipe",)
|
||||
RETURN_NAMES = ("sample_audio",)
|
||||
FUNCTION = "audio_sample"
|
||||
|
||||
def audio_sample(self, audio, prompt_text, language):
|
||||
sample_rate = audio["sample_rate"]
|
||||
waveform = audio_to_numpy(audio)
|
||||
# Audio sample preprocessing 音频样本预处理
|
||||
sample_rate = sample_audio["sample_rate"]
|
||||
waveform = audio_to_numpy(sample_audio)
|
||||
if sample_rate != 16000:
|
||||
waveform_16k = librosa.resample(
|
||||
waveform, orig_sr=sample_rate, target_sr=16000)
|
||||
@@ -226,33 +265,43 @@ class sample_audio:
|
||||
else:
|
||||
waveform_24k = waveform
|
||||
|
||||
if language == "Auto":
|
||||
language = py3langid.classify(prompt_text)[0]
|
||||
if not language in ["en", "zh", "ja", "fr", "ko", "de"]:
|
||||
if sample_language == "Auto":
|
||||
sample_language = py3langid.classify(sample_prompt_text)[0]
|
||||
if not sample_language in ["en", "zh", "ja", "fr", "ko", "de"]:
|
||||
logging.error(
|
||||
"Error-MaskGCT-Sample_Audio: language not supported!", exc_info=True)
|
||||
return ([waveform_16k, waveform_24k, prompt_text, language],)
|
||||
return ([pipeline, [waveform_16k, waveform_24k, sample_prompt_text, sample_language]],)
|
||||
|
||||
|
||||
class from_path_load_audio:
|
||||
DESCRIPTION = """
|
||||
Load audio files from a specified path.
|
||||
Output:
|
||||
-Audio: Audio data.
|
||||
-Time(s): Time in seconds.
|
||||
-Sample: Sampling rate in Hertz.
|
||||
-Channel: Number of channels.
|
||||
"""
|
||||
@classmethod
|
||||
def INPUT_TYPES(s):
|
||||
path=os.path.join(folder_paths.base_path, "custom_nodes/ComfyUI_MaskGCT/sample/trump_0.wav")
|
||||
path = os.path.join(folder_paths.base_path,
|
||||
"custom_nodes/ComfyUI_MaskGCT/sample/trump_0.wav")
|
||||
return {
|
||||
"required": {
|
||||
"Audio_FilePath": ("STRING",{"default": path}),
|
||||
"Audio_FilePath": ("STRING", {"default": path}),
|
||||
},
|
||||
}
|
||||
CATEGORY = CATEGORY_NAME
|
||||
RETURN_TYPES = ("AUDIO", "INT", "FLOAT")
|
||||
RETURN_NAMES = ("Audio", "Sample", "Time(s)")
|
||||
RETURN_NAMES = ("Audio", "Sample", "Time(s)","channel")
|
||||
FUNCTION = "load_audio_v2"
|
||||
|
||||
def load_audio_v2(self, Audio_FilePath):
|
||||
audio_tensor, sr = torchaudio.load(Audio_FilePath)
|
||||
time = audio_tensor.shape[-1]/sr
|
||||
audio_tensor = audio_tensor.unsqueeze(0)
|
||||
return ({"waveform": audio_tensor, "sample_rate": sr}, sr, time,)
|
||||
channel = audio_tensor.shape[1]
|
||||
return ({"waveform": audio_tensor, "sample_rate": sr}, sr, time,channel)
|
||||
|
||||
|
||||
CATEGORY_NAME = "MaskGCT"
|
||||
@@ -260,133 +309,189 @@ CATEGORY_NAME = "MaskGCT"
|
||||
|
||||
class maskgct_run_v2:
|
||||
DESCRIPTION = """
|
||||
When target_len is 0, the length is automatically calculated.
|
||||
Note: If target_len exceeds the length corresponding to normal speaking speed,
|
||||
the first half of the audio may become garbled.
|
||||
|
||||
target_len长度为 0 时,自动计算长度。
|
||||
注意:target_len超过正常语速时的长度时,音频前半部分会出现乱码。
|
||||
Run MaskGCT to generate speech.
|
||||
Parameters:
|
||||
-maskgct_pipeline: MaskGCT preprocessing pipeline.
|
||||
-target_text: Text for generating speech, with no length limit.
|
||||
-language: The language for generating speech should match the text language (supports 6 languages),
|
||||
it is recommended to use Auto for automatic recognition.
|
||||
-settings: Optional MaskGCT parameter settings.
|
||||
Output:
|
||||
-Audio: Generated speech.
|
||||
-Audio_List: List of audio after long text cutting.
|
||||
-Batch_Text: List of text after long text cutting.
|
||||
说明: 基于MaskGCT模型的语音生成。
|
||||
输入:
|
||||
运行MaskGCT生成语音.
|
||||
参数:
|
||||
-maskgct_pipeline: MaskGCT预处理管线。
|
||||
-target_text: 用于生成语音的文本,没有长度限制。
|
||||
-language: 生成语音的语言应与文本语言对应(支持6种语言),建议使用Auto自动识别。
|
||||
-settings: 可选MaskGCT参数设置。
|
||||
输出:
|
||||
-Audio: 生成的语音。
|
||||
-Audio_List: 长文本切割后的音频列表。
|
||||
-Batch_Text: 长文本切割后的文本列表。
|
||||
"""
|
||||
|
||||
@classmethod
|
||||
def INPUT_TYPES(s):
|
||||
wav_path = os.path.join(OBJECT_DIR, "sample/trump_0.wav")
|
||||
return {
|
||||
"required": {
|
||||
"maskgct_pipeline": ("maskgct_pipeline",),
|
||||
"sample_audio": ("audio_pipe",),
|
||||
"target_text": ("STRING", {"default": "We do not break. We never give in. We never back down.",
|
||||
"multiline": True}),
|
||||
"language": (["Auto", "en", "zh", "ja", "fr", "ko", "de",], {"default": "Auto"}),
|
||||
"target_len": ("INT", {"default": 0, "min": 0, "max": 99999}),
|
||||
},
|
||||
"optional": {
|
||||
"maskgct_setting": ("maskgct_setting",),
|
||||
"w2vbert_setting": ("w2vbert_setting",)
|
||||
"required": {
|
||||
"maskgct_pipeline": ("maskgct_pipeline",),
|
||||
"target_text": ("STRING", {"default": "We do not break. We never give in. We never back down.",
|
||||
"multiline": True}),
|
||||
"language": (["Auto", "en", "zh", "ja", "fr", "ko", "de",], {"default": "Auto"}),
|
||||
},
|
||||
"optional": {
|
||||
"setting": ("maskgct_setting",),
|
||||
}
|
||||
}
|
||||
}
|
||||
CATEGORY = CATEGORY_NAME
|
||||
RETURN_TYPES = ("AUDIO",)
|
||||
RETURN_NAMES = ("AUDIO",)
|
||||
RETURN_TYPES = ("AUDIO", "AUDIO", "STRING")
|
||||
RETURN_NAMES = ("Audio", "Audio_List", "Batch_Text")
|
||||
FUNCTION = "maskgct_semantic"
|
||||
|
||||
def maskgct_semantic(self,
|
||||
maskgct_pipeline,
|
||||
sample_audio,
|
||||
target_text,
|
||||
language,
|
||||
target_len,
|
||||
maskgct_setting=None,
|
||||
w2vbert_setting=None,
|
||||
setting=None,
|
||||
):
|
||||
# speech_16k = librosa.load(sample_wav_path, sr=16000)[0]
|
||||
if target_len == 0:
|
||||
target_len = None
|
||||
|
||||
# Set default values 设置默认值
|
||||
if setting == None:
|
||||
ms = [25, 2.5, 0.75, [25, 10, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], 2.5, 0.75]
|
||||
target_len, text_slice_length, pause_time = [0, 120, 0.7]
|
||||
else:
|
||||
ms = setting[0]
|
||||
target_len, text_slice_length, pause_time = setting[1]
|
||||
if language == "Auto":
|
||||
language = py3langid.classify(target_text)[0]
|
||||
if not language in ["en", "zh", "ja", "fr", "ko", "de"]:
|
||||
logging.error(
|
||||
"Error-Maskgct_run_v2: language not supported!", exc_info=True)
|
||||
if maskgct_setting == None:
|
||||
maskgct_setting = [25, 2.5, 0.75]
|
||||
if w2vbert_setting == None:
|
||||
w2vbert_setting = [
|
||||
[25, 10, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], 2.5, 0.75]
|
||||
|
||||
recovered_audio = maskgct_pipeline.maskgct_inference(
|
||||
[sample_audio[0], sample_audio[1]],
|
||||
sample_audio[2],
|
||||
target_text,
|
||||
sample_audio[3],
|
||||
language,
|
||||
target_len,
|
||||
*maskgct_setting,
|
||||
*w2vbert_setting
|
||||
)
|
||||
recovered_audio = torch.Tensor(recovered_audio, device="cpu")
|
||||
recovered_audio = recovered_audio.repeat(1, 1, 1)
|
||||
return ({"waveform": recovered_audio, "sample_rate": 24000},)
|
||||
# Split Text 切分文本
|
||||
if isinstance(target_text, list):
|
||||
text_list = target_text
|
||||
else:
|
||||
_, text_list = slice_combination(
|
||||
target_text, text_slice_length, language)
|
||||
if target_len == 0 or len(text_list) > 1:
|
||||
target_len = None
|
||||
|
||||
# Generate fragmented audio from segmented text 通过切分的文本生成片段音频
|
||||
audio_list = []
|
||||
audio_data_list = []
|
||||
sample_audio = maskgct_pipeline[1]
|
||||
for i in tqdm(text_list):
|
||||
recovered_audio = maskgct_pipeline[0].maskgct_inference(
|
||||
[sample_audio[0], sample_audio[1]],
|
||||
sample_audio[2],
|
||||
i,
|
||||
sample_audio[3],
|
||||
language,
|
||||
target_len,
|
||||
*ms
|
||||
)
|
||||
recovered_audio = torch.Tensor(recovered_audio, device="cpu")
|
||||
recovered_audio = recovered_audio.repeat(1, 1, 1)
|
||||
audio_data_list.append(recovered_audio)
|
||||
|
||||
# Prepare for output 准备输出
|
||||
if len(audio_data_list) == 1:
|
||||
merged_audio = {
|
||||
"waveform": audio_data_list[0], "sample_rate": 24000}
|
||||
audio_list = recovered_audio
|
||||
else:
|
||||
pause_time = 0.5
|
||||
pause_audio = torch.zeros(1, 1, int(pause_time*24000), device="cpu")
|
||||
audio_data_list_pause = []
|
||||
for i in range(len(audio_data_list)):
|
||||
# Composite audio list 合成音频列表
|
||||
audio_list.append(
|
||||
{"waveform": audio_data_list[i], "sample_rate": 24000})
|
||||
# Synthesize audio data list and add pauses 合成音频数据列表,并增加停顿
|
||||
audio_data_list_pause.append(audio_data_list[i])
|
||||
if text_list[i][-1] in [".", "!", "?", "。", "!", "?", "\n"]:
|
||||
audio_data_list_pause.append(pause_audio)
|
||||
merged_audio = {"waveform": torch.cat(
|
||||
audio_data_list_pause, dim=2), "sample_rate": 24000}
|
||||
return (merged_audio, audio_list, text_list)
|
||||
|
||||
|
||||
class maskgct_setting:
|
||||
@classmethod
|
||||
def INPUT_TYPES(s):
|
||||
return {
|
||||
"required": {
|
||||
"n_time_steps": ("INT", {"default": 25, "min": 1, "max": 99999}),
|
||||
"cfg": ("FLOAT", {"default": 2.5, "min": 0.01, "max": 50.0, "step": 0.01}),
|
||||
"rescale_cfg": ("FLOAT", {"default": 0.75, "min": 0.01, "max": 10.0, "step": 0.01}),
|
||||
},
|
||||
}
|
||||
CATEGORY = CATEGORY_NAME
|
||||
RETURN_TYPES = ("maskgct_setting",)
|
||||
RETURN_NAMES = ("maskgct_setting",)
|
||||
FUNCTION = "setting"
|
||||
|
||||
def setting(self, n_time_steps, cfg, rescale_cfg):
|
||||
return ([n_time_steps, cfg, rescale_cfg],)
|
||||
|
||||
|
||||
class w2vbert_setting:
|
||||
DESCRIPTION = """
|
||||
explain:
|
||||
n_time_steps is a list that contains 12 elements,
|
||||
corresponding to the n_time_steps at 12 different positions.
|
||||
You do not need to input it; by default, it uses [25, 10, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1].
|
||||
If you wish to make fine adjustments,
|
||||
You can also use my ComfyUI-3D-MeshTool plugin to generate the n_time_steps for w2vbert.
|
||||
说明:
|
||||
n_time_steps是一个列表,包含12个元素,分别对应12个位置的n_time_steps。
|
||||
可以不输入,默认使用[25, 10, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]
|
||||
如果你想精细的调整,可以使用 ComfyUI-3D-MeshTool 插件来生成w2vbert的n_time_steps后输入
|
||||
"""
|
||||
1. Model Parameters:
|
||||
maskgct_* prefixed parameters correspond to the configuration of the MaskGCT model.
|
||||
w2vbert_* prefixed parameters correspond to the configuration of the W2VBert model.
|
||||
w2vbert_time_steps is an optional parameter that is a list containing 12 elements,
|
||||
each corresponding to the n_time_steps at 12 different positions.
|
||||
It can be left blank, and will default to [25, 10, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1].
|
||||
If you want to fine-tune, you can use the ComfyUI-3D-MeshTool plugin to integrate w2vbert_time_steps.
|
||||
2. Text Parameters:
|
||||
target_len:The length of each output audio segment, 0 indicates automatic calculation.
|
||||
This parameter is invalid when slicing is in effect.
|
||||
text_slice_length: The length of the text slice, default is 120 English characters,
|
||||
the slice ratio will be automatically adjusted for different languages.
|
||||
pause_time: The pause time between each sentence (period, exclamation mark, question mark),
|
||||
default is 0.5 seconds.
|
||||
|
||||
说明:
|
||||
1.模型参数:
|
||||
maskgct_* 前缀的参数对应MaskGCT模型的配置。
|
||||
w2vbert_* 前缀的参数对应W2VBert模型的配置。
|
||||
w2vbert_time_steps可选参数是一个列表,包含12个元素,分别对应12个位置的n_time_steps
|
||||
可不输入,将默认使用[25, 10, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]
|
||||
如果你想精细的调整,可以使用 ComfyUI-3D-MeshTool 插件来接入w2vbert_time_steps
|
||||
2.文本参数:
|
||||
target_len: 输出的每段音频长度,0表示自动计算。文本切片生效时该参数无效。
|
||||
text_slice_length: 文本切片的长度,默认120英文字符,0不切片,不同语言会自动调整切片比例。
|
||||
pause_time: 每句话(以句号、感叹号、问号区分)之间的停顿时间,默认0.5秒。
|
||||
"""
|
||||
@classmethod
|
||||
def INPUT_TYPES(s):
|
||||
return {
|
||||
"required": {
|
||||
"cfg": ("FLOAT", {"default": 2.5, "min": 0.01, "max": 50.0, "step": 0.01}),
|
||||
"rescale_cfg": ("FLOAT", {"default": 0.75, "min": 0.01, "max": 10.0, "step": 0.01}),
|
||||
},
|
||||
"optional": {
|
||||
"n_time_steps": ("LIST",),
|
||||
"required": {
|
||||
"maskgct_time_steps": ("INT", {"default": 25, "min": 1, "max": 99999}),
|
||||
"maskgct_cfg": ("FLOAT", {"default": 2.5, "min": 0.01, "max": 50.0, "step": 0.01}),
|
||||
"maskgct_rescale_cfg": ("FLOAT", {"default": 0.75, "min": 0.01, "max": 10.0, "step": 0.01}),
|
||||
"w2vbert_cfg": ("FLOAT", {"default": 2.5, "min": 0.01, "max": 50.0, "step": 0.01}),
|
||||
"w2vbert_rescale_cfg": ("FLOAT", {"default": 0.75, "min": 0.01, "max": 10.0, "step": 0.01}),
|
||||
"target_len": ("INT", {"default": 0, "min": 0, "max": 99999}),
|
||||
"text_slice_length": ("INT", {"default": 120, "min": 0, "max": 8192, "step": 1}),
|
||||
"pause_time": ("FLOAT", {"default": 0.7, "min": 0.0, "max": 4.0, "step": 0.01, "display": "slider"})
|
||||
},
|
||||
"optional": {
|
||||
"w2vbert_time_steps": ("LIST",),
|
||||
}
|
||||
}
|
||||
}
|
||||
CATEGORY = CATEGORY_NAME
|
||||
RETURN_TYPES = ("w2vbert_setting",)
|
||||
RETURN_NAMES = ("w2vbert_setting",)
|
||||
RETURN_TYPES = ("maskgct_setting",)
|
||||
RETURN_NAMES = ("setting",)
|
||||
FUNCTION = "setting"
|
||||
|
||||
def setting(self, cfg, rescale_cfg, n_time_steps=None):
|
||||
if n_time_steps == None:
|
||||
n_time_steps = [25, 10, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]
|
||||
return ([n_time_steps, cfg, rescale_cfg],)
|
||||
def setting(self, maskgct_time_steps, maskgct_cfg, maskgct_rescale_cfg, w2vbert_cfg, w2vbert_rescale_cfg,
|
||||
target_len, text_slice_length, pause_time,
|
||||
w2vbert_time_steps=None):
|
||||
if w2vbert_time_steps == None:
|
||||
w2vbert_time_steps = [25, 10, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]
|
||||
return ([
|
||||
[maskgct_time_steps, maskgct_cfg, maskgct_rescale_cfg, w2vbert_time_steps, w2vbert_cfg, w2vbert_rescale_cfg],
|
||||
[target_len, text_slice_length, pause_time]
|
||||
],)
|
||||
|
||||
|
||||
CATEGORY_NAME = "MaskGCT/Convert_txt"
|
||||
|
||||
|
||||
class whisper_large_v3:
|
||||
DESCRIPTION = """
|
||||
Simple use of the whisper-large-v3-turbo model for speech-to-text.
|
||||
简单的使用whisper-large-v3-turbo模型进行语音文字。
|
||||
"""
|
||||
@classmethod
|
||||
def INPUT_TYPES(s):
|
||||
return {
|
||||
@@ -430,6 +535,17 @@ CATEGORY_NAME = "MaskGCT/Audio_Edit"
|
||||
|
||||
|
||||
class audio_resample:
|
||||
DESCRIPTION = """
|
||||
Adjust the audio sampling rate, whether to resample
|
||||
(not resampling can adjust the audio speed, but the frequency will change).
|
||||
Parameters:
|
||||
-sample: Target sampling rate, range 1 - 4MHz
|
||||
-resample: Whether to resample.
|
||||
调整音频采样率,是否重采样(不重采样可调整音频速度,但是频率会变)。
|
||||
参数:
|
||||
-sample: 目标采样率,范围 1 - 4MHz
|
||||
-resample: 是否重采样。
|
||||
"""
|
||||
@classmethod
|
||||
def INPUT_TYPES(s):
|
||||
return {
|
||||
@@ -446,7 +562,7 @@ class audio_resample:
|
||||
|
||||
def audio_sampling(self, audio, sample, re_sample):
|
||||
audio_sample = audio["sample_rate"]
|
||||
if audio_sample != sample :
|
||||
if audio_sample != sample:
|
||||
audio_data = audio["waveform"]
|
||||
if isinstance(audio_data, torch.Tensor) and re_sample:
|
||||
audio_data = res.resample(audio_data, audio_sample, sample)
|
||||
@@ -466,6 +582,16 @@ class audio_resample:
|
||||
|
||||
|
||||
class audio_capture_percentage:
|
||||
DESCRIPTION = """
|
||||
Trim audio from the beginning and end by percentage.
|
||||
Parameters:
|
||||
-start: Start percentage.
|
||||
-end: End percentage.\
|
||||
从开始和结尾按百分比截取音频。
|
||||
参数:
|
||||
-start: 开始百分比。
|
||||
-end: 结尾百分比。
|
||||
"""
|
||||
@classmethod
|
||||
def INPUT_TYPES(s):
|
||||
return {
|
||||
@@ -501,6 +627,23 @@ class audio_capture_percentage:
|
||||
|
||||
|
||||
class get_audio_data:
|
||||
DESCRIPTION = """
|
||||
Retrieve audio data.
|
||||
Output:
|
||||
-sample: Sampling rate.
|
||||
-Time(s): Audio duration in seconds.
|
||||
-channel: Number of channels.
|
||||
-batch_size: Number of audio batches.
|
||||
-Data_Length: Length of audio data.
|
||||
|
||||
获取音频数据。
|
||||
输出:
|
||||
-sample: 采样率。
|
||||
-Time(s): 音频时长(秒)。
|
||||
-channel: 通道数。
|
||||
-bath_size: 音频批次数量。
|
||||
-Data_Length: 音频数据长度。
|
||||
"""
|
||||
@classmethod
|
||||
def INPUT_TYPES(s):
|
||||
return {
|
||||
@@ -509,18 +652,38 @@ class get_audio_data:
|
||||
},
|
||||
}
|
||||
CATEGORY = CATEGORY_NAME
|
||||
RETURN_TYPES = ("INT", "FLOAT", "INT")
|
||||
RETURN_NAMES = ("sample", "Time(s)", "Length(samples)")
|
||||
RETURN_TYPES = ("INT", "INT", "INT", "FLOAT", "INT")
|
||||
RETURN_NAMES = ("sample", "channel", "bath_size", "Time(s)", "Data_Length")
|
||||
FUNCTION = "audio_get_data"
|
||||
|
||||
def audio_get_data(self, audio):
|
||||
audio_data = audio["waveform"]
|
||||
channel = audio_data.shape[1]
|
||||
bath_size = audio_data.shape[0]
|
||||
n = audio_data.shape[-1]
|
||||
time = n/audio["sample_rate"]
|
||||
return (audio["sample_rate"], time, n)
|
||||
return (audio["sample_rate"],channel, bath_size,time, n)
|
||||
|
||||
|
||||
class get_text_data:
|
||||
DESCRIPTION = """
|
||||
Retrieve language text data.
|
||||
Output:
|
||||
-language: Detected language.
|
||||
-character_length: Character length.
|
||||
-words: Array of segmented word strings.
|
||||
-words_number: Number of words.
|
||||
-symbols: Array of segmented symbol strings.
|
||||
-symbols_number: Number of symbols.
|
||||
获取语言文本数据。
|
||||
输出:
|
||||
-language: 识别出的语言。
|
||||
-character_length: 字符长度。
|
||||
-words: 分割的单词字符串数组。
|
||||
-words_number: 单词数量。
|
||||
-symbols: 分割的符号字符串数组。
|
||||
-symbols_number: 符号数量。
|
||||
"""
|
||||
@classmethod
|
||||
def INPUT_TYPES(s):
|
||||
return {
|
||||
@@ -529,22 +692,32 @@ class get_text_data:
|
||||
},
|
||||
}
|
||||
CATEGORY = CATEGORY_NAME
|
||||
RETURN_TYPES = ("STRING", "STRING", "INT", "STRING", "INT",)
|
||||
RETURN_NAMES = ("language", "words", "word_number",
|
||||
RETURN_TYPES = ("STRING", "INT", "STRING", "INT", "STRING", "INT",)
|
||||
RETURN_NAMES = ("language", "character_length", "words", "word_number",
|
||||
"symbols", "symbol_number")
|
||||
FUNCTION = "get_language"
|
||||
|
||||
def get_language(self, text):
|
||||
language, _ = obtain_language(text)
|
||||
characters_length = len(text)
|
||||
words = re.findall(r'\b\w+\b', text)
|
||||
word_number = len(words)
|
||||
symbols = re.findall(r'[^\w\s]', text)
|
||||
symbol_number = len(symbols)
|
||||
return (language, words, word_number, symbols, symbol_number)
|
||||
return (language, len(text), words, word_number, symbols, symbol_number)
|
||||
|
||||
|
||||
class remove_blank_space:
|
||||
DESCRIPTION = """
|
||||
Remove silent audio data (for speech-to-text preprocessing).
|
||||
Parameters:
|
||||
-threshold: Audio data with absolute values below this threshold will be removed.
|
||||
-time_length: Limit the processing length to save time.
|
||||
|
||||
去除静音音频数据(用于音频转文字预处理)。\
|
||||
参数: \
|
||||
-threshold: 低于该阈值的绝对值音频数据都将被移除\
|
||||
-time_length: 限制处理长度,可节省时间。\
|
||||
"""
|
||||
@classmethod
|
||||
def INPUT_TYPES(s):
|
||||
return {
|
||||
@@ -624,17 +797,61 @@ class remove_blank_space:
|
||||
return new_audio
|
||||
|
||||
|
||||
class multilingual_slice:
|
||||
DESCRIPTION = """
|
||||
Divide natural language text into multiple segments according to commas/periods, etc.,
|
||||
and merge each segment to a specified length (built-in feature of MaskGCT Run V2).
|
||||
Parameters:
|
||||
-language: Language selection (the reading speed varies for the same paragraph in different languages,
|
||||
and the cutting length will be automatically adjusted through language detection),
|
||||
choose Auto for automatic language recognition.
|
||||
-text_slice_length: Maximum character length, the length of the combined segments will not exceed this value.
|
||||
Output:
|
||||
-sentence: Short sentences divided by punctuation marks (text list).
|
||||
-sentence_number: The number of divided sentences.
|
||||
-length_sentence: Short sentences recombined to the specified length after division (text list).
|
||||
-length_sentence_n: The number of sentences recombined to the specified length after division.
|
||||
|
||||
将自然语言文本按照逗号/句号等分成多个片段,并将每个片段合并到指定长度(MaskGCT Run V2内置功能)。
|
||||
参数:
|
||||
-language: 语言选择(同一段话不同语言的阅读速度不同,将通过语言自动调整切割长度), 选Auto自动识别语言。
|
||||
-text_slice_length: 最大字符长度,片段组合的长度不会超过此值。
|
||||
输出:
|
||||
-sentence: 按标点符号分割后的短句(文本列表)。
|
||||
-sentence_number: 分割后的句子数量。
|
||||
-length_sentence: 分割后重新组合到指定长度后的短句(文本列表)。
|
||||
-length_sentence_n: 分割后重新组合到指定长度后的句子数量。
|
||||
"""
|
||||
@classmethod
|
||||
def INPUT_TYPES(s):
|
||||
return {
|
||||
"required": {
|
||||
"text": ("STRING", {"default": "We do not break. We never give in. We never back down."}),
|
||||
"language": (["Auto", "en", "zh", "ja", "fr", "ko", "de",], {"default": "Auto"}),
|
||||
"text_slice_length": ("INT", {"default": 100, "min": 0, "max": 8192}),
|
||||
},
|
||||
}
|
||||
CATEGORY = CATEGORY_NAME
|
||||
RETURN_TYPES = ("STRING","INT","STRING","INT")
|
||||
RETURN_NAMES = ("sentence","sentence_number","length_sentence","length_sentence_n",)
|
||||
FUNCTION = "multilingual_slice"
|
||||
|
||||
def multilingual_slice(self, text, language, text_slice_length):
|
||||
if language == "Auto":
|
||||
language = None
|
||||
sentence, length_sentence = slice_combination(text, text_slice_length, language)
|
||||
return (sentence,len(sentence),length_sentence,len(text))
|
||||
|
||||
|
||||
NODE_CLASS_MAPPINGS = {
|
||||
# MaskGCT/MaskGCT_Load
|
||||
"load_maskgct_model": load_maskgct_model,
|
||||
"load_w2vbert_model": load_w2vbert_model,
|
||||
"maskgct_pipeline": maskgct_pipeline,
|
||||
"sample_audio": sample_audio,
|
||||
"from_path_load_audio": from_path_load_audio,
|
||||
# MaskGCT
|
||||
"maskgct_run_v2": maskgct_run_v2,
|
||||
"maskgct_setting": maskgct_setting,
|
||||
"w2vbert_setting": w2vbert_setting,
|
||||
# MaskGCT/Convert_txt
|
||||
"whisper_large_v3": whisper_large_v3,
|
||||
# MaskGCT/Audio_Edit
|
||||
@@ -643,6 +860,7 @@ NODE_CLASS_MAPPINGS = {
|
||||
"get_audio_data": get_audio_data,
|
||||
"get_text_data": get_text_data,
|
||||
"remove_blank_space": remove_blank_space,
|
||||
"multilingual_slice": multilingual_slice,
|
||||
}
|
||||
|
||||
NODE_DISPLAY_NAME_MAPPINGS = {
|
||||
@@ -650,12 +868,10 @@ NODE_DISPLAY_NAME_MAPPINGS = {
|
||||
"load_maskgct_model": "Load MaskGCT Model",
|
||||
"load_w2vbert_model": "Load W2VBert Model",
|
||||
"maskgct_pipeline": "MaskGCT Pipeline",
|
||||
"sample_audio": "Sample Audio",
|
||||
"from_path_load_audio": "Load Audio from Path",
|
||||
# MaskGCT
|
||||
"maskgct_run_v2": "MaskGCT Run V2",
|
||||
"maskgct_setting": "MaskGCT Setting",
|
||||
"w2vbert_setting": "W2VBert Setting",
|
||||
# MaskGCT/Convert_txt
|
||||
"whisper_large_v3": "Speech Recognition-whisper_large_v3",
|
||||
# MaskGCT/Audio_Edit
|
||||
@@ -664,4 +880,5 @@ NODE_DISPLAY_NAME_MAPPINGS = {
|
||||
"get_audio_data": "Get Audio Data",
|
||||
"get_text_data": "Get Text Data",
|
||||
"remove_blank_space": "Remove Blank Space",
|
||||
"multilingual_slice": "Multilingual Slice",
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user