code wiki / _hdl_build / nx_voice_v2_gate.nx

nx_voice_v2_gate.nx source

↩ module page · 109 lines · 5903 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" 11 12func 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 } 13func g_pn(v: i64) -> i64 { 14 let b: *u8 = sys_mmap(28); var x: i64 = v 15 if x < 0 { b[0]=45; sys_write(1,b,1); x = 0 - x } 16 if x == 0 { b[0]=48; sys_write(1,b,1); return 0 } 17 var d: i64=0; var y: i64=x 18 while y>0 { d=d+1; y=y/10 } 19 var i: i64=d-1; y=x 20 while i>=0 { b[i]=(48+(y%10)) as u8; y=y/10; i=i-1 } 21 sys_write(1,b,d); return 0 22} 23func g_check(name: *u8, cond: i64) -> i64 { 24 if cond==1 { g_puts(" PASS " as *u8) } else { g_puts(" FAIL " as *u8) } 25 g_puts(name); g_puts("\n" as *u8); return cond 26} 27func g_abs(v: i64) -> i64 { if v < 0 { return 0 - v } return v } 28func 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 } 29func 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 } 30func ld64(buf: *u8, idx: i64) -> i64 { 31 var v: i64 = 0; var b: i64 = 0 32 while b < 8 { v = v | (buf[idx*8 + b] << (b*8)); b = b + 1 } 33 return v 34} 35 36func main() -> i64 { 37 g_puts("nx_voice v2 gate (LPC residual + entropy + synthesis, end-to-end MEASURED)\n" as *u8) 38 var pass: i64 = 0; var total: i64 = 0 39 let n: i64 = 64; let order: i64 = 8 40 let ALPHA: i64 = 65 // residual symbols up to |32| 41 42 let s_pcm: *u8 = sys_mmap(n*2) 43 let R: *u8 = sys_mmap((order+1)*8) 44 let kbuf: *u8 = sys_mmap((order+1)*8) 45 let a_q30: *u8 = sys_mmap((order+1)*8) 46 let ebuf: *u8 = sys_mmap((order+1)*8) 47 let e_i64: *i64 = sys_mmap(n*8) as *i64 48 let eq: *i64 = sys_mmap(n*8) as *i64 49 let eq2: *i64 = sys_mmap(n*8) as *i64 50 let e_pcm: *u8 = sys_mmap(n*2) 51 let s_recon: *u8 = sys_mmap(n*2) 52 let ent: *u8 = sys_mmap(2048) 53 let freq: *i64 = sys_mmap(ALPHA*8) as *i64 54 let cum: *i64 = sys_mmap((ALPHA+1)*8) as *i64 55 56 // voiced-ish signal: periodic smooth bumps (period 16) -- an AR-predictable waveform 57 var i: i64 = 0 58 while i < n { let p: i64 = i % 16; st16(s_pcm, i, 40 + p*(16-p)*2); i = i + 1 } 59 var sig_range: i64 = 0 60 i = 0; while i < n { let v: i64 = g_abs(ld16(s_pcm, i)); if v > sig_range { sig_range = v } i = i + 1 } 61 62 // 1) LPC analysis: autocorrelation -> Levinson -> a[1..p] in Q30 63 nx_lpc_autocorr(s_pcm, n, order, R) 64 nx_lpc_levinson(R, order, kbuf, a_q30, ebuf) 65 66 // 2) analysis filter: e[n] = s[n] + Sum_{k=1..p} a[k]*s[n-k] (a in Q30 -> >>30) 67 var maxe: i64 = 0 68 i = 0 69 while i < n { 70 var pred: i64 = 0 71 var k: i64 = 1 72 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 73 let e: i64 = ld16(s_pcm, i) + pred 74 e_i64[i] = e 75 if g_abs(e) > maxe { maxe = g_abs(e) } 76 i = i + 1 77 } 78 79 // 3) quantise the residual so it fits the entropy alphabet (this is the codec's lossy step) 80 var Q: i64 = (maxe + 31) / 32; if Q < 1 { Q = 1 } // size Q so the quantised residual fits +/-32 without clamping 81 i = 0 82 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 } 83 84 // 4) entropy-code the quantised residual, then decode it back (lossless) 85 let ft: i64 = re_build_model(ALPHA, freq, cum) 86 let ent_bytes: i64 = re_encode(eq, n, ALPHA, cum, ft, ent, 2048) 87 re_decode(ent, ent_bytes, n, ALPHA, cum, ft, eq2) 88 var ent_ok: i64 = 1 89 i = 0; while i < n { if eq2[i] != eq[i] { ent_ok = 0 } i = i + 1 } 90 91 // 5) dequantise -> e_pcm, then LPC synthesis: s[n] = e[n] - Sum a[k]s[n-k] 92 i = 0; while i < n { st16(e_pcm, i, eq2[i] * Q); i = i + 1 } 93 nx_lpc_synth(e_pcm, n, a_q30, order, s_recon) 94 95 var recon_maxerr: i64 = 0 96 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 } 97 let raw_resid_bytes: i64 = n * 2 // storing the residual raw (i16/sample) 98 99 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) 100 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) 101 102 pass = pass + g_check("entropy round-trips the residual losslessly" as *u8, ent_ok); total=total+1 103 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 104 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 105 106 g_puts("---- voice v2 gate: passed " as *u8); g_pn(pass); g_puts(" / " as *u8); g_pn(total); g_puts(" ----\n" as *u8) 107 if pass == total { g_puts("verdict=GREEN\n" as *u8); sys_exit(0); return 0 } 108 g_puts("verdict=RED\n" as *u8); sys_exit(1); return 1 109}