mirror of
https://github.com/myronblair/jarvis
synced 2026-07-29 17:22:35 -05:00
Fix camera/mic interference: detection runs during TTS, 12s exit threshold, require no recent commands
- Remove isSpeaking outer guard — detection loop now runs always so lastFaceSeen stays current - While isSpeaking + no face detected: refresh lastFaceSeen to prevent exit timer ticking during TTS - Raise no-face exit threshold from 3s to 12s — sporadic missed frames no longer kill the session - Add noCommandMs guard: camera never exits voice mode within 12s of last spoken command - Voice auto-trigger still respects isSpeaking (wont reactivate mid-response)
This commit is contained in:
+16
-10
@@ -1539,7 +1539,8 @@ async function startCamera() {
|
|||||||
addMessage('system', 'Face detection active — reactor tracking engaged.');
|
addMessage('system', 'Face detection active — reactor tracking engaged.');
|
||||||
|
|
||||||
faceLoopId = setInterval(async () => {
|
faceLoopId = setInterval(async () => {
|
||||||
if (!cameraActive || isSpeaking) return;
|
if (!cameraActive) return;
|
||||||
|
// Run detection even while speaking — needed for tracking + prevents lastFaceSeen staling out
|
||||||
try {
|
try {
|
||||||
const detection = await faceapi.detectSingleFace(
|
const detection = await faceapi.detectSingleFace(
|
||||||
document.getElementById('faceVideo'),
|
document.getElementById('faceVideo'),
|
||||||
@@ -1548,25 +1549,30 @@ async function startCamera() {
|
|||||||
const now = Date.now();
|
const now = Date.now();
|
||||||
if (detection) {
|
if (detection) {
|
||||||
lastFaceSeen = now;
|
lastFaceSeen = now;
|
||||||
const {x, y, width, height} = detection.box;
|
const ratio = (detection.box.width * detection.box.height) / (320 * 240);
|
||||||
const ratio = (width * height) / (320 * 240);
|
|
||||||
|
|
||||||
// Drive the reactor to follow the face
|
// Always drive the reactor
|
||||||
updateFaceTarget(detection.box, 320, 240);
|
updateFaceTarget(detection.box, 320, 240);
|
||||||
|
|
||||||
// Trigger voice if face fills >3% of frame
|
// Only auto-trigger voice when not already speaking/active, cooldown passed
|
||||||
if (ratio > 0.03 && !voiceMode && now > autoMicCooldown) {
|
if (ratio > 0.03 && !voiceMode && !isSpeaking && now > autoMicCooldown) {
|
||||||
autoMicCooldown = now + 9000;
|
autoMicCooldown = now + 9000;
|
||||||
document.getElementById('cameraBtn').classList.add('cam-sensing');
|
document.getElementById('cameraBtn').classList.add('cam-sensing');
|
||||||
enterVoiceMode();
|
enterVoiceMode();
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
clearFaceTarget();
|
// While JARVIS is speaking, keep lastFaceSeen fresh so the exit timer doesn't tick down
|
||||||
// No face — exit voice mode if face gone >3s
|
if (isSpeaking) { lastFaceSeen = now; }
|
||||||
if (voiceMode && now - lastFaceSeen > 3000) {
|
else { clearFaceTarget(); }
|
||||||
|
|
||||||
|
document.getElementById('cameraBtn').classList.remove('cam-sensing');
|
||||||
|
|
||||||
|
// Exit voice mode only if: face gone >12s AND no command in that same window AND not speaking
|
||||||
|
const noFaceMs = now - lastFaceSeen;
|
||||||
|
const noCommandMs = now - (voiceLastCmd || 0);
|
||||||
|
if (voiceMode && !isSpeaking && noFaceMs > 12000 && noCommandMs > 12000) {
|
||||||
exitVoiceMode();
|
exitVoiceMode();
|
||||||
}
|
}
|
||||||
document.getElementById('cameraBtn').classList.remove('cam-sensing');
|
|
||||||
}
|
}
|
||||||
} catch(_) {}
|
} catch(_) {}
|
||||||
}, 600);
|
}, 600);
|
||||||
|
|||||||
Reference in New Issue
Block a user