| |
| """Copy of VoxCPM2_Gradio_Colab_AIQUEST.ipynb |
| |
| Automatically generated by Colab. |
| |
| Original file is located at |
| https://colab.research.google.com/drive/1Qv6hRNMMm0Afyxyy85UyfUb4WsWKN9cJ |
| |
| <div style='text-align: center; padding: 30px; background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); border-radius: 15px; margin: 10px 0; box-shadow: 0 10px 30px rgba(0,0,0,0.2);'> |
| <h1 style='color: white; margin: 0 0 8px 0; font-size: 2.5em;'>ποΈ VoxCPM2 β Multilingual TTS</h1> |
| <h3 style='color: #f0f0f0; margin: 0 0 5px 0; font-weight: 400;'>Google Colab Edition β Created by <strong>AIQUEST</strong></h3> |
| <p style='color: #ddd; margin: 0 0 0 0;'>T4 GPU (16GB VRAM) | 2B Parameters Β· 30 Languages Β· 48kHz Output Β· Voice Design & Cloning</p> |
| </div> |
| |
| --- |
| --- |
| |
| ### β οΈ Quick Start |
| 1. Go to **Runtime β Change runtime type β T4 GPU** |
| 2. Run all cells in order |
| 3. The Gradio UI link will appear at the bottom |
| """ |
|
|
| |
|
|
| |
| import torch |
| assert torch.cuda.is_available(), "β No GPU detected! Go to Runtime β Change runtime type β T4 GPU" |
| gpu_name = torch.cuda.get_device_name(0) |
| vram_gb = torch.cuda.get_device_properties(0).total_memory / 1e9 |
| print(f"β
GPU: {gpu_name} | VRAM: {vram_gb:.1f} GB") |
|
|
| |
| import torch |
| import torch._dynamo |
|
|
| |
| torch._dynamo.config.suppress_errors = True |
| torch._dynamo.config.disable = True |
|
|
| from voxcpm import VoxCPM |
|
|
| print("Loading VoxCPM2 with denoiser (quality mode)...") |
| print(f"GPU: {torch.cuda.get_device_name(0)}") |
| print(f"VRAM: {torch.cuda.get_device_properties(0).total_memory / 1e9:.1f} GB") |
|
|
| model = VoxCPM.from_pretrained( |
| "openbmb/VoxCPM2", |
| load_denoiser=True, |
| optimize=False, |
| ) |
|
|
| SAMPLE_RATE = model.tts_model.sample_rate |
|
|
| |
| print("Warming up...") |
| warmup_chunks = [] |
| for chunk in model.generate( |
| text="Hello, warm up test.", |
| inference_timesteps=5, |
| ): |
| warmup_chunks.append(chunk) |
|
|
| used = torch.cuda.memory_allocated() / 1e9 |
| total = torch.cuda.get_device_properties(0).total_memory / 1e9 |
| print(f"β
Model loaded! VRAM: {used:.1f} / {total:.1f} GB") |
| print(f"Sample rate: {SAMPLE_RATE} Hz") |
|
|
| |
| import gradio as gr |
| import soundfile as sf |
| import numpy as np |
| import tempfile |
| import os |
| import random |
|
|
| CSS = """ |
| @import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;600;700&display=swap'); |
| * { font-family: 'Inter', sans-serif !important; } |
| .gradio-container { max-width: 1000px !important; margin: auto !important; } |
| .brand-header { text-align: center; background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); padding: 28px; border-radius: 15px; margin-bottom: 20px; box-shadow: 0 10px 25px rgba(102,126,234,0.3); } |
| .brand-title { color: white; font-size: 2em; font-weight: 700; margin: 0 0 6px 0; } |
| .brand-subtitle { color: rgba(255,255,255,0.88); font-size: 1em; margin-bottom: 16px; } |
| .btn-row { display: flex; justify-content: center; gap: 10px; flex-wrap: wrap; } |
| .social-btn { display: inline-flex; align-items: center; justify-content: center; min-width: 150px; padding: 10px 18px; border-radius: 10px; font-weight: 700; font-size: 13px; text-decoration: none; color: white; white-space: nowrap; } |
| .yt-btn { background: #FF0000; box-shadow: 0 4px 12px rgba(255,0,0,0.3); } |
| .x-btn { background: #000000; box-shadow: 0 4px 12px rgba(0,0,0,0.25); } |
| .sup-btn { background: linear-gradient(135deg,#f6d365,#fda085); box-shadow: 0 4px 12px rgba(253,160,133,0.35); } |
| button.primary { background: linear-gradient(135deg, #667eea 0%, #764ba2 100%) !important; color: white !important; font-weight: 600 !important; border-radius: 12px !important; } |
| """ |
|
|
| BRAND_HTML = """ |
| <div class="brand-header"> |
| <div class="brand-title">ποΈ VoxCPM2 β Multilingual TTS</div> |
| <div class="brand-subtitle">Created by <strong>AIQuest Academy</strong> | 2B Parameters Β· 30 Languages Β· 48kHz Output Β· Voice Design & Cloning</div> |
| <div class="btn-row"> |
| <a href="https://www.youtube.com/@aiquestacademy?sub_confirmation=1" target="_blank" class="social-btn yt-btn">βΆ Subscribe</a> |
| <a href="https://x.com/aiquestacademy" target="_blank" class="social-btn x-btn">π Follow on X</a> |
| <a href="https://aiquest.site" target="_blank" class="social-btn sup-btn">β€οΈ Support My Work</a> |
| </div> |
| </div> |
| """ |
|
|
| |
| def save_wav(wav_array: np.ndarray) -> str: |
| tmp = tempfile.NamedTemporaryFile(suffix=".wav", delete=False) |
| sf.write(tmp.name, wav_array, SAMPLE_RATE) |
| return tmp.name |
|
|
|
|
| def collect_audio(generator) -> np.ndarray: |
| chunks = [] |
| for chunk in generator: |
| arr = np.asarray(chunk) |
| if arr.ndim == 0: |
| arr = arr.reshape(1) |
| chunks.append(arr) |
| if not chunks: |
| raise gr.Error("Model returned no audio.") |
| return np.concatenate(chunks) |
|
|
|
|
| def resolve_seed(seed, locked): |
| if locked and seed is not None and seed >= 0: |
| s = int(seed) |
| else: |
| s = random.randint(0, 2**31 - 1) |
| torch.manual_seed(s) |
| torch.cuda.manual_seed_all(s) |
| return s |
|
|
|
|
| |
| def tts_generate(text, cfg, steps, seed, locked): |
| if not text.strip(): |
| raise gr.Error("Please enter some text.") |
| used_seed = resolve_seed(seed, locked) |
| gen = model.generate(text=text, cfg_value=cfg, inference_timesteps=int(steps)) |
| wav = collect_audio(gen) |
| return save_wav(wav), used_seed |
|
|
|
|
| |
| def voice_design(description, text, cfg, steps, seed, locked): |
| if not text.strip(): |
| raise gr.Error("Please enter text content.") |
| if not description.strip(): |
| raise gr.Error("Please enter a voice description.") |
| combined = f"({description.strip()}){text.strip()}" |
| used_seed = resolve_seed(seed, locked) |
| gen = model.generate(text=combined, cfg_value=cfg, inference_timesteps=int(steps)) |
| wav = collect_audio(gen) |
| return save_wav(wav), used_seed |
|
|
|
|
| |
| def voice_clone(text, ref_audio, style, cfg, steps, seed, locked): |
| if not text.strip(): |
| raise gr.Error("Please enter text.") |
| if ref_audio is None: |
| raise gr.Error("Please upload reference audio.") |
| if style and style.strip(): |
| text = f"({style.strip()}){text.strip()}" |
| used_seed = resolve_seed(seed, locked) |
| gen = model.generate( |
| text=text, |
| reference_wav_path=ref_audio, |
| cfg_value=cfg, |
| inference_timesteps=int(steps), |
| ) |
| wav = collect_audio(gen) |
| return save_wav(wav), used_seed |
|
|
|
|
| |
| def ultimate_clone(text, ref_audio, transcript, cfg, steps, seed, locked): |
| if not text.strip(): |
| raise gr.Error("Please enter text.") |
| if ref_audio is None: |
| raise gr.Error("Please upload reference audio.") |
| if not transcript.strip(): |
| raise gr.Error("Please enter the reference audio transcript.") |
| used_seed = resolve_seed(seed, locked) |
| gen = model.generate( |
| text=text, |
| prompt_wav_path=ref_audio, |
| prompt_text=transcript, |
| reference_wav_path=ref_audio, |
| cfg_value=cfg, |
| inference_timesteps=int(steps), |
| ) |
| wav = collect_audio(gen) |
| return save_wav(wav), used_seed |
|
|
|
|
| |
| def seed_row(): |
| with gr.Row(): |
| seed = gr.Number(value=-1, label="Seed", precision=0, scale=3) |
| locked = gr.Checkbox(value=False, label="π Lock Seed", scale=1) |
| return seed, locked |
|
|
|
|
| |
| with gr.Blocks(theme=gr.themes.Soft(), css=CSS) as demo: |
| gr.HTML(BRAND_HTML) |
|
|
| with gr.Tab("π£οΈ Text-to-Speech"): |
| with gr.Accordion("π Instructions", open=False): |
| gr.Markdown( |
| "Enter any text and the model will synthesize speech with natural prosody.\n\n" |
| "- **CFG Scale**: Higher = more adherence to text, lower = more natural/relaxed\n" |
| "- **Inference Steps**: Higher = better quality but slower (try 6-10 for speed)\n" |
| "- **Seed**: Shows the seed used. Check **π Lock Seed** to reuse it for reproducible results.\n" |
| "- **Supported Languages (30):** Arabic, Burmese, Chinese, Danish, Dutch, English, " |
| "Finnish, French, German, Greek, Hebrew, Hindi, Indonesian, Italian, Japanese, Khmer, " |
| "Korean, Lao, Malay, Norwegian, Polish, Portuguese, Russian, Spanish, Swahili, Swedish, " |
| "Tagalog, Thai, Turkish, Vietnamese\n" |
| "- **Chinese Dialects:** εε·θ―, η²€θ―, ε΄θ―, δΈεθ―, ζ²³εθ―, ιθ₯Ώθ―, ε±±δΈθ―, 倩ζ΄₯θ―, ι½εθ―" |
| ) |
| with gr.Row(): |
| with gr.Column(): |
| tts_text = gr.Textbox(label="Text", lines=4, placeholder="Enter text in any of the 30 supported languages...") |
| with gr.Row(): |
| tts_cfg = gr.Slider(0.5, 5.0, value=2.8, step=0.1, label="CFG Scale") |
| tts_steps = gr.Slider(5, 30, value=15, step=1, label="Inference Steps") |
| tts_seed, tts_locked = seed_row() |
| tts_btn = gr.Button("π£οΈ Generate Speech", variant="primary", size="lg") |
| with gr.Column(): |
| tts_out = gr.Audio(label="Output", type="filepath") |
| tts_btn.click(tts_generate, [tts_text, tts_cfg, tts_steps, tts_seed, tts_locked], [tts_out, tts_seed]) |
|
|
| with gr.Tab("π¨ Voice Design"): |
| with gr.Accordion("π Instructions", open=False): |
| gr.Markdown( |
| "Create a brand-new voice from a natural-language description β no reference audio needed!\n\n" |
| "**How it works:** Describe the voice you want (gender, age, tone, emotion, pace, accent) " |
| "and the model will create it.\n\n" |
| "**Examples:**\n" |
| "- `A young woman, gentle and sweet voice`\n" |
| "- `An elderly British man, deep and authoritative`\n" |
| "- `A cheerful child, energetic and playful`\n" |
| "- `A calm female narrator with a warm tone`\n\n" |
| "π‘ *Results may vary between runs β try 1-3 times to get the desired voice.*\n" |
| "π *Lock the seed to reproduce a voice you liked.*" |
| ) |
| with gr.Row(): |
| with gr.Column(): |
| vd_desc = gr.Textbox(label="Voice Description", lines=2, placeholder="A young woman, gentle and sweet voice") |
| vd_text = gr.Textbox(label="Text Content", lines=3, placeholder="Hello, welcome to VoxCPM2!") |
| with gr.Row(): |
| vd_cfg = gr.Slider(0.5, 5.0, value=2.8, step=0.1, label="CFG Scale") |
| vd_steps = gr.Slider(5, 30, value=15, step=1, label="Inference Steps") |
| vd_seed, vd_locked = seed_row() |
| vd_btn = gr.Button("π¨ Design Voice", variant="primary", size="lg") |
| with gr.Column(): |
| vd_out = gr.Audio(label="Output", type="filepath") |
| vd_btn.click(voice_design, [vd_desc, vd_text, vd_cfg, vd_steps, vd_seed, vd_locked], [vd_out, vd_seed]) |
|
|
| with gr.Tab("ποΈ Voice Cloning"): |
| with gr.Accordion("π Instructions", open=False): |
| gr.Markdown( |
| "Upload a short reference audio clip to clone the voice.\n\n" |
| "- The model clones **timbre, accent, and speaking style** from the reference\n" |
| "- Optionally add a **style description** to control emotion/pace while preserving timbre\n" |
| "- Reference audio should be **clear and 5-30 seconds** for best results\n" |
| "- The model accepts **16kHz input** and outputs **48kHz audio**\n\n" |
| "**Style control examples:**\n" |
| "- `slightly faster, cheerful tone`\n" |
| "- `slow and dramatic`\n" |
| "- `whispering, intimate`" |
| ) |
| with gr.Row(): |
| with gr.Column(): |
| vc_ref = gr.Audio(label="Reference Audio", type="filepath") |
| vc_text = gr.Textbox(label="Text to Synthesize", lines=3, placeholder="This is a cloned voice generated by VoxCPM2.") |
| vc_style = gr.Textbox(label="Style Description (optional)", lines=1, placeholder="slightly faster, cheerful tone") |
| with gr.Row(): |
| vc_cfg = gr.Slider(0.5, 5.0, value=2.8, step=0.1, label="CFG Scale") |
| vc_steps = gr.Slider(5, 30, value=15, step=1, label="Inference Steps") |
| vc_seed, vc_locked = seed_row() |
| vc_btn = gr.Button("ποΈ Clone Voice", variant="primary", size="lg") |
| with gr.Column(): |
| vc_out = gr.Audio(label="Output", type="filepath") |
| vc_btn.click(voice_clone, [vc_text, vc_ref, vc_style, vc_cfg, vc_steps, vc_seed, vc_locked], [vc_out, vc_seed]) |
|
|
| with gr.Tab("ποΈ Ultimate Cloning"): |
| with gr.Accordion("π Instructions", open=False): |
| gr.Markdown( |
| "Provide reference audio **and** its exact transcript for maximum fidelity cloning.\n\n" |
| "- This mode reproduces **every vocal nuance**: timbre, rhythm, emotion, and style\n" |
| "- The model continues seamlessly from the reference audio\n" |
| "- **Both** the reference audio and its transcript are required\n" |
| "- For best results, use the **same audio file** as both reference and prompt\n\n" |
| "π‘ *This is the highest quality cloning mode β use it when you need perfect voice reproduction.*" |
| ) |
| with gr.Row(): |
| with gr.Column(): |
| uc_ref = gr.Audio(label="Reference Audio", type="filepath") |
| uc_transcript = gr.Textbox(label="Reference Audio Transcript", lines=2, placeholder="The exact transcript of the reference audio.") |
| uc_text = gr.Textbox(label="Text to Synthesize", lines=3, placeholder="This is an ultimate cloning demonstration.") |
| with gr.Row(): |
| uc_cfg = gr.Slider(0.5, 5.0, value=2.8, step=0.1, label="CFG Scale") |
| uc_steps = gr.Slider(5, 30, value=15, step=1, label="Inference Steps") |
| uc_seed, uc_locked = seed_row() |
| uc_btn = gr.Button("ποΈ Ultimate Clone", variant="primary", size="lg") |
| with gr.Column(): |
| uc_out = gr.Audio(label="Output", type="filepath") |
| uc_btn.click(ultimate_clone, [uc_text, uc_ref, uc_transcript, uc_cfg, uc_steps, uc_seed, uc_locked], [uc_out, uc_seed]) |
|
|
| |
| demo.queue().launch(share=True, debug=True) |
|
|
| """--- |
| |
| <div align="center"> |
| |
| <a href="https://www.youtube.com/@aiquestacademy?sub_confirmation=1"> |
| <img src="https://img.shields.io/badge/Subscribe%20on%20YouTube-FF0000?style=for-the-badge&logo=youtube&logoColor=white" /> |
| </a> |
| <a href="https://x.com/aiquestacademy"> |
| <img src="https://img.shields.io/badge/Follow%20on%20X-000000?style=for-the-badge&logo=x&logoColor=white" /> |
| </a> |
| <a href="https://aiquest.site"> |
| <img src="https://img.shields.io/badge/Support%20My%20Work-f59e0b?style=for-the-badge&logoColor=white" /> |
| </a> |
| |
| </div> |
| |
| <p align="center" style="color:#6b7280; font-size:12px; margin-top:8px;"> |
| β‘ Made with β€οΈ by <strong>AIQUEST Academy</strong> Β· aiquest.site Β· Β© All rights reserved |
| </p> |
| |
| --- |
| """ |