xref: /btstack/3rd-party/lc3-google/test/decoder.py (revision 4930cef6e21e6da2d7571b9259c7f0fb8bed3d01)
1*4930cef6SMatthias Ringwald#!/usr/bin/env python3
2*4930cef6SMatthias Ringwald#
3*4930cef6SMatthias Ringwald# Copyright 2022 Google LLC
4*4930cef6SMatthias Ringwald#
5*4930cef6SMatthias Ringwald# Licensed under the Apache License, Version 2.0 (the "License");
6*4930cef6SMatthias Ringwald# you may not use this file except in compliance with the License.
7*4930cef6SMatthias Ringwald# You may obtain a copy of the License at
8*4930cef6SMatthias Ringwald#
9*4930cef6SMatthias Ringwald#     http://www.apache.org/licenses/LICENSE-2.0
10*4930cef6SMatthias Ringwald#
11*4930cef6SMatthias Ringwald# Unless required by applicable law or agreed to in writing, software
12*4930cef6SMatthias Ringwald# distributed under the License is distributed on an "AS IS" BASIS,
13*4930cef6SMatthias Ringwald# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14*4930cef6SMatthias Ringwald# See the License for the specific language governing permissions and
15*4930cef6SMatthias Ringwald# limitations under the License.
16*4930cef6SMatthias Ringwald#
17*4930cef6SMatthias Ringwald
18*4930cef6SMatthias Ringwaldimport numpy as np
19*4930cef6SMatthias Ringwaldimport scipy.signal as signal
20*4930cef6SMatthias Ringwaldimport scipy.io.wavfile as wavfile
21*4930cef6SMatthias Ringwaldimport struct
22*4930cef6SMatthias Ringwaldimport argparse
23*4930cef6SMatthias Ringwald
24*4930cef6SMatthias Ringwaldimport build.lc3 as lc3
25*4930cef6SMatthias Ringwaldimport tables as T, appendix_c as C
26*4930cef6SMatthias Ringwald
27*4930cef6SMatthias Ringwaldimport mdct, energy, bwdet, sns, tns, spec, ltpf
28*4930cef6SMatthias Ringwaldimport bitstream
29*4930cef6SMatthias Ringwald
30*4930cef6SMatthias Ringwald### ------------------------------------------------------------------------ ###
31*4930cef6SMatthias Ringwald
32*4930cef6SMatthias Ringwaldclass Decoder:
33*4930cef6SMatthias Ringwald
34*4930cef6SMatthias Ringwald    def __init__(self, dt_ms, sr_hz):
35*4930cef6SMatthias Ringwald
36*4930cef6SMatthias Ringwald        dt = { 7.5: T.DT_7M5, 10: T.DT_10M }[dt_ms]
37*4930cef6SMatthias Ringwald
38*4930cef6SMatthias Ringwald        sr = {  8000: T.SRATE_8K , 16000: T.SRATE_16K, 24000: T.SRATE_24K,
39*4930cef6SMatthias Ringwald               32000: T.SRATE_32K, 48000: T.SRATE_48K }[sr_hz]
40*4930cef6SMatthias Ringwald
41*4930cef6SMatthias Ringwald        self.sr = sr
42*4930cef6SMatthias Ringwald        self.ne = T.NE[dt][sr]
43*4930cef6SMatthias Ringwald        self.ns = T.NS[dt][sr]
44*4930cef6SMatthias Ringwald
45*4930cef6SMatthias Ringwald        self.mdct = mdct.MdctInverse(dt, sr)
46*4930cef6SMatthias Ringwald
47*4930cef6SMatthias Ringwald        self.bwdet = bwdet.BandwidthDetector(dt, sr)
48*4930cef6SMatthias Ringwald        self.spec = spec.SpectrumSynthesis(dt, sr)
49*4930cef6SMatthias Ringwald        self.tns = tns.TnsSynthesis(dt)
50*4930cef6SMatthias Ringwald        self.sns = sns.SnsSynthesis(dt, sr)
51*4930cef6SMatthias Ringwald        self.ltpf = ltpf.LtpfSynthesis(dt, sr)
52*4930cef6SMatthias Ringwald
53*4930cef6SMatthias Ringwald    def decode(self, data):
54*4930cef6SMatthias Ringwald
55*4930cef6SMatthias Ringwald        b = bitstream.BitstreamReader(data)
56*4930cef6SMatthias Ringwald
57*4930cef6SMatthias Ringwald        bw = self.bwdet.get_bw(b)
58*4930cef6SMatthias Ringwald        if bw > self.sr:
59*4930cef6SMatthias Ringwald            raise ValueError('Invalid bandwidth indication')
60*4930cef6SMatthias Ringwald
61*4930cef6SMatthias Ringwald        self.spec.load(b)
62*4930cef6SMatthias Ringwald
63*4930cef6SMatthias Ringwald        self.tns.load(b, bw, len(data))
64*4930cef6SMatthias Ringwald
65*4930cef6SMatthias Ringwald        pitch = b.read_bit()
66*4930cef6SMatthias Ringwald
67*4930cef6SMatthias Ringwald        self.sns.load(b)
68*4930cef6SMatthias Ringwald
69*4930cef6SMatthias Ringwald        if pitch:
70*4930cef6SMatthias Ringwald            self.ltpf.load(b)
71*4930cef6SMatthias Ringwald        else:
72*4930cef6SMatthias Ringwald            self.ltpf.disable()
73*4930cef6SMatthias Ringwald
74*4930cef6SMatthias Ringwald        x = self.spec.decode(b, bw, len(data))
75*4930cef6SMatthias Ringwald
76*4930cef6SMatthias Ringwald        return (x, bw, pitch)
77*4930cef6SMatthias Ringwald
78*4930cef6SMatthias Ringwald    def synthesize(self, x, bw, pitch, nbytes):
79*4930cef6SMatthias Ringwald
80*4930cef6SMatthias Ringwald        x = self.tns.run(x, bw)
81*4930cef6SMatthias Ringwald
82*4930cef6SMatthias Ringwald        x = self.sns.run(x)
83*4930cef6SMatthias Ringwald
84*4930cef6SMatthias Ringwald        x = np.append(x, np.zeros(self.ns - self.ne))
85*4930cef6SMatthias Ringwald        x = self.mdct.run(x)
86*4930cef6SMatthias Ringwald
87*4930cef6SMatthias Ringwald        x = self.ltpf.run(x, len(data))
88*4930cef6SMatthias Ringwald
89*4930cef6SMatthias Ringwald        return x
90*4930cef6SMatthias Ringwald
91*4930cef6SMatthias Ringwald    def run(self, data):
92*4930cef6SMatthias Ringwald
93*4930cef6SMatthias Ringwald        (x, bw, pitch) = self.decode(data)
94*4930cef6SMatthias Ringwald
95*4930cef6SMatthias Ringwald        x = self.synthesize(x, bw, pitch, len(data))
96*4930cef6SMatthias Ringwald
97*4930cef6SMatthias Ringwald        return x
98*4930cef6SMatthias Ringwald
99*4930cef6SMatthias Ringwald### ------------------------------------------------------------------------ ###
100*4930cef6SMatthias Ringwald
101*4930cef6SMatthias Ringwalddef check_appendix_c(dt):
102*4930cef6SMatthias Ringwald
103*4930cef6SMatthias Ringwald    ok = True
104*4930cef6SMatthias Ringwald
105*4930cef6SMatthias Ringwald    dec_c = lc3.setup_decoder(int(T.DT_MS[dt] * 1000), 16000)
106*4930cef6SMatthias Ringwald
107*4930cef6SMatthias Ringwald    for i in range(len(C.BYTES_AC[dt])):
108*4930cef6SMatthias Ringwald
109*4930cef6SMatthias Ringwald        pcm = lc3.decode(dec_c, bytes(C.BYTES_AC[dt][i]))
110*4930cef6SMatthias Ringwald        ok = ok and np.max(np.abs(pcm - C.X_HAT_CLIP[dt][i])) < 1
111*4930cef6SMatthias Ringwald
112*4930cef6SMatthias Ringwald    return ok
113*4930cef6SMatthias Ringwald
114*4930cef6SMatthias Ringwalddef check():
115*4930cef6SMatthias Ringwald
116*4930cef6SMatthias Ringwald    ok = True
117*4930cef6SMatthias Ringwald
118*4930cef6SMatthias Ringwald    for dt in range(T.NUM_DT):
119*4930cef6SMatthias Ringwald        ok = ok and check_appendix_c(dt)
120*4930cef6SMatthias Ringwald
121*4930cef6SMatthias Ringwald    return ok
122*4930cef6SMatthias Ringwald
123*4930cef6SMatthias Ringwald### ------------------------------------------------------------------------ ###
124*4930cef6SMatthias Ringwald
125*4930cef6SMatthias Ringwaldif __name__ == "__main__":
126*4930cef6SMatthias Ringwald
127*4930cef6SMatthias Ringwald    parser = argparse.ArgumentParser(description='LC3 Decoder Test Framework')
128*4930cef6SMatthias Ringwald    parser.add_argument('lc3_file',
129*4930cef6SMatthias Ringwald        help='Input bitstream file', type=argparse.FileType('r'))
130*4930cef6SMatthias Ringwald    parser.add_argument('--pyout',
131*4930cef6SMatthias Ringwald        help='Python output file', type=argparse.FileType('w'))
132*4930cef6SMatthias Ringwald    parser.add_argument('--cout',
133*4930cef6SMatthias Ringwald        help='C output file', type=argparse.FileType('w'))
134*4930cef6SMatthias Ringwald    args = parser.parse_args()
135*4930cef6SMatthias Ringwald
136*4930cef6SMatthias Ringwald    ### File Header ###
137*4930cef6SMatthias Ringwald
138*4930cef6SMatthias Ringwald    f_lc3 = open(args.lc3_file.name, 'rb')
139*4930cef6SMatthias Ringwald
140*4930cef6SMatthias Ringwald    header = struct.unpack('=HHHHHHHI', f_lc3.read(18))
141*4930cef6SMatthias Ringwald
142*4930cef6SMatthias Ringwald    if header[0] != 0xcc1c:
143*4930cef6SMatthias Ringwald        raise ValueError('Invalid bitstream file')
144*4930cef6SMatthias Ringwald
145*4930cef6SMatthias Ringwald    if header[4] != 1:
146*4930cef6SMatthias Ringwald        raise ValueError('Unsupported number of channels')
147*4930cef6SMatthias Ringwald
148*4930cef6SMatthias Ringwald    sr_hz = header[2] * 100
149*4930cef6SMatthias Ringwald    bitrate = header[3] * 100
150*4930cef6SMatthias Ringwald    nchannels = header[4]
151*4930cef6SMatthias Ringwald    dt_ms = header[5] / 100
152*4930cef6SMatthias Ringwald
153*4930cef6SMatthias Ringwald    f_lc3.seek(header[1])
154*4930cef6SMatthias Ringwald
155*4930cef6SMatthias Ringwald    ### Setup ###
156*4930cef6SMatthias Ringwald
157*4930cef6SMatthias Ringwald    dec = Decoder(dt_ms, sr_hz)
158*4930cef6SMatthias Ringwald    dec_c = lc3.setup_decoder(int(dt_ms * 1000), sr_hz)
159*4930cef6SMatthias Ringwald
160*4930cef6SMatthias Ringwald    pcm_c  = np.empty(0).astype(np.int16)
161*4930cef6SMatthias Ringwald    pcm_py = np.empty(0).astype(np.int16)
162*4930cef6SMatthias Ringwald
163*4930cef6SMatthias Ringwald    ### Decoding loop ###
164*4930cef6SMatthias Ringwald
165*4930cef6SMatthias Ringwald    nframes = 0
166*4930cef6SMatthias Ringwald
167*4930cef6SMatthias Ringwald    while True:
168*4930cef6SMatthias Ringwald
169*4930cef6SMatthias Ringwald        data = f_lc3.read(2)
170*4930cef6SMatthias Ringwald        if len(data) != 2:
171*4930cef6SMatthias Ringwald            break
172*4930cef6SMatthias Ringwald
173*4930cef6SMatthias Ringwald        if nframes >= 1000:
174*4930cef6SMatthias Ringwald            break
175*4930cef6SMatthias Ringwald
176*4930cef6SMatthias Ringwald        (frame_nbytes,) = struct.unpack('=H', data)
177*4930cef6SMatthias Ringwald
178*4930cef6SMatthias Ringwald        print('Decoding frame %d' % nframes, end='\r')
179*4930cef6SMatthias Ringwald
180*4930cef6SMatthias Ringwald        data = f_lc3.read(frame_nbytes)
181*4930cef6SMatthias Ringwald
182*4930cef6SMatthias Ringwald        x = dec.run(data)
183*4930cef6SMatthias Ringwald        pcm_py = np.append(pcm_py,
184*4930cef6SMatthias Ringwald            np.clip(np.round(x), -32768, 32767).astype(np.int16))
185*4930cef6SMatthias Ringwald
186*4930cef6SMatthias Ringwald        x_c = lc3.decode(dec_c, data)
187*4930cef6SMatthias Ringwald        pcm_c = np.append(pcm_c, x_c)
188*4930cef6SMatthias Ringwald
189*4930cef6SMatthias Ringwald        nframes += 1
190*4930cef6SMatthias Ringwald
191*4930cef6SMatthias Ringwald    print('done ! %16s' % '')
192*4930cef6SMatthias Ringwald
193*4930cef6SMatthias Ringwald    ### Terminate ###
194*4930cef6SMatthias Ringwald
195*4930cef6SMatthias Ringwald    if args.pyout:
196*4930cef6SMatthias Ringwald        wavfile.write(args.pyout.name, sr_hz, pcm_py)
197*4930cef6SMatthias Ringwald    if args.cout:
198*4930cef6SMatthias Ringwald        wavfile.write(args.cout.name, sr_hz, pcm_c)
199*4930cef6SMatthias Ringwald
200*4930cef6SMatthias Ringwald### ------------------------------------------------------------------------ ###
201