| <!DOCTYPE html> |
| <html lang="es"> |
| <head> |
| <meta charset="UTF-8"> |
| <meta name="viewport" content="width=device-width"> |
| <title>Text to Speech Descargar Audio</title> |
| <style> |
| textarea { |
| width: 100%; |
| height: 150px; |
| } |
| button { |
| padding: 10px; |
| margin: 10px 5px; |
| cursor: pointer; |
| } |
| </style> |
| </head> |
| <body> |
| <h3>Text to Speech - Google Español</h3> |
|
|
| <textarea id="lines" placeholder="Ingresa el texto aquí...">Hola, soy Juan. ¿Tú quién eres? No te conozco.</textarea><br> |
|
|
| <button id="speak">Hablar y Grabar</button> |
| <button id="pause">Pausar</button> |
| <button id="resume">Reanudar</button> |
| <button id="cancel">Cancelar</button> |
| <a id="download" style="display:none;">Descargar Audio</a> |
|
|
| <script> |
| |
| if (!window.speechSynthesis || !window.MediaRecorder || !navigator.mediaDevices.getDisplayMedia) { |
| alert("Tu navegador no soporta las APIs necesarias para esta función."); |
| } |
| |
| let voices = []; |
| let mediaRecorder; |
| let chunks = []; |
| let tts = new SpeechSynthesisUtterance(); |
| let fragments = []; |
| let currentFragment = 0; |
| |
| |
| window.speechSynthesis.onvoiceschanged = () => { |
| voices = window.speechSynthesis.getVoices(); |
| let selectedVoice = voices.find(voice => voice.lang === "es-ES" && voice.name.includes("Google")); |
| if (selectedVoice) { |
| tts.voice = selectedVoice; |
| tts.volume = .8; |
| tts.rate = 1.1; |
| tts.pitch = 1; |
| } else { |
| alert("La voz 'Google español' no está disponible en este navegador."); |
| } |
| }; |
| |
| |
| function dividirTexto(texto) { |
| return texto.match(/[^.!?]+[.!?]+/g) || [texto]; |
| } |
| |
| |
| function reproducirSiguienteFragmento() { |
| if (currentFragment < fragments.length) { |
| tts.text = fragments[currentFragment].trim(); |
| window.speechSynthesis.speak(tts); |
| currentFragment++; |
| } else { |
| |
| mediaRecorder.stop(); |
| } |
| } |
| |
| async function speakAndRecord(text) { |
| |
| let stream; |
| try { |
| stream = await navigator.mediaDevices.getDisplayMedia({ audio: true}); |
| } catch (err) { |
| console.error("Error al capturar el audio:", err); |
| alert("No se pudo capturar el audio. Asegúrate de conceder los permisos necesarios."); |
| return; |
| } |
| |
| |
| const videoTracks = stream.getVideoTracks(); |
| videoTracks.forEach(track => { |
| track.stop(); |
| stream.removeTrack(track); |
| }); |
| |
| |
| mediaRecorder = new MediaRecorder(stream, { mimeType: 'audio/webm' }); |
| chunks = []; |
| |
| mediaRecorder.ondataavailable = (e) => { |
| if (e.data.size > 0) { |
| chunks.push(e.data); |
| } |
| }; |
| |
| mediaRecorder.onstop = () => { |
| const audioBlob = new Blob(chunks, { type: 'audio/webm' }); |
| const audioUrl = URL.createObjectURL(audioBlob); |
| const downloadButton = document.getElementById("download"); |
| downloadButton.style.display = "inline"; |
| downloadButton.href = audioUrl; |
| downloadButton.download = 'tts_audio.webm'; |
| downloadButton.textContent = 'Descargar Audio'; |
| }; |
| |
| |
| mediaRecorder.start(); |
| |
| |
| fragments = dividirTexto(text); |
| currentFragment = 0; |
| |
| |
| reproducirSiguienteFragmento(); |
| } |
| |
| |
| tts.onend = () => { |
| reproducirSiguienteFragmento(); |
| }; |
| |
| |
| document.getElementById("speak").addEventListener("click", () => { |
| const text = document.getElementById("lines").value; |
| speakAndRecord(text); |
| }); |
| |
| document.getElementById("pause").addEventListener("click", () => { |
| window.speechSynthesis.pause(); |
| }); |
| |
| document.getElementById("resume").addEventListener("click", () => { |
| window.speechSynthesis.resume(); |
| }); |
| |
| document.getElementById("cancel").addEventListener("click", () => { |
| window.speechSynthesis.cancel(); |
| if (mediaRecorder && mediaRecorder.state !== "inactive") { |
| mediaRecorder.stop(); |
| } |
| }); |
| </script> |
| </body> |
| </html> |
|
|