use std::time::Duration;
use super::buffer::InterleavedBuffer;
#[derive(Debug, Default, Clone)]
pub struct Point {
pub time: Duration,
pub min: f32,
pub max: f32,
}
pub fn mixed_down(
buffer: &[f32],
channel_count: usize,
samples_per_sec: u32,
resolution: usize,
) -> Vec<Point> {
let frame_count = buffer.len() / channel_count;
let mut waveform = Vec::with_capacity(frame_count);
if frame_count <= resolution {
for (frame_index, frame) in buffer.frames(channel_count).enumerate() {
let mono_value = frame
.copied()
.fold(0.0, |accum, iter| accum + iter / channel_count as f32);
waveform.push(Point {
time: Duration::from_secs_f32(frame_index as f32 / samples_per_sec as f32),
min: mono_value,
max: mono_value,
});
}
}
else {
let step_size = frame_count as f32 / resolution as f32;
for res_index in 0..resolution {
let mut min = f32::MAX;
let mut max = f32::MIN;
let frame_index = (res_index as f32 * step_size) as usize;
let slice_start = frame_index * channel_count;
let slice_end =
(((res_index + 1) as f32 * step_size) as usize * channel_count).min(buffer.len());
let slice = &buffer[slice_start..slice_end];
for frame in slice.frames(channel_count) {
let mono_value = frame
.copied()
.fold(0.0, |accum, iter| accum + iter / channel_count as f32);
min = min.min(mono_value);
max = max.max(mono_value);
}
let time = Duration::from_secs_f32(frame_index as f32 / samples_per_sec as f32);
waveform.push(Point { time, min, max });
}
}
waveform
}
pub fn multi_channel(
buffer: &[f32],
channel_count: usize,
samples_per_sec: u32,
resolution: usize,
) -> Vec<Vec<Point>> {
let frame_count = buffer.len() / channel_count;
let mut waveform = vec![Vec::with_capacity(frame_count); channel_count];
if frame_count <= resolution {
for (frame_index, frame) in buffer.frames(channel_count).enumerate() {
let time = Duration::from_secs_f32(frame_index as f32 / samples_per_sec as f32);
for (channel_index, value) in frame.enumerate() {
waveform[channel_index].push(Point {
time,
min: *value,
max: *value,
});
}
}
}
else {
let step_size = frame_count as f32 / resolution as f32;
for res_index in 0..resolution {
let mut min = vec![f32::MAX; channel_count];
let mut max = vec![f32::MIN; channel_count];
let frame_index = (res_index as f32 * step_size) as usize;
let slice_start = frame_index * channel_count;
let slice_end =
(((res_index + 1) as f32 * step_size) as usize * channel_count).min(buffer.len());
let slice = &buffer[slice_start..slice_end];
for frame in slice.frames(channel_count) {
for (channel_index, value) in frame.enumerate() {
min[channel_index] = min[channel_index].min(*value);
max[channel_index] = max[channel_index].max(*value);
}
}
let time = Duration::from_secs_f32(frame_index as f32 / samples_per_sec as f32);
for channel_index in 0..channel_count {
waveform[channel_index].push(Point {
time,
min: min[channel_index],
max: max[channel_index],
});
}
}
}
waveform
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn waveform() {
let read_file = |file_path: &str| {
let mut reader = hound::WavReader::open(file_path).unwrap();
let buffer: Vec<f32> = reader.samples::<i32>().map(|v| v.unwrap() as f32).collect();
let specs = reader.spec();
(buffer, specs)
};
let (long_file_buffer, long_file_specs) = read_file("assets/YuaiLoop.wav");
let (small_file_buffer, small_file_specs) = read_file("assets/AKWF_saw.wav");
let mono_downscale_result = mixed_down(
&long_file_buffer,
long_file_specs.channels as usize,
long_file_specs.sample_rate,
1024,
);
assert_eq!(mono_downscale_result.len(), 1024);
let mono_upscale_result = mixed_down(
&small_file_buffer,
small_file_specs.channels as usize,
small_file_specs.sample_rate,
1024,
);
assert!(mono_upscale_result.len() < 1024);
let downscale_result = multi_channel(
&long_file_buffer,
long_file_specs.channels as usize,
long_file_specs.sample_rate,
1024,
);
assert_eq!(downscale_result[0].len(), 1024);
let upscale_result = multi_channel(
&small_file_buffer,
small_file_specs.channels as usize,
small_file_specs.sample_rate,
1024,
);
assert!(upscale_result[0].len() < 1024);
}
}