Sonora: local 3D audio visualizer (initial import)

Three.js + Web Audio FFT visualizer with Constellation, Texture, Frequency
and Modulation views. Vite build, node --test suite.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JABXBRLs9T6nqHKUVwoTHJ
This commit is contained in:
marcuspaico
2026-09-08 15:57:51 +10:00
commit ab748bfb9d
18 changed files with 2282 additions and 0 deletions

30
tests/analysis.test.js Normal file
View File

@@ -0,0 +1,30 @@
import test from 'node:test';
import assert from 'node:assert/strict';
import { analyzeSpectrum } from '../src/analysis.js';
test('silence produces finite zero features and no peaks', () => {
const f = analyzeSpectrum(new Float32Array(32).fill(-Infinity), new Float32Array(64), 48000);
for (const key of ['rms', 'centroid', 'spread', 'entropy', 'flatness']) assert.equal(f[key], 0);
assert.deepEqual(f.peaks, []);
});
test('a single spectral tone has its exact frequency and zero spread and entropy', () => {
const db = new Float32Array(32).fill(-Infinity); db[4] = -6;
const f = analyzeSpectrum(db, new Float32Array([0.5, -0.5]), 48000);
assert.equal(f.centroid, 3000); assert.equal(f.spread, 0); assert.equal(f.entropy, 0);
assert.equal(f.rms, 0.5); assert.equal(f.peaks[0].frequency, 3000);
});
test('uniform spectral power has maximum normalized entropy and flatness', () => {
const f = analyzeSpectrum(new Float32Array(32).fill(-20), new Float32Array(64), 48000);
assert.ok(Math.abs(f.entropy - 1) < 1e-10);
assert.ok(Math.abs(f.flatness - 1) < 1e-10);
assert.ok(Math.abs(f.centroid - 11625) < 1e-8);
});
test('centroid weights spectral power, not decibel values', () => {
const db = new Float32Array(4).fill(-Infinity); db[1] = 0; db[3] = -10;
const f = analyzeSpectrum(db, new Float32Array(8), 8000);
assert.ok(Math.abs(f.centroid - 1300 / 1.1) < 1e-7);
});
test('nonfinite inputs cannot contaminate 3D coordinates', () => {
const f = analyzeSpectrum(new Float32Array([NaN, Infinity, -Infinity, -30]), new Float32Array([NaN, 0]), 48000);
for (const key of ['rms', 'centroid', 'spread', 'entropy', 'flatness']) assert.ok(Number.isFinite(f[key]));
});