code wiki / (root) / nx_flac.nx

nx_flac.nx source

↩ module page · 256 lines · 8608 B

1// nx_flac.nx -- FLAC lossless audio decoder core. 2// 3// The audio quadrant of this tree held PCM and nothing else. FLAC is the 4// cheapest real codec to close that with: it is native in every browser, it 5// is the archival lossless standard, and its whole decode path is integer 6// arithmetic -- no transform, no float, no psychoacoustics. 7// 8// FLAC is MSB-first (unlike DEFLATE/VP8L), so this composes onto 9// nx_bitstream_read_msb. Its residuals are Rice-coded, which nx_huffman.nx 10// already names as a sibling of its own family. 11// 12// THE SIGN TRAP. Rice codes an unsigned value that must be UNFOLDED to signed 13// by zigzag: even -> +n/2, odd -> -(n+1)/2. Getting that backwards produces 14// audio that decodes without error and sounds like noise, because every 15// residual has the wrong sign but the right magnitude. There is no checksum 16// on the sample path that would catch it -- only the frame CRC, which many 17// decoders skip. 18// 19// genealogy_id: flac_format_spec 20// lineage_id: nx_flac_v1 21// license_tier: ORIGINAL 22 23import "nx_syscalls.nx" 24import "nx_bitstream.nx" 25const NX_MAGIC_1000000: i64 = 1000000 26const NX_MAGIC_88200: i64 = 88200 27const NX_MAGIC_176400: i64 = 176400 28const NX_MAGIC_192000: i64 = 192000 29const NX_MAGIC_8000: i64 = 8000 30const NX_MAGIC_16000: i64 = 16000 31const NX_MAGIC_22050: i64 = 22050 32const NX_MAGIC_24000: i64 = 24000 33const NX_MAGIC_32000: i64 = 32000 34const NX_MAGIC_44100: i64 = 44100 35const NX_MAGIC_48000: i64 = 48000 36const NX_MAGIC_96000: i64 = 96000 37 38const NX_FLAC_MAX_FIXED_ORDER: i64 = 4 39const NX_FLAC_SUB_CONSTANT: i64 = 0 40const NX_FLAC_SUB_VERBATIM: i64 = 1 41const NX_FLAC_SUB_FIXED: i64 = 2 42const NX_FLAC_SUB_LPC: i64 = 3 43 44const NX_FLAC_CH_INDEPENDENT: i64 = 0 45const NX_FLAC_CH_LEFT_SIDE: i64 = 1 46const NX_FLAC_CH_RIGHT_SIDE: i64 = 2 47const NX_FLAC_CH_MID_SIDE: i64 = 3 48 49// ===== zigzag: the sign trap ====================================== 50// 51// Rice emits unsigned folded values; even codes a non-negative sample and 52// odd codes a negative one. Inverting this is silent and catastrophic. 53 54func nx_flac_unfold(v: i64) -> i64 { 55 if (v & 1) == 1 { return 0 - ((v >> 1) + 1) } 56 return v >> 1 57} 58 59func nx_flac_fold(v: i64) -> i64 { 60 if v < 0 { return ((0 - v) << 1) - 1 } 61 return v << 1 62} 63 64// ===== Rice / Golomb-Rice residual ================================ 65// 66// A unary quotient (zeros terminated by a one) then `param` binary bits. 67// Returns the SIGNED residual, or a caller-visible failure through bs.overflow. 68// The unary run is bounded -- a corrupt stream must not spin forever looking 69// for a terminator that is not there. 70 71func nx_flac_read_rice(bs: *NxBitStream, param: i64) -> i64 { 72 if param < 0 { return 0 } 73 var q: i64 = 0 74 var go: i64 = 1 75 while go == 1 { 76 let b: i64 = nx_bitstream_read_msb(bs, 1) 77 if bs.overflow == 1 { go = 0 } else { 78 if b == 1 { go = 0 } else { 79 q = q + 1 80 if q > NX_MAGIC_1000000 { bs.overflow = 1; go = 0 } 81 } } 82 } 83 if bs.overflow == 1 { return 0 } 84 var r: i64 = 0 85 if param > 0 { r = nx_bitstream_read_msb(bs, param) } 86 return nx_flac_unfold((q << param) | r) 87} 88 89// ===== fixed predictors =========================================== 90// 91// Orders 0..4 with the spec's fixed coefficient patterns. `hist` points at 92// the sample immediately BEFORE the one being restored, so hist[0] is x[n-1], 93// hist[-1] is x[n-2], and so on. 94 95func nx_flac_fixed_predict(order: i64, p1: i64, p2: i64, p3: i64, p4: i64) -> i64 { 96 if order == 0 { return 0 } 97 if order == 1 { return p1 } 98 if order == 2 { return 2 * p1 - p2 } 99 if order == 3 { return 3 * p1 - 3 * p2 + p3 } 100 return 4 * p1 - 6 * p2 + 4 * p3 - p4 101} 102 103// restore a whole fixed-predictor subframe in place: buf[0..order-1] are the 104// warm-up samples already decoded, buf[order..n-1] hold residuals on entry 105// and samples on exit. 106func nx_flac_restore_fixed(buf: *i64, n: i64, order: i64) -> i64 { 107 if order < 0 { return 0 } 108 if order > NX_FLAC_MAX_FIXED_ORDER { return 0 } 109 if n < order { return 0 } 110 var i: i64 = order 111 while i < n { 112 var p1: i64 = 0 113 var p2: i64 = 0 114 var p3: i64 = 0 115 var p4: i64 = 0 116 if i >= 1 { p1 = buf[i-1] } 117 if i >= 2 { p2 = buf[i-2] } 118 if i >= 3 { p3 = buf[i-3] } 119 if i >= 4 { p4 = buf[i-4] } 120 buf[i] = buf[i] + nx_flac_fixed_predict(order, p1, p2, p3, p4) 121 i = i + 1 122 } 123 return 1 124} 125 126// ===== LPC restoration ============================================ 127// 128// sum(coef[j] * x[n-1-j]) >> shift, added to the residual. Integer only: 129// the shift is a right-shift of a signed accumulator, which is exactly what 130// the spec mandates for bit-exact reconstruction. 131 132func nx_flac_restore_lpc(buf: *i64, n: i64, order: i64, coef: *i64, shift: i64) -> i64 { 133 if order <= 0 { return 0 } 134 if shift < 0 { return 0 } 135 if n < order { return 0 } 136 var i: i64 = order 137 while i < n { 138 var acc: i64 = 0 139 var j: i64 = 0 140 while j < order { 141 acc = acc + coef[j] * buf[i - 1 - j] 142 j = j + 1 143 } 144 buf[i] = buf[i] + (acc >> shift) 145 i = i + 1 146 } 147 return 1 148} 149 150// ===== stereo decorrelation ======================================= 151// 152// FLAC stores one channel plus a difference. Mid/side carries the dropped 153// low bit of mid in the low bit of side -- forgetting to re-insert it makes 154// every odd-valued sample off by one, which is inaudible on a spectrum plot 155// and fatal to a lossless claim. 156 157func nx_flac_undo_stereo(a: *i64, b: *i64, n: i64, mode: i64) -> i64 { 158 if mode == NX_FLAC_CH_INDEPENDENT { return 1 } 159 var i: i64 = 0 160 while i < n { 161 if mode == NX_FLAC_CH_LEFT_SIDE { 162 // a = left, b = side = left - right 163 b[i] = a[i] - b[i] 164 } else { 165 if mode == NX_FLAC_CH_RIGHT_SIDE { 166 // a = side = left - right, b = right 167 a[i] = a[i] + b[i] 168 } else { 169 // mid/side: a = mid (low bit dropped), b = side 170 let side: i64 = b[i] 171 var mid: i64 = (a[i] << 1) | (side & 1) 172 a[i] = (mid + side) >> 1 173 b[i] = (mid - side) >> 1 174 } } 175 i = i + 1 176 } 177 return 1 178} 179 180// ===== frame header tables ======================================== 181// 182// Returns 0 for the codes that mean "read it from the end of the header" 183// and -1 for reserved/invalid, so a caller can tell "deferred" from "bad". 184 185func nx_flac_block_size(code: i64) -> i64 { 186 if code == 0 { return 0 - 1 } 187 if code == 1 { return 192 } 188 if code <= 5 { return 576 << (code - 2) } 189 if code == 6 { return 0 } 190 if code == 7 { return 0 } 191 return 256 << (code - 8) 192} 193 194func nx_flac_sample_rate(code: i64) -> i64 { 195 if code == 0 { return 0 } 196 if code == 1 { return NX_MAGIC_88200 } 197 if code == 2 { return NX_MAGIC_176400 } 198 if code == 3 { return NX_MAGIC_192000 } 199 if code == 4 { return NX_MAGIC_8000 } 200 if code == 5 { return NX_MAGIC_16000 } 201 if code == 6 { return NX_MAGIC_22050 } 202 if code == 7 { return NX_MAGIC_24000 } 203 if code == 8 { return NX_MAGIC_32000 } 204 if code == 9 { return NX_MAGIC_44100 } 205 if code == 10 { return NX_MAGIC_48000 } 206 if code == 11 { return NX_MAGIC_96000 } 207 if code == 15 { return 0 - 1 } 208 return 0 209} 210 211func nx_flac_bit_depth(code: i64) -> i64 { 212 if code == 0 { return 0 } 213 if code == 1 { return 8 } 214 if code == 2 { return 12 } 215 if code == 3 { return 0 - 1 } 216 if code == 4 { return 16 } 217 if code == 5 { return 20 } 218 if code == 6 { return 24 } 219 if code == 7 { return 32 } 220 return 0 - 1 221} 222 223// ===== CRC-8 (polynomial 0x07) ==================================== 224// 225// Guards the frame header. Cheap, and the only thing standing between a 226// desynced bitstream and hours of confident garbage. 227 228func nx_flac_crc8(data: *u8, n: i64) -> i64 { 229 var crc: i64 = 0 230 var i: i64 = 0 231 while i < n { 232 crc = crc ^ ((data[i] as i64) & 255) 233 var b: i64 = 0 234 while b < 8 { 235 if (crc & 0x80) != 0 { 236 crc = ((crc << 1) ^ 0x07) & 255 237 } else { 238 crc = (crc << 1) & 255 239 } 240 b = b + 1 241 } 242 i = i + 1 243 } 244 return crc & 255 245} 246 247// ===== stream magic =============================================== 248 249func nx_flac_is_stream(b: *u8, n: i64) -> i64 { 250 if n < 4 { return 0 } 251 if (b[0] as i64 & 255) != 0x66 { return 0 } 252 if (b[1] as i64 & 255) != 0x4c { return 0 } 253 if (b[2] as i64 & 255) != 0x61 { return 0 } 254 if (b[3] as i64 & 255) != 0x43 { return 0 } 255 return 1 256}