xref: /aosp_15_r20/external/liblc3/test/sns.py (revision 49fe348c0058011ee60b6957cdd9d52742df84bc)
1*49fe348cSAndroid Build Coastguard Worker#
2*49fe348cSAndroid Build Coastguard Worker# Copyright 2022 Google LLC
3*49fe348cSAndroid Build Coastguard Worker#
4*49fe348cSAndroid Build Coastguard Worker# Licensed under the Apache License, Version 2.0 (the "License");
5*49fe348cSAndroid Build Coastguard Worker# you may not use this file except in compliance with the License.
6*49fe348cSAndroid Build Coastguard Worker# You may obtain a copy of the License at
7*49fe348cSAndroid Build Coastguard Worker#
8*49fe348cSAndroid Build Coastguard Worker#     http://www.apache.org/licenses/LICENSE-2.0
9*49fe348cSAndroid Build Coastguard Worker#
10*49fe348cSAndroid Build Coastguard Worker# Unless required by applicable law or agreed to in writing, software
11*49fe348cSAndroid Build Coastguard Worker# distributed under the License is distributed on an "AS IS" BASIS,
12*49fe348cSAndroid Build Coastguard Worker# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13*49fe348cSAndroid Build Coastguard Worker# See the License for the specific language governing permissions and
14*49fe348cSAndroid Build Coastguard Worker# limitations under the License.
15*49fe348cSAndroid Build Coastguard Worker#
16*49fe348cSAndroid Build Coastguard Worker
17*49fe348cSAndroid Build Coastguard Workerimport numpy as np
18*49fe348cSAndroid Build Coastguard Workerimport scipy.fftpack as fftpack
19*49fe348cSAndroid Build Coastguard Worker
20*49fe348cSAndroid Build Coastguard Workerimport lc3
21*49fe348cSAndroid Build Coastguard Workerimport tables as T, appendix_c as C
22*49fe348cSAndroid Build Coastguard Worker
23*49fe348cSAndroid Build Coastguard Worker### ------------------------------------------------------------------------ ###
24*49fe348cSAndroid Build Coastguard Worker
25*49fe348cSAndroid Build Coastguard Workerclass Sns:
26*49fe348cSAndroid Build Coastguard Worker
27*49fe348cSAndroid Build Coastguard Worker    def __init__(self, dt, sr):
28*49fe348cSAndroid Build Coastguard Worker
29*49fe348cSAndroid Build Coastguard Worker        self.dt = dt
30*49fe348cSAndroid Build Coastguard Worker        self.sr = sr
31*49fe348cSAndroid Build Coastguard Worker        self.I = T.I[dt][sr]
32*49fe348cSAndroid Build Coastguard Worker
33*49fe348cSAndroid Build Coastguard Worker        (self.ind_lf, self.ind_hf, self.shape, self.gain) = \
34*49fe348cSAndroid Build Coastguard Worker            (None, None, None, None)
35*49fe348cSAndroid Build Coastguard Worker
36*49fe348cSAndroid Build Coastguard Worker        (self.idx_a, self.ls_a, self.idx_b, self.ls_b) = \
37*49fe348cSAndroid Build Coastguard Worker            (None, None, None, None)
38*49fe348cSAndroid Build Coastguard Worker
39*49fe348cSAndroid Build Coastguard Worker    def get_data(self):
40*49fe348cSAndroid Build Coastguard Worker
41*49fe348cSAndroid Build Coastguard Worker        data = { 'lfcb' : self.ind_lf, 'hfcb' : self.ind_hf,
42*49fe348cSAndroid Build Coastguard Worker                 'shape' : self.shape, 'gain' : self.gain,
43*49fe348cSAndroid Build Coastguard Worker                 'idx_a' : self.idx_a, 'ls_a' : self.ls_a }
44*49fe348cSAndroid Build Coastguard Worker
45*49fe348cSAndroid Build Coastguard Worker        if self.idx_b is not None:
46*49fe348cSAndroid Build Coastguard Worker            data.update({ 'idx_b' : self.idx_b, 'ls_b' : self.ls_b })
47*49fe348cSAndroid Build Coastguard Worker
48*49fe348cSAndroid Build Coastguard Worker        return data
49*49fe348cSAndroid Build Coastguard Worker
50*49fe348cSAndroid Build Coastguard Worker    def get_nbits(self):
51*49fe348cSAndroid Build Coastguard Worker
52*49fe348cSAndroid Build Coastguard Worker        return 38
53*49fe348cSAndroid Build Coastguard Worker
54*49fe348cSAndroid Build Coastguard Worker    def spectral_shaping(self, scf, inv, x):
55*49fe348cSAndroid Build Coastguard Worker
56*49fe348cSAndroid Build Coastguard Worker        ## Scale factors interpolation
57*49fe348cSAndroid Build Coastguard Worker
58*49fe348cSAndroid Build Coastguard Worker        scf_i = np.empty(4*len(scf))
59*49fe348cSAndroid Build Coastguard Worker        scf_i[0     ] = scf[0]
60*49fe348cSAndroid Build Coastguard Worker        scf_i[1     ] = scf[0]
61*49fe348cSAndroid Build Coastguard Worker        scf_i[2:62:4] = scf[:15] + 1/8 * (scf[1:] - scf[:15])
62*49fe348cSAndroid Build Coastguard Worker        scf_i[3:63:4] = scf[:15] + 3/8 * (scf[1:] - scf[:15])
63*49fe348cSAndroid Build Coastguard Worker        scf_i[4:64:4] = scf[:15] + 5/8 * (scf[1:] - scf[:15])
64*49fe348cSAndroid Build Coastguard Worker        scf_i[5:64:4] = scf[:15] + 7/8 * (scf[1:] - scf[:15])
65*49fe348cSAndroid Build Coastguard Worker        scf_i[62    ] = scf[15 ] + 1/8 * (scf[15] - scf[14 ])
66*49fe348cSAndroid Build Coastguard Worker        scf_i[63    ] = scf[15 ] + 3/8 * (scf[15] - scf[14 ])
67*49fe348cSAndroid Build Coastguard Worker
68*49fe348cSAndroid Build Coastguard Worker        nb = len(self.I) - 1
69*49fe348cSAndroid Build Coastguard Worker
70*49fe348cSAndroid Build Coastguard Worker        if nb < 32:
71*49fe348cSAndroid Build Coastguard Worker            n4 = round(abs(1-32/nb)*nb)
72*49fe348cSAndroid Build Coastguard Worker            n2 = nb - n4
73*49fe348cSAndroid Build Coastguard Worker
74*49fe348cSAndroid Build Coastguard Worker            for i in range(n4):
75*49fe348cSAndroid Build Coastguard Worker                scf_i[i] = np.mean(scf_i[4*i:4*i+4])
76*49fe348cSAndroid Build Coastguard Worker
77*49fe348cSAndroid Build Coastguard Worker            for i in range(n4, n4+n2):
78*49fe348cSAndroid Build Coastguard Worker                scf_i[i] = np.mean(scf_i[2*n4+2*i:2*n4+2*i+2])
79*49fe348cSAndroid Build Coastguard Worker
80*49fe348cSAndroid Build Coastguard Worker            scf_i = scf_i[:n4+n2]
81*49fe348cSAndroid Build Coastguard Worker
82*49fe348cSAndroid Build Coastguard Worker        elif nb < 64:
83*49fe348cSAndroid Build Coastguard Worker            n2 = 64 - nb
84*49fe348cSAndroid Build Coastguard Worker
85*49fe348cSAndroid Build Coastguard Worker            for i in range(n2):
86*49fe348cSAndroid Build Coastguard Worker                scf_i[i] = np.mean(scf_i[2*i:2*i+2])
87*49fe348cSAndroid Build Coastguard Worker            scf_i = np.append(scf_i[:n2], scf_i[2*n2:])
88*49fe348cSAndroid Build Coastguard Worker
89*49fe348cSAndroid Build Coastguard Worker        g_sns = np.power(2, [ -scf_i, scf_i ][inv])
90*49fe348cSAndroid Build Coastguard Worker
91*49fe348cSAndroid Build Coastguard Worker        ## Spectral shaping
92*49fe348cSAndroid Build Coastguard Worker
93*49fe348cSAndroid Build Coastguard Worker        y = np.empty(len(x))
94*49fe348cSAndroid Build Coastguard Worker        I = self.I
95*49fe348cSAndroid Build Coastguard Worker
96*49fe348cSAndroid Build Coastguard Worker        for b in range(nb):
97*49fe348cSAndroid Build Coastguard Worker            y[I[b]:I[b+1]] = x[I[b]:I[b+1]] * g_sns[b]
98*49fe348cSAndroid Build Coastguard Worker
99*49fe348cSAndroid Build Coastguard Worker        return y
100*49fe348cSAndroid Build Coastguard Worker
101*49fe348cSAndroid Build Coastguard Worker
102*49fe348cSAndroid Build Coastguard Workerclass SnsAnalysis(Sns):
103*49fe348cSAndroid Build Coastguard Worker
104*49fe348cSAndroid Build Coastguard Worker    def __init__(self, dt, sr):
105*49fe348cSAndroid Build Coastguard Worker
106*49fe348cSAndroid Build Coastguard Worker        super().__init__(dt, sr)
107*49fe348cSAndroid Build Coastguard Worker
108*49fe348cSAndroid Build Coastguard Worker    def compute_scale_factors(self, e, att, nbytes):
109*49fe348cSAndroid Build Coastguard Worker
110*49fe348cSAndroid Build Coastguard Worker        dt = self.dt
111*49fe348cSAndroid Build Coastguard Worker        sr = self.sr
112*49fe348cSAndroid Build Coastguard Worker        hr = self.sr >= T.SRATE_48K_HR
113*49fe348cSAndroid Build Coastguard Worker
114*49fe348cSAndroid Build Coastguard Worker        ## Padding
115*49fe348cSAndroid Build Coastguard Worker
116*49fe348cSAndroid Build Coastguard Worker        if len(e) < 32:
117*49fe348cSAndroid Build Coastguard Worker            n4 = round(abs(1-32/len(e))*len(e))
118*49fe348cSAndroid Build Coastguard Worker            n2 = len(e) - n4
119*49fe348cSAndroid Build Coastguard Worker
120*49fe348cSAndroid Build Coastguard Worker            e = np.append(np.zeros(3*n4+n2), e)
121*49fe348cSAndroid Build Coastguard Worker            for i in range(n4):
122*49fe348cSAndroid Build Coastguard Worker                e[4*i+0] = e[4*i+1] = \
123*49fe348cSAndroid Build Coastguard Worker                e[4*i+2] = e[4*i+3] = e[3*n4+n2+i]
124*49fe348cSAndroid Build Coastguard Worker
125*49fe348cSAndroid Build Coastguard Worker            for i in range(2*n4, 2*n4+n2):
126*49fe348cSAndroid Build Coastguard Worker                e[2*i+0] = e[2*i+1] = e[2*n4+n2+i]
127*49fe348cSAndroid Build Coastguard Worker
128*49fe348cSAndroid Build Coastguard Worker        elif len(e) < 64:
129*49fe348cSAndroid Build Coastguard Worker            n2 = 64 - len(e)
130*49fe348cSAndroid Build Coastguard Worker
131*49fe348cSAndroid Build Coastguard Worker            e = np.append(np.empty(n2), e)
132*49fe348cSAndroid Build Coastguard Worker            for i in range(n2):
133*49fe348cSAndroid Build Coastguard Worker                e[2*i+0] = e[2*i+1] = e[n2+i]
134*49fe348cSAndroid Build Coastguard Worker
135*49fe348cSAndroid Build Coastguard Worker        ## Smoothing
136*49fe348cSAndroid Build Coastguard Worker
137*49fe348cSAndroid Build Coastguard Worker        e_s = np.zeros(len(e))
138*49fe348cSAndroid Build Coastguard Worker        e_s[0   ] = 0.75 * e[0   ] + 0.25 * e[1   ]
139*49fe348cSAndroid Build Coastguard Worker        e_s[1:63] = 0.25 * e[0:62] + 0.5  * e[1:63] + 0.25 * e[2:64]
140*49fe348cSAndroid Build Coastguard Worker        e_s[  63] = 0.25 * e[  62] + 0.75 * e[  63]
141*49fe348cSAndroid Build Coastguard Worker
142*49fe348cSAndroid Build Coastguard Worker        ## Pre-emphasis
143*49fe348cSAndroid Build Coastguard Worker
144*49fe348cSAndroid Build Coastguard Worker        g_tilt = [ 14, 18, 22, 26, 30, 30, 34 ][self.sr]
145*49fe348cSAndroid Build Coastguard Worker        e_p = e_s * (10 ** ((np.arange(64) * g_tilt) / 630))
146*49fe348cSAndroid Build Coastguard Worker
147*49fe348cSAndroid Build Coastguard Worker        ## Noise floor
148*49fe348cSAndroid Build Coastguard Worker
149*49fe348cSAndroid Build Coastguard Worker        noise_floor = max(np.average(e_p) * (10 ** (-40/10)), 2 ** -32)
150*49fe348cSAndroid Build Coastguard Worker        e_p = np.fmax(e_p, noise_floor * np.ones(len(e)))
151*49fe348cSAndroid Build Coastguard Worker
152*49fe348cSAndroid Build Coastguard Worker        ## Logarithm
153*49fe348cSAndroid Build Coastguard Worker
154*49fe348cSAndroid Build Coastguard Worker        e_l = np.log2(10 ** -31 + e_p) / 2
155*49fe348cSAndroid Build Coastguard Worker
156*49fe348cSAndroid Build Coastguard Worker        ## Band energy grouping
157*49fe348cSAndroid Build Coastguard Worker
158*49fe348cSAndroid Build Coastguard Worker        w = [ 1/12, 2/12, 3/12, 3/12, 2/12, 1/12 ]
159*49fe348cSAndroid Build Coastguard Worker
160*49fe348cSAndroid Build Coastguard Worker        e_4 = np.zeros(len(e_l) // 4)
161*49fe348cSAndroid Build Coastguard Worker        e_4[0   ] = w[0] * e_l[0] + np.sum(w[1:] * e_l[:5])
162*49fe348cSAndroid Build Coastguard Worker        e_4[1:15] = [ np.sum(w * e_l[4*i-1:4*i+5]) for i in range(1, 15) ]
163*49fe348cSAndroid Build Coastguard Worker        e_4[  15] = np.sum(w[:5] * e_l[59:64]) + w[5] * e_l[63]
164*49fe348cSAndroid Build Coastguard Worker
165*49fe348cSAndroid Build Coastguard Worker        ## Mean removal and scaling, attack handling
166*49fe348cSAndroid Build Coastguard Worker
167*49fe348cSAndroid Build Coastguard Worker        cf = [ 0.85, 0.6 ][hr]
168*49fe348cSAndroid Build Coastguard Worker        if hr and nbytes * 8 > [ 1150, 2300, 0, 4400 ][self.dt]:
169*49fe348cSAndroid Build Coastguard Worker            cf *= [ 0.25, 0.35 ][ self.dt == T.DT_10M ]
170*49fe348cSAndroid Build Coastguard Worker
171*49fe348cSAndroid Build Coastguard Worker        scf = cf * (e_4 - np.average(e_4))
172*49fe348cSAndroid Build Coastguard Worker
173*49fe348cSAndroid Build Coastguard Worker        scf_a = np.zeros(len(scf))
174*49fe348cSAndroid Build Coastguard Worker        scf_a[0   ] = np.mean(scf[:3])
175*49fe348cSAndroid Build Coastguard Worker        scf_a[1   ] = np.mean(scf[:4])
176*49fe348cSAndroid Build Coastguard Worker        scf_a[2:14] = [ np.mean(scf[i:i+5]) for i in range(12) ]
177*49fe348cSAndroid Build Coastguard Worker        scf_a[  14] = np.mean(scf[12:])
178*49fe348cSAndroid Build Coastguard Worker        scf_a[  15] = np.mean(scf[13:])
179*49fe348cSAndroid Build Coastguard Worker
180*49fe348cSAndroid Build Coastguard Worker        scf_a = (0.5 if self.dt != T.DT_7M5 else 0.3) * \
181*49fe348cSAndroid Build Coastguard Worker                (scf_a - np.average(scf_a))
182*49fe348cSAndroid Build Coastguard Worker
183*49fe348cSAndroid Build Coastguard Worker        return scf_a if att else scf
184*49fe348cSAndroid Build Coastguard Worker
185*49fe348cSAndroid Build Coastguard Worker    def enum_mpvq(self, v):
186*49fe348cSAndroid Build Coastguard Worker
187*49fe348cSAndroid Build Coastguard Worker        sign = None
188*49fe348cSAndroid Build Coastguard Worker        index = 0
189*49fe348cSAndroid Build Coastguard Worker        x = 0
190*49fe348cSAndroid Build Coastguard Worker
191*49fe348cSAndroid Build Coastguard Worker        for (n, vn) in enumerate(v[::-1]):
192*49fe348cSAndroid Build Coastguard Worker
193*49fe348cSAndroid Build Coastguard Worker            if sign is not None and vn != 0:
194*49fe348cSAndroid Build Coastguard Worker                index = 2*index + sign
195*49fe348cSAndroid Build Coastguard Worker            if vn != 0:
196*49fe348cSAndroid Build Coastguard Worker                sign = 1 if vn < 0 else 0
197*49fe348cSAndroid Build Coastguard Worker
198*49fe348cSAndroid Build Coastguard Worker            index += T.SNS_MPVQ_OFFSETS[n][x]
199*49fe348cSAndroid Build Coastguard Worker            x += abs(vn)
200*49fe348cSAndroid Build Coastguard Worker
201*49fe348cSAndroid Build Coastguard Worker        return (index, bool(sign))
202*49fe348cSAndroid Build Coastguard Worker
203*49fe348cSAndroid Build Coastguard Worker    def quantize(self, scf):
204*49fe348cSAndroid Build Coastguard Worker
205*49fe348cSAndroid Build Coastguard Worker        ## Stage 1
206*49fe348cSAndroid Build Coastguard Worker
207*49fe348cSAndroid Build Coastguard Worker        dmse_lf = [ np.sum((scf[:8] - T.SNS_LFCB[i]) ** 2) for i in range(32) ]
208*49fe348cSAndroid Build Coastguard Worker        dmse_hf = [ np.sum((scf[8:] - T.SNS_HFCB[i]) ** 2) for i in range(32) ]
209*49fe348cSAndroid Build Coastguard Worker
210*49fe348cSAndroid Build Coastguard Worker        self.ind_lf = np.argmin(dmse_lf)
211*49fe348cSAndroid Build Coastguard Worker        self.ind_hf = np.argmin(dmse_hf)
212*49fe348cSAndroid Build Coastguard Worker
213*49fe348cSAndroid Build Coastguard Worker        st1 = np.append(T.SNS_LFCB[self.ind_lf], T.SNS_HFCB[self.ind_hf])
214*49fe348cSAndroid Build Coastguard Worker        r1 = scf - st1
215*49fe348cSAndroid Build Coastguard Worker
216*49fe348cSAndroid Build Coastguard Worker        ## Stage 2
217*49fe348cSAndroid Build Coastguard Worker
218*49fe348cSAndroid Build Coastguard Worker        t2_rot = fftpack.dct(r1, norm = 'ortho')
219*49fe348cSAndroid Build Coastguard Worker        x = np.abs(t2_rot)
220*49fe348cSAndroid Build Coastguard Worker
221*49fe348cSAndroid Build Coastguard Worker        ## Stage 2 Shape search, step 1
222*49fe348cSAndroid Build Coastguard Worker
223*49fe348cSAndroid Build Coastguard Worker        K = 6
224*49fe348cSAndroid Build Coastguard Worker
225*49fe348cSAndroid Build Coastguard Worker        proj_fac = (K - 1) / sum(np.abs(t2_rot))
226*49fe348cSAndroid Build Coastguard Worker        y3 = np.floor(x * proj_fac).astype(int)
227*49fe348cSAndroid Build Coastguard Worker
228*49fe348cSAndroid Build Coastguard Worker        ## Stage 2 Shape search, step 2
229*49fe348cSAndroid Build Coastguard Worker
230*49fe348cSAndroid Build Coastguard Worker        corr_xy = np.sum(y3 * x)
231*49fe348cSAndroid Build Coastguard Worker        energy_y = np.sum(y3 * y3)
232*49fe348cSAndroid Build Coastguard Worker
233*49fe348cSAndroid Build Coastguard Worker        k0 = sum(y3)
234*49fe348cSAndroid Build Coastguard Worker        for k in range(k0, K):
235*49fe348cSAndroid Build Coastguard Worker            q_pvq = ((corr_xy + x) ** 2) / (energy_y + 2*y3 + 1)
236*49fe348cSAndroid Build Coastguard Worker            n_best = np.argmax(q_pvq)
237*49fe348cSAndroid Build Coastguard Worker
238*49fe348cSAndroid Build Coastguard Worker            corr_xy += x[n_best]
239*49fe348cSAndroid Build Coastguard Worker            energy_y += 2*y3[n_best] + 1
240*49fe348cSAndroid Build Coastguard Worker            y3[n_best] += 1
241*49fe348cSAndroid Build Coastguard Worker
242*49fe348cSAndroid Build Coastguard Worker        ## Stage 2 Shape search, step 3
243*49fe348cSAndroid Build Coastguard Worker
244*49fe348cSAndroid Build Coastguard Worker        K = 8
245*49fe348cSAndroid Build Coastguard Worker
246*49fe348cSAndroid Build Coastguard Worker        y2 = y3.copy()
247*49fe348cSAndroid Build Coastguard Worker
248*49fe348cSAndroid Build Coastguard Worker        for k in range(sum(y2), K):
249*49fe348cSAndroid Build Coastguard Worker            q_pvq = ((corr_xy + x) ** 2) / (energy_y + 2*y2 + 1)
250*49fe348cSAndroid Build Coastguard Worker            n_best = np.argmax(q_pvq)
251*49fe348cSAndroid Build Coastguard Worker
252*49fe348cSAndroid Build Coastguard Worker            corr_xy += x[n_best]
253*49fe348cSAndroid Build Coastguard Worker            energy_y += 2*y2[n_best] + 1
254*49fe348cSAndroid Build Coastguard Worker            y2[n_best] += 1
255*49fe348cSAndroid Build Coastguard Worker
256*49fe348cSAndroid Build Coastguard Worker
257*49fe348cSAndroid Build Coastguard Worker        ## Stage 2 Shape search, step 4
258*49fe348cSAndroid Build Coastguard Worker
259*49fe348cSAndroid Build Coastguard Worker        y1 = np.append(y2[:10], [0] * 6)
260*49fe348cSAndroid Build Coastguard Worker
261*49fe348cSAndroid Build Coastguard Worker        ## Stage 2 Shape search, step 5
262*49fe348cSAndroid Build Coastguard Worker
263*49fe348cSAndroid Build Coastguard Worker        corr_xy -= sum(y2[10:] * x[10:])
264*49fe348cSAndroid Build Coastguard Worker        energy_y -= sum(y2[10:] * y2[10:])
265*49fe348cSAndroid Build Coastguard Worker
266*49fe348cSAndroid Build Coastguard Worker        ## Stage 2 Shape search, step 6
267*49fe348cSAndroid Build Coastguard Worker
268*49fe348cSAndroid Build Coastguard Worker        K = 10
269*49fe348cSAndroid Build Coastguard Worker
270*49fe348cSAndroid Build Coastguard Worker        for k in range(sum(y1), K):
271*49fe348cSAndroid Build Coastguard Worker            q_pvq = ((corr_xy + x[:10]) ** 2) / (energy_y + 2*y1[:10] + 1)
272*49fe348cSAndroid Build Coastguard Worker            n_best = np.argmax(q_pvq)
273*49fe348cSAndroid Build Coastguard Worker
274*49fe348cSAndroid Build Coastguard Worker            corr_xy += x[n_best]
275*49fe348cSAndroid Build Coastguard Worker            energy_y += 2*y1[n_best] + 1
276*49fe348cSAndroid Build Coastguard Worker            y1[n_best] += 1
277*49fe348cSAndroid Build Coastguard Worker
278*49fe348cSAndroid Build Coastguard Worker        ## Stage 2 Shape search, step 7
279*49fe348cSAndroid Build Coastguard Worker
280*49fe348cSAndroid Build Coastguard Worker        y0 = np.append(y1[:10], [ 0 ] * 6)
281*49fe348cSAndroid Build Coastguard Worker
282*49fe348cSAndroid Build Coastguard Worker        q_pvq = ((corr_xy + x[10:]) ** 2) / (energy_y + 2*y0[10:] + 1)
283*49fe348cSAndroid Build Coastguard Worker        n_best = 10 + np.argmax(q_pvq)
284*49fe348cSAndroid Build Coastguard Worker
285*49fe348cSAndroid Build Coastguard Worker        y0[n_best] += 1
286*49fe348cSAndroid Build Coastguard Worker
287*49fe348cSAndroid Build Coastguard Worker        ## Stage 2 Shape search, step 8
288*49fe348cSAndroid Build Coastguard Worker
289*49fe348cSAndroid Build Coastguard Worker        y0 *= np.sign(t2_rot).astype(int)
290*49fe348cSAndroid Build Coastguard Worker        y1 *= np.sign(t2_rot).astype(int)
291*49fe348cSAndroid Build Coastguard Worker        y2 *= np.sign(t2_rot).astype(int)
292*49fe348cSAndroid Build Coastguard Worker        y3 *= np.sign(t2_rot).astype(int)
293*49fe348cSAndroid Build Coastguard Worker
294*49fe348cSAndroid Build Coastguard Worker        ## Stage 2 Shape search, step 9
295*49fe348cSAndroid Build Coastguard Worker
296*49fe348cSAndroid Build Coastguard Worker        xq = [ y / np.sqrt(sum(y ** 2)) for y in (y0, y1, y2, y3) ]
297*49fe348cSAndroid Build Coastguard Worker
298*49fe348cSAndroid Build Coastguard Worker        ## Shape and gain combination determination
299*49fe348cSAndroid Build Coastguard Worker
300*49fe348cSAndroid Build Coastguard Worker        G = [ T.SNS_VQ_REG_ADJ_GAINS, T.SNS_VQ_REG_LF_ADJ_GAINS,
301*49fe348cSAndroid Build Coastguard Worker              T.SNS_VQ_NEAR_ADJ_GAINS, T.SNS_VQ_FAR_ADJ_GAINS ]
302*49fe348cSAndroid Build Coastguard Worker
303*49fe348cSAndroid Build Coastguard Worker        dMSE = [ [ sum((t2_rot - G[j][i] * xq[j]) ** 2)
304*49fe348cSAndroid Build Coastguard Worker                   for i in range(len(G[j])) ] for j in range(4) ]
305*49fe348cSAndroid Build Coastguard Worker
306*49fe348cSAndroid Build Coastguard Worker        self.shape = np.argmin([ np.min(dMSE[j]) for j in range(4) ])
307*49fe348cSAndroid Build Coastguard Worker        self.gain = np.argmin(dMSE[self.shape])
308*49fe348cSAndroid Build Coastguard Worker
309*49fe348cSAndroid Build Coastguard Worker        gain = G[self.shape][self.gain]
310*49fe348cSAndroid Build Coastguard Worker
311*49fe348cSAndroid Build Coastguard Worker        ## Enumeration of the selected PVQ pulse configurations
312*49fe348cSAndroid Build Coastguard Worker
313*49fe348cSAndroid Build Coastguard Worker        if self.shape == 0:
314*49fe348cSAndroid Build Coastguard Worker            (self.idx_a, self.ls_a) = self.enum_mpvq(y0[:10])
315*49fe348cSAndroid Build Coastguard Worker            (self.idx_b, self.ls_b) = self.enum_mpvq(y0[10:])
316*49fe348cSAndroid Build Coastguard Worker        elif self.shape == 1:
317*49fe348cSAndroid Build Coastguard Worker            (self.idx_a, self.ls_a) = self.enum_mpvq(y1[:10])
318*49fe348cSAndroid Build Coastguard Worker            (self.idx_b, self.ls_b) = (None, None)
319*49fe348cSAndroid Build Coastguard Worker        elif self.shape == 2:
320*49fe348cSAndroid Build Coastguard Worker            (self.idx_a, self.ls_a) = self.enum_mpvq(y2)
321*49fe348cSAndroid Build Coastguard Worker            (self.idx_b, self.ls_b) = (None, None)
322*49fe348cSAndroid Build Coastguard Worker        elif self.shape == 3:
323*49fe348cSAndroid Build Coastguard Worker            (self.idx_a, self.ls_a) = self.enum_mpvq(y3)
324*49fe348cSAndroid Build Coastguard Worker            (self.idx_b, self.ls_b) = (None, None)
325*49fe348cSAndroid Build Coastguard Worker
326*49fe348cSAndroid Build Coastguard Worker        ## Synthesis of the Quantized scale factor
327*49fe348cSAndroid Build Coastguard Worker
328*49fe348cSAndroid Build Coastguard Worker        scf_q = st1 + gain * fftpack.idct(xq[self.shape], norm = 'ortho')
329*49fe348cSAndroid Build Coastguard Worker
330*49fe348cSAndroid Build Coastguard Worker        return scf_q
331*49fe348cSAndroid Build Coastguard Worker
332*49fe348cSAndroid Build Coastguard Worker    def run(self, eb, att, nbytes, x):
333*49fe348cSAndroid Build Coastguard Worker
334*49fe348cSAndroid Build Coastguard Worker        scf = self.compute_scale_factors(eb, att, nbytes)
335*49fe348cSAndroid Build Coastguard Worker        scf_q = self.quantize(scf)
336*49fe348cSAndroid Build Coastguard Worker        y = self.spectral_shaping(scf_q, False, x)
337*49fe348cSAndroid Build Coastguard Worker
338*49fe348cSAndroid Build Coastguard Worker        return y
339*49fe348cSAndroid Build Coastguard Worker
340*49fe348cSAndroid Build Coastguard Worker    def store(self, b):
341*49fe348cSAndroid Build Coastguard Worker
342*49fe348cSAndroid Build Coastguard Worker        shape = self.shape
343*49fe348cSAndroid Build Coastguard Worker        gain_msb_bits = np.array([ 1, 1, 2, 2 ])[shape]
344*49fe348cSAndroid Build Coastguard Worker        gain_lsb_bits = np.array([ 0, 1, 0, 1 ])[shape]
345*49fe348cSAndroid Build Coastguard Worker
346*49fe348cSAndroid Build Coastguard Worker        b.write_uint(self.ind_lf, 5)
347*49fe348cSAndroid Build Coastguard Worker        b.write_uint(self.ind_hf, 5)
348*49fe348cSAndroid Build Coastguard Worker
349*49fe348cSAndroid Build Coastguard Worker        b.write_bit(shape >> 1)
350*49fe348cSAndroid Build Coastguard Worker
351*49fe348cSAndroid Build Coastguard Worker        b.write_uint(self.gain >> gain_lsb_bits, gain_msb_bits)
352*49fe348cSAndroid Build Coastguard Worker
353*49fe348cSAndroid Build Coastguard Worker        b.write_bit(self.ls_a)
354*49fe348cSAndroid Build Coastguard Worker
355*49fe348cSAndroid Build Coastguard Worker        if self.shape == 0:
356*49fe348cSAndroid Build Coastguard Worker            sz_shape_a = 2390004
357*49fe348cSAndroid Build Coastguard Worker            index_joint = self.idx_a + \
358*49fe348cSAndroid Build Coastguard Worker                (2 * self.idx_b + self.ls_b + 2) * sz_shape_a
359*49fe348cSAndroid Build Coastguard Worker
360*49fe348cSAndroid Build Coastguard Worker        elif self.shape == 1:
361*49fe348cSAndroid Build Coastguard Worker            sz_shape_a = 2390004
362*49fe348cSAndroid Build Coastguard Worker            index_joint = self.idx_a + (self.gain & 1) * sz_shape_a
363*49fe348cSAndroid Build Coastguard Worker
364*49fe348cSAndroid Build Coastguard Worker        elif self.shape == 2:
365*49fe348cSAndroid Build Coastguard Worker            index_joint = self.idx_a
366*49fe348cSAndroid Build Coastguard Worker
367*49fe348cSAndroid Build Coastguard Worker        elif self.shape == 3:
368*49fe348cSAndroid Build Coastguard Worker            sz_shape_a = 15158272
369*49fe348cSAndroid Build Coastguard Worker            index_joint = sz_shape_a + (self.gain & 1) + 2 * self.idx_a
370*49fe348cSAndroid Build Coastguard Worker
371*49fe348cSAndroid Build Coastguard Worker        b.write_uint(index_joint, 14 - gain_msb_bits)
372*49fe348cSAndroid Build Coastguard Worker        b.write_uint(index_joint >> (14 - gain_msb_bits), 12)
373*49fe348cSAndroid Build Coastguard Worker
374*49fe348cSAndroid Build Coastguard Worker
375*49fe348cSAndroid Build Coastguard Workerclass SnsSynthesis(Sns):
376*49fe348cSAndroid Build Coastguard Worker
377*49fe348cSAndroid Build Coastguard Worker    def __init__(self, dt, sr):
378*49fe348cSAndroid Build Coastguard Worker
379*49fe348cSAndroid Build Coastguard Worker        super().__init__(dt, sr)
380*49fe348cSAndroid Build Coastguard Worker
381*49fe348cSAndroid Build Coastguard Worker    def deenum_mpvq(self, index, ls, npulses, n):
382*49fe348cSAndroid Build Coastguard Worker
383*49fe348cSAndroid Build Coastguard Worker        y = np.zeros(n, dtype=np.intc)
384*49fe348cSAndroid Build Coastguard Worker        pos = 0
385*49fe348cSAndroid Build Coastguard Worker
386*49fe348cSAndroid Build Coastguard Worker        for i in range(len(y)-1, -1, -1):
387*49fe348cSAndroid Build Coastguard Worker
388*49fe348cSAndroid Build Coastguard Worker            if index > 0:
389*49fe348cSAndroid Build Coastguard Worker                yi = 0
390*49fe348cSAndroid Build Coastguard Worker                while index < T.SNS_MPVQ_OFFSETS[i][npulses - yi]: yi += 1
391*49fe348cSAndroid Build Coastguard Worker                index -= T.SNS_MPVQ_OFFSETS[i][npulses - yi]
392*49fe348cSAndroid Build Coastguard Worker            else:
393*49fe348cSAndroid Build Coastguard Worker                yi = npulses
394*49fe348cSAndroid Build Coastguard Worker
395*49fe348cSAndroid Build Coastguard Worker            y[pos] = [ yi, -yi ][int(ls)]
396*49fe348cSAndroid Build Coastguard Worker            pos += 1
397*49fe348cSAndroid Build Coastguard Worker
398*49fe348cSAndroid Build Coastguard Worker            npulses -= yi
399*49fe348cSAndroid Build Coastguard Worker            if npulses <= 0:
400*49fe348cSAndroid Build Coastguard Worker                break
401*49fe348cSAndroid Build Coastguard Worker
402*49fe348cSAndroid Build Coastguard Worker            if yi > 0:
403*49fe348cSAndroid Build Coastguard Worker                ls = index & 1
404*49fe348cSAndroid Build Coastguard Worker                index >>= 1
405*49fe348cSAndroid Build Coastguard Worker
406*49fe348cSAndroid Build Coastguard Worker        return y
407*49fe348cSAndroid Build Coastguard Worker
408*49fe348cSAndroid Build Coastguard Worker    def unquantize(self):
409*49fe348cSAndroid Build Coastguard Worker
410*49fe348cSAndroid Build Coastguard Worker        ## SNS VQ Decoding
411*49fe348cSAndroid Build Coastguard Worker
412*49fe348cSAndroid Build Coastguard Worker        y = np.empty(16, dtype=np.intc)
413*49fe348cSAndroid Build Coastguard Worker
414*49fe348cSAndroid Build Coastguard Worker        if self.shape == 0:
415*49fe348cSAndroid Build Coastguard Worker            y[:10] = self.deenum_mpvq(self.idx_a, self.ls_a, 10, 10)
416*49fe348cSAndroid Build Coastguard Worker            y[10:] = self.deenum_mpvq(self.idx_b, self.ls_b,  1,  6)
417*49fe348cSAndroid Build Coastguard Worker        elif self.shape == 1:
418*49fe348cSAndroid Build Coastguard Worker            y[:10] = self.deenum_mpvq(self.idx_a, self.ls_a, 10, 10)
419*49fe348cSAndroid Build Coastguard Worker            y[10:] = np.zeros(6, dtype=np.intc)
420*49fe348cSAndroid Build Coastguard Worker        elif self.shape == 2:
421*49fe348cSAndroid Build Coastguard Worker            y = self.deenum_mpvq(self.idx_a, self.ls_a, 8, 16)
422*49fe348cSAndroid Build Coastguard Worker        elif self.shape == 3:
423*49fe348cSAndroid Build Coastguard Worker            y = self.deenum_mpvq(self.idx_a, self.ls_a, 6, 16)
424*49fe348cSAndroid Build Coastguard Worker
425*49fe348cSAndroid Build Coastguard Worker        ## Unit energy normalization
426*49fe348cSAndroid Build Coastguard Worker
427*49fe348cSAndroid Build Coastguard Worker        y = y / np.sqrt(sum(y ** 2))
428*49fe348cSAndroid Build Coastguard Worker
429*49fe348cSAndroid Build Coastguard Worker        ## Reconstruction of the quantized scale factors
430*49fe348cSAndroid Build Coastguard Worker
431*49fe348cSAndroid Build Coastguard Worker        G = [ T.SNS_VQ_REG_ADJ_GAINS, T.SNS_VQ_REG_LF_ADJ_GAINS,
432*49fe348cSAndroid Build Coastguard Worker              T.SNS_VQ_NEAR_ADJ_GAINS, T.SNS_VQ_FAR_ADJ_GAINS ]
433*49fe348cSAndroid Build Coastguard Worker
434*49fe348cSAndroid Build Coastguard Worker        gain = G[self.shape][self.gain]
435*49fe348cSAndroid Build Coastguard Worker
436*49fe348cSAndroid Build Coastguard Worker        scf = np.append(T.SNS_LFCB[self.ind_lf], T.SNS_HFCB[self.ind_hf]) \
437*49fe348cSAndroid Build Coastguard Worker                + gain * fftpack.idct(y, norm = 'ortho')
438*49fe348cSAndroid Build Coastguard Worker
439*49fe348cSAndroid Build Coastguard Worker        return scf
440*49fe348cSAndroid Build Coastguard Worker
441*49fe348cSAndroid Build Coastguard Worker    def load(self, b):
442*49fe348cSAndroid Build Coastguard Worker
443*49fe348cSAndroid Build Coastguard Worker        self.ind_lf = b.read_uint(5)
444*49fe348cSAndroid Build Coastguard Worker        self.ind_hf = b.read_uint(5)
445*49fe348cSAndroid Build Coastguard Worker
446*49fe348cSAndroid Build Coastguard Worker        shape_msb = b.read_bit()
447*49fe348cSAndroid Build Coastguard Worker
448*49fe348cSAndroid Build Coastguard Worker        gain_msb_bits = 1 + shape_msb
449*49fe348cSAndroid Build Coastguard Worker        self.gain = b.read_uint(gain_msb_bits)
450*49fe348cSAndroid Build Coastguard Worker
451*49fe348cSAndroid Build Coastguard Worker        self.ls_a = b.read_bit()
452*49fe348cSAndroid Build Coastguard Worker
453*49fe348cSAndroid Build Coastguard Worker        index_joint  = b.read_uint(14 - gain_msb_bits)
454*49fe348cSAndroid Build Coastguard Worker        index_joint |= b.read_uint(12) << (14 - gain_msb_bits)
455*49fe348cSAndroid Build Coastguard Worker
456*49fe348cSAndroid Build Coastguard Worker        if shape_msb == 0:
457*49fe348cSAndroid Build Coastguard Worker            sz_shape_a = 2390004
458*49fe348cSAndroid Build Coastguard Worker
459*49fe348cSAndroid Build Coastguard Worker            if index_joint >= sz_shape_a * 14:
460*49fe348cSAndroid Build Coastguard Worker                raise ValueError('Invalide SNS joint index')
461*49fe348cSAndroid Build Coastguard Worker
462*49fe348cSAndroid Build Coastguard Worker            self.idx_a = index_joint % sz_shape_a
463*49fe348cSAndroid Build Coastguard Worker            index_joint = index_joint // sz_shape_a
464*49fe348cSAndroid Build Coastguard Worker            if index_joint >= 2:
465*49fe348cSAndroid Build Coastguard Worker                self.shape = 0
466*49fe348cSAndroid Build Coastguard Worker                self.idx_b = (index_joint - 2) // 2
467*49fe348cSAndroid Build Coastguard Worker                self.ls_b =  (index_joint - 2)  % 2
468*49fe348cSAndroid Build Coastguard Worker            else:
469*49fe348cSAndroid Build Coastguard Worker                self.shape = 1
470*49fe348cSAndroid Build Coastguard Worker                self.gain = (self.gain << 1) + (index_joint & 1)
471*49fe348cSAndroid Build Coastguard Worker
472*49fe348cSAndroid Build Coastguard Worker        else:
473*49fe348cSAndroid Build Coastguard Worker            sz_shape_a = 15158272
474*49fe348cSAndroid Build Coastguard Worker            if index_joint >= sz_shape_a + 1549824:
475*49fe348cSAndroid Build Coastguard Worker                raise ValueError('Invalide SNS joint index')
476*49fe348cSAndroid Build Coastguard Worker
477*49fe348cSAndroid Build Coastguard Worker            if index_joint < sz_shape_a:
478*49fe348cSAndroid Build Coastguard Worker                self.shape = 2
479*49fe348cSAndroid Build Coastguard Worker                self.idx_a = index_joint
480*49fe348cSAndroid Build Coastguard Worker            else:
481*49fe348cSAndroid Build Coastguard Worker                self.shape = 3
482*49fe348cSAndroid Build Coastguard Worker                index_joint -= sz_shape_a
483*49fe348cSAndroid Build Coastguard Worker                self.gain = (self.gain << 1) + (index_joint % 2)
484*49fe348cSAndroid Build Coastguard Worker                self.idx_a = index_joint // 2
485*49fe348cSAndroid Build Coastguard Worker
486*49fe348cSAndroid Build Coastguard Worker    def run(self, x):
487*49fe348cSAndroid Build Coastguard Worker
488*49fe348cSAndroid Build Coastguard Worker        scf = self.unquantize()
489*49fe348cSAndroid Build Coastguard Worker        y = self.spectral_shaping(scf, True, x)
490*49fe348cSAndroid Build Coastguard Worker
491*49fe348cSAndroid Build Coastguard Worker        return y
492*49fe348cSAndroid Build Coastguard Worker
493*49fe348cSAndroid Build Coastguard Worker### ------------------------------------------------------------------------ ###
494*49fe348cSAndroid Build Coastguard Worker
495*49fe348cSAndroid Build Coastguard Workerdef check_analysis(rng, dt, sr):
496*49fe348cSAndroid Build Coastguard Worker
497*49fe348cSAndroid Build Coastguard Worker    ok = True
498*49fe348cSAndroid Build Coastguard Worker
499*49fe348cSAndroid Build Coastguard Worker    analysis = SnsAnalysis(dt, sr)
500*49fe348cSAndroid Build Coastguard Worker
501*49fe348cSAndroid Build Coastguard Worker    for i in range(10):
502*49fe348cSAndroid Build Coastguard Worker        ne = T.I[dt][sr][-1]
503*49fe348cSAndroid Build Coastguard Worker        x  = rng.random(ne) * 1e4
504*49fe348cSAndroid Build Coastguard Worker        e  = rng.random(len(T.I[dt][sr]) - 1) * 1e10
505*49fe348cSAndroid Build Coastguard Worker
506*49fe348cSAndroid Build Coastguard Worker        if sr >= T.SRATE_48K_HR:
507*49fe348cSAndroid Build Coastguard Worker            for nbits in (1144, 1152, 2296, 2304, 4400, 4408):
508*49fe348cSAndroid Build Coastguard Worker                y = analysis.run(e, False, nbits // 8, x)
509*49fe348cSAndroid Build Coastguard Worker                data = analysis.get_data()
510*49fe348cSAndroid Build Coastguard Worker
511*49fe348cSAndroid Build Coastguard Worker                (y_c, data_c) = lc3.sns_analyze(
512*49fe348cSAndroid Build Coastguard Worker                    dt, sr, nbits // 8, e, False, x)
513*49fe348cSAndroid Build Coastguard Worker
514*49fe348cSAndroid Build Coastguard Worker                for k in data.keys():
515*49fe348cSAndroid Build Coastguard Worker                    ok = ok and data_c[k] == data[k]
516*49fe348cSAndroid Build Coastguard Worker
517*49fe348cSAndroid Build Coastguard Worker                ok = ok and lc3.sns_get_nbits() == analysis.get_nbits()
518*49fe348cSAndroid Build Coastguard Worker                ok = ok and np.amax(np.abs(y - y_c)) < 1e-1
519*49fe348cSAndroid Build Coastguard Worker
520*49fe348cSAndroid Build Coastguard Worker        else:
521*49fe348cSAndroid Build Coastguard Worker            for att in (0, 1):
522*49fe348cSAndroid Build Coastguard Worker                y = analysis.run(e, att, 0, x)
523*49fe348cSAndroid Build Coastguard Worker                data = analysis.get_data()
524*49fe348cSAndroid Build Coastguard Worker
525*49fe348cSAndroid Build Coastguard Worker                (y_c, data_c) = lc3.sns_analyze(dt, sr, 0, e, att, x)
526*49fe348cSAndroid Build Coastguard Worker
527*49fe348cSAndroid Build Coastguard Worker                for k in data.keys():
528*49fe348cSAndroid Build Coastguard Worker                    ok = ok and data_c[k] == data[k]
529*49fe348cSAndroid Build Coastguard Worker
530*49fe348cSAndroid Build Coastguard Worker                ok = ok and lc3.sns_get_nbits() == analysis.get_nbits()
531*49fe348cSAndroid Build Coastguard Worker                ok = ok and np.amax(np.abs(y - y_c)) < 1e-1
532*49fe348cSAndroid Build Coastguard Worker
533*49fe348cSAndroid Build Coastguard Worker    return ok
534*49fe348cSAndroid Build Coastguard Worker
535*49fe348cSAndroid Build Coastguard Workerdef check_synthesis(rng, dt, sr):
536*49fe348cSAndroid Build Coastguard Worker
537*49fe348cSAndroid Build Coastguard Worker    ok = True
538*49fe348cSAndroid Build Coastguard Worker
539*49fe348cSAndroid Build Coastguard Worker    synthesis = SnsSynthesis(dt, sr)
540*49fe348cSAndroid Build Coastguard Worker
541*49fe348cSAndroid Build Coastguard Worker    for i in range(100):
542*49fe348cSAndroid Build Coastguard Worker
543*49fe348cSAndroid Build Coastguard Worker        synthesis.ind_lf = rng.integers(0, 32)
544*49fe348cSAndroid Build Coastguard Worker        synthesis.ind_hf = rng.integers(0, 32)
545*49fe348cSAndroid Build Coastguard Worker
546*49fe348cSAndroid Build Coastguard Worker        shape = rng.integers(0, 4)
547*49fe348cSAndroid Build Coastguard Worker        sz_shape_a = [ 2390004, 2390004, 15158272, 774912 ][shape]
548*49fe348cSAndroid Build Coastguard Worker        sz_shape_b = [ 6, 1, 0, 0 ][shape]
549*49fe348cSAndroid Build Coastguard Worker        synthesis.shape = shape
550*49fe348cSAndroid Build Coastguard Worker        synthesis.gain = rng.integers(0, [ 2, 4, 4, 8 ][shape])
551*49fe348cSAndroid Build Coastguard Worker        synthesis.idx_a = rng.integers(0, sz_shape_a, endpoint=True)
552*49fe348cSAndroid Build Coastguard Worker        synthesis.ls_a = bool(rng.integers(0, 1, endpoint=True))
553*49fe348cSAndroid Build Coastguard Worker        synthesis.idx_b = rng.integers(0, sz_shape_b, endpoint=True)
554*49fe348cSAndroid Build Coastguard Worker        synthesis.ls_b = bool(rng.integers(0, 1, endpoint=True))
555*49fe348cSAndroid Build Coastguard Worker
556*49fe348cSAndroid Build Coastguard Worker        ne = T.I[dt][sr][-1]
557*49fe348cSAndroid Build Coastguard Worker        x  = rng.random(ne) * 1e4
558*49fe348cSAndroid Build Coastguard Worker
559*49fe348cSAndroid Build Coastguard Worker        y = synthesis.run(x)
560*49fe348cSAndroid Build Coastguard Worker        y_c = lc3.sns_synthesize(dt, sr, synthesis.get_data(), x)
561*49fe348cSAndroid Build Coastguard Worker        ok = ok and np.amax(np.abs(1 - y/y_c)) < 1e-5
562*49fe348cSAndroid Build Coastguard Worker
563*49fe348cSAndroid Build Coastguard Worker    return ok
564*49fe348cSAndroid Build Coastguard Worker
565*49fe348cSAndroid Build Coastguard Workerdef check_analysis_appendix_c(dt):
566*49fe348cSAndroid Build Coastguard Worker
567*49fe348cSAndroid Build Coastguard Worker    i0 = dt - T.DT_7M5
568*49fe348cSAndroid Build Coastguard Worker    sr = T.SRATE_16K
569*49fe348cSAndroid Build Coastguard Worker
570*49fe348cSAndroid Build Coastguard Worker    ok = True
571*49fe348cSAndroid Build Coastguard Worker
572*49fe348cSAndroid Build Coastguard Worker    for i in range(len(C.E_B[i0])):
573*49fe348cSAndroid Build Coastguard Worker
574*49fe348cSAndroid Build Coastguard Worker        scf = lc3.sns_compute_scale_factors(dt, sr, 0, C.E_B[i0][i], False)
575*49fe348cSAndroid Build Coastguard Worker        ok = ok and np.amax(np.abs(scf - C.SCF[i0][i])) < 1e-4
576*49fe348cSAndroid Build Coastguard Worker
577*49fe348cSAndroid Build Coastguard Worker        (lf, hf) = lc3.sns_resolve_codebooks(scf)
578*49fe348cSAndroid Build Coastguard Worker        ok = ok and lf == C.IND_LF[i0][i] and hf == C.IND_HF[i0][i]
579*49fe348cSAndroid Build Coastguard Worker
580*49fe348cSAndroid Build Coastguard Worker        (y, yn, shape, gain) = lc3.sns_quantize(scf, lf, hf)
581*49fe348cSAndroid Build Coastguard Worker        ok = ok and np.any(y[0][:16] - C.SNS_Y0[i0][i] == 0)
582*49fe348cSAndroid Build Coastguard Worker        ok = ok and np.any(y[1][:10] - C.SNS_Y1[i0][i] == 0)
583*49fe348cSAndroid Build Coastguard Worker        ok = ok and np.any(y[2][:16] - C.SNS_Y2[i0][i] == 0)
584*49fe348cSAndroid Build Coastguard Worker        ok = ok and np.any(y[3][:16] - C.SNS_Y3[i0][i] == 0)
585*49fe348cSAndroid Build Coastguard Worker        ok = ok and shape == 2*C.SUBMODE_MSB[i0][i] + C.SUBMODE_LSB[i0][i]
586*49fe348cSAndroid Build Coastguard Worker        ok = ok and gain == C.G_IND[i0][i]
587*49fe348cSAndroid Build Coastguard Worker
588*49fe348cSAndroid Build Coastguard Worker        scf_q = lc3.sns_unquantize(lf, hf, yn[shape], shape, gain)
589*49fe348cSAndroid Build Coastguard Worker        ok = ok and np.amax(np.abs(scf_q - C.SCF_Q[i0][i])) < 1e-5
590*49fe348cSAndroid Build Coastguard Worker
591*49fe348cSAndroid Build Coastguard Worker        x = lc3.sns_spectral_shaping(dt, sr, C.SCF_Q[i0][i], False, C.X[i0][i])
592*49fe348cSAndroid Build Coastguard Worker        ok = ok and np.amax(np.abs(1 - x/C.X_S[i0][i])) < 1e-5
593*49fe348cSAndroid Build Coastguard Worker
594*49fe348cSAndroid Build Coastguard Worker        (x, data) = lc3.sns_analyze(dt, sr, 0, C.E_B[i0][i], False, C.X[i0][i])
595*49fe348cSAndroid Build Coastguard Worker        ok = ok and data['lfcb'] == C.IND_LF[i0][i]
596*49fe348cSAndroid Build Coastguard Worker        ok = ok and data['hfcb'] == C.IND_HF[i0][i]
597*49fe348cSAndroid Build Coastguard Worker        ok = ok and data['shape'] == 2*C.SUBMODE_MSB[i0][i] + \
598*49fe348cSAndroid Build Coastguard Worker                                       C.SUBMODE_LSB[i0][i]
599*49fe348cSAndroid Build Coastguard Worker        ok = ok and data['gain'] == C.G_IND[i0][i]
600*49fe348cSAndroid Build Coastguard Worker        ok = ok and data['idx_a'] == C.IDX_A[i0][i]
601*49fe348cSAndroid Build Coastguard Worker        ok = ok and data['ls_a'] == C.LS_IND_A[i0][i]
602*49fe348cSAndroid Build Coastguard Worker        ok = ok and (C.IDX_B[i0][i] is None or
603*49fe348cSAndroid Build Coastguard Worker            data['idx_b'] == C.IDX_B[i0][i])
604*49fe348cSAndroid Build Coastguard Worker        ok = ok and (C.LS_IND_B[i0][i] is None or
605*49fe348cSAndroid Build Coastguard Worker            data['ls_b'] == C.LS_IND_B[i0][i])
606*49fe348cSAndroid Build Coastguard Worker        ok = ok and np.amax(np.abs(1 - x/C.X_S[i0][i])) < 1e-5
607*49fe348cSAndroid Build Coastguard Worker
608*49fe348cSAndroid Build Coastguard Worker    return ok
609*49fe348cSAndroid Build Coastguard Worker
610*49fe348cSAndroid Build Coastguard Workerdef check_synthesis_appendix_c(dt):
611*49fe348cSAndroid Build Coastguard Worker
612*49fe348cSAndroid Build Coastguard Worker    i0 = dt - T.DT_7M5
613*49fe348cSAndroid Build Coastguard Worker    sr = T.SRATE_16K
614*49fe348cSAndroid Build Coastguard Worker
615*49fe348cSAndroid Build Coastguard Worker    ok = True
616*49fe348cSAndroid Build Coastguard Worker
617*49fe348cSAndroid Build Coastguard Worker    for i in range(len(C.X_HAT_TNS[i0])):
618*49fe348cSAndroid Build Coastguard Worker
619*49fe348cSAndroid Build Coastguard Worker        data = {
620*49fe348cSAndroid Build Coastguard Worker            'lfcb'  : C.IND_LF[i0][i], 'hfcb' : C.IND_HF[i0][i],
621*49fe348cSAndroid Build Coastguard Worker            'shape' : 2*C.SUBMODE_MSB[i0][i] + C.SUBMODE_LSB[i0][i],
622*49fe348cSAndroid Build Coastguard Worker            'gain'  : C.G_IND[i0][i],
623*49fe348cSAndroid Build Coastguard Worker            'idx_a' : C.IDX_A[i0][i],
624*49fe348cSAndroid Build Coastguard Worker            'ls_a'  : C.LS_IND_A[i0][i],
625*49fe348cSAndroid Build Coastguard Worker            'idx_b' : C.IDX_B[i0][i] if C.IDX_B[i0][i] is not None else 0,
626*49fe348cSAndroid Build Coastguard Worker            'ls_b'  : C.LS_IND_B[i0][i] if C.LS_IND_B[i0][i] is not None else 0,
627*49fe348cSAndroid Build Coastguard Worker        }
628*49fe348cSAndroid Build Coastguard Worker
629*49fe348cSAndroid Build Coastguard Worker        x = lc3.sns_synthesize(dt, sr, data, C.X_HAT_TNS[i0][i])
630*49fe348cSAndroid Build Coastguard Worker        ok = ok and np.amax(np.abs(x - C.X_HAT_SNS[i0][i])) < 1e0
631*49fe348cSAndroid Build Coastguard Worker
632*49fe348cSAndroid Build Coastguard Worker    return ok
633*49fe348cSAndroid Build Coastguard Worker
634*49fe348cSAndroid Build Coastguard Workerdef check():
635*49fe348cSAndroid Build Coastguard Worker
636*49fe348cSAndroid Build Coastguard Worker    rng = np.random.default_rng(1234)
637*49fe348cSAndroid Build Coastguard Worker    ok = True
638*49fe348cSAndroid Build Coastguard Worker
639*49fe348cSAndroid Build Coastguard Worker    for dt in range(T.NUM_DT):
640*49fe348cSAndroid Build Coastguard Worker        for sr in range(T.SRATE_8K, T.SRATE_48K + 1):
641*49fe348cSAndroid Build Coastguard Worker            ok = ok and check_analysis(rng, dt, sr)
642*49fe348cSAndroid Build Coastguard Worker            ok = ok and check_synthesis(rng, dt, sr)
643*49fe348cSAndroid Build Coastguard Worker
644*49fe348cSAndroid Build Coastguard Worker    for dt in ( T.DT_2M5, T.DT_5M, T.DT_10M ):
645*49fe348cSAndroid Build Coastguard Worker        for sr in ( T.SRATE_48K_HR, T.SRATE_96K_HR ):
646*49fe348cSAndroid Build Coastguard Worker            ok = ok and check_analysis(rng, dt, sr)
647*49fe348cSAndroid Build Coastguard Worker            ok = ok and check_synthesis(rng, dt, sr)
648*49fe348cSAndroid Build Coastguard Worker
649*49fe348cSAndroid Build Coastguard Worker    for dt in ( T.DT_7M5, T.DT_10M ):
650*49fe348cSAndroid Build Coastguard Worker        check_analysis_appendix_c(dt)
651*49fe348cSAndroid Build Coastguard Worker        check_synthesis_appendix_c(dt)
652*49fe348cSAndroid Build Coastguard Worker
653*49fe348cSAndroid Build Coastguard Worker    return ok
654*49fe348cSAndroid Build Coastguard Worker
655*49fe348cSAndroid Build Coastguard Worker### ------------------------------------------------------------------------ ###
656