188 lines
8.8 KiB
Python
188 lines
8.8 KiB
Python
"""
|
|
===========================================================
|
|
INSTALACIÓN DE PYTHON Y DEPENDENCIAS (SISTEMA WINDOWS)
|
|
===========================================================
|
|
|
|
1. INSTALACIÓN DE PYTHON:
|
|
- Descarga Python para Windows desde: https://www.python.org/downloads/windows/
|
|
- Se recomienda instalar Python 3.12 o superior.
|
|
- Durante la instalación, asegúrate de marcar la opción "Add Python to PATH" para que puedas ejecutar Python y pip desde la línea de comandos.
|
|
|
|
2. CONFIGURACIÓN DEL ENTORNO:
|
|
- Abre una ventana de comandos (CMD o PowerShell).
|
|
|
|
3. INSTALACIÓN DE DEPENDENCIAS:
|
|
Ejecuta los siguientes comandos para instalar las dependencias necesarias:
|
|
|
|
pip install sounddevice -> Para capturar audio en tiempo real.
|
|
pip install numpy -> Para operaciones numéricas y manejo de arrays.
|
|
pip install openai-whisper -> Para transcripción con el modelo Whisper.
|
|
* Requiere además:
|
|
- ffmpeg: Descárgalo desde https://ffmpeg.org/download.html#build-windows y agrega la carpeta "bin" al PATH.
|
|
- PyTorch: Instálalo siguiendo las instrucciones en https://pytorch.org/
|
|
pip install pydub -> Para manipulación y segmentación de audio (requiere ffmpeg instalado y en el PATH).
|
|
pip install pyAudioAnalysis -> (No se usará la diarización en este script, pero se puede instalar para otros usos).
|
|
pip install hmmlearn -> Dependencia de pyAudioAnalysis.
|
|
pip install matplotlib -> Usado por pyAudioAnalysis para gráficos.
|
|
pip install eyed3 -> Dependencia de pyAudioAnalysis para manejo de metadatos.
|
|
pip install imbalanced-learn -> En algunos casos requerido por pyAudioAnalysis.
|
|
pip install plotly -> En algunos casos requerido por pyAudioAnalysis.
|
|
pip install SpeechRecognition -> Para Google Speech-to-Text.
|
|
|
|
4. EJECUCIÓN DEL SCRIPT:
|
|
- Guarda este código en un archivo (por ejemplo, "transcriptor.py").
|
|
- Ejecuta el script desde la línea de comandos con:
|
|
python transcriptor.py
|
|
- Sigue las instrucciones en pantalla para ingresar el código de idioma para Google Speech-to-Text.
|
|
|
|
Nota: En Windows, para capturar el audio del sistema (lo que se reproduce en el sistema), puede ser necesario configurar el dispositivo WASAPI loopback.
|
|
===========================================================
|
|
"""
|
|
|
|
import sounddevice as sd # Captura de audio en tiempo real
|
|
import numpy as np # Procesamiento numérico
|
|
import queue # Manejo de colas
|
|
import threading # Para procesamiento en segundo plano (hilos)
|
|
import time # Funciones de tiempo y pausas
|
|
import wave # Manejo de archivos WAV
|
|
import os # Operaciones con archivos/sistema
|
|
import datetime # Para trabajar con fechas y horas
|
|
import whisper # Transcripción con modelo Whisper (openai-whisper)
|
|
from pydub import AudioSegment # Manipulación de audio (pydub)
|
|
import speech_recognition as sr # Transcripción con Google Speech-to-Text
|
|
|
|
# ================== Configuración ==================
|
|
SAMPLE_RATE = 44100 # Frecuencia de muestreo (Hz)
|
|
CHUNK_DURATION = 6 # Duración en segundos de cada fragmento a procesar
|
|
MIN_VOLUME_THRESHOLD = 0.003 # Umbral mínimo de RMS para descartar silencios excesivos
|
|
transcription_file = "250506_UNIVERSAL_ROBOTS_ROBOPLUS.txt" # Archivo donde se guardará la transcripción
|
|
|
|
# Para Whisper: Si TARGET_LANGUAGE es None, Whisper detecta automáticamente el idioma.
|
|
TARGET_LANGUAGE = 'es' # Ejemplo: 'es' para español
|
|
|
|
# Cola para almacenar los fragmentos de audio capturados
|
|
audio_queue = queue.Queue()
|
|
|
|
# Cargar el modelo Whisper (puedes elegir 'base', 'small', 'medium', etc.)
|
|
print("Cargando modelo Whisper...")
|
|
model = whisper.load_model('base')
|
|
print("Modelo cargado.")
|
|
|
|
# ================== Captura de Audio ==================
|
|
def audio_callback(indata, frames, time_info, status):
|
|
"""
|
|
Callback de sounddevice para capturar audio en tiempo real.
|
|
Los datos se copian y se colocan en la cola para su posterior procesamiento.
|
|
"""
|
|
if status:
|
|
print("Status:", status)
|
|
audio_queue.put(indata.copy())
|
|
|
|
# ================== Procesamiento en Segundo Plano ==================
|
|
def process_audio():
|
|
"""
|
|
Acumula fragmentos de audio hasta alcanzar CHUNK_DURATION segundos y luego los procesa.
|
|
"""
|
|
audio_buffer = []
|
|
samples_needed = int(CHUNK_DURATION * SAMPLE_RATE)
|
|
while True:
|
|
try:
|
|
data = audio_queue.get(timeout=1)
|
|
audio_buffer.append(data)
|
|
total_samples = sum(chunk.shape[0] for chunk in audio_buffer)
|
|
if total_samples >= samples_needed:
|
|
# Concatenar los fragmentos acumulados en un único fragmento
|
|
audio_chunk = np.concatenate(audio_buffer, axis=0)
|
|
audio_buffer = [] # Reiniciar buffer tras el procesamiento
|
|
process_chunk(audio_chunk)
|
|
except queue.Empty:
|
|
continue
|
|
|
|
def process_chunk(chunk):
|
|
"""
|
|
Procesa un fragmento de audio completo:
|
|
1. Verifica que el audio no esté en silencio (según el umbral).
|
|
2. Guarda el fragmento en un archivo WAV temporal.
|
|
3. Transcribe el fragmento utilizando tanto Whisper como Google Speech-to-Text.
|
|
4. Guarda la transcripción (con timestamp) en 'transcripcion.txt' y elimina los archivos temporales.
|
|
"""
|
|
# Calcular RMS y el volumen máximo del fragmento
|
|
rms = np.sqrt(np.mean(chunk**2))
|
|
max_volume = np.max(np.abs(chunk))
|
|
print(f"RMS: {rms:.5f}, Volumen máximo detectado: {max_volume:.5f}")
|
|
|
|
if rms < MIN_VOLUME_THRESHOLD:
|
|
print("Fragmento demasiado silencioso. Se omite.")
|
|
return
|
|
|
|
# Convertir de float32 (rango [-1, 1]) a int16
|
|
chunk_int16 = np.int16(chunk * 32767)
|
|
|
|
temp_filename = "temp_audio.wav"
|
|
try:
|
|
with wave.open(temp_filename, 'wb') as wf:
|
|
channels = chunk.shape[1] if chunk.ndim > 1 else 1
|
|
wf.setnchannels(channels)
|
|
wf.setsampwidth(2) # 2 bytes para int16
|
|
wf.setframerate(SAMPLE_RATE)
|
|
wf.writeframes(chunk_int16.tobytes())
|
|
except Exception as e:
|
|
print("Error al guardar el archivo temporal:", e)
|
|
return
|
|
|
|
# Registrar el timestamp de inicio del fragmento
|
|
chunk_start_time = datetime.datetime.now()
|
|
|
|
# ----------------- Transcripción con Whisper -----------------
|
|
try:
|
|
if TARGET_LANGUAGE is not None:
|
|
result_whisper = model.transcribe(temp_filename, language=TARGET_LANGUAGE)
|
|
else:
|
|
result_whisper = model.transcribe(temp_filename)
|
|
text_whisper = result_whisper["text"].strip()
|
|
except Exception as e:
|
|
text_whisper = f"[Error transcribiendo con Whisper: {e}]"
|
|
|
|
# ----------------- Transcripción con Google Speech-to-Text -----------------
|
|
r = sr.Recognizer()
|
|
try:
|
|
with sr.AudioFile(temp_filename) as source:
|
|
audio_data = r.record(source)
|
|
text_google = r.recognize_google(audio_data, language=google_language)
|
|
except Exception as e:
|
|
text_google = f"[Error transcribiendo con Google: {e}]"
|
|
|
|
# Formatear el timestamp
|
|
timestamp_str = chunk_start_time.strftime("%Y-%m-%d %H:%M:%S")
|
|
# Preparar la línea de transcripción (sin identificación de narrador)
|
|
transcript_line = (f"[{timestamp_str}] Whisper: {text_whisper}\n"
|
|
f"[{timestamp_str}] Google: {text_google}\n")
|
|
print(transcript_line)
|
|
with open(transcription_file, "a", encoding="utf-8") as f:
|
|
f.write(transcript_line)
|
|
|
|
# Eliminar el archivo temporal
|
|
os.remove(temp_filename)
|
|
|
|
# ================== EJECUCIÓN PRINCIPAL ==================
|
|
if __name__ == "__main__":
|
|
# Preguntar al usuario el código de idioma para Google Speech-to-Text.
|
|
google_language = input("Ingrese el código de idioma para Google Speech-to-Text (ej: es-ES, en-US, ca-ES, enter para es-ES): ").strip()
|
|
if not google_language:
|
|
google_language = "es-ES" # Valor por defecto si no se ingresa nada
|
|
|
|
# Iniciar el hilo de procesamiento en segundo plano
|
|
processing_thread = threading.Thread(target=process_audio, daemon=True)
|
|
processing_thread.start()
|
|
|
|
try:
|
|
# Nota: En Windows, para capturar el audio del sistema puede ser necesario configurar el dispositivo WASAPI loopback.
|
|
with sd.InputStream(samplerate=SAMPLE_RATE, channels=1, callback=audio_callback):
|
|
print("Capturando audio... Presiona Ctrl+C para detener.")
|
|
while True:
|
|
time.sleep(0.1)
|
|
except KeyboardInterrupt:
|
|
print("Interrupción del usuario. Finalizando ejecución...")
|
|
except Exception as e:
|
|
print("Error:", e)
|