Sustain (Audio Op)
Sustain — an asymmetric 'hold then decay' envelope that's still a stub
- signal
- SIGNAL
OpSustain (display name "Sustain (Audio Op)") is the fanciest smoothing idea in ComfyUI-AudioReactive, and it's also one of the unfinished ones. Where Smooth blurs symmetrically, Sustain is deliberately asymmetric - a "hold then decay" envelope. The source:
def sustain(k=500):
def sustain_(y, sr=None):
win_sustain = signal.windows.hann(2*k)
win_sustain[:k] = 0
filtered = signal.convolve(y.ravel(), win_sustain, mode='same') / sum(win_sustain)
return filtered
return sustain_
Build a Hann window of length 2k, zero out its first half, and convolve. The zeroed half means the window only ever looks backward in time - a hit makes the output jump immediately, then decay smoothly over the following k samples, with no "pre-echo" of events before they happen. That's a causal envelope follower, and it's the right tool for the "pulse then breathe" feel: the animation snaps on the beat and swells down between beats. (The author left a TODO for an exponential-window decay() variant in the same file, which tells you the direction this was heading.)
Here's the honest part: it doesn't work yet. sustain is a factory - sustain(k=500) returns the envelope follower - and the node wrapper calls every operator as f(y, sr), so it calls sustain(y, sr) with the signal as k. That returns a closure parked in the SIGNAL's y slot. The node completes without error and the function object flows downstream until real math throws a TypeError. The info_schema confirms it: only a signal input, no k to tune.
What to do instead
- If a workflow contains "Sustain (Audio Op)", expect it to break downstream; route around it.
- Want pulse-then-decay today? The pack's
Smoosh+Stretchpair gets you partway (range shaping, not time shaping). For real envelope-follower behavior, smooth the curve in the keyframe domain afterSignalToCurve, or just useRmswith a short analysis window - its frame averaging is already a form of decay. - Watch the repo. This is a complete, correct DSP idea in the source; it needs a
kinput wired to the wrapper.
Inputs and outputs
As shipped:
signal(SIGNAL) - in.SIGNAL- out, containing a function object, not an enveloped signal. Don't build on it.
Installing it
Manager (search "AudioReactive") or:
cd ComfyUI/custom_nodes
git clone https://github.com/dmarx/ComfyUI-AudioReactive
Restart and sit through the first-load auto-install of scipy (the Hann window and convolution), scikit-learn, librosa, loguru. ModuleNotFoundError: No module named 'keyframed' on load → pip install keyframed.
The takeaway
Sustain is another "great backend, missing wrapper" node in dmarx's port of his video-killed-the-radio-star notebook. It's part of the pack's parameterized family - Sustain, Smooth, Bandpass, Clamp, Modulo, Pow, Quantize - all stubbed, while the no-parameter operators work today. Skim nodes/audio_utils.py to tell which family any node belongs to before you wire it into a real workflow.
Inputs (1)
| Name | Type | Default | Description |
|---|---|---|---|
| signal | SIGNAL | — |
Outputs (1)
| Name | Type | Description |
|---|---|---|
| SIGNAL | SIGNAL | — |