"""DreamVoice β€” Gradio 6 entry-point for the bedtime-story voice-cloning app. HuggingFace ZeroGPU compatible: all GPU functions decorated with @spaces.GPU. Models are loaded lazily at module level (outside @spaces.GPU) for CUDA emulation. """ from __future__ import annotations import os import spaces import gradio as gr from audio_utils import prepare_reference from story import generate_story, VALID_GENRES, VALID_MOODS from tts import clone_and_speak from indic_text import translate_to_kannada from indic_tts import narrate_kannada from ui_html import starfield_background, storybook_html, storybook_markup GENRE_CHOICES = [ ("🦁 Animals", "animals"), ("πŸ‘‘ Kingdom", "kingdom"), ("πŸš€ Space", "space"), ("πŸ‰ Dragons", "dragons"), ("🌊 Ocean", "ocean"), ("🌲 Forest", "forest"), ] MOOD_CHOICES = [ ("✨ Magical", "magical"), ("πŸ˜„ Funny", "funny"), ("😴 Calming", "calming"), ("πŸŒ™ Dreamy", "dreamy"), ] LANGUAGE_MAP = {"English": "en", "ಕನ್ನ಑ Kannada": "kn"} REF_SENTENCE = "Once upon a time, in a cozy little house, a gentle voice began to tell a bedtime story." def _stage_css() -> str: return """ """ @spaces.GPU(duration=120) def _tell_story(audio_path, genre, mood, hero_name, language_label, length, energy): """Full pipeline: prepare ref -> story -> clone & speak. Runs on ZeroGPU.""" errors = [] if not audio_path: errors.append("Please record or upload a voice clip first.") if genre not in VALID_GENRES: errors.append("Please choose a genre.") if mood not in VALID_MOODS: errors.append("Please choose a mood.") if errors: raise ValueError(" ".join(errors)) lang_code = LANGUAGE_MAP.get(language_label, "en") energy_f = max(0.0, min(1.0, float(energy) / 100.0)) ref_wav = None try: ref_wav = prepare_reference(audio_path) english_story = generate_story( genre, mood, hero_name.strip() if hero_name else "", language="en", length=length, energy=energy_f, ) if lang_code == "kn": kannada_story = translate_to_kannada(english_story) narration_path = narrate_kannada(ref_wav, REF_SENTENCE, kannada_story, mood=mood, energy=energy_f) display = storybook_markup(kannada_story, "ಇಂದಿನ ಕΰ²₯ೆ Β· Tonight's Story") else: narration_path = clone_and_speak(ref_wav, english_story, mood=mood, energy=energy_f) display = storybook_markup(english_story, "Tonight's Story") return display, narration_path finally: if ref_wav and os.path.exists(ref_wav): try: os.unlink(ref_wav) except OSError: pass def _run_story(audio_path, genre, mood, hero_name, language_label, length, energy): """UI generator: loading scene -> pipeline -> reveal/error.""" yield ( gr.update(visible=False), gr.update(visible=True), gr.update(visible=False), gr.update(), gr.update(), gr.update(visible=False, value=""), ) try: display, audio = _tell_story(audio_path, genre, mood, hero_name, language_label, length, energy) except Exception as exc: yield ( gr.update(visible=True), gr.update(visible=False), gr.update(visible=False), gr.update(), gr.update(), gr.update(visible=True, value=f'
😴 {exc}
'), ) return yield ( gr.update(visible=False), gr.update(visible=False), gr.update(visible=True), display, audio, gr.update(visible=False, value=""), ) def _build_app() -> gr.Blocks: with gr.Blocks(title="DreamVoice β€” Bedtime Stories in Mom's Voice") as demo: starfield_background() with gr.Column(visible=True) as stage1: gr.HTML('

πŸŽ™οΈ Record Mom\'s Voice

') gr.HTML( '
' 'Read this aloud, slowly & clearly:' f'“{REF_SENTENCE}”' "
" ) audio_input = gr.Audio( sources=["microphone", "upload"], type="filepath", label="Record or upload a 5–60 s voice clip", ) gr.HTML( '

' "Find a quiet room. Reading the sentence above lets us narrate Kannada in this " "voice. Your recording is processed for this session only and never saved." "

" ) gr.HTML('

Choose a genre

') genre_radio = gr.Radio( choices=GENRE_CHOICES, value="animals", label="", elem_classes=["dv-radio-group", "dv-tiles"], ) gr.HTML('

Pick a mood

') mood_radio = gr.Radio( choices=MOOD_CHOICES, value="magical", label="", elem_classes=["dv-radio-group", "dv-tiles"], ) hero_name_input = gr.Textbox( label="Hero's name (optional)", placeholder="e.g. Luna", max_lines=1, ) gr.HTML('

Story language

') language_radio = gr.Radio( choices=["English", "ಕನ್ನ಑ Kannada"], value="English", label="", elem_classes=["dv-radio-group"], ) gr.HTML('

Story length

') length_radio = gr.Radio( choices=["1 min", "2 min", "3 min"], value="2 min", label="", elem_classes=["dv-radio-group"], ) gr.HTML('

Delivery β€” 😴 calm ↔ energetic πŸŽ‰

') energy_slider = gr.Slider( minimum=0, maximum=100, value=45, step=5, label="", show_label=False, elem_classes=["dv-slider"], ) error_box = gr.HTML(visible=False) tell_btn = gr.Button("Tell Me a Story ✨", elem_classes=["dv-btn-accent"]) with gr.Column(visible=False) as loading_panel: gr.HTML( '
' '
πŸŒ™
' '
zzz
' '

Tucking you in… weaving tonight\'s story

' '

Writing the story, then narrating it in your voice β€” ' "this can take a little while.

" "
" ) with gr.Column(visible=False) as stage2: story_display = storybook_html("", "Tonight's Story") audio_player = gr.Audio( label="πŸŒ™ Listen to your story", type="filepath", interactive=False, autoplay=True, elem_classes=["dv-player"], ) gr.HTML( '" '

Told in your loved one\'s voice, just for you ✨

' ) again_btn = gr.Button("Create Another Story πŸ”„", elem_classes=["dv-btn-accent"]) tell_btn.click( fn=_run_story, inputs=[audio_input, genre_radio, mood_radio, hero_name_input, language_radio, length_radio, energy_slider], outputs=[stage1, loading_panel, stage2, story_display, audio_player, error_box], api_name="tell_story", ) again_btn.click( fn=lambda: ( gr.update(visible=True), gr.update(visible=False), gr.update(visible=False), gr.update(visible=False, value=""), ), inputs=[], outputs=[stage1, loading_panel, stage2, error_box], ) gr.HTML( '' ) return demo if __name__ == "__main__": demo = _build_app() demo.queue().launch(css=_stage_css(), server_port=7870)