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