#!/usr/bin/env python3
#
# Copyright 2024 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#

import argparse
import lc3
import struct
import sys
import wave

parser = argparse.ArgumentParser(description='LC3 Decoder')

parser.add_argument(
    'lc3_file', nargs='?',
    help='Input bitstream file, default is stdin',
    type=argparse.FileType('rb'), default=sys.stdin.buffer)

parser.add_argument(
    'wav_file', nargs='?',
    help='Output wave file, default is stdout',
    type=argparse.FileType('wb'), default=sys.stdout.buffer)

parser.add_argument(
    '--bit_depth',
    help='Output bit depth, default is 16 bits',
    type=int, choices=[16, 24], default=16)

parser.add_argument(
    '--libpath', help='LC3 Library path')

args = parser.parse_args()

# --- LC3 File input ---

f_lc3 = args.lc3_file

header = struct.unpack('=HHHHHHHI', f_lc3.read(18))
if header[0] != 0xcc1c:
    raise ValueError('Invalid bitstream file')

samplerate = header[2] * 100
nchannels = header[4]
frame_duration = header[5] * 10
stream_length = header[7]

# --- Setup output ---

bit_depth = args.bit_depth
pcm_size = nchannels * (bit_depth // 8)

f_wav = args.wav_file
wavfile = wave.open(f_wav)
wavfile.setnchannels(nchannels)
wavfile.setsampwidth(bit_depth // 8)
wavfile.setframerate(samplerate)
wavfile.setnframes(stream_length)

# --- Setup decoder ---

dec = lc3.Decoder(
    frame_duration, samplerate, nchannels, libpath=args.libpath)
frame_length = dec.get_frame_samples()
encoded_length = stream_length + dec.get_delay_samples()

# --- Decoding loop ---

for i in range(0, encoded_length, frame_length):

    lc3_frame_size = struct.unpack('=H', f_lc3.read(2))[0]
    pcm = dec.decode(f_lc3.read(lc3_frame_size), bit_depth=bit_depth)

    pcm = pcm[max(encoded_length - stream_length - i, 0) * pcm_size:
              min(encoded_length - i, frame_length) * pcm_size]

    wavfile.writeframesraw(pcm)

# --- Cleanup ---

wavfile.close()

for f in (f_lc3, f_wav):
    if f is not sys.stdout.buffer:
        f.close()
