Skip to content

Spectral Editing

spectralEdit(...) is libsonare's region-based STFT editor — STFT (short-time Fourier transform) is the time–frequency grid a signal is cut into before it can be edited by frequency, and it is explained in the glossary. Use spectralEdit when you want to change a time and frequency rectangle inside a mono clip: attenuate a whistle, mute a short hum, boost a narrow band, or heal a small dropout from neighboring frames.

It is an offline transform. The output keeps the same sample rate and length as the input, and each region operation is applied in order to the same STFT buffer.

SPECTRAL EDIT · REGIONIDLE
Spectral editing — erase one region, leave the rest

A steady pad is given a narrow whistle over a middle time window (Artifact). One `spectralEdit` region op — the same time x frequency rectangle the whistle occupies — removes it (Edited), and the averaged spectra show the in-band spike collapse while everything outside the shaded band is untouched. Switch the mode to compare attenuate (turn the bins down by gainDb), mute (silence them) and heal (rebuild them from neighbouring frames), and flip Compare to audition each side. NOTCH is the in-band reduction in dB.

Compare
Mode

When To Use It

GoalMode
Turn a selected band up or down by an exact amountgain
Reduce a selected band — same formula as gain, conventionally passed a negative gainDbattenuate
Remove a selected rectangle completelymute
Fill a small artifact from nearby framesheal

gain and attenuate run the identical math; they exist as two names so your code reads by intent. Use gain when you mean to boost and attenuate (with a negative gainDb) when you mean to cut.

Use Editing DSP for pitch, time, note, and voice edits. Use Mastering Processors for whole-track tone, dynamics, repair, and delivery processing.

Usage

typescript
import { init, spectralEdit } from '@libraz/libsonare';

await init();

const repaired = spectralEdit(samples, sampleRate, [
  {
    startSample: Math.round(1.25 * sampleRate),
    endSample: Math.round(1.55 * sampleRate),
    lowHz: 7600,
    highHz: 8300,
    mode: 'attenuate',
    gainDb: -18,
  },
  {
    startSample: Math.round(2.1 * sampleRate),
    endSample: Math.round(2.18 * sampleRate),
    lowHz: 0,
    highHz: 400,
    mode: 'heal',
  },
], {
  nFft: 2048,
  hopLength: 512,
  window: 'hann',
  healRadiusFrames: 2,
});
python
import libsonare as sonare

repaired = sonare.spectral_edit(
    samples,
    sample_rate,
    [
        sonare.SpectralRegionOp(
            start_sample=int(1.25 * sample_rate),
            end_sample=int(1.55 * sample_rate),
            low_hz=7600,
            high_hz=8300,
            gain_db=-18,
            mode="attenuate",
        ),
        sonare.SpectralRegionOp(
            start_sample=int(2.1 * sample_rate),
            end_sample=int(2.18 * sample_rate),
            low_hz=0,
            high_hz=400,
            mode="heal",
        ),
    ],
    n_fft=2048,
    hop_length=512,
    window="hann",
    heal_radius_frames=2,
)

Options

You give the region in samples and hertz, but the edit lands on the STFT grid those two options build:

What nFft and hopLength build
input samplesnFft — window lengthwindowed frameshopLengthspectrogram columnsone FFT per frame → one column
  • windowed frames
  • the same samples, analysed again
  • spectrogram columns
nFft is the length of one analysis window and hopLength is how far the next one starts after it, so at the defaults every window overlaps the previous one by three quarters. Each window becomes one spectrogram column — the vertical strips your time range is applied to, sliced into frequency bins your lowHz/highHz range picks from.
FieldMeaning
startSample / endSampleTime rectangle in input samples. Omitting endSample (Python: leave end_sample at its default -1 sentinel) spans to the end of the signal. In Python, end_sample=0 is an empty region, not the full signal.
lowHz / highHzFrequency rectangle in Hz. Values are clamped to [0, Nyquist]; highHz <= 0 means Nyquist.
gainDbGain in dB. Applied identically by gain and attenuate as magnitude *= 10^(gainDb/20); pass a negative value to reduce. Ignored by mute/heal.
nFftPower-of-two FFT size, default 2048.
hopLengthSTFT hop, default 512; must satisfy 0 < hopLength <= nFft / 2.
windowhann, hamming, blackman, or rectangular.
healRadiusFramesNeighbor frames on each side used by heal, default 2; must be >= 1.

Small regions usually work best. Broad, long, or repeatedly healed regions can sound phasey because the edit happens in an STFT representation rather than a source-separated model.