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}