Skip to main content

max / audiofiles

Audio/DSP A+: prove the similarity metric + 8-bit decode; hoist STFT scratch Ultra-fuzz --deep, DSP NOTE tail. - similarity: a randomized property test proves feature_distance satisfies the triangle inequality (incl. missing/imputed dims and the non-finite values the ingest guard maps to imputed). The VP-tree's prune correctness depends on this; it was asserted in a doc comment, now enforced by test. - decode: an 8-bit PCM hound-fallback roundtrip test proves the offset-128 / scale-by-2^(bits-1) handling is correct (the audit flagged it unproven; it's now proven-correct, no code change needed). - spectral: hoist the per-frame `windowed` + `spectrum` STFT scratch buffers out of the hot loop (refilled / overwritten each frame); `magnitudes` stays per-frame since it's moved into magnitude_frames for MFCC. 588 core tests pass (2 new); clippy clean. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Author: Max Johnson <me@maxj.phd> · 2026-06-23 16:52 UTC
Commit: 3e04818eb561af5a30ddf037f48e49eccfc57776
Parent: 67b31ad
3 files changed, +101 insertions, -9 deletions
@@ -232,6 +232,37 @@ mod tests {
232 232 }
233 233
234 234 #[test]
235 + fn hound_fallback_decodes_8bit_pcm() {
236 + // The 8-bit hound fallback scaling was unproven (8-bit WAV is stored
237 + // offset-128 unsigned; hound returns it signed when read as i32, and the
238 + // code scales by 2^(bits-1)=128). Prove the roundtrip end to end.
239 + let dir = tempfile::tempdir().unwrap();
240 + let path = dir.path().join("eight.wav");
241 + let spec = hound::WavSpec {
242 + channels: 1,
243 + sample_rate: 8000,
244 + bits_per_sample: 8,
245 + sample_format: hound::SampleFormat::Int,
246 + };
247 + {
248 + let mut w = hound::WavWriter::create(&path, spec).unwrap();
249 + for s in [i8::MIN, 0, i8::MAX, 64, -64] {
250 + w.write_sample(s).unwrap();
251 + }
252 + w.finalize().unwrap();
253 + }
254 + let decoded = decode_wav_hound(&path).unwrap();
255 + assert_eq!(decoded.sample_rate, 8000);
256 + assert_eq!(decoded.channels, 1);
257 + // Scale by 2^7 = 128: MIN -> -1.0, 0 -> 0.0, MAX -> 127/128, etc.
258 + let expected = [-1.0f32, 0.0, 127.0 / 128.0, 64.0 / 128.0, -64.0 / 128.0];
259 + assert_eq!(decoded.samples.len(), expected.len());
260 + for (got, exp) in decoded.samples.iter().zip(expected.iter()) {
261 + assert!((got - exp).abs() < 1e-6, "8-bit decode: got {got}, expected {exp}");
262 + }
263 + }
264 +
265 + #[test]
235 266 fn decoded_audio_field_access() {
236 267 let audio = DecodedAudio {
237 268 samples: vec![0.0, 0.5, -0.5, 1.0],
@@ -102,23 +102,30 @@ fn compute_spectral_inner(
102 102 let mut onset_diffs = Vec::new();
103 103 let mut magnitude_frames = Vec::new();
104 104
105 + // Hoisted STFT scratch, reused every frame: `windowed` is refilled before each
106 + // FFT (realfft also uses it as scratch) and `spectrum` is overwritten by
107 + // `process`. Only `magnitudes` stays per-frame, since it is moved into
108 + // `magnitude_frames` for the later MFCC pass.
109 + let mut windowed: Vec<f64> = vec![0.0; window_size];
110 + let mut spectrum = fft.make_output_vec();
111 +
105 112 let mut pos = 0;
106 113 while pos + window_size <= samples.len() {
107 - // Apply window
108 - let mut windowed: Vec<f64> = samples[pos..pos + window_size]
109 - .iter()
110 - .enumerate()
111 - .map(|(i, &s)| s as f64 * hann[i])
112 - .collect();
114 + // Apply window into the reused buffer.
115 + for (w, (&s, &h)) in windowed
116 + .iter_mut()
117 + .zip(samples[pos..pos + window_size].iter().zip(hann.iter()))
118 + {
119 + *w = s as f64 * h;
120 + }
113 121
114 - // FFT
115 - let mut spectrum = fft.make_output_vec();
122 + // FFT (overwrites `spectrum`).
116 123 if fft.process(&mut windowed, &mut spectrum).is_err() {
117 124 pos += hop_size;
118 125 continue;
119 126 }
120 127
121 - // Magnitude spectrum
128 + // Magnitude spectrum (retained per-frame for MFCC).
122 129 let magnitudes: Vec<f64> = spectrum.iter().map(|c| c.norm()).collect();
123 130
124 131 // Spectral centroid: frequency-weighted average of the magnitude spectrum.
@@ -489,6 +489,60 @@ mod tests {
489 489 use crate::test_helpers::insert_fake_sample;
490 490 use crate::analysis::{self, AnalysisResult};
491 491
492 + /// `feature_distance` is documented as a true weighted-Euclidean metric — the
493 + /// VP-tree's triangle-inequality prune depends on it (a pseudometric could
494 + /// drop a genuine nearest neighbour). Prove the property rather than assert it
495 + /// in prose: over randomized vectors (incl. missing/imputed dims and the
496 + /// non-finite values the ingest guard maps to imputed), the triangle
497 + /// inequality d(a,c) <= d(a,b) + d(b,c) must hold.
498 + #[test]
499 + fn feature_distance_satisfies_triangle_inequality() {
500 + // Deterministic LCG (no rand dep; reproducible). Numerical Recipes constants.
501 + let mut state: u64 = 0x9E3779B97F4A7C15;
502 + let mut next = || {
503 + state = state.wrapping_mul(6364136223846793005).wrapping_add(1442695040888963407);
504 + (state >> 33) as f64 / (1u64 << 31) as f64 // [0, 2)
505 + };
506 + // A dim is None ~1/4 of the time, NaN ~1/16 (exercise the finite guard),
507 + // else a value in roughly the normalized [0,1] range (with some spill).
508 + let mut gen_vec = || {
509 + let mut d = || -> Option<f64> {
510 + let r = next();
511 + if r < 0.5 {
512 + None
513 + } else if r < 0.625 {
514 + Some(f64::NAN)
515 + } else {
516 + Some(next() / 2.0) // ~[0,1)
517 + }
518 + };
519 + FeatureVector {
520 + bpm: d(), duration: d(), lufs: d(), spectral_centroid: d(),
521 + spectral_flatness: d(), spectral_rolloff: d(), zero_crossing_rate: d(),
522 + onset_strength: d(), spectral_bandwidth: d(), centroid_variance: d(),
523 + crest_factor: d(), attack_time: d(),
524 + }
525 + };
526 +
527 + let weights = FeatureWeights::default();
528 + for _ in 0..5000 {
529 + let a = gen_vec();
530 + let b = gen_vec();
531 + let c = gen_vec();
532 + let dab = feature_distance(&a, &b, &weights);
533 + let dbc = feature_distance(&b, &c, &weights);
534 + let dac = feature_distance(&a, &c, &weights);
535 + // All distances finite (the non-finite guard holds).
536 + assert!(dab.is_finite() && dbc.is_finite() && dac.is_finite());
537 + // Triangle inequality with a small float-rounding tolerance.
538 + assert!(
539 + dac <= dab + dbc + 1e-9,
540 + "triangle inequality violated: d(a,c)={dac} > d(a,b)+d(b,c)={}",
541 + dab + dbc
542 + );
543 + }
544 + }
545 +
492 546 fn insert_with_features(db: &Database, hash: &str, bpm: f64, duration: f64) {
493 547 insert_fake_sample(db, hash);
494 548 let result = AnalysisResult {