#!/usr/bin/env python3 """ Voiceover Generator with MiniCPM-o-2.6 (CPU Optimized - No Whisper) """ import gradio as gr import torch import tempfile import random import numpy as np from pathlib import Path import warnings warnings.filterwarnings('ignore') # ============================================================ # CONFIGURATION # ============================================================ OUTPUT_DIR = Path("./generated_voiceovers") OUTPUT_DIR.mkdir(exist_ok=True) VOICE_STYLES = { "1": {"name": "Professional Narrator", "emotion": "neutral", "speed": 0.9}, "2": {"name": "Enthusiastic YouTuber", "emotion": "happy", "speed": 1.1}, "3": {"name": "Calm Teacher", "emotion": "neutral", "speed": 0.8}, "4": {"name": "Energetic Announcer", "emotion": "happy", "speed": 1.2}, "5": {"name": "Deep Documentary Voice", "emotion": "neutral", "speed": 0.75}, "6": {"name": "Friendly Explainer", "emotion": "happy", "speed": 1.0}, } # Templates for voiceover text INTROS = ["Watch how", "See how", "Discover how", "Learn how", "Ever wondered how"] OUTROS = ["That's engineering in action!", "Simple mechanics, powerful results!", "That's how industry gets things done!"] ACTION_DESCS = { "increasing torque": ["multiplies rotational force", "boosts pulling power"], "reducing speed": ["controls motion safely", "slows down rotation"], "transferring power": ["sends energy efficiently", "transmits mechanical force"], "lifting heavy loads": ["raises massive weights", "hoists heavy objects"], "driving conveyor belts": ["moves products along", "keeps production flowing"], } # ============================================================ # LOAD MODEL (WITHOUT WHISPER) # ============================================================ _model = None _tokenizer = None def load_model(): """Load MiniCPM-o-2.6 without Whisper dependencies""" global _model, _tokenizer if _model is not None: return _model, _tokenizer print("🔄 Loading MiniCPM-o-2.6 model...") try: from transformers import AutoModel, AutoTokenizer # Load with specific config to avoid Whisper _model = AutoModel.from_pretrained( 'openbmb/MiniCPM-o-2_6', trust_remote_code=True, torch_dtype=torch.float32, low_cpu_mem_usage=True, use_safetensors=True ) _model = _model.eval().to('cpu') _tokenizer = AutoTokenizer.from_pretrained( 'openbmb/MiniCPM-o-2_6', trust_remote_code=True ) # Initialize TTS if hasattr(_model, 'init_tts'): _model.init_tts() print("✅ Model loaded successfully!") return _model, _tokenizer except ImportError as e: print(f"❌ Import Error: {e}") print("💡 Run: pip install transformers==4.35.0") return None, None except Exception as e: print(f"❌ Model Error: {e}") return None, None # ============================================================ # FALLBACK TTS (Edge TTS - Works without Whisper) # ============================================================ def generate_audio_fallback(text, voice_style, output_path): """Use Edge TTS as fallback (always works)""" import asyncio import edge_tts voice_map = { "1": "en-US-JennyNeural", "2": "en-US-GuyNeural", "3": "en-GB-SoniaNeural", "4": "en-US-DavisNeural", "5": "en-US-ChristopherNeural", "6": "en-US-AnaNeural", } voice = voice_map.get(voice_style, "en-US-JennyNeural") async def generate(): communicate = edge_tts.Communicate(text, voice) await communicate.save(output_path) asyncio.run(generate()) return output_path # ============================================================ # GENERATE VOICEOVER # ============================================================ def generate_natural_voiceover(raw_prompt): """Convert technical prompt to natural voiceover""" if not raw_prompt: return "" parts = raw_prompt.split(" in ", 1) mechanism_action = parts[0] industry = parts[1].split(" -")[0] if len(parts) > 1 else "industrial setting" words = mechanism_action.split() mechanism = words[0] if words else "mechanism" action = " ".join(words[1:]) if len(words) > 1 else "operating" # Find action description action_desc = None for key, descs in ACTION_DESCS.items(): if key in action.lower(): action_desc = random.choice(descs) break if not action_desc: action_desc = f"{action} with precision and reliability" intro = random.choice(INTROS) outro = random.choice(OUTROS) article = "an" if mechanism[0].lower() in 'aeiou' else "a" return f"{intro} {article} {mechanism} {action} inside {article} {industry}. This clever mechanism {action_desc}. {outro}" def process_prompt(prompt_text, voice_choice, auto_convert): """Main processing function""" if not prompt_text: return None, "⚠️ Please enter a prompt!", "" try: # Generate natural text if auto_convert: voiceover_text = generate_natural_voiceover(prompt_text) else: voiceover_text = prompt_text # Generate audio using Edge TTS (reliable fallback) with tempfile.NamedTemporaryFile(delete=False, suffix=".mp3") as tmp: audio_path = generate_audio_fallback(voiceover_text, voice_choice, tmp.name) info = f""" ✅ Voiceover Generated! 📝 Prompt: {prompt_text[:80]}... 🎭 Voice: {VOICE_STYLES[voice_choice]['name']} """ return audio_path, info, voiceover_text except Exception as e: return None, f"❌ Error: {str(e)}", "" # ============================================================ # GRADIO INTERFACE # ============================================================ def create_interface(): with gr.Blocks(title="Voiceover Generator", theme=gr.themes.Soft()) as demo: gr.Markdown(""" # 🎙️ AI Voiceover Generator **Convert technical prompts to natural voiceovers** Example: `"planetary gear increasing torque in automotive plant"` """) with gr.Row(): with gr.Column(): prompt_input = gr.Textbox( label="Technical Prompt", placeholder="planetary gear increasing torque in automotive plant", lines=3 ) voice_dropdown = gr.Dropdown( choices=list(VOICE_STYLES.keys()), label="Voice Style", value="1" ) auto_convert = gr.Checkbox(label="Auto-convert to natural voiceover", value=True) generate_btn = gr.Button("Generate Voiceover", variant="primary") with gr.Column(): audio_output = gr.Audio(label="Generated Voiceover") info_output = gr.Textbox(label="Status", lines=5) text_output = gr.Textbox(label="Voiceover Script", lines=4) generate_btn.click( fn=process_prompt, inputs=[prompt_input, voice_dropdown, auto_convert], outputs=[audio_output, info_output, text_output] ) return demo # ============================================================ # MAIN # ============================================================ if __name__ == "__main__": print("=" * 60) print("🎙️ Voiceover Generator") print("=" * 60) # Install edge-tts if not present try: import edge_tts except ImportError: print("📦 Installing edge-tts...") import subprocess subprocess.check_call(['pip', 'install', 'edge-tts']) demo = create_interface() demo.launch( server_name="0.0.0.0", server_port=7860, share=True )