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