Files

184 lines
4.9 KiB
Python

import glob
import os
import torchaudio
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)
audio = {"waveform": waveform.unsqueeze(0), "sample_rate": sample_rate}
return (audio, 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 (
[{"waveform": waveform.unsqueeze(0), "sample_rate": sample_rate} for waveform, sample_rate 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,
):
subfolder = os.path.dirname(os.path.normpath(filename_prefix))
full_output_folder = os.path.join(self.output_dir, subfolder)
os.makedirs(full_output_folder, exist_ok=True)
if "." not in file_name:
file_name = file_name + _AUDIO_FILE_FORMAT_EXT_MAP[file_format]
for b in range(audio["waveform"].shape[0]):
filename_with_batch_num = file_name.replace("%batch_num%", str(b))
filename_with_batch_num = (
os.path.basename(os.path.normpath(filename_prefix)) + "_" + filename_with_batch_num
)
file = os.path.join(self.output_dir, filename_with_batch_num)
torchaudio.save(file, audio["waveform"][b], 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 = ("sample_rate",)
FUNCTION = "prop"
OUTPUT_NODE = True
def prop(self, audio: AudioData):
return (audio["sample_rate"],)
NODE_CLASS_MAPPINGS = {
"SDT_LoadAudio": LoadAudio,
"SDT_LoadAudios": LoadAudios,
"SDT_SaveAudio": SaveAudio,
"SDT_AudioProperty": AudioProperty,
}
NODE_DISPLAY_NAME_MAPPINGS = {
"SDT_LoadAudio": "Load Audio",
"SDT_LoadAudios": "Load Audios",
"SDT_SaveAudio": "Save Audio",
"SDT_AudioProperty": "Audio Property",
}