← Volver a proyectos

Proyecto 02 / Caso de estudio

IA aplicada · escritorio · cien por cien local

Escucho japonés y lo traduzco sin que el audio salga del equipo.

Una ventana flotante que captura lo que suena en el PC o lo que dice el micrófono, lo reconoce y lo traduce, y pinta el resultado encima de todo. Dos modelos neuronales trabajando en una CPU de sobremesa. Ni una llamada a la red, ni una cuenta, ni una cuota: después de instalar, funciona sin conexión.

0peticiones a la nube
INT8en CPU, sin CUDA
4 spor fragmento de audio
510líneas de Python
RolIdea, modelo, interfaz y empaquetado
PlataformaWindows 10 y 11, sin GPU dedicada
TecnologíasPython 3.10, faster-whisper, CTranslate2, PySide6
01

El reto

Ver algo en japonés con subtítulos en español depende de que alguien los haya hecho antes. En directo no los hay, y las soluciones que sí existen se llevan tu audio a un servidor de terceros. Para una película da igual; para una reunión privada o para lo que está sonando en tu propio portátil, no. Quería el resultado sin entregar el audio a nadie y sin depender de cobertura.

02

La solución

Whisper Small cuantizado a enteros de ocho bits sobre CPU, con detección de voz y sin memoria del fragmento anterior. El japonés se convierte a inglés; si se pide español, un segundo modelo neuronal hace el salto. Un hilo captura, otro traduce y una cola de dos posiciones decide qué se muestra cuando el modelo no llega al ritmo del audio.

03

El aprendizaje

Que el trabajo no era escribir las quinientas diez líneas sino decidir cuatro cosas: el tamaño del fragmento, forzar la CPU en lugar de detectar el hardware, separar la traducción en dos saltos y nunca dejar que el modelo toque el hilo de la interfaz. Aquí el valor está en las decisiones, no en el volumen.

Arquitectura

Dos hilos, una cola de dos y una ventana que no estorba.

Dos ficheros de Python y seis dependencias con rango fijado. La captura no conoce el modelo y el modelo no conoce la interfaz: se hablan por una cola acotada y por señales de Qt.

La fuente se elige en pantalla: el micrófono o el bucle de captura del sistema, es decir, lo que el PC está reproduciendo. El audio entra a dieciséis mil hercios, se lee en cuadros de doscientos milisegundos, se mezcla a mono y se acumula hasta el tamaño del fragmento. Cuatro segundos es un compromiso medido, no un número redondo: con dos segundos hay menos retraso pero el modelo pierde el contexto y traduce peor la frase larga; con seis u ocho gana contexto a costa de latencia. Cuatro es el punto donde se puede seguir una conversación.

La cola tiene capacidad máxima de dos fragmentos. Cuando se llena, se descarta el más viejo y entra el nuevo. Es la decisión que más me gusta del proyecto: si el modelo va lento, el subtítulo no se acumula ni se retrasa un minuto entero, simplemente muestra el presente. Un subtítulo de hace cuarenta segundos no sirve para nada.

El reconocimiento corre con faster-whisper sobre CTranslate2 y tres parámetros que valen más que el código entero: detección de voz activa con trescientos milisegundos de silencio mínimo, haz de cinco, y memoria del texto anterior desactivada. Esa última es la que evita el fallo clásico de Whisper, que se pone a repetir y a inventar agarrándose a lo que dijo en el corte previo. El modelo se carga en el hilo de trabajo, nunca en el de interfaz, así que la ventana responde mientras el modelo despierta.

Encima de la ventana: rectángulo redondeado dibujado a mano con QPainter, arrastre para colocarla donde no tape, y modo «ignorar clics» que deja pasar el ratón a lo que hay debajo. Sin eso, una app así estorba en dos minutos.

micrófono · bucle de audio del sistema → lectura 200 ms @ 16 kHz · mezcla a mono → fragmento de 4 s → cola (máx. 2, se descarta el viejo) → Whisper Small · INT8 · CPU · ja → en · haz 5 · VAD 300 ms → [si se pide español] traductor neuronal en → es · haz 4 → ventana flotante sin borde, siempre encima, clics atravesables
  • Reconocimiento

      faster-whisperWhisper SmallCTranslate2INT8 en CPUDetección de voz
  • Traducción

      Paquetes Argosinglés → españolSentencePieceHaz de búsqueda 4
  • Interfaz

      PySide6QPainterSin bordeSiempre encimaClics atravesables
  • Concurrencia

      2 hilos propiosCola acotadaSeñales de QtParada con expiración
  • Dependencias

      Seis, con rango fijadoSin servicios externosinstall.bat y run.bat

Decisiones

Cuatro decisiones, cada una con su factura.

Ninguna se tomó por gusto. Todas tienen una alternativa descartada y un coste que pagué.

01

Forzar CPU en lugar de detectar el hardware

CTranslate2 detecta una GPU NVIDIA en muchos equipos, intenta usarla y revienta porque no están instaladas las bibliotecas de cálculo. El comentario en el código lo dice con las siglas puestas: cublas64_12.dll. Fijar device="cpu" y compute_type="int8" renuncia a la velocidad de una tarjeta que igual no está, a cambio de que arranque en cualquier portátil. Detectar el hardware y no saber qué hacer con él es peor que no detectarlo.

02

Dos saltos en vez de uno: japonés → inglés → español

Whisper traduce bien al inglés; su salida directa al español es más irregular. Así que el audio siempre va a inglés y un segundo modelo neuronal hace el salto final solo si se pide español. El coste es un salto más y algo de latencia. El beneficio es concreto: el inglés conserva mejor nombres propios y matices porque el modelo no tiene que hacer las dos cosas a la vez, y en la ruta corta queda el original reconocido, que es revisable.

03

Whisper Small antes que un modelo diminuto

Existen modelos de habla mucho más ligeros que caben enteros en una CPU modesta. Descarté la variante mínima tras compararla: la diferencia de calidad era demasiado grande para tragársela en una película con diálogo cruzado. Elegí el modelo pesado y lo cuantizado a ocho bits, que es la forma de pagar la calidad sin comprar una tarjeta gráfica. La calidad baja; el requisito de hardware baja mucho más.

04

Reconocer sin filtrar, y decirlo

La aplicación no aplica moderación ni sustituye insultos, y está escrito en su documentación. Un subtítulo que cambia lo que se dijo deja de ser un subtítulo y pasa a ser otra cosa. Es una decisión de producto, no un descuido: el objetivo era reproducir contenido, no higienizarlo. Preferí dejarlo por escrito a que lo descubra quien lo use.

Detalles que se ven

Lo que solo se hace así después de romperlo una vez.

Tres cosas pequeñas que son las que delatan si alguien ha instalado su propio programa en un equipo ajeno.

Validar las rutas del archivo de modelos antes de desempaquetar. Un paquete de traducción es un ZIP que viene de internet con permiso para escribir en tu disco. El instalador recorre cada entrada, la resuelve y comprueba que cae dentro del destino; si alguna apunta fuera, aborta con un mensaje claro. Tratar un archivo descargado como de confianza es un error de seguridad con nombre propio y no hace falta descubrirlo uno.

No mostrar dos veces la misma línea. El reconocimiento devuelve a veces el mismo texto en fragmentos consecutivos. Si se pinta tal cual, la ventana parpadea. Se guarda la línea anterior y se descarta la repetición: un detalle invisible cuando funciona y muy visible cuando falta.

Decir qué está pasando en cada momento. La barra de estado indica qué modelo está cargado y qué ruta se está usando. Cuando un resultado sale raro, se puede achacar a una causa concreta en lugar de a un misterio. Y Windows pide permiso de micrófono incluso para capturar el audio del sistema: eso también está documentado, porque si no lo sabe el usuario, el primer arranque parece estar roto.

  • 510líneas de Python en dos ficheros
  • 6dependencias, todas con rango fijado
  • 2modelos neuronales cargados en local
  • 16.000hercios de entrada, lectura en cuadros de 200 ms
  • 2fragmentos en cola antes de descartar el más viejo
  • 300milisegundos de silencio para cortar un enunciado

Lo que haría distinto

Lo primero, pruebas. Ahora mismo no tiene ni una, y se pueden escribir: un fragmento de audio conocido con su transcripción esperada, y una prueba que verifique la cadena de cola e interfaz con el modelo sustituido por un doble. Es exactamente el patrón que sí apliqué en Storm the Castle para probar lógica que depende de un modelo que no siempre responde. Lo segundo, cortar por frase en vez de por tiempo: los cuatro segundos parten enunciados por la mitad y el traductor pierde contexto justo donde más lo necesita. Lo correcto es segmentar por detección de silencio o fin de enunciado, manteniendo el tiempo máximo como techo. Es la mejora que más ganaría la calidad final, y la tengo apuntada desde el primer día que lo usé con una película.