diff --git a/public/app.js b/public/app.js index 2afb453..4049a0f 100644 --- a/public/app.js +++ b/public/app.js @@ -2000,11 +2000,17 @@ const initApp = () => { const btnAnalyzeTranscription = document.getElementById('btnAnalyzeTranscription'); const recordProcessingText = document.getElementById('recordProcessingText'); const recordProcessingSubtext = document.getElementById('recordProcessingSubtext'); - +// Variáveis de gravação let mediaRecorder = null; let audioChunks = []; let recordingStartTime = null; let timerInterval = null; + let audioContext = null; + let analyser = null; + let frequencyData = null; + let silenceFrames = 0; + let totalFrames = 0; + let vadCheckInterval = null; let generatedReport = null; let generatedCriancas = null; let generatedTurma = 'não informada'; @@ -2021,6 +2027,9 @@ const initApp = () => { } } clearInterval(timerInterval); + clearInterval(vadCheckInterval); + if (audioContext) { audioContext.close(); audioContext = null; } + if (analyser) { analyser = null; frequencyData = null; } audioChunks = []; generatedReport = null; recordModal.style.display = 'none'; @@ -2074,6 +2083,52 @@ const initApp = () => { options = { mimeType: 'audio/mp4' }; } } + // Configurar AudioContext para VAD + audioContext = new AudioContext(); + const source = audioContext.createMediaStreamSource(stream); + analyser = audioContext.createAnalyser(); + analyser.fftSize = 256; + source.connect(analyser); + frequencyData = new Uint8Array(analyser.frequencyBinCount); + + // Iniciar verificação de silêncio (VAD) + silenceFrames = 0; + totalFrames = 0; + vadCheckInterval = setInterval(() => { + if (!analyser) return; + analyser.getByteFrequencyData(frequencyData); + const avg = frequencyData.reduce((a, b) => a + b, 0) / frequencyData.length; + totalFrames++; + if (avg < 10) { + silenceFrames++; + } else { + silenceFrames = Math.max(0, silenceFrames - 2); // recupera mais rápido + } + // Atualizar indicator visual + const recordWaveform = document.getElementById('recordWaveform'); + if (recordWaveform && recordWaveform.children[0]) { + const bars = recordWaveform.children; + bars.forEach((bar) => { + const h = Math.max(4, (avg / 255) * 50 + Math.random() * 10); + bar.style.height = `${h}px`; + }); + } + // Se mais de 60% dos últimos 30 frames for silêncio, avisar + if (totalFrames > 30 && (silenceFrames / 30) > 0.6) { + const btnStopRecord = document.getElementById('btnStopRecord'); + if (btnStopRecord && !btnStopRecord.disabled) { + btnStopRecord.style.borderColor = '#ff6b6b'; + btnStopRecord.title = 'Silêncio detectado - Clique se quiser parar'; + } + } else { + const btnStopRecord = document.getElementById('btnStopRecord'); + if (btnStopRecord) { + btnStopRecord.style.borderColor = ''; + btnStopRecord.title = ''; + } + } + }, 100); + mediaRecorder = new MediaRecorder(stream, options); mediaRecorder.ondataavailable = (e) => { @@ -2110,6 +2165,9 @@ const initApp = () => { } } clearInterval(timerInterval); + clearInterval(vadCheckInterval); + if (audioContext) { audioContext.close(); audioContext = null; } + if (analyser) { analyser = null; frequencyData = null; } // Mostra processando com texto de transcrição if (recordProcessingText) recordProcessingText.textContent = 'Transcrevendo áudio...';