use std::f32;
#[cfg(feature = "png")]
use std::path::Path;
use crate::errors::SonogramError;
use crate::window_fn;
use crate::SpecCompute;
type WindowFn = fn(usize, usize) -> f32;
pub struct SpecOptionsBuilder {
data: Vec<f32>, sample_rate: u32, channel: u16, scale_factor: Option<f32>, do_normalise: bool, downsample_divisor: Option<usize>,
num_bins: usize, step_size: usize, window_fn: WindowFn, }
impl SpecOptionsBuilder {
pub fn new(num_bins: usize) -> Self {
SpecOptionsBuilder {
data: vec![],
sample_rate: 11025,
channel: 1,
scale_factor: None,
do_normalise: false,
downsample_divisor: None,
num_bins,
window_fn: window_fn::rectangular,
step_size: num_bins,
}
}
#[cfg(feature = "hound")]
pub fn load_data_from_file(self, fname: &Path) -> Result<Self, SonogramError> {
let mut reader = hound::WavReader::open(fname)?;
if 16 != reader.spec().bits_per_sample {
return Err(SonogramError::InvalidCodec);
}
if self.channel > reader.spec().channels {
return Err(SonogramError::InvalidChannel);
}
let data: Vec<i16> = {
let first_sample = self.channel as usize - 1;
let step_size = reader.spec().channels as usize;
let mut s = reader.samples();
for _ in 0..first_sample {
s.next();
}
s.step_by(step_size).map(|x| x.unwrap()).collect()
};
let sample_rate = reader.spec().sample_rate;
Ok(self.load_data_from_memory(data, sample_rate))
}
pub fn load_data_from_memory(mut self, data: Vec<i16>, sample_rate: u32) -> Self {
self.data = data.iter().map(|&x| x as f32 / (i16::MAX as f32)).collect();
self.sample_rate = sample_rate;
self
}
pub fn load_data_from_memory_f32(mut self, data: Vec<f32>, sample_rate: u32) -> Self {
self.data = data;
self.sample_rate = sample_rate;
self
}
pub fn downsample(mut self, divisor: usize) -> Self {
self.downsample_divisor = Some(divisor);
self
}
pub fn channel(mut self, channel: u16) -> Self {
self.channel = channel;
self
}
pub fn normalise(mut self) -> Self {
self.do_normalise = true;
self
}
pub fn scale(mut self, scale_factor: f32) -> Self {
self.scale_factor = Some(scale_factor);
self
}
pub fn set_window_fn(mut self, window_fn: WindowFn) -> Self {
self.window_fn = window_fn;
self
}
pub fn set_step_size(mut self, step_size: usize) -> Self {
self.step_size = step_size;
self
}
pub fn build(mut self) -> Result<SpecCompute, SonogramError> {
if self.data.is_empty() {
return Err(SonogramError::IncompleteData);
}
if self.channel == 0 {
return Err(SonogramError::InvalidChannel);
}
if let Some(divisor) = self.downsample_divisor {
if divisor == 0 {
return Err(SonogramError::InvalidDivisor);
}
if divisor > 1 {
for (j, i) in (0..self.data.len() - divisor).step_by(divisor).enumerate() {
let sum: f32 = self.data[i..i + divisor].iter().fold(0.0, |mut sum, &val| {
sum += val;
sum
});
let avg = sum / (divisor as f32);
self.data[j] = avg;
}
self.data.resize(self.data.len() / divisor, 0.0);
self.sample_rate /= divisor as u32;
}
}
if self.do_normalise {
let max = self
.data
.iter()
.reduce(|max, x| if x > max { x } else { max })
.unwrap();
let norm = 1.0 / max;
for x in self.data.iter_mut() {
*x *= norm;
}
}
if let Some(scale_factor) = self.scale_factor {
for x in self.data.iter_mut() {
*x *= scale_factor;
}
}
Ok(SpecCompute::new(
self.num_bins,
self.step_size,
self.data,
self.window_fn,
))
}
}