code wiki / _hdl_build / nx_aec_gate.nx
nx_aec_gate.nx source
↩ module page · 48 lines · 2955 B
1// nx_aec_gate.nx -- proves + MEASURES sovereign echo cancellation (nx_aec): an NLMS filter learns the echo path
2// and cancels the far-end echo while preserving the near-end voice.
3import "nx_syscalls.nx"
4import "nx_gate_emit_lib.nx"
5import "nx_aec.nx"
6
7func g_abs(v: i64) -> i64 { if v<0 { return 0-v } return v }
8// energy of arr[from..to)
9func energy(arr: *i64, from: i64, to: i64) -> i64 { var s: i64=0; var i: i64=from; while i<to { s = s + arr[i]*arr[i]; i=i+1 } return s }
10
11func main() -> i64 {
12 g_puts("nx_aec gate (NLMS acoustic echo cancellation, MEASURED)\n" as *u8)
13 var pass: i64 = 0; var total: i64 = 0
14 let n: i64 = 400; let L: i64 = 8; let D: i64 = 3 // echo path: half-amplitude at delay 3
15 let ref: *i64 = sys_mmap(n*8) as *i64 // far-end reference (broadband for NLMS excitation)
16 let mic: *i64 = sys_mmap(n*8) as *i64
17 let near: *i64 = sys_mmap(n*8) as *i64
18 let out: *i64 = sys_mmap(n*8) as *i64
19 let w: *i64 = sys_mmap(L*8) as *i64
20
21 var i: i64=0
22 while i<n { ref[i] = ((i*37) % 200) - 100; near[i] = ((i*53) % 80) - 40; i=i+1 } // distinct near/far signals
23
24 // 1) PURE echo (no near speech): mic = echo(ref). AEC should drive the residual toward 0.
25 i=0; while i<L { w[i]=0; i=i+1 }
26 i=0; while i<n { var e: i64=0; if i-D>=0 { e = ref[i-D] / 2 } mic[i] = e; i=i+1 }
27 aec_run(mic, ref, n, L, 4096, w, out) // step ~ mu 0.75
28 let echo_in: i64 = energy(mic, 300, 400) // input echo energy (steady state)
29 let echo_out: i64 = energy(out, 300, 400) // residual after cancellation
30 g_puts(" [measure] pure echo: input energy=" as *u8); g_pn(echo_in); g_puts(" residual=" as *u8); g_pn(echo_out); g_puts("\n" as *u8)
31 pass = pass + g_check("echo cancelled >= 8x (residual << input)" as *u8, echo_out * 8 < echo_in); total=total+1
32
33 // 2) near speech + echo: mic = near + echo. AEC should leave ~near (echo removed, voice preserved).
34 i=0; while i<L { w[i]=0; i=i+1 }
35 i=0; while i<n { var e: i64=0; if i-D>=0 { e = ref[i-D] / 2 } mic[i] = near[i] + e; i=i+1 }
36 aec_run(mic, ref, n, L, 4096, w, out)
37 // residual error vs the true near speech (steady state)
38 let resid: *i64 = sys_mmap(n*8) as *i64
39 i=300; while i<400 { resid[i] = out[i] - near[i]; i=i+1 }
40 let near_e: i64 = energy(near, 300, 400)
41 let resid_e: i64 = energy(resid, 300, 400)
42 g_puts(" [measure] near+echo: near energy=" as *u8); g_pn(near_e); g_puts(" (out-near) residual=" as *u8); g_pn(resid_e); g_puts("\n" as *u8)
43 pass = pass + g_check("near voice preserved, echo removed (residual << near energy)" as *u8, resid_e * 4 < near_e); total=total+1
44
45 g_puts("---- aec gate: passed " as *u8); g_pn(pass); g_puts(" / " as *u8); g_pn(total); g_puts(" ----\n" as *u8)
46 if pass == total { g_puts("verdict=GREEN\n" as *u8); sys_exit(0); return 0 }
47 g_puts("verdict=RED\n" as *u8); sys_exit(1); return 1
48}