import edge_tts import gradio as gr import tempfile import anyio import re import json import asyncio language_dict = { 'English-Jenny (Female)': 'en-US-JennyNeural', 'English-Guy (Male)': 'en-US-GuyNeural', 'English-Ana (Female)': 'en-US-AnaNeural', 'English-Aria (Female)': 'en-US-AriaNeural', 'English-Christopher (Male)': 'en-US-ChristopherNeural', 'English-Eric (Male)': 'en-US-EricNeural', 'English-Michelle (Female)': 'en-US-MichelleNeural', 'English-Roger (Male)': 'en-US-RogerNeural', 'Spanish (Mexican)-Dalia (Female)': 'es-MX-DaliaNeural', 'Spanish (Mexican)-Jorge- (Male)': 'es-MX-JorgeNeural', 'Korean-Sun-Hi- (Female)': 'ko-KR-SunHiNeural', 'Korean-InJoon- (Male)': 'ko-KR-InJoonNeural', 'Thai-Premwadee- (Female)': 'th-TH-PremwadeeNeural', 'Thai-Niwat- (Male)': 'th-TH-NiwatNeural', 'Vietnamese-HoaiMy- (Female)': 'vi-VN-HoaiMyNeural', 'Vietnamese-NamMinh- (Male)': 'vi-VN-NamMinhNeural', 'Japanese-Nanami- (Female)': 'ja-JP-NanamiNeural', 'Japanese-Keita- (Male)': 'ja-JP-KeitaNeural', 'French-Denise- (Female)': 'fr-FR-DeniseNeural', 'French-Eloise- (Female)': 'fr-FR-EloiseNeural', 'French-Henri- (Male)': 'fr-FR-HenriNeural', 'Brazilian-Francisca- (Female)': 'pt-BR-FranciscaNeural', 'Brazilian-Antonio- (Male)': 'pt-BR-AntonioNeural', 'Indonesian-Ardi- (Male)': 'id-ID-ArdiNeural', 'Indonesian-Gadis- (Female)': 'id-ID-GadisNeural', 'Hebrew-Avri- (Male)': 'he-IL-AvriNeural', 'Hebrew-Hila- (Female)': 'he-IL-HilaNeural', 'Italian-Isabella- (Female)': 'it-IT-IsabellaNeural', 'Italian-Diego- (Male)': 'it-IT-DiegoNeural', 'Italian-Elsa- (Female)': 'it-IT-ElsaNeural', 'Dutch-Colette- (Female)': 'nl-NL-ColetteNeural', 'Dutch-Fenna- (Female)': 'nl-NL-FennaNeural', 'Dutch-Maarten- (Male)': 'nl-NL-MaartenNeural', 'Malese-Osman- (Male)': 'ms-MY-OsmanNeural', 'Malese-Yasmin- (Female)': 'ms-MY-YasminNeural', 'Norwegian-Pernille- (Female)': 'nb-NO-PernilleNeural', 'Norwegian-Finn- (Male)': 'nb-NO-FinnNeural', 'Swedish-Sofie- (Female)': 'sv-SE-SofieNeural', 'ArabicSwedish-Mattias- (Male)': 'sv-SE-MattiasNeural', 'Arabic-Hamed- (Male)': 'ar-SA-HamedNeural', 'Arabic-Zariyah- (Female)': 'ar-SA-ZariyahNeural', 'Greek-Athina- (Female)': 'el-GR-AthinaNeural', 'Greek-Nestoras- (Male)': 'el-GR-NestorasNeural', 'German-Katja- (Female)': 'de-DE-KatjaNeural', 'German-Amala- (Female)': 'de-DE-AmalaNeural', 'German-Conrad- (Male)': 'de-DE-ConradNeural', 'German-Killian- (Male)': 'de-DE-KillianNeural', 'Afrikaans-Adri- (Female)': 'af-ZA-AdriNeural', 'Afrikaans-Willem- (Male)': 'af-ZA-WillemNeural', 'Ethiopian-Ameha- (Male)': 'am-ET-AmehaNeural', 'Ethiopian-Mekdes- (Female)': 'am-ET-MekdesNeural', 'Arabic (UAD)-Fatima- (Female)': 'ar-AE-FatimaNeural', 'Arabic (UAD)-Hamdan- (Male)': 'ar-AE-HamdanNeural', 'Arabic (Bahrain)-Ali- (Male)': 'ar-BH-AliNeural', 'Arabic (Bahrain)-Laila- (Female)': 'ar-BH-LailaNeural', 'Arabic (Algeria)-Ismael- (Male)': 'ar-DZ-IsmaelNeural', 'Arabic (Egypt)-Salma- (Female)': 'ar-EG-SalmaNeural', 'Arabic (Egypt)-Shakir- (Male)': 'ar-EG-ShakirNeural', 'Arabic (Iraq)-Bassel- (Male)': 'ar-IQ-BasselNeural', 'Arabic (Iraq)-Rana- (Female)': 'ar-IQ-RanaNeural', 'Arabic (Jordan)-Sana- (Female)': 'ar-JO-SanaNeural', 'Arabic (Jordan)-Taim- (Male)': 'ar-JO-TaimNeural', 'Arabic (Kuwait)-Fahed- (Male)': 'ar-KW-FahedNeural', 'Arabic (Kuwait)-Noura- (Female)': 'ar-KW-NouraNeural', 'Arabic (Lebanon)-Layla- (Female)': 'ar-LB-LaylaNeural', 'Arabic (Lebanon)-Rami- (Male)': 'ar-LB-RamiNeural', 'Arabic (Libya)-Iman- (Female)': 'ar-LY-ImanNeural', 'Arabic (Libya)-Omar- (Male)': 'ar-LY-OmarNeural', 'Arabic (Morocco)-Jamal- (Male)': 'ar-MA-JamalNeural', 'Arabic (Morocco)-Mouna- (Female)': 'ar-MA-MounaNeural', 'Arabic (Oman)-Abdullah- (Male)': 'ar-OM-AbdullahNeural', 'Arabic (Oman)-Aysha- (Female)': 'ar-OM-AyshaNeural', 'Arabic (Qatar)-Amal- (Female)': 'ar-QA-AmalNeural', 'Arabic (Qatar)-Moaz- (Male)': 'ar-QA-MoazNeural', 'Arabic (Syrian Arab Republic)-Amany- (Female)': 'ar-SY-AmanyNeural', 'Arabic (Syrian Arab Republic)-Laith- (Male)': 'ar-SY-LaithNeural', 'Arabic (Tunisia)-Hedi- (Male)': 'ar-TN-HediNeural', 'Arabic (Tunisia)-Reem- (Female)': 'ar-TN-ReemNeural', 'Arabic (Yemen )-Maryam- (Female)': 'ar-YE-MaryamNeural', 'Arabic (Yemen )-Saleh- (Male)': 'ar-YE-SalehNeural', 'Azerbaijani-Babek- (Male)': 'az-AZ-BabekNeural', 'Azerbaijani-Banu- (Female)': 'az-AZ-BanuNeural', 'Bulgarian-Borislav- (Male)': 'bg-BG-BorislavNeural', 'Bulgarian-Kalina- (Female)': 'bg-BG-KalinaNeural', 'Bengali (Bangladesh)-Nabanita- (Female)': 'bn-BD-NabanitaNeural', 'Bengali (Bangladesh)-Pradeep- (Male)': 'bn-BD-PradeepNeural', 'Bengali (India)-Bashkar- (Male)': 'bn-IN-BashkarNeural', 'Bengali (India)-Tanishaa- (Female)': 'bn-IN-TanishaaNeural', 'Bosniak (Bosnia and Herzegovina)-Goran- (Male)': 'bs-BA-GoranNeural', 'Bosniak (Bosnia and Herzegovina)-Vesna- (Female)': 'bs-BA-VesnaNeural', 'Catalan (Spain)-Joana- (Female)': 'ca-ES-JoanaNeural', 'Catalan (Spain)-Enric- (Male)': 'ca-ES-EnricNeural', 'Czech (Czech Republic)-Antonin- (Male)': 'cs-CZ-AntoninNeural', 'Czech (Czech Republic)-Vlasta- (Female)': 'cs-CZ-VlastaNeural', 'Welsh (UK)-Aled- (Male)': 'cy-GB-AledNeural', 'Welsh (UK)-Nia- (Female)': 'cy-GB-NiaNeural', 'Danish (Denmark)-Christel- (Female)': 'da-DK-ChristelNeural', 'Danish (Denmark)-Jeppe- (Male)': 'da-DK-JeppeNeural', 'German (Austria)-Ingrid- (Female)': 'de-AT-IngridNeural', 'German (Austria)-Jonas- (Male)': 'de-AT-JonasNeural', 'German (Switzerland)-Jan- (Male)': 'de-CH-JanNeural', 'German (Switzerland)-Leni- (Female)': 'de-CH-LeniNeural', 'English (Australia)-Natasha- (Female)': 'en-AU-NatashaNeural', 'English (Australia)-William- (Male)': 'en-AU-WilliamNeural', 'English (Canada)-Clara- (Female)': 'en-CA-ClaraNeural', 'English (Canada)-Liam- (Male)': 'en-CA-LiamNeural', 'English (UK)-Libby- (Female)': 'en-GB-LibbyNeural', 'English (UK)-Maisie- (Female)': 'en-GB-MaisieNeural', 'English (UK)-Ryan- (Male)': 'en-GB-RyanNeural', 'English (UK)-Sonia- (Female)': 'en-GB-SoniaNeural', 'English (UK)-Thomas- (Male)': 'en-GB-ThomasNeural', 'English (Hong Kong)-Sam- (Male)': 'en-HK-SamNeural', 'English (Hong Kong)-Yan- (Female)': 'en-HK-YanNeural', 'English (Ireland)-Connor- (Male)': 'en-IE-ConnorNeural', 'English (Ireland)-Emily- (Female)': 'en-IE-EmilyNeural', 'English (India)-Neerja- (Female)': 'en-IN-NeerjaNeural', 'English (India)-Prabhat- (Male)': 'en-IN-PrabhatNeural', 'English (Kenya)-Asilia- (Female)': 'en-KE-AsiliaNeural', 'English (Kenya)-Chilemba- (Male)': 'en-KE-ChilembaNeural', 'English (Nigeria)-Abeo- (Male)': 'en-NG-AbeoNeural', 'English (Nigeria)-Ezinne- (Female)': 'en-NG-EzinneNeural', 'English (New Zealand)-Mitchell- (Male)': 'en-NZ-MitchellNeural', 'English (Philippines)-James- (Male)': 'en-PH-JamesNeural', 'English (Philippines)-Rosa- (Female)': 'en-PH-RosaNeural', 'English (Singapore)-Luna- (Female)': 'en-SG-LunaNeural', 'English (Singapore)-Wayne- (Male)': 'en-SG-WayneNeural', 'English (Tanzania)-Elimu- (Male)': 'en-TZ-ElimuNeural', 'English (Tanzania)-Imani- (Female)': 'en-TZ-ImaniNeural', 'English (South Africa)-Leah- (Female)': 'en-ZA-LeahNeural', 'English (South Africa)-Luke- (Male)': 'en-ZA-LukeNeural', 'Spanish (Argentina)-Elena- (Female)': 'es-AR-ElenaNeural', 'Spanish (Argentina)-Tomas- (Male)': 'es-AR-TomasNeural', 'Spanish (Bolivia)-Marcelo- (Male)': 'es-BO-MarceloNeural', 'Spanish (Bolivia)-Sofia- (Female)': 'es-BO-SofiaNeural', 'Spanish (Colombia)-Gonzalo- (Male)': 'es-CO-GonzaloNeural', 'Spanish (Colombia)-Salome- (Female)': 'es-CO-SalomeNeural', 'Spanish (Costa Rica)-Juan- (Male)': 'es-CR-JuanNeural', 'Spanish (Costa Rica)-Maria- (Female)': 'es-CR-MariaNeural', 'Spanish (Cuba)-Belkys- (Female)': 'es-CU-BelkysNeural', 'Spanish (Dominican Republic)-Emilio- (Male)': 'es-DO-EmilioNeural', 'Spanish (Dominican Republic)-Ramona- (Female)': 'es-DO-RamonaNeural', 'Spanish (Ecuador)-Andrea- (Female)': 'es-EC-AndreaNeural', 'Spanish (Ecuador)-Luis- (Male)': 'es-EC-LuisNeural', 'Spanish (Spain)-Alvaro- (Male)': 'es-ES-AlvaroNeural', 'Spanish (Spain)-Elvira- (Female)': 'es-ES-ElviraNeural', 'Spanish (Equatorial Guinea)-Teresa- (Female)': 'es-GQ-TeresaNeural', 'Spanish (Guatemala)-Andres- (Male)': 'es-GT-AndresNeural', 'Spanish (Guatemala)-Marta- (Female)': 'es-GT-MartaNeural', 'Spanish (Honduras)-Carlos- (Male)': 'es-HN-CarlosNeural', 'Spanish (Honduras)-Karla- (Female)': 'es-HN-KarlaNeural', 'Spanish (Nicaragua)-Federico- (Male)': 'es-NI-FedericoNeural', 'Spanish (Nicaragua)-Yolanda- (Female)': 'es-NI-YolandaNeural', 'Spanish (Panama)-Margarita- (Female)': 'es-PA-MargaritaNeural', 'Spanish (Panama)-Roberto- (Male)': 'es-PA-RobertoNeural', 'Spanish (Peru)-Alex- (Male)': 'es-PE-AlexNeural', 'Spanish (Peru)-Camila- (Female)': 'es-PE-CamilaNeural', 'Spanish (Puerto Rico)-Karina- (Female)': 'es-PR-KarinaNeural', 'Spanish (Puerto Rico)-Victor- (Male)': 'es-PR-VictorNeural', 'Spanish (Paraguay)-Mario- (Male)': 'es-PY-MarioNeural', 'Spanish (Paraguay)-Tania- (Female)': 'es-PY-TaniaNeural', 'Spanish (El Salvador)-Lorena- (Female)': 'es-SV-LorenaNeural', 'Spanish (El Salvador)-Rodrigo- (Male)': 'es-SV-RodrigoNeural', 'Spanish (United States)-Alonso- (Male)': 'es-US-AlonsoNeural', 'Spanish (United States)-Paloma- (Female)': 'es-US-PalomaNeural', 'Spanish (Uruguay)-Mateo- (Male)': 'es-UY-MateoNeural', 'Spanish (Uruguay)-Valentina- (Female)': 'es-UY-ValentinaNeural', 'Spanish (Venezuela)-Paola- (Female)': 'es-VE-PaolaNeural', 'Spanish (Venezuela)-Sebastian- (Male)': 'es-VE-SebastianNeural', 'Estonian (Estonia)-Anu- (Female)': 'et-EE-AnuNeural', 'Estonian (Estonia)-Kert- (Male)': 'et-EE-KertNeural', 'Persian (Iran)-Dilara- (Female)': 'fa-IR-DilaraNeural', 'Persian (Iran)-Farid- (Male)': 'fa-IR-FaridNeural', 'Finnish (Finland)-Harri- (Male)': 'fi-FI-HarriNeural', 'Finnish (Finland)-Noora- (Female)': 'fi-FI-NooraNeural', 'French (Belgium)-Charline- (Female)': 'fr-BE-CharlineNeural', 'French (Belgium)-Gerard- (Male)': 'fr-BE-GerardNeural', 'French (Canada)-Sylvie- (Female)': 'fr-CA-SylvieNeural', 'French (Canada)-Antoine- (Male)': 'fr-CA-AntoineNeural', 'French (Canada)-Jean- (Male)': 'fr-CA-JeanNeural', 'French (Switzerland)-Ariane- (Female)': 'fr-CH-ArianeNeural', 'French (Switzerland)-Fabrice- (Male)': 'fr-CH-FabriceNeural', 'Irish (Ireland)-Colm- (Male)': 'ga-IE-ColmNeural', 'Irish (Ireland)-Orla- (Female)': 'ga-IE-OrlaNeural', 'Galician (Spain)-Roi- (Male)': 'gl-ES-RoiNeural', 'Galician (Spain)-Sabela- (Female)': 'gl-ES-SabelaNeural', 'Gujarati (India)-Dhwani- (Female)': 'gu-IN-DhwaniNeural', 'Gujarati (India)-Niranjan- (Male)': 'gu-IN-NiranjanNeural', 'Hindi (India)-Madhur- (Male)': 'hi-IN-MadhurNeural', 'Hindi (India)-Swara- (Female)': 'hi-IN-SwaraNeural', 'Croatian (Croatia)-Gabrijela- (Female)': 'hr-HR-GabrijelaNeural', 'Croatian (Croatia)-Srecko- (Male)': 'hr-HR-SreckoNeural', 'Hungarian (Hungary)-Noemi- (Female)': 'hu-HU-NoemiNeural', 'Hungarian (Hungary)-Tamas- (Male)': 'hu-HU-TamasNeural', 'Icelandic (Iceland)-Gudrun- (Female)': 'is-IS-GudrunNeural', 'Icelandic (Iceland)-Gunnar- (Male)': 'is-IS-GunnarNeural', 'Javanese (Indonesia)-Dimas- (Male)': 'jv-ID-DimasNeural', 'Javanese (Indonesia)-Siti- (Female)': 'jv-ID-SitiNeural', 'Georgian (Georgia)-Eka- (Female)': 'ka-GE-EkaNeural', 'Georgian (Georgia)-Giorgi- (Male)': 'ka-GE-GiorgiNeural', 'Kazakh (Kazakhstan)-Aigul- (Female)': 'kk-KZ-AigulNeural', 'Kazakh (Kazakhstan)-Daulet- (Male)': 'kk-KZ-DauletNeural', 'Khmer (Cambodia)-Piseth- (Male)': 'km-KH-PisethNeural', 'Khmer (Cambodia)-Sreymom- (Female)': 'km-KH-SreymomNeural', 'Kannada (India)-Gagan- (Male)': 'kn-IN-GaganNeural', 'Kannada (India)-Sapna- (Female)': 'kn-IN-SapnaNeural', 'Lao (Laos)-Chanthavong- (Male)': 'lo-LA-ChanthavongNeural', 'Lao (Laos)-Keomany- (Female)': 'lo-LA-KeomanyNeural', 'Lithuanian (Lithuania)-Leonas- (Male)': 'lt-LT-LeonasNeural', 'Lithuanian (Lithuania)-Ona- (Female)': 'lt-LT-OnaNeural', 'Latvian (Latvia)-Everita- (Female)': 'lv-LV-EveritaNeural', 'Latvian (Latvia)-Nils- (Male)': 'lv-LV-NilsNeural', 'Macedonian (North Macedonia)-Aleksandar- (Male)': 'mk-MK-AleksandarNeural', 'Macedonian (North Macedonia)-Marija- (Female)': 'mk-MK-MarijaNeural', 'Malayalam (India)-Midhun- (Male)': 'ml-IN-MidhunNeural', 'Malayalam (India)-Sobhana- (Female)': 'ml-IN-SobhanaNeural', 'Mongolian (Mongolia)-Bataa- (Male)': 'mn-MN-BataaNeural', 'Mongolian (Mongolia)-Yesui- (Female)': 'mn-MN-YesuiNeural', 'Marathi (India)-Aarohi- (Female)': 'mr-IN-AarohiNeural', 'Marathi (India)-Manohar- (Male)': 'mr-IN-ManoharNeural', 'Maltese (Malta)-Grace- (Female)': 'mt-MT-GraceNeural', 'Maltese (Malta)-Joseph- (Male)': 'mt-MT-JosephNeural', 'Burmese (Myanmar)-Nilar- (Female)': 'my-MM-NilarNeural', 'Burmese (Myanmar)-Thiha- (Male)': 'my-MM-ThihaNeural', 'Nepali (Nepal)-Hemkala- (Female)': 'ne-NP-HemkalaNeural', 'Nepali (Nepal)-Sagar- (Male)': 'ne-NP-SagarNeural', 'Dutch (Belgium)-Arnaud- (Male)': 'nl-BE-ArnaudNeural', 'Dutch (Belgium)-Dena- (Female)': 'nl-BE-DenaNeural', 'Polish (Poland)-Marek- (Male)': 'pl-PL-MarekNeural', 'Polish (Poland)-Zofia- (Female)': 'pl-PL-ZofiaNeural', 'Pashto (Afghanistan)-Gul Nawaz- (Male)': 'ps-AF-Gul', } # Global variables to store audio segments and timing data audio_segments = [] timing_data = [] def split_text_into_segments(text, max_words=10): """Split text into segments for better timing control""" words = text.split() segments = [] current_segment = [] for word in words: current_segment.append(word) if len(current_segment) >= max_words or word.endswith(('.', '!', '?', ';')): segments.append(' '.join(current_segment)) current_segment = [] if current_segment: segments.append(' '.join(current_segment)) return segments async def generate_timed_audio_segments(text, voice): """Generate audio segments with timing information""" global audio_segments, timing_data segments = split_text_into_segments(text) audio_segments = [] timing_data = [] current_time = 0 for i, segment in enumerate(segments): # Generate audio for this segment communicate = edge_tts.Communicate(segment, voice) with tempfile.NamedTemporaryFile(delete=False, suffix=f"_segment_{i}.wav") as tmp_file: tmp_path = tmp_file.name await communicate.save(tmp_path) # Estimate duration (rough estimation - you could use audio analysis for accuracy) word_count = len(segment.split()) estimated_duration = word_count * 0.6 # Roughly 0.6 seconds per word audio_segments.append(tmp_path) timing_data.append({ 'segment': segment, 'start_time': current_time, 'duration': estimated_duration, 'audio_path': tmp_path, 'segment_index': i }) current_time += estimated_duration return segments, timing_data def create_interactive_html(text, timing_data): """Create HTML with clickable text segments""" html_content = """
No text provided.
" voice = language_dict.get(language_code, "en-US-JennyNeural") try: # Generate the full audio file communicate = edge_tts.Communicate(text, voice) with tempfile.NamedTemporaryFile(delete=False, suffix=".wav") as tmp_file: full_audio_path = tmp_file.name await communicate.save(full_audio_path) # Generate timed segments for interactive transcript segments, timing_data = await generate_timed_audio_segments(text, voice) # Create interactive HTML interactive_html = create_interactive_html(text, timing_data) success_message = f"Speech synthesis completed! Generated {len(segments)} interactive segments." return success_message, full_audio_path, interactive_html except Exception as e: return f"Error: {str(e)}", None, f"Error generating transcript: {str(e)}
" async def play_from_position(segment_index): """Function to handle playing from a specific segment""" global audio_segments, timing_data if 0 <= segment_index < len(audio_segments): audio_path = audio_segments[segment_index] return audio_path return None def create_enhanced_interface(): """Create the enhanced Gradio interface""" # Input components input_text = gr.Textbox( lines=5, label="Input Text", placeholder="Enter the text you want to convert to speech..." ) language = gr.Dropdown( choices=list(language_dict.keys()), label="Choose the Voice Model", value="English-Jenny (Female)" ) # Output components output_text = gr.Textbox(label="Status") output_audio = gr.Audio(type="filepath", label="Generated Audio") interactive_transcript = gr.HTML(label="Interactive Transcript") # Create the interface interface = gr.Interface( fn=text_to_speech_with_interactive_transcript, inputs=[input_text, language], outputs=[output_text, output_audio, interactive_transcript], title="Enhanced Edge TTS with Interactive Transcription", description=""" Microsoft Edge Text-To-Speech with Interactive Features **Features:** - 🎯 **Click to Play**: Click on any part of the transcript to start playback from that position - 🎨 **Visual Feedback**: Text segments highlight during interaction - 🌍 **Multi-language Support**: Support for 100+ voices in multiple languages - ⚡ **Real-time Processing**: Fast speech synthesis with segment-based timing **How to use:** 1. Enter your text in the input box 2. Select your preferred voice from the dropdown 3. Click "Submit" to generate audio and interactive transcript 4. Click on any part of the transcript text to play from that position *Enhanced version of [Nick088](https://linktr.ee/Nick088) Forked & Fixed [Ilaria TTS](https://huggingface.co/spaces/TheStinger/Ilaria_TTS)* """, theme=gr.themes.Soft(), css=""" .gradio-container { font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; } .text-segment:hover { background-color: #e3f2fd !important; transform: translateY(-1px); box-shadow: 0 2px 4px rgba(0,0,0,0.1); } .text-segment.active { background-color: #ffeb3b !important; font-weight: bold !important; } .text-segment.playing { background-color: #c8e6c9 !important; animation: pulse 1.5s infinite; } @keyframes pulse { 0% { opacity: 1; } 50% { opacity: 0.7; } 100% { opacity: 1; } } #transcript { user-select: none; max-height: 400px; overflow-y: auto; } #currentSegment { background: #f0f0f0; padding: 8px; border-radius: 4px; margin-top: 8px; } """, examples=[ [ "Hello! Welcome to the enhanced Edge TTS application. This demo shows how you can click on different parts of this text to start playback from any position. Try clicking on various words and sentences to see the interactive transcription in action.", "English-Jenny (Female)" ], [ "The quick brown fox jumps over the lazy dog. This sentence contains every letter of the alphabet and is commonly used for testing purposes. Click anywhere in this text to start playback from that position.", "English-Guy (Male)" ], [ "Bonjour! Ceci est un exemple en français. Vous pouvez cliquer sur n'importe quelle partie de ce texte pour démarrer la lecture à partir de cette position. C'est vraiment pratique pour l'apprentissage des langues!", "French-Denise- (Female)" ], [ "¡Hola! Este es un ejemplo en español. Puedes hacer clic en cualquier parte de este texto para comenzar la reproducción desde esa posición. Es muy útil para el aprendizaje de idiomas.", "Spanish (Mexican)-Dalia (Female)" ] ] ) return interface # Additional utility functions for better segment management def merge_audio_segments(segment_paths, start_index=0): """Merge audio segments starting from a specific index""" # This would require audio processing libraries like pydub # For now, return the path of the starting segment if start_index < len(segment_paths): return segment_paths[start_index] return None def estimate_reading_time(text): """Estimate reading time for text""" words = len(text.split()) # Average reading speed: 150-200 words per minute # For TTS, usually faster: ~180-220 words per minute reading_speed_wpm = 200 time_minutes = words / reading_speed_wpm time_seconds = time_minutes * 60 return time_seconds def clean_text_for_tts(text): """Clean and prepare text for TTS processing""" # Remove excessive whitespace text = re.sub(r'\s+', ' ', text.strip()) # Handle abbreviations and special characters replacements = { '&': 'and', '@': 'at', '#': 'number', '%': 'percent', '+': 'plus', '=': 'equals' } for old, new in replacements.items(): text = text.replace(old, new) return text def get_voice_info(voice_key): """Get detailed information about a voice""" if voice_key in language_dict: voice_code = language_dict[voice_key] parts = voice_key.split('-') language = parts[0] if parts else "Unknown" name_gender = parts[1] if len(parts) > 1 else "Unknown" return { 'language': language, 'name_gender': name_gender, 'voice_code': voice_code, 'display_name': voice_key } return None # Main execution if __name__ == "__main__": print("Starting Enhanced Edge TTS Application...") print("Features:") print("- Interactive transcript with click-to-play functionality") print("- Multi-language support with 100+ voices") print("- Real-time segment highlighting") print("- Enhanced user interface") interface = create_enhanced_interface() anyio.run(interface.launch, backend="asyncio")