use std::fmt;
use md5::Digest;
use super::arrayutils::deinterleave;
use super::arrayutils::find_min_and_max;
use super::arrayutils::le_bytes_to_i32s;
use super::constant::MAX_BLOCK_SIZE;
use super::constant::MAX_CHANNELS;
use super::constant::MIN_BLOCK_SIZE;
use super::error::verify_range;
use super::error::verify_true;
use super::error::SourceError;
use super::error::VerifyError;
pub trait Fill {
fn fill_interleaved(&mut self, interleaved: &[i32]) -> Result<(), SourceError>;
fn fill_le_bytes(&mut self, bytes: &[u8], bytes_per_sample: usize) -> Result<(), SourceError>;
}
impl<T: Fill, U: Fill> Fill for (T, U) {
#[inline]
fn fill_interleaved(&mut self, interleaved: &[i32]) -> Result<(), SourceError> {
self.0.fill_interleaved(interleaved)?;
self.1.fill_interleaved(interleaved)
}
#[inline]
fn fill_le_bytes(&mut self, bytes: &[u8], bytes_per_sample: usize) -> Result<(), SourceError> {
self.0.fill_le_bytes(bytes, bytes_per_sample)?;
self.1.fill_le_bytes(bytes, bytes_per_sample)
}
}
impl<T> Fill for &mut T
where
T: Fill,
{
#[inline]
fn fill_interleaved(&mut self, interleaved: &[i32]) -> Result<(), SourceError> {
T::fill_interleaved(self, interleaved)
}
#[inline]
fn fill_le_bytes(&mut self, bytes: &[u8], bytes_per_sample: usize) -> Result<(), SourceError> {
T::fill_le_bytes(self, bytes, bytes_per_sample)
}
}
#[derive(Clone, Debug)]
pub struct FrameBuf {
samples: Vec<i32>,
size: usize,
filled_size: usize,
readbuf: Vec<i32>,
}
impl FrameBuf {
pub(crate) fn new_stereo_buffer() -> Self {
Self {
samples: vec![0i32; 256 * 2],
size: 256,
filled_size: 0,
readbuf: vec![],
}
}
pub fn with_size(channels: usize, size: usize) -> Result<Self, VerifyError> {
verify_range!("FrameBuf::with_size (channels)", channels, 1..=MAX_CHANNELS)?;
verify_range!(
"FrameBuf::with_size (block size)",
size,
MIN_BLOCK_SIZE..=MAX_BLOCK_SIZE
)?;
Ok(Self {
samples: vec![0i32; size * channels],
size,
filled_size: 0,
readbuf: vec![],
})
}
pub const fn size(&self) -> usize {
self.size
}
pub const fn filled_size(&self) -> usize {
self.filled_size
}
pub(crate) fn fill_stereo_with_iter<I>(&mut self, iter: I)
where
I: Iterator<Item = (i32, i32)>,
{
assert_eq!(2, self.channels());
let (m_slice, s_slice) = self.samples.split_at_mut(self.size);
self.filled_size = 0;
let dest_iter = m_slice.iter_mut().zip(s_slice.iter_mut());
for ((m, s), (dest_m, dest_s)) in iter.take(self.size).zip(dest_iter) {
*dest_m = m;
*dest_s = s;
self.filled_size += 1;
}
}
pub fn resize(&mut self, new_size: usize) {
let channels = self.channels();
self.size = new_size;
self.samples.resize(self.size * channels, 0i32);
}
pub fn channels(&self) -> usize {
self.samples.len() / self.size
}
pub(crate) fn channel_slice(&self, ch: usize) -> &[i32] {
&self.samples[ch * self.size..(ch * self.size + self.filled_size)]
}
#[cfg(test)]
pub(crate) fn raw_slice(&self) -> &[i32] {
&self.samples
}
pub(crate) fn verify_samples(&self, bits_per_sample: usize) -> Result<(), VerifyError> {
let max_allowed = (1i32 << (bits_per_sample - 1)) - 1;
let min_allowed = -(1i32 << (bits_per_sample - 1));
for ch in 0..self.channels() {
let (min, max) = find_min_and_max::<64>(self.channel_slice(ch), 0i32);
if min < min_allowed || max > max_allowed {
return Err(VerifyError::new(
"input.framebuf",
&format!("input sample must be in the range of bits={bits_per_sample}"),
));
}
}
Ok(())
}
}
impl Fill for FrameBuf {
fn fill_interleaved(&mut self, interleaved: &[i32]) -> Result<(), SourceError> {
let stride = self.size();
let channels = self.channels();
deinterleave(interleaved, channels, stride, &mut self.samples);
self.filled_size = interleaved.len() / channels;
Ok(())
}
#[inline]
fn fill_le_bytes(&mut self, bytes: &[u8], bytes_per_sample: usize) -> Result<(), SourceError> {
let sample_count = bytes.len() / bytes_per_sample;
self.readbuf.resize(sample_count, 0);
le_bytes_to_i32s(bytes, &mut self.readbuf, bytes_per_sample);
let stride = self.size();
let channels = self.channels();
deinterleave(&self.readbuf, self.channels(), stride, &mut self.samples);
self.filled_size = sample_count / channels;
Ok(())
}
}
#[derive(Clone)]
pub struct Context {
md5: md5::Md5,
bytes_per_sample: usize,
channels: usize,
sample_count: usize,
frame_count: usize,
}
impl Context {
pub fn new(bits_per_sample: usize, channels: usize) -> Self {
let bytes_per_sample = bits_per_sample.div_ceil(8);
assert!(
bytes_per_sample <= 4,
"bits_per_sample={bits_per_sample} cannot be larger than 32."
);
Self {
md5: md5::Md5::new(),
bytes_per_sample,
channels,
sample_count: 0,
frame_count: 0,
}
}
#[inline]
pub fn bytes_per_sample(&self) -> usize {
self.bytes_per_sample
}
#[inline]
#[allow(clippy::unnecessary_lazy_evaluations)] pub fn current_frame_number(&self) -> Option<usize> {
(self.frame_count > 0).then(|| self.frame_count - 1)
}
#[inline]
pub fn md5_digest(&self) -> [u8; 16] {
self.md5.clone().finalize().into()
}
#[inline]
pub fn total_samples(&self) -> usize {
self.sample_count
}
}
impl Fill for Context {
fn fill_interleaved(&mut self, interleaved: &[i32]) -> Result<(), SourceError> {
if interleaved.is_empty() {
return Ok(());
}
for v in interleaved {
self.md5.update(&v.to_le_bytes()[0..self.bytes_per_sample]);
}
self.sample_count += interleaved.len() / self.channels;
self.frame_count += 1;
Ok(())
}
#[inline]
fn fill_le_bytes(&mut self, bytes: &[u8], bytes_per_sample: usize) -> Result<(), SourceError> {
if bytes.is_empty() {
return Ok(());
}
self.md5.update(bytes);
self.sample_count += bytes.len() / self.channels / bytes_per_sample;
self.frame_count += 1;
Ok(())
}
}
impl fmt::Debug for Context {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let digest = format!("{:?}", self.md5.clone().finalize());
f.debug_struct("Context")
.field("bytes_per_sample", &self.bytes_per_sample)
.field("channels", &self.channels)
.field("sample_count", &self.sample_count)
.field("frame_count", &self.frame_count)
.field("md5", &digest)
.finish()
}
}
pub trait Source {
fn channels(&self) -> usize;
fn bits_per_sample(&self) -> usize;
fn sample_rate(&self) -> usize;
fn read_samples<F: Fill>(
&mut self,
block_size: usize,
dest: &mut F,
) -> Result<usize, SourceError>;
fn len_hint(&self) -> Option<usize> {
None
}
}
impl<T: Source> Source for &mut T {
fn channels(&self) -> usize {
T::channels(self)
}
fn bits_per_sample(&self) -> usize {
T::bits_per_sample(self)
}
fn sample_rate(&self) -> usize {
T::sample_rate(self)
}
fn read_samples<F: Fill>(
&mut self,
block_size: usize,
dest: &mut F,
) -> Result<usize, SourceError> {
T::read_samples(self, block_size, dest)
}
fn len_hint(&self) -> Option<usize> {
T::len_hint(self)
}
}
pub trait Seekable: Source {
fn is_empty(&self) -> bool {
self.len() == 0
}
fn len(&self) -> usize;
fn read_samples_from<F: Fill>(
&mut self,
offset: usize,
block_size: usize,
context: &mut F,
) -> Result<usize, SourceError>;
}
impl<T: Seekable> Seekable for &mut T {
fn is_empty(&self) -> bool {
T::is_empty(self)
}
fn len(&self) -> usize {
T::len(self)
}
fn read_samples_from<F: Fill>(
&mut self,
offset: usize,
block_size: usize,
context: &mut F,
) -> Result<usize, SourceError> {
T::read_samples_from(self, offset, block_size, context)
}
}
#[derive(Clone, Debug)]
#[allow(clippy::module_name_repetitions)]
pub struct MemSource {
channels: usize,
bits_per_sample: usize,
sample_rate: usize,
samples: Vec<i32>,
read_head: usize,
}
impl MemSource {
pub fn from_samples(
samples: &[i32],
channels: usize,
bits_per_sample: usize,
sample_rate: usize,
) -> Self {
Self {
channels,
bits_per_sample,
sample_rate,
samples: samples.to_owned(),
read_head: 0,
}
}
pub fn as_slice(&self) -> &[i32] {
&self.samples
}
}
impl Source for MemSource {
fn channels(&self) -> usize {
self.channels
}
fn bits_per_sample(&self) -> usize {
self.bits_per_sample
}
fn sample_rate(&self) -> usize {
self.sample_rate
}
fn read_samples<F: Fill>(
&mut self,
block_size: usize,
dest: &mut F,
) -> Result<usize, SourceError> {
self.read_samples_from(self.read_head, block_size, dest)
}
fn len_hint(&self) -> Option<usize> {
Some(self.len())
}
}
impl Seekable for MemSource {
fn len(&self) -> usize {
self.samples.len() / self.channels()
}
fn read_samples_from<F: Fill>(
&mut self,
offset: usize,
block_size: usize,
dest: &mut F,
) -> Result<usize, SourceError> {
let to_read = block_size * self.channels;
let begin = std::cmp::min(offset * self.channels, self.samples.len());
let end = std::cmp::min(offset * self.channels + to_read, self.samples.len());
let src = &self.samples[begin..end];
dest.fill_interleaved(src)?;
let read_samples = (end - begin) / self.channels;
self.read_head += read_samples;
Ok(read_samples)
}
}
#[cfg(test)]
#[allow(clippy::pedantic, clippy::nursery, clippy::needless_range_loop)]
mod tests {
use super::*;
#[test]
fn reading_and_deinterleaving() {
let mut signal = vec![];
let block_size = 512;
let channels = 4;
for t in 0..block_size {
for _ch in 0..channels {
signal.push(t as i32);
}
}
let mut src = MemSource::from_samples(&signal, channels, 16, 16000);
let mut framebuf_and_ctx = (
FrameBuf::with_size(channels, block_size).unwrap(),
Context::new(16, channels),
);
let read = src
.read_samples_from(0, block_size, &mut framebuf_and_ctx)
.expect("Read error");
assert_eq!(read, block_size);
let (framebuf, _ctx) = framebuf_and_ctx;
let mut head = 0;
for _ch in 0..channels {
for t in 0..block_size {
assert_eq!(framebuf.raw_slice()[head], t as i32);
head += 1;
}
}
}
#[test]
fn sequential_read() {
let mut signal = vec![];
let total_size = 1100;
let channels = 3;
for t in 0..total_size {
for ch in 0..channels {
let sign: i32 = if ch == 0 { 1 } else { -1 };
signal.push(sign * t);
}
}
let block_size = 128;
let mut src = MemSource::from_samples(&signal, channels, 16, 16000);
let ctx = Context::new(16, channels);
let framebuf = FrameBuf::with_size(channels, block_size).unwrap();
let mut framebuf_and_ctx = (framebuf, ctx);
for step in 0..8 {
let read = src
.read_samples(block_size, &mut framebuf_and_ctx)
.expect("Read error");
assert_eq!(read, 128);
assert_eq!(src.read_head, 128 * (step + 1));
for t in 0..block_size {
assert_eq!(
framebuf_and_ctx.0.channel_slice(0)[t],
(block_size * step + t) as i32
);
assert_eq!(
framebuf_and_ctx.0.channel_slice(1)[t],
-((block_size * step + t) as i32)
);
}
}
let read = src
.read_samples(block_size, &mut framebuf_and_ctx)
.expect("Read error");
assert_eq!(read, 76);
for t in 0..76 {
assert_eq!(framebuf_and_ctx.0.channel_slice(0)[t], (1024 + t) as i32);
assert_eq!(framebuf_and_ctx.0.channel_slice(1)[t], -((1024 + t) as i32));
assert_eq!(framebuf_and_ctx.0.channel_slice(2)[t], -((1024 + t) as i32));
}
}
#[test]
fn md5_computation() {
let mut ctx = Context::new(16, 2);
ctx.fill_interleaved(&[0i32; 32 * 2])
.expect("update failed");
assert_eq!(
ctx.md5_digest(),
[
0xF0, 0x9F, 0x35, 0xA5, 0x63, 0x78, 0x39, 0x45, 0x8E, 0x46, 0x2E, 0x63, 0x50, 0xEC,
0xBC, 0xE4
]
);
let mut ctx = Context::new(16, 2);
ctx.fill_interleaved(&[0xABCDi32; 32 * 2])
.expect("update failed");
assert_eq!(
ctx.md5_digest(),
[
0x02, 0x3D, 0x3A, 0xE9, 0x26, 0x0B, 0xB0, 0xC9, 0x51, 0xF6, 0x5B, 0x25, 0x24, 0x62,
0xB1, 0xFA
]
);
}
}
#[cfg(all(test, feature = "simd-nightly"))]
mod bench {
use super::*;
extern crate test;
use test::bench::Bencher;
use test::black_box;
#[bench]
fn feeding_bytes_to_context(b: &mut Bencher) {
let (bytes_per_sample, channels, block_size) = (2, 2, 4096);
let mut ctx = Context::new(bytes_per_sample, channels);
let signal_bytes = vec![0u8; bytes_per_sample * channels * block_size];
b.iter(|| ctx.fill_le_bytes(black_box(&signal_bytes), bytes_per_sample));
}
}