Experimentando con la síntesis del habla

 

 

 

  • Register!

  • Índice
    1. Hola Mundo
    2. Una implementación básica: reproducir y pausar
    3. Yendo más allá: ajustando la velocidad de lectura
    4. Yendo más allá: modificando lo leído
    5. Yendo más allá: ajustes de ritmo sintético
    6. ¿ Está speechSynthesislisto para la producción?
    7. ¿Querer aprender más?
      1. Otras lecturas

    ¿Se pregunta cómo empezar a utilizar Web Speech API? Si no está familiarizado, esta API le brinda a usted (el desarrollador) la capacidad de habilitar la voz en su sitio web en dos direcciones: escuchar a sus usuarios a través de la interfaz SpeechRecognition y responderles a través de la interfaz SpeechSynthesis. En este artículo, Aaron Gustafson lo guía a través de esta API experimental y cubre todo lo que necesita saber para ayudarlo a comprender mejor cómo funciona.

     

    He estado pensando mucho en el habla durante los últimos años. De hecho, ha sido un tema importante en varias de mis charlas últimamente, incluida mi bien recibida charla en Smashing Conference “ Diseñando la conversación ”. Como tal, he estado muy interesado en el desarrollo de Web Speech API.

    Si no está familiarizado, esta API le brinda a usted (el desarrollador) la capacidad de habilitar la voz en su sitio web en dos direcciones: escuchar a sus usuarios a través de la interfaz SpeechRecognition y responderles a través de la interfaz SpeechSynthesis . Todo esto se hace a través de una API de JavaScript, lo que facilita la prueba de soporte. Esta capacidad de prueba lo convierte en un excelente candidato para una mejora progresiva, pero hablaremos de eso en un momento.

    Gran parte de mi interés surge de mi deseo personal de experimentar nuevas formas de interactuar con la web. También soy un gran admirador de los podcasts y me encanta escuchar contenido excelente mientras conduzco y en otras situaciones en las que mis ojos deben estar en otra parte o simplemente estoy demasiado cansado para leer. Web Speech API abre una amplia gama de oportunidades para crear interacciones de usuario increíblemente útiles y naturales al poder escuchar y responder con lenguaje natural:

    – Hola Instapaper, ¡empieza a leer desde mi cola!

    — Claro, Aarón...

    Las posibilidades creadas por este conjunto de API relativamente simple son realmente asombrosas. Hay aplicaciones en accesibilidad, Internet de las cosas, automoción, gobierno, y la lista sigue y sigue. Yendo un paso más allá, imagina combinar esta tecnología con API de traducción en tiempo real (que también comenzaron a aparecer recientemente). De repente, podemos abrir la Web a millones de personas que luchan con la alfabetización o que necesitan servicios en un país donde no leen ni hablan el idioma. Este. Cambios. Todo.

     

    Pero volvamos a la API Web Speech. Como dije, había estado controlando las especificaciones por un tiempo, revisé varias demos y demás, pero aún no había tenido tiempo de jugar. Entonces Dave Rupert finalmente me animó a actuar con un solo tweet:

    En aproximadamente una hora, había conseguido una implementación básica para mi blog que permitiría a los usuarios escuchar una publicación de blog en lugar de leerla. Unas horas más tarde, agregué más funciones, pero no todo fue vino y rosas, y terminé teniendo que eliminar algunas funciones del widget para mejorar su estabilidad. Pero me estoy adelantando.

    Decidí presionar el botón de pausa durante unos días para escribir lo que aprendí y lo que todavía no entiendo completamente con la esperanza de que podamos comenzar a analizar algunas de las mejores prácticas para usar esta increíble característica. Tal vez incluso podamos encontrar algunas formas de mejorarlo.

    Hola Mundo

    Hasta ahora, mis exploraciones en la API Web Speech se han centrado exclusivamente en el ámbito de la síntesis de voz. Llegar a "Hola mundo" es relativamente sencillo y simplemente implica crear un nuevo SpeechSynthesisUtterance(que es lo que quieres decir) y luego pasarlo al método speechSynthesisdel objeto :speak()

    var to_speak = new SpeechSynthesisUtterance('Hello world!');window.speechSynthesis.speak(to_speak);

    No todos los navegadores soportan esta API, aunque la mayoría de los modernos sí lo hacen . Dicho esto, para evitar generar errores, debemos envolver todo en un condicional simple que pruebe la existencia de la característica antes de usarla:

    if ( 'speechSynthesis' in window ) { var to_speak = new SpeechSynthesisUtterance('Hello world!'); window.speechSynthesis.speak(to_speak);}

    Consulte Pen Experimentando con la síntesis del habla, ejemplo 1 de Aaron Gustafson ( @aarongustafson ) en CodePen .

    " Enlace al ejemplo en soundcloud.com "

    Una vez que tenga un ejemplo básico funcionando, podrá hacer bastantes ajustes. Por ejemplo, puede modificar la velocidad de lectura ajustando la propiedad SpeechSynthesisUtterancedel objeto rate. Acepta valores de 0,1 a 10. Considero que 1,4 es una velocidad bastante cómoda; cualquier valor superior a 3 me suena a ruido.

     

    Consulte Pen Experimentando con la síntesis del habla, ejemplo 1 de Aaron Gustafson ( @aarongustafson ) en CodePen .

    También puedes ajustar cosas como el tono , el volumen de la voz, incluso el idioma que se habla y la voz misma . Soy un gran admirador de los valores predeterminados en la mayoría de las cosas, así que te dejaré explorar esas opciones en tu propio tiempo. Para mi experimento, opté por cambiar el valor predeterminado ratea 1.4, y eso fue todo.

    Una implementación básica: reproducir y pausar

    Cuando comencé a trabajar con este código en mi propio sitio web, quería ofrecer cuatro controles a mis lectores:

    • jugar
    • pausa
    • aumentar la velocidad de lectura
    • disminuir la velocidad de lectura

    Los dos primeros fueron relativamente fáciles. Los dos últimos causaron problemas, de los que hablaré en breve.

    Para empezar, repetí como un loro el código que Dave había tuiteado:

    var to_speak = new SpeechSynthesisUtterance( document.querySelector('main').textContent);window.speechSynthesis.speak(to_speak);

    Este código toma el contenido de texto ( textContent) del mainelemento y lo convierte en un archivo SpeechSynthesisUtterance. Luego activa el sintetizador para que pronuncie ese contenido. Suficientemente simple.

    Por supuesto, no quería que el contenido comenzara a leerse inmediatamente, así que comencé a crear una interfaz de usuario para controlarlo. Lo hice en JavaScript, dentro del condicional de detección de funciones, en lugar de HTML, porque no quería que apareciera la interfaz si la función no estaba disponible (o si JavaScript fallaba por algún motivo). Sería frustrante para los usuarios.

    Creé los botones y asigné algunos controladores de eventos para conectar la funcionalidad. Mi primer pase se vio así:

    var $buttons = document.createElement('p'), $button = document.createElement('button'), $play = $button.cloneNode(), $pause = $button.cloneNode(), paused = false, to_speak;if ( 'speechSynthesis' in window ) { // content to speak to_speak = new SpeechSynthesisUtterance( document.querySelector('main').textContent ); // set the rate a little faster than 1x to_speak.rate = 1.4; // event handlers to_speak.onpause = function(){ paused = true; }; // button events function play() { if ( paused ) { paused = false; window.speechSynthesis.resume(); } else { window.speechSynthesis.speak( to_speak ); } } function pause() { window.speechSynthesis.pause(); } // play button $play.innerText = 'Play'; $play.addEventListener( 'click', play, false ); $buttons.appendChild( $play ); // pause button $pause.innerText = 'Pause'; $pause.addEventListener( 'click', pause, false ); $buttons.appendChild( $pause );} else { // sad panda $buttons.innerText = 'Unfortunately your browser doesn’t support this feature.';}document.body.appendChild( $buttons );

    Este código crea un botón de reproducción y un botón de pausa y los agrega al documento. También asigna los controladores de eventos correspondientes. Como era de esperar, el botón de reproducción llama speechSynthesis.speak(), como vimos antes, pero debido a que la pausa también está en juego, lo configuro para que diga el texto seleccionado o continúe hablando (usando speechSynthesis.resume()) si el discurso está en pausa. El botón de pausa lo controla activándolo speechSynthesis.pause(). Realicé un seguimiento del estado del motor de voz utilizando la variable booleana paused. Puedes cambiar los neumáticos de este código en CodePen .

     

    Quiero (ejem) hacer una pausa por un momento para leer el speak()comando, porque es fácil de malinterpretar. A primera vista, se podría pensar que hace que lo proporcionado SpeechSynthesisUtterancese lea en voz alta desde el principio, razón por la cual me gustaría hacerlo resume()después de hacer una pausa. Eso es cierto, pero es sólo una parte. La interfaz de síntesis de voz en realidad mantiene una cola para que se pronuncie el contenido. La llamada speak()envía un mensaje nuevo SpeechSynthesisUtterancea esa cola y hace que el sintetizador comience a leer ese contenido si aún no lo está hablando. Si ya está en el proceso de leer algo, el nuevo contenido ocupa su lugar al final de la cola y espera pacientemente su turno. Si desea ver esto en acción, consulte mi bifurcación de la demostración de velocidad de lectura .

    Si desea borrar la cola por completo en cualquier momento, puede llamar speechSynthesis.cancel(). Al probar la síntesis de voz con contenido de formato largo, es útil tenerlo listo en la consola del navegador.

    Yendo más allá: ajustando la velocidad de lectura

    Como mencioné, también quería darles a los usuarios control sobre la velocidad de lectura utilizada por el sintetizador de voz. Podemos ajustar esto usando la ratepropiedad de un SpeechSynthesisUtteranceobjeto. Eso es fantástico, pero no puedes (al menos actualmente) ajustar la velocidad de a SpeechSynthesisUtteranceuna vez que el sintetizador comienza a reproducirlo, ni siquiera mientras está en pausa. No sé lo suficiente sobre el funcionamiento interno de los sintetizadores de voz para saber si esto es simplemente un descuido en la interfaz o una limitación importante de los sintetizadores en sí, pero me obligó a encontrar una forma creativa de sortear esta limitación.

    Experimenté con varios enfoques diferentes para esto y finalmente me decidí por uno que funciona razonablemente bien, a pesar de que parece excesivo. Pero me estoy adelantando otra vez.

    Cada SpeechSynthesisUtteranceobjeto ofrece un puñado de eventos que puedes conectar para hacer varias cosas. Como era de esperar, onpausese activa cuando el discurso está en pausa, onendse activa cuando el sintetizador ha terminado de leerlo, etc. El SpeechSynthesisEventobjeto pasado a cada uno de estos incluye información sobre lo que está sucediendo con el sintetizador, como la posición del cursor virtual ( charIndex), el período de tiempo después de que la corriente SpeechSynthesisUtterancecomenzó a leerse ( elapsedTime) y una referencia a la SpeechSynthesisUtterancemisma ( utterance). Venta de Minerales

    Originalmente, mi plan para permitir el ajuste de la velocidad de lectura en tiempo real era capturar la posición del cursor virtual mediante un evento de pausa para poder detener e iniciar una nueva grabación a la nueva velocidad. Cuando el usuario ajustaba la velocidad de lectura, pausaba el sintetizador, tomaba el charIndex, retrocedía en el texto hasta el espacio anterior, cortaba desde allí hasta el final de la cadena para recopilar el resto de lo que debía leerse, limpiaba la cola y inicie el sintetizador nuevamente con el resto del contenido. Eso habría funcionado y debería haber sido confiable, pero Chrome seguía dándome un charIndexde 0y en Edge siempre era undefined. Firefox rastreó charIndexperfectamente. Presenté un error para Chromium y uno para Edge también.

     

    Afortunadamente, otro evento, onboundary, se activa cada vez que se alcanza el límite de una palabra u oración. Es un poco más ruidoso, programáticamente hablando, porque onpauseel evento se activa con tanta frecuencia, pero rastrea de manera confiable la posición del cursor virtual en todos los navegadores que admiten síntesis de voz, que es lo que necesitaba.

    Aquí está el código de seguimiento:

    var progress_index = 0;to_speak.onboundary = function( e ) { if ( e.name == 'word' ) { progress_index = e.charIndex; }};

    Una vez que estuve configurado para rastrear el cursor, agregué una entrada numérica a la interfaz de usuario para permitir a los usuarios cambiar la velocidad:

    var $speed = document.createElement('p'), $speed_label = document.createElement('label'), $speed_value = document.createElement('input');// label the field$speed_label.innerText = 'Speed';$speed_label.htmlFor = 'speed_value';$speed.appendChild( $speed_label );// insert the form control$speed_value.type = 'number';$speed_value.id = 'speed_value';$speed_value.min = '0.1';$speed_value.max = '10';$speed_value.step = '0.1';$speed_value.value = Math.round( to_speak.rate * 10 ) / 10;$speed.appendChild( $speed_value );document.body.appendChild($speed);

    Luego, agregué un detector de eventos para rastrear cuándo cambia y actualizar el sintetizador de voz:

    function adjustSpeed() { // cancel the original utterance window.speechSynthesis.cancel(); // find the previous space var previous_space = to_speak.text.lastIndexOf( ' ', progress_index ); // get the remains of the original string to_speak.text = to_speak.text.slice( previous_space ); // math to 1 decimal place speed = Math.round( $speed_value.value * 10 ) / 10; // adjust the rate if ( speed 10 ) { speed = 10; } else if ( speed 0.1 ) { speed = 0.1; } to_speak.rate = speed; // return to speaking window.speechSynthesis.speak( to_speak );}$speed_value.addEventListener( 'change', adjustSpeed, false );

    Esto funciona razonablemente bien, pero al final decidí que no era un gran admirador de la experiencia, ni estaba convencido de que fuera realmente necesaria, por lo que esta funcionalidad permanece comentada en el código fuente de mi sitio web. Puede tomar una decisión después de verlo en acción en CodePen .

    Yendo más allá: modificando lo leído

    En la parte superior de cada publicación de blog, justo después del título, incluyo bastantes metadatos sobre la publicación, incluidos elementos como la fecha de publicación, etiquetas de la publicación, recuentos de comentarios y menciones web, etc. Quería controlar selectivamente qué contenido de esa colección se lee porque solo una parte es realmente relevante en ese contexto. Para mantener la configuración fuera de JavaScript y en el marcado declarativo al que pertenece, opté por que JavaScript busque un classnombre específico, "no leer", y excluya esos elementos del contenido que se leería. Sin embargo, para que funcionara, necesitaba revisar cómo estaba recopilando el contenido para leer en primer lugar.

     

    Quizás recuerdes que estoy usando la textContentpropiedad para extraer el contenido:

    var to_speak = new SpeechSynthesisUtterance( document.querySelector('main').textContent);

    Eso está muy bien cuando quieres capturar todo, pero si quieres ser más selectivo, es mejor que muevas el contenido a la memoria para poder manipularlo sin causar repintados y demás.

    var $content = document.querySelector('main').cloneNode(true);

    Con un clon mainen la memoria, puedo comenzar el proceso de reducirlo solo a lo que quiero:

    var to_speak = new SpeechSynthesisUtterance() $content = document.querySelector('main').cloneNode(true), $skip = $content.querySelectorAll('.dont-read');// don’t readArray.prototype.forEach.call( $skip, function( $el ){ $el.innerHTML = '';});to_speak.text = $content.textContent;

    Aquí, he separado la creación del SpeechSynthesisUtterancepara que el código sea un poco más claro. Luego, cloné el mainelemento ( $content) y construí una nodeListserie de elementos que quiero que se ignoren ( $skip). Luego recorrí el práctico método de nodeListpréstamo y establecí el contenido de cada uno en una cadena vacía, eliminándolos efectivamente del contenido. Al final, configuré la propiedad de texto en la del elemento clonado . Debido a que todo esto se hace en el archivo clonado , la página no se ve afectada.ArrayforEachmaintextContentmain

    Hecho y hecho.

    Yendo más allá: ajustes de ritmo sintético

    Lamentablemente, el valor de a SpeechSynthesisUtterancesólo puede ser texto. Si ingresa HTML, leerá los nombres de las etiquetas y las barras. Es por eso que la mayoría de las demostraciones utilizan una entrada para recopilar lo que desea leer o en lo que se basa textContentpara extraer texto de la página. La razón por la que esto me entristece es que significa que pierdes el control total sobre el ritmo del contenido.

    Pero no todo esta perdido. Los sintetizadores de voz son bastante impresionantes a la hora de reconocer el efecto que debería tener la puntuación en la entonación y el ritmo. Para volver al primer ejemplo que compartí, considere la diferencia cuando coloca una coma entre "hola" y "mundo":

    if ( 'speechSynthesis' in window ) { var to_speak = new SpeechSynthesisUtterance('Hello, world!'); window.speechSynthesis.speak(to_speak);} 

    Consulte Pen Experimentando con la síntesis del habla, ejemplo 2 de Aaron Gustafson ( @aarongustafson ) en CodePen .

    " Enlace al ejemplo en soundcloud.com "

     

    Aquí está el original nuevamente, para que puedas comparar:

    Consulte Pen Experimentando con la síntesis del habla, ejemplo 1 de Aaron Gustafson ( @aarongustafson ) en CodePen .

    " Enlace al ejemplo en soundcloud.com "

    Con esto en mente, decidí modificar el ritmo de la prosa hablada insertando comas artificialmente en los elementos específicos que siguen el patrón que acabo de mostrar para ocultar contenido:

    var $pause_before = $content.querySelectorAll( 'h2, h3, h4, h5, h6, p, li, dt, blockquote, pre, figure, footer');// synthetic pausesArray.prototype.forEach.call( $pause_before, function( $el ){ $el.innerHTML = ' , ' + $el.innerHTML;});

    Mientras hacía esto, también noté algunos problemas con ciertos elementos que se encuentran en el contenido que los rodea. En particular, esto estaba sucediendo con prelos elementos. Para mitigar eso, utilicé el mismo enfoque para intercambiar retornos de carro, saltos de línea y demás por espacios:

    var $space = $content.querySelectorAll('pre');// spacing out contentArray.prototype.forEach.call( $space, function( $el ){ $el.innerHTML = ' ' + $el.innerHTML.replace(/[rnt]/g, ' ') + ' ';});

    Con esos ajustes implementados, estoy increíblemente feliz con la experiencia auditiva. Si desea ver todo este código en contexto, diríjase a mi repositorio de GitHub. Es probable que el código que utilice para colocar la interfaz de usuario en la página deba ser diferente del que hice yo, pero el resto del código debe ser plug-and-play.

    ¿ Está speechSynthesislisto para la producción?

    Tal como está ahora, Web Speech API no se ha convertido en un estándar y ni siquiera está en el camino de los estándares . Es una API experimental y algunos de los detalles de la especificación siguen cambiando. Por ejemplo, la elapsedTimepropiedad de un SpeechSynthesisEventseguimiento original fue de milisegundos y luego cambió a segundos. Si estuviera haciendo cálculos que dependieran de ese número para hacer algo más en la interfaz, podría obtener experiencias muy diferentes en Chrome (que todavía usa milisegundos) y Edge (que usa segundos).

    Si me concedieran un deseo para esta especificación, además de la estandarización, sería el ajuste de velocidad, tono y volumen en tiempo real. Puedo entender la necesidad de reiniciar las cosas para que el texto se lea con otra voz, pero los demás sienten que deberían ser manipulables en tiempo real. Pero repito, no sé nada sobre el funcionamiento interno de los sintetizadores de voz, por lo que podría no ser técnicamente posible.

    En términos de implementaciones reales del navegador, la síntesis de voz básica como la que he cubierto aquí es bastante sólida en los navegadores que admiten la API . Como mencioné, Chrome y Edge actualmente no informan con precisión la posición del cursor virtual cuando la síntesis de voz está en pausa, pero no creo que eso sea un factor decisivo. Lo problemático es lo inestables que se vuelven las cosas cuando empiezas a combinar funciones como ajustes de velocidad de lectura en tiempo real, pausas y demás. A menudo, el sintetizador simplemente deja de funcionar y se niega a volver a funcionar. Si desea que esto suceda, eche un vistazo a una demostración que configuré . Lo más probable es que este problema desaparezca si la API permitiera la manipulación de propiedades en tiempo real, por ejemplo, rateporque no sería necesario cancel()reiniciar el sintetizador con cada ajuste.

    En pocas palabras, si está considerando esto como una mejora progresiva para un sitio web con mucho contenido y solo desea las funciones más básicas, debería estar listo. Si quieres ser sofisticado, es posible que te decepciones o tengas que idear acrobacias de codificación más inteligentes de las que he reunido.

    ¿Querer aprender más?

    Como ocurre con la mayoría de las cosas en la web, aprendí muchísimo viendo las fuentes, demostraciones y demás de otras personas, y la documentación, naturalmente. Estos son algunos de mis favoritos (algunos de los cuales vinculé en contexto):

    • “ Especificación de la API de voz web: borrador del editor ”, W3C
    • “ API de voz web ”, Red de desarrolladores de Mozilla
    • " Aplicaciones web que hablan: Introducción a la API de síntesis de voz ", Eric Bidelman, desarrolladores de Google
    • “ API de síntesis de voz ”, desarrollador de Microsoft (demostración de Edge)

    Otras lecturas

    • Mejora de la experiencia del usuario con la API de voz web
    • Directrices para diseñar con audio
    • ¿Qué es el diseño de experiencia de usuario? Descripción general, herramientas y recursos
    • Cómo el marketing cambió la programación orientada a objetos en JavaScript

    (rb, yk, il, al, mrn)Explora más en

    • Codificación
    • javascript
    • Navegadores
    • API





    Tal vez te puede interesar:

    1. ¿Deberían abrirse los enlaces en ventanas nuevas?
    2. 24 excelentes tutoriales de AJAX
    3. 70 técnicas nuevas y útiles de AJAX y JavaScript
    4. Más de 45 excelentes recursos y repositorios de fragmentos de código

    Experimentando con la síntesis del habla

    Experimentando con la síntesis del habla

    Register! Índice Hola Mundo Una implementaci

    programar

    es

    https://aprendeprogramando.es/static/images/programar-experimentando-con-la-sintesis-del-habla-908-0.jpg

    2024-05-20

     

    Experimentando con la síntesis del habla
    Experimentando con la síntesis del habla

    Si crees que alguno de los contenidos (texto, imagenes o multimedia) en esta página infringe tus derechos relativos a propiedad intelectual, marcas registradas o cualquier otro de tus derechos, por favor ponte en contacto con nosotros en el mail [email protected] y retiraremos este contenido inmediatamente

     

     

    Top 20