13. Función Hablar

 

Tutorial Paso a Paso: Creando un Asistente Virtual con Voz

En este tutorial, vamos a construir un asistente virtual que no solo reconozca lo que dices, sino que también te responda en voz alta. Organizaremos el código en funciones para que sea más fácil de entender y mantener.

engine = pyttx3.init()

engine.say("Hola como estas?")
engine.runAndWait()

Para que el código funcione correctamente, necesitas importar la librería pyttsx3 y crear el motor de texto a voz. Aquí tienes la versión completa:

python
import pyttsx3

# Inicializar el motor de texto a voz
engine = pyttsx3.init()

def hablar(por_hablar):
    """
    Función que recibe un texto y lo reproduce en voz alta.
    """
    engine.say("Bienvenido de vuelta.")
    engine.runAndWait()

# Llamar a la función
hablar('hola')

Explicación paso a paso

1. Importar la librería pyttsx3

python
import pyttsx3
  • Esta línea importa la librería pyttsx3, que se utiliza para la conversión de texto a voz (TTS, por sus siglas en inglés). Es una herramienta que funciona sin conexión a internet.

2. Inicializar el motor

python
engine = pyttx3.init()
  • Aquí se crea el objeto engine llamando al método init(). Este objeto es el encargado de manejar todo lo relacionado con el habla, como la velocidad, el volumen, la voz, etc.

3. Definir la función hablar

python
def hablar(por_hablar):
  • Se define una función llamada hablar que recibe un parámetro llamado por_hablar. Aunque el parámetro está definido, dentro de la función no se está utilizando (esto es importante, lo veremos más adelante).

4. Contenido de la función

python
    engine.say("Bienvenido de vuelta.")
    engine.runAndWait()
  • engine.say(): encola el mensaje de texto que se va a decir.

  • engine.runAndWait(): procesa la cola de mensajes y reproduce el texto en voz alta. El programa espera a que termine de hablar antes de continuar.

5. Llamar a la función

python
hablar('hola')
  • Aquí se ejecuta la función hablar pasándole el argumento 'hola'.


⚠️ Observación importante sobre el código

Aunque la función hablar recibe el argumento por_hablaren realidad no lo usa. Siemre dice:

"Bienvenido de vuelta."

Sin importar si llamas a la función con hablar('hola')hablar('adiós') o cualquier otro valor.

✅ Posible mejora

Si quisieras que la función diga exactamente lo que se le pasa como argumento, deberías cambiar el contenido del engine.say():

python
def hablar(por_hablar):
    engine.say(por_hablar)  # Usa el argumento recibido
    engine.runAndWait()

Salida esperada (auditiva)

Si ejecutas el código original (con la frase fija), escucharás:

"Bienvenido de vuelta."

Si corriges la función como te sugerí, escucharás:

"hola"


Paso 1: Configuración Inicial y Función de Bienvenida

Primero, importamos las librerías necesarias y configuramos el motor de texto a voz. También creamos una función simple para dar la bienvenida.

python
import speech_recognition as sr
import pyttsx3

listener = sr.Recognizer()
engine = pyttsx3.init()
voces = engine.getProperty('voices')
engine.setProperty('voice', voces[2].id)  # Seleccionamos una voz en español

def bienvenida():
    engine.say("Bienvenido de vuelta.")
    engine.runAndWait()

bienvenida()

Explicación:

  • sr.Recognizer(): Objeto para reconocer voz.

  • pyttsx3.init(): Inicia el motor de texto a voz.

  • bienvenida(): Función que dice "Bienvenido de vuelta" cuando se llama.


Paso 2: Crear una Función para Hablar

Muy bien, ahora vamos a hacer una función de hablar. Incorporarla en nuestro código para que, en vez de tener que estar viendo lo que nuestro asistente virtual responde, mejor vamos a escucharlo.

Vamos a poner un parámetro que va a ser por_hablar con la idea de que nosotros utilicemos esta función más adelante.

python
def hablar(por_hablar):
    engine.say(por_hablar)
    engine.runAndWait()

hablar('hola')

Explicación:

  • La función hablar() toma un texto (por_hablar) y lo convierte en voz.

  • Al final, probamos la función con hablar('hola').


Paso 3: Mejorando la Función de Hablar

Aquí corregimos la definición de la función. Nota que en el intento anterior había un error: no se debe poner el valor directamente en el parámetro.

python
def hablar(por_hablar):
    engine.say(por_hablar)
    engine.runAndWait()

hablar('hola')

Explicación:

  • La función ahora está bien definida: hablar(por_hablar) recibe un argumento y lo usa dentro de engine.say().


Paso 4: Escuchando y Reconociendo Voz

Ahora vamos a integrar la escucha activa con el micrófono. Usaremos un bucle infinito para estar siempre escuchando.

python
bienvenida()

while True:
    with sr.Microphone() as source:
        print('Escuchando...')
        audio = listener.listen(source, phrase_time_limit=5)

    try:
        print('Reconociendo...')
        text = listener.recognize_google(audio, language='es-US')
        print(text)
    except Exception as e:
        print('No pude entenderlo bien, repítelo por favor')
        print(e)

Explicación:

  • Usamos sr.Microphone() como fuente de audio.

  • listener.listen() captura el audio con un límite de 5 segundos por frase.

  • recognize_google() convierte el audio a texto usando el API de Google (español de EE.UU.).

  • Si hay error, mostramos un mensaje en consola.


Paso 5: Responder en Voz con la Función Hablar

En vez de solo imprimir, ahora haremos que el asistente repita lo que escuchó, usando nuestra función hablar().

python
while True:
    with sr.Microphone() as source:
        print('Escuchando...')
        audio = listener.listen(source, phrase_time_limit=5)

    try:
        print('Reconociendo...')
        text = listener.recognize_google(audio, language='es-US')
        hablar(text)
    except Exception as e:
        print('No pude entenderlo bien, repítelo por favor')
        print(e)

Explicación:

  • Reemplazamos print(text) por hablar(text) para que el asistente hable lo que entendió.


Paso 6: Hablar el Mensaje de Error

Ahora haremos que también hable cuando no entienda algo, en lugar de solo imprimirlo.

python
while True:
    with sr.Microphone() as source:
        print('Escuchando...')
        audio = listener.listen(source, phrase_time_limit=5)

    try:
        print('Reconociendo...')
        text = listener.recognize_google(audio, language='es-US')
        hablar(text)
    except Exception as e:
        hablar('No pude entenderlo bien, repítelo por favor')
        print(e)

Explicación:

  • En el except, ahora usamos hablar() para que el error también sea audible.


Paso 7: Imprimir y Hablar el Texto Reconocido

Para mayor comodidad, haremos que el asistente hable y también imprima el texto en la terminal.

python
while True:
    with sr.Microphone() as source:
        print('Escuchando...')
        audio = listener.listen(source, phrase_time_limit=5)

    try:
        print('Reconociendo...')
        text = listener.recognize_google(audio, language='es-US')
        hablar(text)
        print(text)
    except Exception as e:
        hablar('No pude entenderlo bien, repítelo por favor')
        print(e)

Explicación:

  • Ahora tenemos tanto salida de voz como visual.


Paso 8: Organizar en una Función de Escuchar

Vamos a poner esto un poco más ordenado. Todo esto que nos escucha y reconoce básicamente, y vamos a poner todo esto en una función que sea escuchar().

python
def escuchar():
    while True:
        with sr.Microphone() as source:
            print('Escuchando...')
            audio = listener.listen(source, phrase_time_limit=5)

        try:
            print('Reconociendo...')
            text = listener.recognize_google(audio, language='es-US')
            hablar(text)
            print(text)
        except Exception as e:
            hablar("No pude entenderlo bien, repítelo por favor")
            print(e)

Explicación:

  • Movemos todo el bucle dentro de una función escuchar() para encapsular la lógica.


Paso 9: Integrar Todo con Bienvenida y Escucha Continua

Finalmente, llamamos a la función de bienvenida y luego a la función de escuchar para que el asistente comience a funcionar.

python
bienvenida()
escuchar()

Explicación:

  • Primero damos la bienvenida.

  • Luego entramos en el bucle infinito de escucha y respuesta.


Resultado Final

Cuando ejecutes el código completo, escucharás "Bienvenido de vuelta" y luego el asistente quedará a la espera de lo que digas. Repetirá en voz alta lo que entendió y también lo mostrará en la terminal. Si no entiende algo, te pedirá que lo repitas.



La Solución: Inicializar el Motor Una Sola Vez

En tu código original (o en el que te mostré en el tutorial), lo más probable es que estuvieras creando un nuevo motor (pyttsx3.init()) cada vez que querías hablar. Para solucionar el bloqueo, debemos asegurarnos de que el motor se inicie una única vez y luego se reutilice.

Te propongo dos formas de implementarlo, manteniendo tu estructura lo más parecida posible.

Opción 1 (Recomendada): Usando una variable global

Esta es la forma más directa y la que menos modifica tu código. Simplemente declaramos el engine como variable global para que se cree una sola vez.

python
import pyttsx3
import speech_recognition as sr

# Inicializar el motor UNA SOLA VEZ a nivel global
engine = pyttsx3.init()

def hablar(texto):
    # Esta función ahora usa el motor global, sin crear uno nuevo
    engine.say(texto)
    engine.runAndWait()

# --- El resto de tu código (reconocimiento, etc.) ---
# Ejemplo de uso:
# hablar("Hola, este mensaje no bloqueará el programa en el segundo intento")

Explicación:
Al mover pyttsx3.init() fuera de cualquier función, el motor se crea una vez cuando el programa arranca. La función hablar() simplemente utiliza ese motor ya existente. Esto evita los conflictos de crear y destruir el motor múltiples veces.

Opción 2: Usando una Clase (Programación Orientada a Objetos)

Si prefieres una estructura más limpia y profesional, especialmente si tu proyecto va a crecer, puedes encapsular el motor en una clase.

python
import pyttsx3
import speech_recognition as sr

class AsistenteVoz:
    def __init__(self):
        # El motor se inicializa cuando creamos el objeto AsistenteVoz
        self.engine = pyttsx3.init()

    def hablar(self, texto):
        # Usamos el motor que guardamos en 'self.engine'
        self.engine.say(texto)
        self.engine.runAndWait()

# --- Crear el asistente y usarlo ---
mi_asistente = AsistenteVoz()

# Ejemplo de uso:
# mi_asistente.hablar("Hola, esto funciona a la primera")
# mi_asistente.hablar("Y también a la segunda")

Código Completo Integrado con Reconocimiento de Voz

Aquí te muestro cómo quedaría tu programa original (el del tutorial) con la Opción 1 aplicada. Verás que la estructura es prácticamente la misma, pero el molesto bloqueo desaparecerá.

python
import speech_recognition as sr
import pyttsx3

# --- SOLUCIÓN: Inicializar el motor UNA SOLA VEZ aquí fuera ---
engine = pyttsx3.init()
# -------------------------------------------------------------

def hablar(texto):
    # Esta función ahora usa el motor global
    engine.say(texto)
    engine.runAndWait()

# Escuchar micrófono
recognizer = sr.Recognizer()
with sr.Microphone() as source:
    print("Escuchando...")
    audio = recognizer.listen(source)

try:
    texto = recognizer.recognize_google(audio, language='es-ES')
    print(f"Has dicho: {texto}")
    hablar(f"Has dicho: {texto}")
except:
    hablar("No entendí lo que dijiste")

# Ahora puedes llamar a 'hablar()' de nuevo sin problemas.
# Por ejemplo, podrías poner todo esto en un bucle while True:
# while True:
#     ... (código para escuchar y hablar) ...

¿Por qué funciona?

  • Inicialización única: Crear el motor una sola vez evita que los recursos del sistema se asignen y liberen constantemente, lo que a menudo es la causa de los bloqueos .

  • Reutilización: El motor de pyttsx3 está diseñado para ser una instancia que dura toda la vida de la aplicación. Al reutilizarlo, respetas su ciclo de vida natural.

Con este pequeño cambio, tu programa debería ser capaz de hablar tantas veces como sea necesario sin bloqueos. ¡Pruébalo y me cuentas! Si tienes alguna otra duda, aquí estoy para ayudarte.

📊 Diagrama de Flujo de la Función Hablar Integrada

text

┌─────────────────────────────────────────────────────────┐

│          ASISTENTE VIRTUAL - FLUJO COMPLETO            │

├─────────────────────────────────────────────────────────┤

│ 1. Inicializar:                                         │

│    • Motor de voz (pyttsx3)                            │

│    • Reconocedor de voz (speech_recognition)           │

│    • Configurar voz (español, velocidad, volumen)      │

├─────────────────────────────────────────────────────────┤

│ 2. Bienvenida automática:                              │

│    • Determinar hora del día → Saludo apropiado        │

│    • Llamar a self.hablar() con mensaje de bienvenida  │

├─────────────────────────────────────────────────────────┤

│                     BUCLE PRINCIPAL                     │

├─────────────────────────────────────────────────────────┤

│ 3. Escuchar comando:                                   │

│    • Usar micrófono                                    │

│    • Convertir audio a texto                           │

│    • Guardar en historial                              │

├─────────────────────────────────────────────────────────┤

│ 4. Procesar comando:                                   │

│    • Identificar tipo de comando                       │

│    • Llamar función específica (decir_hora, etc.)      │

├─────────────────────────────────────────────────────────┤

│ 5. Responder usando hablar():                          │

│    • self.hablar("mensaje", nivel='tipo')              │

│    • Mostrar en consola con emoji                      │

│    • Guardar en historial                              │

│    • Decir por voz                                     │

│    • Aplicar pausa según tipo                          │

├─────────────────────────────────────────────────────────┤

│ 6. Repetir bucle o terminar si es "adiós"              │

└─────────────────────────────────────────────────────────┘


🎮 Ejercicios de Práctica

Ejercicio 1: Añadir Nuevos Comandos

python

# Añade estos comandos a tu asistente:


def decir_clima(self):

    """Dice el clima actual (simulado)"""

    # Tu código aquí

    pass


def decir_edad(self, años):

    """Calcula y dice edad en diferentes unidades"""

    # Tu código aquí

    pass


def decir_poema(self):

    """Dice un poema corto"""

    # Tu código aquí

    pass

Ejercicio 2: Mejorar la Función Hablar

python

# Añade estas características a la función hablar:


def hablar_con_emocion(self, texto, emocion='neutro'):

    """

    Habla con diferentes emociones

    

    emocion puede ser: feliz, triste, enojado, sorprendido, etc.

    Ajusta velocidad, volumen y pitch según la emoción

    """

    # Tu código aquí

    pass

Ejercicio 3: Sistema de Aprendizaje

python

# Crea un sistema que aprenda del usuario:


def aprender_respuesta(self, pregunta, respuesta):

    """Aprende una nueva respuesta para una pregunta"""

    # Tu código aquí

    pass


def recordar_usuario(self, nombre, preferencias):

    """Recuerda información del usuario entre sesiones"""

    # Tu código aquí

    pass


📋 Checklist de la Función Hablar Perfecta

Una función hablar() ideal debería:

  • ✅ Centralizar toda la salida de voz

  • ✅ Ser configurable (velocidad, volumen, voz)

  • ✅ Tener niveles (normal, importante, error, pregunta)

  • ✅ Registrar en historial/log

  • ✅ Mostrar en consola con formato

  • ✅ Manejar pausas automáticamente

  • ✅ Ser reutilizable en todo el programa

  • ✅ Tener manejo de errores robusto


❓ Cuestionario de Repaso

Pregunta 1:

¿Cuál es el principal propósito de crear una función hablar() centralizada?

  • A) Hacer el programa más lento

  • B) Evitar repetir código en todo el programa

  • C) Obligar al usuario a escuchar más

  • D) Aumentar el volumen automáticamente

Respuesta correcta: B
Explicación: Una función centralizada elimina la repetición de engine.say() y engine.runAndWait() en múltiples lugares, haciendo el código más limpio y mantenible.


Pregunta 2:

¿Qué parámetro adicional podemos añadir a hablar() para hacerla más versátil?

  • A) nivel - para diferenciar tipos de mensajes

  • B) color - para cambiar el color en consola

  • C) hora - para decir a qué hora hablar

  • D) repeticiones - para repetir el mensaje

Respuesta correcta: A
Explicación: Un parámetro nivel permite tener diferentes estilos para mensajes normales, importantes, de error, preguntas, etc., haciendo la comunicación más rica.


Pregunta 3:

¿Por qué es útil guardar las conversaciones en un historial?

  • A) Para hacer el programa más pesado

  • B) Para depurar problemas y mejorar el asistente

  • C) Para espiar al usuario

  • D) Para llenar espacio en disco

Respuesta correcta: B
Explicación: Un historial permite ver qué comandos se usan más, qué errores ocurren y cómo mejorar las respuestas del asistente.


Pregunta 4:

En la función hablar() mejorada, ¿qué hace el parámetro pausa?

  • A) Espera antes de empezar a hablar

  • B) Espera después de terminar de hablar

  • C) Cambia la velocidad del habla

  • D) Aumenta el volumen gradualmente

Respuesta correcta: B
Explicación: Una pausa después de hablar permite que el usuario procese la información antes de la siguiente interacción, especialmente útil en listas o información compleja.


Pregunta 5:

¿Qué ventaja tiene usar una clase para el asistente en lugar de solo funciones?

  • A) Permite agrupar datos y comportamientos relacionados

  • B) Hace el código más corto

  • C) Obliga a usar programación orientada a objetos

  • D) Es más fácil para principiantes

Respuesta correcta: A
Explicación: Una clase permite agrupar el estado (nombre, historial, configuración) con los métodos (hablar, escuchar, procesar) en una unidad coherente y reutilizable.


Pregunta 6 (Bonus):

¿Por qué es importante tener respuestas aleatorias para comandos como "gracias"?

  • A) Para hacer el programa impredecible

  • B) Para que el asistente parezca más humano y menos robótico

  • C) Para probar diferentes voces

  • D) Para aumentar la dificultad

Respuesta correcta: B
Explicación: Variaciones en las respuestas hacen que la interacción sea más natural y menos repetitiva, mejorando la experiencia del usuario.


🌟 ¡Felicidades! Has Completado el Tutorial del Asistente Virtual

Tu asistente virtual ahora está COMPLETO y puede:

  1. ✅ Escuchar comandos de voz en español

  2. ✅ Hablar con voz natural y configurable

  3. ✅ Saludar apropiadamente según la hora

  4. ✅ Procesar múltiples comandos útiles

  5. ✅ Mantener historial de conversaciones

  6. ✅ Tener personalidad con respuestas variadas

  7. ✅ Ser extensible para añadir nuevas funciones

Proyectos avanzados sugeridos:

  1. 🌐 Conectar a internet para clima, noticias, etc.

  2. 🤖 Añadir IA con ChatGPT o similar

  3. 📱 Crear interfaz gráfica con tkinter o PyQt

  4. 🔌 Conectar a APIs de Google, Spotify, etc.

  5. 📁 Guardar configuraciones entre sesiones

📌 Tu misión final:

  1. Ejecuta el asistente completo

  2. Prueba todos los comandos

  3. Añade AL MENOS una función nueva

  4. Comparte tu creación y experiencia

🎉 ¡Eres oficialmente un creador de asistentes virtuales!
Recuerda: cada gran proyecto comienza con un "Hola mundo" y crece con práctica y curiosidad. ¡Sigue programando!



https://www.youtube.com/watch?v=Pj9KbAKpQyk

https://www.youtube.com/watch?v=-0tIy8wWtzE

https://www.youtube.com/watch?v=YqSSId7xfwU

https://www.youtube.com/watch?v=MjK-j7YJ5YI


https://www.youtube.com/watch?v=l2G8-iQYfoA



https://www.youtube.com/watch?v=t-YO0XeHczU


Comentarios

Entradas más populares de este blog

1-7. Transforma tu audio a texto

10. Haz que tu asistente hable

8. NUEVO - Solución si tu micrófono no está captando tu audio