Also known as: adaptive smoothing, soft-error handling, amplitude smoothing
MBE adaptive smoothing is the IMBE decoder’s soft-error handling stage: using a running estimate of the channel error rate, it tames the amplitude spikes and voicing glitches that survive forward error correction on a weak signal, and mutes frames the channel has corrupted beyond rescue.1 Its defining property is restraint — on a clean channel it changes nothing at all, only advancing its trackers, so well-decoded audio is bit-identical with and without the stage.
The error-rate recursion
The stage keeps a single scalar, a leaky-integrator estimate of the channel error rate driven by how many bits the FEC had to correct each frame:
er = 0.95·er_prev + 0.000365·correctedBits
This is the JMBE/OP25 recursion exactly. A frame is declared hopeless and muted when
er > SmoothMuteErrorRate = 0.0875; at the IMBE FEC budget (~15 correctable bits per frame) that
threshold requires a sustained ~12+ corrected bits per frame — a channel so bad the audio would be
unintelligible anyway. UpdateErrorRate in internal/voice/mbe/smoothing.go advances this
estimate on every frame, on every code path, so the running rate is uniform regardless of what
else the decoder does.
The clean-channel guarantee
Before touching any model parameter, Smooth checks a clean-frame test: if er ≤ 0.005 and the
current frame’s corrected-bit count is ≤ 6, it advances only its local-energy tracker and returns,
leaving the amplitudes M and the voicing decisions Vl exactly as decoded. This is the
guarantee that adaptive smoothing never regresses good audio — a clean call decodes identically
whether or not the stage is present, which also makes the stage safe to run unconditionally.
Scale-free thresholds
The reference decoders express their amplitude and voicing thresholds as fixed absolute constants tied to their internal amplitude scale. GopherTrunk’s linear amplitudes live on a different scale, so the two parameter-cleanup rules are expressed relative to a running local-energy estimate instead, which tracks the recent speech level:
le = 0.95·le_prev + 0.05·RM0 (floored at 1.0)
The error-rate recursion and the mute threshold, by contrast, are scale-free and match the reference exactly. Two cleanups then run, but only once the clean-frame test has failed:
- Amplitude cap. If the frame energy
RM0spikes above2.0 × localEnergy— a hallmark of bit-error corruption — every amplitude is scaled bysqrt(cap / RM0)to pull the frame back to a plausible level. - Voicing cleanup. A harmonic whose amplitude exceeds
2.0 × RMS(the per-harmonic RMS of the smoothed local energy) is almost certainly voiced, so if a corrupted voicing bit marked it unvoiced, the stage reclaims it by forcingVl[l] = 1.
Where it sits
Adaptive smoothing runs on the decoded model parameters after spectral enhancement and before the voiced and unvoiced synthesis stages consume them. It is the counterpart to enhancement: enhancement is a perceptual polish applied to good parameters, while smoothing is damage control applied to corrupted ones. The two never fight, because smoothing is inert whenever the channel is clean.
Relevance to SDR
For a scanner working real, marginal RF, adaptive smoothing is what makes a fading P25 Phase 1 signal degrade into a muffled-but-intelligible warble and then a clean mute, rather than erupting into loud bit-error squawks. It converts the FEC’s corrected-bit count — information the decoder already has — into a graceful-degradation policy, so the listener hears the signal fade instead of the decoder failing.
Sources
-
Multi-Band Excitation — Wikipedia, on the IMBE model parameters that adaptive smoothing cleans up. ↩