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