xref: /btstack/3rd-party/lc3-google/test/spec.py (revision 4930cef6e21e6da2d7571b9259c7f0fb8bed3d01)
1#
2# Copyright 2022 Google LLC
3#
4# Licensed under the Apache License, Version 2.0 (the "License");
5# you may not use this file except in compliance with the License.
6# You may obtain a copy of the License at
7#
8#     http://www.apache.org/licenses/LICENSE-2.0
9#
10# Unless required by applicable law or agreed to in writing, software
11# distributed under the License is distributed on an "AS IS" BASIS,
12# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13# See the License for the specific language governing permissions and
14# limitations under the License.
15#
16
17import numpy as np
18
19import build.lc3 as lc3
20import tables as T, appendix_c as C
21
22import bwdet as m_bwdet
23import ltpf as m_ltpf
24import sns as m_sns
25import tns as m_tns
26
27### ------------------------------------------------------------------------ ###
28
29class SpectrumQuantization:
30
31    def __init__(self, dt, sr):
32
33        self.dt = dt
34        self.sr = sr
35
36    def get_gain_offset(self, nbytes):
37
38        g_off = (nbytes * 8) // (10 * (1 + self.sr))
39        g_off = -min(115, g_off) - (105 + 5*(1 + self.sr))
40
41        return g_off
42
43    def get_noise_indices(self, bw, xq, lastnz):
44
45        nf_start = [ 18, 24 ][self.dt]
46        nf_width = [  2,  3 ][self.dt]
47
48        bw_stop = int([ 80, 160, 240, 320, 400 ][bw] * (T.DT_MS[self.dt] / 10))
49
50        xq = np.append(xq[:lastnz], np.zeros(len(xq) - lastnz))
51
52        i_nf = [ np.all(xq[k-nf_width:min(bw_stop, k+nf_width+1)] == 0)
53                    for k in range(nf_start, bw_stop) ]
54
55        return (i_nf, nf_start, bw_stop)
56
57
58class SpectrumAnalysis(SpectrumQuantization):
59
60    def __init__(self, dt, sr):
61
62        super().__init__(dt, sr)
63
64        self.reset_off  = 0
65        self.nbits_off  = 0
66        self.nbits_spec = 0
67        self.nbits_est  = 0
68
69        self.g_idx = None
70
71    def estimate_gain(self, x, nbits_spec, nbits_off, g_off):
72
73        nbits = int(nbits_spec + nbits_off + 0.5)
74
75        ### Energy (dB) by 4 MDCT coefficients
76
77        e = [ np.sum(x[4*k:4*(k+1)] ** 2) for k in range(len(x) // 4) ]
78        e = 10 * np.log10(2**-31 + np.array(e))
79
80        ### Compute gain index
81
82        g_idx = 255
83
84        for i in range(8):
85            factor = 1 << (7 - i)
86            g_idx -= factor
87            tmp = 0
88            iszero = 1
89
90            for ei in e[-1::-1]:
91
92                if ei * 28/20 < g_idx + g_off:
93                    if iszero == 0:
94                        tmp += 2.7*28/20
95                else:
96                    if g_idx + g_off < (ei - 43) * 28/20:
97                        tmp += 2*ei*28/20 - 2*(g_idx + g_off) - 36*28/20
98                    else:
99                        tmp += ei*28/20 - (g_idx + g_off) + 7*28/20
100                    iszero = 0
101
102            if tmp > nbits * 1.4 * 28/20 and iszero == 0:
103                g_idx += factor
104
105        ### Limit gain index
106
107        x_max = np.amax(np.abs(x))
108        if x_max > 0:
109            g_min = 28 * np.log10(x_max / (32768 - 0.375))
110            g_min = np.ceil(g_min).astype(int) - g_off
111            reset_off = g_idx < g_min
112        else:
113            g_min = 0
114            reset_off = True
115
116        if reset_off:
117            g_idx = g_min
118
119        return (g_idx + g_off, reset_off)
120
121    def quantize(self, g_int, x):
122
123        xg = x / 10 ** (g_int / 28)
124
125        xq = np.where(xg < 0, np.ceil(xg - 0.375), np.floor(xg + 0.375))
126        xq = xq.astype(int)
127        xq = np.fmin(np.fmax(xq, -32768), 32767)
128
129        nz_pairs = np.any([ xq[::2] != 0, xq[1::2] != 0 ], axis=0)
130        lastnz = len(xq) - 2 * np.argmax(nz_pairs[-1::-1])
131        if not np.any(nz_pairs):
132            lastnz = 0
133
134        return (xg, xq, lastnz)
135
136    def compute_nbits(self, nbytes, x, lastnz, nbits_spec):
137
138        mode =   1 if nbytes >= 20 * (3 + self.sr) else 0
139        rate = 512 if nbytes >  20 * (1 + self.sr) else 0
140
141        nbits_est = 0
142        nbits_trunc = 0
143        nbits_lsb = 0
144        lastnz_trunc = 2
145        c = 0
146
147        for n in range(0, lastnz, 2):
148            t = c + rate
149            if n > len(x) // 2:
150                t += 256
151
152            a = abs(x[n  ])
153            b = abs(x[n+1])
154            lev = 0
155            while max(a, b) >= 4:
156                nbits_est += \
157                    T.AC_SPEC_BITS[T.AC_SPEC_LOOKUP[t + lev*1024]][16];
158                if lev == 0 and mode == 1:
159                    nbits_lsb += 2
160                else:
161                    nbits_est += 2 * 2048
162
163                a >>= 1
164                b >>= 1
165                lev = min(lev + 1, 3)
166
167            nbits_est += \
168                T.AC_SPEC_BITS[T.AC_SPEC_LOOKUP[t + lev*1024]][a + 4*b]
169
170            a_lsb = abs(x[n  ])
171            b_lsb = abs(x[n+1])
172            nbits_est += (min(a_lsb, 1) + min(b_lsb, 1)) * 2048
173            if lev > 0 and mode == 1:
174                a_lsb >>= 1;
175                b_lsb >>= 1;
176                nbits_lsb += int(a_lsb == 0 and x[n  ] != 0)
177                nbits_lsb += int(b_lsb == 0 and x[n+1] != 0)
178
179            if (x[n] != 0 or x[n+1] != 0) and \
180                    (nbits_est <= nbits_spec * 2048):
181                lastnz_trunc = n + 2;
182                nbits_trunc = nbits_est
183
184            t = 1 + (a + b) * (lev + 1) if lev <= 1 else 12 + lev;
185            c = (c & 15) * 16 + t;
186
187        nbits_est = (nbits_est + 2047) // 2048 + nbits_lsb;
188        nbits_trunc = (nbits_trunc + 2047) // 2048
189
190        self.rate = rate
191        self.lsb_mode = mode == 1 and nbits_est > nbits_spec
192
193        return (nbits_est, nbits_trunc, lastnz_trunc, self.lsb_mode)
194
195    def adjust_gain(self, g_idx, nbits, nbits_spec):
196
197        T1 = [  80,  230,  380,  530,  680 ]
198        T2 = [ 500, 1025, 1550, 2075, 2600 ]
199        T3 = [ 850, 1700, 2550, 3400, 4250 ]
200
201        sr = self.sr
202
203        if nbits < T1[sr]:
204            delta = (nbits + 48) / 16
205
206        elif nbits < T2[sr]:
207            a = T1[sr] / 16 + 3
208            b = T2[sr] / 48
209            delta = a + (nbits - T1[sr]) * (b - a) / (T2[sr] - T1[sr])
210
211        elif nbits < T3[sr]:
212            delta = nbits / 48
213
214        else:
215            delta = T3[sr] / 48;
216
217        delta = np.fix(delta + 0.5).astype(int)
218
219        if (g_idx < 255 and nbits > nbits_spec) or \
220           (g_idx >   0 and nbits < nbits_spec - (delta + 2)):
221
222            if nbits < nbits_spec - (delta + 2):
223                return - 1
224
225            if g_idx == 254 or nbits < nbits_spec + delta:
226                return 1
227
228            else:
229                return 2
230
231        return 0
232
233    def estimate_noise(self, bw, xq, lastnz, x):
234
235        (i_nf, nf_start, nf_stop) = self.get_noise_indices(bw, xq, lastnz)
236
237        nf = 8 - 16 * sum(abs(x[nf_start:nf_stop] * i_nf)) / sum(i_nf) \
238                if sum(i_nf) > 0 else 0
239
240        return min(max(np.rint(nf).astype(int), 0), 7)
241
242    def run(self,
243        bw, nbytes, nbits_bw, nbits_ltpf, nbits_sns, nbits_tns, x):
244
245        sr = self.sr
246
247        ### Bit budget
248
249        nbits_gain = 8
250        nbits_nf   = 3
251
252        nbits_ari  = np.ceil(np.log2(len(x) / 2)).astype(int)
253        nbits_ari += 3 + min((8*nbytes - 1) // 1280, 2)
254
255        nbits_spec = 8*nbytes - \
256            nbits_bw - nbits_ltpf - nbits_sns - nbits_tns - \
257            nbits_gain - nbits_nf - nbits_ari
258
259        ### Global gain estimation
260
261        nbits_off = self.nbits_off + self.nbits_spec - self.nbits_est
262        nbits_off = min(40, max(-40, nbits_off))
263
264        nbits_off = 0 if self.reset_off else \
265                    0.8 * self.nbits_off + 0.2 * nbits_off
266
267        g_off = self.get_gain_offset(nbytes)
268
269        (g_int, self.reset_off) = \
270            self.estimate_gain(x, nbits_spec, nbits_off, g_off)
271        self.nbits_off = nbits_off
272        self.nbits_spec = nbits_spec
273
274        ### Quantization
275
276        (xg, xq, lastnz) = self.quantize(g_int, x)
277
278        (nbits_est, nbits_trunc, lastnz_trunc, _) = \
279            self.compute_nbits(nbytes, xq, lastnz, nbits_spec)
280
281        self.nbits_est = nbits_est
282
283        ### Adjust gain and requantize
284
285        g_adj = self.adjust_gain(g_int - g_off, nbits_est, nbits_spec)
286
287        (xg, xq, lastnz) = self.quantize(g_adj, xg)
288
289        (nbits_est, nbits_trunc, lastnz_trunc, lsb_mode) = \
290            self.compute_nbits(nbytes, xq, lastnz, nbits_spec)
291
292        self.g_idx = g_int + g_adj - g_off
293        self.xq = xq
294        self.lastnz = lastnz_trunc
295
296        self.nbits_residual_max = nbits_spec - nbits_trunc + 4
297        self.xg = xg
298
299        ### Noise factor
300
301        self.noise_factor = self.estimate_noise(bw, xq, lastnz, x)
302
303        return (self.xq, self.lastnz, self.xg)
304
305    def store(self, b):
306
307        ne = T.NE[self.dt][self.sr]
308        nbits_lastnz = np.ceil(np.log2(ne/2)).astype(int)
309
310        b.write_uint((self.lastnz >> 1) - 1, nbits_lastnz)
311        b.write_uint(self.lsb_mode, 1)
312
313    def encode(self, bits):
314
315        ### Noise factor
316
317        bits.write_uint(self.noise_factor, 3)
318
319        ### Quantized data
320
321        lsbs = []
322
323        x = self.xq
324        c = 0
325
326        for n in range(0, self.lastnz, 2):
327            t = c + self.rate
328            if n > len(x) // 2:
329                t += 256
330
331            a = abs(x[n  ])
332            b = abs(x[n+1])
333            lev = 0
334            while max(a, b) >= 4:
335
336                bits.ac_encode(
337                    T.AC_SPEC_CUMFREQ[T.AC_SPEC_LOOKUP[t + lev*1024]][16],
338                    T.AC_SPEC_FREQ[T.AC_SPEC_LOOKUP[t + lev*1024]][16])
339
340                if lev == 0 and self.lsb_mode:
341                    lsb_0 = a & 1
342                    lsb_1 = b & 1
343                else:
344                    bits.write_bit(a & 1)
345                    bits.write_bit(b & 1)
346
347                a >>= 1
348                b >>= 1
349                lev = min(lev + 1, 3)
350
351            bits.ac_encode(
352                T.AC_SPEC_CUMFREQ[T.AC_SPEC_LOOKUP[t + lev*1024]][a + 4*b],
353                T.AC_SPEC_FREQ[T.AC_SPEC_LOOKUP[t + lev*1024]][a + 4*b])
354
355            a_lsb = abs(x[n  ])
356            b_lsb = abs(x[n+1])
357            if lev > 0 and self.lsb_mode:
358                a_lsb >>= 1
359                b_lsb >>= 1
360
361                lsbs.append(lsb_0)
362                if a_lsb == 0 and x[n+0] != 0:
363                    lsbs.append(int(x[n+0] < 0))
364
365                lsbs.append(lsb_1)
366                if b_lsb == 0 and x[n+1] != 0:
367                    lsbs.append(int(x[n+1] < 0))
368
369            if a_lsb > 0:
370                bits.write_bit(int(x[n+0] < 0))
371
372            if b_lsb > 0:
373                bits.write_bit(int(x[n+1] < 0))
374
375            t = 1 + (a + b) * (lev + 1) if lev <= 1 else 12 + lev;
376            c = (c & 15) * 16 + t;
377
378        ### Residual data
379
380        if self.lsb_mode == 0:
381            nbits_residual = min(bits.get_bits_left(), self.nbits_residual_max)
382
383            for i in range(len(self.xg)):
384
385                if self.xq[i] == 0:
386                    continue
387
388                bits.write_bit(self.xg[i] >= self.xq[i])
389                nbits_residual -= 1
390                if nbits_residual <= 0:
391                    break
392
393        else:
394            nbits_residual = min(bits.get_bits_left(), len(lsbs))
395            for lsb in lsbs[:nbits_residual]:
396                bits.write_bit(lsb)
397
398
399class SpectrumSynthesis(SpectrumQuantization):
400
401    def __init__(self, dt, sr):
402
403        super().__init__(dt, sr)
404
405    def fill_noise(self, bw, x, lastnz, f_nf, nf_seed):
406
407        (i_nf, nf_start, nf_stop) = self.get_noise_indices(bw, x, lastnz)
408
409        k_nf = nf_start +  np.argwhere(i_nf)
410        l_nf = (8 - f_nf)/16
411
412        for k in k_nf:
413            nf_seed = (13849 + nf_seed * 31821) & 0xffff
414            x[k] = [ -l_nf, l_nf ][nf_seed < 0x8000]
415
416        return x
417
418    def load(self, b):
419
420        ne = T.NE[self.dt][self.sr]
421        nbits_lastnz = np.ceil(np.log2(ne/2)).astype(int)
422
423        self.lastnz = (b.read_uint(nbits_lastnz) + 1) << 1
424        self.lsb_mode = b.read_uint(1)
425        self.g_idx = b.read_uint(8)
426
427        if self.lastnz > ne:
428            raise ValueError('Invalid count of coded samples')
429
430    def decode(self, bits, bw, nbytes):
431
432        ### Noise factor
433
434        f_nf = bits.read_uint(3)
435
436        ### Quantized data
437
438        x = np.zeros(T.NE[self.dt][self.sr])
439        rate = 512 if nbytes >  20 * (1 + self.sr) else 0
440
441        levs = np.zeros(len(x), dtype=np.int)
442        c = 0
443
444        for n in range(0, self.lastnz, 2):
445            t = c + rate
446            if n > len(x) // 2:
447                t += 256
448
449            for lev in range(14):
450
451                s = t + min(lev, 3) * 1024
452
453                sym = bits.ac_decode(
454                    T.AC_SPEC_CUMFREQ[T.AC_SPEC_LOOKUP[s]],
455                    T.AC_SPEC_FREQ[T.AC_SPEC_LOOKUP[s]])
456
457                if sym < 16:
458                    break
459
460                if self.lsb_mode == 0 or lev > 0:
461                    x[n  ] += bits.read_bit() << lev
462                    x[n+1] += bits.read_bit() << lev
463
464            if lev >= 14:
465                raise ValueError('Out of range value')
466
467            a = sym %  4
468            b = sym // 4
469
470            levs[n  ] = lev
471            levs[n+1] = lev
472
473            x[n  ] += a << lev
474            x[n+1] += b << lev
475
476            if x[n] and bits.read_bit():
477                x[n] = -x[n]
478
479            if x[n+1] and bits.read_bit():
480                x[n+1] = -x[n+1]
481
482            lev = min(lev, 3)
483            t = 1 + (a + b) * (lev + 1) if lev <= 1 else 12 + lev;
484            c = (c & 15) * 16 + t;
485
486        ### Residual data
487
488        nbits_residual = bits.get_bits_left()
489        if nbits_residual < 0:
490            raise ValueError('Out of bitstream')
491
492        if self.lsb_mode == 0:
493
494            xr = np.zeros(len(x), dtype=np.bool)
495
496            for i in range(len(x)):
497
498                if nbits_residual <= 0:
499                    xr.resize(i)
500                    break
501
502                if x[i] == 0:
503                    continue
504
505                xr[i] = bits.read_bit()
506                nbits_residual -= 1
507
508        else:
509
510            for i in range(len(levs)):
511
512                if nbits_residual <= 0:
513                    break
514
515                if levs[i] <= 0:
516                    continue
517
518                lsb = bits.read_bit()
519                nbits_residual -= 1
520                if not lsb:
521                    continue
522
523                sign = int(x[i] < 0)
524
525                if x[i] == 0:
526
527                    if nbits_residual <= 0:
528                        break
529
530                    sign = bits.read_bit()
531                    nbits_residual -= 1
532
533                x[i] += [ 1, -1 ][sign]
534
535        ### Set residual and noise
536
537        nf_seed = sum(abs(x.astype(np.int)) * range(len(x)))
538
539        zero_frame = (self.lastnz <= 2 and x[0] == 0 and x[1] == 0
540                      and self.g_idx <= 0 and nf >= 7)
541
542        if self.lsb_mode == 0:
543
544            for i in range(len(xr)):
545
546                if x[i] and xr[i] == 0:
547                    x[i] += [ -0.1875, -0.3125 ][x[i] < 0]
548                elif x[i]:
549                    x[i] += [  0.1875,  0.3125 ][x[i] > 0]
550
551        if not zero_frame:
552            x = self.fill_noise(bw, x, self.lastnz, f_nf, nf_seed)
553
554        ### Rescale coefficients
555
556        g_int = self.get_gain_offset(nbytes) + self.g_idx
557        x *= 10 ** (g_int / 28)
558
559        return x
560
561
562def initial_state():
563    return { 'nbits_off' : 0.0, 'nbits_spare' : 0 }
564
565
566### ------------------------------------------------------------------------ ###
567
568def check_estimate_gain(rng, dt, sr):
569
570    ne = T.I[dt][sr][-1]
571    ok = True
572
573    analysis = SpectrumAnalysis(dt, sr)
574
575    for i in range(10):
576        x = rng.random(ne) * i * 1e2
577
578        nbytes = 20 + int(rng.random() * 100)
579        nbits_budget = 8 * nbytes - int(rng.random() * 100)
580        nbits_off = rng.random() * 10
581        g_off = 10 - int(rng.random() * 20)
582
583        (g_int, reset_off) = \
584            analysis.estimate_gain(x, nbits_budget, nbits_off, g_off)
585
586        (g_int_c, reset_off_c) = lc3.spec_estimate_gain(
587            dt, sr, x, nbits_budget, nbits_off, -g_off)
588
589        ok = ok and g_int_c == g_int
590        ok = ok and reset_off_c == reset_off
591
592    return ok
593
594def check_quantization(rng, dt, sr):
595
596    ne = T.I[dt][sr][-1]
597    ok = True
598
599    analysis = SpectrumAnalysis(dt, sr)
600
601    for g_int in range(-128, 128):
602
603        x = rng.random(ne) *  1e2
604        nbytes = 20 + int(rng.random() * 30)
605
606        (xg, xq, nq) = analysis.quantize(g_int, x)
607        (xg_c, xq_c, nq_c) = lc3.spec_quantize(dt, sr, g_int, x)
608
609        ok = ok and np.amax(np.abs(1 - xg_c/xg)) < 1e-6
610        ok = ok and np.any(abs(xq_c - xq) < 1)
611        ok = ok and nq_c == nq
612
613    return ok
614
615def check_compute_nbits(rng, dt, sr):
616
617    ne = T.I[dt][sr][-1]
618    ok = True
619
620    analysis = SpectrumAnalysis(dt, sr)
621
622    for nbytes in range(20, 150):
623
624        nbits_budget = nbytes * 8 - int(rng.random() * 100)
625        xq = (rng.random(ne) * 8).astype(int)
626        nq = ne // 2 + int(rng.random() * ne // 2)
627
628        nq = nq - nq % 2
629        if xq[nq-2] == 0 and xq[nq-1] == 0:
630            xq[nq-2] = 1
631
632        (nbits, nbits_trunc, nq_trunc, lsb_mode) = \
633            analysis.compute_nbits(nbytes, xq, nq, nbits_budget)
634
635        (nbits_c, nq_c, _) = \
636            lc3.spec_compute_nbits(dt, sr, nbytes, xq, nq, 0)
637
638        (nbits_trunc_c, nq_trunc_c, lsb_mode_c) = \
639            lc3.spec_compute_nbits(dt, sr, nbytes, xq, nq, nbits_budget)
640
641        ok = ok and nbits_c == nbits
642        ok = ok and nbits_trunc_c == nbits_trunc
643        ok = ok and nq_trunc_c == nq_trunc
644        ok = ok and lsb_mode_c == lsb_mode
645
646    return ok
647
648def check_adjust_gain(rng, dt, sr):
649
650    ne = T.I[dt][sr][-1]
651    ok = True
652
653    analysis = SpectrumAnalysis(dt, sr)
654
655    for g_idx in (0, 128, 254, 255):
656        for nbits in range(50, 5000, 5):
657            nbits_budget = int(nbits * (0.95 + (rng.random() * 0.1)))
658
659            g_adj = analysis.adjust_gain(g_idx, nbits, nbits_budget)
660
661            g_adj_c = lc3.spec_adjust_gain(sr, g_idx, nbits, nbits_budget)
662
663            ok = ok and g_adj_c == g_adj
664
665    return ok
666
667def check_unit(rng, dt, sr):
668
669    ns = T.NS[dt][sr]
670    ne = T.I[dt][sr][-1]
671    ok = True
672
673    state_c = initial_state()
674
675    bwdet = m_bwdet.BandwidthDetector(dt, sr)
676    ltpf = m_ltpf.LtpfAnalysis(dt, sr)
677    tns = m_tns.TnsAnalysis(dt)
678    sns = m_sns.SnsAnalysis(dt, sr)
679    analysis = SpectrumAnalysis(dt, sr)
680
681    nbytes = 100
682
683    for i in range(10):
684
685        x = rng.random(ns) * 1e4
686        e = rng.random(min(len(x), 64)) * 1e10
687
688        bwdet.run(e)
689        pitch_present = ltpf.run(x)
690        tns.run(x[:ne], sr, False, nbytes)
691        sns.run(e, False, x)
692
693        (xq, nq, _) = analysis.run(sr, nbytes, bwdet.get_nbits(),
694            ltpf.get_nbits(), sns.get_nbits(), tns.get_nbits(), x[:ne])
695
696        (_, xq_c, side_c) = lc3.spec_analyze(
697            dt, sr, nbytes, pitch_present, tns.get_data(), state_c, x[:ne])
698
699        ok = ok and side_c['g_idx'] == analysis.g_idx
700        ok = ok and side_c['nq'] == nq
701        ok = ok and np.any(abs(xq_c - xq) < 1)
702
703    return ok
704
705def check_noise(rng, dt, bw):
706
707    ne = T.NE[dt][bw]
708    ok = True
709
710    analysis = SpectrumAnalysis(dt, bw)
711
712    for i in range(10):
713
714        xq = ((rng.random(ne) - 0.5) * 10 ** (0.5)).astype(int)
715        nq = ne - int(rng.random() * 5)
716        x  = rng.random(ne) * i * 1e-1
717
718        nf = analysis.estimate_noise(bw, xq, nq, x)
719        nf_c = lc3.spec_estimate_noise(dt, bw, xq, nq, x)
720
721        ok = ok and nf_c == nf
722
723    return ok
724
725def check_appendix_c(dt):
726
727    sr = T.SRATE_16K
728    ne = T.NE[dt][sr]
729    ok = True
730
731    state_c = initial_state()
732
733    for i in range(len(C.X_F[dt])):
734
735        g_int = lc3.spec_estimate_gain(dt, sr, C.X_F[dt][i],
736            C.NBITS_SPEC[dt][i], C.NBITS_OFFSET[dt][i], -C.GG_OFF[dt][i])[0]
737        ok = ok and g_int == C.GG_IND[dt][i] + C.GG_OFF[dt][i]
738
739        (_, xq, nq) = lc3.spec_quantize(dt, sr,
740            C.GG_IND[dt][i] + C.GG_OFF[dt][i], C.X_F[dt][i])
741        ok = ok and np.any((xq - C.X_Q[dt][i]) == 0)
742        ok = ok and nq == C.LASTNZ[dt][i]
743
744        nbits = lc3.spec_compute_nbits(dt, sr,
745            C.NBYTES[dt], C.X_Q[dt][i], C.LASTNZ[dt][i], 0)[0]
746        ok = ok and nbits == C.NBITS_EST[dt][i]
747
748        g_adj = lc3.spec_adjust_gain(sr,
749            C.GG_IND[dt][i], C.NBITS_EST[dt][i], C.NBITS_SPEC[dt][i])
750        ok = ok and g_adj == C.GG_IND_ADJ[dt][i] - C.GG_IND[dt][i]
751
752        if C.GG_IND_ADJ[dt][i] != C.GG_IND[dt][i]:
753
754            (_, xq, nq) = lc3.spec_quantize(dt, sr,
755                C.GG_IND_ADJ[dt][i] + C.GG_OFF[dt][i], C.X_F[dt][i])
756            lastnz = C.LASTNZ_REQ[dt][i]
757            ok = ok and np.any(((xq - C.X_Q_REQ[dt][i])[:lastnz]) == 0)
758
759        tns_data = {
760            'nfilters' : C.NUM_TNS_FILTERS[dt][i],
761            'lpc_weighting' : [ True, True ],
762            'rc_order' : [ C.RC_ORDER[dt][i][0], 0 ],
763            'rc' : [ C.RC_I_1[dt][i] - 8, np.zeros(8, dtype = np.int) ]
764        }
765
766        (x, xq, side) = lc3.spec_analyze(dt, sr, C.NBYTES[dt],
767            C.PITCH_PRESENT[dt][i], tns_data, state_c, C.X_F[dt][i])
768
769        ok = ok and np.abs(state_c['nbits_off'] - C.NBITS_OFFSET[dt][i]) < 1e-5
770        if C.GG_IND_ADJ[dt][i] != C.GG_IND[dt][i]:
771            xq = C.X_Q_REQ[dt][i]
772            nq = C.LASTNZ_REQ[dt][i]
773            ok = ok and side['g_idx'] == C.GG_IND_ADJ[dt][i]
774            ok = ok and side['nq'] == nq
775            ok = ok and np.any(((xq[:nq] - xq[:nq])) == 0)
776        else:
777            xq = C.X_Q[dt][i]
778            nq = C.LASTNZ[dt][i]
779            ok = ok and side['g_idx'] == C.GG_IND[dt][i]
780            ok = ok and side['nq'] == nq
781            ok = ok and np.any((xq[:nq] - C.X_Q[dt][i][:nq]) == 0)
782        ok = ok and side['lsb_mode'] == C.LSB_MODE[dt][i]
783
784        gg = C.GG[dt][i] if C.GG_IND_ADJ[dt][i] == C.GG_IND[dt][i] \
785                else C.GG_ADJ[dt][i]
786
787        nf = lc3.spec_estimate_noise(dt, C.P_BW[dt][i],
788                xq, nq, C.X_F[dt][i] / gg)
789        ok = ok and nf == C.F_NF[dt][i]
790
791    return ok
792
793def check():
794
795    rng = np.random.default_rng(1234)
796    ok = True
797
798    for dt in range(T.NUM_DT):
799        for sr in range(T.NUM_SRATE):
800            ok = ok and check_estimate_gain(rng, dt, sr)
801            ok = ok and check_quantization(rng, dt, sr)
802            ok = ok and check_compute_nbits(rng, dt, sr)
803            ok = ok and check_adjust_gain(rng, dt, sr)
804            ok = ok and check_unit(rng, dt, sr)
805            ok = ok and check_noise(rng, dt, sr)
806
807    for dt in range(T.NUM_DT):
808        ok = ok and check_appendix_c(dt)
809
810    return ok
811
812### ------------------------------------------------------------------------ ###
813