Mezclando voces de Polly con pistas de audio

Francisco Rivas
Diseñando para la Voz
12 min readMay 6, 2019

--

Hace un par de semanas Daniel Mittendorf publicaba un tuit apuntando a un pequeño proyecto en el que mostraba cómo se podían mezclar voces de Polly con sonidos de fondo para ofrecer una experiencia más completa en nuestras Skills. El proyecto está desarrollado en JS utilizando el ASK SDK para NodeJS. Inspirado en este proyecto he creado la versión Python utilizando el ASK SDK para Python.

¿Qué esperar de este artículo?

  • Comprender cómo mezclar voces de Polly con clips de audio de fondo.

Requisitos

  • Cuenta en la Consola de AWS. Esto para poder utilizar Lambda, S3, entre otros.
  • Cuenta de desarrollador de Alexa. Esto permite crear Skills.
  • Conocimientos básicos de linea de comandos.
  • Archivos de audio para utilizar de fondo (más detalles debajo).

Acerca de AWS Polly

¿Qué es?

El servicio AWS Polly te permite convertir texto en habla (TTS, text-to-speech).

Precios

Como (casi) todos los servicios de AWS ofrecen un plan gratuito de hasta 5 millones de caracteres al mes o solicitudes marcas de voz durante el primer año de utilización desde que se envía la primera solicitud. Adicionalmente, ofrecen 1 millón de caracteres o solicitudes de marcas de voz al mes por $4, fuera de la capa gratuita.

Por ejemplo, se pueden enviar 1000 solicitudes de 1000 caracteres, esto tendría un precio de $4.

La carta de Jeff Bezos a los accionistas de este año son unos 15.500 caracteres tendría un precio de $0,006.

Voces

Ofrece 57 voces en un total de 28 idiomas, más información aquí. En el caso del Español (España) hay 3:

Conchita: Da la impresión de una mujer mas madura, diría que de alrededor de 50 años. Me parece una voz muy bonita para historias, cuentos, consejos de madre, resumen de noticias.

Lucía: Es notablemente más joven. Diría que menos de 40 años y más de 28 años. Me parece también una voz muy bonita y apropiada para anuncios de eventos musicales o de arte en general, productos cosméticos.

Enrique: Diría que debe tener de 38 a 45 años. Buena voz para resumen de noticias, resumen deportivo.

Puedes probar estas y otras voces aquí.

Importante

  • El stream de audio generado por AWS Polly tiene una frecuencia de muestreo (sample rate) de 8000Hz, 16000Hz y 220250Hz (por defecto). Esto es primordial al momento de mezclar con otros streams de audio y en nuestro caso un MP3. Es necesario que tengan el mismo valor.

Archivos de audio para nuestras Skills

Conseguir clips de audio o incluso temas enteros, a día de hoy, es mas sencillo. Solo debemos tener en cuenta el tema de las licencias.

Existen básicamente tres tipos de licencias:

Royalty free: Son las licencias que permiten al consumidor utilizar una pista de audio que tiene derechos reservados (copyright) sin tener que pagar royalties recurrentes por su reproducción. Este tipo de licencias se pagan solo una vez.

Public Domain: Son las licencias que permiten al consumidor la reproducción de contenido que no tiene derechos reservados (copyright), sin permiso o consentimiento del autor de forma gratuita. Esto en esencia implica que este contenido puede ser copiado, distribuido, interpretado y mostrado en público gratuitamente como si perteneciese a todo el mundo.

Creative Commons: Es una de las múltiples licencias publicas con derechos reservados. Esta permite la distribución gratuita de contenido con derechos reservados. La idea de esta licencia es garantizar de cierta forma que el autor recibe el reconocimiento justo por su autoría. Muchos de las pistas de audio disponibles para utilizar están protegidos bajo esta licencia. A día de hoy este tipo de licencias son muy utilizadas en todo tipo de contenido imágenes, audio, vídeo, iconos, fotos, etc.

Aquí tienes una recopilación de sitios en los que puedes obtener música.

SSML: La etiqueta audio

Cuando desarrollamos nuestras Skills el servicio de Alexa se encarga de convertir el texto en habla, encargándose también de las pausas luego de cada signo de puntuación, entonando apropiadamente al encontrar signos de interrogación o exclamación, etc.

El Voice Browser Working Group de la W3C, como parte de la actividad W3C Voice Browser, han decidido crear un conjunto de estándares para el acceso a la web utilizando el habla, uno de ellos es el Lenguaje de Marcado de Síntesis del Habla o Speech Synthesis Markup Language (SSML). SSML se basa en el lenguaje de marcado XML, ofreciendo etiquetas adicionales para generar habla sintética en la web y otras aplicaciones. De esta forma se puede controlar la velocidad de pronunciación, el pitch, rate, volumen, etc en diferentes plataformas.

Esta etiqueta permite hace que el servicio de Alexa reproduzca un archivo de audio al momento de interpretar (render) la respuesta. Se utiliza generalmente para incluir audio pre-grabado como efectos de sonido, música de introducción, sonido de marca, etc.

La etiqueta puede ser incluida en cualquier parte del texto que deseamos que el servicio de Alexa convierta. Por ejemplo:

<speak>
Bienvenidos a Pumped Crossfit.
<audio src="https://s3-eu-west-1.amazonaws.com/mixedpolly/hip_hop_sample.mp3" />
Te ofrecemos información sobre nuestros WODs y tus clases.
</speak>

En este caso Alexa dice “Bienvenidos a Pumped Crossfit” y reproduce la música de introducción seguido del mensaje “Te ofrecemos información sobre nuestros WODs y tus clases”.

Importante

  • En una misma respuesta se pueden incluir máximo 5 audios.
  • La duración máxima de los audios incluidos es de 240 segundos.
  • El formato de audio soportado es MP3.
  • Los archivos deben estar disponibles públicamente y servidos por HTTPS.
  • Certificados SSL auto-firmados no están permitidos.
  • El bit rate debe ser 48kpbs. Este rate es de buena calidad para el habla pero no lo suficiente para música.
  • El sample rate puede ser de 22050Hz, 24000Hz o 16000Hz.
  • No incluir información sensible en los audios.
  • No se requiere autenticación para solicitar los archivos de audio.
  • Se sugiere que los archivos estén almacenados en un servidor cercano al servidor donde está almacenada la Skill.
  • Es posible servidor HLS (HTTP Live Streaming), sin embargo, tomando en cuenta que es live seguramente excede los 240 segundos por tanto fallará. En este caso no hay diferencia entre servir un archivo estático (MP3).
  • Si utilizamos S3 para almacenar los archivos de audio, debemos agregar las políticas correspondientes a nuestro usuario Lambda (usualmente lambda_basic_execution). Además el bucket debe ser público y los archivos también. Es posible utilizar CORS para esto.

Herramientas

En muchos casos será necesario re-codificar el archivo para cumplir con las condiciones del servicio de Alexa. Para ello existen unas cuantas herramientas: Audacity, SoX y FFmepg. Recomiendo dar un vistazo a la documentación de Amazon con respecto a la re-codificación.

En este caso utilizamos SoX. De hecho reutilizo el ejecutable disponible en el proyecto de Daniel aquí.

Es muy importante que la frecuencia de muestreo del audio de fondo sea de 22050Hz ya que es la frecuencia del stream de audio de Polly es esta.

Classic Skill Intro: Objetivo, estructura, paquetes

Photo by Helloquence on Unsplash

Objetivo

La Skill te da la bienvenida y solicita un nombre, dependiendo del género del nombre continuará utilizando una voz u otra. Es un proyecto de ejemplo.

Estructura

La Skill tiene los siguientes requests e intents:

LaunchRequest
HelpIntent
CancelOrStopIntent
FallbackIntent
SessionEndedRequest
CatchAllException
HelloWorldIntent

A grandes rasgos la idea es:

  1. Inicia la Skill dando la bienvenida. Es la voz de Lucía con música de fondo (LaunchRequest).
  2. Solicitará un nombre y dependiendo del genero de este en el siguiente mensaje continuará utilizando la voz de Lucía o cambiará a la de Enrique (HelloWorldIntent).
  3. Se cierra la sesión.

Paquetes adicionales

Necesitamos algunos paquetes adicionales para poder realizar las distintas tareas: utilizar AWS Polly, mezclar la pista de audio y el stream de Polly, subir el archivo de audio a S3, determinar el genero del nombre, etc. Estos paquetes son:

  • boto3 (viene por defecto al instalar ask_sdk): lo utilizaré para crear una conexión a S3 y a Polly.
  • subprocess: Para las tareas que tienen que ver con el entorno, como copiar archivos.
  • hashlib: Crear el nombre del archivo resultante de la mezcla del stream de audio de Polly y la pista de fondo.
  • mutagen: Permite obtener la duración del stream de audio obtenido de Polly.
  • gender_guesser: Es un paquete que te permite saber el género de un nombre.

Veamos cómo funciona la Skill

El proyecto completo lo puedes conseguir aquí. El archivo principal se llama hello_world.py -el nombre mas original del mundo :)-.

Necesitamos que en el entorno en el que se ejecuta nuestra función Lambda esté disponible el comando sox que nos permitirá hacer la mezcla del stream de audio de Polly y la música de fondo.

En el directorio audio de la función Lambda incluyo el ejecutable de sox que es posible ejecutar en ese entorno. El entorno de la función Lambda es de solo lectura. Necesitamos permisos de ejecución sobre este comando y permisos de lectura para crear un archivo mp3 con el stream de audio que obtenemos de Poly y la mezcla final del audio mezclado. La única forma de hacer esto en el entorno Lambda es utilizando el directorio /tmp.

Definimos algunas variables que vamos a utilizar:

Línea a línea:

#1, definimos la región en la que nos encontramos.

#2, definimos el nombre del bucket S3 en el que vamos a almacenar los archivos de audio.

#3, definimos la URL que corresponde al servicio Polly.

#4, definimos la URL del bucket que utilizaremos para almacenar los archivos. Puede ser otro servicio siempre y cuando tenga un certificado SSL firmado por una autoridad reconocida por Amazon. Los objetos deben ser públicos, esto es, deben estar disponibles desde internet.

#4 y #6, definimos la ubicación de los archivos que vamos a utilizar como pista de fondo. Es importante mencionar que utilizamos la variable de entorno LAMBDA_TASK_ROOT que nos da acceso al sistema de archivos del entorno en el que nuestra función Lambda se está ejecutando. Cuando cargamos nuestra función Lambda el paquete se despliega en /var/task por lo que si hacemos ls en ese directorio veremos todos los directorios de nuestra función, entre ellos se encuentra el directorio audio que es donde he decidido almacenar los archivos de audio que quiero utilizar como música de fondo.

Creamos una función que se encargue de hacer una copia del ejecutable com permisos de ejecución en /tmp en caso de que no encuentre el ejecutable.

Creamos una función que devuelve un objeto Polly que utilizamos para convertir el texto en habla con la voz que elegimos.

Ahora la parte mágica…

Ahora lo mas importante, la función que se encarga de utilizar Polly para crear el stream de audio, almacenarlo en un archivo y realizar la mezcla con la pista de audio de fondo.

La función generatePollyMix recibe como parámetros:

  • El objeto polly que obtenemos de la función connectToPolly.
  • El texto que deseamos convertir (text).
  • El nombre de la voz que deseamos utilizar (voice) — recordamos que puede ser Lucía, Conchita o Enrique.
  • La ruta al archivo que tiene la pista de audio (backgroundSFX).
  • Aunque tiene un valor por defecto (mp3), el formato del archivo resultante (format).

Línea a línea:

#2, podemos ver como utilizamos el método synthesize_speech que recibe como parámetros el formato, el texto y la voz que queremos utilizar para el audio.

#3, abrimos (creamos) un archivo en modo escritura de tipo binario (w = write, b = binary). La razon de que este sea binario es porque la información del stream de audio que se obtiene de Polly es en bytes.

#4, #5 y #6, se encargan de escribir la información en el archivo y cerrarlo. Se escribe en /tmp porque es el sitio disponible en el entorno en el que ejecutamos la función Lambda.

#8 y #9, primero hacemos un parsing y luego calculamos la duración del archivo que tiene el audio convertido por Polly.

#11, hacemos la mezcla, de la siguiente forma:

  • /tmp/sox es la ruta del ejecutable de la herramienta que nos permite hacer maravillas con archivos de audio.
  • -m indica a sox que debe combinar los archivos siguientes.
  • {backgroundSFX} es la ruta al archivo (pista) de audio de fondo.
  • -C 48.01 indica el factor de compresión. Esto varía dependiendo del formato. El valor sugerido es 48.01
  • /tmp/output.mp3 es el nombre del archivo resultante de la mezcla.
  • rate NNNNNHz es la frecuencia de muestreo. Utilizamos la más alta para obtener la mejor relación calidad/tamaño de archivo.
  • gain -l 16 indica incremento o atenuación en la potencia, se indica en decibeles (dB). En caso de ser positivo incrementa, en caso de ser negativo atenúa. En este caso especificamos el flag -l 16 que indica la limitación de 16 dB sin cortar el audio. Habría que probar con compand.
  • trim 0 {audio_length + 4} indica que ajustaremos la duración del audio de salida a la duración del audio obtenido de Polly. Indica desde los 0 segundos hasta la duración de ese audio + 4 segundos. Los 4 segundos adicionales es para no cortar de lleno el audio. También es posible hacer un fade out.

#13, ejecutamos el comando descrito anteriormente devolviendo el código resultante de la ejecución para verificar que el proceso ha terminado correctamente. Redirecciono la salida estándar (STDOUT) incluso al canal de errores para obtener siempre la información pase lo que pase.

Tenemos otra función getS3AudioFile:

Línea a línea:

#2, lo que hago es calcular el md5 del archivo resultante y utilizo eso como el nombre del archivo que subiré a S3, esto me permite verificar posteriormente si un archivo con el mismo contenido que he creado ya existe y no volver a invertir una solicitud de conversión.

#3, utilizo el cliente S3 que creado para subir el objeto (archivo). Adicionalmente, de forma explícita establezco los permisos a ese objeto. Esto es porque por defecto son objetos privados y por tanto la función Lambda no tendría acceso a este.

#5 y #6, elimino los archivos del sistema de archivos local, no me interesa consumir espacio innecesario.

#7, devuelvo la etiqueta audio que utilizare en la respuesta de Alexa.

Iniciando la Skill: Sobre LaunchRequest

Línea a línea:

#2, llamamos a la función prepareTools() que se encarga de asegurar siempre que sox está disponible.

#4, llamamos a la función connectToPolly() que se encarga de crear el objeto polly que luego utilizamos para hacer la conversión de texto al habla.

#6, llamamos a la función generatePollyMix pasando todos los parámetros necesarios. Aunque almaceno el valor que devuelve esta función no lo utilizo, por el momento, para hacer la verificación de su ejecución exitosa. Es bueno agregarla.

#8, llamamos a la función getS3AudioFile() que nos devuelve la URL del archivo de audio resultante de la mezcla.

#10, es donde especificamos realmente la respuesta que Alexa dará al usuario, nos fijamos precisamente en {audio_mix} que es, en este caso, el texto de bienvenida con la música de fondo.

Sobre HelloWorldIntent

Como he mencionado mas arriba, durante el inicio de la Skill se solicita al usuario que diga un nombre. En este intent se pretende determinar el género del nombre que ha dicho el usuario y utilizar una voz de Polly que corresponda con ese género — no parece de entrada que esto tenga alguna utilidad :).

Línea a línea:

#3, de igual forma llamamos a la función connectToPolly() para obtener un objeto que nos permite acceder a los servicios de AWS Polly.

#5, creo el objeto gender_detector que nos permite acceder a los métodos de este paquete.

#7, utilizamos uno de los mas esperados helpers, el get_slot_value. Que devuelve el valor que se ha obtenido del usuario para el slot especificado. Para más información mirar aquí y aquí.

#10, utilizamos el gender_detector para que determine el género del nombre que ha dicho el usuario. Utilizamos el método capitalize() puesto que este método espera que la primera letra del nombre sea mayúscula, no funciona en todo minúscula.

#13, #15 y #17, al igual que en LaunchRequest generamos el audio utilizando Polly la diferencia en este caso es que verificamos primero el resultado de gender_detector para, si es chica, utilizar la voz de Lucía o, si es chico, la de Enrique. Seguido, obtenemos la URL del archivo de audio mezclado y finalmente enviamos la respuesta al usuario.

Notas finales

  • Es importante controlar el volumen de cada audio. Audacity es una excelente herramienta para esto. Te sugiero dar un vistazo a este artículo.
  • Puedes agregar efectos fade in y fade out para mejorar la experiencia auditiva y disminuir la fricción de audios cortados abruptamente.
  • He notado que los graves pueden llegar a tapar las voces de Polly. Quizá sea un asunto de volumen (ganancia?).
  • Puedes probar la respuesta al usuario en la pestaña Voice & Tone del simulador en el portal de desarrolladores de Alexa.
  • Sugiero agregar una verificación del código que devuelve la ejecución del comando sox para confirmar que todo ha ido bien. En caso de que no sea exitosa la ejecución puedes imprimir la salida simplemente eliminando .returncode.

En este artículo

  • He descrito de forma breve el servicio de AWS Polly.
  • He descrito, también de forma breve, SSML y la etiqueta audio, algunas herramientas que se pueden utilizar manipular los archivos de audio.
  • Describo la Skill de ejemplo. Prestar atención especial a LaunchRequest, HelloWorldIntent y sobre todo la función generatePollyMix.

Referencias

Muchas gracias por leer.

Escribió para Diseñando para la Voz, Francisco.

¿Conoces una mejor forma de mezclar las voces de Polly y una pista de audio?, ¿Has probado ya mezclar alguna voz de Polly con audio de fondo?, cuéntanos tu experiencia.

--

--

Francisco Rivas
Diseñando para la Voz

Alexa Developer | Coffee Enthusiast | Percussionist | Curious (life, tech) | Keen on learning