# # Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # import numpy as np import scipy.fft import lc3 import tables as T, appendix_c as C ### ------------------------------------------------------------------------ ### class Mdct: W = [ [ T.W_2M5_8K , T.W_2M5_16K, T.W_2M5_24K, T.W_2M5_32K, T.W_2M5_48K, T.W_2M5_48K_HR, T.W_2M5_96K_HR ], [ T.W_5M_8K , T.W_5M_16K , T.W_5M_24K , T.W_5M_32K , T.W_5M_48K , T.W_5M_48K_HR , T.W_5M_96K_HR ], [ T.W_7M5_8K , T.W_7M5_16K, T.W_7M5_24K, T.W_7M5_32K, T.W_7M5_48K, None, None ], [ T.W_10M_8K , T.W_10M_16K, T.W_10M_24K, T.W_10M_32K, T.W_10M_48K, T.W_10M_48K_HR, T.W_10M_96K_HR ] ] def __init__(self, dt, sr): self.ns = T.NS[dt][sr] self.nd = T.ND[dt][sr] self.t = np.zeros(2*self.ns) self.w = Mdct.W[dt][sr] class MdctForward(Mdct): def __init__(self, dt, sr): super().__init__(dt, sr) def run(self, x): ns = self.ns nd = self.nd self.t[nd:nd+ns] = x t = self.t * self.w self.t[0:nd] = x[ns-nd:] n = len(t) n2 = n // 2 z = t * np.exp(-2j * np.pi * np.arange(n) / (2*n)) z = scipy.fft.fft(z)[:n2] z = z * np.exp(-2j * np.pi * (n2/2 + 0.5) * (np.arange(n2) + 0.5) / (2 * n2)) return np.real(z) * np.sqrt(2/n2) class MdctInverse(Mdct): def __init__(self, dt, sr): super().__init__(dt, sr) def run(self, x): ns = self.ns nd = self.nd n = len(x) x = np.append(x, -x[::-1]) z = x * np.exp(2j * np.pi * (n/2 + 0.5) * np.arange(2*n) / (2*n)) z = scipy.fft.ifft(z) * n z = z * np.exp(2j * np.pi * (np.arange(2*n) + (n/2 + 0.5)) / (4*n)) t = np.real(z) * np.sqrt(2/n) t = t * self.w[::-1] y = np.empty(ns) y[:nd] = t[ns-nd:ns] + self.t[2*ns-nd:] y[nd:] = t[ns:2*ns-nd] self.t = t return y ### ------------------------------------------------------------------------ ### def check_forward_unit(rng, dt, sr): ns = T.NS[dt][sr] nd = T.ND[dt][sr] ok = True x = (2 * rng.random(ns)) - 1 y = [ None ] * 2 y_c = [ None ] * 2 mdct = MdctForward(dt, sr) y[0] = mdct.run(x) y[1] = mdct.run(x) (y_c[0], d_c) = lc3.mdct_forward(dt, sr, x, np.zeros(nd)) y_c[1] = lc3.mdct_forward(dt, sr, x, d_c)[0] ok = ok and np.amax(np.abs(y[0] - y_c[0])) < 1e-5 ok = ok and np.amax(np.abs(y[1] - y_c[1])) < 1e-5 return ok def check_forward_appendix_c(dt): i0 = dt - T.DT_7M5 sr = T.SRATE_16K ok = True ns = T.NS[dt][sr] nd = T.ND[dt][sr] (y, d) = lc3.mdct_forward(dt, sr, C.X_PCM[i0][0], np.zeros(nd)) ok = ok and np.amax(np.abs(y - C.X[i0][0])) < 1e-1 (y, d) = lc3.mdct_forward(dt, sr, C.X_PCM[i0][1], d) ok = ok and np.amax(np.abs(y - C.X[i0][1])) < 1e-1 return ok def check_inverse_unit(rng, dt, sr): ns = T.NS[dt][sr] nd = T.ND[dt][sr] ok = True x = (2 * rng.random(ns)) - 1 y = [ None ] * 2 y_c = [ None ] * 2 mdct = MdctInverse(dt, sr) y[0] = mdct.run(x) y[1] = mdct.run(x) (y_c[0], d_c) = lc3.mdct_inverse(dt, sr, x, np.zeros(nd)) y_c[1] = lc3.mdct_inverse(dt, sr, x, d_c)[0] ok = ok and np.amax(np.abs(y[0] - y_c[0])) < 1e-5 ok = ok and np.amax(np.abs(y[1] - y_c[1])) < 1e-5 return ok def check_inverse_appendix_c(dt): i0 = dt - T.DT_7M5 sr = T.SRATE_16K ok = True ns = T.NS[dt][sr] nd = T.ND[dt][sr] (y, d0) = lc3.mdct_inverse(dt, sr, C.X_HAT_SNS[i0][0], np.zeros(nd)) yr = C.T_HAT_MDCT[i0][0][ns-nd:2*ns-nd] dr = C.T_HAT_MDCT[i0][0][2*ns-nd:] ok = ok and np.amax(np.abs(yr - y )) < 1e-1 ok = ok and np.amax(np.abs(dr - d0)) < 1e-1 (y, d1) = lc3.mdct_inverse(dt, sr, C.X_HAT_SNS[i0][1], d0) yr[ :nd] = C.T_HAT_MDCT[i0][1][ns-nd:ns] + d0 yr[nd:ns] = C.T_HAT_MDCT[i0][1][ns:2*ns-nd] dr = C.T_HAT_MDCT[i0][1][2*ns-nd:] ok = ok and np.amax(np.abs(yr - y )) < 1e-1 ok = ok and np.amax(np.abs(dr - d1)) < 1e-1 return ok def check(): rng = np.random.default_rng(1234) ok = True for dt in range(T.NUM_DT): for sr in range(T.SRATE_8K, T.SRATE_48K + 1): ok = ok and check_forward_unit(rng, dt, sr) ok = ok and check_inverse_unit(rng, dt, sr) for dt in ( T.DT_2M5, T.DT_5M, T.DT_10M ): for sr in ( T.SRATE_48K_HR, T.SRATE_96K_HR ): ok = ok and check_forward_unit(rng, dt, sr) ok = ok and check_inverse_unit(rng, dt, sr) for dt in ( T.DT_7M5, T.DT_10M ): ok = ok and check_forward_appendix_c(dt) ok = ok and check_inverse_appendix_c(dt) return ok