Proyecto de Transcripción de Voz en Tiempo Real

 

 Proyecto de Transcripción de Voz en Tiempo Real

Te explico este sistema de transcripción de voz que convierte audio en texto en tiempo real utilizando reconocimiento de voz offline.

📋 ¿Qué es este proyecto?

Es una aplicación web que permite:

  • Transcribir voz a texto en tiempo real usando el micrófono

  • Formatear el texto con diferentes alineaciones

  • Guardar automáticamente el contenido transcrito

  • Funcionar completamente offline después de la instalación

🛠️ Tecnologías utilizadas

  • Vosk: Motor de reconocimiento de voz offline (modelo en español)

  • Python/Flask: Backend del servidor

  • Socket.IO: Comunicación en tiempo real entre cliente y servidor

  • SoundDevice: Captura de audio desde el micrófono

  • HTML/CSS/JS: Interfaz web del usuario

🗂️ Estructura del proyecto

text
proyecto/
├── app.py                 # Servidor principal Flask
├── transcriptor.py        # Clase para manejar la transcripción
├── models/
│   └── vosk-model-small-es-0.42/  # Modelo de lenguaje en español
├── static/
│   └── script.js          # JavaScript del cliente
└── templates/
    └── index.html         # Interfaz web

🔧 Pasos para ponerlo en marcha

1. Instalar dependencias

bash
# Instalar Python si no lo tienes (3.7 o superior)
# Luego instalar las dependencias:
pip install flask flask-socketio sounddevice vosk

2. Descargar el modelo de lenguaje

bash
# Crear carpeta models si no existe
mkdir models

# Descargar y extraer el modelo de Vosk para español
# Puedes descargarlo desde: https://alphacephei.com/vosk/models
# El modelo recomendado es vosk-model-small-es-0.42

3. Ejecutar la aplicación

bash
python app.py

4. Acceder a la aplicación

Abre tu navegador y ve a: http://localhost:5000

🎯 Cómo usar la aplicación

  1. Conectar micrófono: Asegúrate de tener un micrófono conectado

  2. Iniciar transcripción: Haz clic en el botón "Iniciar"

  3. Hablar claramente: El sistema comenzará a transcribir tu voz

  4. Formatear texto: Usa los botones de alineación para dar formato

  5. Detener: Haz clic en "Detener" para finalizar la transcripción

⚙️ Posibles problemas y soluciones

  1. Error de permisos del micrófono:

    • Asegúrate de permitir el acceso al micrófono en el navegador

  2. No se detecta audio:

    • Verifica la configuración de audio de tu sistema

    • Comprueba que el micrófono esté seleccionado como dispositivo de entrada

  3. Error al cargar el modelo Vosk:

    • Verifica que la ruta del modelo sea correcta

    • Asegúrate de que el modelo se haya descargado completamente

  4. Problemas de dependencias:

    • Si hay errores con SoundDevice en Windows, instala: pip install portaudio

📝 Personalizaciones posibles

  • Cambiar el modelo de lenguaje: Descarga otros modelos de Vosk y actualiza la ruta

  • Modificar la interfaz: Edita el archivo templates/index.html

  • Añadir más funcionalidades: Implementa nuevos botones de formato en script.js

¿Necesitas ayuda con algún paso en particular o tienes algún error al ejecutarlo?


Proyecto de Transcripción de Voz en Tiempo Real

Aquí está el código completo del proyecto de transcripción de voz:

app.py (Servidor Principal)

python
from flask import Flask, render_template
from flask_socketio import SocketIO
import os
from transcriptor import Transcriptor

app = Flask(__name__)
app.config['SECRET_KEY'] = 'secret_key'
socketio = SocketIO(app)

transcriptor = None

@app.route('/')
def index():
    return render_template('index.html')

@socketio.on('iniciar')
def iniciar_transcripcion():
    global transcriptor
    if transcriptor is None:
        transcriptor = Transcriptor(socketio)
    transcriptor.iniciar()
    print("Transcripción iniciada")

@socketio.on('detener')
def detener_transcripcion():
    global transcriptor
    if transcriptor:
        transcriptor.detener()
        print("Transcripción detenida")

@socketio.on('guardar_texto')
def guardar_texto(texto):
    # Aquí puedes implementar el guardado en archivo o base de datos
    print("Texto recibido para guardar:", texto)

if __name__ == '__main__':
    socketio.run(app, debug=True)

transcriptor.py (Clase de Transcripción)

python
import queue
import sounddevice as sd
from vosk import Model, KaldiRecognizer
import json
import threading

class Transcriptor:
    def __init__(self, socketio, model_path="models/vosk-model-small-es-0.42", sample_rate=16000):
        self.sample_rate = sample_rate
        self.q = queue.Queue()
        self.model = Model(model_path)
        self.recognizer = KaldiRecognizer(self.model, self.sample_rate)
        self.thread = None
        self.running = False
        self.transcripcion = ""
        self.socketio = socketio

    def _callback(self, indata, frames, time, status):
        if status:
            print("⚠️", status)
        self.q.put(bytes(indata))

    def _procesar_audio(self):
        with sd.RawInputStream(samplerate=self.sample_rate, blocksize=8000, dtype='int16',
                               channels=1, callback=self._callback):
            print("🎤 Transcripción iniciada...")
            last_partial = ""
            while self.running:
                try:
                    data = self.q.get(timeout=0.5)
                    if self.recognizer.AcceptWaveform(data):
                        result = json.loads(self.recognizer.Result())
                        texto = result.get("text", "")
                        if texto:
                            self.transcripcion += texto + " "
                            self.socketio.emit("texto_final", texto)
                    else:
                        parcial = json.loads(self.recognizer.PartialResult()).get("partial", "")
                        if parcial and parcial != last_partial and len(parcial) > 6:
                            self.socketio.emit("texto_parcial", parcial)
                            last_partial = parcial
                except queue.Empty:
                    continue

    def iniciar(self):
        if self.running:
            return
        self.running = True
        self.transcripcion = ""
        self.thread = threading.Thread(target=self._procesar_audio, daemon=True)
        self.thread.start()

    def detener(self):
        self.running = False
        if self.thread:
            self.thread.join()

    def obtener_transcripcion(self):
        return self.transcripcion.strip()

templates/index.html (Interfaz Web)

html
<!DOCTYPE html>
<html lang="es">
<head>
  <meta charset="UTF-8">
  <title>Dictado de Voz Enriquecido</title>
  <style>
    body { 
      font-family: Arial, sans-serif; 
      padding: 2rem; 
      max-width: 1000px;
      margin: 0 auto;
      background-color: #f5f5f5;
    }
    h1 {
      color: #2c3e50;
      text-align: center;
    }
    .controls {
      background: white;
      padding: 1rem;
      border-radius: 8px;
      box-shadow: 0 2px 10px rgba(0,0,0,0.1);
      margin-bottom: 1rem;
    }
    button { 
      margin: 0.5rem; 
      padding: 0.7rem 1.2rem; 
      font-size: 1rem; 
      border: none;
      border-radius: 4px;
      cursor: pointer;
      transition: all 0.3s;
    }
    #btnIniciar {
      background: #2ecc71;
      color: white;
    }
    #btnDetener {
      background: #e74c3c;
      color: white;
    }
    .align-btn { 
      background: #f0f0f0; 
      color: #333;
    }
    .align-btn.active { 
      background: #3498db; 
      color: white; 
    }
    #editor {
      width: 100%; 
      min-height: 300px; 
      margin-top: 1rem;
      border: 2px solid #ddd; 
      padding: 1rem; 
      font-size: 1.1rem;
      background: #fff; 
      border-radius: 6px;
      box-shadow: 0 2px 10px rgba(0,0,0,0.1);
    }
    #editor:focus { 
      outline: none; 
      border-color: #3498db; 
    }
    #parcial { 
      font-style: italic; 
      color: #7f8c8d; 
      margin-top: 0.5rem;
      padding: 0.5rem;
      background: #f9f9f9;
      border-radius: 4px;
    }
    #estado {
      margin: 0.5rem 0;
      font-weight: bold;
      color: #34495e;
    }
    .status-inactive {
      color: #e74c3c;
    }
    .status-active {
      color: #2ecc71;
    }
  </style>
</head>
<body>
  <h1>🎙️ Dictado de Voz con Vosk</h1>
  
  <div class="controls">
    <button id="btnIniciar" onclick="iniciar()">Iniciar Transcripción</button>
    <button id="btnDetener" onclick="detener()">Detener Transcripción</button>
    
    <div>
      <button class="align-btn" onclick="setAlign('left')">↖️ Izquierda</button>
      <button class="align-btn" onclick="setAlign('center')">↕️ Centrado</button>
      <button class="align-btn" onclick="setAlign('right')">↗️ Derecha</button>
      <button class="align-btn" onclick="setAlign('justify')">↔️ Justificado</button>
    </div>
    
    <div id="estado" class="status-inactive">Estado: Inactivo</div>
  </div>
  
  <div id="editor" contenteditable="true" spellcheck="true"></div>
  <div id="parcial">⏳ Esperando para transcribir...</div>
 
  <script src="https://cdn.socket.io/4.5.4/socket.io.min.js"></script>
  <script src="/static/script.js"></script>
</body>
</html>

static/script.js (Cliente JavaScript)

javascript
const socket = io();
const editor = document.getElementById("editor");
const parcial = document.getElementById("parcial");
const estado = document.getElementById("estado");
const alignBtns = document.querySelectorAll('.align-btn');
const btnIniciar = document.getElementById("btnIniciar");
const btnDetener = document.getElementById("btnDetener");

let currentAlign = 'left';

// Inicializar botones de alineación
alignBtns[0].classList.add('active');

function iniciar() {
  socket.emit("iniciar");
  estado.textContent = "Estado: Escuchando...";
  estado.className = "status-active";
  parcial.textContent = "⏳ Escuchando... Habla ahora";
  btnIniciar.disabled = true;
  btnDetener.disabled = false;
}

function detener() {
  socket.emit("detener");
  estado.textContent = "Estado: Inactivo";
  estado.className = "status-inactive";
  parcial.textContent = "Transcripción detenida";
  btnIniciar.disabled = false;
  btnDetener.disabled = true;
  
  // Envía el contenido con formato al servidor
  socket.emit("guardar_texto", editor.innerHTML);
}

function setAlign(align) {
  // Actualiza el botón activo
  alignBtns.forEach(btn => btn.classList.remove('active'));
  event.target.classList.add('active');
 
  // Aplica la alineación al texto seleccionado o al editor completo
  document.execCommand('justifyLeft', false, null);
  document.execCommand('justifyCenter', false, null);
  document.execCommand('justifyRight', false, null);
  document.execCommand('justifyFull', false, null);
 
  // Ejecuta el comando específico
  switch(align) {
    case 'left':
      document.execCommand('justifyLeft', false, null);
      break;
    case 'center':
      document.execCommand('justifyCenter', false, null);
      break;
    case 'right':
      document.execCommand('justifyRight', false, null);
      break;
    case 'justify':
      document.execCommand('justifyFull', false, null);
      break;
  }
 
  currentAlign = align;
  editor.focus();
}

// Eventos de Socket.io
socket.on("texto_parcial", data => {
  parcial.textContent = "⏳ Reconociendo: " + data;
});

socket.on("texto_final", data => {
  const span = document.createElement("span");
  span.textContent = data + " ";
  editor.appendChild(span);
  parcial.textContent = "✓ Texto añadido. Continúa hablando...";
  
  // Hacer scroll al final del editor
  editor.scrollTop = editor.scrollHeight;
});

// Inicializar estado de botones
btnDetener.disabled = true;

📋 Explicación del Código

1. app.py (Servidor Flask)

  • Crea un servidor web con Flask y Socket.IO

  • Maneja las conexiones en tiempo real con los clientes

  • Inicia y detiene la transcripción cuando recibe los eventos correspondientes

  • Sirve la página web principal y maneja la comunicación bidireccional

2. transcriptor.py (Motor de Transcripción)

  • Transcriptor: Clase principal que gestiona todo el proceso de transcripción

  • init: Inicializa el modelo de Vosk y configura la captura de audio

  • _callback: Función que se ejecuta cuando se captura audio del micrófono

  • _procesar_audio: Procesa el audio en un hilo separado y extrae el texto

  • iniciar/detener: Controlan el proceso de transcripción

3. index.html (Interfaz de Usuario)

  • Proporciona una interfaz limpia y fácil de usar

  • Botones para iniciar/detener la transcripción y formatear el texto

  • Editor de texto donde aparece el texto transcrito

  • Área para mostrar el texto parcial mientras se reconoce

4. script.js (Lógica del Cliente)

  • Gestiona la comunicación con el servidor mediante Socket.IO

  • Actualiza la interfaz en tiempo real con el texto transcrito

  • Controla los botones y el formateo del texto

  • Proporciona feedback visual al usuario

🚀 Características Principales

  1. Transcripción en tiempo real: El texto aparece mientras hablas

  2. Interfaz intuitiva: Diseño limpio y fácil de usar

  3. Formatos de texto: Permite alinear el texto a izquierda, centro, derecha o justificado

  4. Comunicación bidireccional: Usa WebSockets para actualizaciones instantáneas

  5. Reconocimiento offline: No requiere conexión a internet después de instalar el modelo

🔧 Instalación y Uso

  1. Instala las dependencias: pip install flask flask-socketio sounddevice vosk

  2. Descarga el modelo de español de Vosk y colócalo en la carpeta models

  3. Ejecuta la aplicación: python app.py

  4. Abre tu navegador en http://localhost:5000

  5. Conecta un micrófono y haz clic en "Iniciar Transcripción"

Este sistema es ideal para tomar notas mediante dictado, crear transcripciones de audio, o para cualquier aplicación que requiera convertir voz en texto de manera eficiente y offline

Comentarios

Entradas más populares de este blog

index

Vosk en una Página Web

2transcriptor.py-4000 # 4000 frames = 0.25s