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) }