fix: VAD - detector de silêncio no browser
- AudioContext + Analyser para monitorar nível de áudio em tempo real - Threshold: avg < 10 por 60% dos frames = silêncio detectado - Botão parar fica vermelho quando silêncio é detectado (aviso visual) - Tooltip 'Silêncio detectado - Clique se quiser parar' - Cleanup de AudioContext ao parar Isso mitiga hallucinação do Whisper em áudios silenciosos/muito limpos
This commit is contained in:
+59
-1
@@ -2000,11 +2000,17 @@ const initApp = () => {
|
|||||||
const btnAnalyzeTranscription = document.getElementById('btnAnalyzeTranscription');
|
const btnAnalyzeTranscription = document.getElementById('btnAnalyzeTranscription');
|
||||||
const recordProcessingText = document.getElementById('recordProcessingText');
|
const recordProcessingText = document.getElementById('recordProcessingText');
|
||||||
const recordProcessingSubtext = document.getElementById('recordProcessingSubtext');
|
const recordProcessingSubtext = document.getElementById('recordProcessingSubtext');
|
||||||
|
// Variáveis de gravação
|
||||||
let mediaRecorder = null;
|
let mediaRecorder = null;
|
||||||
let audioChunks = [];
|
let audioChunks = [];
|
||||||
let recordingStartTime = null;
|
let recordingStartTime = null;
|
||||||
let timerInterval = 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 generatedReport = null;
|
||||||
let generatedCriancas = null;
|
let generatedCriancas = null;
|
||||||
let generatedTurma = 'não informada';
|
let generatedTurma = 'não informada';
|
||||||
@@ -2021,6 +2027,9 @@ const initApp = () => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
clearInterval(timerInterval);
|
clearInterval(timerInterval);
|
||||||
|
clearInterval(vadCheckInterval);
|
||||||
|
if (audioContext) { audioContext.close(); audioContext = null; }
|
||||||
|
if (analyser) { analyser = null; frequencyData = null; }
|
||||||
audioChunks = [];
|
audioChunks = [];
|
||||||
generatedReport = null;
|
generatedReport = null;
|
||||||
recordModal.style.display = 'none';
|
recordModal.style.display = 'none';
|
||||||
@@ -2074,6 +2083,52 @@ const initApp = () => {
|
|||||||
options = { mimeType: 'audio/mp4' };
|
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 = new MediaRecorder(stream, options);
|
||||||
|
|
||||||
mediaRecorder.ondataavailable = (e) => {
|
mediaRecorder.ondataavailable = (e) => {
|
||||||
@@ -2110,6 +2165,9 @@ const initApp = () => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
clearInterval(timerInterval);
|
clearInterval(timerInterval);
|
||||||
|
clearInterval(vadCheckInterval);
|
||||||
|
if (audioContext) { audioContext.close(); audioContext = null; }
|
||||||
|
if (analyser) { analyser = null; frequencyData = null; }
|
||||||
|
|
||||||
// Mostra processando com texto de transcrição
|
// Mostra processando com texto de transcrição
|
||||||
if (recordProcessingText) recordProcessingText.textContent = 'Transcrevendo áudio...';
|
if (recordProcessingText) recordProcessingText.textContent = 'Transcrevendo áudio...';
|
||||||
|
|||||||
Reference in New Issue
Block a user