code wiki / (root) / nx_resid_entropy.nx

nx_resid_entropy.nx source

↩ module page · 66 lines · 2901 B

1// nx_resid_entropy.nx -- wires the sovereign range coder (nx_range_coder) into LPC-RESIDUAL entropy coding: the voice 2// codec's documented v2 q/bit lever. nx_voice_codec.nx itself notes "entropy coding of residual via nx_range_coder ... 3// is the v2 path". LPC residuals are Laplacian (energy concentrated near 0); a static Laplacian frequency model + 4// arithmetic coding packs them at near-Shannon density, far below a fixed N-bit-per-sample representation -- a real, 5// measured bitrate reduction at identical quality (the residual is coded losslessly). Reusable by the codec's 6// encode/decode. license_tier: ORIGINAL 7import "nx_range_coder.nx" 8const K_MAGIC_4096: i64 = 4096 9 10// signed residual <-> unsigned symbol index (small |residual| -> small index, so the model puts mass there) 11func re_zz(r: i64) -> i64 { if r < 0 { return (0 - r) * 2 - 1 } return r * 2 } 12func re_unzz(z: i64) -> i64 { if (z & 1) == 1 { return 0 - ((z + 1) / 2) } return z / 2 } 13 14// build a Laplacian frequency model over symbols 0..ALPHA-1: freq halves per residual magnitude, floor 1 so every 15// symbol stays codeable. Fills freq[ALPHA] + cum[ALPHA+1] (cumulative); returns ft (total frequency). 16func re_build_model(ALPHA: i64, freq: *i64, cum: *i64) -> i64 { 17 var z: i64 = 0 18 while z < ALPHA { 19 let mag: i64 = (z + 1) / 2 20 var f: i64 = K_MAGIC_4096 >> mag 21 if f < 1 { f = 1 } 22 freq[z] = f 23 z = z + 1 24 } 25 var c: i64 = 0 26 var i: i64 = 0 27 while i < ALPHA { cum[i] = c; c = c + freq[i]; i = i + 1 } 28 cum[ALPHA] = c 29 return c 30} 31// range-encode n residual samples into out[]; returns the byte count (the compressed residual the codec transmits). 32func re_encode(resid: *i64, n: i64, ALPHA: i64, cum: *i64, ft: i64, out: *u8, cap: i64) -> i64 { 33 let s: *RcEnc = sys_mmap(256) as *RcEnc 34 nx_rc_enc_init(s, out, cap) 35 var i: i64 = 0 36 while i < n { 37 var z: i64 = re_zz(resid[i]) 38 if z < 0 { z = 0 } 39 if z >= ALPHA { z = ALPHA - 1 } 40 nx_rc_enc_symbol(s, cum[z], cum[z + 1], ft) 41 i = i + 1 42 } 43 return nx_rc_enc_done(s) 44} 45// range-decode n samples from inb[] into out_resid[] (mirrors re_encode exactly). returns 0. 46func re_decode(inb: *u8, len: i64, n: i64, ALPHA: i64, cum: *i64, ft: i64, out_resid: *i64) -> i64 { 47 let d: *RcDec = sys_mmap(256) as *RcDec 48 nx_rc_dec_init(d, inb, len) 49 var i: i64 = 0 50 while i < n { 51 var target: i64 = nx_rc_dec_get_target(d, ft) 52 if target >= ft { target = ft - 1 } 53 var z: i64 = 0 54 var found: i64 = 0 55 while found == 0 { 56 if cum[z + 1] > target { found = 1 } else { 57 z = z + 1 58 if z >= ALPHA - 1 { found = 1 } // clamp to the last symbol 59 } 60 } 61 nx_rc_dec_update(d, cum[z], cum[z + 1], ft) 62 out_resid[i] = re_unzz(z) 63 i = i + 1 64 } 65 return 0 66}