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}