diff --git a/nodes/custom_io_v3.py b/nodes/custom_io_v3.py index 103ecf2..26187ef 100644 --- a/nodes/custom_io_v3.py +++ b/nodes/custom_io_v3.py @@ -100,6 +100,20 @@ class AdvSamplerInfo(io.ComfyTypeIO): """ Type = dict # Advanced sampler info is stored as a dictionary +@io.comfytype(io_type="ADV_AUDIO_INFO") +class AdvAudioInfo(io.ComfyTypeIO): + """ + Advanced audio information type for SageUtils metadata nodes. + Contains metadata about the advanced audio settings without loading the actual audio model. + """ + Type = dict # Advanced audio info is stored as a dictionary + # Keys are: + # "duration": float + # "bpm": int + # "timesignature": str + # "keyscale": str + # "language": str + @io.comfytype(io_type="OLLAMA_OPTIONS") class OllamaOptions(io.ComfyTypeIO): """ diff --git a/nodes/image_v3.py b/nodes/image_v3.py index f2c9258..d860eaf 100644 --- a/nodes/image_v3.py +++ b/nodes/image_v3.py @@ -33,6 +33,8 @@ from ..utils.common import get_files_in_dir from ..utils.constants import QUICK_ASPECT_RATIOS, MAX_RESOLUTION from ..utils.helpers_image import calc_padding, resize_needed, image_manipulate +from .custom_io_v3 import AdvAudioInfo + class Sage_EmptyLatentImagePassthrough(io.ComfyNode): @classmethod def define_schema(cls): @@ -574,6 +576,107 @@ class Sage_ReferenceImage(io.ComfyNode): return io.NodeOutput(ref_latent_node.out(0), encoder_node.out(0), expand=graph.finalize()) +class Sage_AceAdvOptions(io.ComfyNode): + @classmethod + def define_schema(cls): + return io.Schema( + node_id="Sage_AceAdvOptions", + display_name="Ace Advanced Options", + description="Advanced options for Ace Step 1.5 audio encoding. These options can be used to fine-tune the behavior of the audio encoding process.", + category="Sage Utils/conditioning", + inputs=[ + io.Combo.Input("language", options=["en", "ja", "zh", "es", "de", "fr", "pt", "ru", "it", "nl", "pl", "tr", "vi", "cs", "fa", "id", "ko", "uk", "hu", "ar", "sv", "ro", "el"]), + io.Float.Input("cfg_scale", default=2.0, min=0.0, max=100.0, step=0.1, advanced=True), + io.Float.Input("temperature", default=0.85, min=0.0, max=2.0, step=0.01, advanced=True), + io.Float.Input("top_p", default=0.9, min=0.0, max=2000.0, step=0.01, advanced=True), + io.Int.Input("top_k", default=0, min=0, max=100, advanced=True), + io.Float.Input("min_p", default=0.000, min=0.0, max=1.0, step=0.001, advanced=True), + ], + outputs=[ + AdvAudioInfo.Output("adv_audio_info", display_name="Advanced Audio Info", tooltip="The advanced audio options for Ace Step 1.5 encoding."), + ], + ) + + @classmethod + def execute(cls, **kwargs) -> io.NodeOutput: + language = kwargs.get("language", "en") + cfg_scale = kwargs.get("cfg_scale", 2.0) + temperature = kwargs.get("temperature", 0.85) + top_p = kwargs.get("top_p", 0.9) + top_k = kwargs.get("top_k", 0) + min_p = kwargs.get("min_p", 0.000) + + adv_audio_info = { + "language": language, + "cfg_scale": cfg_scale, + "temperature": temperature, + "top_p": top_p, + "top_k": top_k, + "min_p": min_p + } + + return io.NodeOutput(adv_audio_info) + +class Sage_Ace15AudioEncode(io.ComfyNode): + @classmethod + def define_schema(cls): + return io.Schema( + node_id="Sage_Ace15AudioEncode", + display_name="Ace Step 1.5 Audio Encode", + description="Encodes an audio clip into a conditioning using the Ace Step 1.5 model. This is used to create a conditioning from an audio reference.", + category="Sage Utils/conditioning", + inputs=[ + io.Clip.Input("clip"), + io.String.Input("tags", force_input=True, multiline=True, dynamic_prompts=True), + io.String.Input("lyrics", force_input=True, multiline=True, dynamic_prompts=True), + io.Int.Input("seed", default=0, min=0, max=0xffffffffffffffff, control_after_generate=True), + io.Float.Input("duration", default=120.0, min=0.0, max=2000.0, step=0.1), + io.Int.Input("bpm", default=120, min=10, max=300), + io.Combo.Input("timesignature", options=['2', '3', '4', '6']), + io.Combo.Input("keyscale", options=[f"{root} {quality}" for quality in ["major", "minor"] for root in ["C", "C#", "Db", "D", "D#", "Eb", "E", "F", "F#", "Gb", "G", "G#", "Ab", "A", "A#", "Bb", "B"]]), + io.Boolean.Input("generate_audio_codes", default=True, tooltip="Enable the LLM that generates audio codes. This can be slow but will increase the quality of the generated audio. Turn this off if you are giving the model an audio reference.", advanced=True), + AdvAudioInfo.Input("adv_audio_info", display_name="Advanced Audio Info", tooltip="Advanced audio options for Ace Step 1.5 encoding.", advanced=True) + ], + outputs=[io.Conditioning.Output()], + ) + + @classmethod + def execute(cls, **kwargs) -> io.NodeOutput: + clip = kwargs.get("clip") + tags = kwargs.get("tags", "") + lyrics = kwargs.get("lyrics", "") + seed = kwargs.get("seed", 0) + duration = kwargs.get("duration", 120.0) + bpm = kwargs.get("bpm", 120) + timesignature = kwargs.get("timesignature", "4") + keyscale = kwargs.get("keyscale", "C major") + generate_audio_codes = kwargs.get("generate_audio_codes", True) + + adv_audio_info = kwargs.get("adv_audio_info", {}) + language = adv_audio_info.get("language", "en") + cfg_scale = adv_audio_info.get("cfg_scale", 2.0) + temperature = adv_audio_info.get("temperature", 0.85) + top_p = adv_audio_info.get("top_p", 0.9) + top_k = adv_audio_info.get("top_k", 0) + min_p = adv_audio_info.get("min_p", 0.000) + + tokens = clip.tokenize(tags, + lyrics=lyrics, + bpm=bpm, + duration=duration, + timesignature=int(timesignature), + language=language, + keyscale=keyscale, + seed=seed, + generate_audio_codes=generate_audio_codes, + cfg_scale=cfg_scale, + temperature=temperature, + top_p=top_p, + top_k=top_k, + min_p=min_p) + conditioning = clip.encode_from_tokens_scheduled(tokens) + return io.NodeOutput(conditioning) + IMAGE_NODES = [ Sage_EmptyLatentImagePassthrough, Sage_EmptyAceStep15LatentAudio, @@ -583,7 +686,9 @@ IMAGE_NODES = [ Sage_GuessResolutionByRatio, Sage_QuickResPicker, Sage_CubiqImageResize, - Sage_ReferenceImage + Sage_ReferenceImage, + Sage_AceAdvOptions, + Sage_Ace15AudioEncode, ] """ IMAGE_CLASS_MAPPINGS = {