Madow Inputs ποΈ
Every ACE-Step dial in one node β and a record of exactly what ran
- madow
- file_path
- context
- validation
You're three renders into an ACE-Step session and you've already lost track of which cfg, which seed, which lyrics produced the take you actually liked. That's the exact problem Madow Inputs exists to kill. It's the parameter-hub node from NovaFemme's Nova Audio Player pack: every ACE-Step generation parameter in one node, with named presets, warn-only validation, and a context output that records precisely what produced a take.
Here's the context that makes it click. ACE-Step is the open local music model - genuinely good instrumentals on a modest card, weak vocals, the thing the community treats as the local Suno. But running it in ComfyUI means babysitting a whole constellation: a sampler, a text encoder, an empty-latent node, an APG sampler node, a sampling-model helper. Parameters that belong together live apart, and nothing downstream knows what actually ran. Madow Inputs collects the loose change: 27 parameters, namespaced so the two cfg-shaped ones can't collide - ksampler.cfg (the sampler's) versus text.cfg_scale (the text encoder's).
How it works
The widgets are grouped on the node - APG, Sampler, Text, File - but the clever part is what the node emits beyond the values. Four outputs: madow, file_path, context, and validation.
The one you'll care about if you're logging runs is context. It's a JSON-ish blob carrying the exact parameters, plus two SHA-256 hashes: one seed-excluded (groups runs that differ only by seed - that's your seed-noise-floor question answered from a log) and one seed-included (did I already run this exact config?). The naming fields are deliberately kept out of both hashes, so renaming a file never splits identical renders into different groups. Wire context into the Nova Player node's optional context input and every measured take records what produced it. That's the whole point of the pack: listen to a generation and know, not guess, what made it.
Presets load as one JSON file each from presets/, so they're shareable and hand-editable. Loading one writes the real widgets - the backend never substitutes values, which means a saved workflow records what actually ran, not a preset name. validation is a text output of cross-field checks that warn rather than block: a caption saying "98 BPM" against a bpm widget of 122, vocals without lyrics, a duration that disagrees with the latent you wired in, apg.eta above 1. Warnings also print to your console, because a conflict you don't see on the node body is a conflict that costs a render.
The inputs that matter
You will not touch all 27. The ones a beginner actually sets:
caption_promptandcaption_lyrics- multiline text; the actual song spec.music_bpm,music_duration,music_timesignature,music_language,music_keyscale- the musical frame. The last three are ACE-Step's own combo domains, read live from ACE-Step's text-encode node when it's installed, so they connect without coercion.ksampler_steps,ksampler_cfg,ksampler_seed,ksampler_sampler_name,ksampler_scheduler- the generation core, defaults already sensible for ACE-Step (er_sde,linear_quadratic, 80 steps, cfg 2.8).- The optional
latent_secondsinput: wire inEmptyAceStepLatentAudio's seconds here and Madow checks it againstmusic_durationfor you.
The APG group (apg_eta, apg_norm_threshold, apg_momentum) belongs to the APG sampler node and shift to the sampling-model helper - leave them at defaults until you know why you're changing them. One aside: the player's "APG meter" is a different APG entirely (it measures artifacts). Same acronym, zero relationship, and yes, that's confusing.
Install
It's part of ComfyUI-NovaAudioPlayer, so you get the whole pack. ComfyUI Manager β search "Nova Audio", or:
cd ComfyUI/custom_nodes
git clone https://github.com/NovaFemme/ComfyUI-NovaAudioPlayer.git
Then restart ComfyUI. No extra Python dependencies - nothing beyond what ComfyUI ships (scipy is only used for accurate LUFS in the player and degrades gracefully if missing). You do need the ACE-Step custom nodes installed for the actual music generation; this node is the control panel, not the engine.
Where people trip
The big one: this node doesn't generate anything. If you grab it without ACE-Step wired up, you get a very tidy dashboard and silence. Also worth knowing - the time-signature/language/key-scale dropdowns fall back to fixed lists when ACE-Step's encode node isn't present, which is fine until you try to connect them to nothing. And the hashes and presets only pay for themselves if you actually log context through the player or your own logger. Use it alone as a tidy parameter panel if you like; just know the provenance machinery is the headline, and it needs a downstream log to earn its keep.
Inputs (29)
| Name | Type | Default | Description |
|---|---|---|---|
| apg_eta | FLOAT | 0.45-10β10 | β |
| apg_norm_threshold | FLOAT | 4.000β100 | β |
| apg_momentum | FLOAT | 0.20-5β5 | β |
| sched_shift | FLOAT | 3.000β100 | β |
| ksampler_steps | INT | 801β10000 | β |
| ksampler_cfg | FLOAT | 2.800β100 | β |
| ksampler_sampler_name | COMBO | er_sde | 44 options: euler, euler_cfg_pp, euler_ancestral, euler_ancestral_cfg_pp, heun, heunpp2, +38 |
| ksampler_scheduler | COMBO | linear_quadratic | 9 options: simple, sgm_uniform, karras, exponential, ddim_uniform, beta, +3 |
| ksampler_denoise | FLOAT | 1.000β1 | β |
| ksampler_seed | INT | 00β18446744073709550000 | β |
| caption_prompt | STRING | β | |
| caption_lyrics | STRING | β | |
| music_bpm | INT | 1201β400 | β |
| music_duration | FLOAT | 120.01β3600 | β |
| music_timesignature | COMBO | 4 | 4 options: 2, 3, 4, 6 |
| music_language | COMBO | en | 23 options: en, ja, zh, es, de, fr, +17 |
| music_keyscale | COMBO | E minor | 34 options: C major, C# major, Db major, D major, D# major, Eb major, +28 |
| text_cfg_scale | FLOAT | 2.000β100 | β |
| lm_temperature | FLOAT | 0.720β10 | β |
| lm_top_p | FLOAT | 0.900β1 | β |
| lm_top_k | INT | 00β10000 | β |
| lm_min_p | FLOAT | 0.000β1 | β |
| lm_generate_audio_codes | BOOLEAN | true | β |
| file_prefix | STRING | β | |
| file_name | STRING | β | |
| file_folder | STRING | β | |
| file_separator | STRING | _ | β |
| preset_nameopt | STRING | Set by the preset bar. Recorded in context so a run can be traced to the preset it came from; never used to supply values. | |
| latent_secondsopt | FLOAT | 0.000β3600 | Optional. Wire EmptyAceStepLatentAudio's seconds here to check it against duration. |
Outputs (4)
| Name | Type | Description |
|---|---|---|
| madow | MADOW | β |
| file_path | STRING | β |
| context | STRING | β |
| validation | STRING | β |