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
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
# Instalar Python si no lo tienes (3.7 o superior)
# Luego instalar las dependencias:
pip install flask flask-socketio sounddevice vosk2. Descargar el modelo de lenguaje
# 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.423. Ejecutar la aplicación
python app.py4. Acceder a la aplicación
Abre tu navegador y ve a: http://localhost:5000
🎯 Cómo usar la aplicación
Conectar micrófono: Asegúrate de tener un micrófono conectado
Iniciar transcripción: Haz clic en el botón "Iniciar"
Hablar claramente: El sistema comenzará a transcribir tu voz
Formatear texto: Usa los botones de alineación para dar formato
Detener: Haz clic en "Detener" para finalizar la transcripción
⚙️ Posibles problemas y soluciones
Error de permisos del micrófono:
Asegúrate de permitir el acceso al micrófono en el navegador
No se detecta audio:
Verifica la configuración de audio de tu sistema
Comprueba que el micrófono esté seleccionado como dispositivo de entrada
Error al cargar el modelo Vosk:
Verifica que la ruta del modelo sea correcta
Asegúrate de que el modelo se haya descargado completamente
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)
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)
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)
<!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)
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
Transcripción en tiempo real: El texto aparece mientras hablas
Interfaz intuitiva: Diseño limpio y fácil de usar
Formatos de texto: Permite alinear el texto a izquierda, centro, derecha o justificado
Comunicación bidireccional: Usa WebSockets para actualizaciones instantáneas
Reconocimiento offline: No requiere conexión a internet después de instalar el modelo
🔧 Instalación y Uso
Instala las dependencias:
pip install flask flask-socketio sounddevice voskDescarga el modelo de español de Vosk y colócalo en la carpeta
modelsEjecuta la aplicación:
python app.pyAbre tu navegador en
http://localhost:5000Conecta 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
Publicar un comentario