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}