// AVA — Beat tab: the beat detection & prediction lab. // // Large beat indicator + SET BEAT tap-tempo button, live confidence meter, // sensitivity / min-interval sliders and the prediction switch (all persisted). 'use client' import { motion } from 'framer-motion' import { Timer } from 'lucide-react' import { Badge } from '@/components/ui/badge' import { Label } from '@/components/ui/label' import { Separator } from '@/components/ui/separator' import { Slider } from '@/components/ui/slider' import { Switch } from '@/components/ui/switch' import { BeatDot } from '@/components/ava/beat-dot' import { tapBeat } from '@/components/ava/audio-source' import { useAvaStore } from '@/lib/store' import { cn } from '@/lib/utils' const SOURCE_LABELS: Record = { audio: 'AUDIO', predicted: 'PRED', tap: 'TAP', none: 'NONE', } export function BeatTab() { const stats = useAvaStore((s) => s.stats) const lastTap = useAvaStore((s) => s.lastTap) const beatFlash = useAvaStore((s) => s.beatFlash) const beatConfig = useAvaStore((s) => s.beatConfig) const setBeatValue = useAvaStore((s) => s.setBeatValue) const beat = stats?.beat const bpm = beat?.bpm ?? 0 const confidence = beat?.confidence ?? 0 const beatCount = stats?.beatCount ?? 0 const lastSource = beatFlash?.source ?? 'none' const tapFeedback = lastTap ? lastTap.locked ? `Locked: ${lastTap.bpm.toFixed(1)} BPM` : `Tap ${lastTap.tapCount}… keep tapping` : 'Tap along with the music — Space works too' return (
{/* big dot + readouts */}
{bpm > 0 ? bpm.toFixed(1) : '—'} BPM
{SOURCE_LABELS[lastSource] ?? lastSource.toUpperCase()} {beatCount} beats
{/* SET BEAT */} SET BEAT tap to the music · space works too

{tapFeedback}

{/* confidence */}
Tempo lock confidence {Math.round(confidence * 100)}%
{/* detector controls */}
×{beatConfig.threshold.toFixed(2)}
setBeatValue('threshold', v)} aria-label="Beat sensitivity (threshold)" className="py-1.5" />

A beat fires when the bass energy exceeds this multiple of the recent average — lower = more sensitive.

{beatConfig.minInterval} ms
setBeatValue('minInterval', v)} aria-label="Minimum beat interval" className="py-1.5" />

Fastest allowed beat rate — raise it to ignore double-hits and rolls.

Keeps the beat alive when the bass drops out.

setBeatValue('prediction', v)} aria-label="Beat prediction" className="data-[state=checked]:bg-emerald-400" />

How it works: beats are detected from the smoothed bass energy crossing the sensitivity threshold. Consistent intervals lock a tempo (the confidence meter), and a phase-locked predictor then keeps firing beats on the learned grid — shown as a hollow ring with a PRED badge — whenever the bass disappears. Set Beat teaches the tempo directly: tap along and two or more taps lock the period and phase to your rhythm.

) }