diff --git a/src/App.jsx b/src/App.jsx index c85db9d..2a15ee1 100644 --- a/src/App.jsx +++ b/src/App.jsx @@ -1,6 +1,7 @@ import { useState, useCallback, useRef, useEffect } from 'react' import AudioCapture from './components/AudioCapture' import ProgressionBanner from './components/ProgressionBanner' +import ProgressionSuggestions from './components/ProgressionSuggestions' import Fretboard from './components/Fretboard' import Tuner from './components/Tuner' import Piano from './components/Piano' @@ -25,6 +26,10 @@ export default function App() { const [instrument, setInstrument] = useState('guitar') // 'guitar' | 'piano' const [showTuner, setShowTuner] = useState(false) + // ── BPM estimation from chord-change intervals ──────────────────────────── + const [bpm, setBpm] = useState(null) + const chordTimestampsRef = useRef([]) + // ── Key: auto-detected + optional lock ─────────────────────────────────── const [keyInfo, setKeyInfo] = useState(null) // auto-detected const [lockedKey, setLockedKey] = useState(null) // { root, mode } or null @@ -77,12 +82,14 @@ export default function App() { pendingKeyRef.current = null chromaIdxRef.current = 0 chromaRingRef.current = Array.from({ length: CHROMA_SMOOTH }, () => new Float32Array(12)) + chordTimestampsRef.current = [] setKeyInfo(null) setLockedKey(null) effectiveKeyRef.current = null setChordHistory([]) setDetectedProgression(null) setTopKeyCandidates([]) + setBpm(null) } // ── Key lock handlers ───────────────────────────────────────────────────── @@ -185,6 +192,22 @@ export default function App() { return [...prev.slice(-30), winner] }) + // BPM: track chord commit timestamps and estimate tempo + const now = performance.now() + const ts = chordTimestampsRef.current + ts.push(now) + if (ts.length > 8) ts.shift() + if (ts.length >= 3) { + const intervals = [] + for (let i = 1; i < ts.length; i++) intervals.push(ts[i] - ts[i - 1]) + const avg = intervals.reduce((a, b) => a + b) / intervals.length + let estimated = Math.round(60000 / avg) + // Chord changes are often every 2 beats — normalise to 55–220 BPM range + while (estimated < 55) estimated *= 2 + while (estimated > 220) estimated /= 2 + setBpm(estimated) + } + // Inject chord tones into note history with boost weight so key detection // anchors to confirmed chords rather than transient melody notes const chordPCs = getChordTones(winner) @@ -292,11 +315,13 @@ export default function App() { keyInfo={effectiveKey} detectedProgression={detectedProgression} currentChord={currentChord} + bpm={bpm} /> - {/* ── Instrument view — select in header ── */} -
-
+ {/* ── Instrument + progressions row ── */} +
+ {/* Left: fretboard / piano */} +
Instrument