Skip to content

Commit df38f0a

Browse files
committed
update readme for modelscope download
1 parent 9adfaf6 commit df38f0a

3 files changed

Lines changed: 81 additions & 10 deletions

File tree

README.md

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,7 @@ from voxcpm import VoxCPM
103103
import soundfile as sf
104104

105105
model = VoxCPM.from_pretrained(
106-
"openbmb/VoxCPM2"
106+
"openbmb/VoxCPM2",
107107
load_denoiser=False,
108108
)
109109

@@ -116,6 +116,28 @@ sf.write("demo.wav", wav, model.tts_model.sample_rate)
116116
print("saved: demo.wav")
117117
```
118118

119+
If you prefer downloading from ModelScope first, you can use:
120+
121+
```bash
122+
pip install modelscope
123+
```
124+
125+
```python
126+
from modelscope.hub.snapshot_download import snapshot_download
127+
from voxcpm import VoxCPM
128+
import soundfile as sf
129+
130+
local_model_dir = snapshot_download("OpenBMB/VoxCPM2")
131+
model = VoxCPM.from_pretrained(local_model_dir, load_denoiser=False)
132+
133+
wav = model.generate(
134+
text="VoxCPM2 is the current recommended release for realistic multilingual speech synthesis.",
135+
cfg_value=2.0,
136+
inference_timesteps=10,
137+
)
138+
sf.write("demo.wav", wav, model.tts_model.sample_rate)
139+
```
140+
119141
#### 🎨 Voice Design
120142

121143
Create a voice from a natural-language description — no reference audio needed. **Format:** put the description in parentheses at the start of `text`(e.g. `"(your voice description)The text to synthesize."`):

README_zh.md

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,7 @@ from voxcpm import VoxCPM
102102
import soundfile as sf
103103

104104
model = VoxCPM.from_pretrained(
105-
"openbmb/VoxCPM2"
105+
"openbmb/VoxCPM2",
106106
load_denoiser=False,
107107
)
108108

@@ -115,6 +115,28 @@ sf.write("demo.wav", wav, model.tts_model.sample_rate)
115115
print("已保存: demo.wav")
116116
```
117117

118+
如果你希望先从 ModelScope 下载模型到本地(适用于国内网络访问),可以使用:
119+
120+
```bash
121+
pip install modelscope
122+
```
123+
124+
```python
125+
from modelscope.hub.snapshot_download import snapshot_download
126+
from voxcpm import VoxCPM
127+
import soundfile as sf
128+
129+
local_model_dir = snapshot_download("OpenBMB/VoxCPM2")
130+
model = VoxCPM.from_pretrained(local_model_dir, load_denoiser=False)
131+
132+
wav = model.generate(
133+
text="VoxCPM2 是目前推荐使用的多语言语音合成版本。",
134+
cfg_value=2.0,
135+
inference_timesteps=10,
136+
)
137+
sf.write("demo.wav", wav, model.tts_model.sample_rate)
138+
```
139+
118140
#### 🎨 音色设计
119141

120142
用自然语言描述创建全新音色,无需参考音频。**格式:**`text` 开头用括号写入音色描述(如 `"(音色描述)要合成的文本。"`):

src/voxcpm/model/voxcpm2.py

Lines changed: 35 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -402,19 +402,26 @@ def forward(
402402
def _dtype(self):
403403
return get_dtype(self.config.dtype)
404404

405-
def _encode_wav(self, wav_path: str, padding_mode: str = "right") -> torch.Tensor:
405+
def _encode_wav(
406+
self,
407+
wav_path: str,
408+
padding_mode: str = "right",
409+
trim_silence_vad: bool = False,
410+
) -> torch.Tensor:
406411
"""Load, trim, pad and VAE-encode an audio file.
407412
408413
Args:
409414
wav_path: path to the audio file.
410415
padding_mode: "right" (default) or "left" padding for alignment.
416+
trim_silence_vad: whether to apply VAD-based silence trimming.
411417
412418
Returns:
413419
audio_feat: (T, P, D) tensor of latent patches.
414420
"""
415421
audio, _ = librosa.load(wav_path, sr=self._encode_sample_rate, mono=True)
416422
audio = torch.from_numpy(audio).unsqueeze(0)
417-
audio = _trim_audio_silence_vad(audio, self._encode_sample_rate, max_silence_ms=200.0)
423+
if trim_silence_vad:
424+
audio = _trim_audio_silence_vad(audio, self._encode_sample_rate, max_silence_ms=200.0)
418425
patch_len = self.patch_size * self.chunk_size
419426
if audio.size(1) % patch_len != 0:
420427
padding_size = patch_len - audio.size(1) % patch_len
@@ -475,6 +482,7 @@ def _generate(
475482
retry_badcase: bool = False,
476483
retry_badcase_max_times: int = 3,
477484
retry_badcase_ratio_threshold: float = 6.0,
485+
trim_silence_vad: bool = False,
478486
streaming: bool = False,
479487
streaming_prefix_len: int = 4,
480488
) -> Generator[torch.Tensor, None, None]:
@@ -495,8 +503,12 @@ def _generate(
495503
)
496504
text_length = text_token.shape[0]
497505

498-
ref_feat = self._encode_wav(reference_wav_path, padding_mode="right")
499-
prompt_feat = self._encode_wav(prompt_wav_path, padding_mode="left")
506+
ref_feat = self._encode_wav(
507+
reference_wav_path,
508+
padding_mode="right",
509+
trim_silence_vad=trim_silence_vad,
510+
)
511+
prompt_feat = self._encode_wav(prompt_wav_path, padding_mode="left", trim_silence_vad=trim_silence_vad)
500512
prompt_audio_length = prompt_feat.size(0)
501513

502514
ref_tokens, ref_feats, ref_t_mask, ref_a_mask = self._make_ref_prefix(ref_feat, text_token.device)
@@ -538,7 +550,11 @@ def _generate(
538550
)
539551
text_length = text_token.shape[0]
540552

541-
ref_feat = self._encode_wav(reference_wav_path, padding_mode="right")
553+
ref_feat = self._encode_wav(
554+
reference_wav_path,
555+
padding_mode="right",
556+
trim_silence_vad=trim_silence_vad,
557+
)
542558
ref_tokens, ref_feats, ref_t_mask, ref_a_mask = self._make_ref_prefix(ref_feat, text_token.device)
543559

544560
text_pad_feat = torch.zeros(
@@ -595,7 +611,7 @@ def _generate(
595611
)
596612
text_length = text_token.shape[0]
597613

598-
prompt_feat = self._encode_wav(prompt_wav_path, padding_mode="left")
614+
prompt_feat = self._encode_wav(prompt_wav_path, padding_mode="left", trim_silence_vad=trim_silence_vad)
599615
prompt_audio_length = prompt_feat.size(0)
600616
prompt_pad_token = torch.zeros(prompt_audio_length, dtype=torch.int32, device=text_token.device)
601617
text_pad_feat = torch.zeros(
@@ -677,6 +693,7 @@ def build_prompt_cache(
677693
prompt_text: str = None,
678694
prompt_wav_path: str = None,
679695
reference_wav_path: str = None,
696+
trim_silence_vad: bool = False,
680697
):
681698
"""
682699
Build prompt cache for subsequent generation.
@@ -693,6 +710,8 @@ def build_prompt_cache(
693710
Must be paired with ``prompt_text``.
694711
reference_wav_path: reference audio path for voice cloning
695712
(structurally isolated via ref_audio tokens).
713+
trim_silence_vad: whether to apply VAD-based silence trimming
714+
before encoding prompt/reference audio.
696715
697716
Returns:
698717
prompt_cache: dict used by ``_generate_with_prompt_cache``.
@@ -705,11 +724,19 @@ def build_prompt_cache(
705724
cache = {}
706725

707726
if reference_wav_path:
708-
cache["ref_audio_feat"] = self._encode_wav(reference_wav_path, padding_mode="right")
727+
cache["ref_audio_feat"] = self._encode_wav(
728+
reference_wav_path,
729+
padding_mode="right",
730+
trim_silence_vad=trim_silence_vad,
731+
)
709732

710733
if prompt_wav_path and prompt_text is not None:
711734
cache["prompt_text"] = prompt_text
712-
cache["audio_feat"] = self._encode_wav(prompt_wav_path, padding_mode="left")
735+
cache["audio_feat"] = self._encode_wav(
736+
prompt_wav_path,
737+
padding_mode="left",
738+
trim_silence_vad=trim_silence_vad,
739+
)
713740

714741
has_ref = "ref_audio_feat" in cache
715742
has_prompt = "audio_feat" in cache

0 commit comments

Comments
 (0)