use crate::{TimeBase, audio_window_functions, media};
use std::{convert::TryInto, ops::Range};
#[derive(Clone, Debug)]
pub enum AudioData<'a> {
Preloaded(media::PreloadedAudioData<'a>),
Lazy,
}
impl AudioData<'_> {
pub fn duration_in_seconds(&self) -> f32 {
match self {
AudioData::Lazy => 0.,
AudioData::Preloaded(data) => data.duration_in_seconds(),
}
}
pub fn duration_in_frames(&self, tb: &TimeBase) -> usize {
match self {
AudioData::Lazy => 0,
AudioData::Preloaded(data) => data.duration_in_frames(tb.fps),
}
}
pub fn duration_in_samples(&self) -> usize {
match self {
AudioData::Lazy => 0,
AudioData::Preloaded(data) => data.samples.len(),
}
}
pub fn sample_rate(&self) -> u32 {
match self {
AudioData::Lazy => 0,
AudioData::Preloaded(data) => data.sample_rate,
}
}
pub fn get_range(&self, range: Range<usize>) -> Option<&[f32]> {
match self {
AudioData::Preloaded(data) => data.get_range(range),
AudioData::Lazy => None,
}
}
pub fn get_frame_data(&self, length: usize, frame: usize, fps: i64) -> &[f32] {
match self {
AudioData::Preloaded(data) => data.get_frame_data(length, frame, fps).unwrap_or(&[]),
AudioData::Lazy => &[],
}
}
}
#[derive(Debug)]
pub enum SampleSize {
S2,
S4,
S8,
S16,
S32,
S64,
S128,
S256,
S512,
S1024,
}
#[derive(Debug)]
pub struct VisualizeFrameInput<'a> {
pub audio: &'a AudioData<'a>,
pub sample_size: SampleSize,
pub smooth_level: usize,
pub window: Option<audio_window_functions::WindowFunction>,
}
#[allow(clippy::unnecessary_lazy_evaluations)]
fn apply_fft_to_frame(
sample_size: &SampleSize,
window: &Option<audio_window_functions::WindowFunction>,
audio_data: &AudioData,
frame: usize,
fps: i64,
) -> Vec<microfft::Complex32> {
let apply_window = |size| -> Vec<f32> {
let samples_per_frame = audio_data.get_frame_data(size, frame, fps);
if let Some(window_function) = window {
audio_window_functions::apply_window_function(*window_function, samples_per_frame)
} else {
samples_per_frame.to_vec()
}
};
match sample_size {
SampleSize::S2 => {
let mut buffer: [_; 2] = apply_window(2).try_into().unwrap_or_else(|_| [0.0; 2]);
microfft::real::rfft_2(&mut buffer).to_vec()
}
SampleSize::S4 => {
let mut buffer: [_; 4] = apply_window(4).try_into().unwrap_or_else(|_| [0.0; 4]);
microfft::real::rfft_4(&mut buffer).to_vec()
}
SampleSize::S8 => {
let mut buffer: [_; 8] = apply_window(8).try_into().unwrap_or_else(|_| [0.0; 8]);
microfft::real::rfft_8(&mut buffer).to_vec()
}
SampleSize::S16 => {
let mut buffer: [_; 16] = apply_window(16).try_into().unwrap_or_else(|_| [0.0; 16]);
microfft::real::rfft_16(&mut buffer).to_vec()
}
SampleSize::S32 => {
let mut buffer: [_; 32] = apply_window(32).try_into().unwrap_or_else(|_| [0.0; 32]);
microfft::real::rfft_32(&mut buffer).to_vec()
}
SampleSize::S64 => {
let mut buffer: [_; 64] = apply_window(64).try_into().unwrap_or_else(|_| [0.0; 64]);
microfft::real::rfft_64(&mut buffer).to_vec()
}
SampleSize::S128 => {
let mut buffer: [_; 128] = apply_window(128).try_into().unwrap_or_else(|_| [0.0; 128]);
microfft::real::rfft_128(&mut buffer).to_vec()
}
SampleSize::S256 => {
let mut buffer: [_; 256] = apply_window(256).try_into().unwrap_or_else(|_| [0.0; 256]);
microfft::real::rfft_256(&mut buffer).to_vec()
}
SampleSize::S512 => {
let mut buffer: [_; 512] = apply_window(512).try_into().unwrap_or_else(|_| [0.0; 512]);
microfft::real::rfft_512(&mut buffer).to_vec()
}
SampleSize::S1024 => {
let mut buffer: [_; 1024] = apply_window(1024)
.try_into()
.unwrap_or_else(|_| [0.0; 1024]);
microfft::real::rfft_1024(&mut buffer).to_vec()
}
}
}
pub fn get_visualization(
frame: usize,
fps: usize,
VisualizeFrameInput {
sample_size,
audio,
window,
..
}: &VisualizeFrameInput,
) -> Vec<f32> {
let mut spectrum = apply_fft_to_frame(sample_size, window, audio, frame, fps as i64);
spectrum[0].im = 0.0;
spectrum.iter().map(|x| x.norm()).collect::<Vec<f32>>()
}
pub fn center_spectrum_low_frequencies(spectrum: &[f32]) -> Vec<f32> {
let mut pretty_spectrum = vec![0.0; spectrum.len()];
let mid = spectrum.len() / 2 - 1;
for (i, _) in spectrum.iter().enumerate() {
pretty_spectrum[i] = match i {
i if i < mid => spectrum[(mid - i) * 2],
i if i == mid => spectrum[0],
_ => spectrum[(i - mid) * 2 - 1],
};
}
pretty_spectrum
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_prettify_spectrum() {
assert_eq!(
center_spectrum_low_frequencies([1., 2., 3., 4., 5., 6., 7., 8.].as_slice()),
vec![7., 5., 3., 1., 2., 4., 6., 8.]
);
}
}