Módulo 1: Introducción a IA Multimodal

3. Modalidad Audio

Descripción

La modalidad audio abarca dos direcciones: convertir audio a texto (transcripción/Speech-to-Text) y convertir texto a audio (síntesis de voz/Text-to-Speech). En esta cápsula aprenderás cómo funcionan ambas direcciones, qué modelos existen, cuáles son sus capacidades y limitaciones, y verás código ejecutable para transcribir audio con Whisper y generar voz con OpenAI TTS.

Por qué importa: Audio es la segunda modalidad más demandada después de visión. Transcribir reuniones, crear asistentes de voz, generar podcasts, y construir pipelines audio → texto → LLM son casos de uso reales en producción. El módulo 5 profundiza en audio; aquí obtienes los fundamentos.

Conexión con el módulo: En la cápsula 02 viste visión (imagen → texto). Aquí ves audio: tanto la dirección de input (audio → texto) como la de output (texto → audio). Juntas, visión y audio son los pilares del ecosistema multimodal que dominarás en esta guía.


Las Dos Direcciones del Audio

Speech-to-Text (STT): Audio → Texto

Recibes un archivo de audio (MP3, WAV, M4A) y obtienes texto transcrito.

Input:  [reunion_30min.mp3]
Output: "Buenos días a todos. Hoy vamos a revisar los resultados del Q4..."

Modelos principales:

  • Whisper (OpenAI): El estándar de la industria. 50+ idiomas. API o modelo local.
  • Google Speech-to-Text: Buena alternativa. 100+ idiomas. Streaming en tiempo real.
  • AssemblyAI: API moderna. Speaker diarization. Detección de sentimiento.

Text-to-Speech (TTS): Texto → Audio

Recibes texto y generas un archivo de audio con voz sintetizada.

Input:  "El resumen del informe trimestral es positivo."
Output: [resumen.mp3]  ← archivo de audio con voz natural

Modelos principales:

  • OpenAI TTS: Voces naturales (alloy, echo, fable, onyx, nova, shimmer). API simple.
  • ElevenLabs: Voces ultra-realistas. Clonación de voz. Más caro pero superior en naturalidad.
  • Google Cloud TTS: Amplio soporte de idiomas. Voces WaveNet.

Transcripción con Whisper (OpenAI)

Ejemplo básico

from openai import OpenAI
from pathlib import Path

client = OpenAI()

def transcribe_audio(audio_path: str) -> str:
    """Transcribe un archivo de audio usando Whisper."""
    with open(audio_path, "rb") as audio_file:
        transcription = client.audio.transcriptions.create(
            model="whisper-1",
            file=audio_file
        )
    return transcription.text

# Uso
text = transcribe_audio("reunion.mp3")
print(text)
# Output esperado:
# "Buenos días a todos. Hoy vamos a revisar los resultados del cuarto
#  trimestre. Las ventas aumentaron un 15% respecto al trimestre anterior..."

Con opciones avanzadas

def transcribe_with_options(
    audio_path: str,
    language: str = None,
    prompt: str = None,
    response_format: str = "text",
    temperature: float = 0
) -> str:
    """Transcribe audio con opciones avanzadas.

    Args:
        audio_path: Ruta al archivo de audio
        language: Código ISO 639-1 (ej: "es", "en", "fr")
        prompt: Contexto para mejorar transcripción (nombres propios, jerga)
        response_format: "text", "json", "srt", "verbose_json", "vtt"
        temperature: 0-1. Mayor = más creativo (menos literal)
    """
    with open(audio_path, "rb") as audio_file:
        kwargs = {
            "model": "whisper-1",
            "file": audio_file,
            "response_format": response_format,
            "temperature": temperature
        }
        if language:
            kwargs["language"] = language
        if prompt:
            kwargs["prompt"] = prompt

        transcription = client.audio.transcriptions.create(**kwargs)

    if response_format == "text":
        return transcription
    return transcription  # JSON, SRT, VTT

# Uso: transcripción en español con contexto
text = transcribe_with_options(
    "reunion_tech.mp3",
    language="es",
    prompt="NIEVA, bootcamp, FastAPI, LangChain, RAG"
)

¿Para qué sirve prompt? Whisper puede confundirse con nombres propios, acrónimos o jerga técnica. Al pasar un prompt con las palabras esperadas, mejora la precisión:

# Sin prompt: "El framework fast a pi permite crear APIs..."
# Con prompt: "El framework FastAPI permite crear APIs..."
prompt = "FastAPI, LangChain, RAG, ChromaDB, Pydantic"

Formatos de audio soportados

FormatoExtensiónTamaño máximoNotas
MP3.mp325 MBEl más común
MP4.mp425 MBAudio de video
MPEG.mpeg25 MBAudio genérico
MPGA.mpga25 MBMPEG Audio
M4A.m4a25 MBApple audio
WAV.wav25 MBSin compresión (archivos grandes)
WebM.webm25 MBAudio web

Límite: 25 MB por archivo. Para archivos más grandes, necesitas dividir en chunks.

Dividir audio largo

from pydub import AudioSegment
import math

def split_audio(
    audio_path: str,
    chunk_duration_ms: int = 10 * 60 * 1000  # 10 minutos
) -> list[str]:
    """Divide audio largo en chunks para Whisper."""
    audio = AudioSegment.from_file(audio_path)
    total_duration = len(audio)
    num_chunks = math.ceil(total_duration / chunk_duration_ms)

    chunk_paths = []
    for i in range(num_chunks):
        start = i * chunk_duration_ms
        end = min((i + 1) * chunk_duration_ms, total_duration)
        chunk = audio[start:end]
        chunk_path = f"chunk_{i:03d}.mp3"
        chunk.export(chunk_path, format="mp3")
        chunk_paths.append(chunk_path)

    return chunk_paths


def transcribe_long_audio(audio_path: str) -> str:
    """Transcribe audio de cualquier duración."""
    import os
    size_mb = os.path.getsize(audio_path) / (1024 * 1024)

    if size_mb <= 24:
        return transcribe_audio(audio_path)

    chunks = split_audio(audio_path)
    transcriptions = []
    for chunk_path in chunks:
        text = transcribe_audio(chunk_path)
        transcriptions.append(text)
        os.remove(chunk_path)  # limpieza

    return " ".join(transcriptions)

Síntesis de Voz con OpenAI TTS

Ejemplo básico

from openai import OpenAI
from pathlib import Path

client = OpenAI()

def text_to_speech(
    text: str,
    output_path: str = "output.mp3",
    voice: str = "alloy",
    model: str = "tts-1"
) -> str:
    """Genera audio desde texto usando OpenAI TTS.

    Voces disponibles: alloy, echo, fable, onyx, nova, shimmer
    Modelos: tts-1 (rápido), tts-1-hd (alta calidad)
    """
    response = client.audio.speech.create(
        model=model,
        voice=voice,
        input=text
    )
    response.stream_to_file(output_path)
    return output_path

# Uso
audio_file = text_to_speech(
    "El análisis del documento muestra tres hallazgos principales.",
    voice="nova"
)
print(f"Audio generado: {audio_file}")
# Output esperado: Audio generado: output.mp3

Voces disponibles

VozCaracterísticasMejor para
alloyNeutral, balanceadaUso general, documentación
echoMás grave, masculinaNarraciones, podcasts
fableCálida, expresivaStorytelling, educación
onyxGrave, seriaPresentaciones formales
novaClara, femeninaAsistentes, tutoriales
shimmerSuave, amigableMeditación, bienestar

TTS de alta calidad

# Modelo estándar: tts-1 (rápido, bueno para streaming)
# Modelo HD: tts-1-hd (más lento, mayor calidad)

audio_standard = text_to_speech("Hola mundo", model="tts-1")
audio_hd = text_to_speech("Hola mundo", model="tts-1-hd")

¿Cuándo usar HD? Para contenido final (podcast, video, presentación). Para prototipos y desarrollo, tts-1 es suficiente y más rápido.

Formatos de output

# Por defecto: MP3
# También soporta: opus, aac, flac

response = client.audio.speech.create(
    model="tts-1",
    voice="alloy",
    input="Texto de ejemplo",
    response_format="opus"  # Mejor para streaming
)
FormatoUso ideal
mp3Descarga, reproducción general
opusStreaming, baja latencia
aacCompatibilidad con iOS/Apple
flacMáxima calidad sin pérdida

Costos de TTS

ModeloCosto
tts-1$15 / 1M caracteres
tts-1-hd$30 / 1M caracteres

Ejemplo: Un texto de 500 palabras (~3,000 caracteres) cuesta ~$0.045 con tts-1.


Comparación: Whisper vs Alternativas

CriterioWhisper (OpenAI)Google STTAssemblyAI
Idiomas50+100+100+
Costo$0.006/min~$0.006-0.024/min~$0.015/min
CalidadExcelenteExcelenteExcelente
Tiempo realNo (solo archivos)Sí (streaming)Sí (streaming)
Speaker IDNo nativo
Modelo localSí (open-source)NoNo
API simplicityMuy simpleComplejaMedia

¿Cuándo usar cada uno?

  • Whisper: API simple, buen precio, calidad excelente, ya usas OpenAI para otras cosas
  • Google STT: Necesitas streaming en tiempo real o muchos idiomas raros
  • AssemblyAI: Necesitas speaker diarization (quién dijo qué) o análisis de sentimiento

Comparación: OpenAI TTS vs ElevenLabs

CriterioOpenAI TTSElevenLabs
NaturalidadAltaMuy alta
Voces6 predefinidas100+ predefinidas + clonación
ClonaciónNoSí (con muestras)
Costo$15/1M charsDesde $5/mo (límite de chars)
IdiomasMultiidioma automáticoMultiidioma
APIMuy simpleSimple
LatenciaBajaMedia

¿Cuándo usar ElevenLabs? Cuando la calidad de voz es crítica (podcast, audiobook, asistente premium) o necesitas clonar una voz específica.


Pipeline: Audio → Texto → LLM

El patrón más potente combina transcripción con procesamiento por LLM:

def audio_to_summary(audio_path: str) -> dict:
    """Pipeline completo: audio → transcripción → resumen."""
    # Paso 1: Transcribir
    transcript = transcribe_audio(audio_path)

    # Paso 2: Resumir con LLM
    response = client.chat.completions.create(
        model="gpt-4o-mini",
        messages=[
            {
                "role": "system",
                "content": "Eres un asistente que resume reuniones de forma concisa."
            },
            {
                "role": "user",
                "content": (
                    f"Resume esta transcripción de reunión en:\n"
                    f"1. Resumen ejecutivo (2-3 frases)\n"
                    f"2. Puntos clave (bullets)\n"
                    f"3. Action items (si hay)\n\n"
                    f"Transcripción:\n{transcript}"
                )
            }
        ],
        max_tokens=500
    )
    summary = response.choices[0].message.content

    return {
        "transcript": transcript,
        "summary": summary,
        "audio_file": audio_path
    }

# Uso
result = audio_to_summary("reunion_standup.mp3")
print(result["summary"])

Este pipeline es la base de herramientas como Otter.ai, Fireflies, y Notion AI para reuniones.

Variante: Audio → Texto → LLM → Audio

Puedes cerrar el ciclo: transcribir, procesar, y devolver audio.

Reunión (audio) → Whisper (transcripción) → GPT-4o-mini (resumen)
→ TTS (audio del resumen)

Esto permite crear asistentes que "escuchan" una reunión y "dicen" el resumen. El ejercicio 3 implementa exactamente esto.


Troubleshooting

Problema 1: "Invalid file format"

Causa: Formato de audio no soportado o archivo corrupto.

Solución:

from pydub import AudioSegment

def convert_to_mp3(audio_path: str) -> str:
    """Convierte cualquier formato de audio a MP3."""
    audio = AudioSegment.from_file(audio_path)
    output_path = audio_path.rsplit(".", 1)[0] + ".mp3"
    audio.export(output_path, format="mp3")
    return output_path

Problema 2: Archivo mayor a 25MB

Causa: Grabación larga sin comprimir.

Solución: Usa la función split_audio mostrada arriba, o comprime primero:

def compress_audio(audio_path: str, bitrate: str = "64k") -> str:
    """Comprime audio reduciendo bitrate."""
    audio = AudioSegment.from_file(audio_path)
    output_path = audio_path.rsplit(".", 1)[0] + "_compressed.mp3"
    audio.export(output_path, format="mp3", bitrate=bitrate)
    return output_path

Problema 3: Transcripción imprecisa

Causa: Audio de baja calidad, ruido de fondo, acentos fuertes.

Solución:

  • Especifica language explícitamente (no dejes que Whisper adivine)
  • Usa prompt con vocabulario esperado
  • Para audio muy ruidoso, preprocesa con pydub (normalizar volumen, filtrar ruido)

Problema 4: TTS suena robótico

Causa: Texto sin puntuación o formato que guíe la prosodia.

Solución:

  • Usa puntuación natural: comas, puntos, signos de interrogación
  • Divide textos largos en párrafos
  • Experimenta con diferentes voces (nova y fable tienden a sonar más naturales)

Ejercicios

Ejercicio 1: Transcripción básica (Fácil)

Escribe una función que transcriba un audio en español y devuelva el texto en mayúsculas.

Ver solución
def transcribe_uppercase(audio_path: str) -> str:
    with open(audio_path, "rb") as f:
        transcription = client.audio.transcriptions.create(
            model="whisper-1",
            file=f,
            language="es"
        )
    return transcription.text.upper()

Explicación: Especificar language="es" mejora la precisión para español. .upper() convierte a mayúsculas.

Ejercicio 2: Generar audio con voz personalizada (Fácil)

Crea una función que genere audio de un texto con la voz que el usuario elija, validando que sea una voz válida.

Ver solución
VALID_VOICES = {"alloy", "echo", "fable", "onyx", "nova", "shimmer"}

def generate_speech(text: str, voice: str = "alloy") -> str:
    if voice not in VALID_VOICES:
        raise ValueError(
            f"Voz '{voice}' no válida. Opciones: {VALID_VOICES}"
        )

    output_path = f"speech_{voice}.mp3"
    response = client.audio.speech.create(
        model="tts-1",
        voice=voice,
        input=text
    )
    response.stream_to_file(output_path)
    return output_path

Explicación: La validación antes de llamar a la API evita errores y da mensajes claros al usuario.

Ejercicio 3: Pipeline audio → resumen → audio (Medio)

Crea un pipeline que: (1) transcriba audio, (2) resuma con LLM, (3) genere audio del resumen.

Ver solución
def audio_summary_pipeline(
    audio_path: str,
    summary_voice: str = "nova"
) -> dict:
    # 1. Transcribir
    with open(audio_path, "rb") as f:
        transcript = client.audio.transcriptions.create(
            model="whisper-1",
            file=f,
            language="es"
        ).text

    # 2. Resumir
    response = client.chat.completions.create(
        model="gpt-4o-mini",
        messages=[{
            "role": "user",
            "content": (
                f"Resume en 3 frases cortas:\n\n{transcript}"
            )
        }],
        max_tokens=200
    )
    summary = response.choices[0].message.content

    # 3. Generar audio del resumen
    speech = client.audio.speech.create(
        model="tts-1",
        voice=summary_voice,
        input=summary
    )
    output_path = "resumen_audio.mp3"
    speech.stream_to_file(output_path)

    return {
        "transcript": transcript,
        "summary": summary,
        "audio_summary": output_path
    }

Explicación: Este es el pipeline multimodal completo: audio → texto → LLM → audio. Es la base de muchos productos reales (Otter.ai, NotebookLM).

Ejercicio 4: Transcripción con timestamps (Medio)

Usa response_format="verbose_json" para obtener transcripción con timestamps. Extrae las primeras 5 frases con su tiempo de inicio.

Ver solución
import json

def transcribe_with_timestamps(audio_path: str) -> list[dict]:
    with open(audio_path, "rb") as f:
        transcription = client.audio.transcriptions.create(
            model="whisper-1",
            file=f,
            response_format="verbose_json"
        )

    segments = transcription.segments[:5]
    result = []
    for seg in segments:
        result.append({
            "start": round(seg["start"], 1),
            "end": round(seg["end"], 1),
            "text": seg["text"].strip()
        })
    return result

# Output esperado:
# [{"start": 0.0, "end": 3.5, "text": "Buenos días a todos."},
#  {"start": 3.5, "end": 8.2, "text": "Hoy vamos a revisar los resultados."},
#  ...]

Explicación: verbose_json devuelve segmentos con start, end, text. Útil para subtítulos (SRT/VTT) y para ubicar momentos específicos en grabaciones largas.


Resumen

En esta cápsula aprendiste:

  • Speech-to-Text (STT): Audio → texto. Whisper es el estándar; Google STT y AssemblyAI son alternativas con streaming
  • Text-to-Speech (TTS): Texto → audio. OpenAI TTS tiene 6 voces naturales; ElevenLabs para calidad premium
  • Whisper acepta MP3, WAV, M4A y más. Límite de 25MB. Soporta 50+ idiomas
  • El parámetro prompt en Whisper mejora precisión con vocabulario específico
  • OpenAI TTS tiene dos modelos: tts-1 (rápido) y tts-1-hd (alta calidad)
  • El pipeline audio → texto → LLM es el patrón más potente: transcribir, procesar, actuar
  • Para audio largo, divide en chunks antes de transcribir
  • Costos: Whisper ~$0.006/min. TTS ~$15/1M caracteres

Próxima cápsula: Combinaciones multimodales — cómo conectar visión, audio y texto en pipelines.


Recursos Adicionales

  1. Whisper API Docs — Documentación oficial de Whisper
  2. OpenAI TTS Docs — Documentación oficial de TTS
  3. Whisper GitHub (open-source) — Modelo local de Whisper
  4. ElevenLabs API — TTS premium con clonación de voz
  5. pydub Documentation — Manipulación de audio en Python
  6. AssemblyAI Docs — Alternativa con speaker diarization