Hay preguntas similares para Java e iOS , pero me pregunto cómo detectar el silencio en javascript para grabaciones de audio a través getUserMedia() . Así dado:
navigator.mediaDevices.getUserMedia({ audio: true }) .then(stream => { const mediaRecorder = new MediaRecorder(stream); mediaRecorder.start(); const audioChunks = []; mediaRecorder.addEventListener("dataavailable", event => { audioChunks.push(event.data); }); mediaRecorder.addEventListener("stop", () => { const audioBlob = new Blob(audioChunks); const audioUrl = URL.createObjectURL(audioBlob); const audio = new Audio(audioUrl); audio.play(); }); }); Me pregunto si hay algo que se pueda verificar en los objetos Blob , URL o Audio en el evento de stop por ausencia de audio. En el caso de un micrófono defectuoso o un dispositivo virtual seleccionado, cualquier cosa en ese sentido. Previamente estaba comprobando el tamaño de la mancha, pero el audio silencioso todavía tiene un tamaño de archivo. Puedo hacer esto en el backend a través de ffmpeg , pero espero que haya una manera en JS puro para simplificar.
Con esta solución inspirada en visualizaciones con Web Audio API , puede configurar los decibeles mínimos requeridos y detectar si se grabó algo.
const MIN_DECIBELS = -45; navigator.mediaDevices.getUserMedia({ audio: true }) .then(stream => { const mediaRecorder = new MediaRecorder(stream); mediaRecorder.start(); const audioChunks = []; mediaRecorder.addEventListener("dataavailable", event => { audioChunks.push(event.data); }); const audioContext = new AudioContext(); const audioStreamSource = audioContext.createMediaStreamSource(stream); const analyser = audioContext.createAnalyser(); analyser.minDecibels = MIN_DECIBELS; audioStreamSource.connect(analyser); const bufferLength = analyser.frequencyBinCount; const domainData = new Uint8Array(bufferLength); let soundDetected = false; const detectSound = () => { if (soundDetected) { return } analyser.getByteFrequencyData(domainData); for (let i = 0; i < bufferLength; i++) { const value = domainData[i]; if (domainData[i] > 0) { soundDetected = true } } window.requestAnimationFrame(detectSound); }; window.requestAnimationFrame(detectSound); mediaRecorder.addEventListener("stop", () => { const audioBlob = new Blob(audioChunks); const audioUrl = URL.createObjectURL(audioBlob); const audio = new Audio(audioUrl); audio.play(); console.log({ soundDetected }); }); });Este código puede ejecutar una función para cada diálogo que detecta. se ejecuta en un bucle hasta que el usuario lo detiene:
VOICE_MIN_DECIBELS = -35; DELAY_BETWEEN_DIALOGS = 400; DIALOG_MAX_LENGTH = 60*1000; MEDIA_RECORDER = null; IS_RECORDING = false; //startRecording: function startRecording(){ IS_RECORDING = true; record(); } //stopRecording: function stopRecording(){ IS_RECORDING = false; if(MEDIA_RECORDER !== null) MEDIA_RECORDER.stop(); } //record: function record(){ navigator.mediaDevices.getUserMedia({ audio: true }) .then(stream => { //start recording: MEDIA_RECORDER = new MediaRecorder(stream); MEDIA_RECORDER.start(); //save audio chunks: const audioChunks = []; MEDIA_RECORDER.addEventListener("dataavailable", event => { audioChunks.push(event.data); }); //analisys: const audioContext = new AudioContext(); const audioStreamSource = audioContext.createMediaStreamSource(stream); const analyser = audioContext.createAnalyser(); analyser.minDecibels = VOICE_MIN_DECIBELS; audioStreamSource.connect(analyser); const bufferLength = analyser.frequencyBinCount; const domainData = new Uint8Array(bufferLength); //loop: const time = new Date(); let startTime, lastDetectedTime = time.getTime(); let anySoundDetected = false; const detectSound = () => { //recording stoped by user: if(IS_RECORDING) return; time = new Date(); currentTime = time.getTime(); //time out: if(currentTime > startTime + DIALOG_MAX_LENGTH){ MEDIA_RECORDER.stop(); return; } //a dialog detected: if( anySoundDetected === true && currentTime > lastDetectedTime + DELAY_BETWEEN_DIALOGS ){ MEDIA_RECORDER.stop(); return; } //check for detection: analyser.getByteFrequencyData(domainData); for(let i = 0; i < bufferLength; i++) if(domainData[i] > 0){ anySoundDetected = true; time = new Date(); lastDetectedTime = time.getTime(); } //continue the loop: window.requestAnimationFrame(detectSound); }; window.requestAnimationFrame(detectSound); //stop event: MEDIA_RECORDER.addEventListener('stop', () => { //stop all the tracks: stream.getTracks().forEach(track => track.stop()); if(!anySoundDetected) return; //send to server: const audioBlob = new Blob(audioChunks, {'type': 'audio/mp3'}); doWhateverWithAudio(audioBlob); //start recording again: record(); }); }); } //doWhateverWithAudio: function doWhateverWithAudio(audioBlob){ //.... send to server, downlod, etc. }