← All tutorials

The Wiener Filter

A gain per bin, set by speech.

On this page

1 The gain from two PSDs 2 The gain across frequency 3 Why it is the best guess 4 Wiener next to subtraction 5 Guessing the SNR, and smoothing it 6 Undoing a known filter 7 In practice 8 When the Wiener gain goes wrong 9 References

The Wiener filter cleans a noisy spectrogram, the short-time Fourier transform (STFT) of a clip, by multiplying each bin by a gain between 0 and 1. The gain makes the cleaned value as close as possible to the clean speech on average (the smallest mean squared error), and it comes out as the share of the bin’s power that is speech. That share is unknown and is guessed frame by frame.

noisy STFTnoise fromthe pausesspeech vsnoise guessWiener gainper binkeep thenoisy phaseinverse STFT
Figure 1. The method in order, 1 to 6. Only 3 and 4 differ from spectral subtraction.

1 The gain from two PSDs

As in Spectral Subtraction for Denoising, each STFT frame and bin holds a noisy value, speech plus noise, and the noise power spectral density (PSD) PD, its power per hertz, is learned in the pauses. For example, a bin with speech power 3 and noise power 1 is three-quarters speech. The Wiener gain is that share, 0.75. It scales the bin’s amplitude by 0.75, so about 0.56 of its power is left.

What one noisy bin holds.
Y = S + D
Y is the noisy value in one frame and bin, S its speech part and D its noise part. Speech and noise are unrelated, so on average their powers add: the example bin holds power 3 + 1 = 4. Every gain on this page acts on Y.
The Wiener gain for one bin.
G = PSPS + PD
PS and PD are the speech and noise power in this bin, and G is the speech share of the power, between 0 and 1. G multiplies the bin’s amplitude, so the power left is G²: the example bin gets G = 34 and keeps 916 of its power. A bin that is mostly noise is turned down.
ξ = PSPD
ξ, the a priori SNR, is how much the speech outweighs the noise in this bin; the example has ξ = 3.
G = ξ1 + ξ
Divide top and bottom by PD. A large ξ gives G near 1 (keep the bin), a small ξ gives G near 0, and ξ = 1 (0 dB) gives G = 12. It builds on the PSD, and Sections 2 to 6 all use it.

Figure 2 also draws power subtraction, the rule of Spectral Subtraction for Denoising that takes the noise power off the noisy power.

0 dB

Figure 2. The noisy bin’s speech and noise, then what is left. 1Its Wiener gain.

G multiplies the amplitude, so the power left is G². Power subtraction keeps more at the same SNR. At ξ = 1 Wiener’s amplitude gain is 0.5 and power subtraction’s 0.707; at ξ = 10 they are 0.909 and 0.953.

Power subtraction’s share at the average noisy power.
1 − PD|Y|² ≈ ξ1 + ξ = G
Power subtraction keeps the power share 1 − PD|Y|². It equals G exactly when |Y|² equals its average, (1 + ξ) PD, so its amplitude gain is √G, above Wiener’s G. It builds on the Wiener gain card and is used by the comparison in Section 4.

2 The gain across frequency

Each bin gets its own gain, so across frequency the gain follows the voice: high where a harmonic, one of the evenly spaced peaks of a voiced sound, stands above the noise, low in between.

speech PSD and noise PSD−20020dBspeechnoiseWiener gain G01k2k3k4 kHz01
Figure 3. 1Speech PSD. 2Noise PSD, flat. 3The gain, high on harmonics, low between.

A gain per frequency is a filter’s magnitude response (How FIR and IIR Filters Shape Magnitude and Phase), with zero phase here, since the noisy phase is kept.

3 Why it is the best guess

A Bayesian estimate per bin treats the speech value S, before Y is seen, as a Gaussian (a bell curve) centered on 0 with power PS, the prior, and the noise as another Gaussian with power PD. Once Y is seen, Bayes’ rule gives the posterior, the bell of values S could still have; the Gaussian conditioning example is on Probability and Bayes Rule.

speech 3 times noise, ξ = 3, G = 0.750YG·Ypriorposteriorspeech a third of noise, ξ = 1/3, G = 0.250YG·Y
Figure 4. 1The prior. 2The posterior given Y; its center G·Y is the Wiener output.
The best guess in mean squared error.
Ŝ = E[ S | Y ]
A hat marks an estimate, so Ŝ is the guess of S. E[ · ] is an average (an expectation) and the bar reads “given” (not a magnitude here): the best guess in mean squared error is the average of S over the posterior.
E[ S | Y ] = G · Y
With Gaussian speech and noise that average is Y scaled by the same Wiener gain G, so it shrinks toward 0 more when the noise is relatively bigger. The prior pulls the guess toward 0 and the measurement pulls it toward Y. Each pull is weighted by the other one’s uncertainty. The measurement gets the weight PSPS + PD, which is G. A wide prior (large PS) knows little about S, so Y counts more. The prior gets the rest, 1 − G, because loud noise (large PD) makes Y less reliable. So the guess sits the share G of the way from 0 to Y. With speech 3 times the noise, G = 0.75 and the guess is close to Y. With speech a third of the noise, G = 0.25 and it is close to 0 (Figure 4). It builds on Bayes’ rule and is used by Section 4, where ξ has to be guessed.

So under the Gaussian assumption no other per-bin rule has a smaller mean squared error; Wang and Chen (2018) call the Wiener filter the optimal estimator of speech in the power spectrum.

4 Wiener next to subtraction

PS is unknown, so ξ has to be guessed, and the simplest guess uses this frame alone. It reads γ, this frame’s bin power over the noise estimate. On average γ is 1 + ξ, since the noise alone gives the 1, so γ − 1 is the guess, written ξ because the hat marks a guess. With it every rule becomes a curve in γ. Magnitude subtraction takes the noise amplitude, the square root of PD, off the noisy amplitude, so it turns bins down harder than power subtraction.

at 3 dB: 0.71, 0.50, 0.29−50510152000.51γ in dBgain
Figure 5. 1Power subtraction. 2Wiener. 3Magnitude subtraction.
The one-frame SNR guess and the gain it gives.
γ = |Y|²PD
γ, the a posteriori SNR, is this frame’s bin power over the noise estimate.
ξ = max(γ − 1, 0)
The noisy power minus the noise, over the noise, never below zero.
G = 1 − 1γ
Put into G = ξ1 + ξ, the guess gives this gain for γ ≥ 1. It is exactly the square of power subtraction’s gain, so the same noise bins survive, only dimmer, and the gain shape alone does not remove musical noise, the short random tones left by noise peaks, heard as twinkling. It builds on the gain of Section 1 and is used by the smoothed guess in Section 5.

5 Guessing the SNR, and smoothing it

The one-frame guess jumps with every noise spike. Ephraim and Malah (1984) steadied it by mixing in last frame’s cleaned output, the decision-directed estimate, written here in the form Chinaev and Haeb-Umbach (2016) give. For example, take a = 0.98, last frame’s cleaned power at 4 times the noise, and a noise spike of γ = 10 this frame, so this frame alone says 9. The table compares the two guesses.

GuessWeight on last frame’s 4Weight on this frame’s 9ξ
One-frame019
Decision-directed0.980.024.1

Illustrative values, from the decision-directed card at a = 0.98.

The decision-directed SNR guess for frame m.
ξm = a · |Ŝm−1|²PD
The first term trusts last frame's cleaned power over the noise; m − 1 is the previous frame and a is a weight near 1 (0.98 in the decision-directed paper). Speech changes slowly from frame to frame, so that cleaned estimate is a steadier guide than one noisy frame.
+ (1 − a) · max(γm − 1, 0)
Add a little of this frame’s own evidence, never below zero; with a = 0.98 a lone noise spike adds only 2% of its γ − 1 for that frame. It is the one-pole smoother from How FIR and IIR Filters Shape Magnitude and Phase, run across frames.

Noise suppressors in phones and hearing aids face this trade-off on every call. A weight close to 1 removes the twinkling in the pauses, but it is slow to follow the start of each word (Nahma et al. 2019). In Figure 6 a word start lags while the guess sits more than 10 dB under the true SNR; red marks those frames.

0.98

Figure 6. Toy speech. 1Wiener output. 2SNR guess; dashed, the true SNR.

powersubtractionone-frameguesssmoothedguess0%20%40%noise bins left in the pauses35%27%2%
Figure 7. Pauses of Figure 6. 1Power subtraction. 2, 3Wiener.

6 Undoing a known filter

Sometimes noise is not the only damage. A lavalier microphone clipped under a shirt muffles the voice, and if that muffling was measured, the filter’s frequency response H is known.

The muffled, noisy recording.
y = s ∗ h + d
y is the recording, s the clean speech, h the muffling filter, ∗ convolution and d the noise.
Y = H · S + D
In each frequency bin convolution becomes a product, so the speech S is scaled by H and the noise D adds. Dividing by H would undo the filter, except where H is nearly 0. If the shirt keeps |H| = 0.01 at 8 kHz, dividing boosts that band 100 times, noise included. It builds on convolution and is used by the Wiener deconvolution gain card.

The Wiener deconvolution gain W divides by H only where the voice still stands above the noise; the card after Figure 8 writes it out. Four illustrative values of H show the trade-off, with ξ = 100 (20 dB) in every bin.

Band|H|Share of the voice restored
500 Hz10.99
2 kHz0.50.96
4 kHz0.10.5
8 kHz0.010.01

Illustrative values; the share is how much of the voice comes back.

plain inverse 1/|H|deconvolution gain W110100500 Hz|H| 12 kHz|H| 0.54 kHz|H| 0.18 kHz|H| 0.01share restored, W · H10
Figure 8. Illustrative H, ξ = 20 dB. 1At 8 kHz, no 100 times boost. 2That band stays erased.
The Wiener deconvolution gain W for one bin.
W = H* PS|H|² PS + PD
H* is H with the sign of its phase flipped, which undoes the filter’s phase shift. PS and PD are the speech and noise PSDs of Section 1. W is complex, unlike the real gain G. The thesis writes the PSDs Px and Pv (Anushiravani 2016).
W = 1H · ξ|H|²ξ|H|² + 1
The same gain with ξ = PSPD, the SNR of Section 1, top and bottom divided by PD: the plain inverse times a Wiener gain whose SNR is ξ|H|², the SNR left after the muffling. It is near 1 where ξ|H|² is well above 1 and near 0 below; at 8 kHz in the table ξ|H|² = 0.01. With H = 1 it is ξ1 + ξ again.
What comes out of one bin.
Ŝ = W · Y
Ŝ estimates the clean speech; its speech part is W H S, so kept bands come back and erased bands stay erased. Why a room cannot be undone this way is on the Reverberation and Dereverberation page.
20 dB

Figure 9. Gain against |H|, plain inverse dashed. 1It turns where the SNR left is 1.

Raising ξ lets the gain follow 1/|H| further down; lowering it makes the gain turn earlier. ξ has no default; it is measured per recording, from PS and PD.

This code undoes the measured muffling of the lavalier recording.

import numpy as np

# y: the lavalier recording; h: its measured muffling; xi: P_S / P_D per bin
n = len(y) + len(h) - 1
Y, H = np.fft.rfft(y, n), np.fft.rfft(h, n)
W = np.conj(H) / (np.abs(H) ** 2 + 1 / xi)
S_hat = W * Y
s_hat = np.fft.irfft(S_hat, n)[:len(y)]
  1. 1FFTs long enough that the filter does not wrap around, then the gain W, line 1 of the deconvolution gain card with top and bottom divided by PS.
  2. 2The estimate Ŝ = W · Y (the output card).
  3. 3The inverse FFT, trimmed to the recording’s length.

NumPy documents numpy.fft.

7 In practice

StepHow often, on what dataState it keepsCost
Noise estimateas in spectral subtraction, on noise-only framesthe noise PSD per binone mean per bin
SNR guess, decision-directedonce per frame and bin, on this frame and the last clean estimatethe previous frame’s clean estimatea few multiplies per bin
Wiener gainonce per frame and bin, on the SNR guessnone beyond the SNR guessone divide per bin, plus the STFT
Wiener deconvolutiononce per recording, offline, on the whole recordingnonetwo FFTs of the recording length, one multiply per bin, one inverse FFT

Rates, state and cost come from the sources in References.

The Wiener gain with the one-frame SNR guess, on the toy recording from Spectral Subtraction for Denoising.

import numpy as np
import librosa

SR, N_FFT, HOP = 8000, 256, 128
t = np.arange(2 * SR) / SR
voice = np.sin(2 * np.pi * 440 * t) * (t > 0.5)      # 440 Hz tone from 0.5 s
y = voice + 0.3 * np.random.default_rng(0).standard_normal(t.size)
Y = librosa.stft(y, n_fft=N_FFT, hop_length=HOP)     # (129, frames)
P_D = np.mean(np.abs(Y[:, 1:11]) ** 2, axis=1, keepdims=True)

gamma = np.abs(Y) ** 2 / P_D
xi = np.maximum(gamma - 1, 1e-3)                     # floor at -30 dB
G = xi / (1 + xi)
s_hat = librosa.istft(G * Y, hop_length=HOP, length=len(y))
  1. 1The toy recording and noise estimate from the subtraction page.
  2. 2γ for every bin and frame (eq card in Section 4, line 1).
  3. 3The one-frame guess ξ, line 2 of the eq card in Section 4, floored at 0.001, which is −30 dB.
  4. 4The Wiener gain (eq card in Section 1, line 3).
  5. 5Applies the gain and rebuilds the audio (marker 6 in Figure 1).

The decision-directed guess as a function that runs one frame at a time, with a = 0.98.

import numpy as np

def dd_wiener(Y, P_D, A=0.98, XI_MIN=1e-3):
    S_hat = np.zeros_like(Y)
    prev = np.zeros(Y.shape[0])                      # last frame's cleaned power
    for m in range(Y.shape[1]):
        gamma = np.abs(Y[:, m]) ** 2 / P_D
        xi = A * prev / P_D + (1 - A) * np.maximum(gamma - 1, 0)
        xi = np.maximum(xi, XI_MIN)
        G = xi / (1 + xi)
        S_hat[:, m] = G * Y[:, m]
        prev = np.abs(S_hat[:, m]) ** 2
    return S_hat
  1. 1The smoothing weight a = 0.98 and the −30 dB floor.
  2. 2The output and last frame’s cleaned power, which starts at zero.
  3. 3One frame at a time, because each guess needs the frame before.
  4. 4γ for this frame.
  5. 5The decision-directed guess, both lines of the eq card in Section 5.
  6. 6The floor on ξ.
  7. 7The Wiener gain from the guess.
  8. 8Applies the gain and keeps this frame’s cleaned power for the next frame.

The dd_wiener function, run on the toy recording of the first code block.

# Y, P_D, HOP and y come from the first code block
S_hat = dd_wiener(Y, P_D[:, 0])                      # one noise value per bin
s_hat = librosa.istft(S_hat, hop_length=HOP, length=len(y))
  1. 1The decision-directed guess from dd_wiener, with PD as one value per bin.
  2. 2Rebuilds the audio by inverse STFT.
Knob, where it entersRaise itLower itDefault
Smoothing a, the weight on last frame in the decision-directed cardSteadier ξ and less musical noise, but onsets lag, so the first frames of a syllable come out quieter; a = 1 never updatesFollows the one-frame guess; a = 0 is that guess0.98 (References)
Floor on ξ, the lowest SNR guess allowed (XI_MIN)Fewer holes and less musical noise, more noise left everywhere; at 0 dB every bin keeps at least half its amplitudeDeeper gaps; with no floor a bin’s gain is 0 whenever its guess is 0, as the one-frame guess is for γ ≤ 1No standard value; Loizou’s logmmse.m uses −25 dB, the code here −30 dB

librosa documents the inverse at librosa.istft.

7.1 Gotchas

8 When the Wiener gain goes wrong

What you seeLikely causeWhat to do
Every bin a little wrongThe noise estimate is off, so ξ is off everywhereRe-estimate PD whenever the noise changes
Twinkling dots (musical noise)ξ guessed frame by frame jumps with the noiseDecision-directed smoothing (Figure 6)
Onsets smeared or echoeya too close to 1 lags behind sudden changesMake a slightly smaller (Figure 6)
Quiet speech goes missingWeak speech in low-SNR bins is turned down with the noiseAccept it, or raise the floor on ξ

Modern denoisers train a network to predict a gain like this per bin, called a mask; the ideal ratio mask of supervised speech separation (References) is the speech share of the power, usually square-rooted; without the root, and with expected powers, it equals the Wiener gain. What does such a model read instead of the raw spectrum? MFCC and Other Audio Features.

9 References