code wiki / _hdl_build / nx_voice_v2_gate.nx

nx_voice_v2_gate.nx source

↩ module page · 117 lines · 6345 B

1// nx_voice_v2_gate.nx -- proves the NishiVoice v2 q/bit path end-to-end: real LPC residual + entropy coding + inverse 2// synthesis (what nx_voice_codec.nx says is "the v2 path"). Chain: autocorr -> levinson (LPC coeffs a[1..p] Q30) -> 3// analysis filter e[n]=s[n]+Sum a[k]s[n-k] -> quantise -> nx_resid_entropy range-code -> decode -> nx_lpc_synth 4// s[n]=e[n]-Sum a[k]s[n-k]. All on the x86_64 LPC lineage (one syscalls module). MEASURES the entropy compression of 5// the residual + the reconstruction fidelity. The entropy lever itself was already proven (nx_resid_entropy_gate -46%). 6import "nx_syscalls_x86_64.nx" 7import "nx_lpc_autocorr.nx" 8import "nx_lpc_levinson.nx" 9import "nx_lpc_synth.nx" 10import "nx_resid_entropy.nx" 11import "nx_gate_verdict.nx" 12 13func g_puts(s: *u8) -> i64 { var n: i64=0; while s[n]!=(0 as u8){n=n+1} sys_write(1,s,n); return 0 } 14func g_pn(v: i64) -> i64 { 15 let b: *u8 = sys_mmap(28); var x: i64 = v 16 if x < 0 { b[0]=45; sys_write(1,b,1); x = 0 - x } 17 if x == 0 { b[0]=48; sys_write(1,b,1); return 0 } 18 var d: i64=0; var y: i64=x 19 while y>0 { d=d+1; y=y/10 } 20 var i: i64=d-1; y=x 21 while i>=0 { b[i]=(48+(y%10)) as u8; y=y/10; i=i-1 } 22 sys_write(1,b,d); return 0 23} 24func g_check(name: *u8, cond: i64) -> i64 { 25 if cond==1 { g_puts(" PASS " as *u8) } else { g_puts(" FAIL " as *u8) } 26 g_puts(name); g_puts("\n" as *u8); return cond 27} 28func g_abs(v: i64) -> i64 { if v < 0 { return 0 - v } return v } 29func st16(buf: *u8, idx: i64, v: i64) -> i64 { var x: i64 = v; if x < 0 { x = x + 65536 } buf[idx*2] = x & 0xff; buf[idx*2+1] = (x >> 8) & 0xff; return 0 } 30func ld16(buf: *u8, idx: i64) -> i64 { var v: i64 = buf[idx*2] | (buf[idx*2+1] << 8); if v >= 32768 { v = v - 65536 } return v } 31func ld64(buf: *u8, idx: i64) -> i64 { 32 var v: i64 = 0; var b: i64 = 0 33 while b < 8 { v = v | (buf[idx*8 + b] << (b*8)); b = b + 1 } 34 return v 35} 36 37func main() -> i64 { 38 g_puts("nx_voice v2 gate (LPC residual + entropy + synthesis, end-to-end MEASURED)\n" as *u8) 39 var pass: i64 = 0; var total: i64 = 0 40 let n: i64 = 64; let order: i64 = 8 41 let ALPHA: i64 = 65 // residual symbols up to |32| 42 43 let s_pcm: *u8 = sys_mmap(n*2) 44 let R: *u8 = sys_mmap((order+1)*8) 45 let kbuf: *u8 = sys_mmap((order+1)*8) 46 let a_q30: *u8 = sys_mmap((order+1)*8) 47 let ebuf: *u8 = sys_mmap((order+1)*8) 48 let e_i64: *i64 = sys_mmap(n*8) as *i64 49 let eq: *i64 = sys_mmap(n*8) as *i64 50 let eq2: *i64 = sys_mmap(n*8) as *i64 51 let e_pcm: *u8 = sys_mmap(n*2) 52 let s_recon: *u8 = sys_mmap(n*2) 53 let ent: *u8 = sys_mmap(2048) 54 let freq: *i64 = sys_mmap(ALPHA*8) as *i64 55 let cum: *i64 = sys_mmap((ALPHA+1)*8) as *i64 56 57 // voiced-ish signal: periodic smooth bumps (period 16) -- an AR-predictable waveform 58 var i: i64 = 0 59 while i < n { let p: i64 = i % 16; st16(s_pcm, i, 40 + p*(16-p)*2); i = i + 1 } 60 var sig_range: i64 = 0 61 i = 0; while i < n { let v: i64 = g_abs(ld16(s_pcm, i)); if v > sig_range { sig_range = v } i = i + 1 } 62 63 // 1) LPC analysis: autocorrelation -> Levinson -> a[1..p] in Q30 64 nx_lpc_autocorr(s_pcm, n, order, R) 65 nx_lpc_levinson(R, order, kbuf, a_q30, ebuf) 66 67 // 2) analysis filter: e[n] = s[n] + Sum_{k=1..p} a[k]*s[n-k] (a in Q30 -> >>30) 68 var maxe: i64 = 0 69 i = 0 70 while i < n { 71 var pred: i64 = 0 72 var k: i64 = 1 73 while k <= order { if i - k >= 0 { pred = pred + ((ld64(a_q30, k-1) * ld16(s_pcm, i-k)) >> 30) } k = k + 1 } // per-term >>30, matching nx_lpc_synth 74 let e: i64 = ld16(s_pcm, i) + pred 75 e_i64[i] = e 76 if g_abs(e) > maxe { maxe = g_abs(e) } 77 i = i + 1 78 } 79 80 // 3) quantise the residual so it fits the entropy alphabet (this is the codec's lossy step) 81 var Q: i64 = (maxe + 31) / 32; if Q < 1 { Q = 1 } // size Q so the quantised residual fits +/-32 without clamping 82 i = 0 83 while i < n { var q: i64 = e_i64[i] / Q; if q > 32 { q = 32 } if q < -32 { q = -32 } eq[i] = q; i = i + 1 } 84 85 // 4) entropy-code the quantised residual, then decode it back (lossless) 86 let ft: i64 = re_build_model(ALPHA, freq, cum) 87 let ent_bytes: i64 = re_encode(eq, n, ALPHA, cum, ft, ent, 2048) 88 re_decode(ent, ent_bytes, n, ALPHA, cum, ft, eq2) 89 var ent_ok: i64 = 1 90 i = 0; while i < n { if eq2[i] != eq[i] { ent_ok = 0 } i = i + 1 } 91 92 // 5) dequantise -> e_pcm, then LPC synthesis: s[n] = e[n] - Sum a[k]s[n-k] 93 i = 0; while i < n { st16(e_pcm, i, eq2[i] * Q); i = i + 1 } 94 nx_lpc_synth(e_pcm, n, a_q30, order, s_recon) 95 96 var recon_maxerr: i64 = 0 97 i = 0; while i < n { let d: i64 = g_abs(ld16(s_recon, i) - ld16(s_pcm, i)); if d > recon_maxerr { recon_maxerr = d } i = i + 1 } 98 let raw_resid_bytes: i64 = n * 2 // storing the residual raw (i16/sample) 99 100 g_puts(" [measure] signal range=" as *u8); g_pn(sig_range); g_puts(" residual max|e|=" as *u8); g_pn(maxe); g_puts(" quant Q=" as *u8); g_pn(Q); g_puts("\n" as *u8) 101 g_puts(" [measure] residual: entropy-coded=" as *u8); g_pn(ent_bytes); g_puts(" B vs raw-i16=" as *u8); g_pn(raw_resid_bytes); g_puts(" B reconstruction max-err=" as *u8); g_pn(recon_maxerr); g_puts(" / signal " as *u8); g_pn(sig_range); g_puts("\n" as *u8) 102 103 pass = pass + g_check("entropy round-trips the residual losslessly" as *u8, ent_ok); total=total+1 104 pass = pass + g_check("entropy-coded residual < raw i16 residual (the v2 q/bit win)" as *u8, ent_bytes < raw_resid_bytes); total=total+1 105 pass = pass + g_check("v2 chain reconstructs faithfully (max-err <= 1/4 of signal range)" as *u8, recon_maxerr * 4 <= sig_range); total=total+1 106 107 g_puts("---- voice v2 gate: passed " as *u8); g_pn(pass); g_puts(" / " as *u8); g_pn(total); g_puts(" ----\n" as *u8) 108 // MIGRATED onto nx_gate_verdict by nx_gate_dry_apply (D001, minimal form): every check 109 // row above is untouched, so the PASS/FAIL vector cannot change; only the hand-rolled 110 // verdict emission is replaced by the ONE shared base class. Proven by nx_gate_migrate verify. 111 let ctr__dry: *i64 = gv_ctr() 112 ctr__dry[0] = pass 113 ctr__dry[1] = total 114 let rc__dry: i64 = gv_verdict("VOICE-V2-GATE" as *u8, ctr__dry, "teeth unchanged; verdict emission migrated onto the shared base class" as *u8) 115 sys_exit(rc__dry) 116 return rc__dry 117}