stft_simple: STFT and inverse STFT

View source: R/stft.R

stft_simpleR Documentation

STFT and inverse STFT

Description

Short-Time Fourier Transform and its inverse for converting a signal between time and frequency domains. stft_simple returns the full complex spectrogram containing both positive and negative frequencies. Any final samples that do not fit into a complete frame are silently dropped unless padWithSilence = TRUE. istft_simple performs inverse STFT.

Usage

stft_simple(
  x,
  samplingRate = NULL,
  wl = 512,
  step = wl%/%2,
  wn = "gaussian",
  zp = 0,
  padWithSilence = FALSE
)

istft_simple(
  spec,
  wl,
  step,
  wn = "gaussian",
  wnSyn = c("wola", "rectangle"),
  type = c("half", "full"),
  fade = FALSE,
  tol = 1e-06
)

istft_timevar(
  spec,
  wl,
  step,
  wn = "gaussian",
  type = c("half", "full"),
  multPitch = 1,
  timeStretch = 1,
  fade = TRUE,
  tol = 1e-06
)

Arguments

x

numeric vector

samplingRate

sampling rate (Hz). If left NULL, time and frequency labels are not added (faster, but not by much)

wl

window length in samples (can be even or odd, but >=3). If wl exceeds the length of input vector, it is reset to wl = length(x). For maximum speed, set wl = nextn(your_original_wl)

step

step in samples

wn

window type accepted by winFun: character string or function

zp

window length after zero padding, samples. No padding is performed if zp < analysis window length in samples. If NULL, the signal is padded to a good number for speeding up the FFT

padWithSilence

if TRUE, pads the sound at both ends with half a window length of silence to resolve the edges properly and avoid dropping the last few samples (not needed if your sound is already padded with some silence)

spec

input complex spectrogram (rows = frequency, columns = time)

wnSyn

synthesis window: 'wola' = weighted overlap-add (WOLA) / STFT pseudoinverse, avoids spectral artifacts when the complex spectrogram is modified prior to iSTFT (default, recommended for most applications); 'rectangle' = no synthesis window, allows reconstructing the original signal exactly provided that the spectrogram is not modified between STFT and iSTFT

type

"full" = the full spectrogram returned by stft_simple; "half" = just the positive frequencies up to Nyquist (default)

fade

if TRUE, a linear fade-in and fade-out of length wl (but no more than 1/4 of input length) is applied to the output vector ("wola" method only)

tol

to avoid division by 0, denominator values smaller than tol are incremented by tol times sum of squared windows ("wola" method only)

multPitch

pitch multiplier interpolated across frames; > 1 –> raise pitch; must be positive

timeStretch

time stretch factor interpolated across frames; > 1 –> increase duration; must be positive

Details

If wnSyn = "rectangle", no synthesis window is applied and the original signal can be recovered exactly, without any distortion at the beginning and the end (windows that taper to 0 give edge samples zero weight, so pad with silence to reconstruct them). The reconstruction is then exact for the covered portion of the signal when inverting an unnormalized windowed STFT produced by stft_simple, provided there is no zero-padding and the frame grid covers the signal. However, this works only if the complex spectrogram is not modified between STFT and iSTFT. When the spectrogram is modified - for example, when some filter is applied in the frequency domain - it is better to set wnSyn = "wola", which applies the same windowing function to the iFFT of each frame. Finally, the much slower function istft_timevar works with time-variable step sizes in the context of dynamic pitch shifting or time stretching (see shiftPitch). The zp argument of stft_simple() is for analysis/display only. Spectrograms produced with zp > wl cannot be inverted by istft_simple() or istft_timevar() in their current form.

Value

stft_simple returns a complex spectrogram with both positive and negative frequencies as a matrix. If samplingRate is not NULL, row names give frequency in kHz and column names give time in ms. If samplingRate is NULL, no row or column names are added. Rows beyond the Nyquist frequency (half the sampling rate) correspond to negative frequencies. Time stamps correspond to the middle of each frame, starting with half a window length if padWithSilence = FALSE or starting at 0 ms if padWithSilence = TRUE. istft_simple returns a numeric vector.

Examples

## Ex. 1: obtaining a spectrogram
data(speechEx, package = "soundgen")
spec = stft_simple(speechEx@left[1:10000], samplingRate = speechEx@samp.rate,
  wl = 512, step = 100)
image(t(Mod(spec)))
# the spectrum of one frame
plot(as.numeric(rownames(spec)), Mod(spec[, 15]), type = 'l', xlab = 'Freq, kHz')
spec[1:5, 1:5]

# To get the positive frequencies only, use:
halfspec = spec[1:(nrow(spec) %/% 2 + 1), , drop = FALSE]
image(t(Mod(halfspec)))


## Ex. 2: manual narrow-band spectral filter to turn white noise into a whistle
samplingRate = 16000
wl = 317; step = 51
noise = fade(rnorm(samplingRate), samplingRate = samplingRate)
spec = stft_simple(noise, samplingRate, wl = wl, step = step, zp = 0)
spec = spec[1:(wl %/% 2 + 1), ]  # discard negative frequencies

# amplify one frequency band by 50 dB
spec_filtered = spec
spec_filtered[50, ] = spec_filtered[50, ] * 10^(50/20)
image(y = as.numeric(rownames(spec_filtered)), t(log(Mod(spec_filtered))))

# inverse STFT using wnSyn = 'wola'
noise_filtered = fade(istft_simple(spec_filtered, wl = wl, step = step,
  type = 'half', wnSyn = 'wola'), samplingRate = samplingRate)
spectrogram(noise_filtered, samplingRate)
# playme(noise_filtered, samplingRate)


## Ex. 3: reconstructing the input exactly with wnSyn = 'rectangle'
a = rnorm(64)
wl = 11; step = 3  # any wl and step are fine, even or odd
spec_full = stft_simple(a, wl = wl, step = step, wn = 'gaussian')
new_1 = istft_simple(spec_full, wl = wl, step = step, type = "full",
  wn = 'gaussian', wnSyn = 'rectangle')
plot(a, type = "l"); lines(new_1, col = 'green')
# note the missing bit at the end - incomplete final frame dropped

spec_half = spec_full[1:(nrow(spec_full) %/% 2 + 1), ]
new_2 = istft_simple(spec_half, wl = wl, step = step, type = "half",
  wn = 'gaussian', wnSyn = 'wola')  # wola is also exact here
plot(a, type = "l"); lines(new_2, col = 'green')


## Ex. 4: identity check for istft_timevar()
new_timevar = istft_timevar(spec_full, wl = wl, step = step,
  type = "full", multPitch = 1, timeStretch = 1, fade = FALSE)
plot(a, type = "l"); lines(new_timevar, col = 'green')
all(round(new_2, 5) == round(new_timevar, 5))  # should be TRUE
all(round(new_timevar, 5) == round(a[1:length(new_timevar)], 5))
# should be identical as well, except that two last samples in "a" are dropped

soundgen documentation built on Sept. 20, 2026, 5:07 p.m.