use criterion::{criterion_group, criterion_main, BenchmarkId, Criterion, Throughput};
use ruststft::spectrum::{magnitude_into, power_to_db};
use ruststft::{Complex, Stft, Symmetry, Window, WindowFunction};
fn signal_f32(seconds: usize) -> Vec<f32> {
let fs = 44_100usize;
(0..fs * seconds)
.map(|n| {
let t = n as f32 / fs as f32;
0.5 * (2.0 * std::f32::consts::PI * 440.0 * t).sin()
})
.collect()
}
fn signal_f64(seconds: usize) -> Vec<f64> {
let fs = 44_100usize;
(0..fs * seconds)
.map(|n| {
let t = n as f64 / fs as f64;
0.5 * (2.0 * std::f64::consts::PI * 440.0 * t).sin()
})
.collect()
}
fn bench_window(c: &mut Criterion) {
let mut group = c.benchmark_group("window_generation");
for &len in &[1024usize, 4096] {
for (label, func) in [
("hann", WindowFunction::Hann),
("blackman_harris", WindowFunction::BlackmanHarris),
("kaiser", WindowFunction::Kaiser { beta: 8.6 }),
("gaussian", WindowFunction::Gaussian { std: 128.0 }),
] {
group.bench_with_input(BenchmarkId::new(label, len), &len, |b, &len| {
b.iter(|| Window::<f64>::new(func, len, Symmetry::Periodic));
});
}
}
group.finish();
}
fn bench_forward(c: &mut Criterion) {
let mut group = c.benchmark_group("forward_spectrogram");
let s32 = signal_f32(10);
let s64 = signal_f64(10);
group.throughput(Throughput::Elements(s32.len() as u64));
for &win in &[1024usize, 4096] {
group.bench_with_input(BenchmarkId::new("f32", win), &win, |b, &win| {
let mut stft = Stft::builder()
.window(Window::<f32>::hann(win))
.hop_size(win / 4)
.build()
.unwrap();
b.iter(|| stft.spectrogram(&s32));
});
group.bench_with_input(BenchmarkId::new("f64", win), &win, |b, &win| {
let mut stft = Stft::builder()
.window(Window::<f64>::hann(win))
.hop_size(win / 4)
.build()
.unwrap();
b.iter(|| stft.spectrogram(&s64));
});
}
group.finish();
}
fn bench_streaming(c: &mut Criterion) {
let signal = signal_f32(10);
c.bench_function("streaming_columns_1024_f32", |b| {
b.iter(|| {
let mut stft = Stft::builder()
.window(Window::<f32>::hann(1024))
.hop_size(512)
.build()
.unwrap();
let mut column = vec![Complex::new(0.0f32, 0.0); stft.n_freqs()];
for chunk in signal.chunks(4096) {
stft.append(chunk);
while stft.ready() {
stft.process_into(&mut column).unwrap();
stft.step();
}
}
});
});
}
fn bench_round_trip(c: &mut Criterion) {
let signal = signal_f64(5);
c.bench_function("round_trip_1024_f64", |b| {
let mut stft = Stft::builder()
.window(Window::<f64>::hann(1024))
.hop_size(256)
.build()
.unwrap();
b.iter(|| {
let spec = stft.spectrogram(&signal);
let istft = stft.inverse().unwrap();
istft.reconstruct(&spec).unwrap()
});
});
}
fn bench_spectrum(c: &mut Criterion) {
let signal = signal_f64(5);
let mut stft = Stft::builder()
.window(Window::<f64>::hann(1024))
.hop_size(256)
.build()
.unwrap();
let spec = stft.spectrogram(&signal);
let mut group = c.benchmark_group("spectrum");
group.bench_function("magnitude_full", |b| {
let mut mag = vec![0.0f64; spec.n_freqs()];
b.iter(|| {
for col in spec.columns() {
magnitude_into(col, &mut mag);
}
});
});
group.bench_function("power_to_db_full", |b| {
let baseline: Vec<f64> = spec.as_flat().iter().map(|z| z.norm_sqr()).collect();
b.iter_batched_ref(
|| baseline.clone(),
|powers| power_to_db(powers, 1.0, Some(80.0)),
criterion::BatchSize::SmallInput,
);
});
group.finish();
}
#[cfg(feature = "mel")]
fn bench_mel(c: &mut Criterion) {
use ruststft::mel::{DctII, MelFilterBank, MelScale};
use ruststft::spectrum::power;
let fs = 16_000.0;
let n_fft = 1024usize;
let signal: Vec<f64> = (0..(fs as usize) * 5)
.map(|n| {
let t = n as f64 / fs;
0.5 * (2.0 * std::f64::consts::PI * 440.0 * t).sin()
})
.collect();
let mut stft = Stft::builder()
.window(Window::<f64>::hann(n_fft))
.hop_size(n_fft / 4)
.build()
.unwrap();
let spec = stft.spectrogram(&signal);
let bank = MelFilterBank::<f64>::new(40, n_fft, fs, 0.0, fs / 2.0, MelScale::Slaney);
let dct = DctII::<f64>::new(40, 13);
let mut group = c.benchmark_group("mel");
group.bench_function("logmel_mfcc_full", |b| {
let mut mel = vec![0.0f64; 40];
let mut mfcc = vec![0.0f64; 13];
b.iter(|| {
for col in spec.columns() {
let p = power(col);
bank.transform_into(&p, &mut mel);
power_to_db(&mut mel, 1.0, None);
dct.transform_into(&mel, &mut mfcc);
}
});
});
group.finish();
}
#[cfg(feature = "mel")]
criterion_group!(
benches,
bench_window,
bench_forward,
bench_streaming,
bench_round_trip,
bench_spectrum,
bench_mel
);
#[cfg(not(feature = "mel"))]
criterion_group!(
benches,
bench_window,
bench_forward,
bench_streaming,
bench_round_trip,
bench_spectrum
);
criterion_main!(benches);