Generador de frecuencia unica

            Generador de Frecuencias             /* INICIO del contenido de style.css */         body {             font-family: ‘Segoe UI’, Tahoma, Geneva, Verdana, sans-serif;             background-color: #1a1a1a;             color: #e0e0e0;             margin: 0;             display: flex;             justify-content: center;             align-items: center;             min-height: 100%;             padding: 20px;             box-sizing: border-box;             overflow: auto;         }         .container {             background-color: #2c2c2c;             padding: 30px;             border-radius: 10px;             box-shadow: 0 4px 15px rgba(0, 0, 0, 0.4);             width: 100%;             max-width: 500px;             text-align: center;             margin: auto;         }         h1 {             color: #90caf9; /* Azul claro */             margin-bottom: 30px;             font-size: 2em;         }         .control-group {             margin-bottom: 25px;             text-align: left;         }         label {             display: block;             margin-bottom: 8px;             font-weight: bold;             color: #bbdefb; /* Azul un poco más claro */         }         input[type=»range»] {             width: calc(100% – 70px); /* Ajuste para el display */             vertical-align: middle;             -webkit-appearance: none;             height: 10px;             border-radius: 5px;             background: #555;             outline: none;             opacity: 0.9;             transition: opacity .2s;             margin-right: 10px;         }         input[type=»range»]:hover {             opacity: 1;         }         input[type=»range»]::-webkit-slider-thumb {             -webkit-appearance: none;             appearance: none;             width: 20px;             height: 20px;             border-radius: 50%;             background: #90caf9;             cursor: pointer;             border: 2px solid #90caf9;         }         input[type=»range»]::-moz-range-thumb {             width: 20px;             height: 20px;             border-radius: 50%;             background: #90caf9;             cursor: pointer;             border: 2px solid #90caf9;         }         /* Nuevo estilo para el input de texto de frecuencia */         input[type=»text»]#frequency-input {             width: 100%;             padding: 10px;             margin-bottom: 15px; /* Espacio debajo del input de texto */             border-radius: 5px;             border: 1px solid #555;             background-color: #3f3f3f;             color: #e0e0e0;             font-size: 1em;             box-sizing: border-box; /* Incluir padding y border en el width */             text-align: center; /* Centrar el texto para que coincida con el display */         }         input[type=»text»]#frequency-input:focus {             outline: none;             border-color: #90caf9;             box-shadow: 0 0 5px rgba(144, 202, 249, 0.5);         }         span {             vertical-align: middle;             font-weight: bold;             color: #e0e0e0;             min-width: 60px; /* Para mantener el espacio */             display: inline-block;             text-align: right;         }         select {             width: 100%;             padding: 10px;             border-radius: 5px;             border: 1px solid #555;             background-color: #3f3f3f;             color: #e0e0e0;             font-size: 1em;             -webkit-appearance: none;             -moz-appearance: none;             appearance: none;             background-image: url(‘data:image/svg+xml;charset=US-ASCII,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%22292.4%22%20height%3D%22292.4%22%3E%3Cpath%20fill%3D%22%23e0e0e0%22%20d%3D%22M287%2C197.398L146.2%2C56.599L5.4%2C197.398c-11.9%2C11.9-11.9%2C31.3%2C0%2C43.2s31.3%2C11.9%2C43.2%2C0l97.6-97.6l97.6%2C97.6c11.9%2C11.9%2C31.3%2C11.9%2C43.2%2C0S298.9%2C209.298%2C287%2C197.398z%22%2F%3E%3C%2Fsvg%3E’);             background-repeat: no-repeat;             background-position: right 10px top 50%;             background-size: 12px;         }         .buttons {             margin-top: 30px;         }         button {             background-color: #90caf9;             color: #1a1a1a;             border: none;             padding: 12px 25px;             border-radius: 5px;             font-size: 1.1em;             cursor: pointer;             transition: background-color 0.3s ease, transform 0.1s ease;             margin: 0 10px;             font-weight: bold;         }         button:hover:not(:disabled) {             background-color: #bbdefb;             transform: translateY(-2px);         }         button:active:not(:disabled) {             transform: translateY(0);         }         button:disabled {             background-color: #555;             color: #bbb;             cursor: not-allowed;         }         /* Media Queries para pantallas más pequeñas */         @media (max-width: 600px) {             .container {                 padding: 20px;             }             h1 {                 font-size: 1.8em;             }             button {                 padding: 10px 20px;                 font-size: 1em;                 margin: 5px 0;                 width: 100%;             }             .buttons {                 display: flex;                 flex-direction: column;                 gap: 10px;             }             input[type=»range»] {                 width: calc(100% – 50px);             }             span {                 min-width: 40px;             }         }         /* FIN del contenido de style.css */        
       

Generador de Frecuencias

       
            Frecuencia (Hz):                                                                         440.000 Hz        
       
            Tipo de Onda:                             Senoidal                 Cuadrada                 Triangular                 Diente de Sierra                    
       
            Volumen:                         50%        
       
                               
   
            /* INICIO del contenido de script.js */         // 1. Obtener referencias a los elementos HTML (actualizadas para el nuevo input de texto)         const frequencyTextInput = document.getElementById(‘frequency-input’); // Nuevo input de texto         const frequencySlider = document.getElementById(‘frequency-slider’); // Slider, renombrado por claridad         const frequencyDisplay = document.getElementById(‘frequency-display’);         const waveTypeSelect = document.getElementById(‘wave-type’);         const volumeSlider = document.getElementById(‘volume’);         const volumeDisplay = document.getElementById(‘volume-display’);         const startButton = document.getElementById(‘start-button’);         const stopButton = document.getElementById(‘stop-button’);         // 2. Inicializar el AudioContext y nodos de audio         let audioContext;         let oscillator;         let gainNode;         let isPlaying = false;         // Función auxiliar para formatear la frecuencia         // Muestra hasta 3 decimales si no es un número entero, y elimina los ceros finales         function formatFrequency(freq) {             if (typeof freq !== ‘number’ || isNaN(freq)) {                 return »;             }             // Formatea a 3 decimales y luego usa parseFloat().toString() para limpiar ceros finales             let fixed = freq.toFixed(3);             return parseFloat(fixed).toString();         }         // Función para inicializar el AudioContext y configurar los nodos         function initAudio() {             if (!audioContext) {                 audioContext = new (window.AudioContext || window.webkitAudioContext)();             }             gainNode = audioContext.createGain();             gainNode.connect(audioContext.destination);             gainNode.gain.setValueAtTime(parseFloat(volumeSlider.value), audioContext.currentTime);             oscillator = audioContext.createOscillator();             oscillator.type = waveTypeSelect.value;             // Usamos la frecuencia del input de texto como la fuente de verdad para la precisión             let currentFreq = parseFloat(frequencyTextInput.value.replace(‘,’, ‘.’));             // Asegurarse de que la frecuencia esté dentro del rango permitido (1-20000 Hz)             currentFreq = Math.max(1, Math.min(20000, currentFreq));             oscillator.frequency.setValueAtTime(currentFreq, audioContext.currentTime);             oscillator.connect(gainNode);         }         // Función para iniciar la generación de tono         function startTone() {             if (!isPlaying) {                 initAudio();                 oscillator.start();                 isPlaying = true;                 startButton.disabled = true;                 stopButton.disabled = false;             }         }         // Función para detener la generación de tono         function stopTone() {             if (isPlaying) {                 if (oscillator) {                     oscillator.stop();                     oscillator.disconnect();                     oscillator = null;                 }                 isPlaying = false;                 startButton.disabled = false;                 stopButton.disabled = true;             }         }         // 3. Añadir Event Listeners a los controles de UI         startButton.addEventListener(‘click’, startTone);         stopButton.addEventListener(‘click’, stopTone);         // Event Listener para el Input de Texto de Frecuencia (precisión)         frequencyTextInput.addEventListener(‘input’, () => {             let value = frequencyTextInput.value.replace(‘,’, ‘.’); // Permite coma como decimal             let freq = parseFloat(value);             if (isNaN(freq)) {                 // Podrías mostrar un mensaje de error o simplemente no actualizar                 frequencyDisplay.textContent = ‘Frec. Inválida’;                 return;             }             // Clampar la frecuencia dentro del rango (1 a 20000 Hz)             freq = Math.max(1, Math.min(20000, freq));             // Actualizar el slider (se ajustará al valor más cercano si su ‘step’ es 1)             frequencySlider.value = freq;             // Actualizar el display con la frecuencia formateada             frequencyDisplay.textContent = `${formatFrequency(freq)} Hz`;             // Si está sonando, actualizar la frecuencia del oscilador             if (oscillator) {                 oscillator.frequency.setValueAtTime(freq, audioContext.currentTime);             }         });         // Event Listener para el Slider de Frecuencia (ajuste rápido)         frequencySlider.addEventListener(‘input’, () => {             const freq = parseFloat(frequencySlider.value);             // Actualizar el input de texto con el valor del slider (formateado)             frequencyTextInput.value = formatFrequency(freq);             // Actualizar el display             frequencyDisplay.textContent = `${formatFrequency(freq)} Hz`;             // Si está sonando, actualizar la frecuencia del oscilador             if (oscillator) {                 oscillator.frequency.setValueAtTime(freq, audioContext.currentTime);             }         });         // Actualizar tipo de onda si está sonando         waveTypeSelect.addEventListener(‘change’, () => {             if (oscillator) {                 oscillator.type = waveTypeSelect.value;             }         });         // Actualizar volumen y display         volumeSlider.addEventListener(‘input’, () => {             const vol = parseFloat(volumeSlider.value);             volumeDisplay.textContent = `${Math.round(vol * 100)}%`;             if (gainNode) {                 gainNode.gain.setValueAtTime(vol, audioContext.currentTime);             }         });         // Inicializar displays con valores por defecto al cargar la página         document.addEventListener(‘DOMContentLoaded’, () => {             const initialFreq = parseFloat(frequencySlider.value);             frequencyTextInput.value = formatFrequency(initialFreq); // Sincroniza el input de texto al inicio             frequencyDisplay.textContent = `${formatFrequency(initialFreq)} Hz`;             volumeDisplay.textContent = `${Math.round(parseFloat(volumeSlider.value) * 100)}%`;         });         /* FIN del contenido de script.js */