/** * Demo data: three synthetic event types generated as if they had been * recorded (one long clip per type, events well separated), then pushed * through the same onset detector a real recording would meet. Lets the * whole pipeline be tried without a microphone, and gives the browser * smoke test something deterministic to chew on. */ import { detectOnsets } from './analysis/onsets.ts'; import { mulberry32 } from './bench/rng.ts'; import { FS } from './bench/types.ts'; import type { ClipMeta, EventType } from './store.ts'; export interface DemoClip { typeName: string; color: string; samples: Float32Array; meta: ClipMeta; } const uid = () => Math.random().toString(36).slice(2, 10); export function makeDemoData(eventsPerType = 40): DemoClip[] { const rng = mulberry32(1234); const burstBase = new Float32Array(Math.round(0.18 * FS)); for (let i = 0; i < burstBase.length; i++) burstBase[i] = rng() * 2 - 1; const kinds: { name: string; color: string; gen: (out: Float32Array, at: number) => void }[] = [ { name: 'demo low tone', color: '#e4573d', gen: (out, at) => { const f = 300 * (1 + 0.01 * (rng() - 0.5)), tau = 0.06 * (1 + 0.2 * (rng() - 0.5)); for (let i = 0; i < 0.18 * FS && at + i < out.length; i++) { const t = i / FS; out[at + i] += Math.exp(-t / tau) * Math.sin(2 * Math.PI * f * t) * 0.7; } }, }, { name: 'demo high tone', color: '#3d7de4', gen: (out, at) => { const f = 1400 * (1 + 0.01 * (rng() - 0.5)), tau = 0.025 * (1 + 0.2 * (rng() - 0.5)); for (let i = 0; i < 0.18 * FS && at + i < out.length; i++) { const t = i / FS; out[at + i] += Math.exp(-t / tau) * Math.sin(2 * Math.PI * f * t) * 0.7; } }, }, { name: 'demo burst', color: '#3dbb6e', gen: (out, at) => { const tau = 0.012 * (1 + 0.2 * (rng() - 0.5)); for (let i = 0; i < burstBase.length && at + i < out.length; i++) { const t = i / FS; out[at + i] += Math.exp(-t / tau) * (burstBase[i] + 0.15 * (rng() * 2 - 1)) * 0.6; } }, }, ]; return kinds.map((kind) => { const gap = 0.3; const n = Math.round((0.5 + eventsPerType * gap) * FS); const samples = new Float32Array(n); for (let i = 0; i < n; i++) samples[i] = (rng() * 2 - 1) * 0.002; for (let e = 0; e < eventsPerType; e++) { const at = Math.round((0.25 + e * gap + 0.02 * (rng() - 0.5)) * FS); kind.gen(samples, at); } const sens = 0.6; const onsets = detectOnsets(samples, FS, sens); const meta: ClipMeta = { id: uid(), sens, onsets, excluded: [] }; return { typeName: kind.name, color: kind.color, samples, meta }; }); } export function demoTypes(clips: DemoClip[]): EventType[] { return clips.map((c) => ({ id: uid(), name: c.typeName, color: c.color, clips: [c.meta] })); }