import os import speech_recognition as sr import difflib import time from langchain_groq.chat_models import ChatGroq from dotenv import load_dotenv import tempfile import gradio as gr # Load environment variables load_dotenv() class PronunciaPratica: def __init__(self, idioma='pt-BR'): """ Initializes the pronunciation practice application. Args: idioma (str): Language code for speech recognition (e.g., 'pt-BR', 'en-US') """ self.idioma = idioma self.recognizer = sr.Recognizer() # Configure the Groq client using an environment variable api_key = os.getenv("GROQ_API_KEY") if not api_key: raise ValueError("⚠️ Groq API key not found. Set the GROQ_API_KEY environment variable.") self.chat = ChatGroq(model="llama-3.1-8b-instant", api_key=api_key) def gerar_frase(self): """Generate a random phrase for pronunciation practice.""" try: # Map language code to language name for better prompt language_map = { 'pt-BR': 'português', 'en-US': 'inglês', 'es-ES': 'espanhol', 'fr-FR': 'francês', 'it-IT': 'italiano', 'de-DE': 'alemão' } language_name = language_map.get(self.idioma, 'português') # Create a more explicit prompt that specifies the language prompt = f""" Forneça uma frase curta em {language_name} para treinar pronúncia. A frase deve ter entre 5 e 10 palavras. Responda APENAS com a frase em {language_name}, sem explicações. É MUITO IMPORTANTE que a frase seja apenas em {language_name} e não em qualquer outro idioma. """ resposta = self.chat.invoke([{ "role": "system", "content": prompt }]) return resposta.content.strip() except Exception as e: # Default phrases based on language default_phrases = { 'pt-BR': "O sol está brilhando hoje.", 'en-US': "The sun is shining today.", 'es-ES': "El sol está brillando hoy.", 'fr-FR': "Le soleil brille aujourd'hui.", 'it-IT': "Il sole splende oggi.", 'de-DE': "Die Sonne scheint heute." } return f"{default_phrases.get(self.idioma, 'O sol está brilhando hoje.')} (Erro: {str(e)})" def reconhecer_audio(self, audio_path): """Convert recorded audio to text.""" try: # Load the audio file with sr.AudioFile(audio_path) as source: audio_data = self.recognizer.record(source) # Recognize the text texto_falado = self.recognizer.recognize_google(audio_data, language=self.idioma) return texto_falado except sr.UnknownValueError: return "Erro: Não foi possível entender o áudio." except sr.RequestError as e: return f"Erro: Problema na requisição do serviço de reconhecimento. {e}" except Exception as e: return f"Erro: {e}" def avaliar_pronuncia(self, frase_original, frase_falada): """ Evaluate the similarity between the original phrase and the spoken phrase. Returns: tuple: (similarity percentage, incorrect words) """ # Normalize phrases (remove punctuation and convert to lowercase) import re normalizar = lambda texto: re.sub(r'[^\w\s]', '', texto.lower()) original_norm = normalizar(frase_original) falada_norm = normalizar(frase_falada) # Calculate similarity sequencia = difflib.SequenceMatcher(None, original_norm, falada_norm) similaridade = sequencia.ratio() * 100 # Identify incorrect words palavras_originais = original_norm.split() palavras_faladas = falada_norm.split() palavras_incorretas = [] for palavra in palavras_originais: if palavra not in palavras_faladas: palavras_incorretas.append(palavra) return similaridade, palavras_incorretas def obter_feedback(self, similaridade, palavras_incorretas, frase_original, frase_falada): """Generate detailed feedback on pronunciation.""" # Determine language for feedback based on idioma feedback_lang = self.idioma # Choose appropriate language for the feedback prompt if feedback_lang.startswith('pt'): prompt_template = """ Analise a pronúncia do usuário e forneça feedback específico: Frase original: "{frase_original}" Frase falada: "{frase_falada}" Similaridade: {similaridade:.2f}% Palavras possivelmente problemáticas: {palavras_prob} Ofereça dicas específicas para melhorar a pronúncia, focando nos erros mais comuns. Seja breve e construtivo, máximo de 3 linhas. """ palavras_prob = ', '.join(palavras_incorretas) if palavras_incorretas else 'Nenhuma' elif feedback_lang.startswith('en'): prompt_template = """ Analyze the user's pronunciation and provide specific feedback: Original phrase: "{frase_original}" Spoken phrase: "{frase_falada}" Similarity: {similaridade:.2f}% Potentially problematic words: {palavras_prob} Offer specific tips to improve pronunciation, focusing on the most common errors. Be brief and constructive, maximum of 3 lines. """ palavras_prob = ', '.join(palavras_incorretas) if palavras_incorretas else 'None' else: # Default to English for other languages prompt_template = """ Analyze the user's pronunciation and provide specific feedback: Original phrase: "{frase_original}" Spoken phrase: "{frase_falada}" Similarity: {similaridade:.2f}% Potentially problematic words: {palavras_prob} Offer specific tips to improve pronunciation, focusing on the most common errors. Be brief and constructive, maximum of 3 lines. """ palavras_prob = ', '.join(palavras_incorretas) if palavras_incorretas else 'None' prompt = prompt_template.format( frase_original=frase_original, frase_falada=frase_falada, similaridade=similaridade, palavras_prob=palavras_prob ) try: resposta = self.chat.invoke([{"role": "system", "content": prompt}]) return resposta.content.strip() except Exception as e: # Default feedback in case of error, based on language if feedback_lang.startswith('pt'): if similaridade > 90: return "Excelente pronúncia! Continue praticando." elif similaridade > 70: return f"Boa pronúncia, mas pode melhorar. Preste atenção em: {', '.join(palavras_incorretas)}" else: return "Tente novamente, focando na pronúncia clara de cada palavra." else: if similaridade > 90: return "Excellent pronunciation! Keep practicing." elif similaridade > 70: return f"Good pronunciation, but can be improved. Pay attention to: {', '.join(palavras_incorretas)}" else: return "Try again, focusing on clear pronunciation of each word." # Function to map language dropdown to language code def get_language_code(language_name): idiomas = { "Português (Brasil)": "pt-BR", "Inglês (EUA)": "en-US", "Espanhol": "es-ES", "Francês": "fr-FR", "Italiano": "it-IT", "Alemão": "de-DE" } return idiomas.get(language_name, "pt-BR") # Create a global instance of the app app_instance = None # Track the current language current_language_code = "pt-BR" # Functions for Gradio interface def gerar_nova_frase(language_name): global app_instance, current_language_code language_code = get_language_code(language_name) # Only create a new instance if the language has changed if app_instance is None or current_language_code != language_code: app_instance = PronunciaPratica(idioma=language_code) current_language_code = language_code return app_instance.gerar_frase() def process_audio(audio_path, frase_atual, language_name, historico): global app_instance, current_language_code if audio_path is None: return "Nenhum áudio gravado", "", 0, "", historico, "" # Make sure we have an app instance with the current language language_code = get_language_code(language_name) if app_instance is None or current_language_code != language_code: app_instance = PronunciaPratica(idioma=language_code) current_language_code = language_code # Recognize the speech texto_falado = app_instance.reconhecer_audio(audio_path) if texto_falado.startswith("Erro"): return texto_falado, "", 0, "", historico, "" # Evaluate pronunciation similaridade, palavras_incorretas = app_instance.avaliar_pronuncia(frase_atual, texto_falado) # Get detailed feedback feedback = app_instance.obter_feedback(similaridade, palavras_incorretas, frase_atual, texto_falado) # Add to history entry = { "frase": frase_atual, "falado": texto_falado, "similaridade": f"{similaridade:.1f}%", "feedback": feedback, "timestamp": time.strftime("%H:%M:%S") } historico = [entry] + historico if len(historico) > 5: historico = historico[:5] # Modificação aqui para melhorar a visibilidade do histórico history_html = "" for entry in historico: history_html += f"""