From 6c94102afc70ce28eee3d17aad997a056aaf9195 Mon Sep 17 00:00:00 2001 From: Silas Bartha Date: Fri, 11 Oct 2024 19:01:52 -0400 Subject: gltf, wav, and binary codecs --- src/wav/lsb.rs | 153 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 153 insertions(+) create mode 100644 src/wav/lsb.rs (limited to 'src/wav/lsb.rs') diff --git a/src/wav/lsb.rs b/src/wav/lsb.rs new file mode 100644 index 0000000..17e6e28 --- /dev/null +++ b/src/wav/lsb.rs @@ -0,0 +1,153 @@ +use std::io::Cursor; + +use crate::{Codec, Error}; + +use hound::{self, Sample, SampleFormat, WavReader, WavSamples, WavWriter}; +use itertools::{Chunk, Itertools}; +use num_traits::{FromBytes, ToBytes}; + +/// A Least-Significant Bit (LSB) Codec for WAV files. Stores 1 bit of payload data in each sample +/// of a WAV file. Supported sample formats are 8, 16, and 32-bit PCM, and 32-bit float. +pub struct LsbCodec; + +impl Codec for LsbCodec { + fn encode(&self, carrier: &[u8], payload: &[u8]) -> Result, crate::Error> { + if let Ok(mut reader) = hound::WavReader::new(Cursor::new(carrier)) { + let mut encoded = vec![]; + { + let mut writer = WavWriter::new(Cursor::new(&mut encoded), reader.spec()).unwrap(); + match reader.spec().sample_format { + hound::SampleFormat::Float => { + encode::(payload, &mut reader, &mut writer); + } + hound::SampleFormat::Int => { + match reader.spec().bits_per_sample { + 8 => { + encode::(payload, &mut reader, &mut writer); + } + 16 => { + encode::(payload, &mut reader, &mut writer); + } + 32 => { + encode::(payload, &mut reader, &mut writer); + } + _ => return Err(Error::DataInvalid( + "Provided WAV data has an unsupported number of bits per sample." + .into(), + )), + } + } + } + writer.flush().unwrap(); + } + Ok(encoded) + } else { + Err(Error::DataInvalid( + "Could not create WAV reader from provided data".into(), + )) + } + } + + fn decode(&self, encoded: &[u8]) -> Result<(Vec, Vec), crate::Error> { + if let Ok(mut reader) = hound::WavReader::new(Cursor::new(encoded)) { + let decoded = match reader.spec().sample_format { + SampleFormat::Float => decode::(&mut reader)?, + SampleFormat::Int => match reader.spec().bits_per_sample { + 8 => decode::(&mut reader)?, + 16 => decode::(&mut reader)?, + 32 => decode::(&mut reader)?, + _ => return Err(Error::DataNotEncoded), + }, + }; + Ok((encoded.to_vec(), decoded)) + } else { + Err(Error::DataInvalid( + "Could not create WAV reader from provided data".into(), + )) + } + } +} + +fn encode( + payload: &[u8], + reader: &mut WavReader>, + writer: &mut WavWriter>>, +) where + T: Sample + ToBytes + FromBytes, +{ + let payload_len = ((payload.len() + size_of::()) as u32).to_le_bytes(); + let mut payload_iter = payload_len.iter().chain(payload.iter()); + for sample_chunk in &reader.samples::().chunks(8) { + match payload_iter.next() { + Some(payload_byte) => { + encode_byte(writer, *payload_byte, sample_chunk); + } + None => { + for sample in sample_chunk { + writer.write_sample(sample.unwrap()).unwrap(); + } + } + } + } +} + +fn encode_byte( + writer: &mut WavWriter>>, + payload_byte: u8, + sample_chunk: Chunk, T>>, +) where + T: Sample + ToBytes + FromBytes, +{ + for (i, sample) in sample_chunk.enumerate() { + let sample = sample.unwrap(); + let mut sample_bytes = sample.to_le_bytes(); + let payload_bit = (payload_byte >> (7 - i)) & 0b0000_0001; + sample_bytes[1] &= 0b1111_1110; + sample_bytes[1] |= payload_bit; + writer + .write_sample(T::from_le_bytes(&sample_bytes)) + .unwrap(); + } +} + +fn decode(reader: &mut WavReader>) -> Result, Error> +where + T: Sample + ToBytes + FromBytes, +{ + let mut decoded = vec![]; + let mut length_bytes = [0_u8; 4]; + for (i, sample_chunk) in reader + .samples::() + .take(8 * 4) + .chunks(8) + .into_iter() + .enumerate() + { + for (j, sample) in sample_chunk.enumerate() { + let sample = sample.unwrap(); + let sample_bytes = sample.to_le_bytes(); + let payload_bit = (sample_bytes[1] & 0b0000_0001) << (7 - j); + length_bytes[i] |= payload_bit; + } + } + + let payload_length = u32::from_le_bytes(length_bytes) as usize - size_of::(); + if payload_length > reader.samples::().len() { + return Err(Error::DataNotEncoded); + } + + for sample_chunk in &reader.samples::().chunks(8) { + let mut byte = 0_u8; + for (i, sample) in sample_chunk.enumerate() { + let sample = sample.unwrap(); + let sample_bytes = sample.to_le_bytes(); + let payload_bit = (sample_bytes[1] & 0b0000_0001) << (7 - i); + byte |= payload_bit; + } + decoded.push(byte); + if decoded.len() >= payload_length { + break; + } + } + Ok(decoded) +} -- cgit v1.2.3