mutear el micrófono

  mutear el micrófono en lugar del ícono de micrófono:

html
<!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;
        }
        
        #muteButton {
            background-color: #757575;
        }
        
        #muteButton.muted {
            background-color: #D32F2F;
        }
    </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>
                                <button id="muteButton" class="btn" disabled>Silenciar</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 muteButton = document.getElementById('muteButton');
        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;
        let isMuted = false;
        let audioTracks = 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
                    }
                });

                // Guardar pistas de audio
                audioTracks = stream.getAudioTracks();

                // Configurar video
                videoElement.srcObject = stream;

                // Configurar procesamiento de audio
                setupAudioProcessing(stream);

                // Habilitar botones
                startButton.disabled = true;
                stopButton.disabled = false;
                recordButton.disabled = false;
                muteButton.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);
        }

        // Alternar silencio del micrófono
        function toggleMute() {
            if (!audioTracks) return;
            
            isMuted = !isMuted;
            
            audioTracks.forEach(track => {
                track.enabled = !isMuted;
            });
            
            if (isMuted) {
                muteButton.classList.add('muted');
                muteButton.textContent = 'Activar sonido';
            } else {
                muteButton.classList.remove('muted');
                muteButton.textContent = 'Silenciar';
            }
        }

        // 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;
                muteButton.disabled = true;
                downloadButton.disabled = true;
                recordButton.textContent = "Grabar";
                recordButton.classList.remove("btn-warning");
                recordButton.classList.add("btn-success");
                recordingIndicator.style.display = "none";
                timerElement.textContent = "";
                
                // Resetear estado de silencio
                isMuted = false;
                muteButton.classList.remove('muted');
                muteButton.textContent = 'Silenciar';
            }
        }

        // 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);
        
        muteButton.addEventListener('click', toggleMute);
        
        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. Añadí un botón "Silenciar":

    • Se agregó un nuevo botón con id muteButton en la interfaz

    • El botón cambia de color (rojo cuando está silenciado, gris cuando no)

    • Cambia su texto entre "Silenciar" y "Activar sonido"

  2. Funcionalidad de silenciar:

    • Implementé la función toggleMute() que alterna el estado del micrófono

    • Cuando se silencia, se deshabilitan las pistas de audio (track.enabled = false)

    • El medidor de nivel de audio seguirá mostrando actividad (si hay alguna) porque el nodo de audio sigue conectado

  3. Estilos CSS:

    • Añadí estilos específicos para el botón de silencio

    • Cuando está activado el silencio, el botón se vuelve rojo

  4. Manejo de estado:

    • Se añadió la variable isMuted para controlar el estado

    • Se guardan las pistas de audio en audioTracks para poder manipularlas

  5. Integración con otros controles:

    • El botón se habilita/deshabilita junto con los otros controles

    • Se resetea su estado cuando se detiene la captura

Esta implementación permite silenciar el micrófono sin afectar la grabación de video y con una interfaz clara del estado actual.

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