xref: /btstack/3rd-party/lc3-google/test/decoder.py (revision 4c4eb519208b4224604d94b3ed1931841ddd93bb)
14930cef6SMatthias Ringwald#!/usr/bin/env python3
24930cef6SMatthias Ringwald#
34930cef6SMatthias Ringwald# Copyright 2022 Google LLC
44930cef6SMatthias Ringwald#
54930cef6SMatthias Ringwald# Licensed under the Apache License, Version 2.0 (the "License");
64930cef6SMatthias Ringwald# you may not use this file except in compliance with the License.
74930cef6SMatthias Ringwald# You may obtain a copy of the License at
84930cef6SMatthias Ringwald#
94930cef6SMatthias Ringwald#     http://www.apache.org/licenses/LICENSE-2.0
104930cef6SMatthias Ringwald#
114930cef6SMatthias Ringwald# Unless required by applicable law or agreed to in writing, software
124930cef6SMatthias Ringwald# distributed under the License is distributed on an "AS IS" BASIS,
134930cef6SMatthias Ringwald# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
144930cef6SMatthias Ringwald# See the License for the specific language governing permissions and
154930cef6SMatthias Ringwald# limitations under the License.
164930cef6SMatthias Ringwald#
174930cef6SMatthias Ringwald
184930cef6SMatthias Ringwaldimport numpy as np
194930cef6SMatthias Ringwaldimport scipy.signal as signal
204930cef6SMatthias Ringwaldimport scipy.io.wavfile as wavfile
214930cef6SMatthias Ringwaldimport struct
224930cef6SMatthias Ringwaldimport argparse
234930cef6SMatthias Ringwald
24*4c4eb519SMatthias Ringwaldimport lc3
254930cef6SMatthias Ringwaldimport tables as T, appendix_c as C
264930cef6SMatthias Ringwald
274930cef6SMatthias Ringwaldimport mdct, energy, bwdet, sns, tns, spec, ltpf
284930cef6SMatthias Ringwaldimport bitstream
294930cef6SMatthias Ringwald
304930cef6SMatthias Ringwald### ------------------------------------------------------------------------ ###
314930cef6SMatthias Ringwald
324930cef6SMatthias Ringwaldclass Decoder:
334930cef6SMatthias Ringwald
344930cef6SMatthias Ringwald    def __init__(self, dt_ms, sr_hz):
354930cef6SMatthias Ringwald
364930cef6SMatthias Ringwald        dt = { 7.5: T.DT_7M5, 10: T.DT_10M }[dt_ms]
374930cef6SMatthias Ringwald
384930cef6SMatthias Ringwald        sr = {  8000: T.SRATE_8K , 16000: T.SRATE_16K, 24000: T.SRATE_24K,
394930cef6SMatthias Ringwald               32000: T.SRATE_32K, 48000: T.SRATE_48K }[sr_hz]
404930cef6SMatthias Ringwald
414930cef6SMatthias Ringwald        self.sr = sr
424930cef6SMatthias Ringwald        self.ne = T.NE[dt][sr]
434930cef6SMatthias Ringwald        self.ns = T.NS[dt][sr]
444930cef6SMatthias Ringwald
454930cef6SMatthias Ringwald        self.mdct = mdct.MdctInverse(dt, sr)
464930cef6SMatthias Ringwald
474930cef6SMatthias Ringwald        self.bwdet = bwdet.BandwidthDetector(dt, sr)
484930cef6SMatthias Ringwald        self.spec = spec.SpectrumSynthesis(dt, sr)
494930cef6SMatthias Ringwald        self.tns = tns.TnsSynthesis(dt)
504930cef6SMatthias Ringwald        self.sns = sns.SnsSynthesis(dt, sr)
514930cef6SMatthias Ringwald        self.ltpf = ltpf.LtpfSynthesis(dt, sr)
524930cef6SMatthias Ringwald
534930cef6SMatthias Ringwald    def decode(self, data):
544930cef6SMatthias Ringwald
554930cef6SMatthias Ringwald        b = bitstream.BitstreamReader(data)
564930cef6SMatthias Ringwald
57*4c4eb519SMatthias Ringwald        bw = self.bwdet.get(b)
584930cef6SMatthias Ringwald        if bw > self.sr:
594930cef6SMatthias Ringwald            raise ValueError('Invalid bandwidth indication')
604930cef6SMatthias Ringwald
614930cef6SMatthias Ringwald        self.spec.load(b)
624930cef6SMatthias Ringwald
634930cef6SMatthias Ringwald        self.tns.load(b, bw, len(data))
644930cef6SMatthias Ringwald
654930cef6SMatthias Ringwald        pitch = b.read_bit()
664930cef6SMatthias Ringwald
674930cef6SMatthias Ringwald        self.sns.load(b)
684930cef6SMatthias Ringwald
694930cef6SMatthias Ringwald        if pitch:
704930cef6SMatthias Ringwald            self.ltpf.load(b)
714930cef6SMatthias Ringwald        else:
724930cef6SMatthias Ringwald            self.ltpf.disable()
734930cef6SMatthias Ringwald
744930cef6SMatthias Ringwald        x = self.spec.decode(b, bw, len(data))
754930cef6SMatthias Ringwald
764930cef6SMatthias Ringwald        return (x, bw, pitch)
774930cef6SMatthias Ringwald
784930cef6SMatthias Ringwald    def synthesize(self, x, bw, pitch, nbytes):
794930cef6SMatthias Ringwald
804930cef6SMatthias Ringwald        x = self.tns.run(x, bw)
814930cef6SMatthias Ringwald
824930cef6SMatthias Ringwald        x = self.sns.run(x)
834930cef6SMatthias Ringwald
844930cef6SMatthias Ringwald        x = np.append(x, np.zeros(self.ns - self.ne))
854930cef6SMatthias Ringwald        x = self.mdct.run(x)
864930cef6SMatthias Ringwald
874930cef6SMatthias Ringwald        x = self.ltpf.run(x, len(data))
884930cef6SMatthias Ringwald
894930cef6SMatthias Ringwald        return x
904930cef6SMatthias Ringwald
914930cef6SMatthias Ringwald    def run(self, data):
924930cef6SMatthias Ringwald
934930cef6SMatthias Ringwald        (x, bw, pitch) = self.decode(data)
944930cef6SMatthias Ringwald
954930cef6SMatthias Ringwald        x = self.synthesize(x, bw, pitch, len(data))
964930cef6SMatthias Ringwald
974930cef6SMatthias Ringwald        return x
984930cef6SMatthias Ringwald
994930cef6SMatthias Ringwald### ------------------------------------------------------------------------ ###
1004930cef6SMatthias Ringwald
1014930cef6SMatthias Ringwalddef check_appendix_c(dt):
1024930cef6SMatthias Ringwald
1034930cef6SMatthias Ringwald    ok = True
1044930cef6SMatthias Ringwald
1054930cef6SMatthias Ringwald    dec_c = lc3.setup_decoder(int(T.DT_MS[dt] * 1000), 16000)
1064930cef6SMatthias Ringwald
1074930cef6SMatthias Ringwald    for i in range(len(C.BYTES_AC[dt])):
1084930cef6SMatthias Ringwald
1094930cef6SMatthias Ringwald        pcm = lc3.decode(dec_c, bytes(C.BYTES_AC[dt][i]))
1104930cef6SMatthias Ringwald        ok = ok and np.max(np.abs(pcm - C.X_HAT_CLIP[dt][i])) < 1
1114930cef6SMatthias Ringwald
1124930cef6SMatthias Ringwald    return ok
1134930cef6SMatthias Ringwald
1144930cef6SMatthias Ringwalddef check():
1154930cef6SMatthias Ringwald
1164930cef6SMatthias Ringwald    ok = True
1174930cef6SMatthias Ringwald
1184930cef6SMatthias Ringwald    for dt in range(T.NUM_DT):
1194930cef6SMatthias Ringwald        ok = ok and check_appendix_c(dt)
1204930cef6SMatthias Ringwald
1214930cef6SMatthias Ringwald    return ok
1224930cef6SMatthias Ringwald
1234930cef6SMatthias Ringwald### ------------------------------------------------------------------------ ###
1244930cef6SMatthias Ringwald
1254930cef6SMatthias Ringwaldif __name__ == "__main__":
1264930cef6SMatthias Ringwald
1274930cef6SMatthias Ringwald    parser = argparse.ArgumentParser(description='LC3 Decoder Test Framework')
1284930cef6SMatthias Ringwald    parser.add_argument('lc3_file',
1294930cef6SMatthias Ringwald        help='Input bitstream file', type=argparse.FileType('r'))
1304930cef6SMatthias Ringwald    parser.add_argument('--pyout',
1314930cef6SMatthias Ringwald        help='Python output file', type=argparse.FileType('w'))
1324930cef6SMatthias Ringwald    parser.add_argument('--cout',
1334930cef6SMatthias Ringwald        help='C output file', type=argparse.FileType('w'))
1344930cef6SMatthias Ringwald    args = parser.parse_args()
1354930cef6SMatthias Ringwald
1364930cef6SMatthias Ringwald    ### File Header ###
1374930cef6SMatthias Ringwald
1384930cef6SMatthias Ringwald    f_lc3 = open(args.lc3_file.name, 'rb')
1394930cef6SMatthias Ringwald
1404930cef6SMatthias Ringwald    header = struct.unpack('=HHHHHHHI', f_lc3.read(18))
1414930cef6SMatthias Ringwald
1424930cef6SMatthias Ringwald    if header[0] != 0xcc1c:
1434930cef6SMatthias Ringwald        raise ValueError('Invalid bitstream file')
1444930cef6SMatthias Ringwald
1454930cef6SMatthias Ringwald    if header[4] != 1:
1464930cef6SMatthias Ringwald        raise ValueError('Unsupported number of channels')
1474930cef6SMatthias Ringwald
1484930cef6SMatthias Ringwald    sr_hz = header[2] * 100
1494930cef6SMatthias Ringwald    bitrate = header[3] * 100
1504930cef6SMatthias Ringwald    nchannels = header[4]
1514930cef6SMatthias Ringwald    dt_ms = header[5] / 100
1524930cef6SMatthias Ringwald
1534930cef6SMatthias Ringwald    f_lc3.seek(header[1])
1544930cef6SMatthias Ringwald
1554930cef6SMatthias Ringwald    ### Setup ###
1564930cef6SMatthias Ringwald
1574930cef6SMatthias Ringwald    dec = Decoder(dt_ms, sr_hz)
1584930cef6SMatthias Ringwald    dec_c = lc3.setup_decoder(int(dt_ms * 1000), sr_hz)
1594930cef6SMatthias Ringwald
1604930cef6SMatthias Ringwald    pcm_c  = np.empty(0).astype(np.int16)
1614930cef6SMatthias Ringwald    pcm_py = np.empty(0).astype(np.int16)
1624930cef6SMatthias Ringwald
1634930cef6SMatthias Ringwald    ### Decoding loop ###
1644930cef6SMatthias Ringwald
1654930cef6SMatthias Ringwald    nframes = 0
1664930cef6SMatthias Ringwald
1674930cef6SMatthias Ringwald    while True:
1684930cef6SMatthias Ringwald
1694930cef6SMatthias Ringwald        data = f_lc3.read(2)
1704930cef6SMatthias Ringwald        if len(data) != 2:
1714930cef6SMatthias Ringwald            break
1724930cef6SMatthias Ringwald
1734930cef6SMatthias Ringwald        (frame_nbytes,) = struct.unpack('=H', data)
1744930cef6SMatthias Ringwald
1754930cef6SMatthias Ringwald        print('Decoding frame %d' % nframes, end='\r')
1764930cef6SMatthias Ringwald
1774930cef6SMatthias Ringwald        data = f_lc3.read(frame_nbytes)
1784930cef6SMatthias Ringwald
1794930cef6SMatthias Ringwald        x = dec.run(data)
1804930cef6SMatthias Ringwald        pcm_py = np.append(pcm_py,
1814930cef6SMatthias Ringwald            np.clip(np.round(x), -32768, 32767).astype(np.int16))
1824930cef6SMatthias Ringwald
1834930cef6SMatthias Ringwald        x_c = lc3.decode(dec_c, data)
1844930cef6SMatthias Ringwald        pcm_c = np.append(pcm_c, x_c)
1854930cef6SMatthias Ringwald
1864930cef6SMatthias Ringwald        nframes += 1
1874930cef6SMatthias Ringwald
1884930cef6SMatthias Ringwald    print('done ! %16s' % '')
1894930cef6SMatthias Ringwald
1904930cef6SMatthias Ringwald    ### Terminate ###
1914930cef6SMatthias Ringwald
1924930cef6SMatthias Ringwald    if args.pyout:
1934930cef6SMatthias Ringwald        wavfile.write(args.pyout.name, sr_hz, pcm_py)
1944930cef6SMatthias Ringwald    if args.cout:
1954930cef6SMatthias Ringwald        wavfile.write(args.cout.name, sr_hz, pcm_c)
1964930cef6SMatthias Ringwald
1974930cef6SMatthias Ringwald### ------------------------------------------------------------------------ ###
198