muycontento's picture
Update app.py
6eaded5 verified
Raw
History Blame Contribute Delete
18.5 kB
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(
"""
<div style="text-align: center;">
<h1>Miguel KokoClone</h1>
<p>Voice Cloning + English Trainer (British strict professor + London conversation).<br>
Daily shadowing, correction and personal dictionary.</p>
</div>
"""
)
with gr.Tabs():
# Text → Clone
with gr.Tab("Text → Clone"):
with gr.Row():
with gr.Column():
text_input = gr.Textbox(
label="1. Text to Synthesize",
lines=4,
placeholder="Enter the text you want spoken..."
)
lang_input = gr.Dropdown(
label="2. Language",
choices=[
("English", "en"),
("Hindi", "hi"),
("French", "fr"),
("Japanese", "ja"),
("Chinese", "zh"),
("Italian", "it"),
("Spanish", "es"),
("Portuguese", "pt")
],
value="en"
)
ref_audio_input = gr.Audio(
label="3. Reference Voice (Upload or Record)",
type="filepath"
)
submit_btn = gr.Button("Generate Clone", variant="primary")
with gr.Column():
output_audio = gr.Audio(
label="Generated Cloned Audio",
interactive=False,
autoplay=False
)
submit_btn.click(
fn=clone_voice,
inputs=[text_input, lang_input, ref_audio_input],
outputs=output_audio
)
# Audio → Clone
with gr.Tab("Audio → Clone"):
with gr.Row():
with gr.Column():
source_audio_input = gr.Audio(
label="1. Source Audio (speech to re-voice)",
type="filepath"
)
ref_audio_convert_input = gr.Audio(
label="2. Reference Voice (target speaker)",
type="filepath"
)
convert_btn = gr.Button("Convert Voice", variant="primary")
with gr.Column():
convert_output_audio = gr.Audio(
label="Converted Audio",
interactive=False,
autoplay=False
)
convert_btn.click(
fn=convert_voice,
inputs=[source_audio_input, ref_audio_convert_input],
outputs=convert_output_audio
)
# Hablar → Inglés (shadowing + diccionario)
with gr.Tab("Hablar → Inglés (voz clonada + texto)"):
with gr.Row():
with gr.Column():
mic_input = gr.Audio(
label="1. Speak in Spanish (microphone)",
sources=["microphone"],
type="filepath"
)
ref_audio_mic_input = gr.Audio(
label="2. Reference Voice (your voice)",
type="filepath"
)
mic_btn = gr.Button("Speak → English Clone", variant="primary")
with gr.Column():
mic_output_audio = gr.Audio(
label="English Cloned Audio",
interactive=False,
autoplay=False
)
english_text_output = gr.Textbox(
label="English Transcription",
lines=3,
interactive=False
)
mic_btn.click(
fn=mic_to_english_clone,
inputs=[mic_input, ref_audio_mic_input],
outputs=[mic_output_audio, english_text_output]
)
# Pronunciación → corrección (profesor británico estricto)
with gr.Tab("Pronunciación (profesor británico estricto)"):
with gr.Row():
with gr.Column():
pron_audio = gr.Audio(
label="1. Say the sentence in English (microphone)",
sources=["microphone"],
type="filepath"
)
target_sentence = gr.Textbox(
label="2. Target English sentence (British)",
lines=2,
placeholder="Type the exact sentence you want to pronounce..."
)
ref_audio_pron = gr.Audio(
label="3. Reference Voice (your voice)",
type="filepath"
)
pron_btn = gr.Button("Evaluate pronunciation", variant="primary")
with gr.Column():
pron_output_audio = gr.Audio(
label="Correct British version (your cloned voice)",
interactive=False,
autoplay=False
)
pron_feedback = gr.Textbox(
label="Professor feedback",
lines=8,
interactive=False
)
pron_btn.click(
fn=pronunciation_correction,
inputs=[pron_audio, target_sentence, ref_audio_pron],
outputs=[pron_output_audio, pron_feedback]
)
# Conversación avanzada (profesor londinense)
with gr.Tab("Conversación avanzada (profesor londinense)"):
with gr.Row():
with gr.Column():
conv_topic = gr.Dropdown(
label="Conversation topic",
choices=[
"Restaurant",
"Hotel",
"Airport",
"Work",
"Casual"
],
value="Casual"
)
conv_audio = gr.Audio(
label="Speak in English (microphone)",
sources=["microphone"],
type="filepath"
)
conv_ref = gr.Audio(
label="Reference Voice (your voice)",
type="filepath"
)
conv_btn = gr.Button("Send to professor", variant="primary")
with gr.Column():
conv_output_audio = gr.Audio(
label="British corrected version (your cloned voice)",
interactive=False,
autoplay=False
)
conv_corrected_text = gr.Textbox(
label="Natural British sentence",
lines=3,
interactive=False
)
conv_feedback = gr.Textbox(
label="Professor (London) feedback + next question",
lines=10,
interactive=False
)
conv_btn.click(
fn=conversation_professor,
inputs=[conv_audio, conv_ref, conv_topic],
outputs=[conv_output_audio, conv_corrected_text, conv_feedback]
)
# Mi diccionario
with gr.Tab("Mi diccionario"):
dict_view = gr.JSON(
label="Stored entries (Spanish → English, pronunciation, professor feedback)",
value=load_dictionary()
)
# === Launch ===
if __name__ == "__main__":
demo.launch(server_name="0.0.0.0", share=True)