Synthetic ERLE metric demo

Tutorial goal

Explain ERLE and MSE on a controlled synthetic echo-path identification problem.

Note

New to the terminology? See the lattice DSP concept map and the causality/data-use guide for how online, offline, block, and MIMO examples should be read.

Context

This tutorial is intentionally limited: it is a metric demonstration for a synthetic echo path, not a production acoustic echo canceller. It is useful for understanding the two-signal roles: a far-end/reference signal drives the unknown echo path, while a microphone/desired signal is used to measure the residual after subtracting the estimated echo.

Key idea and equations

With reference signal \(x[n]\) and microphone signal \(m[n]\), an adaptive echo-path model produces

\[\widehat e_{echo}[n] = h_{\theta_n}(x)[n], \qquad r[n] = m[n] - \widehat e_{echo}[n].\]

Echo return loss enhancement is commonly reported as

\[\operatorname{ERLE}_{dB} =10\log_{10}\frac{\mathbb{E}[m^2]}{\mathbb{E}[r^2]}.\]

A causal echo-path filter may use current and past reference samples and previous adaptive state. It must not use future microphone samples to form the current residual.

Causality and data use

This is a controlled two-signal synthetic metric demo. It uses reference/far-end and microphone/desired roles, unlike one-signal AR prediction. Real AEC systems require additional delay control, double-talk handling, nonlinear echo handling, and deployment-specific engineering.

How to read the result

Higher ERLE and lower MSE indicate better cancellation in this controlled synthetic setup only; they are not product-quality acoustic echo-cancellation claims.

Run command

python examples/echo_cancellation_erle_demo.py

Run status

Return code: 0

Captured stdout

Synthetic echo metric demo
--------------------------
No cancellation ERLE:     0.00 dB
FIR/NLMS ERLE:           24.98 dB
Lattice/IIR ERLE:        16.18 dB

ERLE = 10 log10(input echo/error power / output residual error power).
Positive ERLE means residual error power was reduced. It does not by
itself prove speech quality, especially during double-talk.

Final lattice reflection coefficients:
[ 0.2837 -0.1909  0.1629 -0.1216]

Source code

 1"""Small synthetic echo-cancellation metric demo.
 2
 3This example is intentionally modest.  It shows how the stable adaptive
 4lattice/IIR stage behaves on a known synthetic echo problem and how ERLE is
 5computed.  It is not a production acoustic echo canceller.
 6"""
 7
 8from __future__ import annotations
 9
10import numpy as np
11
12from lattice_dsp import HybridEchoCanceller, echo_metrics, generate_echo_problem
13
14
15def fir_nlms(
16    reference: np.ndarray,
17    desired: np.ndarray,
18    *,
19    order: int = 64,
20    mu: float = 0.5,
21    epsilon: float = 1e-8,
22):
23    """Tiny FIR/NLMS reference baseline for this example."""
24
25    w = np.zeros(order, dtype=np.float64)
26    xbuf = np.zeros(order, dtype=np.float64)
27    y = np.zeros_like(reference, dtype=np.float64)
28    e = np.zeros_like(reference, dtype=np.float64)
29    for n, sample in enumerate(reference):
30        xbuf[1:] = xbuf[:-1]
31        xbuf[0] = sample
32        y_n = float(np.dot(w, xbuf))
33        e_n = float(desired[n] - y_n)
34        w += (mu * e_n / (float(np.dot(xbuf, xbuf)) + epsilon)) * xbuf
35        y[n] = y_n
36        e[n] = e_n
37    return y, e, w
38
39
40def main() -> None:
41    problem = generate_echo_problem(
42        samples=16_000,
43        sample_rate=16_000,
44        seed=123,
45        nonlinear_strength=0.0,
46        near_end_power_ratio=0.0,
47        noise_snr_db=60.0,
48        double_talk=False,
49    )
50
51    no_cancel = echo_metrics(problem.microphone, problem.microphone, problem.clean_target)
52
53    _, fir_residual, _ = fir_nlms(problem.reference, problem.microphone, order=64, mu=0.5)
54    fir_metrics = echo_metrics(problem.microphone, fir_residual, problem.clean_target)
55
56    canceller = HybridEchoCanceller(
57        initial_reflection=[0.0] * 4,
58        initial_taps=[0.0] * 5,
59        mu_taps=0.05,
60        mu_reflection=0.001,
61        reflection_update_period=8,
62    )
63    result = canceller.process(
64        problem.reference, problem.microphone, clean_target=problem.clean_target
65    )
66    lattice_metrics = echo_metrics(problem.microphone, result.residual, problem.clean_target)
67
68    print("Synthetic echo metric demo")
69    print("--------------------------")
70    print(f"No cancellation ERLE: {no_cancel.erle_db:8.2f} dB")
71    print(f"FIR/NLMS ERLE:        {fir_metrics.erle_db:8.2f} dB")
72    print(f"Lattice/IIR ERLE:     {lattice_metrics.erle_db:8.2f} dB")
73    print()
74    print("ERLE = 10 log10(input echo/error power / output residual error power).")
75    print("Positive ERLE means residual error power was reduced. It does not by")
76    print("itself prove speech quality, especially during double-talk.")
77    print()
78    print("Final lattice reflection coefficients:")
79    print(np.array2string(result.reflection, precision=4))
80
81
82if __name__ == "__main__":
83    main()