mirror of
https://github.com/9x/sheetmusictrainer.git
synced 2026-09-02 09:34:33 +02:00
antigravity one shot
This commit is contained in:
77
src/hooks/usePitchDetector.ts
Normal file
77
src/hooks/usePitchDetector.ts
Normal file
@@ -0,0 +1,77 @@
|
||||
import { useEffect, useRef, useState, useCallback } from 'react';
|
||||
import { PitchAnalyzer } from '../audio/PitchAnalyzer';
|
||||
import { frequencyToMidi, getNoteDetails } from '../music/NoteUtils';
|
||||
|
||||
interface PitchData {
|
||||
frequency: number;
|
||||
midi: number;
|
||||
note: string;
|
||||
cents: number;
|
||||
clarity: number; // Placeholder for now, maybe uses probability if YIN exposes it
|
||||
}
|
||||
|
||||
export function usePitchDetector(active: boolean) {
|
||||
const analyzerRef = useRef<PitchAnalyzer | null>(null);
|
||||
const [pitchData, setPitchData] = useState<PitchData | null>(null);
|
||||
const [isListening, setIsListening] = useState(false);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
const animationRef = useRef<number | null>(null);
|
||||
|
||||
const updatePitch = useCallback(() => {
|
||||
if (!analyzerRef.current) return;
|
||||
|
||||
const freq = analyzerRef.current.getPitch();
|
||||
if (freq) {
|
||||
const midi = frequencyToMidi(freq);
|
||||
const { scientific } = getNoteDetails(midi);
|
||||
|
||||
// Calculate cents off purely for display if needed,
|
||||
// but for training we usually care if midi matches.
|
||||
|
||||
setPitchData({
|
||||
frequency: freq,
|
||||
midi,
|
||||
note: scientific,
|
||||
cents: 0,
|
||||
clarity: 1
|
||||
});
|
||||
}
|
||||
|
||||
animationRef.current = requestAnimationFrame(updatePitch);
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
if (active) {
|
||||
if (!analyzerRef.current) {
|
||||
analyzerRef.current = new PitchAnalyzer();
|
||||
}
|
||||
|
||||
analyzerRef.current.start()
|
||||
.then(() => {
|
||||
setIsListening(true);
|
||||
updatePitch();
|
||||
})
|
||||
.catch(err => {
|
||||
console.error(err);
|
||||
setError("Could not access microphone.");
|
||||
setIsListening(false);
|
||||
});
|
||||
} else {
|
||||
if (analyzerRef.current) {
|
||||
analyzerRef.current.stop();
|
||||
analyzerRef.current = null;
|
||||
}
|
||||
setIsListening(false);
|
||||
if (animationRef.current) cancelAnimationFrame(animationRef.current);
|
||||
}
|
||||
|
||||
return () => {
|
||||
if (animationRef.current) cancelAnimationFrame(animationRef.current);
|
||||
if (analyzerRef.current) {
|
||||
analyzerRef.current.stop();
|
||||
}
|
||||
};
|
||||
}, [active, updatePitch]);
|
||||
|
||||
return { pitchData, isListening, error };
|
||||
}
|
||||
Reference in New Issue
Block a user