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