Enhancement¶
kudio.enhance.spectral
¶
Statistical single-channel speech enhancement, as a set of interchangeable parts.
Every method here is the same three decisions:
- estimate the noise power spectrum, frame by frame;
- from that, estimate the a priori SNR — how much speech is in this bin;
- turn that into a gain between 0 and 1 and multiply the spectrum by it.
trad_enhance fuses all three into one function with no parameters, so there
is no way to ask what any of them contributed. Splitting them apart is what
makes the methods comparable: swap the gain rule and hold the noise estimator
still, and the difference you hear is the gain rule.
What separates them in practice:
============ ===============================================================
specsub Subtract the noise spectrum. Cheapest, and the reason
"musical noise" has a name: isolated surviving bins warble.
multiband Spectral subtraction with a per-band over-subtraction factor.
Colored noise is not equally loud everywhere, and one global
factor over-suppresses where it is quiet.
wiener Gain xi / (1 + xi). Smooth, so much less musical noise, at
the cost of muffling the speech at low SNR.
mmse_stsa Ephraim & Malah (1984): MMSE estimate of the spectral
amplitude, assuming Gaussian coefficients. Keeps more speech
than Wiener at the same suppression.
logmmse Ephraim & Malah (1985): MMSE in the log domain, which is
closer to how loudness is perceived. The usual default.
omlsa logmmse weighted by a speech-presence probability
(Cohen 2001). Suppresses hard where there is probably no
speech at all, gently where there might be.
spectral_gate A threshold mask rather than a statistical estimator: bins
below (noise mean + n sigma) are attenuated. Not principled,
very predictable, and good on steady broadband noise.
============ ===============================================================
The decision-directed a priori SNR (Ephraim & Malah) is shared by
wiener / mmse_stsa / logmmse / omlsa:
with alpha = 0.98: it is the smoothing that removes musical noise, and
also what makes these methods lag a sudden onset by a frame or two.
Two things to know before reaching for any of them.
They are for noisy audio, and they damage clean audio. Every one trades
speech distortion for noise removal. On the test suite's harmonic clip over
white noise, the seven over mcra raise the SNR by +7.8 dB on average at 0 dB
in and +6.1 at 5 dB; at 10 dB the average is +3.8 and one of them already does
harm, and at 20 dB all seven measure worse than doing nothing (−4.1 dB).
compare_enhancers(reference=…) reports delta_snr_db precisely so that
shows up rather than being assumed away.
They need to hear the noise on its own. Noise estimation works from the quiet
parts, so a clip that is wall-to-wall speech gives them almost nothing to
measure. At 5 dB SNR, logmmse gained +7.1 dB on the clip with pauses and
+0.1 dB on one without. If a recording has no pauses, noise='initial' with a
leading second of room tone beats anything adaptive: +9.5 dB against +2.2 for
mcra.
>>> clean = kudio.spectral_enhance(y, sr) # logmmse
>>> clean = kudio.spectral_enhance(y, sr, method='omlsa', noise='quantile')
>>> for r in kudio.compare_enhancers(y, sr, reference=truth):
... print(r.method, r.si_sdr_db)
Classes:
| Name | Description |
|---|---|
Param |
One tunable knob, described well enough to build a control from it. |
Method |
An enhancement method: what it is, and what it lets you turn. |
NoiseTracker |
A noise estimator as state plus one update, not a whole-file pass. |
CustomEnhancer |
Anything that denoises, wrapped so it can be ranked with the rest. |
EnhanceResult |
One run's output, with everything needed to rank it against another. |
EnhanceFolderResult |
What |
Functions:
| Name | Description |
|---|---|
make_noise_tracker |
A stateful tracker for method. |
estimate_noise_psd |
Noise power spectrum for every frame of |
make_gain_rule |
The per-frame gain function for one of |
spectral_enhance |
Enhance y with one of the methods in |
compare_enhancers |
Run several methods over the same clip and measure each one. |
enhance_folder |
Denoise every |
Attributes:
| Name | Type | Description |
|---|---|---|
CUSTOM_NOISE |
What a custom enhancer's |
|
METHODS |
Dict[str, Method]
|
Every gain rule by name. Each one describes its own parameters -- range, |
NOISE_ESTIMATORS |
Dict[str, str]
|
The four noise estimators by name, each with a sentence on what it assumes |
CUSTOM_NOISE
module-attribute
¶
What a custom enhancer's noise column says: it brought its own, and
naming one of kudio's estimators beside it would be a lie.
METHODS
module-attribute
¶
METHODS: Dict[str, Method] = {
m.name: m
for m in (
Method(
"specsub",
"Spectral subtraction",
"Subtract the noise spectrum. Cheapest, and the origin of musical noise: isolated surviving bins warble between frames.",
(_OVERSUB, _FLOOR, _GAIN_FLOOR),
"Boll 1979",
),
Method(
"multiband",
"Multi-band spectral subtraction",
"Spectral subtraction with a per-band over-subtraction factor set from each band's own SNR — colored noise is not equally loud everywhere, and one global factor over-suppresses where it is not.",
(
Param(
"bands",
"bands",
6,
2,
32,
1,
0,
help="Linearly spaced. More bands track colored noise more closely and are noisier to estimate.",
),
_FLOOR,
_GAIN_FLOOR,
),
"Kamath & Loizou 2002",
),
Method(
"wiener",
"Wiener (decision-directed)",
"Gain xi/(1+xi). Smooth, so far less musical noise than subtraction — it muffles the speech at low SNR instead.",
(_ALPHA, _GAIN_FLOOR),
),
Method(
"mmse_stsa",
"MMSE-STSA",
"MMSE estimate of the spectral amplitude under a Gaussian model. Keeps more speech than Wiener at the same suppression.",
(_ALPHA, _GAIN_FLOOR),
"Ephraim & Malah 1984",
),
Method(
"logmmse",
"log-MMSE",
"MMSE in the log-spectral domain, which is closer to how loudness is perceived. The usual default, and hard to beat without a model.",
(_ALPHA, _GAIN_FLOOR),
"Ephraim & Malah 1985",
),
Method(
"omlsa",
"OM-LSA",
"log-MMSE weighted by the probability that there is any speech in the bin at all: suppress hard where there is probably none, gently where there might be.",
(
_ALPHA,
_GAIN_FLOOR,
Param(
"q",
"P(no speech)",
0.3,
0.01,
0.95,
0.05,
2,
help="A priori probability that a bin holds no speech. Higher suppresses more and risks clipping onsets.",
),
),
"Cohen 2001",
),
Method(
"spectral_gate",
"Spectral gate",
"A threshold mask, not a statistical estimator: attenuate every bin below (noise mean + n sigma), then smooth the mask so the edges do not click. Unprincipled, very predictable.",
(
Param(
"n_std_thresh",
"threshold",
1.5,
0.0,
6.0,
0.1,
1,
" sigma",
help="How far above the noise floor a bin has to be to be kept.",
),
Param(
"prop_decrease",
"strength",
1.0,
0.0,
1.0,
0.05,
2,
help="1.0 applies the mask in full; lower mixes the original back in, which trades noise for artefacts.",
),
Param(
"freq_smooth_hz",
"smooth (freq)",
500.0,
0.0,
4000.0,
50.0,
0,
" Hz",
help="Blur the mask across frequency. Unsmoothed masks are what musical noise sounds like.",
),
Param(
"time_smooth_ms",
"smooth (time)",
50.0,
0.0,
500.0,
10.0,
0,
" ms",
help="Blur the mask across time.",
),
),
"Spectral gating, after noisereduce",
),
)
}
Every gain rule by name. Each one describes its own parameters -- range, default, step, unit and a sentence on what turning it costs -- so a GUI or a command line can build its controls from here instead of restating them.
NOISE_ESTIMATORS
module-attribute
¶
NOISE_ESTIMATORS: Dict[str, str] = {
"mcra": "Minima-controlled recursive averaging: tracks the noise while speech is present, by watching each bin's running minimum. The general-purpose choice, and what trad_enhance uses. It starts from the opening frame, so after a quiet opening it can leave noise in some bins for the first second or two.",
"quantile": "A low quantile of each bin over the whole file, scaled to the mean of steady noise (the 15th percentile of noise power is 0.16 of its mean, 7.9 dB low). Offline only — it needs to see the future — and usually the most accurate of these when the noise is steady and the file is not one continuous shout.",
"initial": "Average of the first few frames, assumed to be room tone. Exact when that is true and badly wrong when it is not.",
"minimum": "Running minimum over a sliding window, bias-compensated. Follows non-stationary noise faster than MCRA and is noisier.",
}
The four noise estimators by name, each with a sentence on what it assumes
about the recording. Pick one with spectral_enhance(..., noise=name).
RECURSIVE_METHODS
module-attribute
¶
Methods whose gain is a function of (xi, gamma) alone, and which therefore
run inside DecisionDirected. The other three look at the whole
spectrum at once and are handled separately.
Param
dataclass
¶
Param(
name: str,
label: str,
default: float,
low: float,
high: float,
step: float = 0.01,
decimals: int = 2,
suffix: str = "",
help: str = "",
)
One tunable knob, described well enough to build a control from it.
Method
dataclass
¶
Method(
name: str,
label: str,
summary: str,
params: Tuple[Param, ...] = (),
reference: str = "",
)
NoiseTracker
¶
A noise estimator as state plus one update, not a whole-file pass.
Written as a batch function, a tracker cannot be used on a live stream; written twice — once batch, once streaming — the two drift. So the update rule lives here once, and the offline path is a loop over it.
quantile is the exception and is deliberately not a tracker: it needs
to see the future.
Methods:
| Name | Description |
|---|---|
update |
One frame in, that frame's noise PSD out. |
Attributes:
| Name | Type | Description |
|---|---|---|
streamable |
can this run on audio that has not finished arriving? |
streamable
class-attribute
instance-attribute
¶
can this run on audio that has not finished arriving?
DecisionDirected
¶
The a priori SNR estimator of Ephraim & Malah, as state plus an update.
gain_fn(xi, gamma) -> gain is the only thing that changes between
Wiener, MMSE-STSA, log-MMSE and OM-LSA; everything around it — the a
posteriori SNR, the smoothing, the feedback of the previous frame's
estimate — is identical, and is why they can be compared at all. It is also
already recursive, which is what makes those four streamable for free.
Source code in kudio/enhance/spectral.py
CustomEnhancer
dataclass
¶
Anything that denoises, wrapped so it can be ranked with the rest.
The statistical methods are named by a string; a trained model, a wrapper around someone else's library, or a one-off experiment is a function. Both belong in the same table — "is the model actually better than log-MMSE" is the question people have, and answering it by running two tools and comparing two printouts is how it goes unanswered.
>>> from kudio_enhance.inference import Enhancer
>>> model = Enhancer.load("runs/exp1")
>>> kudio.compare_enhancers(y, sr, ['logmmse', ('model', model.enhance)])
EnhanceResult
dataclass
¶
EnhanceResult(
method: str,
audio: ndarray,
seconds: float,
lufs: float,
noise_floor_db: float,
noise: str = "mcra",
snr_db: Optional[float] = None,
si_sdr_db: Optional[float] = None,
delta_snr_db: Optional[float] = None,
pesq: Optional[float] = None,
stoi: Optional[float] = None,
error: Optional[str] = None,
)
One run's output, with everything needed to rank it against another.
A run is a (method, noise estimator) pair, not just a method: the two are separate choices of comparable size, so collapsing them into one name would hide half of what produced the result.
The reference metrics are None when no clean reference was given — the
normal case for a real recording, and the reason noise_floor_db and
lufs are always filled in.
Attributes:
| Name | Type | Description |
|---|---|---|
label |
str
|
|
EnhanceFolderResult
dataclass
¶
EnhanceFolderResult(
written: int,
total: int,
outputs: List[Path],
failed: List[Tuple[Path, str]],
floors: Tuple[Tuple[Any, float, float], ...] = (),
)
Bases: ConvertResult
What enhance_folder did.
A kudio.ConvertResult — same written / total / failed — plus
floors, the before-and-after noise floor of each file when it was asked
for. Subclassing rather than inventing a parallel type keeps one shape for
"I processed a folder".
Methods:
| Name | Description |
|---|---|
mean_reduction_db |
Average drop in noise floor, or |
Attributes:
| Name | Type | Description |
|---|---|---|
floors |
Tuple[Tuple[Any, float, float], ...]
|
|
floors
class-attribute
instance-attribute
¶
(path, before_dbfs, after_dbfs) per file; empty unless report=True
mean_reduction_db
¶
Average drop in noise floor, or None without a report.
Source code in kudio/enhance/spectral.py
make_noise_tracker
¶
make_noise_tracker(
method: str = "mcra", **kwargs
) -> NoiseTracker
A stateful tracker for method.
Raises:
| Type | Description |
|---|---|
FeatureError
|
for |
Source code in kudio/enhance/spectral.py
estimate_noise_psd
¶
Noise power spectrum for every frame of power (frames, bins).
See NOISE_ESTIMATORS for what each one assumes. It is as large a
lever as the gain rule, because a gain rule can only be as good as the
noise it is told about.
Source code in kudio/enhance/spectral.py
make_gain_rule
¶
make_gain_rule(
method: str, settings: Optional[dict] = None
) -> Callable[[ndarray, ndarray], ndarray]
The per-frame gain function for one of RECURSIVE_METHODS.
Exposed so the streaming enhancer resolves a method name the same way the offline one does, instead of keeping its own mapping to fall out of step.
Source code in kudio/enhance/spectral.py
spectral_enhance
¶
spectral_enhance(
y: ndarray,
sr: int,
method: str = "logmmse",
*,
noise: str = "mcra",
n_fft: int = 512,
hop_length: Optional[int] = None,
window: str = "hann",
noise_kwargs: Optional[dict] = None,
**params,
) -> ndarray
Enhance y with one of the methods in METHODS.
>>> clean = kudio.spectral_enhance(y, sr) # logmmse
>>> clean = kudio.spectral_enhance(y, sr, method='omlsa', q=0.4)
>>> clean = kudio.spectral_enhance(y, sr, noise='quantile')
The output is the same length as the input, so it lines up sample for sample with what it came from.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
method
|
str
|
see |
'logmmse'
|
noise
|
str
|
see |
'mcra'
|
params
|
method parameters, defaulted from |
{}
|
Raises:
| Type | Description |
|---|---|
FeatureError
|
on an unknown method, non-mono audio, samples that are not finite, or a clip too short to frame. |
Source code in kudio/enhance/spectral.py
590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 | |
compare_enhancers
¶
compare_enhancers(
y: ndarray,
sr: int,
methods: Optional[Sequence[str]] = None,
*,
noises: Optional[Sequence[str]] = None,
reference: Optional[ndarray] = None,
metrics: bool = True,
pesq_mode: str = "auto",
pesq_scale: str = "lqo",
**common,
) -> List[EnhanceResult]
Run several methods over the same clip and measure each one.
Both axes can be swept. methods chooses gain rules, noises chooses
noise estimators, and giving both compares the full grid:
>>> kudio.compare_enhancers(y, sr, ['logmmse'], noises=list(NOISE_ESTIMATORS))
>>> kudio.compare_enhancers(y, sr, noises=['mcra', 'quantile']) # 2 x all
That second axis is not decoration: the estimator moves the result as
much as the gain rule does, so a comparison that sweeps only methods is
looking at half the problem. Which of the two dominates depends on the
clip, which is the reason to measure rather than assume.
With a reference you get SNR, SI-SDR and (where installed) PESQ / STOI,
plus delta_snr_db: the improvement over the untouched input, which is
the number that actually answers "did this help". Without one you get the
residual noise floor and the loudness, which is what a real recording
allows.
A run that raises is reported with error set rather than taking the whole
comparison down — a comparison exists to find out which ones work.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
common
|
passed to every run ( |
{}
|
Raises:
| Type | Description |
|---|---|
FeatureError
|
on an unknown method or estimator, on passing
|
Source code in kudio/enhance/spectral.py
777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 | |
enhance_folder
¶
enhance_folder(
src: Any,
dst: PathLike,
method: Union[
str, Callable, Tuple[str, Callable]
] = "logmmse",
*,
noise: str = "mcra",
n_fft: int = 512,
hop_length: Optional[int] = None,
window: str = "hann",
noise_kwargs: Optional[dict] = None,
sr: Optional[int] = None,
subtype: str = "PCM_16",
overwrite: bool = False,
progress: Optional[
Callable[[int, int, Path], None]
] = None,
on_error: str = "collect",
report: bool = False,
**params,
) -> EnhanceFolderResult
Denoise every .wav file under src into dst.
The counterpart of kudio.convert_folder, and it mirrors the input's
directory structure the same way -- a folder from itself, a list or a
.txt list from the deepest folder its files share -- so two files with
the same name in different subfolders do not collide.
>>> result = kudio.enhance_folder("noisy/", "clean/", 'logmmse')
>>> print(result)
412/412 written, 0 failed
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
method
|
Union[str, Callable, Tuple[str, Callable]]
|
a name from |
'logmmse'
|
n_fft
|
int
|
as in |
512
|
hop_length
|
Optional[int]
|
as in |
None
|
window
|
str
|
as in |
'hann'
|
noise_kwargs
|
Optional[dict]
|
as in |
None
|
report
|
bool
|
also measure each file's noise floor before and after, and return them on the result -- worth it when the question is "did this help" rather than "run it". |
False
|
params
|
method parameters, defaulted from |
{}
|
Returns:
| Type | Description |
|---|---|
EnhanceFolderResult
|
Source code in kudio/enhance/spectral.py
923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 | |
kudio.enhance.streaming
¶
Enhancement for audio that has not finished arriving.
spectral_enhance needs the whole clip. That is fine for a file and useless
for a microphone: you cannot monitor a take through a denoiser that only runs
once the take is over.
Everything needed was already recursive. The decision-directed a priori SNR
feeds the previous frame's estimate into this one, and MCRA updates its noise
estimate a frame at a time — both are state plus an update, and
StreamEnhancer is the overlap-add machinery around them. It shares
those objects with the offline path rather than reimplementing the maths, so
the two cannot give different answers to the same question.
>>> enhancer = kudio.StreamEnhancer(sr=16000, method='logmmse')
>>> for block in microphone:
... play(enhancer.process(block))
>>> tail = enhancer.flush()
What it costs. One analysis frame of latency —
latency_seconds, 32 ms at the default 512-point frame
and 16 kHz. And it is not free of the offline version's judgement: the noise
estimator has only heard what has arrived so far, so the first second is
always the worst second.
What cannot stream. quantile is a statistic over the whole file;
spectral_gate, specsub and multiband in kudio look at the spectrogram as
a block. STREAMABLE_METHODS is the list that does, and asking for
anything else raises rather than quietly giving you something different from
what the same name does offline.
Classes:
| Name | Description |
|---|---|
StreamEnhancer |
Denoise a stream block by block, with a one-frame delay. |
Attributes:
| Name | Type | Description |
|---|---|---|
STREAMABLE_METHODS |
The methods whose gain depends only on this frame's (xi, gamma). |
STREAMABLE_METHODS
module-attribute
¶
STREAMABLE_METHODS = RECURSIVE_METHODS
The methods whose gain depends only on this frame's (xi, gamma).
StreamEnhancer
¶
StreamEnhancer(
sr: int,
method: str = "logmmse",
*,
noise: str = "mcra",
n_fft: int = 512,
hop_length: Optional[int] = None,
window: str = "hann",
noise_kwargs: Optional[dict] = None,
**params,
)
Denoise a stream block by block, with a one-frame delay.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
sr
|
int
|
sample rate. |
required |
method
|
str
|
one of |
'logmmse'
|
noise
|
str
|
|
'mcra'
|
n_fft
|
int
|
analysis frame; also the latency. |
512
|
hop_length
|
Optional[int]
|
defaults to |
None
|
params
|
method parameters, defaulted from |
{}
|
Methods:
| Name | Description |
|---|---|
reset |
Forget every buffer and estimate, keeping the configuration. |
process |
Feed a block in; get the enhanced audio that is finished back. |
flush |
Everything still in flight, once no more audio is coming. |
Attributes:
| Name | Type | Description |
|---|---|---|
latency_seconds |
float
|
How far behind the output runs: one analysis frame. |
Source code in kudio/enhance/streaming.py
latency_seconds
property
¶
How far behind the output runs: one analysis frame.
reset
¶
Forget every buffer and estimate, keeping the configuration.
Source code in kudio/enhance/streaming.py
process
¶
Feed a block in; get the enhanced audio that is finished back.
The returned block is not the same length as the input — nothing
comes out until a whole frame has arrived, and then it emerges in
hop-sized pieces. Concatenate what comes back and call flush
at the end; sample i of that concatenation lines up with sample i
of the input.
Source code in kudio/enhance/streaming.py
flush
¶
Everything still in flight, once no more audio is coming.
Pads the last partial frame with zeros so the tail is processed rather than dropped — a stream that quietly loses its final 30 ms is the kind of bug that only shows up on short takes — and stops where the input stopped, so the whole output is as long as the whole input.
Source code in kudio/enhance/streaming.py
kudio.enhance.traditional
¶
Traditional (non-DNN) speech enhancement.
trad_enhance implements spectral subtraction / Wiener filtering with MCRA
noise estimation (equation numbers reference Loizou, Speech Enhancement:
Theory and Practice). wavelet_low_pass_filter is a soft-threshold wavelet
denoiser.
Functions:
| Name | Description |
|---|---|
wavelet_low_pass_filter |
Soft-threshold the detail coefficients of a wavelet decomposition. |
trad_enhance |
Enhance waveform y using spectral subtraction with MCRA noise |
INIT
¶
First-frame initialization of the noise-estimation state.
Methods:
| Name | Description |
|---|---|
weight |
Weighted spectral average. |
com_min_track |
Continuous minimal tracking. |
mcra |
MCRA algorithm. |
mcra2 |
MCRA-2 algorithm with a frequency-dependent delta [9.60]. |
Source code in kudio/enhance/traditional.py
weight
¶
com_min_track
¶
Continuous minimal tracking.
Source code in kudio/enhance/traditional.py
mcra
¶
MCRA algorithm.
Source code in kudio/enhance/traditional.py
mcra2
¶
MCRA-2 algorithm with a frequency-dependent delta [9.60].
Source code in kudio/enhance/traditional.py
EST
¶
Per-frame update of the noise-estimation state.
Methods:
| Name | Description |
|---|---|
weight |
Weighted spectral average [9.30]. |
com_min_track |
Continuous minimal tracking [9.24-9.25]. |
mcra |
MCRA [9.53-9.59]. |
mcra2 |
MCRA-2 [9.61, 9.25, 9.53-9.59]. |
Source code in kudio/enhance/traditional.py
weight
¶
Weighted spectral average [9.30].
Source code in kudio/enhance/traditional.py
com_min_track
¶
Continuous minimal tracking [9.24-9.25].
Source code in kudio/enhance/traditional.py
mcra
¶
MCRA [9.53-9.59].
Source code in kudio/enhance/traditional.py
mcra2
¶
MCRA-2 [9.61, 9.25, 9.53-9.59].
Source code in kudio/enhance/traditional.py
wavelet_low_pass_filter
¶
Soft-threshold the detail coefficients of a wavelet decomposition.
The threshold is thres times the clip's peak, max |y|. The result
has the input's length; a silent clip comes back as it went in.
Source code in kudio/enhance/traditional.py
trad_enhance
¶
Enhance waveform y using spectral subtraction with MCRA noise estimation and a Wiener-style gain function.
Returns float32 of the same length as y. The last partial frame, under 256 samples, has no frame of its own and comes back silent.
Raises:
| Type | Description |
|---|---|
FeatureError
|
for anything but mono audio, or a clip shorter than one 512-sample frame, which would come back as silence. |
Source code in kudio/enhance/traditional.py
43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 | |