VOLTAlearn

Learn Volta / Write your own DSP

A spectral effect

Write a per-bin law that edits a sound's spectrum frame by frame.

lesson 39 of 39 · about 12 minutes

Open this lesson in the workbench →

Everything so far worked on samples. Some effects are easier to say in the frequency domain: keep only the loudest partials, reset every phase, tilt the spectrum, freeze it. spectralMap runs your law once per frequency bin of a running Fourier transform and resynthesises the result.

bus |> spectralMap (\m -> …) hands your law each bin's magnitude m (a sine of amplitude A reads about A at its bin) and plays back the magnitudes you return. The analysis is 1024 points with a hop of 256, so the node has 1024 samples of latency; the engine compensates.

Inside a spectral law you can read the bin's context: binHz (its centre frequency), framePeak (the loudest bin this frame), magAt d (a neighbour's magnitude), frameIndex. \m -> if m > 0.2 * framePeak then m else 0.0 keeps only the strong partials — a spectral gate.

spectralPolar (\m ph -> [m, ph']) returns the phase too. [m, 0.0] resets every phase every hop: the classic robot voice. State cells work per bin: spectralMap (\m s -> let y = s + 0.1 * (m - s) in [y, y]) [0.0] smears each bin over time.

Put your law in an effect … on track like any other; spectrallib in the library is ten of them, four pinned against the builtin spectral effects.

The starting point#

The lesson opens with this song in the editor. Every edit recompiles and plays.

-- A spectral effect: a wide pad. You are about to edit its spectrum
-- bin by bin.
pad = patch {
  source = saw (freq pitch) + saw (freq pitch * 1.005) + square (freq pitch * 0.5) * 0.4,
  ampEnv = adsr 200ms 600ms 0.6 900ms,
  gain   = vel * 0.14
}

padT = play pad !"<e3 c3 g2 d3>"

What to watch#

Watch the spectrum as you raise the gate's threshold: the quiet bars between the pad's partials disappear first, then the weaker partials themselves, until only a few pure lines remain.

Goals#

The workbench checks these as you work; the solution is there when you want it.

  1. Edit the spectrum: spectralMap (\m -> …)
  2. Read the bin context: framePeak, binHz or magAt
  3. Stretch: a robot voice with spectralPolar

Make it yours#

Write a spectral tilt: multiply each bin by pow (binHz / 1000.0) slope with slope on a macro. Then declare it as an effect and ride the slope from a scene.

Further reading#

Write a spectral effect in the reference.