diff --git a/.github/build_windows_packages.ps1 b/.github/build_windows_packages.ps1 index 2e4acb2a..75918d35 100644 --- a/.github/build_windows_packages.ps1 +++ b/.github/build_windows_packages.ps1 @@ -162,7 +162,7 @@ Copy-Item -Path $curr -Destination $pkgName -Recurse $7zPath = "$pkgName.7z" $start = Get-Date Write-Host "Compress Starting at $start" -& "C:\Program Files\7-Zip\7z.exe" a -t7z "$7zPath" "$pkgName" -m0=lzma2 -mx=9 -md=1g -ms=1g -mmc=500 -mfb=273 -mlc=0 -mlp=4 -mpb=4 -mc=8g -mmt=on -bsp1 +& "C:\Program Files\7-Zip\7z.exe" a -t7z "$7zPath" "$pkgName" -m0=lzma2 -mx=9 -mmt=on -bsp1 $end = Get-Date Write-Host "Elapsed time: $($end - $start)" Get-ChildItem . diff --git a/GPT_SoVITS/inference_webui.py b/GPT_SoVITS/inference_webui.py index 46820145..78cfcca7 100644 --- a/GPT_SoVITS/inference_webui.py +++ b/GPT_SoVITS/inference_webui.py @@ -1295,5 +1295,6 @@ if __name__ == "__main__": inbrowser=True, share=is_share, server_port=infer_ttswebui, + show_api=False, # quiet=True, ) diff --git a/GPT_SoVITS/inference_webui_fast.py b/GPT_SoVITS/inference_webui_fast.py index 0b9525e8..8941482a 100644 --- a/GPT_SoVITS/inference_webui_fast.py +++ b/GPT_SoVITS/inference_webui_fast.py @@ -538,5 +538,6 @@ if __name__ == "__main__": inbrowser=True, share=is_share, server_port=infer_ttswebui, + show_api=False, # quiet=True, ) diff --git a/GPT_SoVITS/text/g2pw/onnx_api.py b/GPT_SoVITS/text/g2pw/onnx_api.py index 7bd8137d..3e3a6ed1 100644 --- a/GPT_SoVITS/text/g2pw/onnx_api.py +++ b/GPT_SoVITS/text/g2pw/onnx_api.py @@ -23,8 +23,11 @@ from .utils import load_config onnxruntime.set_default_logger_severity(3) try: onnxruntime.preload_dlls() -except: +except AttributeError: + pass +except Exception as _: traceback.print_exc() + warnings.filterwarnings("ignore") model_version = "1.1" diff --git a/README.md b/README.md index b32d2fd9..2735a413 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,5 @@ +# +

GPT-SoVITS-WebUI

@@ -7,12 +9,21 @@ A Powerful Few-shot Voice Conversion and Text-to-Speech WebUI.

RVC-Boss%2FGPT-SoVITS | Trendshift - +[![Train In Colab](https://img.shields.io/badge/Colab-Training-F9AB00?style=for-the-badge&logo=googlecolab)](https://colab.research.google.com/github/RVC-Boss/GPT-SoVITS/blob/main/Colab-WebUI.ipynb) +[![Infer In Colab](https://img.shields.io/badge/Colab-Inference-F9AB00?style=for-the-badge&logo=googlecolab)](https://colab.research.google.com/github/RVC-Boss/GPT-SoVITS/blob/main/Colab-Inference.ipynb) +[![Huggingface](https://img.shields.io/badge/HuggingFace-online%20demo-blue.svg?style=for-the-badge&logo=huggingface)](https://huggingface.co/spaces/lj1995/GPT-SoVITS-v2) -[![Open In Colab](https://img.shields.io/badge/Colab-F9AB00?style=for-the-badge&logo=googlecolab&color=525252)](https://colab.research.google.com/github/RVC-Boss/GPT-SoVITS/blob/main/colab_webui.ipynb) -[![License](https://img.shields.io/badge/LICENSE-MIT-green.svg?style=for-the-badge)](https://github.com/RVC-Boss/GPT-SoVITS/blob/main/LICENSE) -[![Huggingface](https://img.shields.io/badge/🤗%20-online%20demo-yellow.svg?style=for-the-badge)](https://huggingface.co/spaces/lj1995/GPT-SoVITS-v2) -[![Discord](https://img.shields.io/discord/1198701940511617164?color=%23738ADB&label=Discord&style=for-the-badge)](https://discord.gg/dnrgs5GHfG) +[![简体中文](https://img.shields.io/badge/简体中文-阅读文档-blue?style=for-the-badge&logo=googledocs&logoColor=white)](https://www.yuque.com/baicaigongchang1145haoyuangong/ib3g1e) +[![English](https://img.shields.io/badge/English-Read%20Docs-blue?style=for-the-badge&logo=googledocs&logoColor=white)](https://rentry.co/GPT-SoVITS-guide#/) +[![Change Log](https://img.shields.io/badge/Change%20Log-View%20Updates-blue?style=for-the-badge&logo=googledocs&logoColor=white)](https://github.com/RVC-Boss/GPT-SoVITS/blob/main/docs/en/Changelog_EN.md) + +[![GitHub release](https://img.shields.io/github/v/release/RVC-Boss/gpt-sovits?style=for-the-badge&logo=github)](https://github.com/RVC-Boss/gpt-sovits/releases) +[![GitHub Stars](https://img.shields.io/github/stars/RVC-Boss/gpt-sovits?style=for-the-badge&logo=github)](https://github.com/RVC-Boss/gpt-sovits/stargazers) +[![License](https://img.shields.io/badge/LICENSE-MIT-green.svg?style=for-the-badge&logo=opensourceinitiative)](https://github.com/RVC-Boss/GPT-SoVITS/blob/main/LICENSE) + +[![Python](https://img.shields.io/badge/Python-3.9%2B-blue.svg?style=for-the-badge&logo=python)](https://www.python.org/downloads/) +[![PyTorch](https://img.shields.io/badge/PyTorch-2.5.1+-ee4c2c?style=for-the-badge&logo=pytorch)](https://pytorch.org/) +[![Image Size](https://img.shields.io/docker/image-size/xxxxrt666/gpt-sovits/latest?style=for-the-badge&logo=docker)](https://hub.docker.com/r/xxxxrt666/gpt-sovits) **English** | [**中文简体**](./docs/cn/README.md) | [**日本語**](./docs/ja/README.md) | [**한국어**](./docs/ko/README.md) | [**Türkçe**](./docs/tr/README.md) @@ -20,7 +31,7 @@ A Powerful Few-shot Voice Conversion and Text-to-Speech WebUI.

--- -## Features: +## Features 1. **Zero-shot TTS:** Input a 5-second vocal sample and experience instant text-to-speech conversion. @@ -34,13 +45,13 @@ A Powerful Few-shot Voice Conversion and Text-to-Speech WebUI.

Unseen speakers few-shot fine-tuning demo: -https://github.com/RVC-Boss/GPT-SoVITS/assets/129054828/05bee1fa-bdd8-4d85-9350-80c060ab47fb + -**User guide: [简体中文](https://www.yuque.com/baicaigongchang1145haoyuangong/ib3g1e) | [English](https://rentry.co/GPT-SoVITS-guide#/)** + ## Installation -For users in China, you can [click here](https://www.codewithgpu.com/i/RVC-Boss/GPT-SoVITS/GPT-SoVITS-Official) to use AutoDL Cloud Docker to experience the full functionality online. +For users in China, you can use [AutoDL Cloud Docker](https://www.codewithgpu.com/i/RVC-Boss/GPT-SoVITS/GPT-SoVITS-Official) to experience the full functionality online. ### Tested Environments @@ -171,7 +182,7 @@ docker exec -it ``` -if you want to switch to V1,then - -```bash -python webui.py v1 -``` - -Or maunally switch version in WebUI - ### Finetune #### Path Auto-filling is now supported @@ -253,7 +251,7 @@ Or maunally switch version in WebUI #### Integrated Package Users -Double-click `go-webui-v2.bat` or use `go-webui-v2.ps1` ,then open the inference webui at `1-GPT-SoVITS-TTS/1C-inference` +Double-click `go-webui.bat` or use `go-webui.ps1` , then open the inference webui at `1-GPT-SoVITS-TTS/1C-inference` #### Others @@ -356,11 +354,6 @@ Use the command line to open the WebUI for UVR5 python tools/uvr5/webui.py "" ``` - - This is how the audio segmentation of the dataset is done using the command line ```bash @@ -435,5 +428,5 @@ Thankful to @Naozumi520 for providing the Cantonese training set and for the gui ## Thanks to all contributors for their efforts - + Contributors diff --git a/docker_build.ps1 b/docker_build.ps1 new file mode 100644 index 00000000..c0508fa6 --- /dev/null +++ b/docker_build.ps1 @@ -0,0 +1,73 @@ +$ErrorActionPreference = "Stop" + +$ScriptDir = Split-Path -Parent $MyInvocation.MyCommand.Definition +Set-Location $ScriptDir + +if (-not (Get-Command "docker" -ErrorAction SilentlyContinue)) { + Write-Host "Docker Not Found" + exit 1 +} + +$Lite = $false +$CudaVersion = "12.6" + +function Write-Help { + Write-Host @" +Usage: powershell -File docker_build.ps1 [OPTIONS] + +Options: + --cuda 12.6|12.8 Specify the CUDA VERSION (REQUIRED) + --lite Build a Lite Image + -h, --help Show this help message and exit + +Examples: + powershell -File docker_build.ps1 --cuda 12.6 --lite +"@ +} + +if ($args.Count -eq 0) { + Write-Help + exit 0 +} + +for ($i = 0; $i -lt $args.Count; $i++) { + switch ($args[$i]) { + '--cuda' { + $i++ + $val = $args[$i] + if ($val -ne "12.6" -and $val -ne "12.8") { + Write-Host "Error: Invalid CUDA_VERSION: $val" + Write-Host "Choose From: [12.6, 12.8]" + exit 1 + } + $CudaVersion = $val + } + '--lite' { + $Lite = $true + } + '-h' { Write-Help; exit 0 } + '--help' { Write-Help; exit 0 } + default { + Write-Host "Unknown Argument: $($args[$i])" + Write-Host "Use -h or --help to see available options." + exit 1 + } + } +} + +$arch = (Get-CimInstance Win32_Processor).Architecture +$TargetPlatform = if ($arch -eq 9) { "linux/amd64" } else { "linux/arm64" } + +if ($Lite) { + $TorchBase = "lite" +} else { + $TorchBase = "full" +} + +docker build ` + --build-arg CUDA_VERSION=$CudaVersion ` + --build-arg LITE=$Lite ` + --build-arg TARGETPLATFORM=$TargetPlatform ` + --build-arg TORCH_BASE=$TorchBase ` + -t "$env:USERNAME/gpt-sovits:local" ` + . \ No newline at end of file diff --git a/docker_build.sh b/docker_build.sh index 354599d2..b6a803fb 100644 --- a/docker_build.sh +++ b/docker_build.sh @@ -25,7 +25,7 @@ print_help() { echo " -h, --help Show this help message and exit" echo "" echo "Examples:" - echo " bash docker_build.sh --cuda 12.6 --funasr --faster-whisper" + echo " bash docker_build.sh --cuda 12.6" } # Show help if no arguments provided diff --git a/docs/cn/README.md b/docs/cn/README.md index 832e75f4..6f41722e 100644 --- a/docs/cn/README.md +++ b/docs/cn/README.md @@ -1,18 +1,29 @@ +# +

GPT-SoVITS-WebUI

-强大的少样本语音转换与语音合成Web用户界面.

+强大的少样本语音转换与语音合成Web用户界面.

[![madewithlove](https://img.shields.io/badge/made_with-%E2%9D%A4-red?style=for-the-badge&labelColor=orange)](https://github.com/RVC-Boss/GPT-SoVITS) RVC-Boss%2FGPT-SoVITS | Trendshift - +[![Train In Colab](https://img.shields.io/badge/Colab-Training-F9AB00?style=for-the-badge&logo=googlecolab)](https://colab.research.google.com/github/RVC-Boss/GPT-SoVITS/blob/main/Colab-WebUI.ipynb) +[![Infer In Colab](https://img.shields.io/badge/Colab-Inference-F9AB00?style=for-the-badge&logo=googlecolab)](https://colab.research.google.com/github/RVC-Boss/GPT-SoVITS/blob/main/Colab-Inference.ipynb) +[![Huggingface](https://img.shields.io/badge/HuggingFace-online%20demo-blue.svg?style=for-the-badge&logo=huggingface)](https://huggingface.co/spaces/lj1995/GPT-SoVITS-v2) -[![Open In Colab](https://img.shields.io/badge/Colab-F9AB00?style=for-the-badge&logo=googlecolab&color=525252)](https://colab.research.google.com/github/RVC-Boss/GPT-SoVITS/blob/main/colab_webui.ipynb) -[![License](https://img.shields.io/badge/LICENSE-MIT-green.svg?style=for-the-badge)](https://github.com/RVC-Boss/GPT-SoVITS/blob/main/LICENSE) -[![Huggingface](https://img.shields.io/badge/🤗%20-online%20demo-yellow.svg?style=for-the-badge)](https://huggingface.co/spaces/lj1995/GPT-SoVITS-v2) -[![Discord](https://img.shields.io/discord/1198701940511617164?color=%23738ADB&label=Discord&style=for-the-badge)](https://discord.gg/dnrgs5GHfG) +[![简体中文](https://img.shields.io/badge/简体中文-阅读文档-blue?style=for-the-badge&logo=googledocs&logoColor=white)](https://www.yuque.com/baicaigongchang1145haoyuangong/ib3g1e) +[![English](https://img.shields.io/badge/English-Read%20Docs-blue?style=for-the-badge&logo=googledocs&logoColor=white)](https://rentry.co/GPT-SoVITS-guide#/) +[![Change Log](https://img.shields.io/badge/更新日志-查看更新-blue?style=for-the-badge&logo=googledocs&logoColor=white)](https://github.com/RVC-Boss/GPT-SoVITS/blob/main/docs/cn/Changelog_CN.md) + +[![GitHub release](https://img.shields.io/github/v/release/RVC-Boss/gpt-sovits?style=for-the-badge&logo=github)](https://github.com/RVC-Boss/gpt-sovits/releases) +[![GitHub Stars](https://img.shields.io/github/stars/RVC-Boss/gpt-sovits?style=for-the-badge&logo=github)](https://github.com/RVC-Boss/gpt-sovits/stargazers) +[![License](https://img.shields.io/badge/LICENSE-MIT-green.svg?style=for-the-badge&logo=opensourceinitiative)](https://github.com/RVC-Boss/GPT-SoVITS/blob/main/LICENSE) + +[![Python](https://img.shields.io/badge/Python-3.9%2B-blue.svg?style=for-the-badge&logo=python)](https://www.python.org/downloads/) +[![PyTorch](https://img.shields.io/badge/PyTorch-2.5.1+-ee4c2c?style=for-the-badge&logo=pytorch)](https://pytorch.org/) +[![Image Size](https://img.shields.io/docker/image-size/xxxxrt666/gpt-sovits/latest?style=for-the-badge&logo=docker)](https://hub.docker.com/r/xxxxrt666/gpt-sovits) [**English**](../../README.md) | **中文简体** | [**日本語**](../ja/README.md) | [**한국어**](../ko/README.md) | [**Türkçe**](../tr/README.md) @@ -36,7 +47,7 @@ -**用户手册: [简体中文](https://www.yuque.com/baicaigongchang1145haoyuangong/ib3g1e) | [English](https://rentry.co/GPT-SoVITS-guide#/)** + ## 安装 @@ -171,7 +182,7 @@ docker exec -it ``` -若想使用 V1,则 - -```bash -python webui.py v1 -``` - -或者在 webUI 内动态切换 - ### 微调 #### 现已支持自动填充路径 @@ -243,13 +245,13 @@ python webui.py v1 3. 进行降噪(可选) 4. 进行 ASR 5. 校对标注 -6. 前往下一个窗口,点击训练 +6. 前往下一个窗口, 点击训练 ### 打开推理 WebUI #### 整合包用户 -双击 `go-webui.bat` 或者使用 `go-webui.ps1` ,然后在 `1-GPT-SoVITS-TTS/1C-推理` 中打开推理 webUI +双击 `go-webui.bat` 或者使用 `go-webui.ps1` , 然后在 `1-GPT-SoVITS-TTS/1C-推理` 中打开推理 webUI #### 其他 @@ -287,7 +289,7 @@ python webui.py 3. 需要从[huggingface](https://huggingface.co/lj1995/GPT-SoVITS/tree/main/gsv-v2final-pretrained) 下载预训练模型文件放到 GPT_SoVITS/pretrained_models/gsv-v2final-pretrained 下 - 中文额外需要下载[G2PWModel.zip(HF)](https://huggingface.co/XXXXRT/GPT-SoVITS-Pretrained/resolve/main/G2PWModel.zip)| [G2PWModel.zip(ModelScope)](https://www.modelscope.cn/models/XXXXRT/GPT-SoVITS-Pretrained/resolve/master/G2PWModel.zip) (下载 G2PW 模型,解压并重命名为`G2PWModel`,将其放到`GPT_SoVITS/text`目录下) + 中文额外需要下载[G2PWModel.zip(HF)](https://huggingface.co/XXXXRT/GPT-SoVITS-Pretrained/resolve/main/G2PWModel.zip)| [G2PWModel.zip(ModelScope)](https://www.modelscope.cn/models/XXXXRT/GPT-SoVITS-Pretrained/resolve/master/G2PWModel.zip) (下载 G2PW 模型, 解压并重命名为`G2PWModel`, 将其放到`GPT_SoVITS/text`目录下) ## V3 更新说明 @@ -416,5 +418,5 @@ python ./tools/asr/fasterwhisper_asr.py -i -o -l -p ## 感谢所有贡献者的努力 - + Contributors diff --git a/docs/ja/README.md b/docs/ja/README.md index 8cedb0d9..668df111 100644 --- a/docs/ja/README.md +++ b/docs/ja/README.md @@ -1,16 +1,27 @@ +# +

GPT-SoVITS-WebUI

-パワフルなFew-Shot音声変換・音声合成 WebUI.

+パワフルなFew-Shot音声変換・音声合成 WebUI.

[![madewithlove](https://img.shields.io/badge/made_with-%E2%9D%A4-red?style=for-the-badge&labelColor=orange)](https://github.com/RVC-Boss/GPT-SoVITS) -
+[![Train In Colab](https://img.shields.io/badge/Colab-Training-F9AB00?style=for-the-badge&logo=googlecolab)](https://colab.research.google.com/github/RVC-Boss/GPT-SoVITS/blob/main/Colab-WebUI.ipynb) +[![Infer In Colab](https://img.shields.io/badge/Colab-Inference-F9AB00?style=for-the-badge&logo=googlecolab)](https://colab.research.google.com/github/RVC-Boss/GPT-SoVITS/blob/main/Colab-Inference.ipynb) +[![Huggingface](https://img.shields.io/badge/HuggingFace-online%20demo-blue.svg?style=for-the-badge&logo=huggingface)](https://huggingface.co/spaces/lj1995/GPT-SoVITS-v2) -[![Open In Colab](https://img.shields.io/badge/Colab-F9AB00?style=for-the-badge&logo=googlecolab&color=525252)](https://colab.research.google.com/github/RVC-Boss/GPT-SoVITS/blob/main/colab_webui.ipynb) -[![License](https://img.shields.io/badge/LICENSE-MIT-green.svg?style=for-the-badge)](https://github.com/RVC-Boss/GPT-SoVITS/blob/main/LICENSE) -[![Huggingface](https://img.shields.io/badge/🤗%20-online%20demo-yellow.svg?style=for-the-badge)](https://huggingface.co/spaces/lj1995/GPT-SoVITS-v2) -[![Discord](https://img.shields.io/discord/1198701940511617164?color=%23738ADB&label=Discord&style=for-the-badge)](https://discord.gg/dnrgs5GHfG) +[![简体中文](https://img.shields.io/badge/简体中文-阅读文档-blue?style=for-the-badge&logo=googledocs&logoColor=white)](https://www.yuque.com/baicaigongchang1145haoyuangong/ib3g1e) +[![English](https://img.shields.io/badge/English-Read%20Docs-blue?style=for-the-badge&logo=googledocs&logoColor=white)](https://rentry.co/GPT-SoVITS-guide#/) +[![Change Log](https://img.shields.io/badge/Change%20Log-View%20Updates-blue?style=for-the-badge&logo=googledocs&logoColor=white)](https://github.com/RVC-Boss/GPT-SoVITS/blob/main/docs/ja/Changelog_JA.md) + +[![GitHub release](https://img.shields.io/github/v/release/RVC-Boss/gpt-sovits?style=for-the-badge&logo=github)](https://github.com/RVC-Boss/gpt-sovits/releases) +[![GitHub Stars](https://img.shields.io/github/stars/RVC-Boss/gpt-sovits?style=for-the-badge&logo=github)](https://github.com/RVC-Boss/gpt-sovits/stargazers) +[![License](https://img.shields.io/badge/LICENSE-MIT-green.svg?style=for-the-badge&logo=opensourceinitiative)](https://github.com/RVC-Boss/GPT-SoVITS/blob/main/LICENSE) + +[![Python](https://img.shields.io/badge/Python-3.9%2B-blue.svg?style=for-the-badge&logo=python)](https://www.python.org/downloads/) +[![PyTorch](https://img.shields.io/badge/PyTorch-2.5.1+-ee4c2c?style=for-the-badge&logo=pytorch)](https://pytorch.org/) +[![Image Size](https://img.shields.io/docker/image-size/xxxxrt666/gpt-sovits/latest?style=for-the-badge&logo=docker)](https://hub.docker.com/r/xxxxrt666/gpt-sovits) [**English**](../../README.md) | [**中文简体**](../cn/README.md) | **日本語** | [**한국어**](../ko/README.md) | [**Türkçe**](../tr/README.md) @@ -18,7 +29,7 @@ --- -## 機能: +## 機能 1. **Zero-Shot TTS:** たった 5 秒間の音声サンプルで、即座にテキストからその音声に変換できます. @@ -32,9 +43,9 @@ 声の事前学習無しかつ Few-Shot でトレーニングされたモデルのデモ: -https://github.com/RVC-Boss/GPT-SoVITS/assets/129054828/05bee1fa-bdd8-4d85-9350-80c060ab47fb + -**ユーザーマニュアル: [简体中文](https://www.yuque.com/baicaigongchang1145haoyuangong/ib3g1e) | [English](https://rentry.co/GPT-SoVITS-guide#/)** + ## インストール @@ -165,7 +176,7 @@ docker exec -it ``` -V1 に切り替えたい場合は - -```bash -python webui.py v1 <言語(オプション)> -``` - -または WebUI で手動でバージョンを切り替えてください. - ### 微調整 #### パス自動補完のサポート @@ -239,7 +241,7 @@ python webui.py v1 <言語(オプション)> #### 統合パッケージ利用者 -`go-webui-v2.bat`をダブルクリックするか、`go-webui-v2.ps1`を使用して、`1-GPT-SoVITS-TTS/1C-inference`で推論 webui を開きます. +`go-webui.bat`をダブルクリックするか、`go-webui.ps1`を使用して、`1-GPT-SoVITS-TTS/1C-inference`で推論 webui を開きます. #### その他 @@ -327,11 +329,6 @@ v2 環境から v3 を使用する方法: python tools/uvr5/webui.py "" ``` - - コマンド ラインを使用してデータセットのオーディオ セグメンテーションを行う方法は次のとおりです. ```bash @@ -352,7 +349,7 @@ python tools/asr/funasr_asr.py -i -o ASR 処理は Faster_Whisper を通じて実行されます(中国語を除く ASR マーキング) -(進行状況バーは表示されません.GPU のパフォーマンスにより時間遅延が発生する可能性があります) +(進行状況バーは表示されません. GPU のパフォーマンスにより時間遅延が発生する可能性があります) ```bash python ./tools/asr/fasterwhisper_asr.py -i -o -l -p @@ -406,5 +403,5 @@ python ./tools/asr/fasterwhisper_asr.py -i -o -l -p ## すべてのコントリビューターに感謝します - + Contributors diff --git a/docs/ko/README.md b/docs/ko/README.md index e6419a11..8728881a 100644 --- a/docs/ko/README.md +++ b/docs/ko/README.md @@ -1,3 +1,5 @@ +# +

GPT-SoVITS-WebUI

@@ -5,12 +7,21 @@ [![madewithlove](https://img.shields.io/badge/made_with-%E2%9D%A4-red?style=for-the-badge&labelColor=orange)](https://github.com/RVC-Boss/GPT-SoVITS) -
+[![Train In Colab](https://img.shields.io/badge/Colab-Training-F9AB00?style=for-the-badge&logo=googlecolab)](https://colab.research.google.com/github/RVC-Boss/GPT-SoVITS/blob/main/Colab-WebUI.ipynb) +[![Infer In Colab](https://img.shields.io/badge/Colab-Inference-F9AB00?style=for-the-badge&logo=googlecolab)](https://colab.research.google.com/github/RVC-Boss/GPT-SoVITS/blob/main/Colab-Inference.ipynb) +[![Huggingface](https://img.shields.io/badge/HuggingFace-online%20demo-blue.svg?style=for-the-badge&logo=huggingface)](https://huggingface.co/spaces/lj1995/GPT-SoVITS-v2) -[![Open In Colab](https://img.shields.io/badge/Colab-F9AB00?style=for-the-badge&logo=googlecolab&color=525252)](https://colab.research.google.com/github/RVC-Boss/GPT-SoVITS/blob/main/colab_webui.ipynb) -[![License](https://img.shields.io/badge/LICENSE-MIT-green.svg?style=for-the-badge)](https://github.com/RVC-Boss/GPT-SoVITS/blob/main/LICENSE) -[![Huggingface](https://img.shields.io/badge/🤗%20-online%20demo-yellow.svg?style=for-the-badge)](https://huggingface.co/spaces/lj1995/GPT-SoVITS-v2) -[![Discord](https://img.shields.io/discord/1198701940511617164?color=%23738ADB&label=Discord&style=for-the-badge)](https://discord.gg/dnrgs5GHfG) +[![简体中文](https://img.shields.io/badge/简体中文-阅读文档-blue?style=for-the-badge&logo=googledocs&logoColor=white)](https://www.yuque.com/baicaigongchang1145haoyuangong/ib3g1e) +[![English](https://img.shields.io/badge/English-Read%20Docs-blue?style=for-the-badge&logo=googledocs&logoColor=white)](https://rentry.co/GPT-SoVITS-guide#/) +[![Change Log](https://img.shields.io/badge/Change%20Log-View%20Updates-blue?style=for-the-badge&logo=googledocs&logoColor=white)](https://github.com/RVC-Boss/GPT-SoVITS/blob/main/docs/ko/Changelog_KO.md) + +[![GitHub release](https://img.shields.io/github/v/release/RVC-Boss/gpt-sovits?style=for-the-badge&logo=github)](https://github.com/RVC-Boss/gpt-sovits/releases) +[![GitHub Stars](https://img.shields.io/github/stars/RVC-Boss/gpt-sovits?style=for-the-badge&logo=github)](https://github.com/RVC-Boss/gpt-sovits/stargazers) +[![License](https://img.shields.io/badge/LICENSE-MIT-green.svg?style=for-the-badge&logo=opensourceinitiative)](https://github.com/RVC-Boss/GPT-SoVITS/blob/main/LICENSE) + +[![Python](https://img.shields.io/badge/Python-3.9%2B-blue.svg?style=for-the-badge&logo=python)](https://www.python.org/downloads/) +[![PyTorch](https://img.shields.io/badge/PyTorch-2.5.1+-ee4c2c?style=for-the-badge&logo=pytorch)](https://pytorch.org/) +[![Image Size](https://img.shields.io/docker/image-size/xxxxrt666/gpt-sovits/latest?style=for-the-badge&logo=docker)](https://hub.docker.com/r/xxxxrt666/gpt-sovits) [**English**](../../README.md) | [**中文简体**](../cn/README.md) | [**日本語**](../ja/README.md) | **한국어** | [**Türkçe**](../tr/README.md) @@ -18,7 +29,7 @@ --- -## 기능: +## 기능 1. **제로샷 텍스트 음성 변환 (TTS):** 5초의 음성 샘플을 입력하면 즉시 텍스트를 음성으로 변환할 수 있습니다. @@ -32,9 +43,9 @@ 보지 못한 발화자의 퓨샷(few-shot) 파인튜닝 데모: -https://github.com/RVC-Boss/GPT-SoVITS/assets/129054828/05bee1fa-bdd8-4d85-9350-80c060ab47fb + -**사용자 설명서: [简体中文](https://www.yuque.com/baicaigongchang1145haoyuangong/ib3g1e) | [English](https://rentry.co/GPT-SoVITS-guide#/)** + ## 설치 @@ -165,7 +176,7 @@ docker exec -it ``` -V1으로 전환하려면, - -```bash -python webui.py v1 <언어(옵션)> -``` - -또는 WebUI에서 수동으로 버전을 전환하십시오. - ### 미세 조정 #### 경로 자동 채우기가 지원됩니다 @@ -239,7 +241,7 @@ python webui.py v1 <언어(옵션)> #### 통합 패키지 사용자 -`go-webui-v2.bat`을 더블 클릭하거나 `go-webui-v2.ps1`를 사용한 다음 `1-GPT-SoVITS-TTS/1C-inference`에서 추론 webui를 엽니다. +`go-webui.bat`을 더블 클릭하거나 `go-webui.ps1`를 사용한 다음 `1-GPT-SoVITS-TTS/1C-inference`에서 추론 webui를 엽니다. #### 기타 @@ -277,13 +279,13 @@ V1 환경에서 V2를 사용하려면: 3. [huggingface](https://huggingface.co/lj1995/GPT-SoVITS/tree/main/gsv-v2final-pretrained)에서 V2 사전 학습 모델을 다운로드하여 `GPT_SoVITS/pretrained_models/gsv-v2final-pretrained`에 넣으십시오. - 중국어 V2 추가: [G2PWModel.zip(HF)](https://huggingface.co/XXXXRT/GPT-SoVITS-Pretrained/resolve/main/G2PWModel.zip)| [G2PWModel.zip(ModelScope)](https://www.modelscope.cn/models/XXXXRT/GPT-SoVITS-Pretrained/resolve/master/G2PWModel.zip) (G2PW 모델을 다운로드하여 압축을 풀고 `G2PWModel`로 이름을 변경한 다음 `GPT_SoVITS/text`에 배치합니다.) + 중국어 V2 추가: [G2PWModel.zip(HF)](https://huggingface.co/XXXXRT/GPT-SoVITS-Pretrained/resolve/main/G2PWModel.zip)| [G2PWModel.zip(ModelScope)](https://www.modelscope.cn/models/XXXXRT/GPT-SoVITS-Pretrained/resolve/master/G2PWModel.zip) (G2PW 모델을 다운로드하여 압축을 풀고 `G2PWModel`로 이름을 변경한 다음 `GPT_SoVITS/text`에 배치합니다) ## V3 릴리스 노트 새로운 기능: -1. 음색 유사성이 더 높아져 목표 음성에 대한 학습 데이터가 적게 필요합니다. (기본 모델을 직접 사용하여 미세 조정 없이 음색 유사성이 크게 향상됩니다.) +1. 음색 유사성이 더 높아져 목표 음성에 대한 학습 데이터가 적게 필요합니다. (기본 모델을 직접 사용하여 미세 조정 없이 음색 유사성이 크게 향상됩니다) 2. GPT 모델이 더 안정적이며 반복 및 생략이 적고, 더 풍부한 감정 표현을 가진 음성을 생성하기가 더 쉽습니다. @@ -404,8 +406,8 @@ python ./tools/asr/fasterwhisper_asr.py -i -o -l -p @Naozumi520 님께 감사드립니다. 광둥어 학습 자료를 제공해 주시고, 광둥어 관련 지식을 지도해 주셔서 감사합니다. -## 모든 기여자들에게 감사드립니다 ;) +## 모든 기여자들에게 감사드립니다 - + Contributors diff --git a/docs/tr/README.md b/docs/tr/README.md index 0a8ee4a8..3de92baa 100644 --- a/docs/tr/README.md +++ b/docs/tr/README.md @@ -1,3 +1,5 @@ +# +

GPT-SoVITS-WebUI

@@ -7,12 +9,21 @@ Güçlü Birkaç Örnekli Ses Dönüştürme ve Metinden Konuşmaya Web Arayüz RVC-Boss%2FGPT-SoVITS | Trendshift - +[![Train In Colab](https://img.shields.io/badge/Colab-Training-F9AB00?style=for-the-badge&logo=googlecolab)](https://colab.research.google.com/github/RVC-Boss/GPT-SoVITS/blob/main/Colab-WebUI.ipynb) +[![Infer In Colab](https://img.shields.io/badge/Colab-Inference-F9AB00?style=for-the-badge&logo=googlecolab)](https://colab.research.google.com/github/RVC-Boss/GPT-SoVITS/blob/main/Colab-Inference.ipynb) +[![Huggingface](https://img.shields.io/badge/HuggingFace-online%20demo-blue.svg?style=for-the-badge&logo=huggingface)](https://huggingface.co/spaces/lj1995/GPT-SoVITS-v2) -[![Open In Colab](https://img.shields.io/badge/Colab-F9AB00?style=for-the-badge&logo=googlecolab&color=525252)](https://colab.research.google.com/github/RVC-Boss/GPT-SoVITS/blob/main/colab_webui.ipynb) -[![License](https://img.shields.io/badge/LICENSE-MIT-green.svg?style=for-the-badge)](https://github.com/RVC-Boss/GPT-SoVITS/blob/main/LICENSE) -[![Huggingface](https://img.shields.io/badge/🤗%20-online%20demo-yellow.svg?style=for-the-badge)](https://huggingface.co/spaces/lj1995/GPT-SoVITS-v2) -[![Discord](https://img.shields.io/discord/1198701940511617164?color=%23738ADB&label=Discord&style=for-the-badge)](https://discord.gg/dnrgs5GHfG) +[![简体中文](https://img.shields.io/badge/简体中文-阅读文档-blue?style=for-the-badge&logo=googledocs&logoColor=white)](https://www.yuque.com/baicaigongchang1145haoyuangong/ib3g1e) +[![English](https://img.shields.io/badge/English-Read%20Docs-blue?style=for-the-badge&logo=googledocs&logoColor=white)](https://rentry.co/GPT-SoVITS-guide#/) +[![Change Log](https://img.shields.io/badge/Change%20Log-View%20Updates-blue?style=for-the-badge&logo=googledocs&logoColor=white)](https://github.com/RVC-Boss/GPT-SoVITS/blob/main/docs/tr/Changelog_TR.md) + +[![GitHub release](https://img.shields.io/github/v/release/RVC-Boss/gpt-sovits?style=for-the-badge&logo=github)](https://github.com/RVC-Boss/gpt-sovits/releases) +[![GitHub Stars](https://img.shields.io/github/stars/RVC-Boss/gpt-sovits?style=for-the-badge&logo=github)](https://github.com/RVC-Boss/gpt-sovits/stargazers) +[![License](https://img.shields.io/badge/LICENSE-MIT-green.svg?style=for-the-badge&logo=opensourceinitiative)](https://github.com/RVC-Boss/GPT-SoVITS/blob/main/LICENSE) + +[![Python](https://img.shields.io/badge/Python-3.9%2B-blue.svg?style=for-the-badge&logo=python)](https://www.python.org/downloads/) +[![PyTorch](https://img.shields.io/badge/PyTorch-2.5.1+-ee4c2c?style=for-the-badge&logo=pytorch)](https://pytorch.org/) +[![Image Size](https://img.shields.io/docker/image-size/xxxxrt666/gpt-sovits/latest?style=for-the-badge&logo=docker)](https://hub.docker.com/r/xxxxrt666/gpt-sovits) [**English**](../../README.md) | [**中文简体**](../cn/README.md) | [**日本語**](../ja/README.md) | [**한국어**](../ko/README.md) | **Türkçe** @@ -20,7 +31,7 @@ Güçlü Birkaç Örnekli Ses Dönüştürme ve Metinden Konuşmaya Web Arayüz --- -## Özellikler: +## Özellikler 1. **Sıfır Örnekli Metinden Konuşmaya:** 5 saniyelik bir vokal örneği girin ve anında metinden konuşmaya dönüşümünü deneyimleyin. @@ -34,9 +45,9 @@ Güçlü Birkaç Örnekli Ses Dönüştürme ve Metinden Konuşmaya Web Arayüz Görünmeyen konuşmacılar birkaç örnekli ince ayar demosu: -https://github.com/RVC-Boss/GPT-SoVITS/assets/129054828/05bee1fa-bdd8-4d85-9350-80c060ab47fb + -**Kullanıcı Kılavuzu: [简体中文](https://www.yuque.com/baicaigongchang1145haoyuangong/ib3g1e) | [English](https://rentry.co/GPT-SoVITS-guide#/)** + ## Kurulum @@ -167,7 +178,7 @@ docker exec -it ``` -V1'e geçmek istiyorsanız, - -```bash -python webui.py v1 -``` - -veya WebUI'de manuel olarak sürüm değiştirin. - ### İnce Ayar #### Yol Otomatik Doldurma artık destekleniyor @@ -243,7 +245,7 @@ veya WebUI'de manuel olarak sürüm değiştirin. #### Entegre Paket Kullanıcıları -`go-webui-v2.bat` dosyasına çift tıklayın veya `go-webui-v2.ps1` kullanın, ardından çıkarım webui'sini `1-GPT-SoVITS-TTS/1C-inference` adresinde açın. +`go-webui.bat` dosyasına çift tıklayın veya `go-webui.ps1` kullanın, ardından çıkarım webui'sini `1-GPT-SoVITS-TTS/1C-inference` adresinde açın. #### Diğerleri @@ -281,11 +283,11 @@ V1 ortamından V2'yi kullanmak için: 3. [huggingface](https://huggingface.co/lj1995/GPT-SoVITS/tree/main/gsv-v2final-pretrained) adresinden v2 önceden eğitilmiş modelleri indirin ve bunları `GPT_SoVITS/pretrained_models/gsv-v2final-pretrained` dizinine yerleştirin. - Ek olarak Çince V2: [G2PWModel.zip(HF)](https://huggingface.co/XXXXRT/GPT-SoVITS-Pretrained/resolve/main/G2PWModel.zip)| [G2PWModel.zip(ModelScope)](https://www.modelscope.cn/models/XXXXRT/GPT-SoVITS-Pretrained/resolve/master/G2PWModel.zip) (G2PW modellerini indirip, zipten çıkarıp, `G2PWModel` olarak yeniden adlandırıp `GPT_SoVITS/text` dizinine yerleştirin.) + Ek olarak Çince V2: [G2PWModel.zip(HF)](https://huggingface.co/XXXXRT/GPT-SoVITS-Pretrained/resolve/main/G2PWModel.zip)| [G2PWModel.zip(ModelScope)](https://www.modelscope.cn/models/XXXXRT/GPT-SoVITS-Pretrained/resolve/master/G2PWModel.zip) (G2PW modellerini indirip, zipten çıkarıp, `G2PWModel` olarak yeniden adlandırıp `GPT_SoVITS/text` dizinine yerleştirin) ## V3 Sürüm Notları -### Yeni Özellikler: +### Yeni Özellikler 1. **Tını benzerliği** daha yüksek olup, hedef konuşmacıyı yakınsamak için daha az eğitim verisi gerekmektedir (tını benzerliği, base model doğrudan kullanılacak şekilde fine-tuning yapılmadan önemli ölçüde iyileştirilmiştir). @@ -293,7 +295,7 @@ V1 ortamından V2'yi kullanmak için: [daha fazla detay]() -### v2 ortamında v3 kullanımı: +### v2 ortamında v3 kullanımı 1. `pip install -r requirements.txt` ile bazı paketleri güncelleyin. @@ -331,11 +333,6 @@ UVR5 için Web Arayüzünü açmak için komut satırını kullanın python tools/uvr5/webui.py "" ``` - - Veri setinin ses segmentasyonu komut satırı kullanılarak bu şekilde yapılır ```bash @@ -410,5 +407,5 @@ python ./tools/asr/fasterwhisper_asr.py -i -o <çıktı> -l ## Tüm katkıda bulunanlara çabaları için teşekkürler - + Contributors diff --git a/install.sh b/install.sh index eba18683..187d07e5 100644 --- a/install.sh +++ b/install.sh @@ -34,8 +34,8 @@ print_help() { echo " -h, --help Show this help message and exit" echo "" echo "Examples:" - echo " bash install.sh --source HF --download-uvr5" - echo " bash install.sh --source ModelScope" + echo " bash install.sh --device CU128 --source HF --download-uvr5" + echo " bash install.sh --device MPS --source ModelScope" } # Show help if no arguments provided @@ -149,7 +149,6 @@ else echo "Installing,Please Wait..." fi done - conda install -c conda-forge -q -y fi echo "Installing ffmpeg and cmake..." diff --git a/requirements.txt b/requirements.txt index 90e4957d..4ad45b2f 100644 --- a/requirements.txt +++ b/requirements.txt @@ -8,7 +8,7 @@ pytorch-lightning>=2.4 gradio<5 ffmpeg-python onnxruntime; platform_machine == "aarch64" or platform_machine == "arm64" -onnxruntime-gpu; platform_machine == "x86_64" or platform_machine == "AMD64" +onnxruntime-gpu; platform_machine == "x86_64" or platform_machine == "amd64" tqdm funasr==1.0.27 cn2an diff --git a/tools/subfix.py b/tools/subfix.py new file mode 100644 index 00000000..6ef91423 --- /dev/null +++ b/tools/subfix.py @@ -0,0 +1,544 @@ +import datetime +import os +import threading +import traceback +from dataclasses import dataclass +from functools import partial +from typing import List + +import click +import gradio as gr +import librosa +import numpy as np +import soundfile +from gradio.components.audio import WaveformOptions + +from tools.i18n.i18n import I18nAuto + +PARTIAL_EXIT = partial(os._exit, 0) + +LANGUAGE_MAP: dict = { + "ZH": "ZH", + "zh": "ZH", + "JP": "JA", + "jp": "JA", + "JA": "JA", + "ja": "JA", + "EN": "EN", + "en": "EN", + "KO": "KO", + "ko": "KO", + "yue": "YUE", + "YUE": "YUE", +} + +LOCK = threading.Lock() + +IS_CLI = True + + +@dataclass +class SubfixErr: + error: Exception + tracebacks: str + + +class Subfix: + batch_size: int = 2 + cur_idx: int = 0 + list_path: str + textboxes: List[gr.Textbox] = [] + audios: List[gr.Audio] = [] + languages: List[gr.Dropdown] = [] + selections: List[gr.Checkbox] = [] + transcriptions_list: List[List[str]] = [] + + merge_audio_button: gr.Button + delete_audio_button: gr.Button + previous_index_button1: gr.Button + next_index_button1: gr.Button + previous_index_button2: gr.Button + next_index_button2: gr.Button + index_slider: gr.Slider + batch_size_slider: gr.Slider + close_button: gr.Button + + def __init__(self, i18n: I18nAuto): + self.i18n = i18n + with gr.Row(equal_height=True): + with gr.Column(scale=2, min_width=160): + self.index_slider = gr.Slider(minimum=0, maximum=1, step=1, label=i18n("音频索引")) + with gr.Column(scale=1, min_width=160): + self.previous_index_button1 = gr.Button(value=i18n("上一页"), elem_id="btn_previous") + with gr.Column(scale=1, min_width=160): + self.next_index_button1 = gr.Button(value=i18n("下一页"), elem_id="btn_next") + with gr.Row(equal_height=True): + with gr.Column(scale=2, min_width=160): + self.batch_size_slider = gr.Slider( + minimum=4, maximum=20, step=2, value=self.batch_size, label=i18n("每页音频条数") + ) + with gr.Column(scale=1, min_width=160): + self.merge_audio_button = gr.Button(value=i18n("合并选中音频")) + with gr.Column(scale=1, min_width=160): + self.delete_audio_button = gr.Button(value=i18n("删除选中音频")) + gr.render( + inputs=[self.index_slider, self.batch_size_slider], + triggers=[self.batch_size_slider.change], + )(self._render_text_area) + + @property + def max_index(self): + return len(self.transcriptions_list) - 1 + + def load_list(self, list_path: str): + with open(list_path, mode="r", encoding="utf-8") as f: + list_data = f.readlines() + for idx, transcriptions in enumerate(list_data): + data = transcriptions.split("|") + if len(data) != 4: + print(f"Error Line {idx + 1}: {'|'.join(data)}") + continue + audio_name, audio_folder, text_language, text = data + self.transcriptions_list.append( + [ + audio_name, + audio_folder, + LANGUAGE_MAP.get(text_language.upper(), text_language.upper()), + text.strip("\n").strip(), + ] + ) + self.list_path = list_path + + def save_list(self): + data = [] + for transcriptions in self.transcriptions_list: + data.append("|".join(transcriptions)) + try: + with open(self.list_path, mode="w", encoding="utf-8") as f: + f.write("\n".join(data)) + except Exception as e: + return SubfixErr(e, traceback.format_exc()) + + def change_index(self, index: int): + audios = [] + texts = [] + languages = [] + checkboxs = [] + with LOCK: + for i in range(index, index + self.batch_size): + if i <= self.max_index: + audios.append(gr.Audio(value=self.transcriptions_list[i][0])) + texts.append(gr.Textbox(value=self.transcriptions_list[i][3], label=self.i18n("Text") + f" {i}")) + languages.append(gr.Dropdown(value=self.transcriptions_list[i][2])) + else: + audios.append(gr.Audio(value=None, interactive=False)) + texts.append(gr.Textbox(value=None, label=self.i18n("Text") + f" {i}", interactive=False)) + languages.append(gr.Dropdown(value=None, interactive=False)) + checkboxs = [gr.Checkbox(False) for i in range(self.batch_size)] + self.cur_idx = index + return *audios, *texts, *languages, *checkboxs + + def next_page(self, index: int): + batch_size = self.batch_size + max_index = max(self.max_index - batch_size + 1, 0) + index = min(index + batch_size, max_index) + return gr.Slider(value=index), *self.change_index(index) + + def previous_page(self, index: int): + batch_size = self.batch_size + index = max(index - batch_size, 0) + return gr.Slider(value=index), *self.change_index(index) + + def delete_audio(self, index, *selected): + delete_index = [i + index for i, _ in enumerate(selected) if _] + delete_index = [i for i in delete_index if i < self.max_index] + for idx in delete_index[::-1]: + self.transcriptions_list.pop(idx) + self.save_list() + return gr.Slider(value=index, maximum=self.max_index), *self.change_index(index) + + def submit(self, *input): + with LOCK: + index = self.cur_idx + batch_size = self.batch_size + texts = input[: len(input) // 2] + languages = input[len(input) // 2 :] + if texts is None or languages is None: + raise ValueError() + print(index, min(index + batch_size, self.max_index)) + for idx in range(index, min(index + batch_size, self.max_index + 1)): + self.transcriptions_list[idx][3] = texts[idx - index].strip().strip("\n") + self.transcriptions_list[idx][2] = languages[idx - index] + result = self.save_list() + if isinstance(result, SubfixErr): + gr.Warning(str(result.error)) + print(result.tracebacks) + + def merge_audio(self, index, *selected): + batch_size = self.batch_size + merge_index = [i + index for i, _ in enumerate(selected) if _] + merge_index = [i for i in merge_index if i < self.max_index] + if len(merge_index) < 2: + return *(gr.skip() for _ in range(batch_size * 3 + 1)), *(gr.Checkbox(False) for _ in range(batch_size)) + else: + merge_texts = [] + merge_audios = [] + first_itm_index = merge_index[0] + first_itm_path = f"{os.path.splitext(self.transcriptions_list[first_itm_index][0])[0]}_{str(datetime.datetime.now().strftime(r'%Y%m%d_%H%M%S'))}.wav" + final_audio_list = [] + for idx in merge_index: + merge_texts.append(self.transcriptions_list[idx][3]) + merge_audios.append(self.transcriptions_list[idx][0]) + for idx in merge_index[:0:-1]: + self.transcriptions_list.pop(idx) + for audio_path in merge_audios: + final_audio_list.append(librosa.load(audio_path, sr=32000, mono=True)[0]) + final_audio_list.append(np.zeros(int(32000 * 0.3))) + final_audio_list.pop() + final_audio = np.concatenate(final_audio_list) + soundfile.write(first_itm_path, final_audio, 32000) + self.transcriptions_list[first_itm_index][0] = first_itm_path + self.transcriptions_list[first_itm_index][3] = ",".join(merge_texts) + return gr.Slider(maximum=self.max_index), *self.change_index(index) + + def _render_text_area(self, index, batch_size): + i18n = self.i18n + self.textboxes = [] + self.audios = [] + self.languages = [] + self.selections = [] + self.batch_size = batch_size + for i in range(index, index + batch_size): + with gr.Row(equal_height=True): + if i <= self.max_index: + with gr.Column(scale=2, min_width=160): + textbox_tmp = gr.Textbox( + value=self.transcriptions_list[i][3], + label=i18n("Text") + f" {i}", + lines=2, + max_lines=3, + interactive=True, + ) + with gr.Column(scale=1, min_width=160): + audio_tmp = gr.Audio( + value=self.transcriptions_list[i][0], + show_label=False, + show_download_button=False, + editable=False, + waveform_options={"show_recording_waveform": False, "show_controls": False}, + ) + with gr.Column(scale=1, min_width=160): + with gr.Group(): + with gr.Row(): + language_tmp = gr.Dropdown( + choices=["ZH", "EN", "JA", "KO", "YUE"], + value=self.transcriptions_list[i][2], + allow_custom_value=True, + label=i18n("文本语言"), + interactive=True, + ) + with gr.Row(): + selection_tmp = gr.Checkbox( + label=i18n("选择音频"), + ) + else: + with gr.Column(scale=2, min_width=160): + textbox_tmp = gr.Textbox( + label=i18n("Text") + f" {i}", + lines=2, + max_lines=3, + elem_id="subfix_textbox", + interactive=False, + ) + with gr.Column(scale=1, min_width=160): + audio_tmp = gr.Audio( + streaming=True, + show_label=False, + show_download_button=False, + interactive=False, + waveform_options=WaveformOptions(show_recording_waveform=False, show_controls=False), + ) + with gr.Column(scale=1, min_width=160): + with gr.Group(): + with gr.Row(): + language_tmp = gr.Dropdown( + choices=["ZH", "EN", "JA", "KO", "YUE"], + value=None, + allow_custom_value=True, + label=i18n("文本语言"), + interactive=False, + ) + with gr.Row(): + selection_tmp = gr.Checkbox( + label=i18n("选择音频"), + interactive=False, + ) + + self.textboxes.append(textbox_tmp) + self.audios.append(audio_tmp) + self.languages.append(language_tmp) + self.selections.append(selection_tmp) + with gr.Row(equal_height=True): + with gr.Column(scale=2, min_width=160): + self.close_button = gr.Button(value=i18n("保存并关闭打标WebUI"), variant="stop") + with gr.Column(scale=1, min_width=160): + self.previous_index_button2 = gr.Button(value=i18n("上一页")) + with gr.Column(scale=1, min_width=160): + self.next_index_button2 = gr.Button(value=i18n("下一页")) + + # Event Trigger Binding + + self.index_slider.release( # Change Index Button + fn=self.submit, + inputs=[ + *self.textboxes, + *self.languages, + ], + outputs=[], + ).success( + fn=self.change_index, + inputs=[ + self.index_slider, + ], + outputs=[ + *self.audios, + *self.textboxes, + *self.languages, + *self.selections, + ], + max_batch_size=1, + trigger_mode="once", + ) + + self.next_index_button1.click( # Next Page Button on the Top + fn=self.submit, + inputs=[ + *self.textboxes, + *self.languages, + ], + outputs=[], + ).success( + fn=self.next_page, + inputs=[ + self.index_slider, + ], + outputs=[ + self.index_slider, + *self.audios, + *self.textboxes, + *self.languages, + *self.selections, + ], + scroll_to_output=True, + trigger_mode="once", + ) + + self.next_index_button2.click( # Next Page Button on the Bottom, Binding to Next Page Button on the Top + lambda: None, + [], + [], + js=""" + () => { + document.getElementById("btn_next").click(); + }""", + trigger_mode="once", + ) + + self.previous_index_button1.click( # Previous Page Button on the Top + fn=self.submit, + inputs=[ + *self.textboxes, + *self.languages, + ], + outputs=[], + ).success( + fn=self.previous_page, + inputs=[ + self.index_slider, + ], + outputs=[ + self.index_slider, + *self.audios, + *self.textboxes, + *self.languages, + *self.selections, + ], + scroll_to_output=True, + trigger_mode="once", + ) + + self.previous_index_button2.click( # Previous Page Button on the Bottom, Binding to Previous Page Button on the Top + lambda: None, + [], + [], + js=""" + () => { + document.getElementById("btn_previous").click(); + }""", + trigger_mode="once", + ) + + self.delete_audio_button.click( # Delete the Audio in the Transcription File + fn=self.submit, + inputs=[ + *self.textboxes, + *self.languages, + ], + outputs=[], + ).success( + fn=self.delete_audio, + inputs=[ + self.index_slider, + *self.selections, + ], + outputs=[ + self.index_slider, + *self.audios, + *self.textboxes, + *self.languages, + *self.selections, + ], + scroll_to_output=True, + ).success( + fn=self.submit, + inputs=[ + *self.textboxes, + *self.languages, + ], + outputs=[], + show_progress="hidden", + ) + + self.merge_audio_button.click( # Delete the Audio in the Transcription File + fn=self.submit, + inputs=[ + *self.textboxes, + *self.languages, + ], + outputs=[], + ).success( + fn=self.merge_audio, + inputs=[ + self.index_slider, + *self.selections, + ], + outputs=[ + self.index_slider, + *self.audios, + *self.textboxes, + *self.languages, + *self.selections, + ], + scroll_to_output=True, + ).success( + fn=self.submit, + inputs=[ + *self.textboxes, + *self.languages, + ], + outputs=[], + show_progress="hidden", + ) + if not IS_CLI: + self.close_button.click( # Close the Subfix Tab, Binding to Close Button on Audio Processing Tab + fn=lambda: None, + inputs=[], + outputs=[], + js=""" + () => { + document.getElementById("btn_close").click(); + }""", + trigger_mode="once", + ) + else: + self.close_button.click( # Close the Subfix Tab, Binding to Close Button on Audio Processing Tab + fn=self.submit, + inputs=[ + *self.textboxes, + *self.languages, + ], + outputs=[], + trigger_mode="once", + ).then( + fn=PARTIAL_EXIT, + inputs=[], + outputs=[], + ) + + def render(self, list_path: str, batch_size: int = 10): + self.batch_size = batch_size + self.transcriptions_list = [] + self.load_list(list_path=list_path) + + +@click.command(name="subfix") +@click.argument( + "list-path", + metavar="", + type=click.Path(exists=True, dir_okay=False, readable=True, writable=True), + required=True, +) +@click.option( + "--i18n-lang", + type=str, + default="Auto", + help="Languages for internationalisation", + show_default=True, +) +@click.option( + "--port", + type=int, + default="9871", + show_default=True, +) +@click.option( + "--share", + type=bool, + default=False, + show_default=True, +) +def main(list_path: str = "", i18n_lang="Auto", port=9871, share=False): + """Web-Based audio subtitle editing and multilingual annotation Tool + + Accept a transcription list path to launch a Gradio WebUI for text editing + """ + + with gr.Blocks(analytics_enabled=False) as app: + subfix = Subfix(I18nAuto(i18n_lang)) + subfix.render(list_path=list_path) + if subfix.max_index >= 0: + timer = gr.Timer(0.1) + + timer.tick( + fn=lambda: ( + gr.Slider(value=0, maximum=subfix.max_index, step=1), + gr.Slider(value=10), + gr.Timer(active=False), + ), + inputs=[], + outputs=[ + subfix.index_slider, + subfix.batch_size_slider, + timer, + ], + ) + else: + timer = gr.Timer(2) + + timer.tick( + fn=lambda x: (_ for _ in ()).throw(gr.Error("Invalid List")) if x is None else None, + inputs=[], + outputs=[], + ) + app.queue().launch( + server_name="0.0.0.0", + inbrowser=True, + share=share, + server_port=port, + quiet=False, + show_api=False, + ) + + +if __name__ == "__main__": + main() diff --git a/tools/subfix_webui.py b/tools/subfix_webui.py deleted file mode 100644 index 3f2fd03e..00000000 --- a/tools/subfix_webui.py +++ /dev/null @@ -1,422 +0,0 @@ -import sys -from tools.i18n.i18n import I18nAuto, scan_language_list -language = sys.argv[-1] if sys.argv[-1] in scan_language_list() else "Auto" -i18n = I18nAuto(language=language) -import argparse -import copy -import json -import os -import uuid - -try: - import gradio.analytics as analytics - - analytics.version_check = lambda: None -except: - ... - -import gradio as gr -import librosa -import numpy as np -import soundfile - -g_json_key_text = "" -g_json_key_path = "" -g_load_file = "" -g_load_format = "" - -g_max_json_index = 0 -g_index = 0 -g_batch = 10 -g_text_list = [] -g_audio_list = [] -g_checkbox_list = [] -g_data_json = [] - - -def reload_data(index, batch): - global g_index - g_index = index - global g_batch - g_batch = batch - datas = g_data_json[index : index + batch] - output = [] - for d in datas: - output.append({g_json_key_text: d[g_json_key_text], g_json_key_path: d[g_json_key_path]}) - return output - - -def b_change_index(index, batch): - global g_index, g_batch - g_index, g_batch = index, batch - datas = reload_data(index, batch) - output = [] - for i, _ in enumerate(datas): - output.append( - # gr.Textbox( - # label=f"Text {i+index}", - # value=_[g_json_key_text]#text - # ) - {"__type__": "update", "label": f"Text {i + index}", "value": _[g_json_key_text]} - ) - for _ in range(g_batch - len(datas)): - output.append( - # gr.Textbox( - # label=f"Text", - # value="" - # ) - {"__type__": "update", "label": "Text", "value": ""} - ) - for _ in datas: - output.append(_[g_json_key_path]) - for _ in range(g_batch - len(datas)): - output.append(None) - for _ in range(g_batch): - output.append(False) - return output - - -def b_next_index(index, batch): - b_save_file() - if (index + batch) <= g_max_json_index: - return index + batch, *b_change_index(index + batch, batch) - else: - return index, *b_change_index(index, batch) - - -def b_previous_index(index, batch): - b_save_file() - if (index - batch) >= 0: - return index - batch, *b_change_index(index - batch, batch) - else: - return 0, *b_change_index(0, batch) - - -def b_submit_change(*text_list): - global g_data_json - change = False - for i, new_text in enumerate(text_list): - if g_index + i <= g_max_json_index: - new_text = new_text.strip() + " " - if g_data_json[g_index + i][g_json_key_text] != new_text: - g_data_json[g_index + i][g_json_key_text] = new_text - change = True - if change: - b_save_file() - return g_index, *b_change_index(g_index, g_batch) - - -def b_delete_audio(*checkbox_list): - global g_data_json, g_index, g_max_json_index - b_save_file() - change = False - for i, checkbox in reversed(list(enumerate(checkbox_list))): - if g_index + i < len(g_data_json): - if checkbox == True: - g_data_json.pop(g_index + i) - change = True - - g_max_json_index = len(g_data_json) - 1 - if g_index > g_max_json_index: - g_index = g_max_json_index - g_index = g_index if g_index >= 0 else 0 - if change: - b_save_file() - # return gr.Slider(value=g_index, maximum=(g_max_json_index if g_max_json_index>=0 else 0)), *b_change_index(g_index, g_batch) - return { - "value": g_index, - "__type__": "update", - "maximum": (g_max_json_index if g_max_json_index >= 0 else 0), - }, *b_change_index(g_index, g_batch) - - -def b_invert_selection(*checkbox_list): - new_list = [not item if item is True else True for item in checkbox_list] - return new_list - - -def get_next_path(filename): - base_dir = os.path.dirname(filename) - base_name = os.path.splitext(os.path.basename(filename))[0] - for i in range(100): - new_path = os.path.join(base_dir, f"{base_name}_{str(i).zfill(2)}.wav") - if not os.path.exists(new_path): - return new_path - return os.path.join(base_dir, f"{str(uuid.uuid4())}.wav") - - -def b_audio_split(audio_breakpoint, *checkbox_list): - global g_data_json, g_max_json_index - checked_index = [] - for i, checkbox in enumerate(checkbox_list): - if checkbox == True and g_index + i < len(g_data_json): - checked_index.append(g_index + i) - if len(checked_index) == 1: - index = checked_index[0] - audio_json = copy.deepcopy(g_data_json[index]) - path = audio_json[g_json_key_path] - data, sample_rate = librosa.load(path, sr=None, mono=True) - audio_maxframe = len(data) - break_frame = int(audio_breakpoint * sample_rate) - - if break_frame >= 1 and break_frame < audio_maxframe: - audio_first = data[0:break_frame] - audio_second = data[break_frame:] - nextpath = get_next_path(path) - soundfile.write(nextpath, audio_second, sample_rate) - soundfile.write(path, audio_first, sample_rate) - g_data_json.insert(index + 1, audio_json) - g_data_json[index + 1][g_json_key_path] = nextpath - b_save_file() - - g_max_json_index = len(g_data_json) - 1 - # return gr.Slider(value=g_index, maximum=g_max_json_index), *b_change_index(g_index, g_batch) - return {"value": g_index, "maximum": g_max_json_index, "__type__": "update"}, *b_change_index(g_index, g_batch) - - -def b_merge_audio(interval_r, *checkbox_list): - global g_data_json, g_max_json_index - b_save_file() - checked_index = [] - audios_path = [] - audios_text = [] - for i, checkbox in enumerate(checkbox_list): - if checkbox == True and g_index + i < len(g_data_json): - checked_index.append(g_index + i) - - if len(checked_index) > 1: - for i in checked_index: - audios_path.append(g_data_json[i][g_json_key_path]) - audios_text.append(g_data_json[i][g_json_key_text]) - for i in reversed(checked_index[1:]): - g_data_json.pop(i) - - base_index = checked_index[0] - base_path = audios_path[0] - g_data_json[base_index][g_json_key_text] = "".join(audios_text) - - audio_list = [] - l_sample_rate = None - for i, path in enumerate(audios_path): - data, sample_rate = librosa.load(path, sr=l_sample_rate, mono=True) - l_sample_rate = sample_rate - if i > 0: - silence = np.zeros(int(l_sample_rate * interval_r)) - audio_list.append(silence) - - audio_list.append(data) - - audio_concat = np.concatenate(audio_list) - - soundfile.write(base_path, audio_concat, l_sample_rate) - - b_save_file() - - g_max_json_index = len(g_data_json) - 1 - - # return gr.Slider(value=g_index, maximum=g_max_json_index), *b_change_index(g_index, g_batch) - return {"value": g_index, "maximum": g_max_json_index, "__type__": "update"}, *b_change_index(g_index, g_batch) - - -def b_save_json(): - with open(g_load_file, "w", encoding="utf-8") as file: - for data in g_data_json: - file.write(f"{json.dumps(data, ensure_ascii=False)}\n") - - -def b_save_list(): - with open(g_load_file, "w", encoding="utf-8") as file: - for data in g_data_json: - wav_path = data["wav_path"] - speaker_name = data["speaker_name"] - language = data["language"] - text = data["text"] - file.write(f"{wav_path}|{speaker_name}|{language}|{text}".strip() + "\n") - - -def b_load_json(): - global g_data_json, g_max_json_index - with open(g_load_file, "r", encoding="utf-8") as file: - g_data_json = file.readlines() - g_data_json = [json.loads(line) for line in g_data_json] - g_max_json_index = len(g_data_json) - 1 - - -def b_load_list(): - global g_data_json, g_max_json_index - with open(g_load_file, "r", encoding="utf-8") as source: - data_list = source.readlines() - for _ in data_list: - data = _.split("|") - if len(data) == 4: - wav_path, speaker_name, language, text = data - g_data_json.append( - {"wav_path": wav_path, "speaker_name": speaker_name, "language": language, "text": text.strip()} - ) - else: - print("error line:", data) - g_max_json_index = len(g_data_json) - 1 - - -def b_save_file(): - if g_load_format == "json": - b_save_json() - elif g_load_format == "list": - b_save_list() - - -def b_load_file(): - if g_load_format == "json": - b_load_json() - elif g_load_format == "list": - b_load_list() - - -def set_global(load_json, load_list, json_key_text, json_key_path, batch): - global g_json_key_text, g_json_key_path, g_load_file, g_load_format, g_batch - - g_batch = int(batch) - - if load_json != "None": - g_load_format = "json" - g_load_file = load_json - elif load_list != "None": - g_load_format = "list" - g_load_file = load_list - else: - g_load_format = "list" - g_load_file = "demo.list" - - g_json_key_text = json_key_text - g_json_key_path = json_key_path - - b_load_file() - - -if __name__ == "__main__": - parser = argparse.ArgumentParser(description="Process some integers.") - parser.add_argument("--load_json", default="None", help="source file, like demo.json") - parser.add_argument("--is_share", default="False", help="whether webui is_share=True") - parser.add_argument("--load_list", default="None", help="source file, like demo.list") - parser.add_argument("--webui_port_subfix", default=9871, help="source file, like demo.list") - parser.add_argument("--json_key_text", default="text", help="the text key name in json, Default: text") - parser.add_argument("--json_key_path", default="wav_path", help="the path key name in json, Default: wav_path") - parser.add_argument("--g_batch", default=10, help="max number g_batch wav to display, Default: 10") - - args = parser.parse_args() - - set_global(args.load_json, args.load_list, args.json_key_text, args.json_key_path, args.g_batch) - - with gr.Blocks(analytics_enabled=False) as demo: - gr.Markdown( - value=i18n("Submit Text: 将当前页所有文本框内容手工保存到内存和文件(翻页前后或者退出标注页面前如果没点这个按钮,你再翻回来就回滚了,白忙活。)") - ) - with gr.Row(): - btn_change_index = gr.Button("Change Index") - btn_submit_change = gr.Button("Submit Text") - btn_merge_audio = gr.Button("Merge Audio") - btn_delete_audio = gr.Button("Delete Audio") - btn_previous_index = gr.Button("Previous Index") - btn_next_index = gr.Button("Next Index") - - with gr.Row(): - index_slider = gr.Slider(minimum=0, maximum=g_max_json_index, value=g_index, step=1, label="Index", scale=3) - splitpoint_slider = gr.Slider( - minimum=0, maximum=120.0, value=0, step=0.1, label="Audio Split Point(s)", scale=3 - ) - btn_audio_split = gr.Button("Split Audio", scale=1) - btn_save_json = gr.Button("Save File", visible=True, scale=1) - btn_invert_selection = gr.Button("Invert Selection", scale=1) - - with gr.Row(): - with gr.Column(): - for _ in range(0, g_batch): - with gr.Row(): - text = gr.Textbox(label="Text", visible=True, scale=5) - audio_output = gr.Audio(label="Output Audio", visible=True, scale=5) - audio_check = gr.Checkbox(label="Yes", show_label=True, info="Choose Audio", scale=1) - g_text_list.append(text) - g_audio_list.append(audio_output) - g_checkbox_list.append(audio_check) - - with gr.Row(): - batchsize_slider = gr.Slider( - minimum=1, maximum=g_batch, value=g_batch, step=1, label="Batch Size", scale=3, interactive=False - ) - interval_slider = gr.Slider(minimum=0, maximum=2, value=0, step=0.01, label="Interval", scale=3) - btn_theme_dark = gr.Button("Light Theme", link="?__theme=light", scale=1) - btn_theme_light = gr.Button("Dark Theme", link="?__theme=dark", scale=1) - - btn_change_index.click( - b_change_index, - inputs=[ - index_slider, - batchsize_slider, - ], - outputs=[*g_text_list, *g_audio_list, *g_checkbox_list], - ) - - btn_submit_change.click( - b_submit_change, - inputs=[ - *g_text_list, - ], - outputs=[index_slider, *g_text_list, *g_audio_list, *g_checkbox_list], - ) - - btn_previous_index.click( - b_previous_index, - inputs=[ - index_slider, - batchsize_slider, - ], - outputs=[index_slider, *g_text_list, *g_audio_list, *g_checkbox_list], - ) - - btn_next_index.click( - b_next_index, - inputs=[ - index_slider, - batchsize_slider, - ], - outputs=[index_slider, *g_text_list, *g_audio_list, *g_checkbox_list], - ) - - btn_delete_audio.click( - b_delete_audio, - inputs=[*g_checkbox_list], - outputs=[index_slider, *g_text_list, *g_audio_list, *g_checkbox_list], - ) - - btn_merge_audio.click( - b_merge_audio, - inputs=[interval_slider, *g_checkbox_list], - outputs=[index_slider, *g_text_list, *g_audio_list, *g_checkbox_list], - ) - - btn_audio_split.click( - b_audio_split, - inputs=[splitpoint_slider, *g_checkbox_list], - outputs=[index_slider, *g_text_list, *g_audio_list, *g_checkbox_list], - ) - - btn_invert_selection.click(b_invert_selection, inputs=[*g_checkbox_list], outputs=[*g_checkbox_list]) - - btn_save_json.click(b_save_file) - - demo.load( - b_change_index, - inputs=[ - index_slider, - batchsize_slider, - ], - outputs=[*g_text_list, *g_audio_list, *g_checkbox_list], - ) - - demo.launch( - server_name="0.0.0.0", - inbrowser=True, - # quiet=True, - share=eval(args.is_share), - server_port=int(args.webui_port_subfix), - ) diff --git a/tools/uvr5/webui.py b/tools/uvr5/webui.py index f5f8d3f6..2ca642aa 100644 --- a/tools/uvr5/webui.py +++ b/tools/uvr5/webui.py @@ -1,23 +1,22 @@ import logging import os +import sys import traceback -import gradio as gr - -from tools.i18n.i18n import I18nAuto -from tools.my_utils import clean_path - -i18n = I18nAuto() - -logger = logging.getLogger(__name__) -import sys - import ffmpeg +import gradio as gr import torch from bsroformer import Roformer_Loader from mdxnet import MDXNetDereverb from vr import AudioPre, AudioPreDeEcho +from tools.i18n.i18n import I18nAuto +from tools.my_utils import clean_path, load_cudnn + +i18n = I18nAuto() + +logger = logging.getLogger(__name__) + weight_uvr5_root = "tools/uvr5/uvr5_weights" uvr5_names = [] for name in os.listdir(weight_uvr5_root): @@ -44,6 +43,7 @@ def html_center(text, label="p"): def uvr(model_name, inp_root, save_root_vocal, paths, save_root_ins, agg, format0): infos = [] + load_cudnn() try: inp_root = clean_path(inp_root) save_root_vocal = clean_path(save_root_vocal) @@ -220,5 +220,6 @@ app.queue().launch( # concurrency_count=511, max_size=1022 inbrowser=True, share=is_share, server_port=webui_port_uvr5, + show_api=False, # quiet=True, ) diff --git a/webui.py b/webui.py index 6434e40e..ff226074 100644 --- a/webui.py +++ b/webui.py @@ -62,8 +62,11 @@ for site_packages_root in site_packages_roots: traceback.print_exc() import shutil import subprocess +from multiprocessing import cpu_count from subprocess import Popen +import gradio as gr + from config import ( exp_root, infer_device, @@ -77,22 +80,11 @@ from config import ( ) from tools import my_utils from tools.i18n.i18n import I18nAuto, scan_language_list +from tools.my_utils import check_details, check_for_existance language = sys.argv[-1] if sys.argv[-1] in scan_language_list() else "Auto" os.environ["language"] = language i18n = I18nAuto(language=language) -from multiprocessing import cpu_count - -from tools.my_utils import check_details, check_for_existance - -# os.environ['PYTORCH_ENABLE_MPS_FALLBACK'] = '1' # 当遇到mps不支持的步骤时使用cpu -try: - import gradio.analytics as analytics - - analytics.version_check = lambda: None -except: - ... -import gradio as gr n_cpu = cpu_count() @@ -384,12 +376,7 @@ def change_label(path_list): if p_label is None: check_for_existance([path_list]) path_list = my_utils.clean_path(path_list) - cmd = '"%s" -s tools/subfix_webui.py --load_list "%s" --webui_port %s --is_share %s' % ( - python_exec, - path_list, - webui_port_subfix, - is_share, - ) + cmd = f'"{python_exec}" -s tools/subfix.py --i18n-lang {language} --port {webui_port_subfix} --share {is_share} "{path_list}"' yield ( process_info(process_name_subfix, "opened"), {"__type__": "update", "visible": False}, @@ -1419,7 +1406,7 @@ with gr.Blocks(title="GPT-SoVITS WebUI", analytics_enabled=False) as app: value=process_info(process_name_slice, "close"), variant="primary", visible=False ) - gr.Markdown(value="0bb-" + i18n("语音降噪工具")+i18n("(不稳定,先别用,可能劣化模型效果!)")) + gr.Markdown(value="0bb-" + i18n("语音降噪工具") + i18n("(不稳定,先别用,可能劣化模型效果!)")) with gr.Row(): with gr.Column(scale=3): with gr.Row(): @@ -1981,5 +1968,6 @@ with gr.Blocks(title="GPT-SoVITS WebUI", analytics_enabled=False) as app: inbrowser=True, share=is_share, server_port=webui_port_main, + show_api=False, # quiet=True, )