|
|
<!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> |
|
|
|