RUIDO DEL MICROFONO-API MediaRecorder

  funcionalidad de grabación de video y descarga, necesitarás usar la API MediaRecorder. Aquí está el código modificado con esta funcionalidad:

html
Copy
Download
Run
<!DOCTYPE html>
<html lang="es">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Webcam con Supresor de Ruido y Grabación</title>
    <style>
        body {
            font-family: Arial, sans-serif;
            display: flex;
            flex-direction: column;
            align-items: center;
            padding: 20px;
            background-color: #f5f5f5;
        }

        #videoElement {
            width: 100%;
            max-width: 640px;
            background-color: #333;
            margin: 20px 0;
            border-radius: 8px;
            box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2);
        }

        .controls {
            display: flex;
            gap: 10px;
            margin-bottom: 20px;
        }

        button {
            padding: 10px 20px;
            background-color: #4285F4;
            color: white;
            border: none;
            border-radius: 4px;
            cursor: pointer;
            font-size: 16px;
            transition: background-color 0.3s;
        }

        button:hover {
            background-color: #3367D6;
        }

        button:disabled {
            background-color: #9E9E9E;
            cursor: not-allowed;
        }

        #errorMsg {
            color: #D32F2F;
            margin-top: 20px;
            text-align: center;
        }

        .audio-panel {
            width: 100%;
            max-width: 640px;
            background-color: white;
            padding: 15px;
            border-radius: 8px;
            box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
            margin-bottom: 20px;
        }

        .meter {
            height: 20px;
            background: #E0E0E0;
            border-radius: 10px;
            overflow: hidden;
            margin-top: 10px;
        }

        .level {
            height: 100%;
            width: 0%;
            background: linear-gradient(to right, #4CAF50, #FFEB3B, #F44336);
            transition: width 0.05s;
        }

        .controls-row {
            display: flex;
            justify-content: space-between;
            margin-top: 10px;
        }

        label {
            display: flex;
            align-items: center;
            gap: 5px;
            cursor: pointer;
        }

        h2 {
            color: #4285F4;
            margin-top: 0;
        }

        #recordingIndicator {
            display: none;
            width: 12px;
            height: 12px;
            background-color: red;
            border-radius: 50%;
            margin-left: 5px;
            animation: pulse 1.5s infinite;
        }

        @keyframes pulse {
            0% { opacity: 1; }
            50% { opacity: 0.3; }
            100% { opacity: 1; }
        }

        #timer {
            margin-left: 10px;
            font-weight: bold;
        }
    </style>
</head>

<body>
    <h1>Webcam con Supresor de Ruido</h1>
    <div class="row g-3 align-items-stretch">
        <div class="col-md-6 d-flex">
            <video id="videoElement" autoplay playsinline class="img-fluid rounded w-100 h-auto bg-dark"></video>
        </div>
        <div class="col-md-6 d-flex">
            <div class="card audio-panel w-100">
                <div class="card-body d-flex flex-column">
                    <h2 class="card-title">Control de Audio</h2>
                    <div class="mb-2">Nivel de micrófono:</div>
                    <div class="progress mb-3" style="height: 20px;">
                        <div class="progress-bar" id="audioLevel" role="progressbar" style="width: 0%;"></div>
                    </div>

                    <div class="mt-auto">
                        <div class="form-group mb-3">
                            <div class="form-check form-switch">
                                <input class="form-check-input" type="checkbox" id="noiseSuppression" checked>
                                <label class="form-check-label" for="noiseSuppression">Supresión de ruido</label>
                            </div>
                        </div>

                        <div class="form-group mb-3">
                            <label for="gainControl" class="form-label">Volumen: <span id="gainValue">125%</span></label>
                            <input type="range" class="form-range" id="gainControl" min="0" max="200" value="125">
                        </div>

                        <div class="d-flex justify-content-between gap-2">
                            <div>
                                <button id="startButton" class="btn btn-primary">Iniciar</button>
                                <button id="stopButton" class="btn btn-danger" disabled>Detener</button>
                            </div>
                            <div>
                                <button id="recordButton" class="btn btn-success" disabled>
                                    Grabar
                                    <span id="recordingIndicator"></span>
                                    <span id="timer"></span>
                                </button>
                                <button id="downloadButton" class="btn btn-secondary" disabled>Descargar</button>
                            </div>
                        </div>
                    </div>
                </div>
            </div>
        </div>
    </div>

    <div id="errorMsg" class="mt-3 alert alert-danger d-none"></div>

    <script>
        // Elementos del DOM
        const videoElement = document.getElementById('videoElement');
        const startButton = document.getElementById('startButton');
        const stopButton = document.getElementById('stopButton');
        const recordButton = document.getElementById('recordButton');
        const downloadButton = document.getElementById('downloadButton');
        const recordingIndicator = document.getElementById('recordingIndicator');
        const timerElement = document.getElementById('timer');
        const errorMsg = document.getElementById('errorMsg');
        const audioLevel = document.getElementById('audioLevel');
        const noiseSuppression = document.getElementById('noiseSuppression');
        const gainControl = document.getElementById('gainControl');
        const gainValue = document.getElementById('gainValue');

        // Objetos para el procesamiento de audio y grabación
        let stream = null;
        let audioContext = null;
        let analyser = null;
        let microphone = null;
        let noiseSuppressor = null;
        let gainNode = null;
        let animationId = null;
        let mediaRecorder = null;
        let recordedChunks = [];
        let recordingStartTime = null;
        let timerInterval = null;

        // Iniciar captura
        async function startCapture() {
            try {
                // Solicitar acceso a dispositivos
                stream = await navigator.mediaDevices.getUserMedia({
                    video: true,
                    audio: {
                        noiseSuppression: true,
                        echoCancellation: true,
                        autoGainControl: true
                    }
                });

                // Configurar video
                videoElement.srcObject = stream;

                // Configurar procesamiento de audio
                setupAudioProcessing(stream);

                // Habilitar botones
                startButton.disabled = true;
                stopButton.disabled = false;
                recordButton.disabled = false;
                downloadButton.disabled = true;
                errorMsg.textContent = '';
            } catch (err) {
                console.error("Error:", err);
                errorMsg.textContent = `Error: ${err.message}`;
            }
        }

        // Configurar procesamiento de audio
        function setupAudioProcessing(stream) {
            // Crear contexto de audio
            audioContext = new(window.AudioContext || window.webkitAudioContext)();

            // Crear nodos de procesamiento
            analyser = audioContext.createAnalyser();
            analyser.fftSize = 64;

            // Nodo de ganancia (control de volumen)
            gainNode = audioContext.createGain();
            updateGain();

            // Conectar el flujo de audio
            microphone = audioContext.createMediaStreamSource(stream);

            // Configurar supresión de ruido (filtro paso alto)
            if (noiseSuppression.checked) {
                setupNoiseSuppression();
            } else {
                microphone.connect(gainNode);
            }

            gainNode.connect(analyser);
            analyser.connect(audioContext.destination);

            // Iniciar visualización
            updateAudioLevel();
        }

        // Configurar supresión de ruido
        function setupNoiseSuppression() {
            if (noiseSuppressor) {
                noiseSuppressor.disconnect();
            }

            // Crear filtro paso alto para reducir ruido de baja frecuencia
            noiseSuppressor = audioContext.createBiquadFilter();
            noiseSuppressor.type = "highpass";
            noiseSuppressor.frequency.value = 150;

            // Conectar los nodos
            microphone.disconnect();
            microphone.connect(noiseSuppressor);
            noiseSuppressor.connect(gainNode);
        }

        // Actualizar control de ganancia
        function updateGain() {
            if (gainNode) {
                const gain = gainControl.value / 100;
                gainNode.gain.value = gain;
                gainValue.textContent = `${gainControl.value}%`;
            }
        }

        // Actualizar visualización de nivel de audio
        function updateAudioLevel() {
            if (!analyser) return;

            const dataArray = new Uint8Array(analyser.frequencyBinCount);
            analyser.getByteFrequencyData(dataArray);

            let sum = 0;
            let count = 0;
            for (let i = 3; i < dataArray.length; i++) {
                sum += dataArray[i];
                count++;
            }
            const average = count > 0 ? sum / count : 0;

            audioLevel.style.width = `${average}%`;

            if (average < 30) {
                audioLevel.style.backgroundColor = "#4CAF50";
            } else if (average < 70) {
                audioLevel.style.backgroundColor = "#FFEB3B";
            } else {
                audioLevel.style.backgroundColor = "#F44336";
            }

            animationId = requestAnimationFrame(updateAudioLevel);
        }

        // Iniciar grabación
        function startRecording() {
            recordedChunks = [];
            
            // Configurar MediaRecorder
            const options = { mimeType: 'video/webm;codecs=vp9' };
            mediaRecorder = new MediaRecorder(stream, options);

            // Evento para recoger datos de grabación
            mediaRecorder.ondataavailable = function(event) {
                if (event.data.size > 0) {
                    recordedChunks.push(event.data);
                }
            };

            // Iniciar grabación
            mediaRecorder.start(100); // Recoger datos cada 100ms

            // Actualizar UI
            recordButton.textContent = "Detener grabación";
            recordButton.classList.remove("btn-success");
            recordButton.classList.add("btn-warning");
            recordingIndicator.style.display = "inline-block";
            downloadButton.disabled = true;
            
            // Iniciar temporizador
            recordingStartTime = new Date();
            updateTimer();
            timerInterval = setInterval(updateTimer, 1000);
        }

        // Detener grabación
        function stopRecording() {
            if (mediaRecorder && mediaRecorder.state !== 'inactive') {
                mediaRecorder.stop();
                
                // Detener temporizador
                clearInterval(timerInterval);
                timerElement.textContent = "";
                
                // Actualizar UI
                recordButton.textContent = "Grabar";
                recordButton.classList.remove("btn-warning");
                recordButton.classList.add("btn-success");
                recordingIndicator.style.display = "none";
                downloadButton.disabled = false;
            }
        }

        // Actualizar temporizador
        function updateTimer() {
            const elapsed = Math.floor((new Date() - recordingStartTime) / 1000);
            const minutes = Math.floor(elapsed / 60).toString().padStart(2, '0');
            const seconds = (elapsed % 60).toString().padStart(2, '0');
            timerElement.textContent = `${minutes}:${seconds}`;
        }

        // Descargar video grabado
        function downloadVideo() {
            const blob = new Blob(recordedChunks, { type: 'video/webm' });
            const url = URL.createObjectURL(blob);
            const a = document.createElement('a');
            a.style.display = 'none';
            a.href = url;
            a.download = `grabacion-${new Date().toISOString().slice(0, 19).replace(/[:T]/g, '-')}.webm`;
            document.body.appendChild(a);
            a.click();
            setTimeout(() => {
                document.body.removeChild(a);
                URL.revokeObjectURL(url);
            }, 100);
        }

        // Detener captura
        function stopCapture() {
            // Detener grabación si está activa
            if (mediaRecorder && mediaRecorder.state === 'recording') {
                stopRecording();
            }
            
            if (stream) {
                // Detener pistas
                stream.getTracks().forEach(track => track.stop());
                videoElement.srcObject = null;

                // Detener procesamiento de audio
                if (animationId) {
                    cancelAnimationFrame(animationId);
                    animationId = null;
                }
                if (audioContext) {
                    audioContext.close();
                    audioContext = null;
                }

                // Detener temporizador
                clearInterval(timerInterval);

                // Resetear UI
                audioLevel.style.width = "0%";
                startButton.disabled = false;
                stopButton.disabled = true;
                recordButton.disabled = true;
                downloadButton.disabled = true;
                recordButton.textContent = "Grabar";
                recordButton.classList.remove("btn-warning");
                recordButton.classList.add("btn-success");
                recordingIndicator.style.display = "none";
                timerElement.textContent = "";
            }
        }

        // Event listeners
        startButton.addEventListener('click', startCapture);
        stopButton.addEventListener('click', stopCapture);
        
        recordButton.addEventListener('click', function() {
            if (recordButton.textContent === "Grabar") {
                startRecording();
            } else {
                stopRecording();
            }
        });
        
        downloadButton.addEventListener('click', downloadVideo);
        
        noiseSuppression.addEventListener('change', () => {
            if (noiseSuppression.checked && microphone && gainNode) {
                setupNoiseSuppression();
            } else if (microphone && gainNode) {
                microphone.disconnect();
                microphone.connect(gainNode);
            }
        });
        
        gainControl.addEventListener('input', updateGain);

        // Limpiar al salir
        window.addEventListener('beforeunload', stopCapture);
    </script>
</body>
</html>

Cambios realizados:

  1. Nuevos botones añadidos:

    • Botón "Grabar/Detener grabación" (alterna entre ambos estados)

    • Botón "Descargar" (para guardar el video)

  2. Indicador visual de grabación:

    • Punto rojo parpadeante cuando está grabando

    • Temporizador que muestra la duración de la grabación

  3. Funcionalidad de grabación:

    • Usa la API MediaRecorder para capturar el stream de video/audio

    • Almacena los datos en recordedChunks

    • Permite detener la grabación y descargar el archivo

  4. Formato del video:

    • El video se guarda en formato WEBM (VP9)

    • El nombre del archivo incluye la fecha y hora actual

Limitaciones importantes:

  1. Guardar en el servidor: Este código solo permite descargar el video al dispositivo del usuario. Para guardarlo directamente en una carpeta del servidor necesitarías:

    • Un backend (Node.js, PHP, etc.)

    • Enviar el video mediante AJAX/Fetch

    • Procesar y guardar el archivo en el servidor

  2. Compatibilidad: El formato WEBM es ampliamente compatible, pero algunos navegadores antiguos podrían requerir formatos diferentes.

Si necesitas que el video se guarde automáticamente en una carpeta del servidor, necesitarás implementar una solución de backend adicional. ¿Te gustaría que también te muestre cómo hacer esa parte

Comentarios

Entradas más populares de este blog

b-Web Speech API

captura video con audio del sistema (como música o sonidos del navegador) pero sin usar el micrófono

EL audio lo envia el navegador-Transcripción de Voz con Whisper