New sibling repo unblocking omni-cortex's two long-standing audio gaps (README.md:570 there): "the production FFT-based encoder" and "the format-decoding front end for arbitrary audio files". Three crates, clawsync-style workspace conventions: - clawaudio-dsp: pure spectral-analysis primitives (Hann window, STFT via rustfft, triangular mel filterbank, log-mel spectrogram). No I/O, no file formats, decoupled from any caller's types -- operates on &[f32] mono PCM + a sample rate. SpectrogramComputer::compute_aggregate is zero-alloc after construction (reused FFT/power scratch buffers), mirroring the hot-path contract omni-cortex's encoder traits require. - clawaudio-decode: WAV (hound) / FLAC (claxon) / OGG-Vorbis (lewton) decode to interleaved f32 PCM, plus fixed-target-rate resampling (rubato). All pure Rust, no C bindings. Deliberately NOT symphonia -- its MPL-2.0 license fails permissive-license-only allow-lists (e.g. omni-cortex's cargo-deny config already only allows MIT/Apache-2.0/ BSD/etc). MP3 deferred -- no sufficiently mature pure-Rust decoder was vetted for this pass. - clawaudio: facade re-exporting both, plus SpectrogramEncoder<const DIM: usize> -- the const-generic drop-in type a consumer's own encoder trait wraps. Final f32->fixed-point quantization is left to the caller, since that's a consumer-specific convention, not something a project-agnostic DSP library should own. Verified end-to-end (56 tests, all passing, cargo fmt/clippy -D warnings clean workspace-wide): decode -> resample -> spectrogram for real ffmpeg-generated WAV/FLAC/OGG fixtures (tiny synthesized sine tones, checked in as tests/fixtures/*, not real/copyrighted audio), plus the correctness properties omni-cortex's existing GoertzelMelEncoder test suite already established (distinct tones dissimilar, tone peaks in the right mel band, silence near floor, deterministic). compute_aggregate benchmarks at ~88us for a 500ms/16kHz/16-mel chunk on real hardware -- comfortably real-time. Co-Authored-By: Claude Sonnet 5 <[email protected]>
45 lines
1.5 KiB
Rust
45 lines
1.5 KiB
Rust
//! Criterion bench for [`SpectrogramComputer::compute_aggregate`] — the
|
|
//! hot per-chunk path. Guards the zero-alloc-after-construction property:
|
|
//! a future change that reintroduces allocation into this loop should
|
|
//! show up here as a clear regression.
|
|
#![allow(clippy::expect_used, clippy::unwrap_used)]
|
|
|
|
use std::hint::black_box;
|
|
|
|
use clawaudio_dsp::{SpectrogramComputer, SpectrogramConfig};
|
|
use criterion::{Criterion, criterion_group, criterion_main};
|
|
|
|
fn sine(freq: f32, sample_rate: u32, n: usize) -> Vec<f32> {
|
|
(0..n)
|
|
.map(|i| (std::f32::consts::TAU * freq * i as f32 / sample_rate as f32).sin())
|
|
.collect()
|
|
}
|
|
|
|
fn bench_compute_aggregate(c: &mut Criterion) {
|
|
let cfg = SpectrogramConfig {
|
|
sample_rate: 16_000,
|
|
fft_size: 512,
|
|
hop_size: 256,
|
|
mel_bins: 16,
|
|
f_low_hz: 80.0,
|
|
f_high_hz: 8_000.0,
|
|
};
|
|
let mut sc = SpectrogramComputer::new(cfg).expect("valid config");
|
|
// ~0.5s chunk at 16kHz, a realistic single-chunk size.
|
|
let mono = sine(440.0, 16_000, 8_000);
|
|
let mut out = vec![0.0f32; sc.mel_bins()];
|
|
|
|
let mut group = c.benchmark_group("dsp");
|
|
group.bench_function("compute_aggregate_16khz_500ms_16mel", |b| {
|
|
b.iter(|| {
|
|
sc.compute_aggregate(black_box(&mono), black_box(&mut out))
|
|
.expect("compute_aggregate");
|
|
black_box(&out);
|
|
});
|
|
});
|
|
group.finish();
|
|
}
|
|
|
|
criterion_group!(benches, bench_compute_aggregate);
|
|
criterion_main!(benches);
|