Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 18 additions & 3 deletions app.py
Original file line number Diff line number Diff line change
Expand Up @@ -225,10 +225,17 @@


class VoxCPMDemo:
def __init__(self, model_id: str = "openbmb/VoxCPM2", device: str = "auto") -> None:
def __init__(
self,
model_id: str = "openbmb/VoxCPM2",
device: str = "auto",
optimize: bool = False,
) -> None:
self.device = resolve_runtime_device(device, "cuda")
logger.info(f"Running VoxCPM on device: {self.device}")
self.optimize = self.device.startswith("cuda")
# Allow enabling torch.compile via param or VOXCPM_OPTIMIZE=1
env_optimize = os.environ.get("VOXCPM_OPTIMIZE", "0").lower() in ("1", "true")
self.optimize = (optimize or env_optimize) and self.device.startswith("cuda")

self.asr_model_id = "iic/SenseVoiceSmall"
self.asr_device = "cuda:0" if self.device.startswith("cuda") else "cpu"
Expand Down Expand Up @@ -560,8 +567,9 @@ def run_demo(
show_error: bool = True,
model_id: str = "openbmb/VoxCPM2",
device: str = "auto",
optimize: bool = False,
):
demo = VoxCPMDemo(model_id=model_id, device=device)
demo = VoxCPMDemo(model_id=model_id, device=device, optimize=optimize)
interface = create_demo_interface(demo)
interface.queue(max_size=10, default_concurrency_limit=1).launch(
server_name=server_name,
Expand Down Expand Up @@ -597,10 +605,17 @@ def run_demo(
default="auto",
help="Runtime device: auto, cpu, mps, cuda, or cuda:N (default: auto)",
)
parser.add_argument(
"--optimize",
action="store_true",
default=False,
help="Enable torch.compile optimization (requires compatible GPU/PyTorch)",
)
args = parser.parse_args()
run_demo(
model_id=args.model_id,
server_name=args.host,
server_port=args.port,
device=args.device,
optimize=args.optimize,
)
40 changes: 40 additions & 0 deletions src/voxcpm/model/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -240,3 +240,43 @@ def resolve_runtime_device(device: Optional[str], configured_device: str = "cuda
f"Unsupported device '{device}'. Supported values are 'auto', 'cpu', 'mps', "
"'cuda', or indexed CUDA devices like 'cuda:0'."
)


def load_audio_file(file_path: str, target_sr: int = 16000) -> torch.Tensor:
"""Load any audio file (WAV, MP3, M4A, AAC, FLAC, OGG, WEBM, etc.) and return a mono 1D float32 tensor."""
# Try librosa / soundfile first
try:
import librosa
audio, _ = librosa.load(file_path, sr=target_sr, mono=True)
return torch.from_numpy(audio.astype(torch.float32.numpy_dtype() if hasattr(torch.float32, 'numpy_dtype') else "float32"))
except Exception:
pass

# Fallback to ffmpeg stream conversion
try:
import io
import subprocess
import soundfile as sf

try:
import imageio_ffmpeg
ffmpeg_exe = imageio_ffmpeg.get_ffmpeg_exe()
except ImportError:
ffmpeg_exe = "ffmpeg"

cmd = [
ffmpeg_exe,
"-nostdin",
"-threads", "0",
"-i", str(file_path),
"-f", "wav",
"-ar", str(target_sr),
"-ac", "1",
"pipe:1",
]
proc = subprocess.run(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, check=True)
audio_np, _ = sf.read(io.BytesIO(proc.stdout), dtype="float32")
return torch.from_numpy(audio_np)
except Exception as e:
raise RuntimeError(f"Failed to load audio file '{file_path}': {e}") from e

4 changes: 2 additions & 2 deletions src/voxcpm/model/voxcpm2.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@
from .utils import (
apply_generation_seed,
get_dtype,
load_audio_file,
materialize_generation_seed,
mask_multichar_chinese_tokens,
next_and_close,
Expand Down Expand Up @@ -414,8 +415,7 @@ def _encode_wav(
Returns:
audio_feat: (T, P, D) tensor of latent patches.
"""
audio, _ = librosa.load(wav_path, sr=self._encode_sample_rate, mono=True)
audio = torch.from_numpy(audio).unsqueeze(0)
audio = load_audio_file(wav_path, target_sr=self._encode_sample_rate).unsqueeze(0)
if trim_silence_vad:
audio = _trim_audio_silence_vad(audio, self._encode_sample_rate, max_silence_ms=200.0)
patch_len = self.patch_size * self.chunk_size
Expand Down