xref: /btstack/3rd-party/lc3-google/python/tools/decoder.py (revision 25d5427a345779b159b63c0d8b197d2dee40cf37)
1#!/usr/bin/env python3
2#
3# Copyright 2024 Google LLC
4#
5# Licensed under the Apache License, Version 2.0 (the "License");
6# you may not use this file except in compliance with the License.
7# You may obtain a copy of the License at
8#
9#     http://www.apache.org/licenses/LICENSE-2.0
10#
11# Unless required by applicable law or agreed to in writing, software
12# distributed under the License is distributed on an "AS IS" BASIS,
13# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14# See the License for the specific language governing permissions and
15# limitations under the License.
16#
17
18import argparse
19import lc3
20import struct
21import sys
22import wave
23
24parser = argparse.ArgumentParser(description='LC3 Decoder')
25
26parser.add_argument(
27    'lc3_file', nargs='?',
28    help='Input bitstream file, default is stdin',
29    type=argparse.FileType('rb'), default=sys.stdin.buffer)
30
31parser.add_argument(
32    'wav_file', nargs='?',
33    help='Output wave file, default is stdout',
34    type=argparse.FileType('wb'), default=sys.stdout.buffer)
35
36parser.add_argument(
37    '--bitdepth',
38    help='Output bitdepth, default is 16 bits',
39    type=int, choices=[16, 24], default=16)
40
41parser.add_argument(
42    '--libpath', help='LC3 Library path')
43
44args = parser.parse_args()
45
46# --- LC3 File input ---
47
48f_lc3 = args.lc3_file
49
50header = struct.unpack('=HHHHHHHI', f_lc3.read(18))
51if header[0] != 0xcc1c:
52    raise ValueError('Invalid bitstream file')
53
54samplerate = header[2] * 100
55nchannels = header[4]
56frame_duration = header[5] / 100
57stream_length = header[7]
58
59# --- Setup output ---
60
61bitdepth = args.bitdepth
62pcm_size = nchannels * (bitdepth // 8)
63
64f_wav = args.wav_file
65wavfile = wave.open(f_wav)
66wavfile.setnchannels(nchannels)
67wavfile.setsampwidth(bitdepth // 8)
68wavfile.setframerate(samplerate)
69wavfile.setnframes(stream_length)
70
71# --- Setup decoder ---
72
73dec = lc3.Decoder(
74    frame_duration, samplerate, nchannels, libpath=args.libpath)
75frame_length = dec.get_frame_samples()
76encoded_length = stream_length + dec.get_delay_samples()
77
78# --- Decoding loop ---
79
80for i in range(0, encoded_length, frame_length):
81
82    lc3_frame_size = struct.unpack('=H', f_lc3.read(2))[0]
83    pcm = dec.decode(f_lc3.read(lc3_frame_size), bitdepth=bitdepth)
84
85    pcm = pcm[max(encoded_length - stream_length - i, 0) * pcm_size:
86              min(encoded_length - i, frame_length) * pcm_size]
87
88    wavfile.writeframesraw(pcm)
89
90# --- Cleanup ---
91
92wavfile.close()
93
94for f in (f_lc3, f_wav):
95    if f is not sys.stdout.buffer:
96        f.close()
97