Spaces:
Sleeping
Sleeping
| # ================================================================================================== | |
| # DEEPFAKE AUDIO - app_ui_demo.py (The UI Sandbox) | |
| # ================================================================================================== | |
| # | |
| # π DESCRIPTION | |
| # This script serves as a lightweight, "UI-Only" verification environment for the Deepfake | |
| # Audio Studio. It is designed to run without the heavy neural dependencies (TensorFlow, PyTorch) | |
| # used in the production pipeline, allowing designers and developers to iterate on the | |
| # Human-Machine Interface (HMI) aesthetics without loading massive model weights. | |
| # | |
| # π€ AUTHORS | |
| # - Amey Thakur (https://github.com/Amey-Thakur) | |
| # - Mega Satish (https://github.com/msatmod) | |
| # | |
| # π€π» CREDITS | |
| # Original Real-Time Voice Cloning methodology by CorentinJ | |
| # Repository: https://github.com/CorentinJ/Real-Time-Voice-Cloning | |
| # | |
| # π PROJECT LINKS | |
| # Repository: https://github.com/Amey-Thakur/DEEPFAKE-AUDIO | |
| # Video Demo: https://youtu.be/i3wnBcbHDbs | |
| # Research: https://github.com/Amey-Thakur/DEEPFAKE-AUDIO/blob/main/DEEPFAKE-AUDIO.ipynb | |
| # | |
| # π LICENSE | |
| # Released under the MIT License | |
| # Release Date: 2021-02-06 | |
| # ================================================================================================== | |
| import os | |
| import sys | |
| from pathlib import Path | |
| # --- ENVIRONMENT CONFIGURATION --- | |
| # We suppress lower-level system warnings to ensure the focus remains on the HMI presentation. | |
| os.environ['TF_CPP_MIN_LOG_LEVEL'] = '3' | |
| os.environ['PYTHONWARNINGS'] = 'ignore' | |
| import warnings | |
| warnings.filterwarnings('ignore') | |
| import numpy as np | |
| import gradio as gr | |
| import base64 | |
| # --- DIRECTORY ARCHITECTURE --- | |
| PROJ_DIR = Path(__file__).parent.absolute() | |
| ROOT_DIR = PROJ_DIR.parent | |
| DATASET_DIR = ROOT_DIR / "Dataset" | |
| SAMPLES_DIR = DATASET_DIR / "samples" | |
| # --- Sample Loading (Dynamic Discovery) --- | |
| ROOT_DIR = PROJ_DIR.parent | |
| SAMPLES_DIR = ROOT_DIR / "Dataset/samples" | |
| if SAMPLES_DIR.exists(): | |
| for f in SAMPLES_DIR.glob("*.wav"): | |
| stem = f.stem | |
| if "_preset" in stem: | |
| name = stem.replace("_preset", "").replace("_", " ").title() | |
| else: | |
| name = stem | |
| SAMPLES[name] = str(f) | |
| def load_preset(name): | |
| if name in SAMPLES: | |
| return SAMPLES[name] | |
| return None | |
| # --- Mock Synthesis (UI Demo Only) --- | |
| def synthesize(audio_file, text, progress=gr.Progress()): | |
| if not audio_file or not text: | |
| return None, "Reference audio and text script are required." | |
| progress(0.2, desc="Extracting Voice Identity") | |
| progress(0.5, desc="Synthesizing Speech") | |
| progress(0.8, desc="Generating High-Fidelity Audio") | |
| progress(0.9, desc="Refining Audio Quality") | |
| progress(1.0, desc="Finalizing") | |
| return None, "β οΈ UI Demo Mode - TensorFlow blocked by system policy. Use Docker to run full synthesis." | |
| def play_intro(): | |
| intro_path = PROJ_DIR / "intro_message.wav" | |
| if intro_path.exists(): | |
| return str(intro_path) | |
| return None | |
| # --- NEON MIC ICON --- | |
| try: | |
| with open(PROJ_DIR / "favicon.png", "rb") as f: | |
| encoded_icon = base64.b64encode(f.read()).decode('utf-8') | |
| NEON_MIC_ICON = f"data:image/png;base64,{encoded_icon}" | |
| except: | |
| NEON_MIC_ICON = "/file=favicon.png" | |
| # --- Minimalist Navy & Orange UI --- | |
| custom_css = """ | |
| /* General Styles */ | |
| @import url('https://fonts.googleapis.com/css2?family=Play:wght@400;700&display=swap'); | |
| * { | |
| font-family: 'Play', sans-serif !important; | |
| -webkit-user-select: none; | |
| -moz-user-select: none; | |
| -ms-user-select: none; | |
| user-select: none; | |
| } | |
| /* Allow selection in inputs */ | |
| input, textarea, .gr-box, .gr-input, .gr-text-input { | |
| -webkit-user-select: text !important; | |
| -moz-user-select: text !important; | |
| -ms-user-select: text !important; | |
| user-select: text !important; | |
| cursor: text !important; | |
| } | |
| body { | |
| background-color: #0a192f !important; | |
| color: #ccd6f6 !important; | |
| overflow-x: hidden; | |
| } | |
| .gradio-container { | |
| background-color: transparent !important; | |
| position: relative !important; | |
| z-index: 1 !important; | |
| } | |
| /* Pseudo-element for the pattern to control opacity properly without affecting text */ | |
| body::before { | |
| content: ""; | |
| position: fixed; | |
| top: 0; | |
| left: 0; | |
| width: 100%; | |
| height: 100%; | |
| background-image: url('FAVICON_PLACEHOLDER') !important; | |
| background-repeat: repeat !important; | |
| background-size: 60px !important; | |
| opacity: 0.05 !important; /* Dimmed to 5% - Perfect watermark */ | |
| pointer-events: none; | |
| z-index: 0; | |
| } | |
| """.replace("FAVICON_PLACEHOLDER", NEON_MIC_ICON) + """ | |
| .main-container { | |
| max-width: 950px !important; | |
| margin: 0 auto !important; | |
| padding: 20px !important; | |
| } | |
| #header h1 { | |
| font-size: 2.2rem; | |
| color: #ff8c00; | |
| margin-bottom: 0px; | |
| letter-spacing: -1px; | |
| } | |
| #header p { font-size: 0.9rem; color: #8892b0; margin-top: 5px; } | |
| #intro-btn { | |
| background: transparent !important; | |
| border: none !important; | |
| font-size: 2.2rem !important; | |
| color: #ff8c00 !important; | |
| font-weight: 800 !important; | |
| cursor: pointer !important; | |
| box-shadow: none !important; | |
| padding: 0 !important; | |
| margin: 0 !important; | |
| transition: all 0.3s ease !important; | |
| } | |
| #intro-btn:hover { | |
| color: #ccd6f6 !important; | |
| transform: scale(1.02); | |
| } | |
| #intro-audio { display: none !important; } | |
| .studio-card { | |
| background: #112240 !important; | |
| border: 1px solid #233554 !important; | |
| border-radius: 12px !important; | |
| padding: 10px !important; | |
| box-shadow: 0 10px 30px -15px rgba(2, 12, 27, 0.7) !important; | |
| margin-bottom: 6px !important; | |
| transition: transform 0.2s ease, border-color 0.2s ease !important; | |
| min-height: 180px !important; | |
| display: flex !important; | |
| flex-direction: column !important; | |
| overflow: visible !important; | |
| } | |
| .studio-card .prose { | |
| margin-bottom: 5px !important; | |
| } | |
| /* Force Play Font on everything */ | |
| * { font-family: 'Play', sans-serif !important; } | |
| span, button, input, label, textarea, select { font-family: 'Play', sans-serif !important; } | |
| .studio-card:hover { | |
| transform: translateY(-2px); | |
| border-color: #ff8c00 !important; | |
| } | |
| /* --- PRO CODE DEFINITIVE "ZERO-BLEED" FIX --- */ | |
| /* 1. FLATTEN THE WRAPPERS: Make specific outer containers invisible */ | |
| #script-box, #status-box, | |
| #script-box > .form, #status-box > .form, | |
| .gray-border, .form { | |
| background-color: transparent !important; | |
| background: transparent !important; | |
| border: none !important; | |
| box-shadow: none !important; | |
| padding: 0 !important; | |
| } | |
| /* 2. STYLE THE INNER INPUTS: Apply the theme ONLY to the interactive element */ | |
| #script-box textarea, #status-box input, | |
| #preset-dropdown-container .gr-dropdown { | |
| background-color: #112240 !important; /* Perfect Match to Studio Card */ | |
| border: 2px solid #ff8c00 !important; | |
| border-radius: 12px !important; | |
| color: #ccd6f6 !important; | |
| box-shadow: none !important; | |
| } | |
| /* Remove placeholder color interference */ | |
| #script-box textarea::placeholder { color: #555 !important; } | |
| /* Focus States */ | |
| #script-box textarea:focus, #status-box input:focus { | |
| border-color: #ffb347 !important; | |
| box-shadow: 0 0 15px rgba(255, 140, 0, 0.4) !important; | |
| } | |
| /* --- PRO CODE "VOICE DECK" (SCROLLABLE LIST) --- */ | |
| /* The Voice Deck Container */ | |
| #voice-deck { | |
| max-height: 70px !important; | |
| overflow-y: auto !important; | |
| background-color: #0d1b2a !important; /* Slightly darker than card for depth */ | |
| border: 2px solid #ff8c00 !important; | |
| border-radius: 12px !important; | |
| padding: 8px !important; | |
| margin-bottom: 10px !important; | |
| scrollbar-width: thin !important; | |
| scrollbar-color: #ff8c00 #0a192f !important; | |
| } | |
| /* Radio Item Styling (Premium Chips) */ | |
| #voice-deck label { | |
| display: flex !important; | |
| align-items: center !important; | |
| width: 100% !important; | |
| background: transparent !important; | |
| border: 1px solid #233554 !important; | |
| border-radius: 8px !important; | |
| margin-bottom: 4px !important; | |
| padding: 8px 12px !important; | |
| transition: all 0.2s ease !important; | |
| cursor: pointer !important; | |
| color: #8892b0 !important; | |
| } | |
| #voice-deck label:hover { | |
| background: #112240 !important; | |
| border-color: #ff8c00 !important; | |
| color: #ff8c00 !important; | |
| } | |
| #voice-deck label.selected, #voice-deck input:checked + span { | |
| background: #233554 !important; | |
| border-color: #ff8c00 !important; | |
| color: #ff8c00 !important; | |
| font-weight: bold !important; | |
| box-shadow: 0 0 10px rgba(255, 140, 0, 0.2) !important; | |
| } | |
| /* Hide the default radio circle */ | |
| #voice-deck input[type="radio"] { | |
| display: none !important; | |
| } | |
| /* Ensure text is nicely aligned */ | |
| #voice-deck span { | |
| margin-left: 0 !important; | |
| font-size: 0.95rem !important; | |
| } | |
| /* Audio Input Transparency Fix */ | |
| #audio-input, #audio-input .gr-input, #audio-input .gr-box, #audio-input .gr-block { | |
| background: transparent !important; | |
| background-color: transparent !important; | |
| border: none !important; | |
| box-shadow: none !important; | |
| } | |
| div[role="option"] { | |
| padding: 10px !important; | |
| color: #ccd6f6 !important; | |
| border-bottom: 1px solid #233554 !important; | |
| } | |
| div[role="option"]:hover, div[role="option"][aria-selected="true"] { | |
| background-color: #233554 !important; | |
| color: #ff8c00 !important; | |
| } | |
| div[role="listbox"]::-webkit-scrollbar { | |
| width: 6px !important; | |
| } | |
| div[role="listbox"]::-webkit-scrollbar-track { | |
| background: #0a192f !important; | |
| border-radius: 10px !important; | |
| } | |
| div[role="listbox"]::-webkit-scrollbar-thumb { | |
| background: #ff8c00 !important; | |
| border-radius: 10px !important; | |
| border: 1px solid #0a192f !important; | |
| } | |
| div[role="listbox"]::-webkit-scrollbar-thumb:hover { | |
| background: #ffa500 !important; | |
| } | |
| /* --- PREMIUM SCROLLBARS --- */ | |
| /* Global Scrollbar Styling */ | |
| ::-webkit-scrollbar { | |
| width: 6px !important; | |
| height: 6px !important; | |
| } | |
| ::-webkit-scrollbar-track { | |
| background: #0a192f !important; | |
| } | |
| ::-webkit-scrollbar-thumb { | |
| background: #ff8c00 !important; | |
| border-radius: 20px !important; | |
| border: 1px solid #0a192f !important; | |
| } | |
| ::-webkit-scrollbar-thumb:hover { | |
| background: #ffa500 !important; | |
| box-shadow: 0 0 10px rgba(255, 140, 0, 0.5) !important; | |
| } | |
| /* Ensure Audio Player Scrollbars match */ | |
| audio::-webkit-scrollbar { | |
| height: 6px !important; | |
| } | |
| div[role="option"] { | |
| background-color: transparent !important; | |
| color: #ccd6f6 !important; | |
| } | |
| div[role="option"]:hover, div[role="option"][aria-selected="true"] { | |
| background-color: #233554 !important; | |
| color: #ff8c00 !important; | |
| } | |
| .btn-primary { | |
| background: #ff8c00 !important; | |
| border: 2px solid #ff8c00 !important; | |
| color: #0a192f !important; | |
| font-weight: 800 !important; | |
| border-radius: 8px !important; | |
| padding: 0 20px !important; | |
| height: 50px !important; | |
| cursor: pointer !important; | |
| transition: all 0.3s ease !important; | |
| width: 100% !important; | |
| } | |
| .btn-primary:hover { | |
| background: transparent !important; | |
| color: #ff8c00 !important; | |
| border-color: #ff8c00 !important; | |
| } | |
| .btn-secondary { | |
| background: transparent !important; | |
| border: 1px solid #233554 !important; | |
| color: #8892b0 !important; | |
| border-radius: 8px !important; | |
| height: 50px !important; | |
| padding: 0 20px !important; | |
| transition: all 0.3s ease !important; | |
| width: 100% !important; | |
| } | |
| .btn-secondary:hover { | |
| background: #ff8c00 !important; | |
| color: #0a192f !important; | |
| border-color: #ff8c00 !important; | |
| } | |
| .info-section { | |
| font-size: 0.85rem; | |
| color: #8892b0; | |
| margin-top: 20px; | |
| padding: 0 15px; | |
| animation: fadeIn 0.8s ease-out; | |
| } | |
| .info-header { | |
| color: #ff8c00 !important; | |
| font-weight: 800 !important; | |
| margin-bottom: 5px !important; | |
| display: block !important; | |
| } | |
| .footer { | |
| text-align: center; | |
| margin-top: 40px; | |
| padding-top: 20px; | |
| border-top: 1px solid #233554; | |
| font-size: 0.8rem; | |
| color: #8892b0; | |
| animation: fadeIn 1s ease-out; | |
| } | |
| .footer a { color: #ff8c00; text-decoration: none; font-weight: 600; transition: opacity 0.2s; } | |
| .footer a:hover { opacity: 0.8; } | |
| .authorship { | |
| margin-bottom: 10px; | |
| font-weight: 600; | |
| } | |
| /* Dropdown Polish */ | |
| .gr-dropdown { | |
| background: #0a192f !important; | |
| border-color: #233554 !important; | |
| margin-bottom: 12px !important; | |
| } | |
| .gr-dropdown:focus-within { | |
| border-color: #ff8c00 !important; | |
| } | |
| /* Premium Progress Bar Styling */ | |
| .gr-progress { | |
| background-color: #0a192f !important; | |
| border: 1px solid #233554 !important; | |
| border-radius: 12px !important; | |
| height: 38px !important; | |
| overflow: hidden !important; | |
| box-shadow: inset 0 2px 4px rgba(0,0,0,0.3) !important; | |
| } | |
| .gr-progress .progress-level { | |
| background: linear-gradient(90deg, #ff8c00, #ffb347) !important; | |
| border-radius: 10px !important; | |
| box-shadow: 0 0 20px rgba(255, 140, 0, 0.4) !important; | |
| height: 100% !important; | |
| transition: width 0.4s cubic-bezier(0.4, 0, 0.2, 1) !important; | |
| position: relative !important; | |
| overflow: hidden !important; | |
| } | |
| .gr-progress .progress-level::after { | |
| content: "" !important; | |
| position: absolute !important; | |
| top: 0 !important; | |
| left: -150% !important; | |
| width: 100% !important; | |
| height: 100% !important; | |
| background: linear-gradient( | |
| 90deg, | |
| transparent, | |
| rgba(255, 255, 255, 0.4), | |
| transparent | |
| ) !important; | |
| animation: apple-shimmer 2s infinite linear !important; | |
| } | |
| @keyframes apple-shimmer { | |
| 0% { left: -150%; } | |
| 100% { left: 150%; } | |
| } | |
| .gr-progress .progress-text { | |
| color: #ffffff !important; | |
| font-family: 'Play', sans-serif !important; | |
| font-weight: 700 !important; | |
| font-size: 0.85rem !important; | |
| line-height: 38px !important; | |
| text-shadow: 0 1px 2px rgba(0,0,0,0.5) !important; | |
| letter-spacing: 0.5px !important; | |
| position: relative !important; | |
| z-index: 5 !important; | |
| } | |
| .progress-container { | |
| padding: 0 !important; | |
| margin: 0 !important; | |
| } | |
| /* Subtle Animations */ | |
| @keyframes fadeIn { | |
| from { opacity: 0; transform: translateY(8px); } | |
| to { opacity: 1; transform: translateY(0); } | |
| } | |
| .main-container { animation: fadeIn 0.6s ease-out; } | |
| .studio-card { transition: transform 0.2s ease, border-color 0.2s ease !important; } | |
| .studio-card:hover { transform: translateY(-2px); border-color: #ff8c00 !important; } | |
| """ | |
| theme = gr.themes.Default( | |
| primary_hue="orange", | |
| secondary_hue="slate", | |
| ).set( | |
| body_background_fill="#0a192f", | |
| block_background_fill="#112240", | |
| input_background_fill="#0a192f", | |
| input_border_color="#233554", | |
| ) | |
| # --- Low-Level Security & Easter Egg --- | |
| custom_js = """ | |
| function() { | |
| document.addEventListener('contextmenu', event => { | |
| event.preventDefault(); | |
| alert('Security Protocol Engaged: System protected by Amey Thakur & Mega Satish'); | |
| console.warn('Security Alert: Unauthorized access attempt detected.'); | |
| }); | |
| console.log("%c STOP! %c You are entering a protected zone.", "color: red; font-size: 50px; font-weight: bold;", "color: white; font-size: 20px;"); | |
| } | |
| """ | |
| # Inject Favicon via Head (Reliable) and Security JS | |
| with gr.Blocks(title="Deepfake Audio Studio", theme=theme, css=custom_css, js=custom_js, head='<link rel="icon" type="image/png" href="{}">'.format(NEON_MIC_ICON)) as demo: | |
| with gr.Column(elem_classes=["main-container"]): | |
| # Minimal Header | |
| with gr.Column(elem_id="header"): | |
| with gr.Row(): | |
| intro_btn = gr.Button("ποΈ Deepfake Audio", elem_id="intro-btn") | |
| gr.Markdown("<div style='text-align: center; margin-top: 5px; margin-bottom: 50px; color: #8892b0;'>A neural voice cloning studio powered by SV2TTS technology</div>") | |
| intro_audio = gr.Audio(visible=True, autoplay=True, elem_id="intro-audio") | |
| # Compact 2x2 Grid | |
| with gr.Row(): | |
| # Voice Deck (Scrollable Radio List) | |
| with gr.Column(elem_classes=["studio-card"]): | |
| gr.Markdown("<div class='card-title'>01. Voice Reference</div>") | |
| # The Voice Deck | |
| preset_dropdown = gr.Radio( | |
| choices=["Custom Upload"] + sorted([k for k in SAMPLES.keys()]), | |
| value="Custom Upload", | |
| label="Voice Selection", | |
| show_label=False, | |
| elem_id="voice-deck", | |
| interactive=True | |
| ) | |
| audio_input = gr.Audio(type="filepath", label="Reference Sample", container=False, show_label=False, elem_id="audio-input") | |
| with gr.Column(): | |
| with gr.Column(elem_classes=["studio-card"], elem_id="synthesis-output-card"): | |
| gr.Markdown("<div class='card-title'>02. Synthesis Output</div>") | |
| audio_output = gr.Audio(label="Generated Result", interactive=False, container=False, show_label=False, elem_id="audio-output") | |
| # Input & Status Row (2x2 Grid Symmetry) | |
| with gr.Row(): | |
| with gr.Column(elem_classes=["studio-card"]): | |
| gr.Markdown("<div class='card-title'>03. Target Script</div>") | |
| text_input = gr.Textbox( | |
| label="Target text to synthesize", | |
| placeholder="Enter audio text...", | |
| lines=3, | |
| show_label=False, | |
| elem_id="script-box" | |
| ) | |
| with gr.Column(elem_classes=["studio-card"]): | |
| gr.Markdown("<div class='card-title'>04. System Status</div>") | |
| status_info = gr.Textbox( | |
| label="System Status", | |
| value="β οΈ UI Demo Mode - TensorFlow blocked by system policy.", | |
| interactive=False, | |
| show_label=False, | |
| elem_id="status-box" | |
| ) | |
| # Controls | |
| with gr.Row(): | |
| with gr.Column(scale=1): | |
| reset_btn = gr.Button("Reset", variant="secondary", elem_id="reset-btn", elem_classes=["btn-secondary"]) | |
| with gr.Column(scale=1): | |
| run_btn = gr.Button("Generate Voice Clone", variant="primary", elem_classes=["btn-primary"]) | |
| # Information Sections (Neat & Compact) | |
| with gr.Row(elem_classes=["info-section"]): | |
| with gr.Column(): | |
| gr.Markdown("<span class='info-header'>How it Works</span>") | |
| gr.Markdown("Extracts speaker identity into a latent embedding to drive neural text-to-speech synthesis.") | |
| with gr.Column(): | |
| gr.Markdown("<span class='info-header'>Privacy Notice</span>") | |
| gr.Markdown("Audio is processed in memory and never stored. For educational and research use only.") | |
| # Minimal Footer | |
| with gr.Column(elem_classes=["footer"]): | |
| gr.HTML(""" | |
| <div class='authorship'> | |
| Created by <a href='https://github.com/Amey-Thakur' target='_blank'>Amey Thakur</a> | |
| & <a href='https://github.com/msatmod' target='_blank'>Mega Satish</a> | |
| </div> | |
| <div style='margin-top: 12px;'> | |
| <a href='https://github.com/Amey-Thakur/DEEPFAKE-AUDIO' target='_blank'>GitHub Repository</a> | | |
| <a href='https://youtu.be/i3wnBcbHDbs' target='_blank'>YouTube Demo</a> | |
| </div> | |
| <p style='margin-top: 12px; opacity: 0.6;'>Β© 2021 Deepfake Audio Studio</p> | |
| """) | |
| # Events | |
| run_btn.click( | |
| fn=synthesize, | |
| inputs=[audio_input, text_input], | |
| outputs=[audio_output, status_info] | |
| ) | |
| reset_btn.click(lambda: (None, "Custom Upload", "", None, "β οΈ UI Demo Mode"), outputs=[audio_input, preset_dropdown, text_input, audio_output, status_info]) | |
| # Preset selection logic | |
| def on_preset_change(name): | |
| if name == "Custom Upload": | |
| return None | |
| return load_preset(name) | |
| preset_dropdown.change(fn=on_preset_change, inputs=[preset_dropdown], outputs=[audio_input]) | |
| # Custom JS to force play because browser autoplay policies are strict | |
| play_js = "() => { setTimeout(() => { const audio = document.querySelector('#intro-audio audio'); if (audio) audio.play(); }, 300); }" | |
| intro_btn.click(fn=play_intro, outputs=intro_audio, js=play_js) | |
| if __name__ == "__main__": | |
| print("ποΈ Launching Deepfake Audio Studio (UI Demo Mode)...") | |
| print("π Open: http://localhost:7860") | |
| demo.queue().launch(server_name="0.0.0.0", server_port=7860, show_error=True, pwa=True) | |