From f0badb13af7f84f92caa4d7d65b56ef4e3456337 Mon Sep 17 00:00:00 2001 From: Arcitec <38923130+Arcitec@users.noreply.github.com> Date: Wed, 10 Sep 2025 17:33:33 +0200 Subject: [PATCH 01/13] feat(webui)!: Easier DeepSpeed launch argument --- webui.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/webui.py b/webui.py index 6ba60a7..c55915a 100644 --- a/webui.py +++ b/webui.py @@ -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, ) # 支持的语言列表 From 752df305495c06312b6b985b373e35eac9a824b4 Mon Sep 17 00:00:00 2001 From: Arcitec <38923130+Arcitec@users.noreply.github.com> Date: Wed, 10 Sep 2025 19:02:26 +0200 Subject: [PATCH 02/13] chore: Move docs to new directory --- README.md | 2 +- {doc => docs}/README_zh.md | 0 2 files changed, 1 insertion(+), 1 deletion(-) rename {doc => docs}/README_zh.md (100%) diff --git a/README.md b/README.md index e919d5d..8732f4b 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,7 @@
-简体中文 | +简体中文 | English
diff --git a/doc/README_zh.md b/docs/README_zh.md similarity index 100% rename from doc/README_zh.md rename to docs/README_zh.md From 30848efd45f91d55b7f79f7f4a9388c74077afd7 Mon Sep 17 00:00:00 2001 From: Arcitec <38923130+Arcitec@users.noreply.github.com> Date: Wed, 10 Sep 2025 17:57:51 +0200 Subject: [PATCH 03/13] docs: Add Alibaba's high-bandwidth PyPI mirror for China --- README.md | 5 ++++- docs/README_zh.md | 2 ++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 8732f4b..26228f9 100644 --- a/README.md +++ b/README.md @@ -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" ``` diff --git a/docs/README_zh.md b/docs/README_zh.md index 01941c8..8af79bc 100644 --- a/docs/README_zh.md +++ b/docs/README_zh.md @@ -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" ``` From c3d7ab4adce98e8f46edb1a31374f2f64d72f51c Mon Sep 17 00:00:00 2001 From: Arcitec <38923130+Arcitec@users.noreply.github.com> Date: Wed, 10 Sep 2025 19:14:46 +0200 Subject: [PATCH 04/13] docs: Add usage note regarding random sampling --- README.md | 4 ++++ docs/README_zh.md | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 26228f9..4e68c71 100644 --- a/README.md +++ b/README.md @@ -314,6 +314,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) diff --git a/docs/README_zh.md b/docs/README_zh.md index 8af79bc..3b8a205 100644 --- a/docs/README_zh.md +++ b/docs/README_zh.md @@ -261,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 From 6113567e94a885193f451d3d81665a609e9efbe5 Mon Sep 17 00:00:00 2001 From: Arcitec <38923130+Arcitec@users.noreply.github.com> Date: Wed, 10 Sep 2025 20:04:18 +0200 Subject: [PATCH 05/13] fix(cli): More robust device priority checks --- indextts/cli.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/indextts/cli.py b/indextts/cli.py index 5349901..39cd047 100644 --- a/indextts/cli.py +++ b/indextts/cli.py @@ -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 From 39a035d1066dee3baeb45a5580555ea2013591f0 Mon Sep 17 00:00:00 2001 From: Arcitec <38923130+Arcitec@users.noreply.github.com> Date: Wed, 10 Sep 2025 20:24:27 +0200 Subject: [PATCH 06/13] feat: Extend GPU Check utility to support more GPUs - Refactored to a unified device listing function. - Now checks every supported hardware acceleration device type and lists the devices for all of them, to give a deeper system analysis. - Added Intel XPU support. - Improved AMD ROCm support. - Improved Apple MPS support. --- tools/gpu_check.py | 94 ++++++++++++++++++++++++++++++++-------------- 1 file changed, 66 insertions(+), 28 deletions(-) diff --git a/tools/gpu_check.py b/tools/gpu_check.py index f6ea8f9..2fe2730 100644 --- a/tools/gpu_check.py +++ b/tools/gpu_check.py @@ -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() From 55095de3170745946b53f8fbbd8344f93a510ade Mon Sep 17 00:00:00 2001 From: Arcitec <38923130+Arcitec@users.noreply.github.com> Date: Wed, 10 Sep 2025 21:27:14 +0200 Subject: [PATCH 07/13] chore: Lock Gradio version due to bug in 5.45.0 Their new 5.45.0 release today breaks the ability to load examples. We have to lock the last working version of Gradio. --- pyproject.toml | 2 +- uv.lock | 42 +++++++++++++++++++++--------------------- 2 files changed, 22 insertions(+), 22 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 73207cb..75cf2f6 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -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 = [ diff --git a/uv.lock b/uv.lock index 880b5da..f88e9b1 100644 --- a/uv.lock +++ b/uv.lock @@ -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]] From 555e146fb4066d465225a850b21243e5b7eccd0e Mon Sep 17 00:00:00 2001 From: Arcitec <38923130+Arcitec@users.noreply.github.com> Date: Thu, 11 Sep 2025 00:06:05 +0200 Subject: [PATCH 08/13] feat(webui): Implement speech synthesis progress bar --- indextts/infer.py | 22 +++++++++++----------- indextts/infer_v2.py | 16 ++++++++++------ 2 files changed, 21 insertions(+), 17 deletions(-) diff --git a/indextts/infer.py b/indextts/infer.py index c776b74..f137f25 100644 --- a/indextts/infer.py +++ b/indextts/infer.py @@ -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") diff --git a/indextts/infer_v2.py b/indextts/infer_v2.py index 7161a58..880fe33 100644 --- a/indextts/infer_v2.py +++ b/indextts/infer_v2.py @@ -298,8 +298,8 @@ 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}, " @@ -400,9 +400,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 +422,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 +556,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 From 9668064377fcba7d3e8643c8680d5f8d37ddb2a9 Mon Sep 17 00:00:00 2001 From: Arcitec <38923130+Arcitec@users.noreply.github.com> Date: Thu, 11 Sep 2025 04:07:58 +0200 Subject: [PATCH 09/13] feat: Implement `emo_alpha` scaling of emotion vectors and emotion text - Added support for `emo_alpha` scaling of emotion vectors and emotion text inputs. - This is a major new feature, which now allows for much more natural speech generation by lowering the influence of the emotion vector/text control modes. - It is particularly useful for the "emotion text description" control mode, where a strength of 0.6 or lower is useful to get much more natural speech. Before this feature, it was not possible to make natural speech with that mode, because QwenEmotion assigns emotion scores to the text from 0.0-1.0, and that score was used directly as an emotion vector. This meant that the text mode always used very high strengths. Now, the user can adjust the strength of the emotions to get very natural results. - Refactored `IndexTTS2.infer()` variable initialization logic to avoid repetition and ensure cleaner code paths. --- indextts/infer_v2.py | 34 +++++++++++++++++++++------------- 1 file changed, 21 insertions(+), 13 deletions(-) diff --git a/indextts/infer_v2.py b/indextts/infer_v2.py index 880fe33..71c36e1 100644 --- a/indextts/infer_v2.py +++ b/indextts/infer_v2.py @@ -301,34 +301,42 @@ class IndexTTS2: 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: From d8997703137434e2d71329721e52e88eff534572 Mon Sep 17 00:00:00 2001 From: Arcitec <38923130+Arcitec@users.noreply.github.com> Date: Thu, 11 Sep 2025 04:19:58 +0200 Subject: [PATCH 10/13] feat(webui): Implement emotion weighting for vectors and text modes - This is a major new feature, which now allows for much more natural speech generation by lowering the influence of the emotion vector/text control modes. - It is particularly useful for the "emotion text description" control mode, where a strength of 0.6 or lower is useful to get much more natural speech. --- webui.py | 49 ++++++++++++++++++++++++++++--------------------- 1 file changed, 28 insertions(+), 21 deletions(-) diff --git a/webui.py b/webui.py index c55915a..2f854d4 100644 --- a/webui.py +++ b/webui.py @@ -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:

''') + with gr.Tab(i18n("音频生成")): with gr.Row(): os.makedirs("prompts",exist_ok=True) @@ -192,9 +194,6 @@ 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) @@ -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) @@ -317,7 +323,8 @@ with gr.Blocks(title="IndexTTS Demo") as demo: outputs=[emotion_reference_group, emo_random, emotion_vector_group, - emo_text_group] + emo_text_group, + emo_weight_group] ) input_text_single.change( From 3b5b6bca85ad01955bf773686647c3983e737dd9 Mon Sep 17 00:00:00 2001 From: Arcitec <38923130+Arcitec@users.noreply.github.com> Date: Thu, 11 Sep 2025 05:42:39 +0200 Subject: [PATCH 11/13] docs: Document the new `emo_alpha` feature for text-to-emotion mode --- README.md | 6 ++++-- docs/README_zh.md | 4 ++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 4e68c71..f883160 100644 --- a/README.md +++ b/README.md @@ -328,6 +328,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): @@ -335,7 +337,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 @@ -348,7 +350,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) ``` diff --git a/docs/README_zh.md b/docs/README_zh.md index 3b8a205..2fba98d 100644 --- a/docs/README_zh.md +++ b/docs/README_zh.md @@ -278,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`),实现文本与情感分离控制: @@ -288,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使用指南 From f041d8eb64e1af1f7a5def01247d564b1a283a5b Mon Sep 17 00:00:00 2001 From: Arcitec <38923130+Arcitec@users.noreply.github.com> Date: Thu, 11 Sep 2025 06:08:08 +0200 Subject: [PATCH 12/13] fix(webui): Fix unintentional empty spacing between control groups --- webui.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/webui.py b/webui.py index 2f854d4..cf81a2b 100644 --- a/webui.py +++ b/webui.py @@ -195,8 +195,8 @@ with gr.Blocks(title="IndexTTS Demo") as demo: emo_upload = gr.Audio(label=i18n("上传情感参考音频"), type="filepath") # 情感随机采样 - 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: @@ -321,7 +321,7 @@ 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_weight_group] From 85ba55a1d3ac92006f898115600f2cfef4e34251 Mon Sep 17 00:00:00 2001 From: Arcitec <38923130+Arcitec@users.noreply.github.com> Date: Thu, 11 Sep 2025 06:37:03 +0200 Subject: [PATCH 13/13] docs: Document the DeepSpeed performance effects --- README.md | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index f883160..b77a31c 100644 --- a/README.md +++ b/README.md @@ -189,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 @@ -262,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