xref: /btstack/3rd-party/lc3-google/test/sns.py (revision 6897da5c53aac5b1f90f41b5b15d0bd43d61dfff)
14930cef6SMatthias Ringwald#
24930cef6SMatthias Ringwald# Copyright 2022 Google LLC
34930cef6SMatthias Ringwald#
44930cef6SMatthias Ringwald# Licensed under the Apache License, Version 2.0 (the "License");
54930cef6SMatthias Ringwald# you may not use this file except in compliance with the License.
64930cef6SMatthias Ringwald# You may obtain a copy of the License at
74930cef6SMatthias Ringwald#
84930cef6SMatthias Ringwald#     http://www.apache.org/licenses/LICENSE-2.0
94930cef6SMatthias Ringwald#
104930cef6SMatthias Ringwald# Unless required by applicable law or agreed to in writing, software
114930cef6SMatthias Ringwald# distributed under the License is distributed on an "AS IS" BASIS,
124930cef6SMatthias Ringwald# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
134930cef6SMatthias Ringwald# See the License for the specific language governing permissions and
144930cef6SMatthias Ringwald# limitations under the License.
154930cef6SMatthias Ringwald#
164930cef6SMatthias Ringwald
174930cef6SMatthias Ringwaldimport numpy as np
184930cef6SMatthias Ringwaldimport scipy.fftpack as fftpack
194930cef6SMatthias Ringwald
204c4eb519SMatthias Ringwaldimport lc3
214930cef6SMatthias Ringwaldimport tables as T, appendix_c as C
224930cef6SMatthias Ringwald
234930cef6SMatthias Ringwald### ------------------------------------------------------------------------ ###
244930cef6SMatthias Ringwald
254930cef6SMatthias Ringwaldclass Sns:
264930cef6SMatthias Ringwald
274930cef6SMatthias Ringwald    def __init__(self, dt, sr):
284930cef6SMatthias Ringwald
294930cef6SMatthias Ringwald        self.dt = dt
304930cef6SMatthias Ringwald        self.sr = sr
31*6897da5cSDirk Helbig        self.I = T.I[dt][sr]
324930cef6SMatthias Ringwald
334930cef6SMatthias Ringwald        (self.ind_lf, self.ind_hf, self.shape, self.gain) = \
344930cef6SMatthias Ringwald            (None, None, None, None)
354930cef6SMatthias Ringwald
364930cef6SMatthias Ringwald        (self.idx_a, self.ls_a, self.idx_b, self.ls_b) = \
374930cef6SMatthias Ringwald            (None, None, None, None)
384930cef6SMatthias Ringwald
394930cef6SMatthias Ringwald    def get_data(self):
404930cef6SMatthias Ringwald
414930cef6SMatthias Ringwald        data = { 'lfcb' : self.ind_lf, 'hfcb' : self.ind_hf,
424930cef6SMatthias Ringwald                 'shape' : self.shape, 'gain' : self.gain,
434930cef6SMatthias Ringwald                 'idx_a' : self.idx_a, 'ls_a' : self.ls_a }
444930cef6SMatthias Ringwald
454930cef6SMatthias Ringwald        if self.idx_b is not None:
464930cef6SMatthias Ringwald            data.update({ 'idx_b' : self.idx_b, 'ls_b' : self.ls_b })
474930cef6SMatthias Ringwald
484930cef6SMatthias Ringwald        return data
494930cef6SMatthias Ringwald
504930cef6SMatthias Ringwald    def get_nbits(self):
514930cef6SMatthias Ringwald
524930cef6SMatthias Ringwald        return 38
534930cef6SMatthias Ringwald
544930cef6SMatthias Ringwald    def spectral_shaping(self, scf, inv, x):
554930cef6SMatthias Ringwald
56*6897da5cSDirk Helbig        ## Scale factors interpolation
574930cef6SMatthias Ringwald
584930cef6SMatthias Ringwald        scf_i = np.empty(4*len(scf))
594930cef6SMatthias Ringwald        scf_i[0     ] = scf[0]
604930cef6SMatthias Ringwald        scf_i[1     ] = scf[0]
614930cef6SMatthias Ringwald        scf_i[2:62:4] = scf[:15] + 1/8 * (scf[1:] - scf[:15])
624930cef6SMatthias Ringwald        scf_i[3:63:4] = scf[:15] + 3/8 * (scf[1:] - scf[:15])
634930cef6SMatthias Ringwald        scf_i[4:64:4] = scf[:15] + 5/8 * (scf[1:] - scf[:15])
644930cef6SMatthias Ringwald        scf_i[5:64:4] = scf[:15] + 7/8 * (scf[1:] - scf[:15])
654930cef6SMatthias Ringwald        scf_i[62    ] = scf[15 ] + 1/8 * (scf[15] - scf[14 ])
664930cef6SMatthias Ringwald        scf_i[63    ] = scf[15 ] + 3/8 * (scf[15] - scf[14 ])
674930cef6SMatthias Ringwald
68*6897da5cSDirk Helbig        nb = len(self.I) - 1
69*6897da5cSDirk Helbig
70*6897da5cSDirk Helbig        if nb < 32:
71*6897da5cSDirk Helbig            n4 = round(abs(1-32/nb)*nb)
72*6897da5cSDirk Helbig            n2 = nb - n4
73*6897da5cSDirk Helbig
74*6897da5cSDirk Helbig            for i in range(n4):
75*6897da5cSDirk Helbig                scf_i[i] = np.mean(scf_i[4*i:4*i+4])
76*6897da5cSDirk Helbig
77*6897da5cSDirk Helbig            for i in range(n4, n4+n2):
78*6897da5cSDirk Helbig                scf_i[i] = np.mean(scf_i[2*n4+2*i:2*n4+2*i+2])
79*6897da5cSDirk Helbig
80*6897da5cSDirk Helbig            scf_i = scf_i[:n4+n2]
81*6897da5cSDirk Helbig
82*6897da5cSDirk Helbig        elif nb < 64:
83*6897da5cSDirk Helbig            n2 = 64 - nb
844930cef6SMatthias Ringwald
854930cef6SMatthias Ringwald            for i in range(n2):
86*6897da5cSDirk Helbig                scf_i[i] = np.mean(scf_i[2*i:2*i+2])
874930cef6SMatthias Ringwald            scf_i = np.append(scf_i[:n2], scf_i[2*n2:])
884930cef6SMatthias Ringwald
894930cef6SMatthias Ringwald        g_sns = np.power(2, [ -scf_i, scf_i ][inv])
904930cef6SMatthias Ringwald
91*6897da5cSDirk Helbig        ## Spectral shaping
924930cef6SMatthias Ringwald
934930cef6SMatthias Ringwald        y = np.empty(len(x))
94*6897da5cSDirk Helbig        I = self.I
954930cef6SMatthias Ringwald
96*6897da5cSDirk Helbig        for b in range(nb):
974930cef6SMatthias Ringwald            y[I[b]:I[b+1]] = x[I[b]:I[b+1]] * g_sns[b]
984930cef6SMatthias Ringwald
994930cef6SMatthias Ringwald        return y
1004930cef6SMatthias Ringwald
1014930cef6SMatthias Ringwald
1024930cef6SMatthias Ringwaldclass SnsAnalysis(Sns):
1034930cef6SMatthias Ringwald
1044930cef6SMatthias Ringwald    def __init__(self, dt, sr):
1054930cef6SMatthias Ringwald
1064930cef6SMatthias Ringwald        super().__init__(dt, sr)
1074930cef6SMatthias Ringwald
108*6897da5cSDirk Helbig    def compute_scale_factors(self, e, att, nbytes):
1094930cef6SMatthias Ringwald
1104930cef6SMatthias Ringwald        dt = self.dt
111*6897da5cSDirk Helbig        sr = self.sr
112*6897da5cSDirk Helbig        hr = self.sr >= T.SRATE_48K_HR
1134930cef6SMatthias Ringwald
114*6897da5cSDirk Helbig        ## Padding
1154930cef6SMatthias Ringwald
116*6897da5cSDirk Helbig        if len(e) < 32:
117*6897da5cSDirk Helbig            n4 = round(abs(1-32/len(e))*len(e))
118*6897da5cSDirk Helbig            n2 = len(e) - n4
119*6897da5cSDirk Helbig
120*6897da5cSDirk Helbig            e = np.append(np.zeros(3*n4+n2), e)
121*6897da5cSDirk Helbig            for i in range(n4):
122*6897da5cSDirk Helbig                e[4*i+0] = e[4*i+1] = \
123*6897da5cSDirk Helbig                e[4*i+2] = e[4*i+3] = e[3*n4+n2+i]
124*6897da5cSDirk Helbig
125*6897da5cSDirk Helbig            for i in range(2*n4, 2*n4+n2):
126*6897da5cSDirk Helbig                e[2*i+0] = e[2*i+1] = e[2*n4+n2+i]
127*6897da5cSDirk Helbig
128*6897da5cSDirk Helbig        elif len(e) < 64:
1294930cef6SMatthias Ringwald            n2 = 64 - len(e)
1304930cef6SMatthias Ringwald
1314930cef6SMatthias Ringwald            e = np.append(np.empty(n2), e)
1324930cef6SMatthias Ringwald            for i in range(n2):
1334930cef6SMatthias Ringwald                e[2*i+0] = e[2*i+1] = e[n2+i]
1344930cef6SMatthias Ringwald
135*6897da5cSDirk Helbig        ## Smoothing
1364930cef6SMatthias Ringwald
1374930cef6SMatthias Ringwald        e_s = np.zeros(len(e))
1384930cef6SMatthias Ringwald        e_s[0   ] = 0.75 * e[0   ] + 0.25 * e[1   ]
1394930cef6SMatthias Ringwald        e_s[1:63] = 0.25 * e[0:62] + 0.5  * e[1:63] + 0.25 * e[2:64]
1404930cef6SMatthias Ringwald        e_s[  63] = 0.25 * e[  62] + 0.75 * e[  63]
1414930cef6SMatthias Ringwald
142*6897da5cSDirk Helbig        ## Pre-emphasis
1434930cef6SMatthias Ringwald
144*6897da5cSDirk Helbig        g_tilt = [ 14, 18, 22, 26, 30, 30, 34 ][self.sr]
1454930cef6SMatthias Ringwald        e_p = e_s * (10 ** ((np.arange(64) * g_tilt) / 630))
1464930cef6SMatthias Ringwald
147*6897da5cSDirk Helbig        ## Noise floor
1484930cef6SMatthias Ringwald
1494930cef6SMatthias Ringwald        noise_floor = max(np.average(e_p) * (10 ** (-40/10)), 2 ** -32)
1504930cef6SMatthias Ringwald        e_p = np.fmax(e_p, noise_floor * np.ones(len(e)))
1514930cef6SMatthias Ringwald
152*6897da5cSDirk Helbig        ## Logarithm
1534930cef6SMatthias Ringwald
1544930cef6SMatthias Ringwald        e_l = np.log2(10 ** -31 + e_p) / 2
1554930cef6SMatthias Ringwald
156*6897da5cSDirk Helbig        ## Band energy grouping
1574930cef6SMatthias Ringwald
1584930cef6SMatthias Ringwald        w = [ 1/12, 2/12, 3/12, 3/12, 2/12, 1/12 ]
1594930cef6SMatthias Ringwald
1604930cef6SMatthias Ringwald        e_4 = np.zeros(len(e_l) // 4)
1614930cef6SMatthias Ringwald        e_4[0   ] = w[0] * e_l[0] + np.sum(w[1:] * e_l[:5])
1624930cef6SMatthias Ringwald        e_4[1:15] = [ np.sum(w * e_l[4*i-1:4*i+5]) for i in range(1, 15) ]
1634930cef6SMatthias Ringwald        e_4[  15] = np.sum(w[:5] * e_l[59:64]) + w[5] * e_l[63]
1644930cef6SMatthias Ringwald
165*6897da5cSDirk Helbig        ## Mean removal and scaling, attack handling
1664930cef6SMatthias Ringwald
167*6897da5cSDirk Helbig        cf = [ 0.85, 0.6 ][hr]
168*6897da5cSDirk Helbig        if hr and nbytes * 8 > [ 1150, 2300, 0, 4400 ][self.dt]:
169*6897da5cSDirk Helbig            cf *= [ 0.25, 0.35 ][ self.dt == T.DT_10M ]
170*6897da5cSDirk Helbig
171*6897da5cSDirk Helbig        scf = cf * (e_4 - np.average(e_4))
1724930cef6SMatthias Ringwald
1734930cef6SMatthias Ringwald        scf_a = np.zeros(len(scf))
174*6897da5cSDirk Helbig        scf_a[0   ] = np.mean(scf[:3])
175*6897da5cSDirk Helbig        scf_a[1   ] = np.mean(scf[:4])
176*6897da5cSDirk Helbig        scf_a[2:14] = [ np.mean(scf[i:i+5]) for i in range(12) ]
177*6897da5cSDirk Helbig        scf_a[  14] = np.mean(scf[12:])
178*6897da5cSDirk Helbig        scf_a[  15] = np.mean(scf[13:])
1794930cef6SMatthias Ringwald
180*6897da5cSDirk Helbig        scf_a = (0.5 if self.dt != T.DT_7M5 else 0.3) * \
1814930cef6SMatthias Ringwald                (scf_a - np.average(scf_a))
1824930cef6SMatthias Ringwald
1834930cef6SMatthias Ringwald        return scf_a if att else scf
1844930cef6SMatthias Ringwald
1854930cef6SMatthias Ringwald    def enum_mpvq(self, v):
1864930cef6SMatthias Ringwald
1874930cef6SMatthias Ringwald        sign = None
1884930cef6SMatthias Ringwald        index = 0
1894930cef6SMatthias Ringwald        x = 0
1904930cef6SMatthias Ringwald
1914930cef6SMatthias Ringwald        for (n, vn) in enumerate(v[::-1]):
1924930cef6SMatthias Ringwald
1934930cef6SMatthias Ringwald            if sign is not None and vn != 0:
1944930cef6SMatthias Ringwald                index = 2*index + sign
1954930cef6SMatthias Ringwald            if vn != 0:
1964930cef6SMatthias Ringwald                sign = 1 if vn < 0 else 0
1974930cef6SMatthias Ringwald
1984930cef6SMatthias Ringwald            index += T.SNS_MPVQ_OFFSETS[n][x]
1994930cef6SMatthias Ringwald            x += abs(vn)
2004930cef6SMatthias Ringwald
2014930cef6SMatthias Ringwald        return (index, bool(sign))
2024930cef6SMatthias Ringwald
2034930cef6SMatthias Ringwald    def quantize(self, scf):
2044930cef6SMatthias Ringwald
205*6897da5cSDirk Helbig        ## Stage 1
2064930cef6SMatthias Ringwald
2074930cef6SMatthias Ringwald        dmse_lf = [ np.sum((scf[:8] - T.SNS_LFCB[i]) ** 2) for i in range(32) ]
2084930cef6SMatthias Ringwald        dmse_hf = [ np.sum((scf[8:] - T.SNS_HFCB[i]) ** 2) for i in range(32) ]
2094930cef6SMatthias Ringwald
2104930cef6SMatthias Ringwald        self.ind_lf = np.argmin(dmse_lf)
2114930cef6SMatthias Ringwald        self.ind_hf = np.argmin(dmse_hf)
2124930cef6SMatthias Ringwald
2134930cef6SMatthias Ringwald        st1 = np.append(T.SNS_LFCB[self.ind_lf], T.SNS_HFCB[self.ind_hf])
2144930cef6SMatthias Ringwald        r1 = scf - st1
2154930cef6SMatthias Ringwald
216*6897da5cSDirk Helbig        ## Stage 2
2174930cef6SMatthias Ringwald
2184930cef6SMatthias Ringwald        t2_rot = fftpack.dct(r1, norm = 'ortho')
2194930cef6SMatthias Ringwald        x = np.abs(t2_rot)
2204930cef6SMatthias Ringwald
221*6897da5cSDirk Helbig        ## Stage 2 Shape search, step 1
2224930cef6SMatthias Ringwald
2234930cef6SMatthias Ringwald        K = 6
2244930cef6SMatthias Ringwald
2254930cef6SMatthias Ringwald        proj_fac = (K - 1) / sum(np.abs(t2_rot))
2264930cef6SMatthias Ringwald        y3 = np.floor(x * proj_fac).astype(int)
2274930cef6SMatthias Ringwald
228*6897da5cSDirk Helbig        ## Stage 2 Shape search, step 2
2294930cef6SMatthias Ringwald
2304930cef6SMatthias Ringwald        corr_xy = np.sum(y3 * x)
2314930cef6SMatthias Ringwald        energy_y = np.sum(y3 * y3)
2324930cef6SMatthias Ringwald
2334930cef6SMatthias Ringwald        k0 = sum(y3)
2344930cef6SMatthias Ringwald        for k in range(k0, K):
2354930cef6SMatthias Ringwald            q_pvq = ((corr_xy + x) ** 2) / (energy_y + 2*y3 + 1)
2364930cef6SMatthias Ringwald            n_best = np.argmax(q_pvq)
2374930cef6SMatthias Ringwald
2384930cef6SMatthias Ringwald            corr_xy += x[n_best]
2394930cef6SMatthias Ringwald            energy_y += 2*y3[n_best] + 1
2404930cef6SMatthias Ringwald            y3[n_best] += 1
2414930cef6SMatthias Ringwald
242*6897da5cSDirk Helbig        ## Stage 2 Shape search, step 3
2434930cef6SMatthias Ringwald
2444930cef6SMatthias Ringwald        K = 8
2454930cef6SMatthias Ringwald
2464930cef6SMatthias Ringwald        y2 = y3.copy()
2474930cef6SMatthias Ringwald
2484930cef6SMatthias Ringwald        for k in range(sum(y2), K):
2494930cef6SMatthias Ringwald            q_pvq = ((corr_xy + x) ** 2) / (energy_y + 2*y2 + 1)
2504930cef6SMatthias Ringwald            n_best = np.argmax(q_pvq)
2514930cef6SMatthias Ringwald
2524930cef6SMatthias Ringwald            corr_xy += x[n_best]
2534930cef6SMatthias Ringwald            energy_y += 2*y2[n_best] + 1
2544930cef6SMatthias Ringwald            y2[n_best] += 1
2554930cef6SMatthias Ringwald
2564930cef6SMatthias Ringwald
257*6897da5cSDirk Helbig        ## Stage 2 Shape search, step 4
2584930cef6SMatthias Ringwald
2594930cef6SMatthias Ringwald        y1 = np.append(y2[:10], [0] * 6)
2604930cef6SMatthias Ringwald
261*6897da5cSDirk Helbig        ## Stage 2 Shape search, step 5
2624930cef6SMatthias Ringwald
2634930cef6SMatthias Ringwald        corr_xy -= sum(y2[10:] * x[10:])
2644930cef6SMatthias Ringwald        energy_y -= sum(y2[10:] * y2[10:])
2654930cef6SMatthias Ringwald
266*6897da5cSDirk Helbig        ## Stage 2 Shape search, step 6
2674930cef6SMatthias Ringwald
2684930cef6SMatthias Ringwald        K = 10
2694930cef6SMatthias Ringwald
2704930cef6SMatthias Ringwald        for k in range(sum(y1), K):
2714930cef6SMatthias Ringwald            q_pvq = ((corr_xy + x[:10]) ** 2) / (energy_y + 2*y1[:10] + 1)
2724930cef6SMatthias Ringwald            n_best = np.argmax(q_pvq)
2734930cef6SMatthias Ringwald
2744930cef6SMatthias Ringwald            corr_xy += x[n_best]
2754930cef6SMatthias Ringwald            energy_y += 2*y1[n_best] + 1
2764930cef6SMatthias Ringwald            y1[n_best] += 1
2774930cef6SMatthias Ringwald
278*6897da5cSDirk Helbig        ## Stage 2 Shape search, step 7
2794930cef6SMatthias Ringwald
2804930cef6SMatthias Ringwald        y0 = np.append(y1[:10], [ 0 ] * 6)
2814930cef6SMatthias Ringwald
2824930cef6SMatthias Ringwald        q_pvq = ((corr_xy + x[10:]) ** 2) / (energy_y + 2*y0[10:] + 1)
2834930cef6SMatthias Ringwald        n_best = 10 + np.argmax(q_pvq)
2844930cef6SMatthias Ringwald
2854930cef6SMatthias Ringwald        y0[n_best] += 1
2864930cef6SMatthias Ringwald
287*6897da5cSDirk Helbig        ## Stage 2 Shape search, step 8
2884930cef6SMatthias Ringwald
2894930cef6SMatthias Ringwald        y0 *= np.sign(t2_rot).astype(int)
2904930cef6SMatthias Ringwald        y1 *= np.sign(t2_rot).astype(int)
2914930cef6SMatthias Ringwald        y2 *= np.sign(t2_rot).astype(int)
2924930cef6SMatthias Ringwald        y3 *= np.sign(t2_rot).astype(int)
2934930cef6SMatthias Ringwald
294*6897da5cSDirk Helbig        ## Stage 2 Shape search, step 9
2954930cef6SMatthias Ringwald
2964930cef6SMatthias Ringwald        xq = [ y / np.sqrt(sum(y ** 2)) for y in (y0, y1, y2, y3) ]
2974930cef6SMatthias Ringwald
298*6897da5cSDirk Helbig        ## Shape and gain combination determination
2994930cef6SMatthias Ringwald
3004930cef6SMatthias Ringwald        G = [ T.SNS_VQ_REG_ADJ_GAINS, T.SNS_VQ_REG_LF_ADJ_GAINS,
3014930cef6SMatthias Ringwald              T.SNS_VQ_NEAR_ADJ_GAINS, T.SNS_VQ_FAR_ADJ_GAINS ]
3024930cef6SMatthias Ringwald
3034930cef6SMatthias Ringwald        dMSE = [ [ sum((t2_rot - G[j][i] * xq[j]) ** 2)
3044930cef6SMatthias Ringwald                   for i in range(len(G[j])) ] for j in range(4) ]
3054930cef6SMatthias Ringwald
3064930cef6SMatthias Ringwald        self.shape = np.argmin([ np.min(dMSE[j]) for j in range(4) ])
3074930cef6SMatthias Ringwald        self.gain = np.argmin(dMSE[self.shape])
3084930cef6SMatthias Ringwald
3094930cef6SMatthias Ringwald        gain = G[self.shape][self.gain]
3104930cef6SMatthias Ringwald
311*6897da5cSDirk Helbig        ## Enumeration of the selected PVQ pulse configurations
3124930cef6SMatthias Ringwald
3134930cef6SMatthias Ringwald        if self.shape == 0:
3144930cef6SMatthias Ringwald            (self.idx_a, self.ls_a) = self.enum_mpvq(y0[:10])
3154930cef6SMatthias Ringwald            (self.idx_b, self.ls_b) = self.enum_mpvq(y0[10:])
3164930cef6SMatthias Ringwald        elif self.shape == 1:
3174930cef6SMatthias Ringwald            (self.idx_a, self.ls_a) = self.enum_mpvq(y1[:10])
3184930cef6SMatthias Ringwald            (self.idx_b, self.ls_b) = (None, None)
3194930cef6SMatthias Ringwald        elif self.shape == 2:
3204930cef6SMatthias Ringwald            (self.idx_a, self.ls_a) = self.enum_mpvq(y2)
3214930cef6SMatthias Ringwald            (self.idx_b, self.ls_b) = (None, None)
3224930cef6SMatthias Ringwald        elif self.shape == 3:
3234930cef6SMatthias Ringwald            (self.idx_a, self.ls_a) = self.enum_mpvq(y3)
3244930cef6SMatthias Ringwald            (self.idx_b, self.ls_b) = (None, None)
3254930cef6SMatthias Ringwald
326*6897da5cSDirk Helbig        ## Synthesis of the Quantized scale factor
3274930cef6SMatthias Ringwald
3284930cef6SMatthias Ringwald        scf_q = st1 + gain * fftpack.idct(xq[self.shape], norm = 'ortho')
3294930cef6SMatthias Ringwald
3304930cef6SMatthias Ringwald        return scf_q
3314930cef6SMatthias Ringwald
332*6897da5cSDirk Helbig    def run(self, eb, att, nbytes, x):
3334930cef6SMatthias Ringwald
334*6897da5cSDirk Helbig        scf = self.compute_scale_factors(eb, att, nbytes)
3354930cef6SMatthias Ringwald        scf_q = self.quantize(scf)
3364930cef6SMatthias Ringwald        y = self.spectral_shaping(scf_q, False, x)
3374930cef6SMatthias Ringwald
3384930cef6SMatthias Ringwald        return y
3394930cef6SMatthias Ringwald
3404930cef6SMatthias Ringwald    def store(self, b):
3414930cef6SMatthias Ringwald
3424930cef6SMatthias Ringwald        shape = self.shape
3434930cef6SMatthias Ringwald        gain_msb_bits = np.array([ 1, 1, 2, 2 ])[shape]
3444930cef6SMatthias Ringwald        gain_lsb_bits = np.array([ 0, 1, 0, 1 ])[shape]
3454930cef6SMatthias Ringwald
3464930cef6SMatthias Ringwald        b.write_uint(self.ind_lf, 5)
3474930cef6SMatthias Ringwald        b.write_uint(self.ind_hf, 5)
3484930cef6SMatthias Ringwald
3494930cef6SMatthias Ringwald        b.write_bit(shape >> 1)
3504930cef6SMatthias Ringwald
3514930cef6SMatthias Ringwald        b.write_uint(self.gain >> gain_lsb_bits, gain_msb_bits)
3524930cef6SMatthias Ringwald
3534930cef6SMatthias Ringwald        b.write_bit(self.ls_a)
3544930cef6SMatthias Ringwald
3554930cef6SMatthias Ringwald        if self.shape == 0:
3564930cef6SMatthias Ringwald            sz_shape_a = 2390004
3574930cef6SMatthias Ringwald            index_joint = self.idx_a + \
3584930cef6SMatthias Ringwald                (2 * self.idx_b + self.ls_b + 2) * sz_shape_a
3594930cef6SMatthias Ringwald
3604930cef6SMatthias Ringwald        elif self.shape == 1:
3614930cef6SMatthias Ringwald            sz_shape_a = 2390004
3624930cef6SMatthias Ringwald            index_joint = self.idx_a + (self.gain & 1) * sz_shape_a
3634930cef6SMatthias Ringwald
3644930cef6SMatthias Ringwald        elif self.shape == 2:
3654930cef6SMatthias Ringwald            index_joint = self.idx_a
3664930cef6SMatthias Ringwald
3674930cef6SMatthias Ringwald        elif self.shape == 3:
3684930cef6SMatthias Ringwald            sz_shape_a = 15158272
3694930cef6SMatthias Ringwald            index_joint = sz_shape_a + (self.gain & 1) + 2 * self.idx_a
3704930cef6SMatthias Ringwald
3714930cef6SMatthias Ringwald        b.write_uint(index_joint, 14 - gain_msb_bits)
3724930cef6SMatthias Ringwald        b.write_uint(index_joint >> (14 - gain_msb_bits), 12)
3734930cef6SMatthias Ringwald
3744930cef6SMatthias Ringwald
3754930cef6SMatthias Ringwaldclass SnsSynthesis(Sns):
3764930cef6SMatthias Ringwald
3774930cef6SMatthias Ringwald    def __init__(self, dt, sr):
3784930cef6SMatthias Ringwald
3794930cef6SMatthias Ringwald        super().__init__(dt, sr)
3804930cef6SMatthias Ringwald
3814930cef6SMatthias Ringwald    def deenum_mpvq(self, index, ls, npulses, n):
3824930cef6SMatthias Ringwald
383*6897da5cSDirk Helbig        y = np.zeros(n, dtype=np.intc)
3844930cef6SMatthias Ringwald        pos = 0
3854930cef6SMatthias Ringwald
3864930cef6SMatthias Ringwald        for i in range(len(y)-1, -1, -1):
3874930cef6SMatthias Ringwald
3884930cef6SMatthias Ringwald            if index > 0:
3894930cef6SMatthias Ringwald                yi = 0
3904930cef6SMatthias Ringwald                while index < T.SNS_MPVQ_OFFSETS[i][npulses - yi]: yi += 1
3914930cef6SMatthias Ringwald                index -= T.SNS_MPVQ_OFFSETS[i][npulses - yi]
3924930cef6SMatthias Ringwald            else:
3934930cef6SMatthias Ringwald                yi = npulses
3944930cef6SMatthias Ringwald
3954930cef6SMatthias Ringwald            y[pos] = [ yi, -yi ][int(ls)]
3964930cef6SMatthias Ringwald            pos += 1
3974930cef6SMatthias Ringwald
3984930cef6SMatthias Ringwald            npulses -= yi
3994930cef6SMatthias Ringwald            if npulses <= 0:
4004930cef6SMatthias Ringwald                break
4014930cef6SMatthias Ringwald
4024930cef6SMatthias Ringwald            if yi > 0:
4034930cef6SMatthias Ringwald                ls = index & 1
4044930cef6SMatthias Ringwald                index >>= 1
4054930cef6SMatthias Ringwald
4064930cef6SMatthias Ringwald        return y
4074930cef6SMatthias Ringwald
4084930cef6SMatthias Ringwald    def unquantize(self):
4094930cef6SMatthias Ringwald
410*6897da5cSDirk Helbig        ## SNS VQ Decoding
4114930cef6SMatthias Ringwald
412*6897da5cSDirk Helbig        y = np.empty(16, dtype=np.intc)
4134930cef6SMatthias Ringwald
4144930cef6SMatthias Ringwald        if self.shape == 0:
4154930cef6SMatthias Ringwald            y[:10] = self.deenum_mpvq(self.idx_a, self.ls_a, 10, 10)
4164930cef6SMatthias Ringwald            y[10:] = self.deenum_mpvq(self.idx_b, self.ls_b,  1,  6)
4174930cef6SMatthias Ringwald        elif self.shape == 1:
4184930cef6SMatthias Ringwald            y[:10] = self.deenum_mpvq(self.idx_a, self.ls_a, 10, 10)
419*6897da5cSDirk Helbig            y[10:] = np.zeros(6, dtype=np.intc)
4204930cef6SMatthias Ringwald        elif self.shape == 2:
4214930cef6SMatthias Ringwald            y = self.deenum_mpvq(self.idx_a, self.ls_a, 8, 16)
4224930cef6SMatthias Ringwald        elif self.shape == 3:
4234930cef6SMatthias Ringwald            y = self.deenum_mpvq(self.idx_a, self.ls_a, 6, 16)
4244930cef6SMatthias Ringwald
425*6897da5cSDirk Helbig        ## Unit energy normalization
4264930cef6SMatthias Ringwald
4274930cef6SMatthias Ringwald        y = y / np.sqrt(sum(y ** 2))
4284930cef6SMatthias Ringwald
429*6897da5cSDirk Helbig        ## Reconstruction of the quantized scale factors
4304930cef6SMatthias Ringwald
4314930cef6SMatthias Ringwald        G = [ T.SNS_VQ_REG_ADJ_GAINS, T.SNS_VQ_REG_LF_ADJ_GAINS,
4324930cef6SMatthias Ringwald              T.SNS_VQ_NEAR_ADJ_GAINS, T.SNS_VQ_FAR_ADJ_GAINS ]
4334930cef6SMatthias Ringwald
4344930cef6SMatthias Ringwald        gain = G[self.shape][self.gain]
4354930cef6SMatthias Ringwald
4364930cef6SMatthias Ringwald        scf = np.append(T.SNS_LFCB[self.ind_lf], T.SNS_HFCB[self.ind_hf]) \
4374930cef6SMatthias Ringwald                + gain * fftpack.idct(y, norm = 'ortho')
4384930cef6SMatthias Ringwald
4394930cef6SMatthias Ringwald        return scf
4404930cef6SMatthias Ringwald
4414930cef6SMatthias Ringwald    def load(self, b):
4424930cef6SMatthias Ringwald
4434930cef6SMatthias Ringwald        self.ind_lf = b.read_uint(5)
4444930cef6SMatthias Ringwald        self.ind_hf = b.read_uint(5)
4454930cef6SMatthias Ringwald
4464930cef6SMatthias Ringwald        shape_msb = b.read_bit()
4474930cef6SMatthias Ringwald
4484930cef6SMatthias Ringwald        gain_msb_bits = 1 + shape_msb
4494930cef6SMatthias Ringwald        self.gain = b.read_uint(gain_msb_bits)
4504930cef6SMatthias Ringwald
4514930cef6SMatthias Ringwald        self.ls_a = b.read_bit()
4524930cef6SMatthias Ringwald
4534930cef6SMatthias Ringwald        index_joint  = b.read_uint(14 - gain_msb_bits)
4544930cef6SMatthias Ringwald        index_joint |= b.read_uint(12) << (14 - gain_msb_bits)
4554930cef6SMatthias Ringwald
4564930cef6SMatthias Ringwald        if shape_msb == 0:
4574930cef6SMatthias Ringwald            sz_shape_a = 2390004
4584930cef6SMatthias Ringwald
4594930cef6SMatthias Ringwald            if index_joint >= sz_shape_a * 14:
4604930cef6SMatthias Ringwald                raise ValueError('Invalide SNS joint index')
4614930cef6SMatthias Ringwald
4624930cef6SMatthias Ringwald            self.idx_a = index_joint % sz_shape_a
4634930cef6SMatthias Ringwald            index_joint = index_joint // sz_shape_a
4644930cef6SMatthias Ringwald            if index_joint >= 2:
4654930cef6SMatthias Ringwald                self.shape = 0
4664930cef6SMatthias Ringwald                self.idx_b = (index_joint - 2) // 2
4674930cef6SMatthias Ringwald                self.ls_b =  (index_joint - 2)  % 2
4684930cef6SMatthias Ringwald            else:
4694930cef6SMatthias Ringwald                self.shape = 1
4704930cef6SMatthias Ringwald                self.gain = (self.gain << 1) + (index_joint & 1)
4714930cef6SMatthias Ringwald
4724930cef6SMatthias Ringwald        else:
4734930cef6SMatthias Ringwald            sz_shape_a = 15158272
4744930cef6SMatthias Ringwald            if index_joint >= sz_shape_a + 1549824:
4754930cef6SMatthias Ringwald                raise ValueError('Invalide SNS joint index')
4764930cef6SMatthias Ringwald
4774930cef6SMatthias Ringwald            if index_joint < sz_shape_a:
4784930cef6SMatthias Ringwald                self.shape = 2
4794930cef6SMatthias Ringwald                self.idx_a = index_joint
4804930cef6SMatthias Ringwald            else:
4814930cef6SMatthias Ringwald                self.shape = 3
4824930cef6SMatthias Ringwald                index_joint -= sz_shape_a
4834930cef6SMatthias Ringwald                self.gain = (self.gain << 1) + (index_joint % 2)
4844930cef6SMatthias Ringwald                self.idx_a = index_joint // 2
4854930cef6SMatthias Ringwald
4864930cef6SMatthias Ringwald    def run(self, x):
4874930cef6SMatthias Ringwald
4884930cef6SMatthias Ringwald        scf = self.unquantize()
4894930cef6SMatthias Ringwald        y = self.spectral_shaping(scf, True, x)
4904930cef6SMatthias Ringwald
4914930cef6SMatthias Ringwald        return y
4924930cef6SMatthias Ringwald
4934930cef6SMatthias Ringwald### ------------------------------------------------------------------------ ###
4944930cef6SMatthias Ringwald
4954930cef6SMatthias Ringwalddef check_analysis(rng, dt, sr):
4964930cef6SMatthias Ringwald
4974930cef6SMatthias Ringwald    ok = True
4984930cef6SMatthias Ringwald
4994930cef6SMatthias Ringwald    analysis = SnsAnalysis(dt, sr)
5004930cef6SMatthias Ringwald
5014930cef6SMatthias Ringwald    for i in range(10):
502*6897da5cSDirk Helbig        ne = T.I[dt][sr][-1]
503*6897da5cSDirk Helbig        x  = rng.random(ne) * 1e4
504*6897da5cSDirk Helbig        e  = rng.random(len(T.I[dt][sr]) - 1) * 1e10
5054930cef6SMatthias Ringwald
506*6897da5cSDirk Helbig        if sr >= T.SRATE_48K_HR:
507*6897da5cSDirk Helbig            for nbits in (1144, 1152, 2296, 2304, 4400, 4408):
508*6897da5cSDirk Helbig                y = analysis.run(e, False, nbits // 8, x)
5094930cef6SMatthias Ringwald                data = analysis.get_data()
5104930cef6SMatthias Ringwald
511*6897da5cSDirk Helbig                (y_c, data_c) = lc3.sns_analyze(
512*6897da5cSDirk Helbig                    dt, sr, nbits // 8, e, False, x)
513*6897da5cSDirk Helbig
514*6897da5cSDirk Helbig                for k in data.keys():
515*6897da5cSDirk Helbig                    ok = ok and data_c[k] == data[k]
516*6897da5cSDirk Helbig
517*6897da5cSDirk Helbig                ok = ok and lc3.sns_get_nbits() == analysis.get_nbits()
518*6897da5cSDirk Helbig                ok = ok and np.amax(np.abs(y - y_c)) < 1e-1
519*6897da5cSDirk Helbig
520*6897da5cSDirk Helbig        else:
521*6897da5cSDirk Helbig            for att in (0, 1):
522*6897da5cSDirk Helbig                y = analysis.run(e, att, 0, x)
523*6897da5cSDirk Helbig                data = analysis.get_data()
524*6897da5cSDirk Helbig
525*6897da5cSDirk Helbig                (y_c, data_c) = lc3.sns_analyze(dt, sr, 0, e, att, x)
5264930cef6SMatthias Ringwald
5274930cef6SMatthias Ringwald                for k in data.keys():
5284930cef6SMatthias Ringwald                    ok = ok and data_c[k] == data[k]
5294930cef6SMatthias Ringwald
5304930cef6SMatthias Ringwald                ok = ok and lc3.sns_get_nbits() == analysis.get_nbits()
5314930cef6SMatthias Ringwald                ok = ok and np.amax(np.abs(y - y_c)) < 1e-1
5324930cef6SMatthias Ringwald
5334930cef6SMatthias Ringwald    return ok
5344930cef6SMatthias Ringwald
5354930cef6SMatthias Ringwalddef check_synthesis(rng, dt, sr):
5364930cef6SMatthias Ringwald
5374930cef6SMatthias Ringwald    ok = True
5384930cef6SMatthias Ringwald
5394930cef6SMatthias Ringwald    synthesis = SnsSynthesis(dt, sr)
5404930cef6SMatthias Ringwald
5414930cef6SMatthias Ringwald    for i in range(100):
5424930cef6SMatthias Ringwald
5434930cef6SMatthias Ringwald        synthesis.ind_lf = rng.integers(0, 32)
5444930cef6SMatthias Ringwald        synthesis.ind_hf = rng.integers(0, 32)
5454930cef6SMatthias Ringwald
5464930cef6SMatthias Ringwald        shape = rng.integers(0, 4)
5474930cef6SMatthias Ringwald        sz_shape_a = [ 2390004, 2390004, 15158272, 774912 ][shape]
5484930cef6SMatthias Ringwald        sz_shape_b = [ 6, 1, 0, 0 ][shape]
5494930cef6SMatthias Ringwald        synthesis.shape = shape
5504930cef6SMatthias Ringwald        synthesis.gain = rng.integers(0, [ 2, 4, 4, 8 ][shape])
5514930cef6SMatthias Ringwald        synthesis.idx_a = rng.integers(0, sz_shape_a, endpoint=True)
5524930cef6SMatthias Ringwald        synthesis.ls_a = bool(rng.integers(0, 1, endpoint=True))
5534930cef6SMatthias Ringwald        synthesis.idx_b = rng.integers(0, sz_shape_b, endpoint=True)
5544930cef6SMatthias Ringwald        synthesis.ls_b = bool(rng.integers(0, 1, endpoint=True))
5554930cef6SMatthias Ringwald
556*6897da5cSDirk Helbig        ne = T.I[dt][sr][-1]
557*6897da5cSDirk Helbig        x  = rng.random(ne) * 1e4
5584930cef6SMatthias Ringwald
5594930cef6SMatthias Ringwald        y = synthesis.run(x)
5604930cef6SMatthias Ringwald        y_c = lc3.sns_synthesize(dt, sr, synthesis.get_data(), x)
561*6897da5cSDirk Helbig        ok = ok and np.amax(np.abs(1 - y/y_c)) < 1e-5
5624930cef6SMatthias Ringwald
5634930cef6SMatthias Ringwald    return ok
5644930cef6SMatthias Ringwald
5654930cef6SMatthias Ringwalddef check_analysis_appendix_c(dt):
5664930cef6SMatthias Ringwald
567*6897da5cSDirk Helbig    i0 = dt - T.DT_7M5
5684930cef6SMatthias Ringwald    sr = T.SRATE_16K
569*6897da5cSDirk Helbig
5704930cef6SMatthias Ringwald    ok = True
5714930cef6SMatthias Ringwald
572*6897da5cSDirk Helbig    for i in range(len(C.E_B[i0])):
5734930cef6SMatthias Ringwald
574*6897da5cSDirk Helbig        scf = lc3.sns_compute_scale_factors(dt, sr, 0, C.E_B[i0][i], False)
575*6897da5cSDirk Helbig        ok = ok and np.amax(np.abs(scf - C.SCF[i0][i])) < 1e-4
5764930cef6SMatthias Ringwald
5774930cef6SMatthias Ringwald        (lf, hf) = lc3.sns_resolve_codebooks(scf)
578*6897da5cSDirk Helbig        ok = ok and lf == C.IND_LF[i0][i] and hf == C.IND_HF[i0][i]
5794930cef6SMatthias Ringwald
5804930cef6SMatthias Ringwald        (y, yn, shape, gain) = lc3.sns_quantize(scf, lf, hf)
581*6897da5cSDirk Helbig        ok = ok and np.any(y[0][:16] - C.SNS_Y0[i0][i] == 0)
582*6897da5cSDirk Helbig        ok = ok and np.any(y[1][:10] - C.SNS_Y1[i0][i] == 0)
583*6897da5cSDirk Helbig        ok = ok and np.any(y[2][:16] - C.SNS_Y2[i0][i] == 0)
584*6897da5cSDirk Helbig        ok = ok and np.any(y[3][:16] - C.SNS_Y3[i0][i] == 0)
585*6897da5cSDirk Helbig        ok = ok and shape == 2*C.SUBMODE_MSB[i0][i] + C.SUBMODE_LSB[i0][i]
586*6897da5cSDirk Helbig        ok = ok and gain == C.G_IND[i0][i]
5874930cef6SMatthias Ringwald
5884930cef6SMatthias Ringwald        scf_q = lc3.sns_unquantize(lf, hf, yn[shape], shape, gain)
589*6897da5cSDirk Helbig        ok = ok and np.amax(np.abs(scf_q - C.SCF_Q[i0][i])) < 1e-5
5904930cef6SMatthias Ringwald
591*6897da5cSDirk Helbig        x = lc3.sns_spectral_shaping(dt, sr, C.SCF_Q[i0][i], False, C.X[i0][i])
592*6897da5cSDirk Helbig        ok = ok and np.amax(np.abs(1 - x/C.X_S[i0][i])) < 1e-5
5934930cef6SMatthias Ringwald
594*6897da5cSDirk Helbig        (x, data) = lc3.sns_analyze(dt, sr, 0, C.E_B[i0][i], False, C.X[i0][i])
595*6897da5cSDirk Helbig        ok = ok and data['lfcb'] == C.IND_LF[i0][i]
596*6897da5cSDirk Helbig        ok = ok and data['hfcb'] == C.IND_HF[i0][i]
597*6897da5cSDirk Helbig        ok = ok and data['shape'] == 2*C.SUBMODE_MSB[i0][i] + \
598*6897da5cSDirk Helbig                                       C.SUBMODE_LSB[i0][i]
599*6897da5cSDirk Helbig        ok = ok and data['gain'] == C.G_IND[i0][i]
600*6897da5cSDirk Helbig        ok = ok and data['idx_a'] == C.IDX_A[i0][i]
601*6897da5cSDirk Helbig        ok = ok and data['ls_a'] == C.LS_IND_A[i0][i]
602*6897da5cSDirk Helbig        ok = ok and (C.IDX_B[i0][i] is None or
603*6897da5cSDirk Helbig            data['idx_b'] == C.IDX_B[i0][i])
604*6897da5cSDirk Helbig        ok = ok and (C.LS_IND_B[i0][i] is None or
605*6897da5cSDirk Helbig            data['ls_b'] == C.LS_IND_B[i0][i])
606*6897da5cSDirk Helbig        ok = ok and np.amax(np.abs(1 - x/C.X_S[i0][i])) < 1e-5
6074930cef6SMatthias Ringwald
6084930cef6SMatthias Ringwald    return ok
6094930cef6SMatthias Ringwald
6104930cef6SMatthias Ringwalddef check_synthesis_appendix_c(dt):
6114930cef6SMatthias Ringwald
612*6897da5cSDirk Helbig    i0 = dt - T.DT_7M5
6134930cef6SMatthias Ringwald    sr = T.SRATE_16K
614*6897da5cSDirk Helbig
6154930cef6SMatthias Ringwald    ok = True
6164930cef6SMatthias Ringwald
617*6897da5cSDirk Helbig    for i in range(len(C.X_HAT_TNS[i0])):
6184930cef6SMatthias Ringwald
6194930cef6SMatthias Ringwald        data = {
620*6897da5cSDirk Helbig            'lfcb'  : C.IND_LF[i0][i], 'hfcb' : C.IND_HF[i0][i],
621*6897da5cSDirk Helbig            'shape' : 2*C.SUBMODE_MSB[i0][i] + C.SUBMODE_LSB[i0][i],
622*6897da5cSDirk Helbig            'gain'  : C.G_IND[i0][i],
623*6897da5cSDirk Helbig            'idx_a' : C.IDX_A[i0][i],
624*6897da5cSDirk Helbig            'ls_a'  : C.LS_IND_A[i0][i],
625*6897da5cSDirk Helbig            'idx_b' : C.IDX_B[i0][i] if C.IDX_B[i0][i] is not None else 0,
626*6897da5cSDirk Helbig            'ls_b'  : C.LS_IND_B[i0][i] if C.LS_IND_B[i0][i] is not None else 0,
6274930cef6SMatthias Ringwald        }
6284930cef6SMatthias Ringwald
629*6897da5cSDirk Helbig        x = lc3.sns_synthesize(dt, sr, data, C.X_HAT_TNS[i0][i])
630*6897da5cSDirk Helbig        ok = ok and np.amax(np.abs(x - C.X_HAT_SNS[i0][i])) < 1e0
6314930cef6SMatthias Ringwald
6324930cef6SMatthias Ringwald    return ok
6334930cef6SMatthias Ringwald
6344930cef6SMatthias Ringwalddef check():
6354930cef6SMatthias Ringwald
6364930cef6SMatthias Ringwald    rng = np.random.default_rng(1234)
6374930cef6SMatthias Ringwald    ok = True
6384930cef6SMatthias Ringwald
6394930cef6SMatthias Ringwald    for dt in range(T.NUM_DT):
640*6897da5cSDirk Helbig        for sr in range(T.SRATE_8K, T.SRATE_48K + 1):
6414930cef6SMatthias Ringwald            ok = ok and check_analysis(rng, dt, sr)
6424930cef6SMatthias Ringwald            ok = ok and check_synthesis(rng, dt, sr)
6434930cef6SMatthias Ringwald
644*6897da5cSDirk Helbig    for dt in ( T.DT_2M5, T.DT_5M, T.DT_10M ):
645*6897da5cSDirk Helbig        for sr in ( T.SRATE_48K_HR, T.SRATE_96K_HR ):
646*6897da5cSDirk Helbig            ok = ok and check_analysis(rng, dt, sr)
647*6897da5cSDirk Helbig            ok = ok and check_synthesis(rng, dt, sr)
648*6897da5cSDirk Helbig
649*6897da5cSDirk Helbig    for dt in ( T.DT_7M5, T.DT_10M ):
650*6897da5cSDirk Helbig        check_analysis_appendix_c(dt)
651*6897da5cSDirk Helbig        check_synthesis_appendix_c(dt)
6524930cef6SMatthias Ringwald
6534930cef6SMatthias Ringwald    return ok
6544930cef6SMatthias Ringwald
6554930cef6SMatthias Ringwald### ------------------------------------------------------------------------ ###
656