use std::fs::File;
use std::io::{self, BufWriter, Read, Write};
use std::process;
use a1800_codec::wav::{self, WavWriter};
use a1800_codec::{A1800Decoder, A1800Encoder};
fn main() {
let args: Vec<String> = std::env::args().collect();
if args.len() < 2 {
print_usage();
process::exit(1);
}
match args[1].as_str() {
"encode" => {
if args.len() < 4 {
eprintln!("Usage: a1800_codec encode <input.wav> <output.a18> [--bitrate N]");
process::exit(1);
}
let input_path = &args[2];
let output_path = &args[3];
let mut bitrate = 16000u16;
let mut i = 4;
while i < args.len() {
match args[i].as_str() {
"--bitrate" => {
i += 1;
if i >= args.len() {
eprintln!("Error: --bitrate requires a value");
process::exit(1);
}
bitrate = args[i].parse().unwrap_or_else(|_| {
eprintln!("Error: invalid bitrate '{}'", args[i]);
process::exit(1);
});
}
other => {
eprintln!("Error: unknown option '{}'", other);
process::exit(1);
}
}
i += 1;
}
if let Err(e) = encode_file(input_path, output_path, bitrate) {
eprintln!("Error: {}", e);
process::exit(1);
}
}
"decode" => {
if args.len() < 4 {
eprintln!("Usage: a1800_codec decode <input.a18> <output.wav> [--sample-rate N]");
process::exit(1);
}
let input_path = &args[2];
let output_path = &args[3];
let mut sample_rate = 16000u32;
let mut i = 4;
while i < args.len() {
match args[i].as_str() {
"--sample-rate" => {
i += 1;
if i >= args.len() {
eprintln!("Error: --sample-rate requires a value");
process::exit(1);
}
sample_rate = args[i].parse().unwrap_or_else(|_| {
eprintln!("Error: invalid sample rate '{}'", args[i]);
process::exit(1);
});
}
other => {
eprintln!("Error: unknown option '{}'", other);
process::exit(1);
}
}
i += 1;
}
if let Err(e) = decode_file(input_path, output_path, sample_rate) {
eprintln!("Error: {}", e);
process::exit(1);
}
}
"--help" | "-h" | "help" => {
print_usage();
}
other => {
eprintln!("Error: unknown command '{}'", other);
print_usage();
process::exit(1);
}
}
}
fn print_usage() {
eprintln!("A1800 audio codec");
eprintln!();
eprintln!("Usage:");
eprintln!(" a1800_codec encode <input.wav> <output.a18> [--bitrate N]");
eprintln!(" a1800_codec decode <input.a18> <output.wav> [--sample-rate N]");
eprintln!();
eprintln!("Options:");
eprintln!(" --bitrate N Encoding bitrate in bps (default: 16000)");
eprintln!(" --sample-rate N Output sample rate in Hz (default: 16000)");
}
fn encode_file(input_path: &str, output_path: &str, bitrate: u16) -> io::Result<()> {
let mut wav_file = File::open(input_path)?;
let (samples, sample_rate) = wav::read_wav_samples(&mut wav_file)?;
eprintln!(
"WAV: {} samples, {} Hz, {:.1}ms",
samples.len(),
sample_rate,
samples.len() as f64 / sample_rate as f64 * 1000.0
);
let mut encoder = A1800Encoder::new(bitrate).map_err(|e| {
io::Error::new(io::ErrorKind::InvalidData, e.to_string())
})?;
let enc_frame_words = encoder.encoded_frame_size();
let enc_frame_bytes = enc_frame_words * 2;
let num_frames = samples.len() / 320;
eprintln!(
"Encoding {} frames at {} bps ({} bytes/frame)...",
num_frames, bitrate, enc_frame_bytes
);
let mut out_file = File::create(output_path)?;
let data_length = (num_frames * enc_frame_bytes) as u32;
out_file.write_all(&data_length.to_le_bytes())?;
out_file.write_all(&bitrate.to_le_bytes())?;
let mut frame_words = vec![0i16; enc_frame_words];
for frame_idx in 0..num_frames {
let pcm_offset = frame_idx * 320;
let pcm_frame = &samples[pcm_offset..pcm_offset + 320];
encoder
.encode_frame(pcm_frame, &mut frame_words)
.map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e.to_string()))?;
for &word in &frame_words {
out_file.write_all(&word.to_le_bytes())?;
}
}
let total_bytes = 6 + data_length as usize;
eprintln!(
"Wrote {} frames ({} bytes) to {}",
num_frames, total_bytes, output_path
);
Ok(())
}
fn decode_file(input_path: &str, output_path: &str, sample_rate: u32) -> io::Result<()> {
let mut input_data = Vec::new();
File::open(input_path)?.read_to_end(&mut input_data)?;
if input_data.len() < 6 {
return Err(io::Error::new(
io::ErrorKind::InvalidData,
"file too small for A18 header (need at least 6 bytes)",
));
}
let data_length = u32::from_le_bytes(input_data[0..4].try_into().unwrap()) as usize;
let bitrate = u16::from_le_bytes(input_data[4..6].try_into().unwrap());
eprintln!(
"A18: data_length={} bytes, bitrate={} bps",
data_length, bitrate
);
let mut decoder = A1800Decoder::new(bitrate).map_err(|e| {
io::Error::new(io::ErrorKind::InvalidData, e.to_string())
})?;
let enc_frame_words = decoder.encoded_frame_size();
let enc_frame_bytes = enc_frame_words * 2;
eprintln!(
"Frame size: {} i16 words ({} bytes), output: {} samples/frame",
enc_frame_words,
enc_frame_bytes,
decoder.decoded_frame_size()
);
let payload = &input_data[6..];
let actual_data_len = payload.len().min(data_length);
if actual_data_len < enc_frame_bytes {
return Err(io::Error::new(
io::ErrorKind::InvalidData,
"no complete frames in file",
));
}
let num_frames = actual_data_len / enc_frame_bytes;
eprintln!("Decoding {} frames...", num_frames);
let out_file = File::create(output_path)?;
let mut wav = WavWriter::new(BufWriter::new(out_file), sample_rate)?;
let mut frame_words = vec![0i16; enc_frame_words];
let mut pcm_output = [0i16; 320];
for frame_idx in 0..num_frames {
let byte_offset = frame_idx * enc_frame_bytes;
let frame_bytes = &payload[byte_offset..byte_offset + enc_frame_bytes];
for (j, word) in frame_words.iter_mut().enumerate() {
*word = i16::from_le_bytes(frame_bytes[j * 2..j * 2 + 2].try_into().unwrap());
}
decoder
.decode_frame(&frame_words, &mut pcm_output)
.map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e.to_string()))?;
wav.write_samples(&pcm_output)?;
}
wav.finish()?;
let total_samples = num_frames * 320;
let duration_ms = total_samples as f64 / sample_rate as f64 * 1000.0;
eprintln!(
"Wrote {} samples ({:.1}ms) to {}",
total_samples, duration_ms, output_path
);
Ok(())
}