diff --git a/index.html b/index.html index 5d267ef..57e6355 100644 --- a/index.html +++ b/index.html @@ -510,41 +510,128 @@ const OPEN_NOTES = [ {label:"G string",note:"G3"},{label:"B string",note:"B3"},{label:"High e string",note:"E4"}, ]; -function OpenStringPractice({ detected, onTarget }) { +function OpenStringPractice({ detected, onTarget, onComplete }) { const [idx, setIdx] = useState(0); - const [done, setDone] = useState({}); + const [heard, setHeard] = useState(false); + const [doneSet, setDoneSet] = useState({}); + const [finished, setFinished] = useState(false); + const [kudos] = useState(()=>{ + const msgs = ["Great job!", "You nailed it!", "Awesome work!", "Sounding good!", "Perfect!", "Nice playing!"]; + return msgs[Math.floor(Math.random()*msgs.length)]; + }); - useEffect(()=>{ onTarget(idx < OPEN_NOTES.length ? OPEN_NOTES[idx].note : null); },[idx]); + const current = OPEN_NOTES[idx]; useEffect(()=>{ - if (!detected || idx >= OPEN_NOTES.length) return; - if (detected === OPEN_NOTES[idx].note){ - setDone(d=>({...d,[idx]:true})); - setTimeout(()=>setIdx(i=>i+1), 700); + if (!finished) onTarget(current ? current.note : null); + },[idx, finished]); + + useEffect(()=>{ + if (!detected || heard || finished || !current) return; + if (detected === current.note) setHeard(true); + },[detected, heard, finished, current]); + + const handleNext = () => { + setDoneSet(d=>({...d,[idx]:true})); + setHeard(false); + if (idx + 1 >= OPEN_NOTES.length) { + setFinished(true); + onTarget(null); + if (onComplete) onComplete(); + } else { + setIdx(i=>i+1); } - },[detected, idx]); + }; + + const NUM_FRETS=5, NS=6, W=320, H=200; + const TOP=44, BOT=H-24, LEFT=44, RIGHT=W-12; + const strGap=(BOT-TOP)/(NS-1); + const fretGap=(RIGHT-LEFT)/NUM_FRETS; + const STRING_LABELS=["e","B","G","D","A","E"]; return ( -
Start with open strings. Enable the mic and the app confirms each note as you pluck.
-Pluck each open string one at a time. The app listens and lights it up green when it hears you - then tap Next to move on.
+