xref: /aosp_15_r20/external/liblc3/test/ltpf.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.signal as signal
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 Resampler_12k8:
26*49fe348cSAndroid Build Coastguard Worker
27*49fe348cSAndroid Build Coastguard Worker    def __init__(self, dt, sr, history = 0):
28*49fe348cSAndroid Build Coastguard Worker
29*49fe348cSAndroid Build Coastguard Worker        self.sr = sr
30*49fe348cSAndroid Build Coastguard Worker        self.p = 192 // T.SRATE_KHZ[sr]
31*49fe348cSAndroid Build Coastguard Worker        self.w = 240 // self.p
32*49fe348cSAndroid Build Coastguard Worker
33*49fe348cSAndroid Build Coastguard Worker        self.n = ((T.DT_MS[dt] * 128) / 10).astype(int)
34*49fe348cSAndroid Build Coastguard Worker        self.d = [ 24, 44 ][dt == T.DT_7M5]
35*49fe348cSAndroid Build Coastguard Worker
36*49fe348cSAndroid Build Coastguard Worker        self.x = np.zeros(self.w + T.NS[dt][sr])
37*49fe348cSAndroid Build Coastguard Worker        self.u = np.zeros(self.n + 2)
38*49fe348cSAndroid Build Coastguard Worker        self.y = np.zeros(self.n + self.d + history)
39*49fe348cSAndroid Build Coastguard Worker
40*49fe348cSAndroid Build Coastguard Worker    def resample(self, x):
41*49fe348cSAndroid Build Coastguard Worker
42*49fe348cSAndroid Build Coastguard Worker        p = self.p
43*49fe348cSAndroid Build Coastguard Worker        w = self.w
44*49fe348cSAndroid Build Coastguard Worker        d = self.d
45*49fe348cSAndroid Build Coastguard Worker        n = self.n
46*49fe348cSAndroid Build Coastguard Worker
47*49fe348cSAndroid Build Coastguard Worker        ### Sliding window
48*49fe348cSAndroid Build Coastguard Worker
49*49fe348cSAndroid Build Coastguard Worker        self.x[:w] = self.x[-w:]
50*49fe348cSAndroid Build Coastguard Worker        self.x[w:] = x
51*49fe348cSAndroid Build Coastguard Worker        self.u[:2] = self.u[-2:]
52*49fe348cSAndroid Build Coastguard Worker
53*49fe348cSAndroid Build Coastguard Worker        if len(self.y) > 2*n + d:
54*49fe348cSAndroid Build Coastguard Worker            self.y[n+d:-n] = self.y[d+2*n:]
55*49fe348cSAndroid Build Coastguard Worker        if len(self.y) > n + d:
56*49fe348cSAndroid Build Coastguard Worker            self.y[-n:] = self.y[:n]
57*49fe348cSAndroid Build Coastguard Worker        self.y[:d] = self.y[n:d+n]
58*49fe348cSAndroid Build Coastguard Worker
59*49fe348cSAndroid Build Coastguard Worker        x = self.x
60*49fe348cSAndroid Build Coastguard Worker        u = self.u
61*49fe348cSAndroid Build Coastguard Worker
62*49fe348cSAndroid Build Coastguard Worker        ### Resampling
63*49fe348cSAndroid Build Coastguard Worker
64*49fe348cSAndroid Build Coastguard Worker        h = np.zeros(240 + p)
65*49fe348cSAndroid Build Coastguard Worker        h[-119:] = T.LTPF_H12K8[:119]
66*49fe348cSAndroid Build Coastguard Worker        h[ :120] = T.LTPF_H12K8[119:]
67*49fe348cSAndroid Build Coastguard Worker
68*49fe348cSAndroid Build Coastguard Worker        for i in range(n):
69*49fe348cSAndroid Build Coastguard Worker            e = (15 * i) // p
70*49fe348cSAndroid Build Coastguard Worker            f = (15 * i)  % p
71*49fe348cSAndroid Build Coastguard Worker            k = np.arange(-120, 120 + p, p) - f
72*49fe348cSAndroid Build Coastguard Worker            u[2+i] = p * np.dot( x[e:e+w+1], np.take(h, k) )
73*49fe348cSAndroid Build Coastguard Worker
74*49fe348cSAndroid Build Coastguard Worker        if self.sr == T.SRATE_8K:
75*49fe348cSAndroid Build Coastguard Worker            u = 0.5 * u
76*49fe348cSAndroid Build Coastguard Worker
77*49fe348cSAndroid Build Coastguard Worker        ### High-pass filtering
78*49fe348cSAndroid Build Coastguard Worker
79*49fe348cSAndroid Build Coastguard Worker        b = [ 0.9827947082978771, -1.9655894165957540, 0.9827947082978771 ]
80*49fe348cSAndroid Build Coastguard Worker        a = [ 1                 , -1.9652933726226904, 0.9658854605688177 ]
81*49fe348cSAndroid Build Coastguard Worker
82*49fe348cSAndroid Build Coastguard Worker        self.y[d:d+n] = b[0] * u[2:] + b[1] * u[1:-1] + b[2] * u[:-2]
83*49fe348cSAndroid Build Coastguard Worker        for i in range(n):
84*49fe348cSAndroid Build Coastguard Worker            self.y[d+i] -= a[1] * self.y[d+i-1] + a[2] * self.y[d+i-2]
85*49fe348cSAndroid Build Coastguard Worker
86*49fe348cSAndroid Build Coastguard Worker        return self.y
87*49fe348cSAndroid Build Coastguard Worker
88*49fe348cSAndroid Build Coastguard Worker
89*49fe348cSAndroid Build Coastguard Workerclass Resampler_6k4:
90*49fe348cSAndroid Build Coastguard Worker
91*49fe348cSAndroid Build Coastguard Worker    def __init__(self, n, history = 0):
92*49fe348cSAndroid Build Coastguard Worker
93*49fe348cSAndroid Build Coastguard Worker        self.x = np.zeros(n + 5)
94*49fe348cSAndroid Build Coastguard Worker        self.n = n // 2
95*49fe348cSAndroid Build Coastguard Worker
96*49fe348cSAndroid Build Coastguard Worker        self.y = np.zeros(self.n + history)
97*49fe348cSAndroid Build Coastguard Worker
98*49fe348cSAndroid Build Coastguard Worker    def resample(self, x):
99*49fe348cSAndroid Build Coastguard Worker
100*49fe348cSAndroid Build Coastguard Worker        n = self.n
101*49fe348cSAndroid Build Coastguard Worker
102*49fe348cSAndroid Build Coastguard Worker        ### Sliding window
103*49fe348cSAndroid Build Coastguard Worker
104*49fe348cSAndroid Build Coastguard Worker        self.x[:3] = self.x[-5:-2]
105*49fe348cSAndroid Build Coastguard Worker        self.x[3:] = x[:2*n+2]
106*49fe348cSAndroid Build Coastguard Worker        x = self.x
107*49fe348cSAndroid Build Coastguard Worker
108*49fe348cSAndroid Build Coastguard Worker        if len(self.y) > 2*n:
109*49fe348cSAndroid Build Coastguard Worker            self.y[n:-n] = self.y[2*n:]
110*49fe348cSAndroid Build Coastguard Worker        if len(self.y) > n:
111*49fe348cSAndroid Build Coastguard Worker            self.y[-n:] = self.y[:n]
112*49fe348cSAndroid Build Coastguard Worker
113*49fe348cSAndroid Build Coastguard Worker        ### Downsampling to 6.4 KHz
114*49fe348cSAndroid Build Coastguard Worker
115*49fe348cSAndroid Build Coastguard Worker        h = [ 0.1236796411180537, 0.2353512128364889, 0.2819382920909148,
116*49fe348cSAndroid Build Coastguard Worker              0.2353512128364889, 0.1236796411180537 ]
117*49fe348cSAndroid Build Coastguard Worker
118*49fe348cSAndroid Build Coastguard Worker        self.y[:n] = [ np.dot(x[2*i:2*i+5], h) for i in range(self.n) ]
119*49fe348cSAndroid Build Coastguard Worker        return self.y
120*49fe348cSAndroid Build Coastguard Worker
121*49fe348cSAndroid Build Coastguard Worker
122*49fe348cSAndroid Build Coastguard Workerdef initial_hp50_state():
123*49fe348cSAndroid Build Coastguard Worker    return { 's1': 0, 's2': 0 }
124*49fe348cSAndroid Build Coastguard Worker
125*49fe348cSAndroid Build Coastguard Worker### ------------------------------------------------------------------------ ###
126*49fe348cSAndroid Build Coastguard Worker
127*49fe348cSAndroid Build Coastguard Workerclass Ltpf:
128*49fe348cSAndroid Build Coastguard Worker
129*49fe348cSAndroid Build Coastguard Worker    def __init__(self, dt, sr):
130*49fe348cSAndroid Build Coastguard Worker
131*49fe348cSAndroid Build Coastguard Worker        self.dt = dt
132*49fe348cSAndroid Build Coastguard Worker        self.sr = sr
133*49fe348cSAndroid Build Coastguard Worker
134*49fe348cSAndroid Build Coastguard Worker        (self.pitch_present, self.pitch_index) = (None, None)
135*49fe348cSAndroid Build Coastguard Worker
136*49fe348cSAndroid Build Coastguard Worker
137*49fe348cSAndroid Build Coastguard Workerclass LtpfAnalysis(Ltpf):
138*49fe348cSAndroid Build Coastguard Worker
139*49fe348cSAndroid Build Coastguard Worker    def __init__(self, dt, sr):
140*49fe348cSAndroid Build Coastguard Worker
141*49fe348cSAndroid Build Coastguard Worker        super().__init__(dt, sr)
142*49fe348cSAndroid Build Coastguard Worker
143*49fe348cSAndroid Build Coastguard Worker        self.resampler_12k8 = Resampler_12k8(dt, sr,
144*49fe348cSAndroid Build Coastguard Worker            history = 232 + (32 if dt == T.DT_2M5 else 0))
145*49fe348cSAndroid Build Coastguard Worker
146*49fe348cSAndroid Build Coastguard Worker        self.resampler_6k4 = Resampler_6k4(self.resampler_12k8.n,
147*49fe348cSAndroid Build Coastguard Worker            history = 114 + (16 if dt == T.DT_2M5 else 0))
148*49fe348cSAndroid Build Coastguard Worker
149*49fe348cSAndroid Build Coastguard Worker        self.active = False
150*49fe348cSAndroid Build Coastguard Worker        self.tc = 0
151*49fe348cSAndroid Build Coastguard Worker        self.pitch = 0
152*49fe348cSAndroid Build Coastguard Worker        self.nc = np.zeros(2)
153*49fe348cSAndroid Build Coastguard Worker
154*49fe348cSAndroid Build Coastguard Worker    def get_data(self):
155*49fe348cSAndroid Build Coastguard Worker
156*49fe348cSAndroid Build Coastguard Worker        return { 'active' : self.active,
157*49fe348cSAndroid Build Coastguard Worker                 'pitch_index' : self.pitch_index }
158*49fe348cSAndroid Build Coastguard Worker
159*49fe348cSAndroid Build Coastguard Worker    def get_nbits(self):
160*49fe348cSAndroid Build Coastguard Worker
161*49fe348cSAndroid Build Coastguard Worker        return 1 + 10 * int(self.pitch_present)
162*49fe348cSAndroid Build Coastguard Worker
163*49fe348cSAndroid Build Coastguard Worker    def correlate(self, x, i0, n, k0, k1):
164*49fe348cSAndroid Build Coastguard Worker
165*49fe348cSAndroid Build Coastguard Worker        return np.array([ np.dot(
166*49fe348cSAndroid Build Coastguard Worker            np.take(x, np.arange(i0, n)),
167*49fe348cSAndroid Build Coastguard Worker            np.take(x, np.arange(i0, n) - k)) for k in range(k0, 1+k1) ])
168*49fe348cSAndroid Build Coastguard Worker
169*49fe348cSAndroid Build Coastguard Worker    def norm_corr(self, x, i0, n, k):
170*49fe348cSAndroid Build Coastguard Worker
171*49fe348cSAndroid Build Coastguard Worker        u  = np.take(x, np.arange(i0, n))
172*49fe348cSAndroid Build Coastguard Worker        v  = np.take(x, np.arange(i0, n) - k)
173*49fe348cSAndroid Build Coastguard Worker        uv = np.dot(u, v)
174*49fe348cSAndroid Build Coastguard Worker        return uv / np.sqrt(np.dot(u, u) * np.dot(v, v)) if uv > 0 else 0
175*49fe348cSAndroid Build Coastguard Worker
176*49fe348cSAndroid Build Coastguard Worker    def run(self, x):
177*49fe348cSAndroid Build Coastguard Worker
178*49fe348cSAndroid Build Coastguard Worker        ### Resampling
179*49fe348cSAndroid Build Coastguard Worker
180*49fe348cSAndroid Build Coastguard Worker        x_12k8 = self.resampler_12k8.resample(x)
181*49fe348cSAndroid Build Coastguard Worker
182*49fe348cSAndroid Build Coastguard Worker        ### Pitch detection algorithm
183*49fe348cSAndroid Build Coastguard Worker
184*49fe348cSAndroid Build Coastguard Worker        x  = self.resampler_6k4.resample(x_12k8)
185*49fe348cSAndroid Build Coastguard Worker        i0 = [-16, 0][self.dt > T.DT_2M5]
186*49fe348cSAndroid Build Coastguard Worker        n  = self.resampler_6k4.n
187*49fe348cSAndroid Build Coastguard Worker
188*49fe348cSAndroid Build Coastguard Worker        r  = self.correlate(x, i0, n, 17, 114)
189*49fe348cSAndroid Build Coastguard Worker        rw = r * (1 - 0.5 * np.arange(len(r)) / (len(r) - 1))
190*49fe348cSAndroid Build Coastguard Worker
191*49fe348cSAndroid Build Coastguard Worker        tc = self.tc
192*49fe348cSAndroid Build Coastguard Worker        k0 = max(0, tc-4)
193*49fe348cSAndroid Build Coastguard Worker        k1 = min(len(r)-1, tc+4)
194*49fe348cSAndroid Build Coastguard Worker        t  = [ 17 + np.argmax(rw), 17 + k0 + np.argmax(r[k0:1+k1]) ]
195*49fe348cSAndroid Build Coastguard Worker
196*49fe348cSAndroid Build Coastguard Worker        nc = [ self.norm_corr(x, i0, n, t[i]) for i in range(2) ]
197*49fe348cSAndroid Build Coastguard Worker        ti = int(nc[1] > 0.85 * nc[0])
198*49fe348cSAndroid Build Coastguard Worker        self.tc = t[ti] - 17
199*49fe348cSAndroid Build Coastguard Worker
200*49fe348cSAndroid Build Coastguard Worker        self.pitch_present = bool(nc[ti] > 0.6)
201*49fe348cSAndroid Build Coastguard Worker
202*49fe348cSAndroid Build Coastguard Worker        ### Pitch-lag parameter
203*49fe348cSAndroid Build Coastguard Worker
204*49fe348cSAndroid Build Coastguard Worker        if self.pitch_present:
205*49fe348cSAndroid Build Coastguard Worker            tc = self.tc + 17
206*49fe348cSAndroid Build Coastguard Worker
207*49fe348cSAndroid Build Coastguard Worker            x  = x_12k8
208*49fe348cSAndroid Build Coastguard Worker            i0 = [-32, 0][self.dt > T.DT_2M5]
209*49fe348cSAndroid Build Coastguard Worker            n  = self.resampler_12k8.n
210*49fe348cSAndroid Build Coastguard Worker
211*49fe348cSAndroid Build Coastguard Worker            k0 = max( 32, 2*tc-4)
212*49fe348cSAndroid Build Coastguard Worker            k1 = min(228, 2*tc+4)
213*49fe348cSAndroid Build Coastguard Worker            r  = self.correlate(x, i0, n, k0-4, k1+4)
214*49fe348cSAndroid Build Coastguard Worker            e  = k0 + np.argmax(r[4:-4])
215*49fe348cSAndroid Build Coastguard Worker
216*49fe348cSAndroid Build Coastguard Worker            h = np.zeros(42)
217*49fe348cSAndroid Build Coastguard Worker            h[-15:] = T.LTPF_H4[:15]
218*49fe348cSAndroid Build Coastguard Worker            h[ :16] = T.LTPF_H4[15:]
219*49fe348cSAndroid Build Coastguard Worker
220*49fe348cSAndroid Build Coastguard Worker            m = np.arange(-4, 5)
221*49fe348cSAndroid Build Coastguard Worker            s = [ np.dot( np.take(r, e-k0+4 + m), np.take(h, 4*m-d) ) \
222*49fe348cSAndroid Build Coastguard Worker                      for d in range(-3, 4) ]
223*49fe348cSAndroid Build Coastguard Worker
224*49fe348cSAndroid Build Coastguard Worker            f = np.argmax(s[3:])            if e <=  32 else \
225*49fe348cSAndroid Build Coastguard Worker                -3 + np.argmax(s)           if e <  127 else \
226*49fe348cSAndroid Build Coastguard Worker                -2 + 2*np.argmax(s[1:-1:2]) if e <  157 else 0
227*49fe348cSAndroid Build Coastguard Worker
228*49fe348cSAndroid Build Coastguard Worker            e -=   (f < 0)
229*49fe348cSAndroid Build Coastguard Worker            f += 4*(f < 0)
230*49fe348cSAndroid Build Coastguard Worker
231*49fe348cSAndroid Build Coastguard Worker            self.pitch_index = 4*e + f    - 128 if e < 127 else \
232*49fe348cSAndroid Build Coastguard Worker                               2*e + f//2 + 126 if e < 157 else e + 283
233*49fe348cSAndroid Build Coastguard Worker
234*49fe348cSAndroid Build Coastguard Worker        else:
235*49fe348cSAndroid Build Coastguard Worker            e = f = 0
236*49fe348cSAndroid Build Coastguard Worker            self.pitch_index = 0
237*49fe348cSAndroid Build Coastguard Worker
238*49fe348cSAndroid Build Coastguard Worker        ### Activation bit
239*49fe348cSAndroid Build Coastguard Worker
240*49fe348cSAndroid Build Coastguard Worker        h = np.zeros(24)
241*49fe348cSAndroid Build Coastguard Worker        h[-7:] = T.LTPF_HI[:7]
242*49fe348cSAndroid Build Coastguard Worker        h[ :8] = T.LTPF_HI[7:]
243*49fe348cSAndroid Build Coastguard Worker
244*49fe348cSAndroid Build Coastguard Worker        x  = x_12k8
245*49fe348cSAndroid Build Coastguard Worker        i0 = [-32, 0][self.dt > T.DT_2M5]
246*49fe348cSAndroid Build Coastguard Worker        n  = self.resampler_12k8.n
247*49fe348cSAndroid Build Coastguard Worker
248*49fe348cSAndroid Build Coastguard Worker        k = np.arange(-2, 3)
249*49fe348cSAndroid Build Coastguard Worker        u = [ np.dot( np.take(x, i-k), np.take(h, 4*k) ) \
250*49fe348cSAndroid Build Coastguard Worker                  for i in range(i0, n) ]
251*49fe348cSAndroid Build Coastguard Worker        v = [ np.dot( np.take(x, i-k), np.take(h, 4*k-f) ) \
252*49fe348cSAndroid Build Coastguard Worker                  for i in range(i0-e, n-e) ]
253*49fe348cSAndroid Build Coastguard Worker
254*49fe348cSAndroid Build Coastguard Worker        nc = max(0, np.dot(u, v)) / np.sqrt(np.dot(u, u) * np.dot(v, v)) \
255*49fe348cSAndroid Build Coastguard Worker                if self.pitch_present else 0
256*49fe348cSAndroid Build Coastguard Worker
257*49fe348cSAndroid Build Coastguard Worker        pitch = e + f/4
258*49fe348cSAndroid Build Coastguard Worker
259*49fe348cSAndroid Build Coastguard Worker        if not self.active:
260*49fe348cSAndroid Build Coastguard Worker            active = (self.dt == T.DT_10M or self.nc[1] > 0.94) \
261*49fe348cSAndroid Build Coastguard Worker                     and self.nc[0] > 0.94 and nc > 0.94
262*49fe348cSAndroid Build Coastguard Worker
263*49fe348cSAndroid Build Coastguard Worker        else:
264*49fe348cSAndroid Build Coastguard Worker            dp = abs(pitch - self.pitch)
265*49fe348cSAndroid Build Coastguard Worker            dc = nc - self.nc[0]
266*49fe348cSAndroid Build Coastguard Worker            active = nc > 0.9 or (dp < 2 and dc > -0.1 and nc > 0.84)
267*49fe348cSAndroid Build Coastguard Worker
268*49fe348cSAndroid Build Coastguard Worker        if not self.pitch_present:
269*49fe348cSAndroid Build Coastguard Worker            active = False
270*49fe348cSAndroid Build Coastguard Worker            pitch = 0
271*49fe348cSAndroid Build Coastguard Worker            nc = 0
272*49fe348cSAndroid Build Coastguard Worker
273*49fe348cSAndroid Build Coastguard Worker        self.active = active
274*49fe348cSAndroid Build Coastguard Worker        self.pitch = pitch
275*49fe348cSAndroid Build Coastguard Worker        self.nc[1] = self.nc[0]
276*49fe348cSAndroid Build Coastguard Worker        self.nc[0] = nc
277*49fe348cSAndroid Build Coastguard Worker
278*49fe348cSAndroid Build Coastguard Worker        return self.pitch_present
279*49fe348cSAndroid Build Coastguard Worker
280*49fe348cSAndroid Build Coastguard Worker    def disable(self):
281*49fe348cSAndroid Build Coastguard Worker
282*49fe348cSAndroid Build Coastguard Worker        self.active = False
283*49fe348cSAndroid Build Coastguard Worker
284*49fe348cSAndroid Build Coastguard Worker    def store(self, b):
285*49fe348cSAndroid Build Coastguard Worker
286*49fe348cSAndroid Build Coastguard Worker        b.write_uint(self.active, 1)
287*49fe348cSAndroid Build Coastguard Worker        b.write_uint(self.pitch_index, 9)
288*49fe348cSAndroid Build Coastguard Worker
289*49fe348cSAndroid Build Coastguard Worker
290*49fe348cSAndroid Build Coastguard Workerclass LtpfSynthesis(Ltpf):
291*49fe348cSAndroid Build Coastguard Worker
292*49fe348cSAndroid Build Coastguard Worker    C_N = [ T.LTPF_N_8K , T.LTPF_N_16K,
293*49fe348cSAndroid Build Coastguard Worker            T.LTPF_N_24K, T.LTPF_N_32K, T.LTPF_N_48K ]
294*49fe348cSAndroid Build Coastguard Worker
295*49fe348cSAndroid Build Coastguard Worker    C_D = [ T.LTPF_D_8K , T.LTPF_D_16K,
296*49fe348cSAndroid Build Coastguard Worker            T.LTPF_D_24K, T.LTPF_D_32K, T.LTPF_D_48K ]
297*49fe348cSAndroid Build Coastguard Worker
298*49fe348cSAndroid Build Coastguard Worker    def __init__(self, dt, sr):
299*49fe348cSAndroid Build Coastguard Worker
300*49fe348cSAndroid Build Coastguard Worker        super().__init__(dt, sr)
301*49fe348cSAndroid Build Coastguard Worker
302*49fe348cSAndroid Build Coastguard Worker        self.C_N = LtpfSynthesis.C_N[sr]
303*49fe348cSAndroid Build Coastguard Worker        self.C_D = LtpfSynthesis.C_D[sr]
304*49fe348cSAndroid Build Coastguard Worker
305*49fe348cSAndroid Build Coastguard Worker        ns = T.NS[dt][sr]
306*49fe348cSAndroid Build Coastguard Worker
307*49fe348cSAndroid Build Coastguard Worker        self.active = [ False, False ]
308*49fe348cSAndroid Build Coastguard Worker        self.pitch_index = 0
309*49fe348cSAndroid Build Coastguard Worker
310*49fe348cSAndroid Build Coastguard Worker        max_pitch_12k8 = 228
311*49fe348cSAndroid Build Coastguard Worker        max_pitch = max_pitch_12k8 * T.SRATE_KHZ[self.sr] / 12.8
312*49fe348cSAndroid Build Coastguard Worker        max_pitch = np.ceil(max_pitch).astype(int)
313*49fe348cSAndroid Build Coastguard Worker
314*49fe348cSAndroid Build Coastguard Worker        self.x = np.zeros(ns)
315*49fe348cSAndroid Build Coastguard Worker        self.y = np.zeros(max_pitch + len(self.C_D[0]))
316*49fe348cSAndroid Build Coastguard Worker
317*49fe348cSAndroid Build Coastguard Worker        self.p_e = [ 0, 0 ]
318*49fe348cSAndroid Build Coastguard Worker        self.p_f = [ 0, 0 ]
319*49fe348cSAndroid Build Coastguard Worker        self.c_n = [ None, None ]
320*49fe348cSAndroid Build Coastguard Worker        self.c_d = [ None, None ]
321*49fe348cSAndroid Build Coastguard Worker
322*49fe348cSAndroid Build Coastguard Worker    def load(self, b):
323*49fe348cSAndroid Build Coastguard Worker
324*49fe348cSAndroid Build Coastguard Worker        self.active[0] = bool(b.read_uint(1))
325*49fe348cSAndroid Build Coastguard Worker        self.pitch_index = b.read_uint(9)
326*49fe348cSAndroid Build Coastguard Worker
327*49fe348cSAndroid Build Coastguard Worker    def disable(self):
328*49fe348cSAndroid Build Coastguard Worker
329*49fe348cSAndroid Build Coastguard Worker        self.active[0] = False
330*49fe348cSAndroid Build Coastguard Worker        self.pitch_index = 0
331*49fe348cSAndroid Build Coastguard Worker
332*49fe348cSAndroid Build Coastguard Worker    def run(self, x, nbytes):
333*49fe348cSAndroid Build Coastguard Worker
334*49fe348cSAndroid Build Coastguard Worker        sr = self.sr
335*49fe348cSAndroid Build Coastguard Worker        dt = self.dt
336*49fe348cSAndroid Build Coastguard Worker
337*49fe348cSAndroid Build Coastguard Worker        ### Filter parameters
338*49fe348cSAndroid Build Coastguard Worker
339*49fe348cSAndroid Build Coastguard Worker        pitch_index = self.pitch_index
340*49fe348cSAndroid Build Coastguard Worker
341*49fe348cSAndroid Build Coastguard Worker        if pitch_index >= 440:
342*49fe348cSAndroid Build Coastguard Worker            p_e = pitch_index - 283
343*49fe348cSAndroid Build Coastguard Worker            p_f = 0
344*49fe348cSAndroid Build Coastguard Worker        elif pitch_index >= 380:
345*49fe348cSAndroid Build Coastguard Worker            p_e = pitch_index // 2 - 63
346*49fe348cSAndroid Build Coastguard Worker            p_f = 2*(pitch_index - 2*(p_e + 63))
347*49fe348cSAndroid Build Coastguard Worker        else:
348*49fe348cSAndroid Build Coastguard Worker            p_e = pitch_index // 4 + 32
349*49fe348cSAndroid Build Coastguard Worker            p_f = pitch_index - 4*(p_e - 32)
350*49fe348cSAndroid Build Coastguard Worker
351*49fe348cSAndroid Build Coastguard Worker        p = (p_e + p_f / 4) * T.SRATE_KHZ[self.sr] / 12.8
352*49fe348cSAndroid Build Coastguard Worker
353*49fe348cSAndroid Build Coastguard Worker        self.p_e[0] = int(p * 4 + 0.5) // 4
354*49fe348cSAndroid Build Coastguard Worker        self.p_f[0] = int(p * 4 + 0.5) - 4*self.p_e[0]
355*49fe348cSAndroid Build Coastguard Worker
356*49fe348cSAndroid Build Coastguard Worker        nbits = round(nbytes*8 * 10 / T.DT_MS[dt])
357*49fe348cSAndroid Build Coastguard Worker        if dt == T.DT_2M5:
358*49fe348cSAndroid Build Coastguard Worker            nbits = int(nbits * (1 - 0.4))
359*49fe348cSAndroid Build Coastguard Worker        elif dt == T.DT_5M:
360*49fe348cSAndroid Build Coastguard Worker            nbits = nbits - 160
361*49fe348cSAndroid Build Coastguard Worker
362*49fe348cSAndroid Build Coastguard Worker        g_idx = max(nbits // 80, 3+sr) - (3+sr)
363*49fe348cSAndroid Build Coastguard Worker
364*49fe348cSAndroid Build Coastguard Worker        g = [ 0.4, 0.35, 0.3, 0.25 ][g_idx] if g_idx < 4 else 0
365*49fe348cSAndroid Build Coastguard Worker        g_idx = min(g_idx, 3)
366*49fe348cSAndroid Build Coastguard Worker
367*49fe348cSAndroid Build Coastguard Worker        self.c_n[0] = 0.85 * g * LtpfSynthesis.C_N[sr][g_idx]
368*49fe348cSAndroid Build Coastguard Worker        self.c_d[0] = g * LtpfSynthesis.C_D[sr][self.p_f[0]]
369*49fe348cSAndroid Build Coastguard Worker
370*49fe348cSAndroid Build Coastguard Worker        ### Transition handling
371*49fe348cSAndroid Build Coastguard Worker
372*49fe348cSAndroid Build Coastguard Worker        n0 = (T.SRATE_KHZ[sr] * 1000) // 400
373*49fe348cSAndroid Build Coastguard Worker        ns = T.NS[dt][sr]
374*49fe348cSAndroid Build Coastguard Worker
375*49fe348cSAndroid Build Coastguard Worker        x  = np.append(x, self.x)
376*49fe348cSAndroid Build Coastguard Worker        y  = np.append(np.zeros(ns), self.y)
377*49fe348cSAndroid Build Coastguard Worker        yc = y.copy()
378*49fe348cSAndroid Build Coastguard Worker
379*49fe348cSAndroid Build Coastguard Worker        c_n = self.c_n
380*49fe348cSAndroid Build Coastguard Worker        c_d = self.c_d
381*49fe348cSAndroid Build Coastguard Worker
382*49fe348cSAndroid Build Coastguard Worker        l_n = len(c_n[0])
383*49fe348cSAndroid Build Coastguard Worker        l_d = len(c_d[0])
384*49fe348cSAndroid Build Coastguard Worker
385*49fe348cSAndroid Build Coastguard Worker        d = [ self.p_e[0] - (l_d - 1) // 2,
386*49fe348cSAndroid Build Coastguard Worker              self.p_e[1] - (l_d - 1) // 2 ]
387*49fe348cSAndroid Build Coastguard Worker
388*49fe348cSAndroid Build Coastguard Worker        for k in range(n0):
389*49fe348cSAndroid Build Coastguard Worker
390*49fe348cSAndroid Build Coastguard Worker            if not self.active[0] and not self.active[1]:
391*49fe348cSAndroid Build Coastguard Worker                y[k] = x[k]
392*49fe348cSAndroid Build Coastguard Worker
393*49fe348cSAndroid Build Coastguard Worker            elif self.active[0] and not self.active[1]:
394*49fe348cSAndroid Build Coastguard Worker                u = np.dot(c_n[0], np.take(x, k - np.arange(l_n))) - \
395*49fe348cSAndroid Build Coastguard Worker                    np.dot(c_d[0], np.take(y, k - d[0] - np.arange(l_d)))
396*49fe348cSAndroid Build Coastguard Worker                y[k] = x[k] - (k/n0) * u
397*49fe348cSAndroid Build Coastguard Worker
398*49fe348cSAndroid Build Coastguard Worker            elif not self.active[0] and self.active[1]:
399*49fe348cSAndroid Build Coastguard Worker                u = np.dot(c_n[1], np.take(x, k - np.arange(l_n))) - \
400*49fe348cSAndroid Build Coastguard Worker                    np.dot(c_d[1], np.take(y, k - d[1] - np.arange(l_d)))
401*49fe348cSAndroid Build Coastguard Worker                y[k] = x[k] - (1 - k/n0) * u
402*49fe348cSAndroid Build Coastguard Worker
403*49fe348cSAndroid Build Coastguard Worker            elif self.p_e[0] == self.p_e[1] and self.p_f[0] == self.p_f[1]:
404*49fe348cSAndroid Build Coastguard Worker                u = np.dot(c_n[0], np.take(x, k - np.arange(l_n))) - \
405*49fe348cSAndroid Build Coastguard Worker                    np.dot(c_d[0], np.take(y, k - d[0] - np.arange(l_d)))
406*49fe348cSAndroid Build Coastguard Worker                y[k] = x[k] - u
407*49fe348cSAndroid Build Coastguard Worker
408*49fe348cSAndroid Build Coastguard Worker            else:
409*49fe348cSAndroid Build Coastguard Worker                u = np.dot(c_n[1], np.take(x, k - np.arange(l_n))) - \
410*49fe348cSAndroid Build Coastguard Worker                    np.dot(c_d[1], np.take(y, k - d[1] - np.arange(l_d)))
411*49fe348cSAndroid Build Coastguard Worker                yc[k] = x[k] - (1 - k/n0) * u
412*49fe348cSAndroid Build Coastguard Worker
413*49fe348cSAndroid Build Coastguard Worker                u = np.dot(c_n[0], np.take(yc, k - np.arange(l_n))) - \
414*49fe348cSAndroid Build Coastguard Worker                    np.dot(c_d[0], np.take(y , k - d[0] - np.arange(l_d)))
415*49fe348cSAndroid Build Coastguard Worker                y[k] = yc[k] - (k/n0) * u
416*49fe348cSAndroid Build Coastguard Worker
417*49fe348cSAndroid Build Coastguard Worker        ### Remainder of the frame
418*49fe348cSAndroid Build Coastguard Worker
419*49fe348cSAndroid Build Coastguard Worker        for k in range(n0, ns):
420*49fe348cSAndroid Build Coastguard Worker
421*49fe348cSAndroid Build Coastguard Worker            if not self.active[0]:
422*49fe348cSAndroid Build Coastguard Worker                y[k] = x[k]
423*49fe348cSAndroid Build Coastguard Worker
424*49fe348cSAndroid Build Coastguard Worker            else:
425*49fe348cSAndroid Build Coastguard Worker                u = np.dot(c_n[0], np.take(x, k - np.arange(l_n))) - \
426*49fe348cSAndroid Build Coastguard Worker                    np.dot(c_d[0], np.take(y, k - d[0] - np.arange(l_d)))
427*49fe348cSAndroid Build Coastguard Worker                y[k] = x[k] - u
428*49fe348cSAndroid Build Coastguard Worker
429*49fe348cSAndroid Build Coastguard Worker        ### Sliding window
430*49fe348cSAndroid Build Coastguard Worker
431*49fe348cSAndroid Build Coastguard Worker        self.active[1] = self.active[0]
432*49fe348cSAndroid Build Coastguard Worker        self.p_e[1] = self.p_e[0]
433*49fe348cSAndroid Build Coastguard Worker        self.p_f[1] = self.p_f[0]
434*49fe348cSAndroid Build Coastguard Worker        self.c_n[1] = self.c_n[0]
435*49fe348cSAndroid Build Coastguard Worker        self.c_d[1] = self.c_d[0]
436*49fe348cSAndroid Build Coastguard Worker
437*49fe348cSAndroid Build Coastguard Worker        self.x = x[:ns]
438*49fe348cSAndroid Build Coastguard Worker        self.y = np.append(self.y[ns:], y[:ns])
439*49fe348cSAndroid Build Coastguard Worker
440*49fe348cSAndroid Build Coastguard Worker        return y[:ns]
441*49fe348cSAndroid Build Coastguard Worker
442*49fe348cSAndroid Build Coastguard Workerdef initial_state():
443*49fe348cSAndroid Build Coastguard Worker    return { 'active' : False, 'pitch': 0, 'nc':  np.zeros(2),
444*49fe348cSAndroid Build Coastguard Worker             'hp50' : initial_hp50_state(),
445*49fe348cSAndroid Build Coastguard Worker             'x_12k8' : np.zeros(384), 'x_6k4' : np.zeros(178), 'tc' : 0 }
446*49fe348cSAndroid Build Coastguard Worker
447*49fe348cSAndroid Build Coastguard Workerdef initial_sstate():
448*49fe348cSAndroid Build Coastguard Worker    return { 'active': False, 'pitch': 0,
449*49fe348cSAndroid Build Coastguard Worker             'c': np.zeros(2*12), 'x': np.zeros(12) }
450*49fe348cSAndroid Build Coastguard Worker
451*49fe348cSAndroid Build Coastguard Worker### ------------------------------------------------------------------------ ###
452*49fe348cSAndroid Build Coastguard Worker
453*49fe348cSAndroid Build Coastguard Workerdef check_resampler(rng, dt, sr):
454*49fe348cSAndroid Build Coastguard Worker
455*49fe348cSAndroid Build Coastguard Worker    ns = T.NS[dt][sr]
456*49fe348cSAndroid Build Coastguard Worker    nt = (5 * T.SRATE_KHZ[sr]) // 4
457*49fe348cSAndroid Build Coastguard Worker    ok = True
458*49fe348cSAndroid Build Coastguard Worker
459*49fe348cSAndroid Build Coastguard Worker    r = Resampler_12k8(dt, sr)
460*49fe348cSAndroid Build Coastguard Worker
461*49fe348cSAndroid Build Coastguard Worker    hp50_c = initial_hp50_state()
462*49fe348cSAndroid Build Coastguard Worker    x_c = np.zeros(nt)
463*49fe348cSAndroid Build Coastguard Worker    y_c = np.zeros(384)
464*49fe348cSAndroid Build Coastguard Worker
465*49fe348cSAndroid Build Coastguard Worker    for run in range(10):
466*49fe348cSAndroid Build Coastguard Worker
467*49fe348cSAndroid Build Coastguard Worker        x = ((2 * rng.random(ns)) - 1) * (2 ** 15 - 1)
468*49fe348cSAndroid Build Coastguard Worker        y = r.resample(x)
469*49fe348cSAndroid Build Coastguard Worker
470*49fe348cSAndroid Build Coastguard Worker        x_c = np.append(x_c[-nt:], x.astype(np.int16))
471*49fe348cSAndroid Build Coastguard Worker        y_c[:-r.n] = y_c[r.n:]
472*49fe348cSAndroid Build Coastguard Worker        y_c = lc3.ltpf_resample(dt, sr, hp50_c, x_c, y_c)
473*49fe348cSAndroid Build Coastguard Worker
474*49fe348cSAndroid Build Coastguard Worker        ok = ok and np.amax(np.abs(y_c[-r.d-r.n:] - y[:r.d+r.n]/2)) < 4
475*49fe348cSAndroid Build Coastguard Worker
476*49fe348cSAndroid Build Coastguard Worker    return ok
477*49fe348cSAndroid Build Coastguard Worker
478*49fe348cSAndroid Build Coastguard Workerdef check_resampler_appendix_c(dt):
479*49fe348cSAndroid Build Coastguard Worker
480*49fe348cSAndroid Build Coastguard Worker    i0 = dt - T.DT_7M5
481*49fe348cSAndroid Build Coastguard Worker    sr = T.SRATE_16K
482*49fe348cSAndroid Build Coastguard Worker
483*49fe348cSAndroid Build Coastguard Worker    ok = True
484*49fe348cSAndroid Build Coastguard Worker
485*49fe348cSAndroid Build Coastguard Worker    nt = (5 * T.SRATE_KHZ[sr]) // 4
486*49fe348cSAndroid Build Coastguard Worker    n  = [ 96, 128 ][i0]
487*49fe348cSAndroid Build Coastguard Worker    k  = [ 44,  24 ][i0] + n
488*49fe348cSAndroid Build Coastguard Worker
489*49fe348cSAndroid Build Coastguard Worker    state = initial_hp50_state()
490*49fe348cSAndroid Build Coastguard Worker
491*49fe348cSAndroid Build Coastguard Worker    x = np.append(np.zeros(nt), C.X_PCM[i0][0])
492*49fe348cSAndroid Build Coastguard Worker    y = np.zeros(384)
493*49fe348cSAndroid Build Coastguard Worker    y = lc3.ltpf_resample(dt, sr, state, x, y)
494*49fe348cSAndroid Build Coastguard Worker    u = y[-k:len(C.X_TILDE_12K8D[i0][0])-k]
495*49fe348cSAndroid Build Coastguard Worker
496*49fe348cSAndroid Build Coastguard Worker    ok = ok and np.amax(np.abs(u - C.X_TILDE_12K8D[i0][0]/2)) < 2
497*49fe348cSAndroid Build Coastguard Worker
498*49fe348cSAndroid Build Coastguard Worker    x = np.append(x[-nt:], C.X_PCM[i0][1])
499*49fe348cSAndroid Build Coastguard Worker    y[:-n] = y[n:]
500*49fe348cSAndroid Build Coastguard Worker    y = lc3.ltpf_resample(dt, sr, state, x, y)
501*49fe348cSAndroid Build Coastguard Worker    u = y[-k:len(C.X_TILDE_12K8D[i0][1])-k]
502*49fe348cSAndroid Build Coastguard Worker
503*49fe348cSAndroid Build Coastguard Worker    ok = ok and np.amax(np.abs(u - C.X_TILDE_12K8D[i0][1]/2)) < 2
504*49fe348cSAndroid Build Coastguard Worker
505*49fe348cSAndroid Build Coastguard Worker    return ok
506*49fe348cSAndroid Build Coastguard Worker
507*49fe348cSAndroid Build Coastguard Workerdef check_analysis(rng, dt, sr):
508*49fe348cSAndroid Build Coastguard Worker
509*49fe348cSAndroid Build Coastguard Worker    ns = T.NS[dt][sr]
510*49fe348cSAndroid Build Coastguard Worker    nt = (5 * T.SRATE_KHZ[sr]) // 4
511*49fe348cSAndroid Build Coastguard Worker    ok = True
512*49fe348cSAndroid Build Coastguard Worker
513*49fe348cSAndroid Build Coastguard Worker    state_c = initial_state()
514*49fe348cSAndroid Build Coastguard Worker    x_c = np.zeros(ns+nt)
515*49fe348cSAndroid Build Coastguard Worker
516*49fe348cSAndroid Build Coastguard Worker    ltpf = LtpfAnalysis(dt, sr)
517*49fe348cSAndroid Build Coastguard Worker
518*49fe348cSAndroid Build Coastguard Worker    t = np.arange(100 * ns) / (T.SRATE_KHZ[sr] * 1000)
519*49fe348cSAndroid Build Coastguard Worker    s = signal.chirp(t, f0=10, f1=2500, t1=t[-1], method='logarithmic')
520*49fe348cSAndroid Build Coastguard Worker
521*49fe348cSAndroid Build Coastguard Worker    for i in range(20):
522*49fe348cSAndroid Build Coastguard Worker
523*49fe348cSAndroid Build Coastguard Worker        x = s[i*ns:(i+1)*ns] * (2 ** 15 - 1)
524*49fe348cSAndroid Build Coastguard Worker
525*49fe348cSAndroid Build Coastguard Worker        pitch_present = ltpf.run(x)
526*49fe348cSAndroid Build Coastguard Worker        data = ltpf.get_data()
527*49fe348cSAndroid Build Coastguard Worker
528*49fe348cSAndroid Build Coastguard Worker        x_c = np.append(x_c[-nt:], x.astype(np.int16))
529*49fe348cSAndroid Build Coastguard Worker        (pitch_present_c, data_c) = lc3.ltpf_analyse(dt, sr, state_c, x_c)
530*49fe348cSAndroid Build Coastguard Worker
531*49fe348cSAndroid Build Coastguard Worker        ok = ok and (not pitch_present or state_c['tc'] == ltpf.tc)
532*49fe348cSAndroid Build Coastguard Worker        ok = ok and np.amax(np.abs(state_c['nc'][0] - ltpf.nc[0])) < 1e-1
533*49fe348cSAndroid Build Coastguard Worker        ok = ok and pitch_present_c == pitch_present
534*49fe348cSAndroid Build Coastguard Worker        ok = ok and data_c['active'] == data['active']
535*49fe348cSAndroid Build Coastguard Worker        ok = ok and data_c['pitch_index'] == data['pitch_index']
536*49fe348cSAndroid Build Coastguard Worker        ok = ok and lc3.ltpf_get_nbits(pitch_present) == ltpf.get_nbits()
537*49fe348cSAndroid Build Coastguard Worker
538*49fe348cSAndroid Build Coastguard Worker    return ok
539*49fe348cSAndroid Build Coastguard Worker
540*49fe348cSAndroid Build Coastguard Workerdef check_synthesis(rng, dt, sr):
541*49fe348cSAndroid Build Coastguard Worker
542*49fe348cSAndroid Build Coastguard Worker    ok = True
543*49fe348cSAndroid Build Coastguard Worker
544*49fe348cSAndroid Build Coastguard Worker    ns = T.NS[dt][sr]
545*49fe348cSAndroid Build Coastguard Worker    nd = 18 * T.SRATE_KHZ[sr]
546*49fe348cSAndroid Build Coastguard Worker
547*49fe348cSAndroid Build Coastguard Worker    synthesis = LtpfSynthesis(dt, sr)
548*49fe348cSAndroid Build Coastguard Worker
549*49fe348cSAndroid Build Coastguard Worker    state_c = initial_sstate()
550*49fe348cSAndroid Build Coastguard Worker    x_c = np.zeros(nd+ns)
551*49fe348cSAndroid Build Coastguard Worker
552*49fe348cSAndroid Build Coastguard Worker    for i in range(50):
553*49fe348cSAndroid Build Coastguard Worker
554*49fe348cSAndroid Build Coastguard Worker        pitch_present = bool(rng.integers(0, 10) >= 1)
555*49fe348cSAndroid Build Coastguard Worker        if not pitch_present:
556*49fe348cSAndroid Build Coastguard Worker            synthesis.disable()
557*49fe348cSAndroid Build Coastguard Worker        else:
558*49fe348cSAndroid Build Coastguard Worker            synthesis.active[0] = bool(rng.integers(0, 5) >= 1)
559*49fe348cSAndroid Build Coastguard Worker            synthesis.pitch_index = rng.integers(0, 512)
560*49fe348cSAndroid Build Coastguard Worker
561*49fe348cSAndroid Build Coastguard Worker        data_c = None if not pitch_present else \
562*49fe348cSAndroid Build Coastguard Worker            { 'active' : synthesis.active[0],
563*49fe348cSAndroid Build Coastguard Worker              'pitch_index' : synthesis.pitch_index }
564*49fe348cSAndroid Build Coastguard Worker
565*49fe348cSAndroid Build Coastguard Worker        x = rng.random(ns) * 1e4
566*49fe348cSAndroid Build Coastguard Worker        nbytes = rng.integers(10*(2+sr), 10*(6+sr))
567*49fe348cSAndroid Build Coastguard Worker
568*49fe348cSAndroid Build Coastguard Worker        x_c[:nd] = x_c[ns:]
569*49fe348cSAndroid Build Coastguard Worker        x_c[nd:] = x
570*49fe348cSAndroid Build Coastguard Worker
571*49fe348cSAndroid Build Coastguard Worker        y = synthesis.run(x, nbytes)
572*49fe348cSAndroid Build Coastguard Worker        x_c = lc3.ltpf_synthesize(dt, sr, nbytes, state_c, data_c, x_c)
573*49fe348cSAndroid Build Coastguard Worker
574*49fe348cSAndroid Build Coastguard Worker        ok = ok and np.amax(np.abs(x_c[nd:] - y)) < 1e-2
575*49fe348cSAndroid Build Coastguard Worker
576*49fe348cSAndroid Build Coastguard Worker    return ok
577*49fe348cSAndroid Build Coastguard Worker
578*49fe348cSAndroid Build Coastguard Workerdef check_analysis_appendix_c(dt):
579*49fe348cSAndroid Build Coastguard Worker
580*49fe348cSAndroid Build Coastguard Worker    i0 = dt - T.DT_7M5
581*49fe348cSAndroid Build Coastguard Worker    sr = T.SRATE_16K
582*49fe348cSAndroid Build Coastguard Worker
583*49fe348cSAndroid Build Coastguard Worker    ok = True
584*49fe348cSAndroid Build Coastguard Worker
585*49fe348cSAndroid Build Coastguard Worker    nt = (5 * T.SRATE_KHZ[sr]) // 4
586*49fe348cSAndroid Build Coastguard Worker
587*49fe348cSAndroid Build Coastguard Worker    state = initial_state()
588*49fe348cSAndroid Build Coastguard Worker
589*49fe348cSAndroid Build Coastguard Worker    x = np.append(np.zeros(nt), C.X_PCM[i0][0])
590*49fe348cSAndroid Build Coastguard Worker    (pitch_present, data) = lc3.ltpf_analyse(dt, sr, state, x)
591*49fe348cSAndroid Build Coastguard Worker
592*49fe348cSAndroid Build Coastguard Worker    ok = ok and C.T_CURR[i0][0] - state['tc'] == 17
593*49fe348cSAndroid Build Coastguard Worker    ok = ok and np.amax(np.abs(state['nc'][0] - C.NC_LTPF[i0][0])) < 1e-5
594*49fe348cSAndroid Build Coastguard Worker    ok = ok and pitch_present == C.PITCH_PRESENT[i0][0]
595*49fe348cSAndroid Build Coastguard Worker    ok = ok and data['pitch_index'] == C.PITCH_INDEX[i0][0]
596*49fe348cSAndroid Build Coastguard Worker    ok = ok and data['active'] == C.LTPF_ACTIVE[i0][0]
597*49fe348cSAndroid Build Coastguard Worker
598*49fe348cSAndroid Build Coastguard Worker    x = np.append(x[-nt:], C.X_PCM[i0][1])
599*49fe348cSAndroid Build Coastguard Worker    (pitch_present, data) = lc3.ltpf_analyse(dt, sr, state, x)
600*49fe348cSAndroid Build Coastguard Worker
601*49fe348cSAndroid Build Coastguard Worker    ok = ok and C.T_CURR[i0][1] - state['tc'] == 17
602*49fe348cSAndroid Build Coastguard Worker    ok = ok and np.amax(np.abs(state['nc'][0] - C.NC_LTPF[i0][1])) < 1e-5
603*49fe348cSAndroid Build Coastguard Worker    ok = ok and pitch_present == C.PITCH_PRESENT[i0][1]
604*49fe348cSAndroid Build Coastguard Worker    ok = ok and data['pitch_index'] == C.PITCH_INDEX[i0][1]
605*49fe348cSAndroid Build Coastguard Worker    ok = ok and data['active'] == C.LTPF_ACTIVE[i0][1]
606*49fe348cSAndroid Build Coastguard Worker
607*49fe348cSAndroid Build Coastguard Worker    return ok
608*49fe348cSAndroid Build Coastguard Worker
609*49fe348cSAndroid Build Coastguard Workerdef check_synthesis_appendix_c(dt):
610*49fe348cSAndroid Build Coastguard Worker
611*49fe348cSAndroid Build Coastguard Worker    sr = T.SRATE_16K
612*49fe348cSAndroid Build Coastguard Worker
613*49fe348cSAndroid Build Coastguard Worker    ok = True
614*49fe348cSAndroid Build Coastguard Worker    if dt != T.DT_10M:
615*49fe348cSAndroid Build Coastguard Worker        return ok
616*49fe348cSAndroid Build Coastguard Worker
617*49fe348cSAndroid Build Coastguard Worker    ns = T.NS[dt][sr]
618*49fe348cSAndroid Build Coastguard Worker    nd = 18 * T.SRATE_KHZ[sr]
619*49fe348cSAndroid Build Coastguard Worker
620*49fe348cSAndroid Build Coastguard Worker    NBYTES = [ C.LTPF_C2_NBITS // 8, C.LTPF_C3_NBITS // 8,
621*49fe348cSAndroid Build Coastguard Worker               C.LTPF_C4_NBITS // 8, C.LTPF_C5_NBITS // 8 ]
622*49fe348cSAndroid Build Coastguard Worker
623*49fe348cSAndroid Build Coastguard Worker    ACTIVE = [ C.LTPF_C2_ACTIVE, C.LTPF_C3_ACTIVE,
624*49fe348cSAndroid Build Coastguard Worker               C.LTPF_C4_ACTIVE, C.LTPF_C5_ACTIVE ]
625*49fe348cSAndroid Build Coastguard Worker
626*49fe348cSAndroid Build Coastguard Worker    PITCH_INDEX = [ C.LTPF_C2_PITCH_INDEX, C.LTPF_C3_PITCH_INDEX,
627*49fe348cSAndroid Build Coastguard Worker                    C.LTPF_C4_PITCH_INDEX, C.LTPF_C5_PITCH_INDEX ]
628*49fe348cSAndroid Build Coastguard Worker
629*49fe348cSAndroid Build Coastguard Worker    X = [ C.LTPF_C2_X, C.LTPF_C3_X,
630*49fe348cSAndroid Build Coastguard Worker          C.LTPF_C4_X, C.LTPF_C5_X ]
631*49fe348cSAndroid Build Coastguard Worker
632*49fe348cSAndroid Build Coastguard Worker    PREV = [ C.LTPF_C2_PREV, C.LTPF_C3_PREV,
633*49fe348cSAndroid Build Coastguard Worker             C.LTPF_C4_PREV, C.LTPF_C5_PREV  ]
634*49fe348cSAndroid Build Coastguard Worker
635*49fe348cSAndroid Build Coastguard Worker    TRANS = [ C.LTPF_C2_TRANS, C.LTPF_C3_TRANS,
636*49fe348cSAndroid Build Coastguard Worker              C.LTPF_C4_TRANS, C.LTPF_C5_TRANS ]
637*49fe348cSAndroid Build Coastguard Worker
638*49fe348cSAndroid Build Coastguard Worker    for i in range(4):
639*49fe348cSAndroid Build Coastguard Worker
640*49fe348cSAndroid Build Coastguard Worker        state = initial_sstate()
641*49fe348cSAndroid Build Coastguard Worker        nbytes = NBYTES[i]
642*49fe348cSAndroid Build Coastguard Worker
643*49fe348cSAndroid Build Coastguard Worker        data = { 'active' : ACTIVE[i][0], 'pitch_index' : PITCH_INDEX[i][0] }
644*49fe348cSAndroid Build Coastguard Worker        x = np.append(np.zeros(nd), X[i][0])
645*49fe348cSAndroid Build Coastguard Worker
646*49fe348cSAndroid Build Coastguard Worker        lc3.ltpf_synthesize(dt, sr, nbytes, state, data, x)
647*49fe348cSAndroid Build Coastguard Worker
648*49fe348cSAndroid Build Coastguard Worker        data = { 'active' : ACTIVE[i][1], 'pitch_index' : PITCH_INDEX[i][1] }
649*49fe348cSAndroid Build Coastguard Worker        x[  :nd-ns] = PREV[i][0][-nd+ns:]
650*49fe348cSAndroid Build Coastguard Worker        x[nd-ns:nd] = PREV[i][1]
651*49fe348cSAndroid Build Coastguard Worker        x[nd:nd+ns] = X[i][1]
652*49fe348cSAndroid Build Coastguard Worker
653*49fe348cSAndroid Build Coastguard Worker        y = lc3.ltpf_synthesize(dt, sr, nbytes, state, data, x)[nd:]
654*49fe348cSAndroid Build Coastguard Worker
655*49fe348cSAndroid Build Coastguard Worker        ok = ok and np.amax(np.abs(y - TRANS[i])) < 1e-3
656*49fe348cSAndroid Build Coastguard Worker
657*49fe348cSAndroid Build Coastguard Worker    return ok
658*49fe348cSAndroid Build Coastguard Worker
659*49fe348cSAndroid Build Coastguard Workerdef check():
660*49fe348cSAndroid Build Coastguard Worker
661*49fe348cSAndroid Build Coastguard Worker    rng = np.random.default_rng(1234)
662*49fe348cSAndroid Build Coastguard Worker    ok = True
663*49fe348cSAndroid Build Coastguard Worker
664*49fe348cSAndroid Build Coastguard Worker    for dt in range(T.NUM_DT):
665*49fe348cSAndroid Build Coastguard Worker        for sr in range(T.SRATE_8K, T.SRATE_48K + 1):
666*49fe348cSAndroid Build Coastguard Worker            ok = ok and check_resampler(rng, dt, sr)
667*49fe348cSAndroid Build Coastguard Worker            ok = ok and check_analysis(rng, dt, sr)
668*49fe348cSAndroid Build Coastguard Worker            ok = ok and check_synthesis(rng, dt, sr)
669*49fe348cSAndroid Build Coastguard Worker
670*49fe348cSAndroid Build Coastguard Worker    for dt in ( T.DT_7M5, T.DT_10M ):
671*49fe348cSAndroid Build Coastguard Worker        ok = ok and check_resampler_appendix_c(dt)
672*49fe348cSAndroid Build Coastguard Worker        ok = ok and check_analysis_appendix_c(dt)
673*49fe348cSAndroid Build Coastguard Worker        ok = ok and check_synthesis_appendix_c(dt)
674*49fe348cSAndroid Build Coastguard Worker
675*49fe348cSAndroid Build Coastguard Worker    return ok
676*49fe348cSAndroid Build Coastguard Worker
677*49fe348cSAndroid Build Coastguard Worker### ------------------------------------------------------------------------ ###
678