from __future__ import annotations import tempfile from pathlib import Path from typing import Tuple import gradio as gr import modal TITLE = "EraEcho" DESCRIPTION = "Transpose modern music into the style of any historical decade." DECADES = [f"{year}s" for year in range(1920, 2030, 10)] def transform(audio_path: str | None, decade: str) -> Tuple[str | None, str]: if not audio_path: return None, "Please upload an audio file first." try: fn = modal.Function.from_name("eraecho", "transform_with_era_ai") with open(audio_path, "rb") as file: audio_bytes = file.read() transformed_bytes, report = fn.remote(audio_bytes, Path(audio_path).name, decade) output = tempfile.NamedTemporaryFile(delete=False, suffix=f"_{decade}_eraecho.wav") output.write(transformed_bytes) output.close() return output.name, report except Exception as exc: return None, f"Error calling Modal backend: {exc}" with gr.Blocks(theme=gr.themes.Soft(), title=TITLE) as demo: gr.Markdown(f"# {TITLE}\n{DESCRIPTION}") gr.Markdown( "Upload a song, choose a decade, and EraEcho returns a historically flavored audio pass plus a fidelity report. " "Processing is handled by Modal (Qwen3-14B + DSP)." ) with gr.Row(): with gr.Column(): audio = gr.Audio(label="Upload music", type="filepath") decade = gr.Dropdown(DECADES, value="1950s", label="Target decade") button = gr.Button("Transpose through time", variant="primary") with gr.Column(): output_audio = gr.Audio(label="EraEcho transformed audio", type="filepath") report = gr.Markdown(label="Historical fidelity report") button.click(transform, inputs=[audio, decade], outputs=[output_audio, report]) if __name__ == "__main__": demo.launch()