Cómo hacer un editor de síntesis de voz

Publicado: 2022-03-10
Resumen rápido ↬ Los asistentes de voz están de camino a los hogares, las muñecas y los bolsillos de las personas. Eso significa que parte de nuestro contenido se pronunciará en voz alta con la ayuda de la síntesis de voz digital. En este tutorial, aprenderá cómo hacer un editor de síntesis de voz Lo que obtiene es lo que escucha (WYGIWYH) utilizando el editor de Sanity.io para texto portátil.

Cuando Steve Jobs presentó la Macintosh en 1984, nos dijo “Hola” desde el escenario. Incluso en ese momento, la síntesis de voz no era realmente una tecnología nueva: Bell Labs desarrolló el vocoder ya a fines de los años 30, y el concepto de una computadora asistente de voz se hizo popular cuando Stanley Kubrick convirtió al vocoder en la voz de HAL9000 en 2001: Una odisea del espacio (1968).

No fue hasta la introducción de Siri de Apple, Amazon Echo y Google Assistant a mediados de la década de 2015 que las interfaces de voz llegaron a los hogares, las muñecas y los bolsillos de un público más amplio. Todavía estamos en una fase de adopción, pero parece que estos asistentes de voz llegaron para quedarse.

En otras palabras, la web ya no es solo texto pasivo en una pantalla . Los editores web y los diseñadores de UX tienen que acostumbrarse a crear contenido y servicios que deben hablarse en voz alta.

Ya nos estamos moviendo rápidamente hacia el uso de sistemas de administración de contenido que nos permiten trabajar con nuestro contenido de forma autónoma y a través de API. La pieza final es crear interfaces editoriales que faciliten la adaptación del contenido para la voz. ¡Así que hagamos precisamente eso!

¡Más después del salto! Continúe leyendo a continuación ↓

¿Qué es SSML?

Mientras que los navegadores web utilizan la especificación W3C para el lenguaje de marcado de hipertexto (HTML) para representar visualmente los documentos, la mayoría de los asistentes de voz utilizan el lenguaje de marcado de síntesis de voz (SSML) al generar voz.

Un ejemplo mínimo usando el elemento raíz <speak> y las etiquetas de párrafo ( <p> ) y oración ( <s> ):

 <speak> <p> <s>This is the first sentence of the paragraph.</s> <s>Here's another sentence.</s> </p> </speak>
Dale al play para escuchar el fragmento:

El SSML comienza a existir cuando introducimos etiquetas para <emphasis> y <prosody> (tono):

 <speak> <p> <s>Put some <emphasis strength="strong">extra weight on these words</emphasis></s> <s>And say <prosody pitch="high" rate="fast">this a bit higher and faster</prosody>!</s> </p> </speak>
Dale al play para escuchar el fragmento:

SSML tiene más funciones, pero esto es suficiente para tener una idea de los conceptos básicos. Ahora, echemos un vistazo más de cerca al editor que usaremos para hacer la interfaz de edición de síntesis de voz.

El editor de texto portátil

Para hacer este editor, usaremos el editor de texto portátil que se incluye en Sanity.io. Portable Text es una especificación JSON para la edición de texto enriquecido, que se puede serializar en cualquier lenguaje de marcado, como SSML. Esto significa que puede usar fácilmente el mismo fragmento de texto en varios lugares usando diferentes lenguajes de marcado.

El editor predeterminado de Sanity.io para Portable Text
Editor predeterminado de Sanity.io para texto portátil (vista previa grande)

Instalando Cordura

Sanity.io es una plataforma para contenido estructurado que viene con un entorno de edición de código abierto creado con React.js. Se tarda dos minutos en ponerlo todo en marcha.

Escriba npm i -g @sanity/cli && sanity init en su terminal y siga las instrucciones. Elija "vacío" cuando se le solicite una plantilla de proyecto.

Si no desea seguir este tutorial y crear este editor desde cero, también puede clonar el código de este tutorial y seguir las instrucciones en README.md .

Cuando se descarga el editor, ejecuta sanity start en la carpeta del proyecto para iniciarlo. Iniciará un servidor de desarrollo que utiliza Hot Module Reloading para actualizar los cambios a medida que edita sus archivos.

Cómo configurar esquemas en Sanity Studio

Crear los archivos del editor

Comenzaremos creando una carpeta llamada ssml-editor en la carpeta / schemas. En esa carpeta, pondremos algunos archivos vacíos:

 /ssml-tutorial/schemas/ssml-editor ├── alias.js ├── emphasis.js ├── annotations.js ├── preview.js ├── prosody.js ├── sayAs.js ├── blocksToSSML.js ├── speech.js ├── SSMLeditor.css └── SSMLeditor.js

Ahora podemos agregar esquemas de contenido en estos archivos. Los esquemas de contenido son lo que define la estructura de datos para el texto enriquecido y lo que utiliza Sanity Studio para generar la interfaz editorial. Son objetos JavaScript simples que en su mayoría requieren solo un name y un type .

También podemos agregar un title y una description para hacerlo un poco más agradable para los editores. Por ejemplo, este es un esquema para un campo de texto simple para un title :

 export default { name: 'title', type: 'string', title: 'Title', description: 'Titles should be short and descriptive' } 
Sanity Studio con un campo de título y un editor para Portable Text
El estudio con nuestro campo de título y el editor predeterminado (Vista previa grande)

Portable Text se basa en la idea de texto enriquecido como datos. Esto es poderoso porque le permite consultar su texto enriquecido y convertirlo en prácticamente cualquier marcado que desee.

Es una matriz de objetos llamados "bloques" que puede considerar como los "párrafos". En un bloque, hay una serie de intervalos secundarios. Cada bloque puede tener un estilo y un conjunto de definiciones de marcas, que describen estructuras de datos distribuidas en los tramos secundarios.

Sanity.io viene con un editor que puede leer y escribir en Portable Text, y se activa colocando el tipo de block dentro de un campo de array , como este:

 // speech.js export default { name: 'speech', type: 'array', title: 'SSML Editor', of: [ { type: 'block' } ] }

Una matriz puede ser de varios tipos. Para un editor de SSML, podrían ser bloques para archivos de audio, pero eso queda fuera del alcance de este tutorial.

Lo último que queremos hacer es agregar un tipo de contenido donde se pueda usar este editor. La mayoría de los asistentes utilizan un modelo de contenido simple de "intenciones" y "cumplimiento":

  • intenciones
    Por lo general, una lista de cadenas utilizadas por el modelo de IA para delinear lo que el usuario quiere hacer.
  • cumplimientos
    Esto sucede cuando se identifica una "intención". Un cumplimiento a menudo es, o al menos, viene con algún tipo de respuesta.

Entonces, hagamos un tipo de contenido simple llamado fulfillment que use el editor de síntesis de voz. Cree un nuevo archivo llamado cumplimiento.js y guárdelo en la carpeta /schema :

 // fulfillment.js export default { name: 'fulfillment', type: 'document', title: 'Fulfillment', of: [ { name: 'title', type: 'string', title: 'Title', description: 'Titles should be short and descriptive' }, { name: 'response', type: 'speech' } ] }

Guarde el archivo y abra schema.js . Agrégalo a tu estudio así:

 // schema.js import createSchema from 'part:@sanity/base/schema-creator' import schemaTypes from 'all:part:@sanity/base/schema-type' import fullfillment from './fullfillment' import speech from './speech' export default createSchema({ name: 'default', types: schemaTypes.concat([ fullfillment, speech, ]) })

Si ahora ejecuta sanity start en su interfaz de línea de comandos dentro de la carpeta raíz del proyecto, el estudio se iniciará localmente y podrá agregar entradas para los cumplimientos. Puede mantener el estudio en funcionamiento mientras continuamos, ya que se recargará automáticamente con nuevos cambios cuando guarde los archivos.

Agregar SSML al editor

De manera predeterminada, el tipo de block le brindará un editor estándar para texto enriquecido visualmente orientado con estilos de encabezado, estilos de decorador para énfasis y fuerte, anotaciones para enlaces y listas. Ahora queremos anular aquellos con los conceptos auditivos que se encuentran en SSML.

Comenzamos definiendo las diferentes estructuras de contenido, con descripciones útiles para los editores, que agregaremos al block en SSMLeditorSchema.js como configuraciones para annotations . Esos son "énfasis", "alias", "prosodia" y "decir como".

Énfasis

Comenzamos con "énfasis", que controla cuánto peso se le da al texto marcado. Lo definimos como una cadena con una lista de valores predefinidos entre los que el usuario puede elegir:

 // emphasis.js export default { name: 'emphasis', type: 'object', title: 'Emphasis', description: 'The strength of the emphasis put on the contained text', fields: [ { name: 'level', type: 'string', options: { list: [ { value: 'strong', title: 'Strong' }, { value: 'moderate', title: 'Moderate' }, { value: 'none', title: 'None' }, { value: 'reduced', title: 'Reduced' } ] } } ] }

Alias

A veces, el término escrito y el hablado difieren. Por ejemplo, desea utilizar la abreviatura de una frase en un texto escrito, pero que la frase completa se lea en voz alta. Por ejemplo:

 <s>This is a <sub alias="Speech Synthesis Markup Language">SSML</sub> tutorial</s>
Dale al play para escuchar el fragmento:

El campo de entrada para el alias es una cadena simple:

 // alias.js export default { name: 'alias', type: 'object', title: 'Alias (sub)', description: 'Replaces the contained text for pronunciation. This allows a document to contain both a spoken and written form.', fields: [ { name: 'text', type: 'string', title: 'Replacement text', } ] }

Prosodia

Con la propiedad de prosodia podemos controlar diferentes aspectos de cómo debe pronunciarse el texto, como el tono, la velocidad y el volumen. El marcado para esto puede verse así:

 <s>Say this with an <prosody pitch="x-low">extra low pitch</prosody>, and this <prosody rate="fast" volume="loud">loudly with a fast rate</prosody></s>
Dale al play para escuchar el fragmento:

Esta entrada tendrá tres campos con opciones de cadena predefinidas:

 // prosody.js export default { name: 'prosody', type: 'object', title: 'Prosody', description: 'Control of the pitch, speaking rate, and volume', fields: [ { name: 'pitch', type: 'string', title: 'Pitch', description: 'The baseline pitch for the contained text', options: { list: [ { value: 'x-low', title: 'Extra low' }, { value: 'low', title: 'Low' }, { value: 'medium', title: 'Medium' }, { value: 'high', title: 'High' }, { value: 'x-high', title: 'Extra high' }, { value: 'default', title: 'Default' } ] } }, { name: 'rate', type: 'string', title: 'Rate', description: 'A change in the speaking rate for the contained text', options: { list: [ { value: 'x-slow', title: 'Extra slow' }, { value: 'slow', title: 'Slow' }, { value: 'medium', title: 'Medium' }, { value: 'fast', title: 'Fast' }, { value: 'x-fast', title: 'Extra fast' }, { value: 'default', title: 'Default' } ] } }, { name: 'volume', type: 'string', title: 'Volume', description: 'The volume for the contained text.', options: { list: [ { value: 'silent', title: 'Silent' }, { value: 'x-soft', title: 'Extra soft' }, { value: 'medium', title: 'Medium' }, { value: 'loud', title: 'Loud' }, { value: 'x-loud', title: 'Extra loud' }, { value: 'default', title: 'Default' } ] } } ] }

Saya s

El último que queremos incluir es <say-as> . Esta etiqueta nos permite ejercer un poco más de control sobre cómo se pronuncia cierta información. Incluso podemos usarlo para emitir pitidos si necesita redactar algo en las interfaces de voz. Eso es @!%& útil!

 <s>Do I have to <say-as interpret-as="expletive">frakking</say-as> <say-as interpret-as="verbatim">spell</say-as> it out for you!?</s>
Dale al play para escuchar el fragmento:
 // sayAs.js export default { name: 'sayAs', type: 'object', title: 'Say as...', description: 'Lets you indicate information about the type of text construct that is contained within the element. It also helps specify the level of detail for rendering the contained text.', fields: [ { name: 'interpretAs', type: 'string', title: 'Interpret as...', options: { list: [ { value: 'cardinal', title: 'Cardinal numbers' }, { value: 'ordinal', title: 'Ordinal numbers (1st, 2nd, 3th...)' }, { value: 'characters', title: 'Spell out characters' }, { value: 'fraction', title: 'Say numbers as fractions' }, { value: 'expletive', title: 'Blip out this word' }, { value: 'unit', title: 'Adapt unit to singular or plural' }, { value: 'verbatim', title: 'Spell out letter by letter (verbatim)' }, { value: 'date', title: 'Say as a date' }, { value: 'telephone', title: 'Say as a telephone number' } ] } }, { name: 'date', type: 'object', title: 'Date', fields: [ { name: 'format', type: 'string', description: 'The format attribute is a sequence of date field character codes. Supported field character codes in format are {y, m, d} for year, month, and day (of the month) respectively. If the field code appears once for year, month, or day then the number of digits expected are 4, 2, and 2 respectively. If the field code is repeated then the number of expected digits is the number of times the code is repeated. Fields in the date text may be separated by punctuation and/or spaces.' }, { name: 'detail', type: 'number', validation: Rule => Rule.required() .min(0) .max(2), description: 'The detail attribute controls the spoken form of the date. For detail='1' only the day fields and one of month or year fields are required, although both may be supplied' } ] } ] }

Ahora podemos importarlos en un archivo annotations.js , lo que hace las cosas un poco más ordenadas.

 // annotations.js export {default as alias} from './alias' export {default as emphasis} from './emphasis' export {default as prosody} from './prosody' export {default as sayAs} from './sayAs'

Ahora podemos importar estos tipos de anotaciones a nuestros esquemas principales:

 // schema.js import createSchema from "part:@sanity/base/schema-creator" import schemaTypes from "all:part:@sanity/base/schema-type" import fulfillment from './fulfillment' import speech from './ssml-editor/speech' import { alias, emphasis, prosody, sayAs } from './annotations' export default createSchema({ name: "default", types: schemaTypes.concat([ fulfillment, speech, alias, emphasis, prosody, sayAs ]) })

Finalmente, ahora podemos agregarlos al editor de esta manera:

 // speech.js export default { name: 'speech', type: 'array', title: 'SSML Editor', of: [ { type: 'block', styles: [], lists: [], marks: { decorators: [], annotations: [ {type: 'alias'}, {type: 'emphasis'}, {type: 'prosody'}, {type: 'sayAs'} ] } } ] }

Tenga en cuenta que también agregamos matrices vacías a styles y decorators . Esto deshabilita los estilos y decoradores predeterminados (como negrita y énfasis) ya que no tienen mucho sentido en este caso específico.

Personalización de la apariencia

Ahora tenemos la funcionalidad en su lugar, pero dado que no hemos especificado ningún ícono, cada anotación usará el ícono predeterminado, lo que hace que el editor sea difícil de usar para los autores. ¡Así que arreglemos eso!

Con el editor de Portable Text es posible inyectar componentes React tanto para los íconos como para la forma en que se debe representar el texto marcado. Aquí, solo dejaremos que algunos emoji hagan el trabajo por nosotros, pero obviamente podrías llegar lejos con esto, haciéndolos dinámicos, etc. Para la prosody , incluso haremos que el ícono cambie según el volumen seleccionado. Tenga en cuenta que omití los campos en estos fragmentos por brevedad, no debe eliminarlos en sus archivos locales.

 // alias.js import React from 'react' export default { name: 'alias', type: 'object', title: 'Alias (sub)', description: 'Replaces the contained text for pronunciation. This allows a document to contain both a spoken and written form.', fields: [ /* all the fields */ ], blockEditor: { icon: () => '', render: ({ children }) => <span>{children} </span>, }, };
 // emphasis.js import React from 'react' export default { name: 'emphasis', type: 'object', title: 'Emphasis', description: 'The strength of the emphasis put on the contained text', fields: [ /* all the fields */ ], blockEditor: { icon: () => '', render: ({ children }) => <span>{children} </span>, }, };
 // prosody.js import React from 'react' export default { name: 'prosody', type: 'object', title: 'Prosody', description: 'Control of the pitch, speaking rate, and volume', fields: [ /* all the fields */ ], blockEditor: { icon: () => '', render: ({ children, volume }) => ( <span> {children} {['x-loud', 'loud'].includes(volume) ? '' : ''} </span> ), }, };
 // sayAs.js import React from 'react' export default { name: 'sayAs', type: 'object', title: 'Say as...', description: 'Lets you indicate information about the type of text construct that is contained within the element. It also helps specify the level of detail for rendering the contained text.', fields: [ /* all the fields */ ], blockEditor: { icon: () => '', render: props => <span>{props.children} </span>, }, }; 
El editor SSML personalizado
El editor con nuestras marcas SSML personalizadas (vista previa grande)

Ahora tiene un editor para editar texto que pueden usar los asistentes de voz. Pero, ¿no sería útil si los editores también pudieran obtener una vista previa de cómo sonará realmente el texto?

Agregar un botón de vista previa usando el texto a voz de Google

La compatibilidad con la síntesis de voz nativa está en camino para los navegadores. Pero en este tutorial, usaremos la API de texto a voz de Google que admite SSML. La creación de esta funcionalidad de vista previa también será una demostración de cómo serializa Portable Text en SSML en cualquier servicio para el que desee utilizarlo.

Envolviendo el editor en un componente React

Comenzamos abriendo el archivo SSMLeditor.js y agregamos el siguiente código:

 // SSMLeditor.js import React, { Fragment } from 'react'; import { BlockEditor } from 'part:@sanity/form-builder'; export default function SSMLeditor(props) { return ( <Fragment> <BlockEditor {...props} /> </Fragment> ); }

Ahora hemos envuelto el editor en nuestro propio componente React. Todos los accesorios que necesita, incluidos los datos que contiene, se transmiten en tiempo real. Para usar realmente este componente, debe importarlo a su archivo speech.js :

 // speech.js import React from 'react' import SSMLeditor from './SSMLeditor.js' export default { name: 'speech', type: 'array', title: 'SSML Editor', inputComponent: SSMLeditor, of: [ { type: 'block', styles: [], lists: [], marks: { decorators: [], annotations: [ { type: 'alias' }, { type: 'emphasis' }, { type: 'prosody' }, { type: 'sayAs' }, ], }, }, ], }

Cuando guarde esto y el estudio se vuelva a cargar, debería verse exactamente igual, pero eso se debe a que aún no hemos comenzado a ajustar el editor.

Convertir texto portátil a SSML

El editor guardará el contenido como Texto portátil, una matriz de objetos en JSON que facilita la conversión de texto enriquecido al formato que necesite. Cuando convierte Portable Text a otra sintaxis o formato, lo llamamos "serialización". Por lo tanto, los "serializadores" son las recetas de cómo se debe convertir el texto enriquecido. En esta sección, agregaremos serializadores para síntesis de voz.

Ya ha creado el archivo blocksToSSML.js . Ahora necesitaremos agregar nuestra primera dependencia. Comience ejecutando el comando de terminal npm init -y dentro de la ssml-editor . Esto agregará un paquete.json donde se enumerarán las dependencias del editor.

Una vez hecho esto, puede ejecutar npm install @sanity/block-content-to-html para obtener una biblioteca que facilite la serialización de Portable Text. Usamos la biblioteca HTML porque SSML tiene la misma sintaxis XML con etiquetas y atributos.

Este es un montón de código, así que no dudes en copiarlo y pegarlo. Explicaré el patrón justo debajo del fragmento:

 // blocksToSSML.js import blocksToHTML, { h } from '@sanity/block-content-to-html' const serializers = { marks: { prosody: ({ children, mark: { rate, pitch, volume } }) => h('prosody', { attrs: { rate, pitch, volume } }, children), alias: ({ children, mark: { text } }) => h('sub', { attrs: { alias: text } }, children), sayAs: ({ children, mark: { interpretAs } }) => h('say-as', { attrs: { 'interpret-as': interpretAs } }, children), break: ({ children, mark: { time, strength } }) => h('break', { attrs: { time: '${time}ms', strength } }, children), emphasis: ({ children, mark: { level } }) => h('emphasis', { attrs: { level } }, children) } } export const blocksToSSML = blocks => blocksToHTML({ blocks, serializers })

Este código exportará una función que toma la matriz de bloques y los recorre. Siempre que un bloque contenga una mark , buscará un serializador para el tipo. Si ha marcado algún texto para que tenga emphasis , utilice esta función del objeto serializadores:

 emphasis: ({ children, mark: { level } }) => h('emphasis', { attrs: { level } }, children)

¿Tal vez reconozcas el parámetro desde donde definimos el esquema? La función h() nos permite definir un elemento HTML, es decir, aquí hacemos “trampas” y hace que devuelva un elemento SSML llamado <emphasis> . También le asignamos el level de atributo, si está definido, y colocamos los elementos children dentro de él, que en la mayoría de los casos será el texto que haya marcado con emphasis .

 { "_type": "block", "_key": "f2c4cf1ab4e0", "style": "normal", "markDefs": [ { "_type": "emphasis", "_key": "99b28ed3fa58", "level": "strong" } ], "children": [ { "_type": "span", "_key": "f2c4cf1ab4e01", "text": "Say this strongly!", "marks": [ "99b28ed3fa58" ] } ] }

Así es como la estructura anterior en Portable Text se serializa en este SSML:

 <emphasis level="strong">Say this strongly</emphasis>

Si desea compatibilidad con más etiquetas SSML, puede agregar más anotaciones en el esquema y agregar los tipos de anotaciones a la sección de marks en los serializadores.

Ahora tenemos una función que devuelve el marcado SSML de nuestro texto enriquecido marcado. La última parte es crear un botón que nos permita enviar este marcado a un servicio de texto a voz.

Agregar un botón de vista previa que le responda

Idealmente, deberíamos haber utilizado las capacidades de síntesis de voz del navegador en la API web. De esa manera, nos habríamos ido con menos código y dependencias.

Sin embargo, a principios de 2019, la compatibilidad del navegador nativo con la síntesis de voz aún se encuentra en sus primeras etapas. Parece que el soporte para SSML está en camino, y hay pruebas de conceptos de implementaciones de JavaScript del lado del cliente para él.

Lo más probable es que vaya a utilizar este contenido con un asistente de voz de todos modos. Tanto el Asistente de Google como Amazon Echo (Alexa) admiten SSML como respuestas en un cumplimiento. En este tutorial, utilizaremos la API de texto a voz de Google, que también suena bien y es compatible con varios idiomas.

Comience por obtener una clave API registrándose en Google Cloud Platform (será gratis para el primer millón de caracteres que procese). Una vez que se haya registrado, puede crear una nueva clave de API en esta página.

Ahora puede abrir su archivo PreviewButton.js y agregarle este código:

 // PreviewButton.js import React from 'react' import Button from 'part:@sanity/components/buttons/default' import { blocksToSSML } from './blocksToSSML' // You should be careful with sharing this key // I put it here to keep the code simple const API_KEY = '<yourAPIkey>' const GOOGLE_TEXT_TO_SPEECH_URL = 'https://texttospeech.googleapis.com/v1beta1/text:synthesize?key=' + API_KEY const speak = async blocks => { // Serialize blocks to SSML const ssml = blocksToSSML(blocks) // Prepare the Google Text-to-Speech configuration const body = JSON.stringify({ input: { ssml }, // Select the language code and voice name (AF) voice: { languageCode: 'en-US', name: 'en-US-Wavenet-A' }, // Use MP3 in order to play in browser audioConfig: { audioEncoding: 'MP3' } }) // Send the SSML string to the API const res = await fetch(GOOGLE_TEXT_TO_SPEECH_URL, { method: 'POST', body }).then(res => res.json()) // Play the returned audio with the Browser's Audo API const audio = new Audio('data:audio/wav;base64,' + res.audioContent) audio.play() } export default function PreviewButton (props) { return <Button style={{ marginTop: '1em' }} onClick={() => speak(props.blocks)}>Speak text</Button> }

He mantenido este código de botón de vista previa al mínimo para que sea más fácil seguir este tutorial. Por supuesto, puede construirlo agregando estado para mostrar si la vista previa se está procesando o hacer posible la vista previa con las diferentes voces que admite la API de Google.

Agregue el botón a SSMLeditor.js :

 // SSMLeditor.js import React, { Fragment } from 'react'; import { BlockEditor } from 'part:@sanity/form-builder'; import PreviewButton from './PreviewButton'; export default function SSMLeditor(props) { return ( <Fragment> <BlockEditor {...props} /> <PreviewButton blocks={props.value} /> </Fragment> ); }

Ahora debería poder marcar su texto con las diferentes anotaciones y escuchar el resultado al presionar "Leer texto". Genial, ¿no?

Has creado un editor de síntesis de voz, ¿y ahora qué?

Si ha seguido este tutorial, ha visto cómo puede usar el editor de Portable Text en Sanity Studio para hacer anotaciones personalizadas y personalizar el editor. Puede usar estas habilidades para todo tipo de cosas, no solo para hacer un editor de síntesis de voz. También ha visto cómo serializar Portable Text en la sintaxis que necesita. Obviamente, esto también es útil si está creando interfaces en React o Vue. Incluso puede usar estas habilidades para generar Markdown desde Portable Text.

No hemos cubierto cómo usas esto junto con un asistente de voz. Si desea intentarlo, puede usar gran parte de la misma lógica que con el botón de vista previa en una función sin servidor y configurarlo como el punto final de la API para un cumplimiento mediante webhooks, por ejemplo, con Dialogflow.

Si desea que escriba un tutorial sobre cómo usar el editor de síntesis de voz con un asistente de voz, no dude en darme una pista en Twitter o compartir en la sección de comentarios a continuación.

Lectura adicional en SmashingMag:

  • Experimentando con SpeechSynthesis
  • Mejora de la experiencia del usuario con la Web Speech API
  • API de accesibilidad: una clave para la accesibilidad web
  • Creación de un chatbot de IA simple con Web Speech API y Node.js