xref: /aosp_15_r20/external/liblc3/test/mdct.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.fft
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 Mdct:
26*49fe348cSAndroid Build Coastguard Worker
27*49fe348cSAndroid Build Coastguard Worker    W = [ [ T.W_2M5_8K , T.W_2M5_16K, T.W_2M5_24K,
28*49fe348cSAndroid Build Coastguard Worker            T.W_2M5_32K, T.W_2M5_48K, T.W_2M5_48K_HR, T.W_2M5_96K_HR ],
29*49fe348cSAndroid Build Coastguard Worker
30*49fe348cSAndroid Build Coastguard Worker          [ T.W_5M_8K  , T.W_5M_16K , T.W_5M_24K ,
31*49fe348cSAndroid Build Coastguard Worker            T.W_5M_32K , T.W_5M_48K , T.W_5M_48K_HR , T.W_5M_96K_HR  ],
32*49fe348cSAndroid Build Coastguard Worker
33*49fe348cSAndroid Build Coastguard Worker          [ T.W_7M5_8K , T.W_7M5_16K, T.W_7M5_24K,
34*49fe348cSAndroid Build Coastguard Worker            T.W_7M5_32K, T.W_7M5_48K, None, None ],
35*49fe348cSAndroid Build Coastguard Worker
36*49fe348cSAndroid Build Coastguard Worker          [ T.W_10M_8K , T.W_10M_16K, T.W_10M_24K,
37*49fe348cSAndroid Build Coastguard Worker            T.W_10M_32K, T.W_10M_48K, T.W_10M_48K_HR, T.W_10M_96K_HR ] ]
38*49fe348cSAndroid Build Coastguard Worker
39*49fe348cSAndroid Build Coastguard Worker    def __init__(self, dt, sr):
40*49fe348cSAndroid Build Coastguard Worker
41*49fe348cSAndroid Build Coastguard Worker        self.ns = T.NS[dt][sr]
42*49fe348cSAndroid Build Coastguard Worker        self.nd = T.ND[dt][sr]
43*49fe348cSAndroid Build Coastguard Worker
44*49fe348cSAndroid Build Coastguard Worker        self.t = np.zeros(2*self.ns)
45*49fe348cSAndroid Build Coastguard Worker        self.w = Mdct.W[dt][sr]
46*49fe348cSAndroid Build Coastguard Worker
47*49fe348cSAndroid Build Coastguard Worker
48*49fe348cSAndroid Build Coastguard Workerclass MdctForward(Mdct):
49*49fe348cSAndroid Build Coastguard Worker
50*49fe348cSAndroid Build Coastguard Worker    def __init__(self, dt, sr):
51*49fe348cSAndroid Build Coastguard Worker
52*49fe348cSAndroid Build Coastguard Worker        super().__init__(dt, sr)
53*49fe348cSAndroid Build Coastguard Worker
54*49fe348cSAndroid Build Coastguard Worker    def run(self, x):
55*49fe348cSAndroid Build Coastguard Worker
56*49fe348cSAndroid Build Coastguard Worker        ns = self.ns
57*49fe348cSAndroid Build Coastguard Worker        nd = self.nd
58*49fe348cSAndroid Build Coastguard Worker
59*49fe348cSAndroid Build Coastguard Worker        self.t[nd:nd+ns] = x
60*49fe348cSAndroid Build Coastguard Worker        t = self.t * self.w
61*49fe348cSAndroid Build Coastguard Worker        self.t[0:nd] = x[ns-nd:]
62*49fe348cSAndroid Build Coastguard Worker
63*49fe348cSAndroid Build Coastguard Worker        n  = len(t)
64*49fe348cSAndroid Build Coastguard Worker        n2 = n // 2
65*49fe348cSAndroid Build Coastguard Worker
66*49fe348cSAndroid Build Coastguard Worker        z = t * np.exp(-2j * np.pi * np.arange(n) / (2*n))
67*49fe348cSAndroid Build Coastguard Worker        z = scipy.fft.fft(z)[:n2]
68*49fe348cSAndroid Build Coastguard Worker        z = z * np.exp(-2j * np.pi *
69*49fe348cSAndroid Build Coastguard Worker                (n2/2 + 0.5) * (np.arange(n2) + 0.5) / (2 * n2))
70*49fe348cSAndroid Build Coastguard Worker        return np.real(z) * np.sqrt(2/n2)
71*49fe348cSAndroid Build Coastguard Worker
72*49fe348cSAndroid Build Coastguard Worker
73*49fe348cSAndroid Build Coastguard Workerclass MdctInverse(Mdct):
74*49fe348cSAndroid Build Coastguard Worker
75*49fe348cSAndroid Build Coastguard Worker    def __init__(self, dt, sr):
76*49fe348cSAndroid Build Coastguard Worker
77*49fe348cSAndroid Build Coastguard Worker        super().__init__(dt, sr)
78*49fe348cSAndroid Build Coastguard Worker
79*49fe348cSAndroid Build Coastguard Worker    def run(self, x):
80*49fe348cSAndroid Build Coastguard Worker
81*49fe348cSAndroid Build Coastguard Worker        ns = self.ns
82*49fe348cSAndroid Build Coastguard Worker        nd = self.nd
83*49fe348cSAndroid Build Coastguard Worker
84*49fe348cSAndroid Build Coastguard Worker        n = len(x)
85*49fe348cSAndroid Build Coastguard Worker
86*49fe348cSAndroid Build Coastguard Worker        x = np.append(x, -x[::-1])
87*49fe348cSAndroid Build Coastguard Worker        z = x * np.exp(2j * np.pi * (n/2 + 0.5) * np.arange(2*n) / (2*n))
88*49fe348cSAndroid Build Coastguard Worker        z = scipy.fft.ifft(z) * n
89*49fe348cSAndroid Build Coastguard Worker        z = z * np.exp(2j * np.pi * (np.arange(2*n) + (n/2 + 0.5)) / (4*n))
90*49fe348cSAndroid Build Coastguard Worker        t = np.real(z) * np.sqrt(2/n)
91*49fe348cSAndroid Build Coastguard Worker
92*49fe348cSAndroid Build Coastguard Worker        t = t * self.w[::-1]
93*49fe348cSAndroid Build Coastguard Worker
94*49fe348cSAndroid Build Coastguard Worker        y = np.empty(ns)
95*49fe348cSAndroid Build Coastguard Worker        y[:nd] = t[ns-nd:ns] + self.t[2*ns-nd:]
96*49fe348cSAndroid Build Coastguard Worker        y[nd:] = t[ns:2*ns-nd]
97*49fe348cSAndroid Build Coastguard Worker        self.t = t
98*49fe348cSAndroid Build Coastguard Worker
99*49fe348cSAndroid Build Coastguard Worker        return y
100*49fe348cSAndroid Build Coastguard Worker
101*49fe348cSAndroid Build Coastguard Worker### ------------------------------------------------------------------------ ###
102*49fe348cSAndroid Build Coastguard Worker
103*49fe348cSAndroid Build Coastguard Workerdef check_forward_unit(rng, dt, sr):
104*49fe348cSAndroid Build Coastguard Worker
105*49fe348cSAndroid Build Coastguard Worker    ns = T.NS[dt][sr]
106*49fe348cSAndroid Build Coastguard Worker    nd = T.ND[dt][sr]
107*49fe348cSAndroid Build Coastguard Worker    ok = True
108*49fe348cSAndroid Build Coastguard Worker
109*49fe348cSAndroid Build Coastguard Worker    x = (2 * rng.random(ns)) - 1
110*49fe348cSAndroid Build Coastguard Worker
111*49fe348cSAndroid Build Coastguard Worker    y   = [ None ] * 2
112*49fe348cSAndroid Build Coastguard Worker    y_c = [ None ] * 2
113*49fe348cSAndroid Build Coastguard Worker
114*49fe348cSAndroid Build Coastguard Worker    mdct = MdctForward(dt, sr)
115*49fe348cSAndroid Build Coastguard Worker    y[0] = mdct.run(x)
116*49fe348cSAndroid Build Coastguard Worker    y[1] = mdct.run(x)
117*49fe348cSAndroid Build Coastguard Worker
118*49fe348cSAndroid Build Coastguard Worker    (y_c[0], d_c) = lc3.mdct_forward(dt, sr, x, np.zeros(nd))
119*49fe348cSAndroid Build Coastguard Worker    y_c[1] = lc3.mdct_forward(dt, sr, x, d_c)[0]
120*49fe348cSAndroid Build Coastguard Worker
121*49fe348cSAndroid Build Coastguard Worker    ok = ok and np.amax(np.abs(y[0] - y_c[0])) < 1e-5
122*49fe348cSAndroid Build Coastguard Worker    ok = ok and np.amax(np.abs(y[1] - y_c[1])) < 1e-5
123*49fe348cSAndroid Build Coastguard Worker
124*49fe348cSAndroid Build Coastguard Worker    return ok
125*49fe348cSAndroid Build Coastguard Worker
126*49fe348cSAndroid Build Coastguard Worker
127*49fe348cSAndroid Build Coastguard Workerdef check_forward_appendix_c(dt):
128*49fe348cSAndroid Build Coastguard Worker
129*49fe348cSAndroid Build Coastguard Worker    i0 = dt - T.DT_7M5
130*49fe348cSAndroid Build Coastguard Worker    sr = T.SRATE_16K
131*49fe348cSAndroid Build Coastguard Worker
132*49fe348cSAndroid Build Coastguard Worker    ok = True
133*49fe348cSAndroid Build Coastguard Worker
134*49fe348cSAndroid Build Coastguard Worker    ns = T.NS[dt][sr]
135*49fe348cSAndroid Build Coastguard Worker    nd = T.ND[dt][sr]
136*49fe348cSAndroid Build Coastguard Worker
137*49fe348cSAndroid Build Coastguard Worker    (y, d) = lc3.mdct_forward(dt, sr, C.X_PCM[i0][0], np.zeros(nd))
138*49fe348cSAndroid Build Coastguard Worker    ok = ok and np.amax(np.abs(y - C.X[i0][0])) < 1e-1
139*49fe348cSAndroid Build Coastguard Worker
140*49fe348cSAndroid Build Coastguard Worker    (y, d) = lc3.mdct_forward(dt, sr, C.X_PCM[i0][1], d)
141*49fe348cSAndroid Build Coastguard Worker    ok = ok and np.amax(np.abs(y - C.X[i0][1])) < 1e-1
142*49fe348cSAndroid Build Coastguard Worker
143*49fe348cSAndroid Build Coastguard Worker    return ok
144*49fe348cSAndroid Build Coastguard Worker
145*49fe348cSAndroid Build Coastguard Worker
146*49fe348cSAndroid Build Coastguard Workerdef check_inverse_unit(rng, dt, sr):
147*49fe348cSAndroid Build Coastguard Worker
148*49fe348cSAndroid Build Coastguard Worker    ns = T.NS[dt][sr]
149*49fe348cSAndroid Build Coastguard Worker    nd = T.ND[dt][sr]
150*49fe348cSAndroid Build Coastguard Worker    ok = True
151*49fe348cSAndroid Build Coastguard Worker
152*49fe348cSAndroid Build Coastguard Worker    x  = (2 * rng.random(ns)) - 1
153*49fe348cSAndroid Build Coastguard Worker
154*49fe348cSAndroid Build Coastguard Worker    y   = [ None ] * 2
155*49fe348cSAndroid Build Coastguard Worker    y_c = [ None ] * 2
156*49fe348cSAndroid Build Coastguard Worker
157*49fe348cSAndroid Build Coastguard Worker    mdct = MdctInverse(dt, sr)
158*49fe348cSAndroid Build Coastguard Worker    y[0] = mdct.run(x)
159*49fe348cSAndroid Build Coastguard Worker    y[1] = mdct.run(x)
160*49fe348cSAndroid Build Coastguard Worker
161*49fe348cSAndroid Build Coastguard Worker    (y_c[0], d_c) = lc3.mdct_inverse(dt, sr, x, np.zeros(nd))
162*49fe348cSAndroid Build Coastguard Worker    y_c[1] = lc3.mdct_inverse(dt, sr, x, d_c)[0]
163*49fe348cSAndroid Build Coastguard Worker
164*49fe348cSAndroid Build Coastguard Worker    ok = ok and np.amax(np.abs(y[0] - y_c[0])) < 1e-5
165*49fe348cSAndroid Build Coastguard Worker    ok = ok and np.amax(np.abs(y[1] - y_c[1])) < 1e-5
166*49fe348cSAndroid Build Coastguard Worker
167*49fe348cSAndroid Build Coastguard Worker    return ok
168*49fe348cSAndroid Build Coastguard Worker
169*49fe348cSAndroid Build Coastguard Worker
170*49fe348cSAndroid Build Coastguard Workerdef check_inverse_appendix_c(dt):
171*49fe348cSAndroid Build Coastguard Worker
172*49fe348cSAndroid Build Coastguard Worker    i0 = dt - T.DT_7M5
173*49fe348cSAndroid Build Coastguard Worker    sr = T.SRATE_16K
174*49fe348cSAndroid Build Coastguard Worker
175*49fe348cSAndroid Build Coastguard Worker    ok = True
176*49fe348cSAndroid Build Coastguard Worker
177*49fe348cSAndroid Build Coastguard Worker    ns = T.NS[dt][sr]
178*49fe348cSAndroid Build Coastguard Worker    nd = T.ND[dt][sr]
179*49fe348cSAndroid Build Coastguard Worker
180*49fe348cSAndroid Build Coastguard Worker    (y, d0) = lc3.mdct_inverse(dt, sr, C.X_HAT_SNS[i0][0], np.zeros(nd))
181*49fe348cSAndroid Build Coastguard Worker    yr = C.T_HAT_MDCT[i0][0][ns-nd:2*ns-nd]
182*49fe348cSAndroid Build Coastguard Worker    dr = C.T_HAT_MDCT[i0][0][2*ns-nd:]
183*49fe348cSAndroid Build Coastguard Worker
184*49fe348cSAndroid Build Coastguard Worker    ok = ok and np.amax(np.abs(yr - y )) < 1e-1
185*49fe348cSAndroid Build Coastguard Worker    ok = ok and np.amax(np.abs(dr - d0)) < 1e-1
186*49fe348cSAndroid Build Coastguard Worker
187*49fe348cSAndroid Build Coastguard Worker    (y, d1) = lc3.mdct_inverse(dt, sr, C.X_HAT_SNS[i0][1], d0)
188*49fe348cSAndroid Build Coastguard Worker    yr[  :nd] = C.T_HAT_MDCT[i0][1][ns-nd:ns] + d0
189*49fe348cSAndroid Build Coastguard Worker    yr[nd:ns] = C.T_HAT_MDCT[i0][1][ns:2*ns-nd]
190*49fe348cSAndroid Build Coastguard Worker    dr        = C.T_HAT_MDCT[i0][1][2*ns-nd:]
191*49fe348cSAndroid Build Coastguard Worker
192*49fe348cSAndroid Build Coastguard Worker    ok = ok and np.amax(np.abs(yr - y )) < 1e-1
193*49fe348cSAndroid Build Coastguard Worker    ok = ok and np.amax(np.abs(dr - d1)) < 1e-1
194*49fe348cSAndroid Build Coastguard Worker
195*49fe348cSAndroid Build Coastguard Worker    return ok
196*49fe348cSAndroid Build Coastguard Worker
197*49fe348cSAndroid Build Coastguard Worker
198*49fe348cSAndroid Build Coastguard Workerdef check():
199*49fe348cSAndroid Build Coastguard Worker
200*49fe348cSAndroid Build Coastguard Worker    rng = np.random.default_rng(1234)
201*49fe348cSAndroid Build Coastguard Worker
202*49fe348cSAndroid Build Coastguard Worker    ok  = True
203*49fe348cSAndroid Build Coastguard Worker
204*49fe348cSAndroid Build Coastguard Worker    for dt in range(T.NUM_DT):
205*49fe348cSAndroid Build Coastguard Worker        for sr in range(T.SRATE_8K, T.SRATE_48K + 1):
206*49fe348cSAndroid Build Coastguard Worker            ok = ok and check_forward_unit(rng, dt, sr)
207*49fe348cSAndroid Build Coastguard Worker            ok = ok and check_inverse_unit(rng, dt, sr)
208*49fe348cSAndroid Build Coastguard Worker
209*49fe348cSAndroid Build Coastguard Worker    for dt in ( T.DT_2M5, T.DT_5M, T.DT_10M ):
210*49fe348cSAndroid Build Coastguard Worker        for sr in ( T.SRATE_48K_HR, T.SRATE_96K_HR ):
211*49fe348cSAndroid Build Coastguard Worker            ok = ok and check_forward_unit(rng, dt, sr)
212*49fe348cSAndroid Build Coastguard Worker            ok = ok and check_inverse_unit(rng, dt, sr)
213*49fe348cSAndroid Build Coastguard Worker
214*49fe348cSAndroid Build Coastguard Worker    for dt in ( T.DT_7M5, T.DT_10M ):
215*49fe348cSAndroid Build Coastguard Worker        ok = ok and check_forward_appendix_c(dt)
216*49fe348cSAndroid Build Coastguard Worker        ok = ok and check_inverse_appendix_c(dt)
217*49fe348cSAndroid Build Coastguard Worker
218*49fe348cSAndroid Build Coastguard Worker    return ok
219