Nodes/ComfyUI-AudioReactive/Sustain (Audio Op)
ComfyUI Node

Sustain (Audio Op)

Sustain — an asymmetric 'hold then decay' envelope that's still a stub

By dmarx·Created 3 years ago·Updated 2 years ago· 11
Sustain (Audio Op)
  • 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 + Stretch pair gets you partway (range shaping, not time shaping). For real envelope-follower behavior, smooth the curve in the keyframe domain after SignalToCurve, or just use Rms with 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 k input 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.

CategoryAudioReactive/Operators

Inputs (1)

NameTypeDefaultDescription
signalSIGNAL

Outputs (1)

NameTypeDescription
SIGNALSIGNAL