| import os |
| import mimetypes |
| import struct |
| import tempfile |
| import gradio as gr |
| from dotenv import load_dotenv |
| from google import genai |
| from google.genai import types |
|
|
| load_dotenv() |
| api_key=os.getenv("GEMINI_API_KEY") |
| if not api_key: |
| raise RuntimeError("Set GEMINI_API_KEY in Hugging Face Secrets.") |
|
|
| client=genai.Client(api_key=api_key) |
|
|
| VOICE_GENDER={"Charon":"Male","Fenrir":"Male","Kore":"Female","Aoede":"Female","Leda":"Female"} |
| CONTEXT_MATRIX={ |
| "Funny":{"Scene":"Stand-up comedy club or a lively local coffee shop discussion","Context":"{gender} voice, highly expressive, comedic timing, laughing naturally inside sentences, playful tone"}, |
| "Serious":{"Scene":"Formal presentation room","Context":"{gender} voice, authoritative, professional tone"}, |
| "Calm":{"Scene":"Meditation sanctuary","Context":"{gender} voice, calm and soothing"}, |
| "Excited":{"Scene":"Sports stadium","Context":"{gender} voice, energetic and enthusiastic"}, |
| } |
|
|
| def wav(raw): |
| bps=16;sr=24000;ch=1 |
| ds=len(raw);ba=ch*(bps//8);br=sr*ba |
| head=struct.pack("<4sI4s4sIHHIIHH4sI",b"RIFF",36+ds,b"WAVE",b"fmt ",16,1,ch,sr,br,ba,bps,b"data",ds) |
| return head+raw |
|
|
| def matrix(v,e,p,a): |
| g=VOICE_GENDER.get(v,"Neutral") |
| m=CONTEXT_MATRIX[e] |
| s=m["Scene"] |
| c=m["Context"].format(gender=g) |
| if p=="Fast": c+=", fast pace" |
| elif p=="Slow": c+=", slow pace" |
| if a!="Neutral": |
| c+=f", {a} accent" |
| return s,c |
|
|
| def generate(transcript,voice,emotion,pace,accent,temp): |
| scene,context=matrix(voice,emotion,pace,accent) |
| prompt=f"""Vocal Environment:{scene} |
| Primary Acting Style:{emotion} |
| Speed:{pace} |
| Accent:{accent} |
| Guide:{context} |
| |
| Transcript: |
| {transcript}""" |
| cfg=types.GenerateContentConfig( |
| temperature=float(temp), |
| response_modalities=["audio"], |
| speech_config=types.SpeechConfig( |
| voice_config=types.VoiceConfig( |
| prebuilt_voice_config=types.PrebuiltVoiceConfig(voice_name=voice) |
| ) |
| ) |
| ) |
| buf=bytearray();native=False |
| for chunk in client.models.generate_content_stream( |
| model="gemini-2.5-flash-preview-tts", |
| contents=[types.Content(role="user",parts=[types.Part.from_text(text=prompt)])], |
| config=cfg): |
| if chunk.parts and chunk.parts[0].inline_data: |
| d=chunk.parts[0].inline_data |
| if d.data: |
| buf.extend(d.data) |
| ext=mimetypes.guess_extension(d.mime_type or "") |
| if ext in [".wav",".mp3"]: |
| native=True |
| audio=bytes(buf) if native else wav(bytes(buf)) |
| f=tempfile.NamedTemporaryFile(delete=False,suffix=".wav") |
| f.write(audio);f.close() |
| return scene,context,f.name |
|
|
| with gr.Blocks(title="Gemini AI TTS Studio Pro") as demo: |
| gr.Markdown("# Gemini AI TTS Studio Pro") |
| txt=gr.Textbox(lines=8,label="Transcript") |
| with gr.Row(): |
| voice=gr.Dropdown(list(VOICE_GENDER.keys()),value="Aoede",label="Voice") |
| emotion=gr.Dropdown(list(CONTEXT_MATRIX.keys()),value="Funny",label="Emotion") |
| with gr.Row(): |
| pace=gr.Dropdown(["Slow","Normal","Fast"],value="Normal",label="Pace") |
| accent=gr.Dropdown(["Neutral","American","British","Australian","Jawa","Sunda"],value="Neutral",label="Accent") |
| temp=gr.Slider(0,2,value=1,label="Temperature") |
| btn=gr.Button("Generate TTS") |
| scene=gr.Textbox(label="Scene") |
| context=gr.Textbox(label="Context") |
| audio=gr.Audio(label="Audio",type="filepath") |
| btn.click(generate,[txt,voice,emotion,pace,accent,temp],[scene,context,audio]) |
|
|
| if __name__=="__main__": |
| demo.launch() |
|
|