"""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'
' "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( 'Tucking you inβ¦ weaving tonight\'s story
' 'Writing the story, then narrating it in your voice β ' "this can take a little while.
" "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)