How a signal’s power spreads over frequency, and how to measure it.
On this page
Tape hiss and mains hum are both noise, but hiss spreads its power evenly over every frequency while hum puts nearly all of it at 60 Hz (50 Hz in most of the world) and its multiples. The power spectral density (PSD) tells them apart. It is one curve, the signal’s power at each frequency per hertz, measured once per clip, and it is the right picture for noise, which has no fixed waveform but a steady spread of power. One spectrum of the whole recording, the periodogram, gives a very jumpy curve; Welch’s method averages the spectra of many short windowed segments, which steadies the curve at the cost of frequency detail.
The PSD Px(f) says how much of a signal’s power sits near each frequency f. It is measured with the FFT (fast Fourier transform), the fast way to compute the DFT (discrete Fourier transform). The FFT splits N samples taken at rate fs into frequency bins fs/N apart, the bin width. Dividing each bin’s power by that width makes it a density, so the same sound gives the same curve whatever N or fs you measure it with.
Tape hiss is close to white noise, the same power at every frequency. Pink noise loses 3 dB, half its power per hertz, every octave, each doubling of frequency, so it sounds deeper. Hum is a row of spikes.
For example, take a microphone preamp’s hiss, recorded at 8 kHz as white noise of power 1 (illustrative). It spreads that power evenly from 0 to 4 kHz, so its PSD sits at 0.00025 per hertz, −36 dB/Hz, and the area 0.00025 × 4,000 gives back the power of 1.
The PSD gives a converter’s noise floor (quantization noise is flat, ADC Fundamentals), the signal-to-noise ratio (SNR) in each band and where hum sits.
The simplest estimate takes one FFT of the whole recording of N samples, squares the magnitude and scales it.
For example, take one second of illustrative fan noise at 16 kHz: white noise through a one-pole low-pass, a filter with one feedback term that dulls the highs. The table measures its first 256 and its first 4,096 samples.
| Samples N | Bins | Bin width | Spread over level |
|---|---|---|---|
| 256 | 129 | 62.5 Hz | 1.06 |
| 4,096 | 2,049 | 3.9 Hz | 0.97 |
Spread over level is how far each bin scatters around the true PSD, divided by it.
For noise this estimate is ragged, as the table shows. Each value jumps by about its own size, and a longer recording only gives more bins, each just as jumpy. Welch (1967) shows that for Gaussian (bell-curve) noise the variance of each value, its average squared jump, is about Px², whatever the length. Figure 3 shows the same on white noise at 8 kHz.
Welch’s fix cuts the recording into K segments, windows each with a Hann window, a smooth bump that fades the segment to zero at both ends, and averages their periodograms.
For example, the equalization match of Section 5 needs the PSD of a phone recording. Take 256 ms of it at 8 kHz, 2,048 samples (illustrative). Cut into K = 4 segments, it gives L = 512, bins 15.6 Hz wide instead of 3.9 Hz, and a noise spread about half as large, since the square root of 4 is 2.
Figure 5 runs Welch on 2,048 samples at 8 kHz: tones at 1,000 and 1,200 Hz in white noise through a one-pole low-pass.
Figure 5. 1Two tones. More pieces steady the noise but merge the tones.
The autocorrelation measures how much a signal looks like itself shifted by a lag τ. A pure 60 Hz hum repeats 60 times a second, every 16.7 ms, so it matches itself at every multiple of that lag. The PSD is the Fourier transform of the autocorrelation (Wiener 1930, Khinchin 1934).
A filter scales each sine (LTI Systems and Convolution, section 7), so passing a signal through it multiplies its PSD by |H(f)|², the squared magnitude response from How FIR and IIR Filters Shape Magnitude and Phase. That is why the noise in Figure 5 slopes down like the one-pole low-pass that made it.
Equalization (EQ) turns bands of frequencies up or down. A 2016 thesis on example-based audio editing describes a phone recording made “facing away from the sound source” on an unknown EQ setting (Anushiravani 2016). It sounds “muffled or squeaky”, as if passed through a band-pass filter. An example recording of the same kind of sound was made with professional equipment, and the goal is to make the phone recording sound like it.
Section 4 says a filter multiplies the PSD by |H(f)|². Solved for that factor, it gives the gain that turns the phone recording’s PSD into the example’s, held back by a floor β where the phone recording is nearly empty. Figure 8 runs it on four bands, with the phone too loud in the mids as in the thesis’s toy example.
The table gives the four bands of Figure 8 at c = 0.05 and c = 0.5.
| Band (Hz) | 125 | 500 | 1,000 | 4,000 |
|---|---|---|---|---|
| Phone Pin | 1 | 4 | 4 | 0.05 |
| Example Pex | 1 | 1 | 1 | 0.5 |
| M, c = 0.05 | 0.90 | 0.24 | 0.24 | 3.07 |
| M in dB | −0.5 | −6.1 | −6.1 | +4.9 |
| Matched | 0.90 | 0.97 | 0.97 | 0.15 |
| M, c = 0.5 | 0.47 | 0.20 | 0.20 | 0.42 |
| M, no floor | 1 | 0.25 | 0.25 | 10 |
Illustrative band powers; matched is the phone power times M at c = 0.05.
With no floor the 4 kHz gain would be 10, lifting a band that holds mostly noise. A larger β lifts that band less and pulls every gain down, and a very large one fades the output toward silence. The mids match and the empty band rises part way, by design.
It works when the example holds the same kind of sound, speech for speech. Setting M[k] = 1 outside chosen bands matches only those bands. Both recordings must first share a sampling rate (Aliasing and Anti-Aliasing Filters, section 5), or the same bin k means two different frequencies.
The same gain matches background noise, so two scenes cut together sound as if recorded in one place. In two film scenes, one with air-conditioner hiss and the next with a faint band of noise near 3 to 5 kHz, first remove each scene’s own noise with a gain per bin, as in Spectral Subtraction for Denoising. Then match one scene’s background noise to the other’s, and add it back under the cleaned speech at that scene’s own SNR.
This code matches the phone recording to the example.
import numpy as np
from scipy import signal
# phone: the muffled phone recording; example: the studio recording, same rate
FS, NPS, FLOOR_C = 16000, 512, 0.05
f, P_in = signal.welch(phone, fs=FS, nperseg=NPS)
f, P_ex = signal.welch(example, fs=FS, nperseg=NPS)
M = P_ex / (P_in + FLOOR_C * P_in.mean())
f, t, X = signal.stft(phone, fs=FS, nperseg=NPS)
Y = np.sqrt(M)[:, None] * X
t, matched = signal.istft(Y, fs=FS, nperseg=NPS)
M has one value per STFT bin (eq cards in Section 3).FLOOR_C as c (match gain card in Section 5, lines 1 and 2).M per bin (fixed EQ card in Section 5, line 2).signal.stft and signal.istft are taught on The STFT and Spectrograms, section 5.
In practice a PSD reads a noise floor, finds hum, matches the tone of two recordings, or learns the noise that the denoising pages remove.
| Step | How often, on what data | State it keeps | Cost |
|---|---|---|---|
| Periodogram | once per clip, on the whole clip | none | one FFT |
| Welch PSD | once per clip, averaging its segments, on overlapped windowed segments | a running sum of periodograms | one short FFT per segment |
| EQ match | once per recording, offline, on the whole recording | the gain M[k] | one Welch pass per recording, then one multiply per STFT bin |
Rates, state and cost come from the sources in References.
Welch's estimate and the plain periodogram of the same noise; the last line prints each curve's spread relative to its level.
import numpy as np
from scipy import signal
FS, L = 16000, 512
x = np.random.default_rng(0).standard_normal(2 * FS)
f, P_welch = signal.welch(x, fs=FS, nperseg=L)
f1, P_one = signal.periodogram(x, fs=FS)
print(P_welch.std() / P_welch.mean(), P_one.std() / P_one.mean())
| Knob, where it enters | Raise it | Lower it | Default |
|---|---|---|---|
| Segment length L, the FFT length per segment | Finer bins, fewer segments, a noisier curve; L = N is the plain periodogram | More segments and a smoother curve, blurrier bins; close peaks merge once fs/L exceeds their gap | 256 samples (scipy.signal.welch) |
| Segment count K, the average in the Welch card | Spread falls roughly as 1/√K | K = 1 is the plain periodogram, as jumpy as one FFT | Not set directly; it follows from the recording length, L and the overlap |
| Overlap between segments | More segments from the same data; gains shrink as segments repeat each other | At 0, segments are independent but fewer | Half a segment (scipy.signal.welch) |
SciPy documents scipy.signal.welch (v1.11.4). Its other defaults are a periodic Hann window, scaling='density' and average='mean'.
average='median', which ignores a few loud segments.| What you see | Likely cause | What to do |
|---|---|---|
| Levels off by a constant factor | Density (V²/Hz) and spectrum (V²) scaling mixed up | SciPy defaults to scaling='density'; use 'spectrum' only to read a tone’s power |
| Everything off by a factor of 2 (3 dB) | One-sided and two-sided spectra compared | One-sided doubles every bin except 0 and N/2; compare like with like |
| A changing sound blurred into one curve | The PSD assumes the statistics stay put | Use a spectrogram (The STFT and Spectrograms) |
| Bin 0 towers over the rest | A constant offset (DC) in the signal | Detrend first; welch does detrend='constant' by default |
| Still ragged | Too few segments | More data, or 50% overlap |
If a fan’s PSD stays put, can it be learned during the pauses and take that much away from every frame? That is Spectral Subtraction for Denoising.