Song Generator
This app uses Ace-Step 1.5XL to generate a full song and allows you to choose the genre and write the lyrics, either automatically or manually.
简介
Song Generator is a ComfyUI workflow that synthesizes complete songs with the Ace-Step 1.5XL audio model. It supports two lyric paths: automatic, where a short topic is expanded into BPM- and duration-aware lyrics, and manual, where you paste your own text. A ComfySwitchNode routes between these paths before the lyrics are encoded by TextEncodeAceStepAudio1.5 and used to guide the audio model. You can preview the result with PreviewAny and export with SaveAudioAdvanced.
Under the hood, UNETLoader and VAELoader load the Ace-Step 1.5XL checkpoints. EmptyAceStep1.5LatentAudio allocates a latent audio canvas sized to your target duration. ModelSamplingAuraFlow configures the sampler for the model, and KSampler performs the iterative denoising on that latent using the conditioning produced by TextEncodeAceStepAudio1.5. ConditioningZeroOut is included to safely zero optional negative conditioning when not used. After sampling, VAEDecodeAudio converts the latent to a waveform for monitoring and export.
The BPM/duration-aware lyric module uses PrimitiveInt/PrimitiveFloat to capture BPM and target length, StringReplace to inject those values into a songwriter instruction, and TextGenerate to create structured verses/choruses sized for the track. StringCompare helps detect empty input, and the ComfySwitchNode cleanly selects between the auto-lyrics and a PrimitiveStringMultiline node for manual lyrics. DualCLIPLoader/CLIPLoader are available for workflows that optionally mix in CLIP-style text conditioning, though the primary path uses TextEncodeAceStepAudio1.5 for Ace-Step.
你需要提供
- 正在读取工作流
在 ComfyUI 中运行。 此工作流无法在本页面运行。你可以在 Comfy Cloud 中打开(已预先加载所需内容),或下载后在自己的 ComfyUI 中使用。
在 Cloud 中试用查看工作流内部
节点图的只读预览。在 Comfy Cloud 中打开或下载后即可编辑节点。
Loading the graph
Open full-size workflow preview- 作者
- Rob
需要自定义节点
- 正在读取工作流
通过代码使用此工作流
尽可能根据工作流图和输入自动生成代码,否则显示已保存的示例。运行代码可能产生费用,请先核对凭据和端点。
What you need
- Runs on
- Comfy Cloud
- 密钥
- COMFY_API_KEY
需要付费的 Cloud 方案和额度。
# Python 3.10+: pip install comfy-sdk==0.4.0
import os
from comfy_sdk import Comfy
api_key = os.environ.get("COMFY_API_KEY", "").strip()
if not api_key: raise RuntimeError("Set COMFY_API_KEY")
os.environ.setdefault("COMFY_BASE_URL", "https://cloud.comfy.org")
client = Comfy(api_key=api_key)
workflow = client.workflows.from_json({
"3": {
"class_type": "KSampler",
"inputs": {
"cfg": 1,
"denoise": 1,
"latent_image": [
"98",
0
],
"model": [
"78",
0
],
"negative": [
"47",
0
],
"positive": [
"94",
0
],
"sampler_name": "euler",
"scheduler": "simple",
"seed": 0,
"steps": 12
},
"_meta": {
"title": "KSampler"
}
},
"18": {
"class_type": "VAEDecodeAudio",
"inputs": {
"samples": [
"3",
0
],
"vae": [
"106",
0
]
},
"_meta": {
"title": "VAE Decode Audio"
}
},
"47": {
"class_type": "ConditioningZeroOut",
"inputs": {
"conditioning": [
"94",
0
]
},
"_meta": {
"title": "Conditioning Zero Out"
}
},
"78": {
"class_type": "ModelSamplingAuraFlow",
"inputs": {
"model": [
"104",
0
],
"sampling": "flow",
"shift": 3
},
"_meta": {
"title": "ModelSamplingAuraFlow"
}
},
"94": {
"class_type": "TextEncodeAceStepAudio1.5",
"inputs": {
"bpm": [
"129",
0
],
"cfg_scale": 2,
"clip": [
"105",
0
],
"duration": [
"128",
0
],
"generate_audio_codes": True,
"keyscale": "E minor",
"language": "en",
"lyrics": [
"116",
0
],
"min_p": 0,
"seed": 1103026267678031,
"tags": "Lo-fi hip-hop chill track with mellow electric piano, soft vinyl crackle, subtle synth pads, low-pass filtered drums, percussion loops, and soft plucked bass for a relaxed, dreamy vibe.",
"temperature": 0.85,
"timesignature": "4",
"top_k": 0,
"top_p": 0.9
},
"_meta": {
"title": "TextEncodeAceStepAudio1.5"
}
},
"98": {
"class_type": "EmptyAceStep1.5LatentAudio",
"inputs": {
"batch_size": 1,
"seconds": [
"128",
0
]
},
"_meta": {
"title": "Empty Ace Step 1.5 Latent Audio"
}
},
"104": {
"class_type": "UNETLoader",
"inputs": {
"unet_name": "acestep_v1.5_xl_turbo_bf16.safetensors",
"weight_dtype": "default"
},
"_meta": {
"title": "Load Diffusion Model"
}
},
"105": {
"class_type": "DualCLIPLoader",
"inputs": {
"clip_name1": "qwen_0.6b_ace15.safetensors",
"clip_name2": "qwen_4b_ace15.safetensors",
"device": "default",
"type": "ace"
},
"_meta": {
"title": "Load CLIP (Dual)"
}
},
"106": {
"class_type": "VAELoader",
"inputs": {
"vae_name": "ace_1.5_vae.safetensors"
},
"_meta": {
"title": "Load VAE"
}
},
"111": {
"class_type": "SaveAudioAdvanced",
"inputs": {
"audio": [
"18",
0
],
"filename_prefix": "audio/ACE_Step1.5_xl_turbo",
"format": "mp3",
"format.quality": "V0"
},
"_meta": {
"title": "Save Audio (Advanced)"
}
},
"112": {
"class_type": "PrimitiveStringMultiline",
"inputs": {
"value": "An upbeat electronic anthem about building workflows in ComfyUI — connecting nodes late at night and watching the render come through."
},
"_meta": {
"title": "User: song topic (USER_INPUT)"
}
},
"113": {
"class_type": "CLIPLoader",
"inputs": {
"clip_name": "qwen3.5_4b_bf16.safetensors",
"device": "default",
"type": "stable_diffusion"
},
"_meta": {
"title": "CLIP Loader (Qwen LLM for lyrics)"
}
},
"114": {
"class_type": "StringReplace",
"inputs": {
"find": "USER_INPUT",
"replace": [
"112",
0
],
"string": "You are a professional songwriter. Write complete, singable lyrics about the topic below.\n\nThe song is about TARGET_DURATION seconds long at TARGET_BPM BPM. Write enough sections to fill that length naturally:\n- around 120-150s: 2 verses + 2 choruses.\n- around 180-240s: also add a bridge.\n- 210s or more: also add a short intro and outro.\nAt slower tempo (under 90 BPM) use fewer words per line; at faster tempo (over 130 BPM) you can fit more, but keep every line easy to sing in one breath.\n\nOutput ONLY the lyrics — no title, no notes, no explanation.\n\nPut each section tag on its own line, e.g. [Intro], [Verse 1], [Chorus], [Verse 2], [Bridge], [Outro].\n\nRules:\n- Blank line between sections.\n- 6-10 syllables per line; keep lines within a section a similar length.\n- Rhyme the verses; give the chorus one short, repeatable hook.\n- Stay with ONE core image or metaphor.\n- Plain, concrete lines a singer can breathe through — no stacked adjectives or vague filler (neon skies, electric hearts, endless dreams).\n\nTopic: USER_INPUT\nLyrics:"
},
"_meta": {
"title": "Lyrics Prompt (fill USER_INPUT)"
}
},
"115": {
"class_type": "TextGenerate",
"inputs": {
"clip": [
"113",
0
],
"max_length": 512,
"mtp": "auto",
"prompt": [
"132",
0
],
"sampling_mode": "on",
"sampling_mode.min_p": 0.05,
"sampling_mode.presence_penalty": 0,
"sampling_mode.repetition_penalty": 1.05,
"sampling_mode.seed": 35146436134,
"sampling_mode.temperature": 0.7,
"sampling_mode.top_k": 64,
"sampling_mode.top_p": 0.95,
"thinking": False,
"use_default_template": True
},
"_meta": {
"title": "Qwen TextGenerate (lyrics)"
}
},
"116": {
"class_type": "ComfySwitchNode",
"inputs": {
"on_false": [
"117",
0
],
"on_true": [
"115",
0
],
"switch": [
"121",
0
]
},
"_meta": {
"title": "Switch (auto: LLM if manual empty)"
}
},
"117": {
"class_type": "PrimitiveStringMultiline",
"inputs": {
"value": ""
},
"_meta": {
"title": "Manual lyrics (leave EMPTY to auto-generate)"
}
},
"119": {
"class_type": "PreviewAny",
"inputs": {
"source": [
"116",
0
]
},
"_meta": {
"title": "Preview Lyrics"
}
},
"121": {
"class_type": "StringCompare",
"inputs": {
"case_sensitive": True,
"mode": "Equal",
"string_a": [
"117",
0
],
"string_b": ""
},
"_meta": {
"title": "Compare Text (manual lyrics == empty)"
}
},
"127": {
"class_type": "PreviewAny",
"inputs": {
"source": [
"129",
0
]
},
"_meta": {
"title": "bpm to string"
}
},
"128": {
"class_type": "PrimitiveFloat",
"inputs": {
"value": 120
},
"_meta": {
"title": "Float (Duration)"
}
},
"129": {
"class_type": "PrimitiveInt",
"inputs": {
"value": 90
},
"_meta": {
"title": "Int (bpm)"
}
},
"130": {
"class_type": "PreviewAny",
"inputs": {
"source": [
"128",
0
]
},
"_meta": {
"title": "duration to string"
}
},
"131": {
"class_type": "StringReplace",
"inputs": {
"find": "TARGET_DURATION",
"replace": [
"130",
0
],
"string": [
"114",
0
]
},
"_meta": {
"title": "Inject DURATION (TARGET_DURATION)"
}
},
"132": {
"class_type": "StringReplace",
"inputs": {
"find": "TARGET_BPM",
"replace": [
"127",
0
],
"string": [
"131",
0
]
},
"_meta": {
"title": "Inject BPM (TARGET_BPM)"
}
}
})
# run() also passes api_key to the partner nodes in this workflow.
job = client.run(workflow, api_key=api_key)常见问答
Yes. Point UNETLoader and VAELoader to the Ace-Step 1.5XL UNet and VAE checkpoints. The workflow relies on TextEncodeAceStepAudio1.5 for conditioning, so ensure the corresponding text encoder resources are available as packaged with Ace-Step.
Duration is enforced by the latent length from EmptyAceStep1.5LatentAudio, so the output length will match closely. BPM guides the lyric density via TextGenerate and prompt injection but does not force strict metronomic alignment; treat it as a strong narrative/timing hint rather than a beat-locked constraint.
Yes. Switch to the Manual path and paste lyrics into PrimitiveStringMultiline. The ComfySwitchNode routes your text directly to TextEncodeAceStepAudio1.5 for conditioning before generation.
Try increasing KSampler steps moderately, lowering CFG if the sound is harsh, and keeping prompts concise and specific. Ensure the correct Ace-Step VAE is loaded, and avoid overly long durations on a single pass if you encounter instability.