code wiki / _hdl_build / nx_nv1_lpc.nx

nx_nv1_lpc.nx source

↩ module page · 244 lines · 7988 B

1// nx_nv1_lpc.nx -- NishiLossless v1 LPC rung: FLAC-class lossless PCM 2// compression INSIDE the NLC1 container (the named next rung in 3// knowledge/specs/2026-06-10-nishilossless-nv1-container.md). 4// 5// New chunk kind 0x4C 'L' = compressed Int16 PCM block. Payload: 6// u8 mode 0..3 = fixed predictor order; 255 = verbatim escape 7// u8 k Rice parameter (0..30; 0 when verbatim) 8// u32 nsamples decoded Int16 sample count (little-endian) 9// mode==255 : nsamples*2 bytes raw LE Int16 10// mode 0..3 : mode*2 bytes warmup raw LE Int16, then the Rice bitstream 11// (LSB-first within each byte), zero-padded to a byte edge. 12// 13// Fixed predictors (FLAC's fixed mode, exact integer math = zero loss): 14// o0: pred = 0 o1: pred = x[i-1] 15// o2: pred = 2x[i-1] - x[i-2] 16// o3: pred = 3x[i-1] - 3x[i-2] + x[i-3] 17// Residual r = x[i] - pred, zigzag u = (r<0 ? -2r-1 : 2r), Rice(u,k) = 18// (u>>k) one-bits, a zero bit, then the k low bits of u LSB-first. 19// 20// The ENCODER costs every (order, k<16) pair EXACTLY plus the verbatim 21// escape and writes whichever is smallest -- so the payload is NEVER 22// larger than 6 + 2*nsamples (noise degrades to verbatim, not expansion). 23// The DECODER is a validator too: every malformation returns a named 24// negative (tamper-evidence doctrine), including out-of-range 25// reconstruction (-7) so a tampered stream cannot decode silently. 26// 27// Scratch is sys_mmap'd per encode call (no munmap helper exists; the 28// team-side callers are gates and one-shot transcoders). 29// license_tier: ORIGINAL 30 31import "nx_nv1.nx" 32 33func nv1l_rd_i16(b: *u8, off: i64) -> i64 { 34 var v: i64 = b[off] & 0xff 35 v = v + ((b[off + 1] & 0xff) * 256) 36 if v >= 32768 { v = v - 65536 } 37 return v 38} 39 40func nv1l_wr_i16(b: *u8, idx: i64, v: i64) -> i64 { 41 var u: i64 = v 42 if u < 0 { u = u + 65536 } 43 b[idx * 2] = (u & 255) as u8 44 b[idx * 2 + 1] = ((u / 256) & 255) as u8 45 return 0 46} 47 48// set one bit (buffer region must be pre-zeroed); returns new bit cursor 49func nv1l_bit_put(b: *u8, pos: i64, bit: i64) -> i64 { 50 if bit == 1 { 51 let by: i64 = pos >> 3 52 b[by] = ((b[by] & 0xff) | (1 << (pos & 7))) as u8 53 } 54 return pos + 1 55} 56 57// read one bit; -1 on overrun past lim (bit count) 58func nv1l_bit_get(b: *u8, cur: *i64, lim: i64) -> i64 { 59 let p: i64 = cur[0] 60 if p >= lim { return 0 - 1 } 61 cur[0] = p + 1 62 return ((b[p >> 3] & 0xff) >> (p & 7)) & 1 63} 64 65func nv1l_rice_put(b: *u8, pos: i64, u: i64, k: i64) -> i64 { 66 var p: i64 = pos 67 var q: i64 = u >> k 68 while q > 0 { p = nv1l_bit_put(b, p, 1); q = q - 1 } 69 p = nv1l_bit_put(b, p, 0) 70 var j: i64 = 0 71 while j < k { 72 p = nv1l_bit_put(b, p, (u >> j) & 1) 73 j = j + 1 74 } 75 return p 76} 77 78// returns the unsigned Rice value, or -1 on bitstream overrun 79func nv1l_rice_get(b: *u8, cur: *i64, lim: i64, k: i64) -> i64 { 80 var q: i64 = 0 81 var bit: i64 = nv1l_bit_get(b, cur, lim) 82 while bit == 1 { 83 q = q + 1 84 bit = nv1l_bit_get(b, cur, lim) 85 } 86 if bit < 0 { return 0 - 1 } 87 var v: i64 = q << k 88 var j: i64 = 0 89 while j < k { 90 let x: i64 = nv1l_bit_get(b, cur, lim) 91 if x < 0 { return 0 - 1 } 92 v = v + (x << j) 93 j = j + 1 94 } 95 return v 96} 97 98// zigzagged residuals of fixed order o into u; returns count n - o 99func nv1l_residuals(x: *i64, n: i64, o: i64, u: *i64) -> i64 { 100 var i: i64 = o 101 while i < n { 102 var pred: i64 = 0 103 if o == 1 { pred = x[i - 1] } 104 if o == 2 { pred = 2 * x[i - 1] - x[i - 2] } 105 if o == 3 { pred = 3 * x[i - 1] - 3 * x[i - 2] + x[i - 3] } 106 let r: i64 = x[i] - pred 107 var z: i64 = 2 * r 108 if r < 0 { z = 0 - z - 1 } 109 u[i - o] = z 110 i = i + 1 111 } 112 return n - o 113} 114 115func nv1l_cost_bits(u: *i64, m: i64, k: i64) -> i64 { 116 var s: i64 = 0 117 var i: i64 = 0 118 while i < m { 119 s = s + (u[i] >> k) + 1 + k 120 i = i + 1 121 } 122 return s 123} 124 125// best Rice k (0..15) for residual set; kout[0]=k, returns total bits 126func nv1l_best_k(u: *i64, m: i64, kout: *i64) -> i64 { 127 var bk: i64 = 0 128 var bb: i64 = nv1l_cost_bits(u, m, 0) 129 var k: i64 = 1 130 while k < 16 { 131 let c: i64 = nv1l_cost_bits(u, m, k) 132 if c < bb { bb = c; bk = k } 133 k = k + 1 134 } 135 kout[0] = bk 136 return bb 137} 138 139// Encode nsamples LE Int16 from pcm into an L-chunk payload at out. 140// Returns payload length, or: -10 outcap < worst case (6 + 2*nsamples), 141// -11 negative nsamples. Payload is never larger than the verbatim escape. 142func nv1l_encode(pcm: *u8, nsamples: i64, out: *u8, outcap: i64) -> i64 { 143 if nsamples < 0 { return 0 - 11 } 144 if outcap < 6 + 2 * nsamples { return 0 - 10 } 145 if nsamples == 0 { 146 out[0] = 0 as u8 147 out[1] = 0 as u8 148 nv1_wr_u32(out, 2, 0) 149 return 6 150 } 151 let x: *i64 = sys_mmap(nsamples * 8 + 64) as *i64 152 let u: *i64 = sys_mmap(nsamples * 8 + 64) as *i64 153 let kbox: *i64 = sys_mmap(64) as *i64 154 var i: i64 = 0 155 while i < nsamples { x[i] = nv1l_rd_i16(pcm, i * 2); i = i + 1 } 156 var bmode: i64 = 255 157 var bk: i64 = 0 158 var bsize: i64 = 6 + 2 * nsamples 159 var o: i64 = 0 160 while o < 4 { 161 if o < nsamples { 162 let m: i64 = nv1l_residuals(x, nsamples, o, u) 163 let bits: i64 = nv1l_best_k(u, m, kbox) 164 let sz: i64 = 6 + 2 * o + (bits + 7) / 8 165 if sz < bsize { bsize = sz; bmode = o; bk = kbox[0] } 166 } 167 o = o + 1 168 } 169 out[0] = bmode as u8 170 out[1] = bk as u8 171 nv1_wr_u32(out, 2, nsamples) 172 if bmode == 255 { 173 i = 0 174 while i < 2 * nsamples { out[6 + i] = pcm[i]; i = i + 1 } 175 return 6 + 2 * nsamples 176 } 177 i = 0 178 while i < 2 * bmode { out[6 + i] = pcm[i]; i = i + 1 } 179 let m2: i64 = nv1l_residuals(x, nsamples, bmode, u) 180 let base: i64 = 6 + 2 * bmode 181 i = base 182 while i < bsize { out[i] = 0 as u8; i = i + 1 } 183 var pos: i64 = base * 8 184 i = 0 185 while i < m2 { pos = nv1l_rice_put(out, pos, u[i], bk); i = i + 1 } 186 return bsize 187} 188 189// Decode an L-chunk payload into LE Int16 at out. Returns sample count or: 190// -1 payload too short -2 bad mode -3 bad k -4 outcap overrun 191// -5 bitstream truncated/overrun -6 warmup malformed (order >= nsamples 192// or verbatim shorter than claimed) -7 reconstructed sample outside 193// Int16 range (tampered stream) 194func nv1l_decode(pl: *u8, plen: i64, out: *u8, outcap: i64) -> i64 { 195 if plen < 6 { return 0 - 1 } 196 let mode: i64 = pl[0] & 0xff 197 let k: i64 = pl[1] & 0xff 198 let ns: i64 = nv1_rd_u32(pl, 2) 199 var modeok: i64 = 0 200 if mode < 4 { modeok = 1 } 201 if mode == 255 { modeok = 1 } 202 if modeok == 0 { return 0 - 2 } 203 if k > 30 { return 0 - 3 } 204 if ns * 2 > outcap { return 0 - 4 } 205 if mode == 255 { 206 if plen < 6 + 2 * ns { return 0 - 6 } 207 var i: i64 = 0 208 while i < 2 * ns { out[i] = pl[6 + i]; i = i + 1 } 209 return ns 210 } 211 if ns == 0 { return 0 } 212 if mode >= ns { return 0 - 6 } 213 if plen < 6 + 2 * mode { return 0 - 6 } 214 var p1: i64 = 0 215 var p2: i64 = 0 216 var p3: i64 = 0 217 var i: i64 = 0 218 while i < mode { 219 let v: i64 = nv1l_rd_i16(pl, 6 + 2 * i) 220 nv1l_wr_i16(out, i, v) 221 p3 = p2; p2 = p1; p1 = v 222 i = i + 1 223 } 224 let cur: *i64 = sys_mmap(64) as *i64 225 cur[0] = (6 + 2 * mode) * 8 226 let lim: i64 = plen * 8 227 while i < ns { 228 let uv: i64 = nv1l_rice_get(pl, cur, lim, k) 229 if uv < 0 { return 0 - 5 } 230 var r: i64 = uv / 2 231 if (uv & 1) == 1 { r = 0 - ((uv + 1) / 2) } 232 var pred: i64 = 0 233 if mode == 1 { pred = p1 } 234 if mode == 2 { pred = 2 * p1 - p2 } 235 if mode == 3 { pred = 3 * p1 - 3 * p2 + p3 } 236 let vv: i64 = pred + r 237 if vv > 32767 { return 0 - 7 } 238 if vv < 0 - 32768 { return 0 - 7 } 239 nv1l_wr_i16(out, i, vv) 240 p3 = p2; p2 = p1; p1 = vv 241 i = i + 1 242 } 243 return ns 244}