xref: /btstack/3rd-party/lc3-google/test/ltpf.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.signal as signal
194930cef6SMatthias Ringwald
204c4eb519SMatthias Ringwaldimport lc3
214930cef6SMatthias Ringwaldimport tables as T, appendix_c as C
224930cef6SMatthias Ringwald
234930cef6SMatthias Ringwald### ------------------------------------------------------------------------ ###
244930cef6SMatthias Ringwald
254930cef6SMatthias Ringwaldclass Resampler_12k8:
264930cef6SMatthias Ringwald
274930cef6SMatthias Ringwald    def __init__(self, dt, sr, history = 0):
284930cef6SMatthias Ringwald
294930cef6SMatthias Ringwald        self.sr = sr
304930cef6SMatthias Ringwald        self.p = 192 // T.SRATE_KHZ[sr]
314930cef6SMatthias Ringwald        self.w = 240 // self.p
324930cef6SMatthias Ringwald
334930cef6SMatthias Ringwald        self.n = ((T.DT_MS[dt] * 128) / 10).astype(int)
34*6897da5cSDirk Helbig        self.d = [ 24, 44 ][dt == T.DT_7M5]
354930cef6SMatthias Ringwald
364930cef6SMatthias Ringwald        self.x = np.zeros(self.w + T.NS[dt][sr])
374930cef6SMatthias Ringwald        self.u = np.zeros(self.n + 2)
384930cef6SMatthias Ringwald        self.y = np.zeros(self.n + self.d + history)
394930cef6SMatthias Ringwald
404930cef6SMatthias Ringwald    def resample(self, x):
414930cef6SMatthias Ringwald
424930cef6SMatthias Ringwald        p = self.p
434930cef6SMatthias Ringwald        w = self.w
444930cef6SMatthias Ringwald        d = self.d
454930cef6SMatthias Ringwald        n = self.n
464930cef6SMatthias Ringwald
474930cef6SMatthias Ringwald        ### Sliding window
484930cef6SMatthias Ringwald
494930cef6SMatthias Ringwald        self.x[:w] = self.x[-w:]
504930cef6SMatthias Ringwald        self.x[w:] = x
514930cef6SMatthias Ringwald        self.u[:2] = self.u[-2:]
524930cef6SMatthias Ringwald
534930cef6SMatthias Ringwald        if len(self.y) > 2*n + d:
544930cef6SMatthias Ringwald            self.y[n+d:-n] = self.y[d+2*n:]
554930cef6SMatthias Ringwald        if len(self.y) > n + d:
564930cef6SMatthias Ringwald            self.y[-n:] = self.y[:n]
574930cef6SMatthias Ringwald        self.y[:d] = self.y[n:d+n]
584930cef6SMatthias Ringwald
594930cef6SMatthias Ringwald        x = self.x
604930cef6SMatthias Ringwald        u = self.u
614930cef6SMatthias Ringwald
62*6897da5cSDirk Helbig        ### Resampling
634930cef6SMatthias Ringwald
644930cef6SMatthias Ringwald        h = np.zeros(240 + p)
654930cef6SMatthias Ringwald        h[-119:] = T.LTPF_H12K8[:119]
664930cef6SMatthias Ringwald        h[ :120] = T.LTPF_H12K8[119:]
674930cef6SMatthias Ringwald
684930cef6SMatthias Ringwald        for i in range(n):
694930cef6SMatthias Ringwald            e = (15 * i) // p
704930cef6SMatthias Ringwald            f = (15 * i)  % p
714930cef6SMatthias Ringwald            k = np.arange(-120, 120 + p, p) - f
724930cef6SMatthias Ringwald            u[2+i] = p * np.dot( x[e:e+w+1], np.take(h, k) )
734930cef6SMatthias Ringwald
744930cef6SMatthias Ringwald        if self.sr == T.SRATE_8K:
754930cef6SMatthias Ringwald            u = 0.5 * u
764930cef6SMatthias Ringwald
77*6897da5cSDirk Helbig        ### High-pass filtering
784930cef6SMatthias Ringwald
794930cef6SMatthias Ringwald        b = [ 0.9827947082978771, -1.9655894165957540, 0.9827947082978771 ]
804930cef6SMatthias Ringwald        a = [ 1                 , -1.9652933726226904, 0.9658854605688177 ]
814930cef6SMatthias Ringwald
824930cef6SMatthias Ringwald        self.y[d:d+n] = b[0] * u[2:] + b[1] * u[1:-1] + b[2] * u[:-2]
834930cef6SMatthias Ringwald        for i in range(n):
844930cef6SMatthias Ringwald            self.y[d+i] -= a[1] * self.y[d+i-1] + a[2] * self.y[d+i-2]
854930cef6SMatthias Ringwald
864930cef6SMatthias Ringwald        return self.y
874930cef6SMatthias Ringwald
884930cef6SMatthias Ringwald
894930cef6SMatthias Ringwaldclass Resampler_6k4:
904930cef6SMatthias Ringwald
914930cef6SMatthias Ringwald    def __init__(self, n, history = 0):
924930cef6SMatthias Ringwald
934930cef6SMatthias Ringwald        self.x = np.zeros(n + 5)
944930cef6SMatthias Ringwald        self.n = n // 2
954930cef6SMatthias Ringwald
964930cef6SMatthias Ringwald        self.y = np.zeros(self.n + history)
974930cef6SMatthias Ringwald
984930cef6SMatthias Ringwald    def resample(self, x):
994930cef6SMatthias Ringwald
1004930cef6SMatthias Ringwald        n = self.n
1014930cef6SMatthias Ringwald
1024930cef6SMatthias Ringwald        ### Sliding window
1034930cef6SMatthias Ringwald
1044930cef6SMatthias Ringwald        self.x[:3] = self.x[-5:-2]
1054930cef6SMatthias Ringwald        self.x[3:] = x[:2*n+2]
1064930cef6SMatthias Ringwald        x = self.x
1074930cef6SMatthias Ringwald
1084930cef6SMatthias Ringwald        if len(self.y) > 2*n:
1094930cef6SMatthias Ringwald            self.y[n:-n] = self.y[2*n:]
1104930cef6SMatthias Ringwald        if len(self.y) > n:
1114930cef6SMatthias Ringwald            self.y[-n:] = self.y[:n]
1124930cef6SMatthias Ringwald
113*6897da5cSDirk Helbig        ### Downsampling to 6.4 KHz
1144930cef6SMatthias Ringwald
1154930cef6SMatthias Ringwald        h = [ 0.1236796411180537, 0.2353512128364889, 0.2819382920909148,
1164930cef6SMatthias Ringwald              0.2353512128364889, 0.1236796411180537 ]
1174930cef6SMatthias Ringwald
1184930cef6SMatthias Ringwald        self.y[:n] = [ np.dot(x[2*i:2*i+5], h) for i in range(self.n) ]
1194930cef6SMatthias Ringwald        return self.y
1204930cef6SMatthias Ringwald
1214930cef6SMatthias Ringwald
1224930cef6SMatthias Ringwalddef initial_hp50_state():
1234930cef6SMatthias Ringwald    return { 's1': 0, 's2': 0 }
1244930cef6SMatthias Ringwald
1254930cef6SMatthias Ringwald### ------------------------------------------------------------------------ ###
1264930cef6SMatthias Ringwald
1274930cef6SMatthias Ringwaldclass Ltpf:
1284930cef6SMatthias Ringwald
1294930cef6SMatthias Ringwald    def __init__(self, dt, sr):
1304930cef6SMatthias Ringwald
1314930cef6SMatthias Ringwald        self.dt = dt
1324930cef6SMatthias Ringwald        self.sr = sr
1334930cef6SMatthias Ringwald
1344930cef6SMatthias Ringwald        (self.pitch_present, self.pitch_index) = (None, None)
1354930cef6SMatthias Ringwald
1364930cef6SMatthias Ringwald
1374930cef6SMatthias Ringwaldclass LtpfAnalysis(Ltpf):
1384930cef6SMatthias Ringwald
1394930cef6SMatthias Ringwald    def __init__(self, dt, sr):
1404930cef6SMatthias Ringwald
1414930cef6SMatthias Ringwald        super().__init__(dt, sr)
1424930cef6SMatthias Ringwald
143*6897da5cSDirk Helbig        self.resampler_12k8 = Resampler_12k8(dt, sr,
144*6897da5cSDirk Helbig            history = 232 + (32 if dt == T.DT_2M5 else 0))
1454930cef6SMatthias Ringwald
146*6897da5cSDirk Helbig        self.resampler_6k4 = Resampler_6k4(self.resampler_12k8.n,
147*6897da5cSDirk Helbig            history = 114 + (16 if dt == T.DT_2M5 else 0))
1484930cef6SMatthias Ringwald
1494930cef6SMatthias Ringwald        self.active = False
1504930cef6SMatthias Ringwald        self.tc = 0
1514930cef6SMatthias Ringwald        self.pitch = 0
1524930cef6SMatthias Ringwald        self.nc = np.zeros(2)
1534930cef6SMatthias Ringwald
1544c4eb519SMatthias Ringwald    def get_data(self):
1554c4eb519SMatthias Ringwald
1564c4eb519SMatthias Ringwald        return { 'active' : self.active,
1574c4eb519SMatthias Ringwald                 'pitch_index' : self.pitch_index }
1584c4eb519SMatthias Ringwald
1594c4eb519SMatthias Ringwald    def get_nbits(self):
1604c4eb519SMatthias Ringwald
1614c4eb519SMatthias Ringwald        return 1 + 10 * int(self.pitch_present)
1624c4eb519SMatthias Ringwald
163*6897da5cSDirk Helbig    def correlate(self, x, i0, n, k0, k1):
1644930cef6SMatthias Ringwald
165*6897da5cSDirk Helbig        return np.array([ np.dot(
166*6897da5cSDirk Helbig            np.take(x, np.arange(i0, n)),
167*6897da5cSDirk Helbig            np.take(x, np.arange(i0, n) - k)) for k in range(k0, 1+k1) ])
1684930cef6SMatthias Ringwald
169*6897da5cSDirk Helbig    def norm_corr(self, x, i0, n, k):
1704930cef6SMatthias Ringwald
171*6897da5cSDirk Helbig        u  = np.take(x, np.arange(i0, n))
172*6897da5cSDirk Helbig        v  = np.take(x, np.arange(i0, n) - k)
1734930cef6SMatthias Ringwald        uv = np.dot(u, v)
1744930cef6SMatthias Ringwald        return uv / np.sqrt(np.dot(u, u) * np.dot(v, v)) if uv > 0 else 0
1754930cef6SMatthias Ringwald
1764930cef6SMatthias Ringwald    def run(self, x):
1774930cef6SMatthias Ringwald
178*6897da5cSDirk Helbig        ### Resampling
1794930cef6SMatthias Ringwald
1804930cef6SMatthias Ringwald        x_12k8 = self.resampler_12k8.resample(x)
1814930cef6SMatthias Ringwald
182*6897da5cSDirk Helbig        ### Pitch detection algorithm
1834930cef6SMatthias Ringwald
1844930cef6SMatthias Ringwald        x  = self.resampler_6k4.resample(x_12k8)
185*6897da5cSDirk Helbig        i0 = [-16, 0][self.dt > T.DT_2M5]
1864930cef6SMatthias Ringwald        n  = self.resampler_6k4.n
1874930cef6SMatthias Ringwald
188*6897da5cSDirk Helbig        r  = self.correlate(x, i0, n, 17, 114)
1894930cef6SMatthias Ringwald        rw = r * (1 - 0.5 * np.arange(len(r)) / (len(r) - 1))
1904930cef6SMatthias Ringwald
1914930cef6SMatthias Ringwald        tc = self.tc
1924930cef6SMatthias Ringwald        k0 = max(0, tc-4)
1934930cef6SMatthias Ringwald        k1 = min(len(r)-1, tc+4)
1944930cef6SMatthias Ringwald        t  = [ 17 + np.argmax(rw), 17 + k0 + np.argmax(r[k0:1+k1]) ]
1954930cef6SMatthias Ringwald
196*6897da5cSDirk Helbig        nc = [ self.norm_corr(x, i0, n, t[i]) for i in range(2) ]
1974930cef6SMatthias Ringwald        ti = int(nc[1] > 0.85 * nc[0])
1984930cef6SMatthias Ringwald        self.tc = t[ti] - 17
1994930cef6SMatthias Ringwald
2004930cef6SMatthias Ringwald        self.pitch_present = bool(nc[ti] > 0.6)
2014930cef6SMatthias Ringwald
202*6897da5cSDirk Helbig        ### Pitch-lag parameter
2034930cef6SMatthias Ringwald
2044930cef6SMatthias Ringwald        if self.pitch_present:
2054930cef6SMatthias Ringwald            tc = self.tc + 17
2064930cef6SMatthias Ringwald
2074930cef6SMatthias Ringwald            x  = x_12k8
208*6897da5cSDirk Helbig            i0 = [-32, 0][self.dt > T.DT_2M5]
2094930cef6SMatthias Ringwald            n  = self.resampler_12k8.n
2104930cef6SMatthias Ringwald
2114930cef6SMatthias Ringwald            k0 = max( 32, 2*tc-4)
2124930cef6SMatthias Ringwald            k1 = min(228, 2*tc+4)
213*6897da5cSDirk Helbig            r  = self.correlate(x, i0, n, k0-4, k1+4)
2144930cef6SMatthias Ringwald            e  = k0 + np.argmax(r[4:-4])
2154930cef6SMatthias Ringwald
2164930cef6SMatthias Ringwald            h = np.zeros(42)
2174930cef6SMatthias Ringwald            h[-15:] = T.LTPF_H4[:15]
2184930cef6SMatthias Ringwald            h[ :16] = T.LTPF_H4[15:]
2194930cef6SMatthias Ringwald
2204930cef6SMatthias Ringwald            m = np.arange(-4, 5)
2214930cef6SMatthias Ringwald            s = [ np.dot( np.take(r, e-k0+4 + m), np.take(h, 4*m-d) ) \
2224930cef6SMatthias Ringwald                      for d in range(-3, 4) ]
2234930cef6SMatthias Ringwald
2244930cef6SMatthias Ringwald            f = np.argmax(s[3:])            if e <=  32 else \
2254930cef6SMatthias Ringwald                -3 + np.argmax(s)           if e <  127 else \
2264930cef6SMatthias Ringwald                -2 + 2*np.argmax(s[1:-1:2]) if e <  157 else 0
2274930cef6SMatthias Ringwald
2284930cef6SMatthias Ringwald            e -=   (f < 0)
2294930cef6SMatthias Ringwald            f += 4*(f < 0)
2304930cef6SMatthias Ringwald
2314930cef6SMatthias Ringwald            self.pitch_index = 4*e + f    - 128 if e < 127 else \
2324930cef6SMatthias Ringwald                               2*e + f//2 + 126 if e < 157 else e + 283
2334930cef6SMatthias Ringwald
2344930cef6SMatthias Ringwald        else:
2354930cef6SMatthias Ringwald            e = f = 0
2364930cef6SMatthias Ringwald            self.pitch_index = 0
2374930cef6SMatthias Ringwald
238*6897da5cSDirk Helbig        ### Activation bit
2394930cef6SMatthias Ringwald
2404930cef6SMatthias Ringwald        h = np.zeros(24)
2414930cef6SMatthias Ringwald        h[-7:] = T.LTPF_HI[:7]
2424930cef6SMatthias Ringwald        h[ :8] = T.LTPF_HI[7:]
2434930cef6SMatthias Ringwald
244*6897da5cSDirk Helbig        x  = x_12k8
245*6897da5cSDirk Helbig        i0 = [-32, 0][self.dt > T.DT_2M5]
246*6897da5cSDirk Helbig        n  = self.resampler_12k8.n
247*6897da5cSDirk Helbig
2484930cef6SMatthias Ringwald        k = np.arange(-2, 3)
2494930cef6SMatthias Ringwald        u = [ np.dot( np.take(x, i-k), np.take(h, 4*k) ) \
250*6897da5cSDirk Helbig                  for i in range(i0, n) ]
2514930cef6SMatthias Ringwald        v = [ np.dot( np.take(x, i-k), np.take(h, 4*k-f) ) \
252*6897da5cSDirk Helbig                  for i in range(i0-e, n-e) ]
2534930cef6SMatthias Ringwald
2544930cef6SMatthias Ringwald        nc = max(0, np.dot(u, v)) / np.sqrt(np.dot(u, u) * np.dot(v, v)) \
2554930cef6SMatthias Ringwald                if self.pitch_present else 0
2564930cef6SMatthias Ringwald
2574930cef6SMatthias Ringwald        pitch = e + f/4
2584930cef6SMatthias Ringwald
2594930cef6SMatthias Ringwald        if not self.active:
2604930cef6SMatthias Ringwald            active = (self.dt == T.DT_10M or self.nc[1] > 0.94) \
2614930cef6SMatthias Ringwald                     and self.nc[0] > 0.94 and nc > 0.94
2624930cef6SMatthias Ringwald
2634930cef6SMatthias Ringwald        else:
2644930cef6SMatthias Ringwald            dp = abs(pitch - self.pitch)
2654930cef6SMatthias Ringwald            dc = nc - self.nc[0]
2664930cef6SMatthias Ringwald            active = nc > 0.9 or (dp < 2 and dc > -0.1 and nc > 0.84)
2674930cef6SMatthias Ringwald
2684930cef6SMatthias Ringwald        if not self.pitch_present:
2694930cef6SMatthias Ringwald            active = False
2704930cef6SMatthias Ringwald            pitch = 0
2714930cef6SMatthias Ringwald            nc = 0
2724930cef6SMatthias Ringwald
2734930cef6SMatthias Ringwald        self.active = active
2744930cef6SMatthias Ringwald        self.pitch = pitch
2754930cef6SMatthias Ringwald        self.nc[1] = self.nc[0]
2764930cef6SMatthias Ringwald        self.nc[0] = nc
2774930cef6SMatthias Ringwald
2784930cef6SMatthias Ringwald        return self.pitch_present
2794930cef6SMatthias Ringwald
2804930cef6SMatthias Ringwald    def disable(self):
2814930cef6SMatthias Ringwald
2824930cef6SMatthias Ringwald        self.active = False
2834930cef6SMatthias Ringwald
2844930cef6SMatthias Ringwald    def store(self, b):
2854930cef6SMatthias Ringwald
2864930cef6SMatthias Ringwald        b.write_uint(self.active, 1)
2874930cef6SMatthias Ringwald        b.write_uint(self.pitch_index, 9)
2884930cef6SMatthias Ringwald
2894930cef6SMatthias Ringwald
2904930cef6SMatthias Ringwaldclass LtpfSynthesis(Ltpf):
2914930cef6SMatthias Ringwald
2924930cef6SMatthias Ringwald    C_N = [ T.LTPF_N_8K , T.LTPF_N_16K,
2934930cef6SMatthias Ringwald            T.LTPF_N_24K, T.LTPF_N_32K, T.LTPF_N_48K ]
2944930cef6SMatthias Ringwald
2954930cef6SMatthias Ringwald    C_D = [ T.LTPF_D_8K , T.LTPF_D_16K,
2964930cef6SMatthias Ringwald            T.LTPF_D_24K, T.LTPF_D_32K, T.LTPF_D_48K ]
2974930cef6SMatthias Ringwald
2984930cef6SMatthias Ringwald    def __init__(self, dt, sr):
2994930cef6SMatthias Ringwald
3004930cef6SMatthias Ringwald        super().__init__(dt, sr)
3014930cef6SMatthias Ringwald
3024930cef6SMatthias Ringwald        self.C_N = LtpfSynthesis.C_N[sr]
3034930cef6SMatthias Ringwald        self.C_D = LtpfSynthesis.C_D[sr]
3044930cef6SMatthias Ringwald
3054930cef6SMatthias Ringwald        ns = T.NS[dt][sr]
3064930cef6SMatthias Ringwald
3074930cef6SMatthias Ringwald        self.active = [ False, False ]
3084930cef6SMatthias Ringwald        self.pitch_index = 0
3094930cef6SMatthias Ringwald
3104930cef6SMatthias Ringwald        max_pitch_12k8 = 228
3114930cef6SMatthias Ringwald        max_pitch = max_pitch_12k8 * T.SRATE_KHZ[self.sr] / 12.8
3124930cef6SMatthias Ringwald        max_pitch = np.ceil(max_pitch).astype(int)
3134930cef6SMatthias Ringwald
3144930cef6SMatthias Ringwald        self.x = np.zeros(ns)
3154930cef6SMatthias Ringwald        self.y = np.zeros(max_pitch + len(self.C_D[0]))
3164930cef6SMatthias Ringwald
3174930cef6SMatthias Ringwald        self.p_e = [ 0, 0 ]
3184930cef6SMatthias Ringwald        self.p_f = [ 0, 0 ]
3194930cef6SMatthias Ringwald        self.c_n = [ None, None ]
3204930cef6SMatthias Ringwald        self.c_d = [ None, None ]
3214930cef6SMatthias Ringwald
3224930cef6SMatthias Ringwald    def load(self, b):
3234930cef6SMatthias Ringwald
3244930cef6SMatthias Ringwald        self.active[0] = bool(b.read_uint(1))
3254930cef6SMatthias Ringwald        self.pitch_index = b.read_uint(9)
3264930cef6SMatthias Ringwald
3274930cef6SMatthias Ringwald    def disable(self):
3284930cef6SMatthias Ringwald
3294930cef6SMatthias Ringwald        self.active[0] = False
3304930cef6SMatthias Ringwald        self.pitch_index = 0
3314930cef6SMatthias Ringwald
3324930cef6SMatthias Ringwald    def run(self, x, nbytes):
3334930cef6SMatthias Ringwald
3344930cef6SMatthias Ringwald        sr = self.sr
3354930cef6SMatthias Ringwald        dt = self.dt
3364930cef6SMatthias Ringwald
337*6897da5cSDirk Helbig        ### Filter parameters
3384930cef6SMatthias Ringwald
3394930cef6SMatthias Ringwald        pitch_index = self.pitch_index
3404930cef6SMatthias Ringwald
3414930cef6SMatthias Ringwald        if pitch_index >= 440:
3424930cef6SMatthias Ringwald            p_e = pitch_index - 283
3434930cef6SMatthias Ringwald            p_f = 0
3444930cef6SMatthias Ringwald        elif pitch_index >= 380:
3454930cef6SMatthias Ringwald            p_e = pitch_index // 2 - 63
3464930cef6SMatthias Ringwald            p_f = 2*(pitch_index - 2*(p_e + 63))
3474930cef6SMatthias Ringwald        else:
3484930cef6SMatthias Ringwald            p_e = pitch_index // 4 + 32
3494930cef6SMatthias Ringwald            p_f = pitch_index - 4*(p_e - 32)
3504930cef6SMatthias Ringwald
3514930cef6SMatthias Ringwald        p = (p_e + p_f / 4) * T.SRATE_KHZ[self.sr] / 12.8
3524930cef6SMatthias Ringwald
3534930cef6SMatthias Ringwald        self.p_e[0] = int(p * 4 + 0.5) // 4
3544930cef6SMatthias Ringwald        self.p_f[0] = int(p * 4 + 0.5) - 4*self.p_e[0]
3554930cef6SMatthias Ringwald
356*6897da5cSDirk Helbig        nbits = round(nbytes*8 * 10 / T.DT_MS[dt])
357*6897da5cSDirk Helbig        if dt == T.DT_2M5:
358*6897da5cSDirk Helbig            nbits = int(nbits * (1 - 0.4))
359*6897da5cSDirk Helbig        elif dt == T.DT_5M:
360*6897da5cSDirk Helbig            nbits = nbits - 160
361*6897da5cSDirk Helbig
3624930cef6SMatthias Ringwald        g_idx = max(nbits // 80, 3+sr) - (3+sr)
3634930cef6SMatthias Ringwald
3644930cef6SMatthias Ringwald        g = [ 0.4, 0.35, 0.3, 0.25 ][g_idx] if g_idx < 4 else 0
3654930cef6SMatthias Ringwald        g_idx = min(g_idx, 3)
3664930cef6SMatthias Ringwald
3674930cef6SMatthias Ringwald        self.c_n[0] = 0.85 * g * LtpfSynthesis.C_N[sr][g_idx]
3684930cef6SMatthias Ringwald        self.c_d[0] = g * LtpfSynthesis.C_D[sr][self.p_f[0]]
3694930cef6SMatthias Ringwald
370*6897da5cSDirk Helbig        ### Transition handling
3714930cef6SMatthias Ringwald
3724930cef6SMatthias Ringwald        n0 = (T.SRATE_KHZ[sr] * 1000) // 400
3734930cef6SMatthias Ringwald        ns = T.NS[dt][sr]
3744930cef6SMatthias Ringwald
3754930cef6SMatthias Ringwald        x  = np.append(x, self.x)
3764930cef6SMatthias Ringwald        y  = np.append(np.zeros(ns), self.y)
3774930cef6SMatthias Ringwald        yc = y.copy()
3784930cef6SMatthias Ringwald
3794930cef6SMatthias Ringwald        c_n = self.c_n
3804930cef6SMatthias Ringwald        c_d = self.c_d
3814930cef6SMatthias Ringwald
3824930cef6SMatthias Ringwald        l_n = len(c_n[0])
3834930cef6SMatthias Ringwald        l_d = len(c_d[0])
3844930cef6SMatthias Ringwald
3854930cef6SMatthias Ringwald        d = [ self.p_e[0] - (l_d - 1) // 2,
3864930cef6SMatthias Ringwald              self.p_e[1] - (l_d - 1) // 2 ]
3874930cef6SMatthias Ringwald
3884930cef6SMatthias Ringwald        for k in range(n0):
3894930cef6SMatthias Ringwald
3904930cef6SMatthias Ringwald            if not self.active[0] and not self.active[1]:
3914930cef6SMatthias Ringwald                y[k] = x[k]
3924930cef6SMatthias Ringwald
3934930cef6SMatthias Ringwald            elif self.active[0] and not self.active[1]:
3944930cef6SMatthias Ringwald                u = np.dot(c_n[0], np.take(x, k - np.arange(l_n))) - \
3954930cef6SMatthias Ringwald                    np.dot(c_d[0], np.take(y, k - d[0] - np.arange(l_d)))
3964930cef6SMatthias Ringwald                y[k] = x[k] - (k/n0) * u
3974930cef6SMatthias Ringwald
3984930cef6SMatthias Ringwald            elif not self.active[0] and self.active[1]:
3994930cef6SMatthias Ringwald                u = np.dot(c_n[1], np.take(x, k - np.arange(l_n))) - \
4004930cef6SMatthias Ringwald                    np.dot(c_d[1], np.take(y, k - d[1] - np.arange(l_d)))
4014930cef6SMatthias Ringwald                y[k] = x[k] - (1 - k/n0) * u
4024930cef6SMatthias Ringwald
4034930cef6SMatthias Ringwald            elif self.p_e[0] == self.p_e[1] and self.p_f[0] == self.p_f[1]:
4044930cef6SMatthias Ringwald                u = np.dot(c_n[0], np.take(x, k - np.arange(l_n))) - \
4054930cef6SMatthias Ringwald                    np.dot(c_d[0], np.take(y, k - d[0] - np.arange(l_d)))
4064930cef6SMatthias Ringwald                y[k] = x[k] - u
4074930cef6SMatthias Ringwald
4084930cef6SMatthias Ringwald            else:
4094930cef6SMatthias Ringwald                u = np.dot(c_n[1], np.take(x, k - np.arange(l_n))) - \
4104930cef6SMatthias Ringwald                    np.dot(c_d[1], np.take(y, k - d[1] - np.arange(l_d)))
4114930cef6SMatthias Ringwald                yc[k] = x[k] - (1 - k/n0) * u
4124930cef6SMatthias Ringwald
4134930cef6SMatthias Ringwald                u = np.dot(c_n[0], np.take(yc, k - np.arange(l_n))) - \
4144930cef6SMatthias Ringwald                    np.dot(c_d[0], np.take(y , k - d[0] - np.arange(l_d)))
4154930cef6SMatthias Ringwald                y[k] = yc[k] - (k/n0) * u
4164930cef6SMatthias Ringwald
417*6897da5cSDirk Helbig        ### Remainder of the frame
4184930cef6SMatthias Ringwald
4194930cef6SMatthias Ringwald        for k in range(n0, ns):
4204930cef6SMatthias Ringwald
4214930cef6SMatthias Ringwald            if not self.active[0]:
4224930cef6SMatthias Ringwald                y[k] = x[k]
4234930cef6SMatthias Ringwald
4244930cef6SMatthias Ringwald            else:
4254930cef6SMatthias Ringwald                u = np.dot(c_n[0], np.take(x, k - np.arange(l_n))) - \
4264930cef6SMatthias Ringwald                    np.dot(c_d[0], np.take(y, k - d[0] - np.arange(l_d)))
4274930cef6SMatthias Ringwald                y[k] = x[k] - u
4284930cef6SMatthias Ringwald
4294930cef6SMatthias Ringwald        ### Sliding window
4304930cef6SMatthias Ringwald
4314930cef6SMatthias Ringwald        self.active[1] = self.active[0]
4324930cef6SMatthias Ringwald        self.p_e[1] = self.p_e[0]
4334930cef6SMatthias Ringwald        self.p_f[1] = self.p_f[0]
4344930cef6SMatthias Ringwald        self.c_n[1] = self.c_n[0]
4354930cef6SMatthias Ringwald        self.c_d[1] = self.c_d[0]
4364930cef6SMatthias Ringwald
4374930cef6SMatthias Ringwald        self.x = x[:ns]
4384930cef6SMatthias Ringwald        self.y = np.append(self.y[ns:], y[:ns])
4394930cef6SMatthias Ringwald
4404930cef6SMatthias Ringwald        return y[:ns]
4414930cef6SMatthias Ringwald
4424930cef6SMatthias Ringwalddef initial_state():
4434930cef6SMatthias Ringwald    return { 'active' : False, 'pitch': 0, 'nc':  np.zeros(2),
4444930cef6SMatthias Ringwald             'hp50' : initial_hp50_state(),
4454930cef6SMatthias Ringwald             'x_12k8' : np.zeros(384), 'x_6k4' : np.zeros(178), 'tc' : 0 }
4464930cef6SMatthias Ringwald
4474930cef6SMatthias Ringwalddef initial_sstate():
4484930cef6SMatthias Ringwald    return { 'active': False, 'pitch': 0,
4494930cef6SMatthias Ringwald             'c': np.zeros(2*12), 'x': np.zeros(12) }
4504930cef6SMatthias Ringwald
4514930cef6SMatthias Ringwald### ------------------------------------------------------------------------ ###
4524930cef6SMatthias Ringwald
4534930cef6SMatthias Ringwalddef check_resampler(rng, dt, sr):
4544930cef6SMatthias Ringwald
4554930cef6SMatthias Ringwald    ns = T.NS[dt][sr]
4564930cef6SMatthias Ringwald    nt = (5 * T.SRATE_KHZ[sr]) // 4
4574930cef6SMatthias Ringwald    ok = True
4584930cef6SMatthias Ringwald
4594930cef6SMatthias Ringwald    r = Resampler_12k8(dt, sr)
4604930cef6SMatthias Ringwald
4614930cef6SMatthias Ringwald    hp50_c = initial_hp50_state()
4624930cef6SMatthias Ringwald    x_c = np.zeros(nt)
4634930cef6SMatthias Ringwald    y_c = np.zeros(384)
4644930cef6SMatthias Ringwald
4654930cef6SMatthias Ringwald    for run in range(10):
4664930cef6SMatthias Ringwald
4674930cef6SMatthias Ringwald        x = ((2 * rng.random(ns)) - 1) * (2 ** 15 - 1)
4684930cef6SMatthias Ringwald        y = r.resample(x)
4694930cef6SMatthias Ringwald
4704930cef6SMatthias Ringwald        x_c = np.append(x_c[-nt:], x.astype(np.int16))
4714930cef6SMatthias Ringwald        y_c[:-r.n] = y_c[r.n:]
4724930cef6SMatthias Ringwald        y_c = lc3.ltpf_resample(dt, sr, hp50_c, x_c, y_c)
4734930cef6SMatthias Ringwald
4744930cef6SMatthias Ringwald        ok = ok and np.amax(np.abs(y_c[-r.d-r.n:] - y[:r.d+r.n]/2)) < 4
4754930cef6SMatthias Ringwald
4764930cef6SMatthias Ringwald    return ok
4774930cef6SMatthias Ringwald
4784930cef6SMatthias Ringwalddef check_resampler_appendix_c(dt):
4794930cef6SMatthias Ringwald
480*6897da5cSDirk Helbig    i0 = dt - T.DT_7M5
4814930cef6SMatthias Ringwald    sr = T.SRATE_16K
482*6897da5cSDirk Helbig
4834930cef6SMatthias Ringwald    ok = True
4844930cef6SMatthias Ringwald
4854930cef6SMatthias Ringwald    nt = (5 * T.SRATE_KHZ[sr]) // 4
486*6897da5cSDirk Helbig    n  = [ 96, 128 ][i0]
487*6897da5cSDirk Helbig    k  = [ 44,  24 ][i0] + n
4884930cef6SMatthias Ringwald
4894930cef6SMatthias Ringwald    state = initial_hp50_state()
4904930cef6SMatthias Ringwald
491*6897da5cSDirk Helbig    x = np.append(np.zeros(nt), C.X_PCM[i0][0])
4924930cef6SMatthias Ringwald    y = np.zeros(384)
4934930cef6SMatthias Ringwald    y = lc3.ltpf_resample(dt, sr, state, x, y)
494*6897da5cSDirk Helbig    u = y[-k:len(C.X_TILDE_12K8D[i0][0])-k]
4954930cef6SMatthias Ringwald
496*6897da5cSDirk Helbig    ok = ok and np.amax(np.abs(u - C.X_TILDE_12K8D[i0][0]/2)) < 2
4974930cef6SMatthias Ringwald
498*6897da5cSDirk Helbig    x = np.append(x[-nt:], C.X_PCM[i0][1])
4994930cef6SMatthias Ringwald    y[:-n] = y[n:]
5004930cef6SMatthias Ringwald    y = lc3.ltpf_resample(dt, sr, state, x, y)
501*6897da5cSDirk Helbig    u = y[-k:len(C.X_TILDE_12K8D[i0][1])-k]
5024930cef6SMatthias Ringwald
503*6897da5cSDirk Helbig    ok = ok and np.amax(np.abs(u - C.X_TILDE_12K8D[i0][1]/2)) < 2
5044930cef6SMatthias Ringwald
5054930cef6SMatthias Ringwald    return ok
5064930cef6SMatthias Ringwald
5074930cef6SMatthias Ringwalddef check_analysis(rng, dt, sr):
5084930cef6SMatthias Ringwald
5094930cef6SMatthias Ringwald    ns = T.NS[dt][sr]
5104930cef6SMatthias Ringwald    nt = (5 * T.SRATE_KHZ[sr]) // 4
5114930cef6SMatthias Ringwald    ok = True
5124930cef6SMatthias Ringwald
5134930cef6SMatthias Ringwald    state_c = initial_state()
5144930cef6SMatthias Ringwald    x_c = np.zeros(ns+nt)
5154930cef6SMatthias Ringwald
5164930cef6SMatthias Ringwald    ltpf = LtpfAnalysis(dt, sr)
5174930cef6SMatthias Ringwald
5184930cef6SMatthias Ringwald    t = np.arange(100 * ns) / (T.SRATE_KHZ[sr] * 1000)
519*6897da5cSDirk Helbig    s = signal.chirp(t, f0=10, f1=2500, t1=t[-1], method='logarithmic')
5204930cef6SMatthias Ringwald
5214930cef6SMatthias Ringwald    for i in range(20):
5224930cef6SMatthias Ringwald
5234930cef6SMatthias Ringwald        x = s[i*ns:(i+1)*ns] * (2 ** 15 - 1)
5244930cef6SMatthias Ringwald
5254930cef6SMatthias Ringwald        pitch_present = ltpf.run(x)
5264930cef6SMatthias Ringwald        data = ltpf.get_data()
5274930cef6SMatthias Ringwald
5284930cef6SMatthias Ringwald        x_c = np.append(x_c[-nt:], x.astype(np.int16))
5294930cef6SMatthias Ringwald        (pitch_present_c, data_c) = lc3.ltpf_analyse(dt, sr, state_c, x_c)
5304930cef6SMatthias Ringwald
5314930cef6SMatthias Ringwald        ok = ok and (not pitch_present or state_c['tc'] == ltpf.tc)
532*6897da5cSDirk Helbig        ok = ok and np.amax(np.abs(state_c['nc'][0] - ltpf.nc[0])) < 1e-1
5334930cef6SMatthias Ringwald        ok = ok and pitch_present_c == pitch_present
5344930cef6SMatthias Ringwald        ok = ok and data_c['active'] == data['active']
5354930cef6SMatthias Ringwald        ok = ok and data_c['pitch_index'] == data['pitch_index']
5364930cef6SMatthias Ringwald        ok = ok and lc3.ltpf_get_nbits(pitch_present) == ltpf.get_nbits()
5374930cef6SMatthias Ringwald
5384930cef6SMatthias Ringwald    return ok
5394930cef6SMatthias Ringwald
5404930cef6SMatthias Ringwalddef check_synthesis(rng, dt, sr):
5414930cef6SMatthias Ringwald
5424930cef6SMatthias Ringwald    ok = True
5434930cef6SMatthias Ringwald
5444930cef6SMatthias Ringwald    ns = T.NS[dt][sr]
5454930cef6SMatthias Ringwald    nd = 18 * T.SRATE_KHZ[sr]
5464930cef6SMatthias Ringwald
5474930cef6SMatthias Ringwald    synthesis = LtpfSynthesis(dt, sr)
5484930cef6SMatthias Ringwald
5494930cef6SMatthias Ringwald    state_c = initial_sstate()
5504930cef6SMatthias Ringwald    x_c = np.zeros(nd+ns)
5514930cef6SMatthias Ringwald
5524930cef6SMatthias Ringwald    for i in range(50):
553*6897da5cSDirk Helbig
5544930cef6SMatthias Ringwald        pitch_present = bool(rng.integers(0, 10) >= 1)
5554930cef6SMatthias Ringwald        if not pitch_present:
5564930cef6SMatthias Ringwald            synthesis.disable()
5574930cef6SMatthias Ringwald        else:
5584930cef6SMatthias Ringwald            synthesis.active[0] = bool(rng.integers(0, 5) >= 1)
5594930cef6SMatthias Ringwald            synthesis.pitch_index = rng.integers(0, 512)
5604930cef6SMatthias Ringwald
5614930cef6SMatthias Ringwald        data_c = None if not pitch_present else \
5624930cef6SMatthias Ringwald            { 'active' : synthesis.active[0],
5634930cef6SMatthias Ringwald              'pitch_index' : synthesis.pitch_index }
5644930cef6SMatthias Ringwald
5654930cef6SMatthias Ringwald        x = rng.random(ns) * 1e4
5664930cef6SMatthias Ringwald        nbytes = rng.integers(10*(2+sr), 10*(6+sr))
5674930cef6SMatthias Ringwald
5684930cef6SMatthias Ringwald        x_c[:nd] = x_c[ns:]
5694930cef6SMatthias Ringwald        x_c[nd:] = x
5704930cef6SMatthias Ringwald
5714930cef6SMatthias Ringwald        y = synthesis.run(x, nbytes)
5724930cef6SMatthias Ringwald        x_c = lc3.ltpf_synthesize(dt, sr, nbytes, state_c, data_c, x_c)
5734930cef6SMatthias Ringwald
5744930cef6SMatthias Ringwald        ok = ok and np.amax(np.abs(x_c[nd:] - y)) < 1e-2
5754930cef6SMatthias Ringwald
5764930cef6SMatthias Ringwald    return ok
5774930cef6SMatthias Ringwald
5784930cef6SMatthias Ringwalddef check_analysis_appendix_c(dt):
5794930cef6SMatthias Ringwald
580*6897da5cSDirk Helbig    i0 = dt - T.DT_7M5
5814930cef6SMatthias Ringwald    sr = T.SRATE_16K
582*6897da5cSDirk Helbig
5834930cef6SMatthias Ringwald    ok = True
5844930cef6SMatthias Ringwald
585*6897da5cSDirk Helbig    nt = (5 * T.SRATE_KHZ[sr]) // 4
586*6897da5cSDirk Helbig
5874930cef6SMatthias Ringwald    state = initial_state()
5884930cef6SMatthias Ringwald
589*6897da5cSDirk Helbig    x = np.append(np.zeros(nt), C.X_PCM[i0][0])
5904930cef6SMatthias Ringwald    (pitch_present, data) = lc3.ltpf_analyse(dt, sr, state, x)
5914930cef6SMatthias Ringwald
592*6897da5cSDirk Helbig    ok = ok and C.T_CURR[i0][0] - state['tc'] == 17
593*6897da5cSDirk Helbig    ok = ok and np.amax(np.abs(state['nc'][0] - C.NC_LTPF[i0][0])) < 1e-5
594*6897da5cSDirk Helbig    ok = ok and pitch_present == C.PITCH_PRESENT[i0][0]
595*6897da5cSDirk Helbig    ok = ok and data['pitch_index'] == C.PITCH_INDEX[i0][0]
596*6897da5cSDirk Helbig    ok = ok and data['active'] == C.LTPF_ACTIVE[i0][0]
5974930cef6SMatthias Ringwald
598*6897da5cSDirk Helbig    x = np.append(x[-nt:], C.X_PCM[i0][1])
5994930cef6SMatthias Ringwald    (pitch_present, data) = lc3.ltpf_analyse(dt, sr, state, x)
6004930cef6SMatthias Ringwald
601*6897da5cSDirk Helbig    ok = ok and C.T_CURR[i0][1] - state['tc'] == 17
602*6897da5cSDirk Helbig    ok = ok and np.amax(np.abs(state['nc'][0] - C.NC_LTPF[i0][1])) < 1e-5
603*6897da5cSDirk Helbig    ok = ok and pitch_present == C.PITCH_PRESENT[i0][1]
604*6897da5cSDirk Helbig    ok = ok and data['pitch_index'] == C.PITCH_INDEX[i0][1]
605*6897da5cSDirk Helbig    ok = ok and data['active'] == C.LTPF_ACTIVE[i0][1]
6064930cef6SMatthias Ringwald
6074930cef6SMatthias Ringwald    return ok
6084930cef6SMatthias Ringwald
6094930cef6SMatthias Ringwalddef check_synthesis_appendix_c(dt):
6104930cef6SMatthias Ringwald
6114930cef6SMatthias Ringwald    sr = T.SRATE_16K
6124930cef6SMatthias Ringwald
613*6897da5cSDirk Helbig    ok = True
6144930cef6SMatthias Ringwald    if dt != T.DT_10M:
6154930cef6SMatthias Ringwald        return ok
6164930cef6SMatthias Ringwald
6174930cef6SMatthias Ringwald    ns = T.NS[dt][sr]
6184930cef6SMatthias Ringwald    nd = 18 * T.SRATE_KHZ[sr]
6194930cef6SMatthias Ringwald
6204930cef6SMatthias Ringwald    NBYTES = [ C.LTPF_C2_NBITS // 8, C.LTPF_C3_NBITS // 8,
6214930cef6SMatthias Ringwald               C.LTPF_C4_NBITS // 8, C.LTPF_C5_NBITS // 8 ]
6224930cef6SMatthias Ringwald
6234930cef6SMatthias Ringwald    ACTIVE = [ C.LTPF_C2_ACTIVE, C.LTPF_C3_ACTIVE,
6244930cef6SMatthias Ringwald               C.LTPF_C4_ACTIVE, C.LTPF_C5_ACTIVE ]
6254930cef6SMatthias Ringwald
6264930cef6SMatthias Ringwald    PITCH_INDEX = [ C.LTPF_C2_PITCH_INDEX, C.LTPF_C3_PITCH_INDEX,
6274930cef6SMatthias Ringwald                    C.LTPF_C4_PITCH_INDEX, C.LTPF_C5_PITCH_INDEX ]
6284930cef6SMatthias Ringwald
6294930cef6SMatthias Ringwald    X = [ C.LTPF_C2_X, C.LTPF_C3_X,
6304930cef6SMatthias Ringwald          C.LTPF_C4_X, C.LTPF_C5_X ]
6314930cef6SMatthias Ringwald
6324930cef6SMatthias Ringwald    PREV = [ C.LTPF_C2_PREV, C.LTPF_C3_PREV,
6334930cef6SMatthias Ringwald             C.LTPF_C4_PREV, C.LTPF_C5_PREV  ]
6344930cef6SMatthias Ringwald
6354930cef6SMatthias Ringwald    TRANS = [ C.LTPF_C2_TRANS, C.LTPF_C3_TRANS,
6364930cef6SMatthias Ringwald              C.LTPF_C4_TRANS, C.LTPF_C5_TRANS ]
6374930cef6SMatthias Ringwald
6384930cef6SMatthias Ringwald    for i in range(4):
6394930cef6SMatthias Ringwald
6404930cef6SMatthias Ringwald        state = initial_sstate()
6414930cef6SMatthias Ringwald        nbytes = NBYTES[i]
6424930cef6SMatthias Ringwald
6434930cef6SMatthias Ringwald        data = { 'active' : ACTIVE[i][0], 'pitch_index' : PITCH_INDEX[i][0] }
6444930cef6SMatthias Ringwald        x = np.append(np.zeros(nd), X[i][0])
6454930cef6SMatthias Ringwald
6464930cef6SMatthias Ringwald        lc3.ltpf_synthesize(dt, sr, nbytes, state, data, x)
6474930cef6SMatthias Ringwald
6484930cef6SMatthias Ringwald        data = { 'active' : ACTIVE[i][1], 'pitch_index' : PITCH_INDEX[i][1] }
6494930cef6SMatthias Ringwald        x[  :nd-ns] = PREV[i][0][-nd+ns:]
6504930cef6SMatthias Ringwald        x[nd-ns:nd] = PREV[i][1]
6514930cef6SMatthias Ringwald        x[nd:nd+ns] = X[i][1]
6524930cef6SMatthias Ringwald
6534930cef6SMatthias Ringwald        y = lc3.ltpf_synthesize(dt, sr, nbytes, state, data, x)[nd:]
6544930cef6SMatthias Ringwald
6554930cef6SMatthias Ringwald        ok = ok and np.amax(np.abs(y - TRANS[i])) < 1e-3
6564930cef6SMatthias Ringwald
6574930cef6SMatthias Ringwald    return ok
6584930cef6SMatthias Ringwald
6594930cef6SMatthias Ringwalddef check():
6604930cef6SMatthias Ringwald
6614930cef6SMatthias Ringwald    rng = np.random.default_rng(1234)
6624930cef6SMatthias Ringwald    ok = True
6634930cef6SMatthias Ringwald
6644930cef6SMatthias Ringwald    for dt in range(T.NUM_DT):
665*6897da5cSDirk Helbig        for sr in range(T.SRATE_8K, T.SRATE_48K + 1):
6664930cef6SMatthias Ringwald            ok = ok and check_resampler(rng, dt, sr)
6674930cef6SMatthias Ringwald            ok = ok and check_analysis(rng, dt, sr)
6684930cef6SMatthias Ringwald            ok = ok and check_synthesis(rng, dt, sr)
6694930cef6SMatthias Ringwald
670*6897da5cSDirk Helbig    for dt in ( T.DT_7M5, T.DT_10M ):
6714930cef6SMatthias Ringwald        ok = ok and check_resampler_appendix_c(dt)
6724930cef6SMatthias Ringwald        ok = ok and check_analysis_appendix_c(dt)
6734930cef6SMatthias Ringwald        ok = ok and check_synthesis_appendix_c(dt)
6744930cef6SMatthias Ringwald
6754930cef6SMatthias Ringwald    return ok
6764930cef6SMatthias Ringwald
6774930cef6SMatthias Ringwald### ------------------------------------------------------------------------ ###
678