import gradio as gr import json import os from pathlib import Path import uuid import fcntl import time from vertex_client import get_vertex_client # gr.NO_RELOAD = False # Counter persistence file COUNTER_FILE = Path("generation_counter.json") # Example texts EXAMPLE_TEXT_ENGLISH = "Welcome to Ringg TTS! This is a text to speech system that can convert your text into natural-sounding audio. Try it out with your own content!" EXAMPLE_TEXT_HINDI = "नमस्ते! मैं रिंग टीटीएस हूँ। मैं आपके टेक्स्ट को प्राकृतिक आवाज़ में बदल सकता हूँ। कृपया अपना टेक्स्ट यहाँ लिखें और सुनें।" EXAMPLE_TEXT_MIXED = "Hello दोस्तों! Welcome to Ringg TTS. यह एक बहुत ही शानदार text to speech system है जो Hindi और English दोनों languages को support करता है।" def load_counter(): """Load universal generation counter from file (thread-safe)""" try: if COUNTER_FILE.exists(): with open(COUNTER_FILE, "r") as f: # Try to acquire shared lock for reading try: fcntl.flock(f.fileno(), fcntl.LOCK_SH) data = json.load(f) fcntl.flock(f.fileno(), fcntl.LOCK_UN) return data.get("count", 0) except Exception: # If locking fails, just read without lock f.seek(0) data = json.load(f) return data.get("count", 0) except Exception as e: print(f"Error loading counter: {e}") return 0 def save_counter(count): """Save universal generation counter to file (thread-safe)""" try: # Use file locking to prevent race conditions with multiple users with open(COUNTER_FILE, "w") as f: try: fcntl.flock(f.fileno(), fcntl.LOCK_EX) json.dump({"count": count, "last_updated": time.time()}, f) f.flush() os.fsync(f.fileno()) fcntl.flock(f.fileno(), fcntl.LOCK_UN) except Exception: # If locking fails, just write without lock json.dump({"count": count, "last_updated": time.time()}, f) f.flush() except Exception as e: print(f"Error saving counter: {e}") def increment_counter(): """Atomically increment and return the new counter value""" try: # Read current value, increment, and save atomically with open(COUNTER_FILE, "r+" if COUNTER_FILE.exists() else "w+") as f: try: fcntl.flock(f.fileno(), fcntl.LOCK_EX) # Read current count f.seek(0) try: data = json.load(f) current_count = data.get("count", 0) except Exception: current_count = 0 # Increment new_count = current_count + 1 # Write back f.seek(0) f.truncate() json.dump({"count": new_count, "last_updated": time.time()}, f) f.flush() os.fsync(f.fileno()) fcntl.flock(f.fileno(), fcntl.LOCK_UN) return new_count except Exception: # Fallback without locking f.seek(0) try: data = json.load(f) current_count = data.get("count", 0) except Exception: current_count = 0 new_count = current_count + 1 f.seek(0) f.truncate() json.dump({"count": new_count, "last_updated": time.time()}, f) f.flush() return new_count except Exception as e: print(f"Error incrementing counter: {e}") return 0 def get_voices(): """Fetch available voices from Vertex AI""" try: vertex_client = get_vertex_client() success, voices_response = vertex_client.get_voices() if success and voices_response: print("✅ Fetched voices from Vertex AI") voices_data = voices_response.get("voices", {}) # Create a list of tuples (display_name, voice_id) voices = [] for voice_id, voice_info in voices_data.items(): name = voice_info.get("name", "Unknown") gender = voice_info.get("gender", "N/A") display_name = f"{name} ({gender})" voices.append((display_name, voice_id)) return sorted(voices, key=lambda x: x[0]) else: print("❌ Failed to fetch voices from Vertex AI") return [] except Exception as e: print(f"❌ Error fetching voices from Vertex AI: {e}") return [] def synthesize_speech(text, voice_id): """Synthesize speech from text using Vertex AI""" if not text or not text.strip(): return None, "⚠️ Please enter some text", "", "", "", "", "", "" if not voice_id: return None, "⚠️ Please select a voice", "", "", "", "", "", "" # Print input text length text_length = len(text) print(f"Input text length: {text_length} characters") try: vertex_client = get_vertex_client() success, audio_bytes, metrics = vertex_client.synthesize( text, voice_id, timeout=60 ) if success and audio_bytes: print("✅ Synthesized audio using Vertex AI") # Save binary audio to temp file audio_file = f"/tmp/ringg_{str(uuid.uuid4())}.wav" with open(audio_file, "wb") as f: f.write(audio_bytes) # Format metrics if available if metrics: total_time = f"{metrics.get('t', 0):.3f}s" rtf = f"{metrics.get('rtf', 0):.4f}" wav_duration = f"{metrics.get('wav_seconds', 0):.2f}s" vocoder_time = f"{metrics.get('t_vocoder', 0):.3f}s" no_vocoder_time = f"{metrics.get('t_no_vocoder', 0):.3f}s" rtf_no_vocoder = f"{metrics.get('rtf_no_vocoder', 0):.4f}" else: total_time = rtf = wav_duration = vocoder_time = no_vocoder_time = ( rtf_no_vocoder ) = "" status_msg = "✅ Audio generated successfully!" return ( audio_file, status_msg, total_time, rtf, wav_duration, vocoder_time, no_vocoder_time, rtf_no_vocoder, ) else: return None, "❌ Failed to generate audio", "", "", "", "", "", "" except Exception as e: print(f"❌ Vertex AI synthesis failed: {e}") return None, f"❌ Error: {str(e)}", "", "", "", "", "", "" # Load initial counter value initial_counter = load_counter() # Create Gradio interface with gr.Blocks( theme=gr.themes.Base( font=[gr.themes.GoogleFont("Source Sans Pro"), "Arial", "sans-serif"] ), css=".gradio-container {max-width: none !important;}", ) as demo: # Title with Health Status with gr.Row(): with gr.Column(scale=4): audio_image = gr.HTML( value="""