|
|
import gradio as gr |
|
|
from transformers import pipeline |
|
|
from pydub import AudioSegment |
|
|
from pydub.utils import make_chunks |
|
|
import tempfile |
|
|
import os |
|
|
import math |
|
|
|
|
|
|
|
|
MODEL_NAME = "openai/whisper-small" |
|
|
CHUNK_LENGTH_MS = 30_000 |
|
|
MAX_FILE_SIZE_MB = 250 |
|
|
TARGET_SAMPLE_RATE = 16000 |
|
|
|
|
|
|
|
|
print(f"Carregando modelo Whisper: {MODEL_NAME}...") |
|
|
|
|
|
transcriber = pipeline( |
|
|
"automatic-speech-recognition", |
|
|
model=MODEL_NAME, |
|
|
device="cpu" |
|
|
) |
|
|
print("Modelo carregado.") |
|
|
|
|
|
|
|
|
def split_audio(audio_path, chunk_length=CHUNK_LENGTH_MS): |
|
|
try: |
|
|
audio = AudioSegment.from_file(audio_path) |
|
|
print(f"Áudio carregado: Duração={audio.duration_seconds:.2f}s, Canais={audio.channels}, Taxa={audio.frame_rate}Hz") |
|
|
chunks = make_chunks(audio, chunk_length) |
|
|
print(f"Áudio dividido em {len(chunks)} chunks de ~{chunk_length/1000}s") |
|
|
return chunks |
|
|
except Exception as e: |
|
|
print(f"Erro ao carregar ou dividir áudio: {e}") |
|
|
raise |
|
|
|
|
|
|
|
|
def prepare_audio(audio_path): |
|
|
try: |
|
|
audio = AudioSegment.from_file(audio_path) |
|
|
|
|
|
prepared_audio = audio.set_frame_rate(TARGET_SAMPLE_RATE).set_channels(1).set_sample_width(2) |
|
|
|
|
|
|
|
|
with tempfile.NamedTemporaryFile(suffix=".wav", delete=False) as temp_f: |
|
|
prepared_path = temp_f.name |
|
|
|
|
|
prepared_audio.export(prepared_path, format="wav") |
|
|
print(f"Áudio preparado e salvo em: {prepared_path}") |
|
|
return prepared_path |
|
|
except Exception as e: |
|
|
print(f"Erro ao preparar áudio: {e}") |
|
|
raise |
|
|
|
|
|
|
|
|
def transcribe_audio_generator(audio_filepath): |
|
|
if audio_filepath is None: |
|
|
yield "Erro: Nenhum arquivo de áudio enviado." |
|
|
return |
|
|
|
|
|
try: |
|
|
file_size_bytes = os.path.getsize(audio_filepath) |
|
|
file_size_mb = file_size_bytes / (1024 * 1024) |
|
|
print(f"Arquivo recebido: {audio_filepath}, Tamanho: {file_size_mb:.2f} MB") |
|
|
|
|
|
|
|
|
if file_size_bytes > MAX_FILE_SIZE_MB * 1024 * 1024: |
|
|
yield f"Erro: O arquivo excede o limite de {MAX_FILE_SIZE_MB} MB. Tamanho atual: {file_size_mb:.2f} MB." |
|
|
return |
|
|
|
|
|
yield f"Iniciando pré-processamento (pode levar um tempo)... Tamanho: {file_size_mb:.2f} MB" |
|
|
|
|
|
prepared_audio_path = None |
|
|
try: |
|
|
prepared_audio_path = prepare_audio(audio_filepath) |
|
|
|
|
|
yield f"Pré-processamento concluído. Dividindo em chunks..." |
|
|
chunks = split_audio(prepared_audio_path) |
|
|
total_chunks = len(chunks) |
|
|
|
|
|
if total_chunks == 0: |
|
|
yield "Erro: Não foi possível dividir o áudio em chunks." |
|
|
return |
|
|
|
|
|
full_transcription = [] |
|
|
yield f"Iniciando transcrição de {total_chunks} chunks (Modelo: {MODEL_NAME}). Isso pode demorar bastante..." |
|
|
|
|
|
|
|
|
for i, chunk in enumerate(chunks): |
|
|
|
|
|
with tempfile.NamedTemporaryFile(suffix=".wav", delete=True) as temp_chunk_file: |
|
|
chunk.export(temp_chunk_file.name, format="wav") |
|
|
|
|
|
|
|
|
|
|
|
result = transcriber( |
|
|
temp_chunk_file.name, |
|
|
chunk_length_s=math.ceil(CHUNK_LENGTH_MS / 1000), |
|
|
return_timestamps=False |
|
|
) |
|
|
transcription = result["text"] |
|
|
|
|
|
|
|
|
chunk_label = f"[Chunk {i+1}/{total_chunks}]" |
|
|
full_transcription.append(f"{chunk_label}: {transcription}") |
|
|
|
|
|
|
|
|
progress_update = f"Processando: {i+1}/{total_chunks} chunks...\n\n" + "\n".join(full_transcription) |
|
|
yield progress_update |
|
|
|
|
|
yield "Transcrição completa!\n\n" + "\n".join(full_transcription) |
|
|
|
|
|
except Exception as e: |
|
|
yield f"Erro durante o processamento: {str(e)}" |
|
|
|
|
|
import traceback |
|
|
print("Erro detalhado:") |
|
|
traceback.print_exc() |
|
|
|
|
|
finally: |
|
|
|
|
|
if prepared_audio_path and os.path.exists(prepared_audio_path): |
|
|
try: |
|
|
os.remove(prepared_audio_path) |
|
|
print(f"Arquivo temporário preparado removido: {prepared_audio_path}") |
|
|
except OSError as e: |
|
|
print(f"Erro ao remover arquivo temporário {prepared_audio_path}: {e}") |
|
|
|
|
|
|
|
|
|
|
|
except Exception as e: |
|
|
yield f"Erro inesperado ao processar áudio: {str(e)}" |
|
|
import traceback |
|
|
print("Erro detalhado:") |
|
|
traceback.print_exc() |
|
|
|
|
|
|
|
|
|
|
|
with gr.Blocks() as demo: |
|
|
gr.Markdown(f"# 🎙️ Whisper Transcription - Áudios Longos (até {MAX_FILE_SIZE_MB} MB)") |
|
|
gr.Markdown(f"**Atenção:** Áudios muito longos podem levar **muito tempo** para processar (potencialmente horas), especialmente com o modelo `{MODEL_NAME}` na CPU.") |
|
|
|
|
|
with gr.Row(): |
|
|
with gr.Column(scale=1): |
|
|
gr.Markdown(f"### 1️⃣ Envie seu áudio (máx. {MAX_FILE_SIZE_MB} MB)") |
|
|
audio_input = gr.Audio(type="filepath", label="Envie um arquivo de áudio") |
|
|
|
|
|
with gr.Column(scale=1): |
|
|
gr.Markdown("### 2️⃣ Resultado da transcrição (atualizado durante o processo)") |
|
|
transcription_output = gr.Textbox(label="Transcrição", lines=20, interactive=False) |
|
|
|
|
|
transcribe_button = gr.Button("🚀 Transcrever Áudio") |
|
|
|
|
|
|
|
|
transcribe_button.click( |
|
|
fn=transcribe_audio_generator, |
|
|
inputs=[audio_input], |
|
|
outputs=[transcription_output] |
|
|
) |
|
|
|
|
|
|
|
|
print("Iniciando interface Gradio...") |
|
|
|
|
|
demo.launch(share=False) |
|
|
print("Interface disponível.") |