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}