VoxCPM / app.py
yelin201875's picture
Update app.py
a093279 verified
Raw
History Blame Contribute Delete
5.06 kB
import os
import uuid
from pathlib import Path
import numpy as np
import gradio as gr
import soundfile as sf
import spaces
from voxcpm import VoxCPM
MODEL_ID = "openbmb/VoxCPM2"
OUTPUT_DIR = Path("outputs")
OUTPUT_DIR.mkdir(exist_ok=True)
_model = None
def get_model():
global _model
if _model is None:
try:
_model = VoxCPM.from_pretrained(
MODEL_ID,
load_denoiser=False,
optimize=False,
)
except TypeError:
_model = VoxCPM.from_pretrained(
MODEL_ID,
load_denoiser=False,
)
return _model
def clean_text(text: str) -> str:
return (
(text or "")
.replace("\u200b", "")
.replace("\u200c", "")
.replace("\u200d", "")
.replace("\ufeff", "")
.replace("\r\n", "\n")
.replace("\r", "\n")
.strip()
)
@spaces.GPU(duration=120)
def synthesize(target_text: str, reference_audio: str, reference_text: str):
target_text = clean_text(target_text)
reference_text = clean_text(reference_text)
if not target_text:
raise gr.Error("Target text is required.")
if not reference_audio:
raise gr.Error("Reference audio is required.")
if not reference_text:
raise gr.Error("Reference transcript is required.")
model = get_model()
sample_rate = model.tts_model.sample_rate
# VoxCPM2 Core Generation
wav = model.generate(
text=target_text,
prompt_wav_path=reference_audio,
prompt_text=reference_text,
reference_wav_path=reference_audio,
cfg_value=2.0,
inference_timesteps=10,
normalize=True,
denoise=False,
)
# Safe convert to numpy array
if hasattr(wav, "detach"):
wav = wav.detach().cpu().numpy()
wav = np.asarray(wav, dtype=np.float32).squeeze()
# 🔥 အစဦးပိုင်း ကလစ်သံ (Click/Pop Artifact) သီးသန့်ကို စစ်ထုတ်ဖျက်ဆီးမည့် Logic အသစ်
search_limit = int(0.35 * sample_rate) # ထိပ်ဆုံး 350ms အတွင်း ရှာဖွေပါမည်
if len(wav) > search_limit:
abs_wav = np.abs(wav[:search_limit])
max_val = np.max(abs_wav) if len(abs_wav) > 0 else 1.0
threshold = max(0.012, max_val * 0.05)
# ၁၀ မီလီစက္ကန့်စာ Frame ကွက်များဖြင့် စကားသံ အစစ်အမှန်ကို ရှာဖွေခြင်း
frame_len = int(0.01 * sample_rate)
start_idx = 0
for i in range(0, search_limit - (frame_len * 2), frame_len):
current_frame = abs_wav[i : i + frame_len]
# တစ်ချက်တည်းထွက်တဲ့ ကလစ်သံမဟုတ်ကြောင်း သေချာစေရန် နောက်ထပ် Frame ကိုပါ ပူးတွဲစစ်ဆေးပါသည်
next_frame = abs_wav[i + frame_len : i + (frame_len * 2)]
if np.mean(current_frame) > threshold and np.mean(next_frame) > threshold:
start_idx = i
break
if start_idx > 0:
# စကားသံ အစဦးပိုင်း ပြတ်မသွားစေရန် 5ms မျှ Cushion ချန်လှပ်၍ ညှပ်ထုတ်ပါသည်
cushion = int(0.005 * sample_rate)
start_idx = max(0, start_idx - cushion)
print(f"[Info] Successfully stripped initial pop/click artifact at index: {start_idx}")
wav = wav[start_idx:]
out_path = OUTPUT_DIR / f"voxcpm2_{uuid.uuid4().hex}.wav"
sf.write(str(out_path), wav, sample_rate)
return str(out_path)
with gr.Blocks(title="Myanmar VoxCPM2 Voice Clone") as demo:
gr.Markdown("## Myanmar VoxCPM2 Voice Clone")
target_text = gr.Textbox(
label="Target text",
lines=5,
placeholder="ဒီမှာ ပြောစေချင်တဲ့ မြန်မာစာသားကို ရိုက်ထည့်ပါ။",
)
reference_audio = gr.Audio(
label="Reference audio",
sources=["upload", "microphone"],
type="filepath",
)
reference_text = gr.Textbox(
label="Reference transcript",
lines=3,
placeholder="Reference audio ထဲမှာ ပြောထားတဲ့ စကားကို တိတိကျကျ ရိုက်ထည့်ပါ။",
)
generate_btn = gr.Button("Generate", variant="primary")
output_audio = gr.Audio(
label="Generated voice",
type="filepath",
)
generate_btn.click(
fn=synthesize,
inputs=[target_text, reference_audio, reference_text],
outputs=output_audio,
api_name="synthesize",
)
if __name__ == "__main__":
demo.queue(max_size=10).launch()