mcelp 1.0.1

Mitsubishi CELP speech codec: a 3.6 kbit/s speech encoder and decoder
Documentation
//! Decode an M-CELP bit stream on stdin to raw mu-law on stdout.
//!
//! Input is one hex-encoded 18-byte frame per line; output is 320 mu-law bytes
//! per frame.  This mirrors the reference `mcelp_codec` in its decode mode.

use std::io::{BufRead, Write};

use mcelp::{Decoder, bitstream};

fn main() -> std::io::Result<()> {
    let stdin = std::io::stdin();
    let stdout = std::io::stdout();
    decode_stream(stdin.lock(), std::io::BufWriter::new(stdout.lock()))
}

/// Decode valid frame lines as they arrive, ignoring malformed input lines.
fn decode_stream(input: impl BufRead, mut output: impl Write) -> std::io::Result<()> {
    let mut decoder = Decoder::new();

    for line in input.lines() {
        let Some(frame) = bitstream::parse_hex_line(&line?) else {
            continue;
        };
        if let Some(pcm) = decoder.decode(&frame) {
            output.write_all(&pcm)?;
        }
    }
    output.flush()
}

#[cfg(test)]
mod tests {
    use mcelp::{Encoder, FRAME};

    use super::*;

    #[test]
    fn stream_matches_decoder_and_skips_invalid_lines() {
        let first = [0xff; FRAME];
        let second = std::array::from_fn(|i| i as u8);
        let mut encoder = Encoder::new();
        let frames = [encoder.encode(&first), encoder.encode(&second)];

        let input = format!(
            "not a frame\n{}\n\n{}\ntruncated\n",
            bitstream::to_hex_line(&frames[0]),
            bitstream::to_hex_line(&frames[1]),
        );

        let mut decoder = Decoder::new();
        let mut expected = Vec::new();
        for frame in &frames {
            if let Some(pcm) = decoder.decode(frame) {
                expected.extend_from_slice(&pcm);
            }
        }

        let mut output = Vec::new();
        decode_stream(input.as_bytes(), &mut output).unwrap();

        assert_eq!(output, expected);
    }
}