Files
kale4eat-ComfyUI-speech-dat…/basic_nodes.py
T
2024-04-01 10:38:35 +09:00

269 lines
7.2 KiB
Python

import glob
import os
import random
import torchaudio
import folder_paths
from . import folder_util
from .node_def import BASE_NODE_CATEGORY, AudioData
_AUDIO_FILE_FORMAT = [
"wav",
"mp3",
"flac",
"vorbis",
"sph",
"amb",
"amr-nb",
"gsm",
]
_AUDIO_FILE_EXT = [
".wav",
".mp3",
".flac",
".ogg",
".sph",
".amb",
"amr",
".gsm",
]
_AUDIO_FILE_FORMAT_EXT_MAP = {
"wav": ".wav",
"mp3": ".mp3",
"flac": ".flac",
"vorbis": ".ogg",
"sph": ".sph",
"amb": ".amb",
"amr-nb": ".amr",
"gsm": ".gsm",
}
def _is_audio_file(f):
return any(ext for ext in _AUDIO_FILE_EXT if f.lower().endswith(ext))
class LoadAudio:
@classmethod
def INPUT_TYPES(cls):
input_dir = folder_util.get_audio_input_directory()
files = [
f
for f in os.listdir(input_dir)
if os.path.isfile(os.path.join(input_dir, f))
]
audio_files = [f for f in files if _is_audio_file(f)]
return {
"required": {"file_name": (sorted(audio_files), {"audio_upload": True})},
}
CATEGORY = BASE_NODE_CATEGORY
RETURN_TYPES = ("AUDIO", "STRING")
RETURN_NAMES = ("audio", "file_name")
FUNCTION = "load_audio"
def load_audio(self, file_name):
file = os.path.join(folder_util.get_audio_input_directory(), file_name)
waveform, sample_rate = torchaudio.load(file)
return (AudioData(waveform, sample_rate), file_name)
class LoadAudios:
@classmethod
def INPUT_TYPES(cls):
input_dir = folder_util.get_audio_input_directory()
dirs = [
os.path.relpath(d, input_dir)
for d in glob.glob(input_dir + "/**", recursive=True)
if os.path.isdir(d)
]
return {
"required": {"dir": (sorted(dirs), {"audio_upload": True})},
}
CATEGORY = BASE_NODE_CATEGORY
RETURN_TYPES = ("AUDIO", "STRING")
RETURN_NAMES = ("audios", "file_names")
OUTPUT_IS_LIST = (True, True)
FUNCTION = "load_audios"
def load_audios(self, dir):
abs_dir = os.path.join(folder_util.get_audio_input_directory(), dir)
abs_dir = os.path.abspath(abs_dir)
files = [
f for f in os.listdir(abs_dir) if os.path.isfile(os.path.join(abs_dir, f))
]
audio_files = [os.path.join(abs_dir, f) for f in files if _is_audio_file(f)]
items = [torchaudio.load(f) for f in audio_files]
return (
[AudioData(item[0], item[1]) for item in items],
[os.path.basename(f) for f in audio_files],
)
class SaveAudio:
def __init__(self):
self.output_dir = folder_util.get_audio_output_directory()
self.output_type = "output"
self.prefix_append = ""
@classmethod
def INPUT_TYPES(cls):
return {
"required": {
"audio": ("AUDIO",),
"file_name": ("STRING", {"default": "audio"}),
"file_format": (_AUDIO_FILE_FORMAT,),
"filename_prefix": ("STRING", {"default": "ComfyUI"}),
},
}
CATEGORY = BASE_NODE_CATEGORY
RETURN_TYPES = ()
FUNCTION = "save_audio"
OUTPUT_NODE = True
def save_audio(
self,
audio: AudioData,
file_name: str,
file_format: str,
filename_prefix: str,
):
if "." not in file_name:
file_name = file_name + _AUDIO_FILE_FORMAT_EXT_MAP[file_format]
subfolder = os.path.dirname(os.path.normpath(filename_prefix))
file_name = (
os.path.basename(os.path.normpath(filename_prefix)) + "_" + file_name
)
full_output_folder = os.path.join(self.output_dir, subfolder)
os.makedirs(full_output_folder, exist_ok=True)
file = os.path.join(self.output_dir, file_name)
torchaudio.save(file, audio.waveform, audio.sample_rate, format=file_format)
return {}
class SaveAudioWithSequentialNumbering:
@classmethod
def INPUT_TYPES(cls):
return {
"required": {
"audio": ("AUDIO",),
"file_format": (_AUDIO_FILE_FORMAT,),
"filename_prefix": ("STRING", {"default": "ComfyUI"}),
},
}
CATEGORY = BASE_NODE_CATEGORY
RETURN_TYPES = ()
FUNCTION = "save_audio"
OUTPUT_NODE = True
def save_audio(
self,
audio: AudioData,
file_format: str,
filename_prefix: str,
):
full_output_folder, filename, counter, _, _ = folder_paths.get_save_image_path(
filename_prefix, folder_util.get_audio_output_directory()
)
file = os.path.join(
full_output_folder,
f"{filename}_{counter:05}_{_AUDIO_FILE_FORMAT_EXT_MAP[file_format]}",
)
torchaudio.save(file, audio.waveform, audio.sample_rate, format=file_format)
return {}
class AudioProperty:
@classmethod
def INPUT_TYPES(cls):
return {
"required": {
"audio": ("AUDIO",),
},
}
CATEGORY = BASE_NODE_CATEGORY
RETURN_TYPES = ("INT",)
RETURN_NAMES = ("sampl_rate",)
FUNCTION = "prop"
OUTPUT_NODE = True
def prop(self, audio: AudioData):
return (audio.sample_rate,)
class PlayAudio:
def __init__(self) -> None:
self.output_dir = folder_paths.get_temp_directory()
@classmethod
def INPUT_TYPES(cls):
return {
"required": {
"audios": ("AUDIO",),
},
}
CATEGORY = BASE_NODE_CATEGORY
INPUT_IS_LIST = (True,)
RETURN_TYPES = ()
FUNCTION = "play_audio"
OUTPUT_NODE = True
def play_audio(self, audios: list[AudioData]):
max_counts = 50
results = []
for audio in audios[:max_counts]:
filename_prefix = "_temp_" + "".join(
random.choice("abcdefghijklmnopqrstupvxyz") for _ in range(5)
)
full_output_folder, filename, counter, subfolder, filename_prefix = (
folder_paths.get_save_image_path(filename_prefix, self.output_dir)
)
filename = f"{filename}.wav"
torchaudio.save(
os.path.join(full_output_folder, filename),
audio.waveform,
audio.sample_rate,
format="wav",
)
results.append(
[
{
"filename": filename,
"subfolder": subfolder,
"type": "temp",
}
]
)
return {
"ui": {"audios": results},
}
NODE_CLASS_MAPPINGS = {
"SDT_LoadAudio": LoadAudio,
"SDT_LoadAudios": LoadAudios,
"SDT_SaveAudio": SaveAudio,
"SDT_SaveAudioWithSequentialNumbering": SaveAudioWithSequentialNumbering,
"SDT_AudioProperty": AudioProperty,
"SDT_PlayAudio": PlayAudio,
}
NODE_DISPLAY_NAME_MAPPINGS = {
"SDT_LoadAudio": "Load Audio",
"SDT_LoadAudios": "Load Audios",
"SDT_SaveAudio": "Save Audio",
"SDT_SaveAudioWithSequentialNumbering": "Save Audio With Sequential Numbering",
"SDT_AudioProperty": "Audio Property",
"SDT_PlayAudio": "Play Audio",
}