Merge pull request #325 from Arcitec/indextts2-arc

IndexTTS2 New Features & Maintenance Patches
This commit is contained in:
nanaoto 2025-09-11 12:55:38 +08:00 committed by GitHub
commit 8336824c71
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
9 changed files with 192 additions and 117 deletions

View File

@ -5,7 +5,7 @@
</div>
<div align="center">
<a href="doc/README_zh.md" style="font-size: 24px">简体中文</a> |
<a href="docs/README_zh.md" style="font-size: 24px">简体中文</a> |
<a href="README.md" style="font-size: 24px">English</a>
</div>
@ -174,9 +174,12 @@ will install the correct versions of all dependencies into your `.venv` director
uv sync --all-extras
```
If the download is slow, please try a *local mirror*, for example China:
If the download is slow, please try a *local mirror*, for example any of these
local mirrors in China (choose one mirror from the list below):
```bash
uv sync --all-extras --default-index "https://mirrors.aliyun.com/pypi/simple"
uv sync --all-extras --default-index "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/simple"
```
@ -186,7 +189,8 @@ uv sync --all-extras --default-index "https://mirrors.tuna.tsinghua.edu.cn/pypi/
> - `--all-extras`: Automatically adds *every* extra feature listed below. You can
> remove this flag if you want to customize your installation choices.
> - `--extra webui`: Adds WebUI support (recommended).
> - `--extra deepspeed`: Adds DeepSpeed support (faster inference).
> - `--extra deepspeed`: Adds DeepSpeed support (may speed up inference on some
> systems).
> [!IMPORTANT]
> **Important (Windows):** The DeepSpeed library may be difficult to install for
@ -259,6 +263,12 @@ uv run webui.py -h
Have fun!
> [!IMPORTANT]
> **DeepSpeed** *may* speed up inference on some systems, but it could also
> make it slower. The performance impact is highly dependent on your specific
> hardware, drivers and operating system. Please try with and without it,
> to discover what works best on your personal system.
#### 📝 Using IndexTTS2 in Python
@ -311,6 +321,10 @@ tts.infer(spk_audio_prompt='examples/voice_07.wav', text=text, output_path="gen.
during inference; the default is `False`, and setting it to `True` enables
randomness:
> [!NOTE]
> Enabling random sampling will reduce the voice cloning fidelity of the speech
> synthesis.
```python
from indextts.infer_v2 import IndexTTS2
tts = IndexTTS2(cfg_path="checkpoints/config.yaml", model_dir="checkpoints", use_fp16=False, use_cuda_kernel=False, use_deepspeed=False)
@ -321,6 +335,8 @@ tts.infer(spk_audio_prompt='examples/voice_10.wav', text=text, output_path="gen.
5. Alternatively, you can enable `use_emo_text` to guide the emotions based on
your provided `text` script. Your text script will then automatically
be converted into emotion vectors.
It's recommended to use `emo_alpha` around 0.6 (or lower) when using the text
emotion modes, for more natural sounding speech.
You can introduce randomness with `use_random` (default: `False`;
`True` enables randomness):
@ -328,7 +344,7 @@ tts.infer(spk_audio_prompt='examples/voice_10.wav', text=text, output_path="gen.
from indextts.infer_v2 import IndexTTS2
tts = IndexTTS2(cfg_path="checkpoints/config.yaml", model_dir="checkpoints", use_fp16=False, use_cuda_kernel=False, use_deepspeed=False)
text = "快躲起来!是他要来了!他要来抓我们了!"
tts.infer(spk_audio_prompt='examples/voice_12.wav', text=text, output_path="gen.wav", use_emo_text=True, use_random=False, verbose=True)
tts.infer(spk_audio_prompt='examples/voice_12.wav', text=text, output_path="gen.wav", emo_alpha=0.6, use_emo_text=True, use_random=False, verbose=True)
```
6. It's also possible to directly provide a specific text emotion description
@ -341,7 +357,7 @@ from indextts.infer_v2 import IndexTTS2
tts = IndexTTS2(cfg_path="checkpoints/config.yaml", model_dir="checkpoints", use_fp16=False, use_cuda_kernel=False, use_deepspeed=False)
text = "快躲起来!是他要来了!他要来抓我们了!"
emo_text = "你吓死我了!你是鬼吗?"
tts.infer(spk_audio_prompt='examples/voice_12.wav', text=text, output_path="gen.wav", use_emo_text=True, emo_text=emo_text, use_random=False, verbose=True)
tts.infer(spk_audio_prompt='examples/voice_12.wav', text=text, output_path="gen.wav", emo_alpha=0.6, use_emo_text=True, emo_text=emo_text, use_random=False, verbose=True)
```

View File

@ -153,6 +153,8 @@ uv sync --all-extras
如中国大陆地区用户下载缓慢,可选用国内镜像:
```bash
uv sync --all-extras --default-index "https://mirrors.aliyun.com/pypi/simple"
uv sync --all-extras --default-index "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/simple"
```
@ -259,9 +261,9 @@ tts.infer(spk_audio_prompt='examples/voice_07.wav', text=text, output_path="gen.
```
4. 可直接指定8维情感向量 `[高兴, 愤怒, 悲伤, 害怕, 厌恶, 忧郁, 惊讶, 平静]`,可用`use_random`开启随机情感采样默认False
> [!NOTE]
> 开启随机采样会降低音色的还原度。
>
```python
from indextts.infer_v2 import IndexTTS2
@ -276,7 +278,7 @@ tts.infer(spk_audio_prompt='examples/voice_10.wav', text=text, output_path="gen.
from indextts.infer_v2 import IndexTTS2
tts = IndexTTS2(cfg_path="checkpoints/config.yaml", model_dir="checkpoints", use_fp16=False, use_cuda_kernel=False, use_deepspeed=False)
text = "快躲起来!是他要来了!他要来抓我们了!"
tts.infer(spk_audio_prompt='examples/voice_12.wav', text=text, output_path="gen.wav", use_emo_text=True, use_random=False, verbose=True)
tts.infer(spk_audio_prompt='examples/voice_12.wav', text=text, output_path="gen.wav", emo_alpha=0.6, use_emo_text=True, use_random=False, verbose=True)
```
6. 可直接指定情感文本描述(`emo_text`),实现文本与情感分离控制:
@ -286,7 +288,7 @@ from indextts.infer_v2 import IndexTTS2
tts = IndexTTS2(cfg_path="checkpoints/config.yaml", model_dir="checkpoints", use_fp16=False, use_cuda_kernel=False, use_deepspeed=False)
text = "快躲起来!是他要来了!他要来抓我们了!"
emo_text = "你吓死我了!你是鬼吗?"
tts.infer(spk_audio_prompt='examples/voice_12.wav', text=text, output_path="gen.wav", use_emo_text=True, emo_text=emo_text, use_random=False, verbose=True)
tts.infer(spk_audio_prompt='examples/voice_12.wav', text=text, output_path="gen.wav", emo_alpha=0.6, use_emo_text=True, emo_text=emo_text, use_random=False, verbose=True)
```
### 旧版IndexTTS1使用指南

View File

@ -47,10 +47,10 @@ def main():
if args.device is None:
if torch.cuda.is_available():
args.device = "cuda:0"
elif torch.mps.is_available():
args.device = "mps"
elif torch.xpu.is_available():
elif hasattr(torch, "xpu") and torch.xpu.is_available():
args.device = "xpu"
elif hasattr(torch, "mps") and torch.mps.is_available():
args.device = "mps"
else:
args.device = "cpu"
args.fp16 = False # Disable FP16 on CPU

View File

@ -291,9 +291,9 @@ class IndexTTS:
- 越大bucket数量越少batch越多推理速度越**占用内存更多可能影响质量
- 越小bucket数量越多batch越少推理速度越**占用内存和质量更接近于非快速推理
"""
print(">> start fast inference...")
print(">> starting fast inference...")
self._set_gr_progress(0, "start fast inference...")
self._set_gr_progress(0, "starting fast inference...")
if verbose:
print(f"origin text:{text}")
start_time = time.perf_counter()
@ -385,7 +385,7 @@ class IndexTTS:
processed_num += batch_num
# gpt speech
self._set_gr_progress(0.2 + 0.3 * processed_num / all_batch_num,
f"gpt inference speech... {processed_num}/{all_batch_num}")
f"gpt speech inference {processed_num}/{all_batch_num}...")
m_start_time = time.perf_counter()
with torch.no_grad():
with torch.amp.autocast(batch_text_tokens.device.type, enabled=self.dtype is not None,
@ -407,7 +407,7 @@ class IndexTTS:
gpt_gen_time += time.perf_counter() - m_start_time
# gpt latent
self._set_gr_progress(0.5, "gpt inference latents...")
self._set_gr_progress(0.5, "gpt latents inference...")
all_idxs = []
all_latents = []
has_warned = False
@ -456,7 +456,7 @@ class IndexTTS:
latent_length = len(all_latents)
# bigvgan chunk decode
self._set_gr_progress(0.7, "bigvgan decode...")
self._set_gr_progress(0.7, "bigvgan decoding...")
tqdm_progress = tqdm(total=latent_length, desc="bigvgan")
for items in chunk_latents:
tqdm_progress.update(len(items))
@ -478,7 +478,7 @@ class IndexTTS:
self.torch_empty_cache()
# wav audio output
self._set_gr_progress(0.9, "save audio...")
self._set_gr_progress(0.9, "saving audio...")
wav = torch.cat(wavs, dim=1)
wav_length = wav.shape[-1] / sampling_rate
print(f">> Reference audio length: {cond_mel_frame * 256 / sampling_rate:.2f} seconds")
@ -509,8 +509,8 @@ class IndexTTS:
# 原始推理模式
def infer(self, audio_prompt, text, output_path, verbose=False, max_text_tokens_per_segment=120,
**generation_kwargs):
print(">> start inference...")
self._set_gr_progress(0, "start inference...")
print(">> starting inference...")
self._set_gr_progress(0, "starting inference...")
if verbose:
print(f"origin text:{text}")
start_time = time.perf_counter()
@ -578,7 +578,7 @@ class IndexTTS:
# print(text_len)
progress += 1
self._set_gr_progress(0.2 + 0.4 * (progress - 1) / len(segments),
f"gpt inference latent... {progress}/{len(segments)}")
f"gpt latents inference {progress}/{len(segments)}...")
m_start_time = time.perf_counter()
with torch.no_grad():
with torch.amp.autocast(text_tokens.device.type, enabled=self.dtype is not None, dtype=self.dtype):
@ -620,7 +620,7 @@ class IndexTTS:
print(f"fix codes shape: {codes.shape}, codes type: {codes.dtype}")
print(f"code len: {code_lens}")
self._set_gr_progress(0.2 + 0.4 * progress / len(segments),
f"gpt inference speech... {progress}/{len(segments)}")
f"gpt speech inference {progress}/{len(segments)}...")
m_start_time = time.perf_counter()
# latent, text_lens_out, code_lens_out = \
with torch.amp.autocast(text_tokens.device.type, enabled=self.dtype is not None, dtype=self.dtype):
@ -644,7 +644,7 @@ class IndexTTS:
# wavs.append(wav[:, :-512])
wavs.append(wav.cpu()) # to cpu before saving
end_time = time.perf_counter()
self._set_gr_progress(0.9, "save audio...")
self._set_gr_progress(0.9, "saving audio...")
wav = torch.cat(wavs, dim=1)
wav_length = wav.shape[-1] / sampling_rate
print(f">> Reference audio length: {cond_mel_frame * 256 / sampling_rate:.2f} seconds")

View File

@ -298,37 +298,45 @@ class IndexTTS2:
emo_vector=None,
use_emo_text=False, emo_text=None, use_random=False, interval_silence=200,
verbose=False, max_text_tokens_per_segment=120, **generation_kwargs):
print(">> start inference...")
self._set_gr_progress(0, "start inference...")
print(">> starting inference...")
self._set_gr_progress(0, "starting inference...")
if verbose:
print(f"origin text:{text}, spk_audio_prompt:{spk_audio_prompt},"
f" emo_audio_prompt:{emo_audio_prompt}, emo_alpha:{emo_alpha}, "
print(f"origin text:{text}, spk_audio_prompt:{spk_audio_prompt}, "
f"emo_audio_prompt:{emo_audio_prompt}, emo_alpha:{emo_alpha}, "
f"emo_vector:{emo_vector}, use_emo_text:{use_emo_text}, "
f"emo_text:{emo_text}")
start_time = time.perf_counter()
if use_emo_text:
if use_emo_text or emo_vector is not None:
# we're using a text or emotion vector guidance; so we must remove
# "emotion reference voice", to ensure we use correct emotion mixing!
emo_audio_prompt = None
emo_alpha = 1.0
# assert emo_audio_prompt is None
# assert emo_alpha == 1.0
if use_emo_text:
# automatically generate emotion vectors from text prompt
if emo_text is None:
emo_text = text
emo_text = text # use main text prompt
emo_dict = self.qwen_emo.inference(emo_text)
print(emo_dict)
print(f"detected emotion vectors from text: {emo_dict}")
# convert ordered dict to list of vectors; the order is VERY important!
emo_vector = list(emo_dict.values())
if emo_vector is not None:
emo_audio_prompt = None
emo_alpha = 1.0
# assert emo_audio_prompt is None
# assert emo_alpha == 1.0
# we have emotion vectors; they can't be blended via alpha mixing
# in the main inference process later, so we must pre-calculate
# their new strengths here based on the alpha instead!
emo_vector_scale = max(0.0, min(1.0, emo_alpha))
if emo_vector_scale != 1.0:
# scale each vector and truncate to 4 decimals (for nicer printing)
emo_vector = [int(x * emo_vector_scale * 10000) / 10000 for x in emo_vector]
print(f"scaled emotion vectors to {emo_vector_scale}x: {emo_vector}")
if emo_audio_prompt is None:
# we are not using any external "emotion reference voice"; use
# speaker's voice as the main emotion reference audio.
emo_audio_prompt = spk_audio_prompt
# must always use alpha=1.0 when we don't have an external reference voice
emo_alpha = 1.0
# assert emo_alpha == 1.0
# 如果参考音频改变了,才需要重新生成, 提升速度
if self.cache_spk_cond is None or self.cache_spk_audio_prompt != spk_audio_prompt:
@ -400,9 +408,10 @@ class IndexTTS2:
self._set_gr_progress(0.1, "text processing...")
text_tokens_list = self.tokenizer.tokenize(text)
segments = self.tokenizer.split_segments(text_tokens_list, max_text_tokens_per_segment)
segments_count = len(segments)
if verbose:
print("text_tokens_list:", text_tokens_list)
print("segments count:", len(segments))
print("segments count:", segments_count)
print("max_text_tokens_per_segment:", max_text_tokens_per_segment)
print(*segments, sep="\n")
do_sample = generation_kwargs.pop("do_sample", True)
@ -421,9 +430,11 @@ class IndexTTS2:
gpt_forward_time = 0
s2mel_time = 0
bigvgan_time = 0
progress = 0
has_warned = False
for sent in segments:
for seg_idx, sent in enumerate(segments):
self._set_gr_progress(0.2 + 0.7 * seg_idx / segments_count,
f"speech synthesis {seg_idx + 1}/{segments_count}...")
text_tokens = self.tokenizer.convert_tokens_to_ids(sent)
text_tokens = torch.tensor(text_tokens, dtype=torch.int32, device=self.device).unsqueeze(0)
if verbose:
@ -553,7 +564,8 @@ class IndexTTS2:
# wavs.append(wav[:, :-512])
wavs.append(wav.cpu()) # to cpu before saving
end_time = time.perf_counter()
self._set_gr_progress(0.9, "save audio...")
self._set_gr_progress(0.9, "saving audio...")
wavs = self.insert_interval_silence(wavs, sampling_rate=sampling_rate, interval_silence=interval_silence)
wav = torch.cat(wavs, dim=1)
wav_length = wav.shape[-1] / sampling_rate

View File

@ -65,7 +65,7 @@ dependencies = [
[project.optional-dependencies]
# To install the WebUI support, use `uv sync --extra webui` (or `--all-extras`).
webui = [
"gradio>=5.44.1",
"gradio==5.44.1",
]
# To install the DeepSpeed support, use `uv sync --extra deepspeed` (or `--all-extras`).
deepspeed = [

View File

@ -1,47 +1,85 @@
import torch
def show_cuda_gpu_list() -> None:
def show_device_list(backend: str) -> int:
"""
Displays a list of all detected GPUs that support the CUDA Torch APIs.
Displays a list of all detected devices for a given PyTorch backend.
Args:
backend: The name of the device backend module (e.g., "cuda", "xpu").
Returns:
The number of devices found if the backend is usable, otherwise 0.
"""
num_gpus = torch.cuda.device_count()
print(f"Number of GPUs found: {num_gpus}")
backend_upper = backend.upper()
for i in range(num_gpus):
gpu_name = torch.cuda.get_device_name(i)
print(f'GPU {i}: "{gpu_name}"')
try:
# Get the backend module from PyTorch, e.g., `torch.cuda`.
# NOTE: Backends always exist even if the user has no devices.
backend_module = getattr(torch, backend)
# Determine which vendor brand name to display.
brand_name = backend_upper
if backend == "cuda":
# NOTE: This also checks for PyTorch's official AMD ROCm support,
# since that's implemented inside the PyTorch CUDA APIs.
# SEE: https://docs.pytorch.org/docs/stable/cuda.html
brand_name = "NVIDIA CUDA / AMD ROCm"
elif backend == "xpu":
brand_name = "Intel XPU"
elif backend == "mps":
brand_name = "Apple MPS"
if not backend_module.is_available():
print(f"PyTorch: No devices found for {brand_name} backend.")
return 0
print(f"PyTorch: {brand_name} is available!")
# Show all available hardware acceleration devices.
device_count = backend_module.device_count()
print(f" * Number of {backend_upper} devices found: {device_count}")
# NOTE: Apple Silicon devices don't have `get_device_name()` at the
# moment, so we'll skip those since we can't get their device names.
# SEE: https://docs.pytorch.org/docs/stable/mps.html
if backend != "mps":
for i in range(device_count):
device_name = backend_module.get_device_name(i)
print(f' * Device {i}: "{device_name}"')
return device_count
except AttributeError:
print(
f'Error: The PyTorch backend "{backend}" does not exist, or is missing the necessary APIs (is_available, device_count, get_device_name).'
)
except Exception as e:
print(f"Error: {e}")
return 0
def check_torch_gpus() -> None:
def check_torch_devices() -> None:
"""
Checks for the availability of various PyTorch GPU acceleration platforms
and prints information about the discovered GPUs.
Checks for the availability of various PyTorch hardware acceleration
platforms and prints information about the discovered devices.
"""
# Check for AMD ROCm/HIP first, since it modifies the CUDA APIs.
# NOTE: The unofficial ROCm/HIP backend exposes the AMD features through
# the CUDA Torch API calls.
if hasattr(torch.backends, "hip") and torch.backends.hip.is_available():
print("PyTorch: AMD ROCm/HIP is available!")
show_cuda_gpu_list()
print("Scanning for PyTorch hardware acceleration devices...\n")
# Check for NVIDIA CUDA.
elif torch.cuda.is_available():
print("PyTorch: NVIDIA CUDA is available!")
show_cuda_gpu_list()
device_count = 0
# Check for Apple Metal Performance Shaders (MPS).
elif torch.backends.mps.is_available():
print("PyTorch: Apple MPS is available!")
# PyTorch with MPS doesn't have a direct equivalent of `device_count()`
# or `get_device_name()` for now, so we just confirm its presence.
print("Using Apple Silicon GPU.")
device_count += show_device_list("cuda") # NVIDIA CUDA / AMD ROCm.
device_count += show_device_list("xpu") # Intel XPU.
device_count += show_device_list("mps") # Apple Metal Performance Shaders (MPS).
if device_count > 0:
print("\nHardware acceleration detected. Your system is ready!")
else:
print("PyTorch: No GPU acceleration detected. Running in CPU mode.")
print("\nNo hardware acceleration detected. Running in CPU mode.")
if __name__ == "__main__":
check_torch_gpus()
check_torch_devices()

42
uv.lock generated
View File

@ -1213,7 +1213,7 @@ requires-dist = [
{ name = "einops", specifier = ">=0.8.1" },
{ name = "ffmpeg-python", specifier = "==0.2.0" },
{ name = "g2p-en", specifier = "==2.1.0" },
{ name = "gradio", marker = "extra == 'webui'", specifier = ">=5.44.1" },
{ name = "gradio", marker = "extra == 'webui'", specifier = "==5.44.1" },
{ name = "jieba", specifier = "==0.42.1" },
{ name = "json5", specifier = "==0.10.0" },
{ name = "keras", specifier = "==2.9.0" },
@ -2910,28 +2910,28 @@ wheels = [
[[package]]
name = "ruff"
version = "0.12.12"
version = "0.13.0"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/a8/f0/e0965dd709b8cabe6356811c0ee8c096806bb57d20b5019eb4e48a117410/ruff-0.12.12.tar.gz", hash = "sha256:b86cd3415dbe31b3b46a71c598f4c4b2f550346d1ccf6326b347cc0c8fd063d6", size = 5359915, upload-time = "2025-09-04T16:50:18.273Z" }
sdist = { url = "https://files.pythonhosted.org/packages/6e/1a/1f4b722862840295bcaba8c9e5261572347509548faaa99b2d57ee7bfe6a/ruff-0.13.0.tar.gz", hash = "sha256:5b4b1ee7eb35afae128ab94459b13b2baaed282b1fb0f472a73c82c996c8ae60", size = 5372863, upload-time = "2025-09-10T16:25:37.917Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/09/79/8d3d687224d88367b51c7974cec1040c4b015772bfbeffac95face14c04a/ruff-0.12.12-py3-none-linux_armv6l.whl", hash = "sha256:de1c4b916d98ab289818e55ce481e2cacfaad7710b01d1f990c497edf217dafc", size = 12116602, upload-time = "2025-09-04T16:49:18.892Z" },
{ url = "https://files.pythonhosted.org/packages/c3/c3/6e599657fe192462f94861a09aae935b869aea8a1da07f47d6eae471397c/ruff-0.12.12-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:7acd6045e87fac75a0b0cdedacf9ab3e1ad9d929d149785903cff9bb69ad9727", size = 12868393, upload-time = "2025-09-04T16:49:23.043Z" },
{ url = "https://files.pythonhosted.org/packages/e8/d2/9e3e40d399abc95336b1843f52fc0daaceb672d0e3c9290a28ff1a96f79d/ruff-0.12.12-py3-none-macosx_11_0_arm64.whl", hash = "sha256:abf4073688d7d6da16611f2f126be86523a8ec4343d15d276c614bda8ec44edb", size = 12036967, upload-time = "2025-09-04T16:49:26.04Z" },
{ url = "https://files.pythonhosted.org/packages/e9/03/6816b2ed08836be272e87107d905f0908be5b4a40c14bfc91043e76631b8/ruff-0.12.12-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:968e77094b1d7a576992ac078557d1439df678a34c6fe02fd979f973af167577", size = 12276038, upload-time = "2025-09-04T16:49:29.056Z" },
{ url = "https://files.pythonhosted.org/packages/9f/d5/707b92a61310edf358a389477eabd8af68f375c0ef858194be97ca5b6069/ruff-0.12.12-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:42a67d16e5b1ffc6d21c5f67851e0e769517fb57a8ebad1d0781b30888aa704e", size = 11901110, upload-time = "2025-09-04T16:49:32.07Z" },
{ url = "https://files.pythonhosted.org/packages/9d/3d/f8b1038f4b9822e26ec3d5b49cf2bc313e3c1564cceb4c1a42820bf74853/ruff-0.12.12-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b216ec0a0674e4b1214dcc998a5088e54eaf39417327b19ffefba1c4a1e4971e", size = 13668352, upload-time = "2025-09-04T16:49:35.148Z" },
{ url = "https://files.pythonhosted.org/packages/98/0e/91421368ae6c4f3765dd41a150f760c5f725516028a6be30e58255e3c668/ruff-0.12.12-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:59f909c0fdd8f1dcdbfed0b9569b8bf428cf144bec87d9de298dcd4723f5bee8", size = 14638365, upload-time = "2025-09-04T16:49:38.892Z" },
{ url = "https://files.pythonhosted.org/packages/74/5d/88f3f06a142f58ecc8ecb0c2fe0b82343e2a2b04dcd098809f717cf74b6c/ruff-0.12.12-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9ac93d87047e765336f0c18eacad51dad0c1c33c9df7484c40f98e1d773876f5", size = 14060812, upload-time = "2025-09-04T16:49:42.732Z" },
{ url = "https://files.pythonhosted.org/packages/13/fc/8962e7ddd2e81863d5c92400820f650b86f97ff919c59836fbc4c1a6d84c/ruff-0.12.12-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:01543c137fd3650d322922e8b14cc133b8ea734617c4891c5a9fccf4bfc9aa92", size = 13050208, upload-time = "2025-09-04T16:49:46.434Z" },
{ url = "https://files.pythonhosted.org/packages/53/06/8deb52d48a9a624fd37390555d9589e719eac568c020b27e96eed671f25f/ruff-0.12.12-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2afc2fa864197634e549d87fb1e7b6feb01df0a80fd510d6489e1ce8c0b1cc45", size = 13311444, upload-time = "2025-09-04T16:49:49.931Z" },
{ url = "https://files.pythonhosted.org/packages/2a/81/de5a29af7eb8f341f8140867ffb93f82e4fde7256dadee79016ac87c2716/ruff-0.12.12-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:0c0945246f5ad776cb8925e36af2438e66188d2b57d9cf2eed2c382c58b371e5", size = 13279474, upload-time = "2025-09-04T16:49:53.465Z" },
{ url = "https://files.pythonhosted.org/packages/7f/14/d9577fdeaf791737ada1b4f5c6b59c21c3326f3f683229096cccd7674e0c/ruff-0.12.12-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:a0fbafe8c58e37aae28b84a80ba1817f2ea552e9450156018a478bf1fa80f4e4", size = 12070204, upload-time = "2025-09-04T16:49:56.882Z" },
{ url = "https://files.pythonhosted.org/packages/77/04/a910078284b47fad54506dc0af13839c418ff704e341c176f64e1127e461/ruff-0.12.12-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:b9c456fb2fc8e1282affa932c9e40f5ec31ec9cbb66751a316bd131273b57c23", size = 11880347, upload-time = "2025-09-04T16:49:59.729Z" },
{ url = "https://files.pythonhosted.org/packages/df/58/30185fcb0e89f05e7ea82e5817b47798f7fa7179863f9d9ba6fd4fe1b098/ruff-0.12.12-py3-none-musllinux_1_2_i686.whl", hash = "sha256:5f12856123b0ad0147d90b3961f5c90e7427f9acd4b40050705499c98983f489", size = 12891844, upload-time = "2025-09-04T16:50:02.591Z" },
{ url = "https://files.pythonhosted.org/packages/21/9c/28a8dacce4855e6703dcb8cdf6c1705d0b23dd01d60150786cd55aa93b16/ruff-0.12.12-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:26a1b5a2bf7dd2c47e3b46d077cd9c0fc3b93e6c6cc9ed750bd312ae9dc302ee", size = 13360687, upload-time = "2025-09-04T16:50:05.8Z" },
{ url = "https://files.pythonhosted.org/packages/c8/fa/05b6428a008e60f79546c943e54068316f32ec8ab5c4f73e4563934fbdc7/ruff-0.12.12-py3-none-win32.whl", hash = "sha256:173be2bfc142af07a01e3a759aba6f7791aa47acf3604f610b1c36db888df7b1", size = 12052870, upload-time = "2025-09-04T16:50:09.121Z" },
{ url = "https://files.pythonhosted.org/packages/85/60/d1e335417804df452589271818749d061b22772b87efda88354cf35cdb7a/ruff-0.12.12-py3-none-win_amd64.whl", hash = "sha256:e99620bf01884e5f38611934c09dd194eb665b0109104acae3ba6102b600fd0d", size = 13178016, upload-time = "2025-09-04T16:50:12.559Z" },
{ url = "https://files.pythonhosted.org/packages/28/7e/61c42657f6e4614a4258f1c3b0c5b93adc4d1f8575f5229d1906b483099b/ruff-0.12.12-py3-none-win_arm64.whl", hash = "sha256:2a8199cab4ce4d72d158319b63370abf60991495fb733db96cd923a34c52d093", size = 12256762, upload-time = "2025-09-04T16:50:15.737Z" },
{ url = "https://files.pythonhosted.org/packages/ac/fe/6f87b419dbe166fd30a991390221f14c5b68946f389ea07913e1719741e0/ruff-0.13.0-py3-none-linux_armv6l.whl", hash = "sha256:137f3d65d58ee828ae136a12d1dc33d992773d8f7644bc6b82714570f31b2004", size = 12187826, upload-time = "2025-09-10T16:24:39.5Z" },
{ url = "https://files.pythonhosted.org/packages/e4/25/c92296b1fc36d2499e12b74a3fdb230f77af7bdf048fad7b0a62e94ed56a/ruff-0.13.0-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:21ae48151b66e71fd111b7d79f9ad358814ed58c339631450c66a4be33cc28b9", size = 12933428, upload-time = "2025-09-10T16:24:43.866Z" },
{ url = "https://files.pythonhosted.org/packages/44/cf/40bc7221a949470307d9c35b4ef5810c294e6cfa3caafb57d882731a9f42/ruff-0.13.0-py3-none-macosx_11_0_arm64.whl", hash = "sha256:64de45f4ca5441209e41742d527944635a05a6e7c05798904f39c85bafa819e3", size = 12095543, upload-time = "2025-09-10T16:24:46.638Z" },
{ url = "https://files.pythonhosted.org/packages/f1/03/8b5ff2a211efb68c63a1d03d157e924997ada87d01bebffbd13a0f3fcdeb/ruff-0.13.0-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2b2c653ae9b9d46e0ef62fc6fbf5b979bda20a0b1d2b22f8f7eb0cde9f4963b8", size = 12312489, upload-time = "2025-09-10T16:24:49.556Z" },
{ url = "https://files.pythonhosted.org/packages/37/fc/2336ef6d5e9c8d8ea8305c5f91e767d795cd4fc171a6d97ef38a5302dadc/ruff-0.13.0-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:4cec632534332062bc9eb5884a267b689085a1afea9801bf94e3ba7498a2d207", size = 11991631, upload-time = "2025-09-10T16:24:53.439Z" },
{ url = "https://files.pythonhosted.org/packages/39/7f/f6d574d100fca83d32637d7f5541bea2f5e473c40020bbc7fc4a4d5b7294/ruff-0.13.0-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:dcd628101d9f7d122e120ac7c17e0a0f468b19bc925501dbe03c1cb7f5415b24", size = 13720602, upload-time = "2025-09-10T16:24:56.392Z" },
{ url = "https://files.pythonhosted.org/packages/fd/c8/a8a5b81d8729b5d1f663348d11e2a9d65a7a9bd3c399763b1a51c72be1ce/ruff-0.13.0-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:afe37db8e1466acb173bb2a39ca92df00570e0fd7c94c72d87b51b21bb63efea", size = 14697751, upload-time = "2025-09-10T16:24:59.89Z" },
{ url = "https://files.pythonhosted.org/packages/57/f5/183ec292272ce7ec5e882aea74937f7288e88ecb500198b832c24debc6d3/ruff-0.13.0-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0f96a8d90bb258d7d3358b372905fe7333aaacf6c39e2408b9f8ba181f4b6ef2", size = 14095317, upload-time = "2025-09-10T16:25:03.025Z" },
{ url = "https://files.pythonhosted.org/packages/9f/8d/7f9771c971724701af7926c14dab31754e7b303d127b0d3f01116faef456/ruff-0.13.0-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:94b5e3d883e4f924c5298e3f2ee0f3085819c14f68d1e5b6715597681433f153", size = 13144418, upload-time = "2025-09-10T16:25:06.272Z" },
{ url = "https://files.pythonhosted.org/packages/a8/a6/7985ad1778e60922d4bef546688cd8a25822c58873e9ff30189cfe5dc4ab/ruff-0.13.0-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:03447f3d18479df3d24917a92d768a89f873a7181a064858ea90a804a7538991", size = 13370843, upload-time = "2025-09-10T16:25:09.965Z" },
{ url = "https://files.pythonhosted.org/packages/64/1c/bafdd5a7a05a50cc51d9f5711da704942d8dd62df3d8c70c311e98ce9f8a/ruff-0.13.0-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:fbc6b1934eb1c0033da427c805e27d164bb713f8e273a024a7e86176d7f462cf", size = 13321891, upload-time = "2025-09-10T16:25:12.969Z" },
{ url = "https://files.pythonhosted.org/packages/bc/3e/7817f989cb9725ef7e8d2cee74186bf90555279e119de50c750c4b7a72fe/ruff-0.13.0-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:a8ab6a3e03665d39d4a25ee199d207a488724f022db0e1fe4002968abdb8001b", size = 12119119, upload-time = "2025-09-10T16:25:16.621Z" },
{ url = "https://files.pythonhosted.org/packages/58/07/9df080742e8d1080e60c426dce6e96a8faf9a371e2ce22eef662e3839c95/ruff-0.13.0-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:d2a5c62f8ccc6dd2fe259917482de7275cecc86141ee10432727c4816235bc41", size = 11961594, upload-time = "2025-09-10T16:25:19.49Z" },
{ url = "https://files.pythonhosted.org/packages/6a/f4/ae1185349197d26a2316840cb4d6c3fba61d4ac36ed728bf0228b222d71f/ruff-0.13.0-py3-none-musllinux_1_2_i686.whl", hash = "sha256:b7b85ca27aeeb1ab421bc787009831cffe6048faae08ad80867edab9f2760945", size = 12933377, upload-time = "2025-09-10T16:25:22.371Z" },
{ url = "https://files.pythonhosted.org/packages/b6/39/e776c10a3b349fc8209a905bfb327831d7516f6058339a613a8d2aaecacd/ruff-0.13.0-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:79ea0c44a3032af768cabfd9616e44c24303af49d633b43e3a5096e009ebe823", size = 13418555, upload-time = "2025-09-10T16:25:25.681Z" },
{ url = "https://files.pythonhosted.org/packages/46/09/dca8df3d48e8b3f4202bf20b1658898e74b6442ac835bfe2c1816d926697/ruff-0.13.0-py3-none-win32.whl", hash = "sha256:4e473e8f0e6a04e4113f2e1de12a5039579892329ecc49958424e5568ef4f768", size = 12141613, upload-time = "2025-09-10T16:25:28.664Z" },
{ url = "https://files.pythonhosted.org/packages/61/21/0647eb71ed99b888ad50e44d8ec65d7148babc0e242d531a499a0bbcda5f/ruff-0.13.0-py3-none-win_amd64.whl", hash = "sha256:48e5c25c7a3713eea9ce755995767f4dcd1b0b9599b638b12946e892123d1efb", size = 13258250, upload-time = "2025-09-10T16:25:31.773Z" },
{ url = "https://files.pythonhosted.org/packages/e1/a3/03216a6a86c706df54422612981fb0f9041dbb452c3401501d4a22b942c9/ruff-0.13.0-py3-none-win_arm64.whl", hash = "sha256:ab80525317b1e1d38614addec8ac954f1b3e662de9d59114ecbf771d00cf613e", size = 12312357, upload-time = "2025-09-10T16:25:35.595Z" },
]
[[package]]

View File

@ -25,7 +25,7 @@ parser.add_argument("--port", type=int, default=7860, help="Port to run the web
parser.add_argument("--host", type=str, default="0.0.0.0", help="Host to run the web UI on")
parser.add_argument("--model_dir", type=str, default="./checkpoints", help="Model checkpoints directory")
parser.add_argument("--fp16", action="store_true", default=False, help="Use FP16 for inference if available")
parser.add_argument("--use_deepspeed", action="store_true", default=False, help="Use DeepSpeed to accelerate if available")
parser.add_argument("--deepspeed", action="store_true", default=False, help="Use DeepSpeed to accelerate if available")
parser.add_argument("--cuda_kernel", action="store_true", default=False, help="Use CUDA kernel for inference if available")
parser.add_argument("--gui_seg_tokens", type=int, default=120, help="GUI: Max tokens per generation segment")
cmd_args = parser.parse_args()
@ -55,7 +55,7 @@ MODE = 'local'
tts = IndexTTS2(model_dir=cmd_args.model_dir,
cfg_path=os.path.join(cmd_args.model_dir, "config.yaml"),
use_fp16=cmd_args.fp16,
use_deepspeed=cmd_args.use_deepspeed,
use_deepspeed=cmd_args.deepspeed,
use_cuda_kernel=cmd_args.cuda_kernel,
)
# 支持的语言列表
@ -126,25 +126,26 @@ def gen_single(emo_control_method,prompt, text,
}
if type(emo_control_method) is not int:
emo_control_method = emo_control_method.value
if emo_control_method == 0:
emo_ref_path = None
if emo_control_method == 0: # emotion from speaker
emo_ref_path = None # remove external reference audio
emo_weight = 1.0
if emo_control_method == 1:
emo_weight = emo_weight
if emo_control_method == 2:
if emo_control_method == 1: # emotion from reference audio
# emo_weight = emo_weight
pass
if emo_control_method == 2: # emotion from custom vectors
vec = [vec1, vec2, vec3, vec4, vec5, vec6, vec7, vec8]
vec_sum = sum([vec1, vec2, vec3, vec4, vec5, vec6, vec7, vec8])
if vec_sum > 1.5:
if sum(vec) > 1.5:
gr.Warning(i18n("情感向量之和不能超过1.5,请调整后重试。"))
return
else:
# don't use the emotion vector inputs for the other modes
vec = None
if emo_text == "":
# erase empty emotion descriptions; `infer()` will then automatically use the main prompt
emo_text = None
print(f"Emo control mode:{emo_control_method},vec:{vec}")
print(f"Emo control mode:{emo_control_method},weight:{emo_weight},vec:{vec}")
output = tts.infer(spk_audio_prompt=prompt, text=text,
output_path=output_path,
emo_audio_prompt=emo_ref_path, emo_alpha=emo_weight,
@ -167,6 +168,7 @@ with gr.Blocks(title="IndexTTS Demo") as demo:
<a href='https://arxiv.org/abs/2506.21619'><img src='https://img.shields.io/badge/ArXiv-2506.21619-red'></a>
</p>
''')
with gr.Tab(i18n("音频生成")):
with gr.Row():
os.makedirs("prompts",exist_ok=True)
@ -192,12 +194,9 @@ with gr.Blocks(title="IndexTTS Demo") as demo:
with gr.Row():
emo_upload = gr.Audio(label=i18n("上传情感参考音频"), type="filepath")
with gr.Row():
emo_weight = gr.Slider(label=i18n("情感权重"), minimum=0.0, maximum=1.6, value=0.8, step=0.01)
# 情感随机采样
with gr.Row():
emo_random = gr.Checkbox(label=i18n("情感随机采样"),value=False,visible=False)
with gr.Row(visible=False) as emotion_randomize_group:
emo_random = gr.Checkbox(label=i18n("情感随机采样"), value=False)
# 情感向量控制部分
with gr.Group(visible=False) as emotion_vector_group:
@ -217,6 +216,9 @@ with gr.Blocks(title="IndexTTS Demo") as demo:
with gr.Row():
emo_text = gr.Textbox(label=i18n("情感描述文本"), placeholder=i18n("请输入情绪描述(或留空以自动使用目标文本作为情绪描述)"), value="", info=i18n("例如:高兴,愤怒,悲伤等"))
with gr.Row(visible=False) as emo_weight_group:
emo_weight = gr.Slider(label=i18n("情感权重"), minimum=0.0, maximum=1.6, value=0.8, step=0.01)
with gr.Accordion(i18n("高级生成参数设置"), open=False):
with gr.Row():
with gr.Column(scale=1):
@ -287,26 +289,30 @@ with gr.Blocks(title="IndexTTS Demo") as demo:
segments_preview: gr.update(value=df),
}
def on_method_select(emo_control_method):
if emo_control_method == 1:
if emo_control_method == 1: # emotion reference audio
return (gr.update(visible=True),
gr.update(visible=False),
gr.update(visible=False),
gr.update(visible=False)
gr.update(visible=False),
gr.update(visible=True)
)
elif emo_control_method == 2:
elif emo_control_method == 2: # emotion vectors
return (gr.update(visible=False),
gr.update(visible=True),
gr.update(visible=True),
gr.update(visible=False)
)
elif emo_control_method == 3:
return (gr.update(visible=False),
gr.update(visible=True),
gr.update(visible=False),
gr.update(visible=True)
)
else:
elif emo_control_method == 3: # emotion text description
return (gr.update(visible=False),
gr.update(visible=True),
gr.update(visible=False),
gr.update(visible=True),
gr.update(visible=True)
)
else: # 0: same as speaker voice
return (gr.update(visible=False),
gr.update(visible=False),
gr.update(visible=False),
gr.update(visible=False),
gr.update(visible=False)
@ -315,9 +321,10 @@ with gr.Blocks(title="IndexTTS Demo") as demo:
emo_control_method.select(on_method_select,
inputs=[emo_control_method],
outputs=[emotion_reference_group,
emo_random,
emotion_randomize_group,
emotion_vector_group,
emo_text_group]
emo_text_group,
emo_weight_group]
)
input_text_single.change(