javascript - speakit - texto a voz google chrome
Síntesis de voz de Chrome con textos más largos (10)
Tengo un problema al tratar de usar Speech Synthesis API en Chrome 33. Funciona perfectamente con un texto más corto, pero si intento texto más largo, simplemente se detiene en el medio. Una vez que se ha detenido una vez, la Síntesis del habla no funciona en Chrome hasta que se reinicie el navegador.
Código de ejemplo ( http://jsfiddle.net/Mdm47/1/ ):
function speak(text) {
var msg = new SpeechSynthesisUtterance();
var voices = speechSynthesis.getVoices();
msg.voice = voices[10];
msg.voiceURI = ''native'';
msg.volume = 1;
msg.rate = 1;
msg.pitch = 2;
msg.text = text;
msg.lang = ''en-US'';
speechSynthesis.speak(msg);
}
speak(''Short text'');
speak(''Collaboratively administrate empowered markets via plug-and-play networks. Dynamically procrastinate B2C users after installed base benefits. Dramatically visualize customer directed convergence without revolutionary ROI. Efficiently unleash cross-media information without cross-media value. Quickly maximize timely deliverables for real-time schemas. Dramatically maintain clicks-and-mortar solutions without functional solutions.'');
speak(''Another short text'');
Deja de hablar en el medio del segundo texto, y no puedo obtener ninguna otra página para hablar después de eso.
¿Es un error del navegador o algún tipo de limitación de seguridad?
2017 y este error todavía está presente. Entiendo muy bien este problema, siendo el desarrollador de la galardonada extensión de Chrome Read Aloud . De acuerdo, es una broma sobre la parte galardonada.
- Su discurso se estancará si es más de 15 segundos.
- Descubrí que Chrome usa un temporizador inactivo de 15 segundos para decidir cuándo desactivar la página de evento / fondo de una extensión. Creo que este es el culpable.
La solución que he utilizado es un algoritmo de fragmentación bastante complicado que respeta la puntuación. Para los idiomas latinos, establezco el tamaño máximo de fragmento en 36 palabras. El código es de código abierto, si te inclinas: https://github.com/ken107/read-aloud/blob/master/js/speech.js (línea 144)
El límite de 36 palabras funciona bien la mayor parte del tiempo, manteniéndose dentro de los 15 segundos. Pero habrá casos en que todavía se atasque. Para recuperarme de eso, uso un temporizador de 16 segundos.
Aquí es a lo que terminé, simplemente divide mis oraciones en el período "."
var voices = window.speechSynthesis.getVoices();
var sayit = function ()
{
var msg = new SpeechSynthesisUtterance();
msg.voice = voices[10]; // Note: some voices don''t support altering params
msg.voiceURI = ''native'';
msg.volume = 1; // 0 to 1
msg.rate = 1; // 0.1 to 10
msg.pitch = 2; //0 to 2
msg.lang = ''en-GB'';
msg.onstart = function (event) {
console.log("started");
};
msg.onend = function(event) {
console.log(''Finished in '' + event.elapsedTime + '' seconds.'');
};
msg.onerror = function(event)
{
console.log(''Errored '' + event);
}
msg.onpause = function (event)
{
console.log(''paused '' + event);
}
msg.onboundary = function (event)
{
console.log(''onboundary '' + event);
}
return msg;
}
var speekResponse = function (text)
{
speechSynthesis.cancel(); // if it errors, this clears out the error.
var sentences = text.split(".");
for (var i=0;i< sentences.length;i++)
{
var toSay = sayit();
toSay.text = sentences[i];
speechSynthesis.speak(toSay);
}
}
Como Michael propuso, las soluciones de Peter son realmente geniales, excepto cuando el texto está en líneas diferentes. Michael creó una demostración para ilustrar mejor el problema con ella. - https://jsfiddle.net/1gzkja90/ y propuso otra solución.
Agregar una forma más sencilla de resolver esto es eliminar los saltos de línea del área de texto en la solución de Peter y funciona muy bien.
//javascript
var noLineBreaks = document.getElementById(''mytextarea'').replace(//n/g,'''');
//jquery
var noLineBreaks = $(''#mytextarea'').val().replace(//n/g,'''');
Entonces, en la solución de Peter podría verse de la siguiente manera:
utterance.text = $(''#mytextarea'').val().replace(//n/g,'''');
Pero aún hay problemas para cancelar el discurso. Simplemente pasa a otra secuencia y no se detiene.
El problema con la respuesta de Peter es que no funciona cuando tienes una cola de síntesis de voz configurada. El script colocará el nuevo fragmento al final de la cola y, por lo tanto, estará fuera de servicio. Ejemplo: https://jsfiddle.net/1gzkja90/
<script type=''text/javascript'' src=''http://code.jquery.com/jquery-2.1.0.js''></script>
<script type=''text/javascript''>
u = new SpeechSynthesisUtterance();
$(document).ready(function () {
$(''.t'').each(function () {
u = new SpeechSynthesisUtterance($(this).text());
speechUtteranceChunker(u, {
chunkLength: 120
}, function () {
console.log(''end'');
});
});
});
/**
* Chunkify
* Google Chrome Speech Synthesis Chunking Pattern
* Fixes inconsistencies with speaking long texts in speechUtterance objects
* Licensed under the MIT License
*
* Peter Woolley and Brett Zamir
*/
var speechUtteranceChunker = function (utt, settings, callback) {
settings = settings || {};
var newUtt;
var txt = (settings && settings.offset !== undefined ? utt.text.substring(settings.offset) : utt.text);
if (utt.voice && utt.voice.voiceURI === ''native'') { // Not part of the spec
newUtt = utt;
newUtt.text = txt;
newUtt.addEventListener(''end'', function () {
if (speechUtteranceChunker.cancel) {
speechUtteranceChunker.cancel = false;
}
if (callback !== undefined) {
callback();
}
});
}
else {
var chunkLength = (settings && settings.chunkLength) || 160;
var pattRegex = new RegExp(''^[//s//S]{'' + Math.floor(chunkLength / 2) + '','' + chunkLength + ''}[.!?,]{1}|^[//s//S]{1,'' + chunkLength + ''}$|^[//s//S]{1,'' + chunkLength + ''} '');
var chunkArr = txt.match(pattRegex);
if (chunkArr[0] === undefined || chunkArr[0].length <= 2) {
//call once all text has been spoken...
if (callback !== undefined) {
callback();
}
return;
}
var chunk = chunkArr[0];
newUtt = new SpeechSynthesisUtterance(chunk);
var x;
for (x in utt) {
if (utt.hasOwnProperty(x) && x !== ''text'') {
newUtt[x] = utt[x];
}
}
newUtt.addEventListener(''end'', function () {
if (speechUtteranceChunker.cancel) {
speechUtteranceChunker.cancel = false;
return;
}
settings.offset = settings.offset || 0;
settings.offset += chunk.length - 1;
speechUtteranceChunker(utt, settings, callback);
});
}
if (settings.modifier) {
settings.modifier(newUtt);
}
console.log(newUtt); //IMPORTANT!! Do not remove: Logging the object out fixes some onend firing issues.
//placing the speak invocation inside a callback fixes ordering and onend issues.
setTimeout(function () {
speechSynthesis.speak(newUtt);
}, 0);
};
</script>
<p class="t">MLA format follows the author-page method of in-text citation. This means that the author''s last name and the page number(s) from which the quotation or paraphrase is taken must appear in the text, and a complete reference should appear on your Works Cited page. The author''s name may appear either in the sentence itself or in parentheses following the quotation or paraphrase, but the page number(s) should always appear in the parentheses, not in the text of your sentence.</p>
<p class="t">Joe waited for the train.</p>
<p class="t">The train was late.</p>
<p class="t">Mary and Samantha took the bus.</p>
En mi caso, la respuesta fue "cortar" la cadena antes de agregarlos a la cola. Vea aquí: http://jsfiddle.net/vqvyjzq4/
Muchos apoyos a Peter para la idea, así como la expresión regular (que todavía tengo que conquistar). Estoy seguro de que el javascript se puede limpiar, esto es más una prueba de concepto.
<script type=''text/javascript'' src=''http://code.jquery.com/jquery-2.1.0.js''></script>
<script type=''text/javascript''>
var chunkLength = 120;
var pattRegex = new RegExp(''^[//s//S]{'' + Math.floor(chunkLength / 2) + '','' + chunkLength + ''}[.!?,]{1}|^[//s//S]{1,'' + chunkLength + ''}$|^[//s//S]{1,'' + chunkLength + ''} '');
$(document).ready(function () {
var element = this;
var arr = [];
var txt = replaceBlank($(element).text());
while (txt.length > 0) {
arr.push(txt.match(pattRegex)[0]);
txt = txt.substring(arr[arr.length - 1].length);
}
$.each(arr, function () {
var u = new SpeechSynthesisUtterance(this.trim());
window.speechSynthesis.speak(u);
});
});
</script>
<p class="t">MLA format follows the author-page method of in-text citation. This means that the author''s last name and the page number(s) from which the quotation or paraphrase is taken must appear in the text, and a complete reference should appear on your Works Cited page. The author''s name may appear either in the sentence itself or in parentheses following the quotation or paraphrase, but the page number(s) should always appear in the parentheses, not in the text of your sentence.</p>
<p class="t">Joe waited for the train.</p>
<p class="t">The train was late.</p>
<p class="t">Mary and Samantha took the bus.</p>
He resuelto el problema al tener una función de temporizador que llama a la función de pausa () y reanudar () y vuelve a llamar al temporizador. En el evento onend borro el temporizador.
var myTimeout;
function myTimer() {
window.speechSynthesis.pause();
window.speechSynthesis.resume();
myTimeout = setTimeout(myTimer, 10000);
}
...
window.speechSynthesis.cancel();
myTimeout = setTimeout(myTimer, 10000);
var toSpeak = "some text";
var utt = new SpeechSynthesisUtterance(toSpeak);
...
utt.onend = function() { clearTimeout(myTimeout); }
window.speechSynthesis.speak(utt);
...
Esto parece funcionar bien.
He tenido este problema por un tiempo con Google Chrome Speech Synthesis. Después de algunas investigaciones, descubrí lo siguiente:
- La ruptura de las emisiones solo ocurre cuando la voz no es una voz nativa ,
- El corte generalmente ocurre entre 200-300 caracteres ,
- Cuando se rompe puede descongelarlo haciendo
speechSynthesis.cancel();
- El evento " onend " a veces decide no disparar. Una peculiar solución a esto es console.log () fuera del objeto de pronunciación antes de hablarlo. También encontré que envolver la invocación del habla en una devolución de llamada setTimeout ayuda a suavizar estos problemas.
En respuesta a estos problemas, he escrito una función que supera el límite de caracteres, fragmentando el texto en emisiones más pequeñas y reproduciéndolos uno tras otro. Obviamente obtendrás algunos sonidos extraños a veces ya que las oraciones pueden fragmentarse en dos emisiones separadas con un pequeño retraso de tiempo entre cada una, sin embargo , el código intentará dividir estos puntos en los signos de puntuación para hacer que las interrupciones en el sonido sean menos obvias.
Actualizar
He hecho que esta solución esté disponible públicamente en https://gist.github.com/woollsta/2d146f13878a301b36d7#file-chunkify-js . Muchas gracias a Brett Zamir por sus contribuciones.
La función:
var speechUtteranceChunker = function (utt, settings, callback) {
settings = settings || {};
var newUtt;
var txt = (settings && settings.offset !== undefined ? utt.text.substring(settings.offset) : utt.text);
if (utt.voice && utt.voice.voiceURI === ''native'') { // Not part of the spec
newUtt = utt;
newUtt.text = txt;
newUtt.addEventListener(''end'', function () {
if (speechUtteranceChunker.cancel) {
speechUtteranceChunker.cancel = false;
}
if (callback !== undefined) {
callback();
}
});
}
else {
var chunkLength = (settings && settings.chunkLength) || 160;
var pattRegex = new RegExp(''^[//s//S]{'' + Math.floor(chunkLength / 2) + '','' + chunkLength + ''}[.!?,]{1}|^[//s//S]{1,'' + chunkLength + ''}$|^[//s//S]{1,'' + chunkLength + ''} '');
var chunkArr = txt.match(pattRegex);
if (chunkArr[0] === undefined || chunkArr[0].length <= 2) {
//call once all text has been spoken...
if (callback !== undefined) {
callback();
}
return;
}
var chunk = chunkArr[0];
newUtt = new SpeechSynthesisUtterance(chunk);
var x;
for (x in utt) {
if (utt.hasOwnProperty(x) && x !== ''text'') {
newUtt[x] = utt[x];
}
}
newUtt.addEventListener(''end'', function () {
if (speechUtteranceChunker.cancel) {
speechUtteranceChunker.cancel = false;
return;
}
settings.offset = settings.offset || 0;
settings.offset += chunk.length - 1;
speechUtteranceChunker(utt, settings, callback);
});
}
if (settings.modifier) {
settings.modifier(newUtt);
}
console.log(newUtt); //IMPORTANT!! Do not remove: Logging the object out fixes some onend firing issues.
//placing the speak invocation inside a callback fixes ordering and onend issues.
setTimeout(function () {
speechSynthesis.speak(newUtt);
}, 0);
};
Cómo usarlo...
//create an utterance as you normally would...
var myLongText = "This is some long text, oh my goodness look how long I''m getting, wooooohooo!";
var utterance = new SpeechSynthesisUtterance(myLongText);
//modify it as you normally would
var voiceArr = speechSynthesis.getVoices();
utterance.voice = voiceArr[2];
//pass it into the chunking function to have it played out.
//you can set the max number of characters by changing the chunkLength property below.
//a callback function can also be added that will fire once the entire text has been spoken.
speechUtteranceChunker(utterance, {
chunkLength: 120
}, function () {
//some code to execute when done
console.log(''done'');
});
Espero que la gente lo encuentre útil.
Otra sugerencia es hacer algo raro con dot o decir DOT y no respetar la pronunciación del discurso al final de la oración.
var CHARACTER_LIMIT = 200;
var lang = "en";
var text = "MLA format follows the author-page method of in-text citation. This means that the author''s last name and the page number(s) from which the quotation or paraphrase is taken must appear in the text, and a complete reference should appear on your Works Cited page. The author''s name may appear either in the sentence itself or in parentheses following the quotation or paraphrase, but the page number(s) should always appear in the parentheses, not in the text of your sentence. Joe waited for the train. The train was late. Mary and Samantha took the bus.";
speak(text, lang)
function speak(text, lang) {
//Support for multipart text (there is a limit on characters)
var multipartText = [];
if (text.length > CHARACTER_LIMIT) {
var tmptxt = text;
while (tmptxt.length > CHARACTER_LIMIT) {
//Split by common phrase delimiters
var p = tmptxt.search(/[:!?.;]+/);
var part = '''';
//Coludn''t split by priority characters, try commas
if (p == -1 || p >= CHARACTER_LIMIT) {
p = tmptxt.search(/[,]+/);
}
//Couldn''t split by normal characters, then we use spaces
if (p == -1 || p >= CHARACTER_LIMIT) {
var words = tmptxt.split('' '');
for (var i = 0; i < words.length; i++) {
if (part.length + words[i].length + 1 > CHARACTER_LIMIT)
break;
part += (i != 0 ? '' '' : '''') + words[i];
}
} else {
part = tmptxt.substr(0, p + 1);
}
tmptxt = tmptxt.substr(part.length, tmptxt.length - part.length);
multipartText.push(part);
//console.log(part.length + " - " + part);
}
//Add the remaining text
if (tmptxt.length > 0) {
multipartText.push(tmptxt);
}
} else {
//Small text
multipartText.push(text);
}
//Play multipart text
for (var i = 0; i < multipartText.length; i++) {
//Use SpeechSynthesis
//console.log(multipartText[i]);
//Create msg object
var msg = new SpeechSynthesisUtterance();
//msg.voice = profile.systemvoice;
//msg.voiceURI = profile.systemvoice.voiceURI;
msg.volume = 1; // 0 to 1
msg.rate = 1; // 0.1 to 10
// msg.rate = usersetting || 1; // 0.1 to 10
msg.pitch = 1; //0 to 2*/
msg.text = multipartText[i];
msg.speak = multipartText;
msg.lang = lang;
msg.onend = self.OnFinishedPlaying;
msg.onerror = function (e) {
console.log(''Error'');
console.log(e);
};
/*GC*/
msg.onstart = function (e) {
var curenttxt = e.currentTarget.text;
console.log(curenttxt);
//highlight(e.currentTarget.text);
//$(''#showtxt'').text(curenttxt);
//console.log(e);
};
//console.log(msg);
speechSynthesis.speak(msg);
}
}
Sí, la api de síntesis de google se detendrá en algún momento durante el discurso de un texto largo.
Podemos ver que el evento onend evento, onpause y onerror de SpeechSynthesisUtterance no se disparará normalmente cuando ocurre la parada repentina, también lo hace el evento speechSynthesis onerror.
Después de varios intentos, halló speechSynthesis.paused funcionando, y speechSynthesis.resume () puede ayudar a reanudar el discurso.
Por lo tanto, solo necesitamos tener un temporizador para verificar el estado de pausa durante la conversación y llamar a speechSynthesis.resume () para continuar. El intervalo debe ser lo suficientemente pequeño como para evitar fallas al continuar la conversación.
let timer = null;
let reading = false;
let readText = function(text) {
if (!reading) {
speechSynthesis.cancel();
if (timer) {
clearInterval(timer);
}
let msg = new SpeechSynthesisUtterance();
let voices = window.speechSynthesis.getVoices();
msg.voice = voices[82];
msg.voiceURI = ''native'';
msg.volume = 1; // 0 to 1
msg.rate = 1.0; // 0.1 to 10
msg.pitch = 1; //0 to 2
msg.text = text;
msg.lang = ''zh-TW'';
msg.onerror = function(e) {
speechSynthesis.cancel();
reading = false;
clearInterval(timer);
};
msg.onpause = function(e) {
console.log(''onpause in '' + e.elapsedTime + '' seconds.'');
}
msg.onend = function(e) {
console.log(''onend in '' + e.elapsedTime + '' seconds.'');
reading = false;
clearInterval(timer);
};
speechSynthesis.onerror = function(e) {
console.log(''speechSynthesis onerror in '' + e.elapsedTime + '' seconds.'');
speechSynthesis.cancel();
reading = false;
clearInterval(timer);
};
speechSynthesis.speak(msg);
timer = setInterval(function(){
if (speechSynthesis.paused) {
console.log("#continue")
speechSynthesis.resume();
}
}, 100);
reading = true;
}
}
Terminé fragmentando el texto y teniendo algo de inteligencia sobre el manejo de varias puntuaciones como puntos, comas, etc. Por ejemplo, no desea dividir el texto en una coma si es parte de un número (es decir, $ 10,000) .
Lo he probado y parece funcionar en conjuntos arbitrariamente grandes de entrada y también parece funcionar no solo en el escritorio sino también en teléfonos Android e iPhones.
Configure una página github para el sintetizador en: https://github.com/unk1911/speech
Puede verlo en vivo en: http://edeliverables.com/tts/
Una solución simple y efectiva es reanudar periódicamente.
function resumeInfinity() {
window.speechSynthesis.resume();
timeoutResumeInfinity = setTimeout(resumeInfinity, 1000);
}
Puede asociar esto con los eventos onend y onstart, por lo que solo invocará el currículum si es necesario. Algo como:
var utterance = new SpeechSynthesisUtterance();
utterance.onstart = function(event) {
resumeInfinity();
};
utterance.onend = function(event) {
clearTimeout(timeoutResumeInfinity);
};
¡Descubrí esto por casualidad!
¡Espero que esto ayude!