import gradio as gr import os import json from datetime import datetime from pathlib import Path import soundfile as sf from core.cloner import KokoClone import whisper from transformers import AutoTokenizer, AutoModelForSeq2SeqLM # === MODELOS BASE === print("Loading Whisper + Translator models...") whisper_model = whisper.load_model("small") translator_tokenizer = AutoTokenizer.from_pretrained("Helsinki-NLP/opus-mt-es-en") translator_model = AutoModelForSeq2SeqLM.from_pretrained("Helsinki-NLP/opus-mt-es-en") print("Loading KokoClone models for the Web UI...") cloner = KokoClone() # === MODELO DE CORRECCIÓN GRAMATICAL BRITÁNICA === grammar_tokenizer = AutoTokenizer.from_pretrained("prithivida/grammar_error_correcter_v1") grammar_model = AutoModelForSeq2SeqLM.from_pretrained("prithivida/grammar_error_correcter_v1") # === Memoria de conversación (solo durante la sesión) === conversation_history = [] # === Diccionario personal === DATA_DIR = Path("/app/user_data") AUDIO_DIR = DATA_DIR / "audios" DICT_PATH = DATA_DIR / "dictionary.json" DATA_DIR.mkdir(parents=True, exist_ok=True) AUDIO_DIR.mkdir(parents=True, exist_ok=True) def load_dictionary(): if DICT_PATH.exists(): with open(DICT_PATH, "r", encoding="utf-8") as f: return json.load(f) return [] def save_dictionary(entries): with open(DICT_PATH, "w", encoding="utf-8") as f: json.dump(entries, f, ensure_ascii=False, indent=2) def add_entry(entry): entries = load_dictionary() entries.append(entry) save_dictionary(entries) # === Spanish speech → English text === def spanish_to_english(audio_file): result = whisper_model.transcribe(audio_file) spanish_text = result["text"] inputs = translator_tokenizer(spanish_text, return_tensors="pt") translated_tokens = translator_model.generate(**inputs) english_text = translator_tokenizer.decode(translated_tokens[0], skip_special_tokens=True) return spanish_text, english_text # === Clone text → voice === def clone_voice(text, lang, ref_audio_path): if not text or not text.strip(): raise gr.Error("Please enter some text.") if not ref_audio_path: raise gr.Error("Please upload or record a reference audio file.") output_file = "gradio_output.wav" try: cloner.generate( text=text, lang=lang, reference_audio=ref_audio_path, output_path=output_file ) return output_file except Exception as e: raise gr.Error(f"An error occurred during generation: {str(e)}") # === Convert audio → revoice === def convert_voice(source_audio_path, ref_audio_path): if not source_audio_path: raise gr.Error("Please upload or record a source audio file.") if not ref_audio_path: raise gr.Error("Please upload or record a reference audio file.") output_file = "gradio_convert_output.wav" try: cloner.convert( source_audio=source_audio_path, reference_audio=ref_audio_path, output_path=output_file ) return output_file except Exception as e: raise gr.Error(f"An error occurred during conversion: {str(e)}") # === Spanish speech → English clone + dictionary === def mic_to_english_clone(audio_file, ref_audio_path): if not audio_file: raise gr.Error("Please speak into the microphone.") if not ref_audio_path: raise gr.Error("Please upload a reference audio (your voice).") spanish_text, english_text = spanish_to_english(audio_file) timestamp = datetime.now().strftime("%Y-%m-%d_%H-%M-%S") output_file = AUDIO_DIR / f"mic_english_clone_{timestamp}.wav" cloner.generate( text=english_text, lang="en", reference_audio=ref_audio_path, output_path=str(output_file) ) entry = { "fecha": datetime.now().strftime("%Y-%m-%d %H:%M:%S"), "modo": "hablar_espanol_a_ingles", "espanol": spanish_text, "ingles_traducido": english_text, "audio_clonado": str(output_file), "nota": "shadowing" } add_entry(entry) return str(output_file), english_text # === Pronunciación → corrección (profesor británico estricto) === def pronunciation_correction(audio_file, target_text, ref_audio_path): if not audio_file: raise gr.Error("Please speak the sentence in English.") if not target_text or not target_text.strip(): raise gr.Error("Please provide the target English sentence.") if not ref_audio_path: raise gr.Error("Please upload a reference audio (your voice).") result = whisper_model.transcribe(audio_file) spoken_text = result["text"].strip() target_clean = target_text.strip() spoken_clean = spoken_text.strip() target_words = target_clean.lower().split() spoken_words = spoken_clean.lower().split() matches = sum(1 for w in spoken_words if w in target_words) score = int(100 * matches / max(len(target_words), 1)) feedback_lines = [] feedback_lines.append("British strict professor report") feedback_lines.append(f"Target sentence: {target_clean}") feedback_lines.append(f"You said: {spoken_clean}") feedback_lines.append(f"Pronunciation score: {score}%") if score == 100: feedback_lines.append("Excellent. Your pronunciation and wording are accurate. We may proceed.") elif score >= 80: feedback_lines.append("Good attempt, but not perfect. Some words were unclear or slightly off.") feedback_lines.append("Repeat the sentence focusing on rhythm and clarity.") else: feedback_lines.append("Your sentence is not sufficiently accurate.") feedback_lines.append("You must repeat it precisely before we continue.") feedback_lines.append("Listen carefully to the corrected version and shadow it several times.") feedback = "\n".join(feedback_lines) timestamp = datetime.now().strftime("%Y-%m-%d_%H-%M-%S") output_file = AUDIO_DIR / f"pronunciation_correct_{timestamp}.wav" cloner.generate( text=target_clean, lang="en", reference_audio=ref_audio_path, output_path=str(output_file) ) entry = { "fecha": datetime.now().strftime("%Y-%m-%d %H:%M:%S"), "modo": "pronunciacion_profesor_britanico_estricto", "frase_objetivo": target_clean, "frase_dicha": spoken_clean, "score": score, "audio_clonado": str(output_file), "feedback": feedback } add_entry(entry) return str(output_file), feedback # === CORRECCIÓN GRAMATICAL BRITÁNICA === def british_grammar_fix(sentence): inputs = grammar_tokenizer.encode(sentence, return_tensors="pt") outputs = grammar_model.generate(inputs, max_length=128) corrected = grammar_tokenizer.decode(outputs[0], skip_special_tokens=True) return corrected # === CONVERSACIÓN AVANZADA (PROFESOR LONDINENSE NATURAL) === def conversation_professor(audio_file, ref_audio_path, topic): if not audio_file: raise gr.Error("Please speak into the microphone.") if not ref_audio_path: raise gr.Error("Please upload your reference voice.") if not topic: raise gr.Error("Please choose a topic for the conversation.") # 1. Transcribir lo que has dicho result = whisper_model.transcribe(audio_file) spoken_text = result["text"].strip() # 2. Corregir gramática británica corrected = british_grammar_fix(spoken_text) # 3. Guardar en historial global conversation_history conversation_history.append({ "user_raw": spoken_text, "user_corrected": corrected, "topic": topic, "timestamp": datetime.now().strftime("%Y-%m-%d %H:%M:%S") }) # 4. Generar feedback estilo londinense natural feedback_lines = [] feedback_lines.append(f"Topic: {topic}") feedback_lines.append(f"You said: {spoken_text}") feedback_lines.append(f"Natural British version: {corrected}") feedback_lines.append("") if corrected.lower() == spoken_text.lower(): feedback_lines.append( "Nice one. That already sounds quite natural. " "Next time, try to expand your answer a bit more." ) else: feedback_lines.append( "Not bad, but there are a few things to polish. " "Use the corrected version and say it again, a bit more confidently." ) if len(conversation_history) > 1: last = conversation_history[-2] feedback_lines.append("") feedback_lines.append( "Earlier you said: \"" + last["user_corrected"] + "\" " "— you're slowly building a nice flow. Keep going." ) feedback_lines.append("") if topic == "Restaurant": feedback_lines.append( "Imagine we're still at the restaurant in London. " "Now tell me what you would like to order, using at least two adjectives." ) elif topic == "Hotel": feedback_lines.append( "We're at the hotel reception. " "Now explain politely that you need a late check-out tomorrow." ) elif topic == "Airport": feedback_lines.append( "We're at Heathrow. " "Now tell me what you would say if your flight was delayed by three hours." ) elif topic == "Work": feedback_lines.append( "We're at the office. " "Now describe your typical working day in three sentences." ) else: feedback_lines.append( "Carry on talking about this topic, but try to be a bit more specific." ) feedback = "\n".join(feedback_lines) # 5. Generar audio con la versión corregida (tu voz clonada) timestamp = datetime.now().strftime("%Y-%m-%d_%H-%M-%S") output_file = AUDIO_DIR / f"professor_conversation_{timestamp}.wav" cloner.generate( text=corrected, lang="en", reference_audio=ref_audio_path, output_path=str(output_file) ) return str(output_file), corrected, feedback # === UI === with gr.Blocks() as demo: gr.Markdown( """
Voice Cloning + English Trainer (British strict professor + London conversation).
Daily shadowing, correction and personal dictionary.