- Add .gitignore for Python, virtual environments, testing artifacts, IDE files, and LC3 test outputs including SQAM audio files - Add AGENTS.md with project context for LC3 implementation testing - Add LC3.TS.p5.pdf test specification document - Add LC3 conformance interoperability test software V1.0.8 with script readme and reference binary symlink
80 lines
2.6 KiB
Python
80 lines
2.6 KiB
Python
#!/usr/bin/env python3
|
|
"""LC3 encoder wrapper for conformanceCheck.py.
|
|
|
|
Called by conformanceCheck.py as:
|
|
python lc3_encode.py -frame_ms <ms> [options] "<input.wav>" "<output.lc3>" <bitrate_bps>
|
|
|
|
Bitstream format: 18-byte header (LC3 TS §3.2.8.2) + per-frame 2-byte size prefix + LC3 payload.
|
|
"""
|
|
|
|
import lc3
|
|
import struct
|
|
import sys
|
|
import wave
|
|
|
|
|
|
def main():
|
|
args = sys.argv[1:]
|
|
|
|
frame_ms = 10.0
|
|
positional = []
|
|
i = 0
|
|
while i < len(args):
|
|
if args[i] == '-frame_ms' and i + 1 < len(args):
|
|
try:
|
|
frame_ms = float(args[i + 1])
|
|
except ValueError:
|
|
print(f'Usage: {sys.argv[0]} -frame_ms <ms> [opts] <input.wav> <output.lc3> <bitrate_bps>',
|
|
file=sys.stderr)
|
|
sys.exit(1)
|
|
i += 2
|
|
elif args[i].startswith('-'):
|
|
i += 1 # skip unknown flags (e.g. -q)
|
|
else:
|
|
positional.append(args[i])
|
|
i += 1
|
|
|
|
if len(positional) < 3:
|
|
print(f'Usage: {sys.argv[0]} -frame_ms <ms> [opts] <input.wav> <output.lc3> <bitrate_bps>',
|
|
file=sys.stderr)
|
|
sys.exit(1)
|
|
|
|
wav_path = positional[0]
|
|
lc3_path = positional[1]
|
|
bitrate_bps = int(positional[2])
|
|
frame_duration_us = int(frame_ms * 1000)
|
|
|
|
with wave.open(wav_path, 'rb') as wavfile:
|
|
samplerate = wavfile.getframerate()
|
|
nchannels = wavfile.getnchannels()
|
|
bit_depth = wavfile.getsampwidth() * 8
|
|
stream_length = wavfile.getnframes()
|
|
|
|
enc = lc3.Encoder(frame_duration_us, samplerate, nchannels)
|
|
frame_size = enc.get_frame_bytes(bitrate_bps)
|
|
frame_length = enc.get_frame_samples()
|
|
resolved_bitrate = enc.resolve_bitrate(frame_size)
|
|
|
|
with open(lc3_path, 'wb') as f_lc3:
|
|
f_lc3.write(struct.pack(
|
|
'=HHHHHHHI',
|
|
0xcc1c, # signature
|
|
18, # header size
|
|
samplerate // 100, # sample rate / 100
|
|
resolved_bitrate // 100, # bitrate / 100
|
|
nchannels,
|
|
int(frame_ms * 100), # frame duration * 100
|
|
0, # reserved
|
|
stream_length, # total PCM samples
|
|
))
|
|
|
|
for _ in range(0, stream_length, frame_length):
|
|
pcm = wavfile.readframes(frame_length)
|
|
encoded = enc.encode(pcm, frame_size, bit_depth=bit_depth)
|
|
f_lc3.write(struct.pack('=H', frame_size))
|
|
f_lc3.write(encoded)
|
|
|
|
|
|
if __name__ == '__main__':
|
|
main()
|