Compare commits

...
Author SHA1 Message Date
erew123 7c7cb72c13 Updated to deal with Gradio 4.xx versions 2024-05-14 21:01:38 +01:00
erew123 3a105ce922 Update tts_server.py
Set API Local to use float as Transformers demands it
2024-05-14 18:19:29 +01:00
erew123 690018c4ab Test code rework to allow custom file names.
Contains a box to add a custom file name. Files are generated in the format {custom-file-name}_{ID_number_XXXXX}_{uuid}

This allows up to 99,999 files generated in 1x single session. 

The uuid needs to be applied to ensure files are not over-written and handle browser based caching of wav files.
2024-05-12 14:00:10 +01:00
erew123 6808439ca1 nailed text-gen-webui spacy version
Added spacy>=3.7.2 to stop the requirements installation hunting around for Spacy versions so much.

Tested this on a fresh install of Text-gen-webui and it ran through without issue.
2024-05-06 23:40:39 +01:00
erew123 8cf56faad2 run dos2unix to repair crlf to lf 2024-05-06 01:39:01 +01:00
erew123 cb7d486288 run dos2unix to repair crlf to lf 2024-05-06 01:38:04 +01:00
erew123 086999fc1b Update atsetup.sh 2024-05-02 23:26:42 +01:00
erew123 a1b889108f Faster narration generation w/low vram
Moved the memory management of the TTS model into the narrator so that it doesnt shift the model backwards and forwards VRAM<>System ram on each portion of the generation. Only the beginning and end. Only benefits when Low VRAM is enabled, but should shave a few seconds of generation of your average generation.
2024-05-01 16:09:59 +01:00
erew123 9f18b24465 Changed how dvae weights are copied. 2024-05-01 02:38:24 +01:00
erew123 a3f157249c Small change to splitting of text 2024-04-28 21:14:25 +01:00
erew123 eb08d31509 Added Streaming Stopping
Currently this is in testing. 

curl -X POST http://localhost:7851/api/stop-generation

Would stop the current streaming generation.

This is more a v2 feature that is just getting tried out by someone else ATM.
2024-04-28 20:37:41 +01:00
erew123 1e1b7fdd8c Merge pull request #190 from ytt246/main
Edited text cleaning to support some commonly used fullwidth/CJK punctuations
2024-04-28 11:12:32 +01:00
IAUSHYJ 5aad7bcc45 tts generater split fix 2024-04-28 00:15:29 +04:00
erew123 dd52597f49 Grad acc steps min setting 2024-04-26 20:50:52 +01:00
erew123 5f836e08b8 Various updates
additional documentation
learning rate selectable
custom folder paths for saving model
selectable model on saving
2024-04-26 15:30:53 +01:00
IAUSHYJ b89b6cdc1d small fix 2024-04-26 13:23:23 +04:00
IAUSHYJ 558aed1888 stop cleaning common chinese punctuations 2024-04-26 03:40:20 +04:00
erew123 5eab3d28df Add files via upload 2024-04-25 21:25:29 +01:00
erew123 fd76f496a0 re-jigged language support/filters 2024-04-25 21:24:44 +01:00
erew123 4d9b44d9a0 Update tts_generator.html 2024-04-25 18:06:49 +01:00
erew123 951df5a239 Update atsetup.bat 2024-04-24 18:40:38 +01:00
erew123 0a35ee2310 Update atsetup.bat 2024-04-24 18:38:32 +01:00
erew123 efeb2e67e0 Add files via upload 2024-04-20 14:54:33 +01:00
erew123 131f14de76 Add files via upload 2024-04-20 14:49:03 +01:00
erew123 4e67931fe9 bug fix 2024-04-20 10:28:18 +01:00
erew123 785525327d Update README.md 2024-04-18 20:07:20 +01:00
erew123 d4647573cd Add files via upload 2024-04-18 19:58:31 +01:00
erew123 c3895b602c Hindi Support 2024-04-18 18:43:25 +01:00
erew123 f18eb9a4a0 Hindi Support 2024-04-18 18:39:10 +01:00
erew123 2834778e8f Added Hindi finetune.py 2024-04-18 18:32:10 +01:00
erew123 2f42c69d25 Update README.md 2024-04-18 15:57:32 +01:00
erew123 8c65f026e6 ST AllTalk FT Model
changed a value for detecting FT model.
2024-04-09 01:17:03 +01:00
erew123 fff01107c8 Update tts_server.py - API request console output 2024-04-06 11:45:32 +01:00
erew123 0bdc66f6e5 Add files via upload 2024-04-05 12:28:59 +01:00
erew123 1c3fc86085 Delete system/at_admin/textnotinside-min.jpg 2024-04-05 12:28:38 +01:00
erew123 d9eb0876a6 Delete system/at_admin/pythonrequirementswindows-min.jpg 2024-04-05 12:28:30 +01:00
erew123 9b4130a8eb Delete system/at_admin/pythonenvironment-min.jpg 2024-04-05 12:28:22 +01:00
erew123 8fb5b4f695 Delete system/at_admin/deepspeedexample-min.jpg 2024-04-05 12:28:14 +01:00
erew123 65e306a198 Delete system/at_admin/atandsdplugin-min.jpg 2024-04-05 12:28:07 +01:00
erew123 3dabb63656 Delete system/at_admin/4abed1ca-min.jpg 2024-04-05 12:27:59 +01:00
erew123 241fa91473 Delete system/at_admin/3c6fde566d6a-min.jpg 2024-04-05 12:27:50 +01:00
erew123 2a5c8dd58a Add files via upload 2024-04-05 12:17:28 +01:00
erew123 2bfa10d7be Fix Windows 10 issue with path/environment. 2024-04-05 12:07:46 +01:00
erew123 3501e2e2f3 Fix crunker bug over networks
Crunker had an update to resolve bad performance over networks. This would affect only the TTS Generator when used remotely from AllTalk.
2024-04-05 10:26:33 +01:00
erew123 679e3a0982 Update atsetup.bat
encapsulate SET path
2024-04-04 19:11:59 +01:00
erew123 35d05c7689 Add files via upload 2024-04-04 11:07:21 +01:00
erew123 084cc2d2a2 Add files via upload 2024-04-04 11:02:59 +01:00
erew123 a96c82ff01 Add files via upload 2024-04-04 10:58:35 +01:00
erew123 aa7ff1940a Add files via upload 2024-04-04 10:30:45 +01:00
erew123 58465ae14e Update Dockerfile 2024-04-04 10:25:33 +01:00
erew123 2f49f61645 Add files via upload 2024-04-04 10:16:30 +01:00
erew123 58a70caebc Update README.md 2024-04-04 01:58:04 +01:00
erew123 d0d470db0f Update README.md 2024-04-04 01:57:02 +01:00
erew123 068fc72346 Update README.md 2024-04-04 01:55:10 +01:00
erew123 fc5c8c748f Standalone reminder re scripts 2024-04-04 00:39:20 +01:00
erew123 24f86d08b7 Update README.md 2024-04-03 11:10:40 +01:00
erew123 99e402664c Update feature_request.md 2024-04-02 16:57:21 +01:00
erew123 24cf9eae59 Update bug-report-support-request.md 2024-04-02 16:56:43 +01:00
erew123 18b1582d2a Fix curl being funny on re-apply requirements 2024-04-02 16:16:10 +01:00
erew123 96d9df9438 Update README.md 2024-04-02 16:13:26 +01:00
erew123 4ca156a032 Waning/Documentation messages
Added a routine to both atsetups to warn about paths with spaces in them.
Added notes to finetune to make certain aspects clearer
Changed a note in readme
2024-04-02 13:39:08 +01:00
erew123 5aecb5b7fd Spelling/Colour correction 2024-04-01 15:24:20 +01:00
erew123 63c308ff05 Fix wav file deletion display 2024-04-01 14:52:31 +01:00
erew123 e6e4b0b47f Merge pull request #156 from erew123/dev
Dev to main
2024-04-01 13:55:59 +01:00
erew123 af4c63f1ee Update README.md 2024-04-01 13:53:10 +01:00
erew123 bedc8148ad Update README.md 2024-04-01 13:52:32 +01:00
erew123 21cd171719 Add files via upload 2024-04-01 13:11:09 +01:00
erew123 856c0c1baa Add files via upload 2024-04-01 13:04:24 +01:00
erew123 09977956f5 Tidy up model download message 2024-04-01 12:44:22 +01:00
erew123 2c7feb31e5 Add files via upload 2024-04-01 12:34:09 +01:00
erew123 cead53a808 Config file checker & clean start-up
Cleaned up the start-up screen & added mini diags
Added a config file updater that can be used to add/remove things from the confignew.json file if ever needed.
2024-04-01 12:28:32 +01:00
erew123 e7996ebc7a Add files via upload 2024-04-01 12:26:42 +01:00
erew123 6815ba0f2e Minor documentation update 2024-04-01 11:00:38 +01:00
erew123 51fa4ce9e9 Cleaner Start-up & mini diags
Updated various bits of code to clean up the AllTalk start-up messages.
2024-04-01 10:48:36 +01:00
erew123 043648e24a Merge pull request #155 from erew123/main
Pull from main to dev
2024-04-01 10:44:21 +01:00
erew123 b76ff028f8 Fixed DeepSpeed again (sorry)
Missed a digit off the Pytorch download and fluffed the version of DeepSpeed.
2024-04-01 03:05:27 +01:00
erew123 14214cc141 Update README.md 2024-03-30 10:48:13 +00:00
erew123 872638a198 Update README.md 2024-03-30 10:46:06 +00:00
erew123 5d2720c118 Update README.md 2024-03-30 10:44:29 +00:00
erew123 460216819a Update README.md 2024-03-29 22:36:10 +00:00
erew123 412d1442a6 Corrected DeepSpeed Path
Hopefully Github takes this file this time. It was changed/updated yesterday, but for whatever reason, the Github didnt have the updated version.
2024-03-29 21:42:23 +00:00
erew123 5a1dd31ab6 Add files via upload 2024-03-29 21:36:16 +00:00
erew123 ce84f153ca Update README.md 2024-03-29 13:07:14 +00:00
erew123 ab3570c899 Update README.md 2024-03-29 12:58:42 +00:00
erew123 42f08cd19c Update README.md 2024-03-29 02:34:22 +00:00
erew123 21fb1b58d9 Update README.md 2024-03-29 02:17:01 +00:00
erew123 d4741edf94 Update atsetup.bat
deepspeed version
2024-03-29 02:04:58 +00:00
erew123 e3c7144272 Update README.md 2024-03-29 01:54:05 +00:00
erew123 27a4fe2338 Update README.md 2024-03-29 01:15:12 +00:00
erew123 a0913db3c9 Update atsetup.sh 2024-03-29 00:43:47 +00:00
erew123 645c0d3a67 Update atsetup.bat
force pytorch upgrade
2024-03-29 00:42:54 +00:00
erew123 e3878b595b Update requirements_googlecolab.txt 2024-03-28 23:25:34 +00:00
erew123 825e4a7d12 Add files via upload 2024-03-28 23:14:02 +00:00
34 changed files with 4858 additions and 1752 deletions

No files matched your search

@@ -1,6 +1,6 @@
--- ---
name: Support request OR Bug report name: Support Request/Issue
about: Create a report to help us improve about: Use this to create a support request or report a bug/issue
title: '' title: ''
labels: '' labels: ''
assignees: '' assignees: ''
+1 -1
View File
@@ -1,6 +1,6 @@
--- ---
name: Feature request name: Feature request
about: Suggest an idea for this project about: Use this to create a Feature request
title: '' title: ''
labels: '' labels: ''
assignees: '' assignees: ''
+10 -3
View File
@@ -1,12 +1,19 @@
FROM ubuntu:22.04 FROM nvidia/cuda:12.1.1-devel-ubuntu22.04
ENV HOST 0.0.0.0 ENV HOST 0.0.0.0
RUN apt-get update && apt-get upgrade -y \ RUN apt-get update && apt-get upgrade -y \
&& apt-get install -y git build-essential portaudio19-dev \ && apt-get install -y git build-essential portaudio19-dev \
python3 python3-pip gcc wget \ python3 python3-pip gcc wget \
ocl-icd-opencl-dev opencl-headers clinfo \
libclblast-dev libopenblas-dev libaio-dev \
&& mkdir -p /etc/OpenCL/vendors && echo "libnvidia-opencl.so.1" > /etc/OpenCL/vendors/nvidia.icd \
&& ln -s /usr/bin/python3 /usr/bin/python && ln -s /usr/bin/python3 /usr/bin/python
WORKDIR /app WORKDIR /app
COPY . . COPY . .
RUN pip install --no-cache-dir -r requirements_other.txt ENV CUDA_DOCKER_ARCH=all
EXPOSE 7851 RUN pip install --no-cache-dir --no-deps -r system/requirements/requirements_docker.txt && \
pip install --no-cache-dir deepspeed
EXPOSE 7851 7852
RUN chmod +x launch.sh RUN chmod +x launch.sh
ENTRYPOINT ["sh", "-c", "./launch.sh"] ENTRYPOINT ["sh", "-c", "./launch.sh"]
+1472 -1349
View File
File diff suppressed because it is too large. Load diff
+32 -11
View File
@@ -1,7 +1,7 @@
@echo off @echo off
cd /D "%~dp0" cd /D "%~dp0"
setlocal enabledelayedexpansion
set PATH=%PATH%;%SystemRoot%\system32 @set "PATH=%PATH%;%SystemRoot%\system32"
:: Generate the ESC character :: Generate the ESC character
for /F %%a in ('echo prompt $E ^| cmd') do set "ESC=%%a" for /F %%a in ('echo prompt $E ^| cmd') do set "ESC=%%a"
@@ -25,6 +25,15 @@ set "L_CYAN=%ESC%[96m"
set "L_WHITE=%ESC%[97m" set "L_WHITE=%ESC%[97m"
set "RESET=%ESC%[0m" set "RESET=%ESC%[0m"
echo "%CD%"| findstr /C:" " >nul && echo. && echo %L_BLUE%ALLTALK WINDOWS SETUP UTILITY%RESET% && echo. && echo. && echo You are trying to install AllTalk in a folder that has a space in the folder path e.g. && echo. && echo C:\%L_RED%program files%RESET%\alltalk_tts && echo. && echo This causes errors with Conda and Python scripts. Please follow this link for reference: && echo. && echo %L_CYAN%https://docs.anaconda.com/free/working-with-conda/reference/faq/#installing-anaconda%RESET% && echo. && echo Please use a folder path that has no spaces in it e.g. && echo. && echo C:\myfiles\alltalk_tts\ && echo. && echo. && pause && goto :end
@rem Check for special characters in installation path
set "SPCHARMESSAGE="WARNING: Special characters were detected in the installation path!" " This can cause the installation to fail!""
echo "%CD%"| findstr /R /C:"[!#\$%&()\*+,;<=>?@\[\]\^`{|}~]" >nul && (
call :PrintBigMessage %SPCHARMESSAGE%
)
set SPCHARMESSAGE=
@rem Check if curl is available @rem Check if curl is available
curl --version >nul 2>&1 curl --version >nul 2>&1
if "%ERRORLEVEL%" NEQ "0" ( if "%ERRORLEVEL%" NEQ "0" (
@@ -120,7 +129,7 @@ if "%StandaloneOption%"=="3" goto STReapplyrequirements
if "%StandaloneOption%"=="4" goto STDeleteCustomStandalone if "%StandaloneOption%"=="4" goto STDeleteCustomStandalone
if "%StandaloneOption%"=="5" goto STPurgepipcache if "%StandaloneOption%"=="5" goto STPurgepipcache
if "%StandaloneOption%"=="8" goto GenerateDiagsStandalone if "%StandaloneOption%"=="8" goto GenerateDiagsStandalone
if "%StandaloneOption%"=="9" goto End if "%StandaloneOption%"=="9" goto EndStandalone
goto StandaloneMenu goto StandaloneMenu
:InstallNvidiaTextGen :InstallNvidiaTextGen
@@ -128,6 +137,8 @@ pip install -r system\requirements\requirements_textgen.txt
if %ERRORLEVEL% neq 0 ( if %ERRORLEVEL% neq 0 (
echo. echo.
echo There was an error installing the requirements. echo There was an error installing the requirements.
echo Have you started your Text-gen-webui Python environment
echo with cmd_{yourOS} before running atsetup.bat?
echo Press any key to return to the menu. echo Press any key to return to the menu.
echo. echo.
pause pause
@@ -383,14 +394,14 @@ echo Downloading and installing PyTorch. This step can take a long time
echo depending on your internet connection and hard drive speed. Please echo depending on your internet connection and hard drive speed. Please
echo be patient. echo be patient.
echo. echo.
pip install torch>=2.2.1+cu121 torchaudio>=2.2.1+cu121 --extra-index-url https://download.pytorch.org/whl/cu121 pip install torch==2.2.2+cu121 torchaudio>=2.2.2+cu121 --upgrade --force-reinstall --extra-index-url https://download.pytorch.org/whl/cu121
echo Installing other requirements. echo Installing other requirements.
echo. echo.
pip install -r system\requirements\requirements_standalone.txt pip install -r system\requirements\requirements_standalone.txt
curl -LO https://github.com/erew123/alltalk_tts/releases/download/DeepSpeed-12.7/deepspeed-0.12.7+d058d4b-cp311-cp311-win_amd64.whl curl -LO https://github.com/erew123/alltalk_tts/releases/download/DeepSpeed-14.0/deepspeed-0.14.0+ce78a63-cp311-cp311-win_amd64.whl
echo Installing DeepSpeed... echo Installing DeepSpeed...
pip install deepspeed-0.12.7+d058d4b-cp311-cp311-win_amd64.whl pip install deepspeed-0.14.0+ce78a63-cp311-cp311-win_amd64.whl
del deepspeed-0.12.7+d058d4b-cp311-cp311-win_amd64.whl del deepspeed-0.14.0+ce78a63-cp311-cp311-win_amd64.whl
@rem Create start_environment.bat to run AllTalk environment @rem Create start_environment.bat to run AllTalk environment
@@ -524,14 +535,14 @@ echo Downloading and installing PyTorch. This step can take a long time
echo depending on your internet connection and hard drive speed. Please echo depending on your internet connection and hard drive speed. Please
echo be patient. echo be patient.
echo. echo.
pip install torch>=2.2.1+cu121 torchaudio>=2.2.1+cu121 --extra-index-url https://download.pytorch.org/whl/cu121 pip install torch==2.2.2+cu121 torchaudio>=2.2.2+cu121 --upgrade --force-reinstall --extra-index-url https://download.pytorch.org/whl/cu121
echo Installing other requirements. echo Installing other requirements.
echo. echo.
pip install -r system\requirements\requirements_standalone.txt pip install -r system\requirements\requirements_standalone.txt
curl -LO https://github.com/erew123/alltalk_tts/releases/download/DeepSpeed-12.7/deepspeed-0.12.7+d058d4b-cp311-cp311-win_amd64.whl C:/Windows/system32/curl.exe -LO https://github.com/erew123/alltalk_tts/releases/download/DeepSpeed-14.0/deepspeed-0.14.0+ce78a63-cp311-cp311-win_amd64.whl
echo Installing DeepSpeed... echo Installing DeepSpeed...
pip install deepspeed-0.12.7+d058d4b-cp311-cp311-win_amd64.whl pip install deepspeed-0.14.0+ce78a63-cp311-cp311-win_amd64.whl
del deepspeed-0.12.7+d058d4b-cp311-cp311-win_amd64.whl del deepspeed-0.14.0+ce78a63-cp311-cp311-win_amd64.whl
if %ERRORLEVEL% neq 0 ( if %ERRORLEVEL% neq 0 (
echo. echo.
echo There was an error. echo There was an error.
@@ -633,6 +644,16 @@ Echo.
pause pause
goto StandaloneMenu goto StandaloneMenu
:EndStandalone
echo Exiting AllTalk Setup Utility...
echo.
Echo Remember, after installation you can....
Echo Run %L_YELLOW%start_alltalk.bat%RESET% to start AllTalk.
Echo Run %L_YELLOW%start_finetune.bat%RESET% to start Finetuning.
Echo Run %L_YELLOW%start_environment.bat%RESET% to start the AllTalk Python environment.
Echo.
exit /b
:End :End
echo Exiting AllTalk Setup Utility... echo Exiting AllTalk Setup Utility...
exit /b exit /b
+36 -2
View File
@@ -28,6 +28,40 @@ check_curl() {
fi fi
} }
# Check if the current directory path contains a space
containsSpace=false
currentPath=$(pwd)
if echo "$currentPath" | grep -q ' '; then
containsSpace=true
fi
if [ "$containsSpace" = true ]; then
echo
echo -e " ${L_BLUE}ALLTALK LINUX SETUP UTILITY${NC}"
echo
echo
echo -e " You are trying to install AllTalk in a folder that has a space in the"
echo -e " folder path e.g."
echo
echo -e " /home/${L_RED}program files${NC}/alltalk_tts"
echo
echo -e " This causes errors with Conda and Python scripts. Please follow this"
echo -e " link for reference:"
echo
echo -e " ${L_CYAN}https://docs.anaconda.com/free/working-with-conda/reference/faq/#installing-anaconda${NC}"
echo
echo -e " Please use a folder path that has no spaces in it e.g."
echo
echo -e " /home/myfiles/alltalk_tts/"
echo
echo
read -p "Press Enter to continue..."
exit 1
else
# Continue with the main menu
echo "Continue with the main menu."
fi
# Main Menu # Main Menu
main_menu() { main_menu() {
while true; do while true; do
@@ -291,7 +325,7 @@ install_custom_standalone() {
echo " Downloading and installing PyTorch. This step can take a long time" echo " Downloading and installing PyTorch. This step can take a long time"
echo " depending on your internet connection and hard drive speed. Please" echo " depending on your internet connection and hard drive speed. Please"
echo " be patient." echo " be patient."
pip install torch>=2.2.1+cu121 torchaudio>=2.2.1+cu121 --extra-index-url https://download.pytorch.org/whl/cu121 pip install torch>=2.2.1+cu121 torchaudio>=2.2.1+cu121 --upgrade --force-reinstall --extra-index-url https://download.pytorch.org/whl/cu121
echo echo
echo " Installing additional requirements." echo " Installing additional requirements."
echo echo
@@ -469,7 +503,7 @@ reapply_standalone() {
echo " Downloading and installing PyTorch. This step can take a long time" echo " Downloading and installing PyTorch. This step can take a long time"
echo " depending on your internet connection and hard drive speed. Please" echo " depending on your internet connection and hard drive speed. Please"
echo " be patient." echo " be patient."
pip install torch>=2.2.1+cu121 torchaudio>=2.2.1+cu121 --extra-index-url https://download.pytorch.org/whl/cu121 pip install torch>=2.2.1+cu121 torchaudio>=2.2.1+cu121 --upgrade --force-reinstall --extra-index-url https://download.pytorch.org/whl/cu121
echo echo
echo " Installing additional requirements." echo " Installing additional requirements."
echo echo
+9 -1
View File
@@ -1,13 +1,21 @@
version: '3.8' version: '3.8'
services: services:
alltalk-tts: alltalk-tts:
image: erew123/alltalk_tts:cpu image: alltalk:v1-9c
restart: unless-stopped restart: unless-stopped
ports: ports:
- "7851:7851" - "7851:7851"
- "7852:7852"
volumes: volumes:
- ./outputs:/app/outputs/ - ./outputs:/app/outputs/
- ./models:/app/models/ - ./models:/app/models/
- ./voices:/app/voices/ - ./voices:/app/voices/
- ./finetune/put-voice-samples-in-here:/app/finetune/put-voice-samples-in-here - ./finetune/put-voice-samples-in-here:/app/finetune/put-voice-samples-in-here
- ./dockerconfig.json:/app/confignew.json - ./dockerconfig.json:/app/confignew.json
deploy:
resources:
reservations:
devices:
- driver: nvidia
count: all
capabilities: [ gpu ]
+321 -159
View File
@@ -291,12 +291,12 @@ def create_temporary_file(folder, suffix=".wav"):
unique_filename = f"custom_tempfile_{int(time.time())}_{random.randint(1, 1000)}{suffix}" unique_filename = f"custom_tempfile_{int(time.time())}_{random.randint(1, 1000)}{suffix}"
return os.path.join(folder, unique_filename) return os.path.join(folder, unique_filename)
def format_audio_list(target_language, whisper_model, out_path, eval_split_number, gradio_progress=progress): def format_audio_list(target_language, whisper_model, out_path, eval_split_number, speaker_name_input, gradio_progress=progress):
pfc_check_fail() pfc_check_fail()
audio_files = [os.path.join(audio_folder, file) for file in os.listdir(audio_folder) if file.endswith(('.mp3', '.flac', '.wav'))] audio_files = [os.path.join(audio_folder, file) for file in os.listdir(audio_folder) if file.endswith(('.mp3', '.flac', '.wav'))]
buffer=0.2 buffer=0.2
eval_percentage = eval_split_number / 100.0 eval_percentage = eval_split_number / 100.0
speaker_name="coqui" speaker_name=speaker_name_input
audio_total_size = 0 audio_total_size = 0
os.makedirs(out_path, exist_ok=True) os.makedirs(out_path, exist_ok=True)
temp_folder = os.path.join(out_path, "temp") # Update with your folder name temp_folder = os.path.join(out_path, "temp") # Update with your folder name
@@ -533,7 +533,7 @@ def basemodel_or_finetunedmodel_choice(value):
elif value == "Existing finetuned model": elif value == "Existing finetuned model":
basemodel_or_finetunedmodel = False basemodel_or_finetunedmodel = False
def train_gpt(language, num_epochs, batch_size, grad_acumm, train_csv, eval_csv, output_path, max_audio_length=255995): def train_gpt(language, num_epochs, batch_size, grad_acumm, train_csv, eval_csv, learning_rate, output_path, max_audio_length=255995):
pfc_check_fail() pfc_check_fail()
# Logging parameters # Logging parameters
RUN_NAME = "XTTS_FT" RUN_NAME = "XTTS_FT"
@@ -657,7 +657,7 @@ def train_gpt(language, num_epochs, batch_size, grad_acumm, train_csv, eval_csv,
optimizer="AdamW", optimizer="AdamW",
optimizer_wd_only_on_weights=OPTIMIZER_WD_ONLY_ON_WEIGHTS, optimizer_wd_only_on_weights=OPTIMIZER_WD_ONLY_ON_WEIGHTS,
optimizer_params={"betas": [0.9, 0.96], "eps": 1e-8, "weight_decay": 1e-2}, optimizer_params={"betas": [0.9, 0.96], "eps": 1e-8, "weight_decay": 1e-2},
lr=5e-06, # learning rate lr=learning_rate, # learning rate
lr_scheduler="MultiStepLR", lr_scheduler="MultiStepLR",
# it was adjusted accordly for the new step scheme # it was adjusted accordly for the new step scheme
lr_scheduler_params={"milestones": [50000 * 18, 150000 * 18, 300000 * 18], "gamma": 0.5, "last_epoch": -1}, lr_scheduler_params={"milestones": [50000 * 18, 150000 * 18, 300000 * 18], "gamma": 0.5, "last_epoch": -1},
@@ -735,6 +735,9 @@ def load_model(xtts_checkpoint, xtts_config, xtts_vocab):
config.load_json(xtts_config) config.load_json(xtts_config)
XTTS_MODEL = Xtts.init_from_config(config) XTTS_MODEL = Xtts.init_from_config(config)
print("[FINETUNE] \033[94mStarting Step 3\033[0m Loading XTTS model!") print("[FINETUNE] \033[94mStarting Step 3\033[0m Loading XTTS model!")
print(xtts_checkpoint)
print(xtts_vocab)
print(xtts_speakers_pth)
XTTS_MODEL.load_checkpoint(config, checkpoint_path=xtts_checkpoint, vocab_path=xtts_vocab, use_deepspeed=False, speaker_file_path=xtts_speakers_pth) XTTS_MODEL.load_checkpoint(config, checkpoint_path=xtts_checkpoint, vocab_path=xtts_vocab, use_deepspeed=False, speaker_file_path=xtts_speakers_pth)
if torch.cuda.is_available(): if torch.cuda.is_available():
XTTS_MODEL.cuda() XTTS_MODEL.cuda()
@@ -773,18 +776,22 @@ def get_available_voices(minimum_size_kb=1200):
] ]
return sorted([str(file) for file in voice_files]) # Return full path as string return sorted([str(file) for file in voice_files]) # Return full path as string
def find_best_models(directory):
"""Find files named 'best_model.pth' in the given directory."""
return [str(file) for file in Path(directory).rglob("best_model.pth")]
def find_models(directory, extension): def find_models(directory, extension):
"""Find files with a specific extension in the given directory.""" """Find files with a specific extension in the given directory."""
return [file for file in Path(directory).rglob(f"*.{extension}")] return [str(file) for file in Path(directory).rglob(f"*.{extension}")]
def find_jsons(directory, filename): def find_jsons(directory, filename):
"""Find files with a specific filename in the given directory.""" """Find files with a specific filename in the given directory."""
return list(Path(directory).rglob(filename)) return [str(file) for file in Path(directory).rglob(filename)]
# Your main directory # Your main directory
main_directory = Path(this_dir) / "finetune" / "tmp-trn" main_directory = Path(this_dir) / "finetune" / "tmp-trn"
# XTTS checkpoint files (*.pth) # XTTS checkpoint files (best_model.pth)
xtts_checkpoint_files = find_models(main_directory, "pth") xtts_checkpoint_files = find_best_models(main_directory)
# XTTS config files (config.json) # XTTS config files (config.json)
xtts_config_files = find_jsons(main_directory, "config.json") xtts_config_files = find_jsons(main_directory, "config.json")
# XTTS vocab files (vocab.json) # XTTS vocab files (vocab.json)
@@ -800,68 +807,70 @@ def find_latest_best_model(folder_path):
latest_file = max(files, key=os.path.getctime, default=None) latest_file = max(files, key=os.path.getctime, default=None)
return latest_file return latest_file
def compact_model(): def compact_model(xtts_checkpoint_copy):
this_dir = Path(__file__).parent.resolve() this_dir = Path(__file__).parent.resolve()
print("THIS DIR:", this_dir)
best_model_path_str = find_latest_best_model(this_dir / "finetune" / "tmp-trn" / "training") best_model_path_str = str(xtts_checkpoint_copy) # Convert to string
print("best_model_path_str", best_model_path_str)
# Check if the best model file exists # Check if the best model file exists
if best_model_path_str is None: if not best_model_path_str:
print("[FINETUNE] No trained model was found.") print("[FINETUNE] No trained model was found.")
return "No trained model was found." return "No trained model was found."
# Convert model_path_str to Path print(f"[FINETUNE] Best model path: {best_model_path_str}")
best_model_path = Path(best_model_path_str)
# Attempt to load the model # Attempt to load the model
try: try:
checkpoint = torch.load(best_model_path, map_location=torch.device("cpu")) checkpoint = torch.load(best_model_path_str, map_location=torch.device("cpu"))
print(f"[FINETUNE] Checkpoint loaded: {best_model_path_str}")
except Exception as e: except Exception as e:
print("[FINETUNE] Error loading checkpoint:", e) print("[FINETUNE] Error loading checkpoint:", e)
raise raise
# Define the target directory
target_dir = this_dir / "models" / "trainedmodel"
# Create the target directory if it doesn't exist
target_dir.mkdir(parents=True, exist_ok=True)
del checkpoint["optimizer"] del checkpoint["optimizer"]
for key in list(checkpoint["model"].keys()): for key in list(checkpoint["model"].keys()):
if "dvae" in key: if "dvae" in key:
del checkpoint["model"][key] del checkpoint["model"][key]
# Define the target directory
target_dir = this_dir / "models" / "trainedmodel"
# Create the target directory if it doesn't exist
target_dir.mkdir(parents=True, exist_ok=True)
# Save the modified checkpoint in the target directory # Save the modified checkpoint in the target directory
torch.save(checkpoint, target_dir / "model.pth") torch.save(checkpoint, str(target_dir / "model.pth")) # Convert to string
# Specify the files you want to copy # Specify the files you want to copy
files_to_copy = ["vocab.json", "config.json", "dvae.pth", "mel_stats.pth","speakers_xtts.pth", ] files_to_copy = ["vocab.json", "config.json", "speakers_xtts.pth", "mel_stats.pth", "dvae.pth"]
for file_name in files_to_copy: for file_name in files_to_copy:
src_path = this_dir / base_path / base_model_path / file_name src_path = this_dir / base_path / base_model_path / file_name
dest_path = target_dir / file_name dest_path = target_dir / file_name
shutil.copy(str(src_path), str(dest_path)) shutil.copy(str(src_path), str(dest_path)) # Convert to string
source_wavs_dir = this_dir / "finetune" / "tmp-trn" / "wavs" source_wavs_dir = this_dir / "finetune" / "tmp-trn" / "wavs"
target_wavs_dir = target_dir / "wavs" target_wavs_dir = target_dir / "wavs"
target_wavs_dir.mkdir(parents=True, exist_ok=True) target_wavs_dir.mkdir(parents=True, exist_ok=True)
# Iterate through files in the source directory # Iterate through files in the source directory
for file_path in source_wavs_dir.iterdir(): for file_path in source_wavs_dir.iterdir():
# Check if it's a file and larger than 1000 KB # Check if it's a file and larger than 1000 KB
if file_path.is_file() and file_path.stat().st_size > 1000 * 1024: if file_path.is_file() and file_path.stat().st_size > 1000 * 1024:
# Copy the file to the target directory # Copy the file to the target directory
shutil.copy(str(file_path), str(target_wavs_dir / file_path.name)) shutil.copy(str(file_path), str(target_wavs_dir / file_path.name)) # Convert to string
print("[FINETUNE] Model copied to '/models/trainedmodel/'") print("[FINETUNE] Model copied to '/models/trainedmodel/'")
return "Model copied to '/models/trainedmodel/'" return "Model copied to '/models/trainedmodel/'"
def compact_lastfinetuned_model(): def compact_lastfinetuned_model(xtts_checkpoint_copy):
this_dir = Path(__file__).parent.resolve() this_dir = Path(__file__).parent.resolve()
best_model_path_str = find_latest_best_model(this_dir / "finetune" / "tmp-trn" / "training") best_model_path_str = xtts_checkpoint_copy
print(f"[FINETUNE] Best model path: {best_model_path_str}")
# Check if the best model file exists # Check if the best model file exists
if best_model_path_str is None: if best_model_path_str is None:
@@ -880,21 +889,21 @@ def compact_lastfinetuned_model():
del checkpoint["optimizer"] del checkpoint["optimizer"]
for key in list(checkpoint["model"].keys()):
if "dvae" in key:
del checkpoint["model"][key]
# Define the target directory # Define the target directory
target_dir = this_dir / "models" / "lastfinetuned" target_dir = this_dir / "models" / "lastfinetuned"
# Create the target directory if it doesn't exist # Create the target directory if it doesn't exist
target_dir.mkdir(parents=True, exist_ok=True) target_dir.mkdir(parents=True, exist_ok=True)
for key in list(checkpoint["model"].keys()):
if "dvae" in key:
del checkpoint["model"][key]
# Save the modified checkpoint in the target directory # Save the modified checkpoint in the target directory
torch.save(checkpoint, target_dir / "model.pth") torch.save(checkpoint, target_dir / "model.pth")
# Specify the files you want to copy # Specify the files you want to copy
files_to_copy = ["vocab.json", "config.json", "dvae.pth", "mel_stats.pth", "speakers_xtts.pth",] files_to_copy = ["vocab.json", "config.json", "speakers_xtts.pth", "mel_stats.pth", "dvae.pth",]
for file_name in files_to_copy: for file_name in files_to_copy:
src_path = this_dir / base_path / base_model_path / file_name src_path = this_dir / base_path / base_model_path / file_name
@@ -916,15 +925,16 @@ def compact_lastfinetuned_model():
return "Model copied to '/models/lastfinetuned/'" return "Model copied to '/models/lastfinetuned/'"
def compact_legacy_model(): def compact_custom_model(xtts_checkpoint_copy, folder_path):
this_dir = Path(__file__).parent.resolve() this_dir = Path(__file__).parent.resolve()
best_model_path_str = os.path.join(this_dir, "finetune", "best_model.pth") best_model_path_str = xtts_checkpoint_copy
print(f"[FINETUNE] Best model path: {best_model_path_str}")
# Check if the best model file exists # Check if the best model file exists
if not os.path.exists(best_model_path_str): if best_model_path_str is None:
print("[FINETUNE] No model called best_model.pth was found in /finetune/") print("[FINETUNE] No trained model was found.")
return "No model called best_model.pth was found in /finetune/" return "No trained model was found."
# Convert model_path_str to Path # Convert model_path_str to Path
best_model_path = Path(best_model_path_str) best_model_path = Path(best_model_path_str)
@@ -938,21 +948,41 @@ def compact_legacy_model():
del checkpoint["optimizer"] del checkpoint["optimizer"]
for key in list(checkpoint["model"].keys()):
if "dvae" in key:
del checkpoint["model"][key]
# Define the target directory # Define the target directory
target_dir = this_dir / "finetune" target_dir = this_dir / "models" / folder_path
# Create the target directory if it doesn't exist # Create the target directory if it doesn't exist
target_dir.mkdir(parents=True, exist_ok=True) target_dir.mkdir(parents=True, exist_ok=True)
for key in list(checkpoint["model"].keys()):
if "dvae" in key:
del checkpoint["model"][key]
# Save the modified checkpoint in the target directory # Save the modified checkpoint in the target directory
torch.save(checkpoint, target_dir / "model.pth") torch.save(checkpoint, target_dir / "model.pth")
print("[FINETUNE] model.pth created in '/finetune/'") # Specify the files you want to copy
return "model.pth created in '/finetune/'" files_to_copy = ["vocab.json", "config.json", "speakers_xtts.pth", "mel_stats.pth", "dvae.pth",]
for file_name in files_to_copy:
src_path = this_dir / base_path / base_model_path / file_name
dest_path = target_dir / file_name
shutil.copy(str(src_path), str(dest_path))
source_wavs_dir = this_dir / "finetune" / "tmp-trn" / "wavs"
target_wavs_dir = target_dir / "wavs"
target_wavs_dir.mkdir(parents=True, exist_ok=True)
# Iterate through files in the source directory
for file_path in source_wavs_dir.iterdir():
# Check if it's a file and larger than 1000 KB
if file_path.is_file() and file_path.stat().st_size > 1000 * 1024:
# Copy the file to the target directory
shutil.copy(str(file_path), str(target_wavs_dir / file_path.name))
print("[FINETUNE] Model copied to '/models/",folder_path,"/")
return f"Model copied to '/models/{folder_path}/'"
def delete_training_data(): def delete_training_data():
# Define the folder to be deleted # Define the folder to be deleted
@@ -1264,6 +1294,9 @@ if __name__ == "__main__":
### 🟥 <u>Important Note - Windows - "UserWarning: huggingface_hub cache-system uses symlinks."</u> ### 🟥 <u>Important Note - Windows - "UserWarning: huggingface_hub cache-system uses symlinks."</u>
◽ This error is caused by Huggingfaces download software downloading the Whisper model. If you get this error, please restart your Windows command prompt with "Run as Administrator" and restart finetuning.<br> ◽ This error is caused by Huggingfaces download software downloading the Whisper model. If you get this error, please restart your Windows command prompt with "Run as Administrator" and restart finetuning.<br>
◽ This should only occur the 1st time it downloads the Whisper model. [Huggingface Reference here](https://huggingface.co/docs/huggingface_hub/en/guides/manage-cache#limitations) ◽ This should only occur the 1st time it downloads the Whisper model. [Huggingface Reference here](https://huggingface.co/docs/huggingface_hub/en/guides/manage-cache#limitations)
### 🟥 <u>Important Note - Language support.</u>
◽ Although I have done my best to help automate this step, the Whisper model is not great with all languages and may fail to build your training data correctly. The Large-v3 may be better at certain languages.<br>
◽ You can find information about the Whisper model [here](https://github.com/openai/whisper?tab=readme-ov-file#available-models-and-languages) and you can find data about manually building training data [here](https://docs.coqui.ai/en/latest/formatting_your_dataset.html), as well as details below about the file structure this step performs.<br>
### 🟦 <u>What you need to do</u> ### 🟦 <u>What you need to do</u>
◽ Please read Coqui's guide on what makes a good dataset [here](https://docs.coqui.ai/en/latest/what_makes_a_good_dataset.html#what-makes-a-good-dataset)<br> ◽ Please read Coqui's guide on what makes a good dataset [here](https://docs.coqui.ai/en/latest/what_makes_a_good_dataset.html#what-makes-a-good-dataset)<br>
◽ Place your audio files in <span style="color: #3366ff;">{str(audio_folder)}</span> ◽ Place your audio files in <span style="color: #3366ff;">{str(audio_folder)}</span>
@@ -1276,6 +1309,7 @@ if __name__ == "__main__":
◽ Whisper is making a best effort to find spoken audio and break it down into smaller audio files. The content of these audio files is then transcribed into CSV fles (which you can edit in Excel or similar).<br> ◽ Whisper is making a best effort to find spoken audio and break it down into smaller audio files. The content of these audio files is then transcribed into CSV fles (which you can edit in Excel or similar).<br>
◽ These files (audio and CSV) are used at the next step to train the model "this is what the audio sounds like and these are the words being spoken".<br> ◽ These files (audio and CSV) are used at the next step to train the model "this is what the audio sounds like and these are the words being spoken".<br>
◽ If you wish to manually look at the CSV files before running the next step, you are welcome to do so and edit them as necessary. The greater the accuracy of the text the better the training will be.<br> ◽ If you wish to manually look at the CSV files before running the next step, you are welcome to do so and edit them as necessary. The greater the accuracy of the text the better the training will be.<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ◽ <span style="color: #3366ff;">/alltalk_tts/finetune/tmp-trn/lang.txt</span> Contains a two digit langauage code e.g. `en`, `fr` etc.<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ◽ <span style="color: #3366ff;">/alltalk_tts/finetune/tmp-trn/metadata_train.csv</span> Contains the list of wavs to text that will be used. This is what the model is trained with.<br> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ◽ <span style="color: #3366ff;">/alltalk_tts/finetune/tmp-trn/metadata_train.csv</span> Contains the list of wavs to text that will be used. This is what the model is trained with.<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ◽ <span style="color: #3366ff;">/alltalk_tts/finetune/tmp-trn/metadata_eval.csv</span> Lists the evaluation wav to text, used to generate TTS while training and evaluate the quality of generated TTS to the original sample.<br> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ◽ <span style="color: #3366ff;">/alltalk_tts/finetune/tmp-trn/metadata_eval.csv</span> Lists the evaluation wav to text, used to generate TTS while training and evaluate the quality of generated TTS to the original sample.<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ◽ <span style="color: #3366ff;">/alltalk_tts/finetune/tmp-trn/wavs/</span> These are the wavs split out from your original training samples and match the contents of the CSV files.<br> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ◽ <span style="color: #3366ff;">/alltalk_tts/finetune/tmp-trn/wavs/</span> These are the wavs split out from your original training samples and match the contents of the CSV files.<br>
@@ -1334,6 +1368,11 @@ if __name__ == "__main__":
maximum=95, # Maximum value maximum=95, # Maximum value
step=1, # Increment step step=1, # Increment step
) )
speaker_name_input = gr.Textbox(
label="The name of the speaker/person you are training",
value="personsname",
visible=True,
)
progress_data = gr.Label( progress_data = gr.Label(
label="Progress:" label="Progress:"
) )
@@ -1345,14 +1384,15 @@ if __name__ == "__main__":
prompt_compute_btn = gr.Button(value="Step 1 - Create dataset") prompt_compute_btn = gr.Button(value="Step 1 - Create dataset")
def preprocess_dataset(language, whisper_model, out_path, eval_split_number, progress=gr.Progress(track_tqdm=True)): def preprocess_dataset(language, whisper_model, out_path, eval_split_number, speaker_name_input, progress=gr.Progress(track_tqdm=True)):
clear_gpu_cache() clear_gpu_cache()
test_for_audio_files = [file for file in os.listdir(audio_folder) if any(file.lower().endswith(ext) for ext in ['.wav', '.mp3', '.flac'])] test_for_audio_files = [file for file in os.listdir(audio_folder) if any(file.lower().endswith(ext) for ext in ['.wav', '.mp3', '.flac'])]
if not test_for_audio_files: if not test_for_audio_files:
return "I cannot find any mp3, wav or flac files in the folder called 'put-voice-samples-in-here'", "", "" return "I cannot find any mp3, wav or flac files in the folder called 'put-voice-samples-in-here'", "", ""
else: else:
try: try:
train_meta, eval_meta, audio_total_size = format_audio_list(target_language=language, whisper_model=whisper_model, out_path=out_path, eval_split_number=eval_split_number, gradio_progress=progress)
train_meta, eval_meta, audio_total_size = format_audio_list(target_language=language, whisper_model=whisper_model, out_path=out_path, eval_split_number=eval_split_number, speaker_name_input=speaker_name_input, gradio_progress=progress)
except: except:
traceback.print_exc() traceback.print_exc()
error = traceback.format_exc() error = traceback.format_exc()
@@ -1373,111 +1413,206 @@ if __name__ == "__main__":
#### GRADIO STEP 2 #### #### GRADIO STEP 2 ####
####################### #######################
with gr.Tab("💻 Step 2 - Training"): with gr.Tab("💻 Step 2 - Training"):
with gr.Tab("Training"):
gr.Markdown(
f"""
### 💻 <u>Training</u><br>
### 🟦 <u>What you need to do</u>
◽ The <span style="color: #3366ff;">Train CSV</span> and <span style="color: #3366ff;">Eval CSV</span> should already be populated. If not, just go back to Step 1 and click "Create Dataset" again.<br>
◽ The default settings below are the suggested settings for most purposes, however you may choose to alter them depending on your specific use case.<br>
◽ There are no absolute correct settings for training. It will vary based on:<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;- What you are training (A human voice, A cartoon voice, A new language entirely etc)<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;- How much audio you have (you may want less or more eval or epochs)<br>
◽ The key indicator of sufficient model training is whether it sounds right to you. Coqui suggests beginning with the base settings for training. If the resulting audio doesn't meet your expectations, additional training sessions may be necessary.<br>
◽ **Number of epochs:** Defines how many times the entire dataset is passed through the neural network; more epochs can improve learning but increase the risk of memorizing the training data.<br>
◽ **Batch size:** Sets how many training examples are used in one iteration. Larger sizes speed up training but require more computational memory.<br>
◽ **Grad(ient) accumulation steps:** Allows larger batches by spreading the computation across multiple steps, enabling training with limited hardware resources.<br>
◽ **Max permitted audio size in seconds:** Limits the length of audio that the model processes. Any audio exceeding this duration will be truncated to fit the model's input constraints.<br>
### 🟨 <u>What this step is doing</u><br>
◽ Very simply put, it's taking all our wav files generated in Step 1, along with our recorded speech in out excel documents and its training the model on that voice e.g. listen to this audio file and this is what is being said in it, so learn to reproduce it.<br>
### 🟩 <u>How long will this take?</u><br>
◽ On a RTX 4070 with factory settings (as below) and 20 minutes of audio, this took 20 minutes. Again, it will vary by system and how much audio you are throwing at it. You have time to go make a coffee.<br>
◽ Look at your command prompt/terminal window if you want to see what it is doing.<br>
"""
)
with gr.Row():
train_csv = gr.Textbox(
label="Train CSV:",
)
eval_csv = gr.Textbox(
label="Eval CSV:",
)
with gr.Row():
num_epochs = gr.Slider(
label="Number of epochs:",
minimum=1,
maximum=100,
step=1,
value=args.num_epochs,
)
batch_size = gr.Slider(
label="Batch size:",
minimum=2,
maximum=512,
step=1,
value=args.batch_size,
)
grad_acumm = gr.Slider(
label="Grad accumulation steps:",
minimum=2,
maximum=128,
step=1,
value=args.grad_acumm,
)
max_audio_length = gr.Slider(
label="Max permitted audio size in seconds:",
minimum=2,
maximum=20,
step=1,
value=args.max_audio_length,
)
with gr.Row():
model_to_train_choices = ["Base Model"]
if finetuned_model:
model_to_train_choices.append("Existing finetuned model")
model_to_train = gr.Radio(
choices=model_to_train_choices,
label="Which model do you want to train?",
value="Base Model"
)
gr.Markdown( gr.Markdown(
f""" f"""
If you have an existing finetuned model in <span style="color: #3366ff;">/models/trainedmodel/</span> you will have an option to select <span style="color: #3366ff;">Existing finetuned model</span>, allowing you to further train it. Otherwise, you will only have Base Model as an option. ### 💻 <u>Training</u><br>
""" ### 🟥 <u>Important Note - Language support.</u>
◽ If this step is failing/erroring you may wish to check your training data was created correctly (Detailed in Step 1), confirming that wav files have been generated and your `metadata_train.csv` and `metadata_eval.csv` files have been populated.<br>
### 🟦 <u>What you need to do</u>
◽ The <span style="color: #3366ff;">Train CSV</span> and <span style="color: #3366ff;">Eval CSV</span> should already be populated. If not, just go back to Step 1 and click "Create Dataset" again.<br>
◽ The default settings below are the suggested settings for most purposes, however you may choose to alter them depending on your specific use case.<br>
◽ There are no absolute correct settings for training. It will vary based on:<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;- What you are training (A human voice, A cartoon voice, A new language entirely etc)<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;- How much audio you have (you may want less or more eval or epochs)<br>
◽ The key indicator of sufficient model training is whether it sounds right to you. Coqui suggests beginning with the base settings for training. If the resulting audio doesn't meet your expectations, additional training sessions may be necessary.<br>
### 🟨 <u>What this step is doing</u><br>
◽ Very simply put, it's taking all our wav files generated in Step 1, along with our recorded speech in out excel documents and its training the model on that voice e.g. listen to this audio file and this is what is being said in it, so learn to reproduce it.<br>
### 🟩 <u>How long will this take?</u><br>
◽ On a RTX 4070 with factory settings (as below) and 20 minutes of audio, this took 20 minutes. Again, it will vary by system and how much audio you are throwing at it. You have time to go make a coffee.<br>
◽ Look at your command prompt/terminal window if you want to see what it is doing.<br>
"""
)
with gr.Row():
train_csv = gr.Textbox(
label="Train CSV:",
scale=2,
)
eval_csv = gr.Textbox(
label="Eval CSV:",
scale=2,
)
learning_rates = gr.Dropdown(
value=5e-6,
label="Learning Rate",
choices=[
("1e-6", 1e-6),
("5e-6", 5e-6),
("1e-5", 1e-5),
("5e-5", 5e-5),
("1e-4", 1e-4),
("5e-4", 5e-4),
("1e-3", 1e-3),
],
type="value",
allow_custom_value=True,
scale=0,
)
with gr.Row():
num_epochs = gr.Slider(
label="Number of epochs:",
minimum=1,
maximum=100,
step=1,
value=args.num_epochs,
)
batch_size = gr.Slider(
label="Batch size:",
minimum=2,
maximum=512,
step=1,
value=args.batch_size,
)
grad_acumm = gr.Slider(
label="Grad accumulation steps:",
minimum=1,
maximum=32,
step=1,
value=args.grad_acumm,
)
max_audio_length = gr.Slider(
label="Max permitted audio size in seconds:",
minimum=2,
maximum=20,
step=1,
value=args.max_audio_length,
)
with gr.Row():
model_to_train_choices = ["Base Model"]
if finetuned_model:
model_to_train_choices.append("Existing finetuned model")
model_to_train = gr.Radio(
choices=model_to_train_choices,
label="Which model do you want to train?",
value="Base Model"
)
gr.Markdown(
f"""
If you have an existing finetuned model in <span style="color: #3366ff;">/models/trainedmodel/</span> you will have an option to select <span style="color: #3366ff;">Existing finetuned model</span>, allowing you to further train it. Otherwise, you will only have Base Model as an option.
"""
)
progress_train = gr.Label(
label="Progress:"
)
logs_tts_train = gr.Textbox(
label="Logs:",
interactive=False,
)
demo.load(read_logs, None, logs_tts_train, every=1)
train_btn = gr.Button(value="Step 2 - Run the training")
def train_model(language, train_csv, eval_csv, learning_rates, num_epochs, batch_size, grad_acumm, output_path, max_audio_length):
clear_gpu_cache()
if not train_csv or not eval_csv:
return "You need to run the data processing step or manually set `Train CSV` and `Eval CSV` fields !", "", "", "", ""
try:
# convert seconds to waveform frames
max_audio_length = int(max_audio_length * 22050)
learning_rate = float(learning_rates) # Convert the learning rate value to a float
config_path, original_xtts_checkpoint, vocab_file, exp_path, speaker_wav = train_gpt(language, num_epochs, batch_size, grad_acumm, train_csv, eval_csv, learning_rate, output_path=str(output_path), max_audio_length=max_audio_length)
except:
traceback.print_exc()
error = traceback.format_exc()
return f"The training was interrupted due an error !! Please check the console to check the full error message! \n Error summary: {error}", "", "", "", ""
# copy original files to avoid parameters changes issues
shutil.copy(config_path, exp_path)
shutil.copy(vocab_file, exp_path)
ft_xtts_checkpoint = os.path.join(exp_path, "best_model.pth")
print("[FINETUNE] Model training done. Move to Step 3")
clear_gpu_cache()
return "Model training done. Move to Step 3", config_path, vocab_file, ft_xtts_checkpoint, speaker_wav
with gr.Tab("Info - Grad Accumulation"):
gr.Markdown(
f"""
### 🟩 <u>Grad Accumulation</u><br>
◽ Gradient accumulation is a technique that allows you to simulate a larger batch size without actually increasing the memory consumption. Here's how it works:<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;◽ Instead of updating the model's parameters after every batch, gradient accumulation enables you to process multiple batches and accumulate their gradients.<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;◽ By setting steps greater than 1, you can process multiple batches before a single optimization step e.g if steps is set to 4, the gradients will be accumulated over 4 batches before updating the model's parameters.<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;◽ This means that you can effectively use a larger batch size while keeping the actual batch size per iteration smaller, thereby reducing the memory footprint.<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;◽ Increasing the steps allows you to find a balance between memory consumption and computational efficiency. You can process more examples per optimization step without exceeding the available memory.<br><br>
◽ However, it's important to note that increasing the gradient accumulation steps does have an impact on the training process:<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;◽ Since the model's parameters are updated less frequently (every grad_accum_steps batches), the training dynamics may be slightly different compared to updating the model after every batch.<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;◽ You may need to adjust the learning rate and other hyperparameters accordingly to compensate for the less frequent updates. Typically, you can increase the learning rate slightly when using gradient accumulation.<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;◽ The training progress will be slower in terms of the number of optimization steps per epoch, as the model updates occur less frequently.<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;◽ However, the overall training time may still be reduced compared to using a smaller batch size without gradient accumulation, as it allows for better utilization of GPU resources.<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;◽ To start, you can try setting steps to a value like 4 and see if it resolves the OOM error. If the error persists, you can experiment with higher values until you find a balance that works for your specific setup.
"""
)
with gr.Tab("Info - Batch Size"):
gr.Markdown(
f"""
### 🟩 <u>Batch Size</u><br>
◽ Batch size refers to the number of training examples processed in a single iteration (forward and backward pass) before updating the model's parameters.<br>
◽ A larger batch size allows for more efficient computation and utilization of hardware resources, especially when using GPUs.<br>
◽ However, increasing the batch size also increases the memory consumption during training, as more examples need to be stored in memory.<br>
◽ If the batch size is too large, it can lead to out-of-memory (OOM) errors, especially when training on GPUs with limited memory.<br>
◽ On the other hand, using a smaller batch size can result in slower training and may require more iterations to converge.<br>
◽ The optimal batch size depends on various factors, such as the model architecture, available hardware resources, and the specific problem being solved.<br>
◽ It's common to experiment with different batch sizes to find a balance between training speed and memory efficiency.<br>
◽ If you encounter OOM errors, try reducing the batch size until the training can proceed without memory issues.<br>
◽ Keep in mind that the batch size also affects the model's generalization and convergence behavior, so it's important to monitor the model's performance while adjusting the batch size.
"""
)
with gr.Tab("Info - Learning Rate"):
gr.Markdown(
f"""
### 🟩 <u>Learning Rate</u><br>
◽ 1e-6: Very small learning rate, slow but stable learning.<br>
◽ 5e-6: Small learning rate, slow but stable learning.<br>
◽ 1e-5: Moderate learning rate, balanced between stability and convergence speed.<br>
◽ 5e-5: Higher learning rate, faster convergence but potential instability.<br>
◽ 1e-4: High learning rate, faster convergence but increased risk of instability.<br>
◽ 5e-4: Very high learning rate, fast convergence but higher risk of instability.<br>
◽ 1e-3: Extremely high learning rate, very fast convergence but high risk of instability.<br><br>
The optimal learning rate depends on the model architecture, dataset, and other hyperparameters.
"""
)
with gr.Tab("Info - Epochs"):
gr.Markdown(
f"""
### 🟩 <u>Epochs</u><br>
◽ An epoch represents a single pass through the entire training dataset during the training process.<br>
◽ In each epoch, the model sees and learns from all the training examples once.<br>
◽ The number of epochs determines how many times the model will iterate over the complete training dataset.<br>
◽ Increasing the number of epochs allows the model to learn more from the data and potentially improve its performance.<br>
◽ However, training for too many epochs can lead to overfitting, where the model becomes too specialized to the training data and fails to generalize well to unseen data.<br>
◽ The optimal number of epochs depends on factors such as the complexity of the problem, the size of the dataset, and the model's capacity.<br>
◽ It's common to use techniques like early stopping or validation monitoring to determine the appropriate number of epochs.<br>
◽ Early stopping involves monitoring the model's performance on a validation set and stopping the training when the performance starts to degrade, indicating potential overfitting.<br>
◽ Validation monitoring involves evaluating the model's performance on a separate validation set after each epoch and selecting the model checkpoint that performs best on the validation set.<br>
◽ It's recommended to start with a reasonable number of epochs and monitor the model's performance to determine if more epochs are needed or if early stopping should be applied
"""
)
with gr.Tab("Info - Max Audio"):
gr.Markdown(
f"""
### 🟩 <u>Max Permitted Audio Size (in seconds)</u><br>
◽ The max permitted audio size determines the maximum duration of audio files that can be used as input to the model during training and inference.<br>
◽ It is specified in seconds and represents the longest audio clip that the model can process.<br>
◽ Limiting the audio size helps to control the memory usage and computational requirements of the model.<br>
◽ If the audio files in your dataset vary in length, it's important to consider the max permitted audio size to ensure that all files can be processed effectively.<br>
◽ Audio files longer than the max permitted size will typically be truncated or split into smaller segments before being fed into the model.<br>
◽ The choice of max permitted audio size depends on the specific requirements of your application and the available hardware resources.<br>
◽ Increasing the max permitted audio size allows the model to handle longer audio clips but also increases the memory consumption and computational burden.<br>
◽ If you encounter memory issues or slow processing times, you may need to reduce the max permitted audio size to find a suitable balance.<br>
◽ It's important to consider the nature of your audio data and choose a max permitted audio size that captures the relevant information while being computationally feasible.
"""
) )
progress_train = gr.Label(
label="Progress:"
)
logs_tts_train = gr.Textbox(
label="Logs:",
interactive=False,
)
demo.load(read_logs, None, logs_tts_train, every=1)
train_btn = gr.Button(value="Step 2 - Run the training")
def train_model(language, train_csv, eval_csv, num_epochs, batch_size, grad_acumm, output_path, max_audio_length):
clear_gpu_cache()
if not train_csv or not eval_csv:
return "You need to run the data processing step or manually set `Train CSV` and `Eval CSV` fields !", "", "", "", ""
try:
# convert seconds to waveform frames
max_audio_length = int(max_audio_length * 22050)
config_path, original_xtts_checkpoint, vocab_file, exp_path, speaker_wav = train_gpt(language, num_epochs, batch_size, grad_acumm, train_csv, eval_csv, output_path=str(output_path), max_audio_length=max_audio_length)
except:
traceback.print_exc()
error = traceback.format_exc()
return f"The training was interrupted due an error !! Please check the console to check the full error message! \n Error summary: {error}", "", "", "", ""
# copy original files to avoid parameters changes issues
shutil.copy(config_path, exp_path)
shutil.copy(vocab_file, exp_path)
ft_xtts_checkpoint = os.path.join(exp_path, "best_model.pth")
print("[FINETUNE] Model training done. Move to Step 3")
clear_gpu_cache()
return "Model training done. Move to Step 3", config_path, vocab_file, ft_xtts_checkpoint, speaker_wav
####################### #######################
#### GRADIO STEP 3 #### #### GRADIO STEP 3 ####
####################### #######################
@@ -1572,7 +1707,7 @@ if __name__ == "__main__":
refresh_button = create_refresh_button( refresh_button = create_refresh_button(
[xtts_checkpoint, xtts_config, xtts_vocab, speaker_reference_audio], [xtts_checkpoint, xtts_config, xtts_vocab, speaker_reference_audio],
[ [
lambda: {"choices": find_models(main_directory, "pth"), "value": ""}, lambda: {"choices": find_best_models(main_directory), "value": ""},
lambda: {"choices": find_jsons(main_directory, "config.json"), "value": ""}, lambda: {"choices": find_jsons(main_directory, "config.json"), "value": ""},
lambda: {"choices": find_jsons(main_directory, "vocab.json"), "value": ""}, lambda: {"choices": find_jsons(main_directory, "vocab.json"), "value": ""},
lambda: {"choices": get_available_voices(), "value": ""}, lambda: {"choices": get_available_voices(), "value": ""},
@@ -1605,7 +1740,8 @@ if __name__ == "__main__":
◽ **Note:** Sample wav files will be copied into the model folder. You will need to MANUALLY copy/move the WAV file(s) you want to use to the AllTalk voices folder, to make them available within the interface.<br> ◽ **Note:** Sample wav files will be copied into the model folder. You will need to MANUALLY copy/move the WAV file(s) you want to use to the AllTalk voices folder, to make them available within the interface.<br>
◽ **Note:** Text-generation-webui users, <span style="color: #3366ff;">OPTION A - Compact and move model to '/trainedmodel/'</span>. will make the model available for use within Text-gen-webui.<br> ◽ **Note:** Text-generation-webui users, <span style="color: #3366ff;">OPTION A - Compact and move model to '/trainedmodel/'</span>. will make the model available for use within Text-gen-webui.<br>
### 🟩 <u>Using your Finetuned model as you main model</u> ### 🟩 <u>Using your Finetuned model as you main model</u>
◽ If you wish to use the model you have just fintuned as your main model, you will need to MANUALLY copy it over the base model in <span style="color: #3366ff;">/models/trainedmodel/</span> after you have used Option A or B.<br> ◽ **Currently** only Text-gen-webui and SillyTavern have loaders built for loading models in the <span style="color: #3366ff;">'/finetuned/'</span> folder. So if you wish to use your finetuned model as elsewhere in AllTalk please copy it over your base model e.g. copy your model over the top of <span style="color: #3366ff;">/models/xttsv2_2.0.2/</span><br>
◽ If you wish to use the model you have just fintuned as your main model, you will need to MANUALLY copy it over the base model e.g copy it over <span style="color: #3366ff;">/models/xttsv2_2.0.2/</span> after you have used Option A or B.<br>
◽ Should you ever wish to revert your model back to the base model, you can delete the files in <span style="color: #3366ff;">/models/xttsv2_2.0.2/</span> and new copies will be re-downloaded when AllTalk starts up.<br> ◽ Should you ever wish to revert your model back to the base model, you can delete the files in <span style="color: #3366ff;">/models/xttsv2_2.0.2/</span> and new copies will be re-downloaded when AllTalk starts up.<br>
### 🟨 <u>Clearing up disk space</u> ### 🟨 <u>Clearing up disk space</u>
◽ If you are not going to train anything again, you can delete the whisper model from inside of your huggingface cache (3GB approx) <br> ◽ If you are not going to train anything again, you can delete the whisper model from inside of your huggingface cache (3GB approx) <br>
@@ -1617,6 +1753,21 @@ if __name__ == "__main__":
final_progress_data = gr.Label( final_progress_data = gr.Label(
label="Progress:" label="Progress:"
) )
with gr.Row():
xtts_checkpoint_copy = gr.Dropdown(
[str(file) for file in xtts_checkpoint_files],
label="XTTS checkpoint path (Select the model you want to copy over):",
value="",
allow_custom_value=True,
scale=3,
)
# Create refresh button
refresh_button = create_refresh_button(
[xtts_checkpoint_copy,],
[
lambda: {"choices": find_best_models(main_directory), "value": ""},
],
elem_class="refresh-button-class")
with gr.Row(): with gr.Row():
compact_btn = gr.Button(value="OPTION A - Compact and move model to '/trainedmodel/'") compact_btn = gr.Button(value="OPTION A - Compact and move model to '/trainedmodel/'")
gr.Markdown( gr.Markdown(
@@ -1631,6 +1782,9 @@ if __name__ == "__main__":
This will compact the model and move it, along with the reference wav's to <span style="color: #3366ff;">/models/lastfinetuned/</span> and will <span style="color: red;">OVERWRITE</span> any model in that location. This will just leave you a finetuned model that you can do with as you please. It will <span style="color: red;">NOT</span> be available in Text-gen-webui unless you copy it over one of the other model loader locations. This will compact the model and move it, along with the reference wav's to <span style="color: #3366ff;">/models/lastfinetuned/</span> and will <span style="color: red;">OVERWRITE</span> any model in that location. This will just leave you a finetuned model that you can do with as you please. It will <span style="color: red;">NOT</span> be available in Text-gen-webui unless you copy it over one of the other model loader locations.
""" """
) )
with gr.Row():
compact_custom_btn = gr.Button(value="OPTION C - Compact and move model to a folder name of your choosing")
folder_path = gr.Textbox(label="Enter a new folder name (will be sub the models folder)", lines=1, value="mycustomfolder")
with gr.Row(): with gr.Row():
delete_training_btn = gr.Button(value="Delete generated training data") delete_training_btn = gr.Button(value="Delete generated training data")
gr.Markdown( gr.Markdown(
@@ -1653,6 +1807,7 @@ if __name__ == "__main__":
whisper_model, whisper_model,
out_path, out_path,
eval_split_number, eval_split_number,
speaker_name_input,
], ],
outputs=[ outputs=[
progress_data, progress_data,
@@ -1668,6 +1823,7 @@ if __name__ == "__main__":
lang, lang,
train_csv, train_csv,
eval_csv, eval_csv,
learning_rates,
num_epochs, num_epochs,
batch_size, batch_size,
grad_acumm, grad_acumm,
@@ -1695,14 +1851,20 @@ if __name__ == "__main__":
speaker_reference_audio, speaker_reference_audio,
], ],
outputs=[progress_gen, tts_output_audio, reference_audio], outputs=[progress_gen, tts_output_audio, reference_audio],
) )
compact_btn.click( compact_btn.click(
fn=compact_model, fn=compact_model,
inputs=[xtts_checkpoint_copy],
outputs=[final_progress_data], outputs=[final_progress_data],
) )
compact_lastfinetuned_btn.click( compact_lastfinetuned_btn.click(
fn=compact_lastfinetuned_model, fn=compact_lastfinetuned_model,
inputs=[xtts_checkpoint_copy],
outputs=[final_progress_data],
)
compact_custom_btn.click(
fn=compact_custom_model,
inputs=[xtts_checkpoint_copy, folder_path],
outputs=[final_progress_data], outputs=[final_progress_data],
) )
delete_training_btn.click( delete_training_btn.click(
+99 -51
View File
@@ -1,10 +1,13 @@
import os import os
import sys
from pathlib import Path from pathlib import Path
import requests import requests
from requests.exceptions import ConnectionError
from tqdm import tqdm from tqdm import tqdm
import importlib.metadata as metadata import importlib.metadata as metadata
import json import json
from packaging import version from packaging import version
from datetime import datetime
################################################################# #################################################################
#### LOAD PARAMS FROM confignew.json - REQUIRED FOR BRANDING #### #### LOAD PARAMS FROM confignew.json - REQUIRED FOR BRANDING ####
@@ -12,14 +15,12 @@ from packaging import version
# STARTUP VARIABLE - Create "this_dir" variable as the current script directory # STARTUP VARIABLE - Create "this_dir" variable as the current script directory
this_dir = Path(__file__).parent.resolve() this_dir = Path(__file__).parent.resolve()
# load standard config file in and get settings # load standard config file in and get settings
def load_config(file_path): def load_config(file_path):
with open(file_path, "r") as config_file: with open(file_path, "r") as config_file:
config = json.load(config_file) config = json.load(config_file)
return config return config
config_file_path = this_dir / "confignew.json" config_file_path = this_dir / "confignew.json"
params = load_config(config_file_path) params = load_config(config_file_path)
@@ -37,12 +38,8 @@ if modeldownload_config_file_path.exists():
files_to_download = settings.get("files_to_download", {}) files_to_download = settings.get("files_to_download", {})
else: else:
# Default settings if the JSON file doesn't exist or is empty # Default settings if the JSON file doesn't exist or is empty
print( print(f"[{params['branding']}Startup] \033[91mWarning\033[0m modeldownload.json is missing so please re-download it and save it in the coquii_tts main folder.")
f"[{params['branding']}Startup] \033[91mWarning\033[0m modeldownload.json is missing so please re-download it and save it in the coquii_tts main folder." print(f"[{params['branding']}Startup] \033[91mWarning\033[0m API Local and XTTSv2 Local will error unless this is corrected.")
)
print(
f"[{params['branding']}Startup] \033[91mWarning\033[0m API Local and XTTSv2 Local will error unless this is corrected."
)
# Read the version specifier from requirements_nvidia.txt # Read the version specifier from requirements_nvidia.txt
with open(this_dir / "system" / "requirements" / "requirements_standalone.txt", "r") as req_file: with open(this_dir / "system" / "requirements" / "requirements_standalone.txt", "r") as req_file:
@@ -55,16 +52,12 @@ for req in requirements:
break break
if tts_version_required is None: if tts_version_required is None:
raise ValueError( raise ValueError(f"[{params['branding']}Startup] \033[91mWarning\033[0m Could not find TTS version specifier in requirements file")
f"[{params['branding']}Startup] \033[91mWarning\033[0m Could not find TTS version specifier in requirements file"
)
def create_directory_if_not_exists(directory): def create_directory_if_not_exists(directory):
if not directory.exists(): if not directory.exists():
directory.mkdir(parents=True) directory.mkdir(parents=True)
def download_file(url, destination): def download_file(url, destination):
response = requests.get(url, stream=True) response = requests.get(url, stream=True)
total_size_in_bytes = int(response.headers.get("content-length", 0)) total_size_in_bytes = int(response.headers.get("content-length", 0))
@@ -83,63 +76,118 @@ def download_file(url, destination):
def check_tts_version(): def check_tts_version():
try: try:
tts_version = metadata.version("tts") tts_version = metadata.version("tts")
print( print(f"[{params['branding']}Startup] \033[92mCurrent TTS Version : \033[93m"+tts_version+"\033[0m")
f"[{params['branding']}Startup] TTS version installed: \033[93m"
+ tts_version
+ "\033[0m"
)
if version.parse(tts_version) < version.parse(tts_version_required): if version.parse(tts_version) < version.parse(tts_version_required):
print( print(f"[{params['branding']}Startup] \033[91mWarning\033[0m TTS version is too old. Please upgrade to version \033[93m"+tts_version_required+"\033[0m or later.\033[0m")
f"[{params['branding']}Startup] \033[91mWarning\033[0m TTS version is too old. Please upgrade to version \033[93m" print(f"[{params['branding']}Startup] \033[91mWarning\033[0m At your terminal/command prompt \033[94mpip install --upgrade tts\033[0m")
+ tts_version_required
+ "\033[0m or later.\033[0m"
)
print(
f"[{params['branding']}Startup] \033[91mWarning\033[0m At your terminal/command prompt \033[94mpip install --upgrade tts\033[0m"
)
else: else:
print(f"[{params['branding']}Startup] TTS version is up to date.") print(f"[{params['branding']}Startup] \033[92mCurrent TTS Version is :\033[93m Up to date\033[0m")
except metadata.PackageNotFoundError: except metadata.PackageNotFoundError:
print( print(f"[{params['branding']}Startup] \033[91mWarning\033[0m TTS is not installed")
f"[{params['branding']}Startup] \033[91mWarning\033[0m TTS is not installed."
)
def check_torch_version():
import torch
pytorch_version = torch.__version__
cuda_version = torch.version.cuda
major, minor, micro = sys.version_info[:3]
python_version = f"{major}.{minor}.{micro}"
print(f"[{params['branding']}Startup] \033[92mCurrent Python Version :\033[93m {python_version}\033[0m")
print(f"[{params['branding']}Startup] \033[92mCurrent PyTorch Version:\033[93m {pytorch_version}\033[0m")
if cuda_version is None:
print(f"[{params['branding']}Startup] \033[92mCurrent CUDA Version :\033[91m CUDA is not available\033[0m")
else:
print(f"[{params['branding']}Startup] \033[92mCurrent CUDA Version :\033[93m {cuda_version}\033[0m")
def ordinal(n):
return "%d%s" % (n, "th" if 4 <= n % 100 <= 20 else {1: "st", 2: "nd", 3: "rd"}.get(n % 10, "th"))
def format_datetime(iso_str):
# Parse the ISO 8601 string to datetime
dt = datetime.strptime(iso_str, "%Y-%m-%dT%H:%M:%SZ")
# Format the datetime into a more readable string
return dt.strftime(f"{ordinal(dt.day)} %B %Y at %H:%M")
def fetch_latest_commit_sha_and_date(owner, repo):
url = f"https://api.github.com/repos/{owner}/{repo}/commits"
try:
response = requests.get(url)
if response.status_code == 200:
latest_commit = response.json()[0]
latest_commit_sha = latest_commit['sha']
latest_commit_date = latest_commit['commit']['committer']['date']
return latest_commit_sha, latest_commit_date
else:
print(f"[{params['branding']}Startup] \033[92m{params['branding']}Github updated :\033[91m Failed to fetch the latest commits due to an unexpected response from GitHub")
return None, None
except ConnectionError:
# This block is executed when a connection error occurs
print(f"[{params['branding']}Startup] \033[92m{params['branding']}Github updated :\033[91m Could not reach GitHub to check for updates\033[0m")
return None, None
def read_or_initialize_sha(file_path):
if os.path.exists(file_path):
with open(file_path, 'r') as file:
data = json.load(file)
return data.get("last_known_commit_sha")
else:
# File doesn't exist, fetch the latest SHA and create the file
latest_commit_sha = fetch_latest_commit_sha_and_date(github_site, github_repository)
if latest_commit_sha:
with open(file_path, 'w') as file:
json.dump({"last_known_commit_sha": latest_commit_sha}, file)
return latest_commit_sha
else:
# Handle the case where GitHub couldn't be reached
return None
def update_sha_file(file_path, new_sha):
with open(file_path, 'w') as file:
json.dump({"last_known_commit_sha": new_sha}, file)
# Check and create directories # Check and create directories
if str(base_path) == "models": if str(base_path) == "models":
create_directory_if_not_exists(this_dir / base_path / model_path) create_directory_if_not_exists(this_dir / base_path / model_path)
else: else:
create_directory_if_not_exists(base_path / model_path) create_directory_if_not_exists(base_path / model_path)
print( print(f"[{params['branding']}Startup] \033[94mInfo\033[0m Custom path set in \033[93mmodeldownload.json\033[0m. Using the following settings:")
f"[{params['branding']}Startup] \033[94mInfo\033[0m Custom path set in \033[93mmodeldownload.json\033[0m. Using the following settings:" print(f"[{params['branding']}Startup] \033[94mInfo\033[0m Base folder Path:\033[93m",base_path,"\033[0m",)
) print(f"[{params['branding']}Startup] \033[94mInfo\033[0m Model folder Path:\033[93m",model_path,"\033[0m",)
print( print(f"[{params['branding']}Startup] \033[94mInfo\033[0m Full Path:\033[93m",base_path / model_path,"\033[0m",)
f"[{params['branding']}Startup] \033[94mInfo\033[0m Base folder Path:\033[93m",
base_path,
"\033[0m",
)
print(
f"[{params['branding']}Startup] \033[94mInfo\033[0m Model folder Path:\033[93m",
model_path,
"\033[0m",
)
print(
f"[{params['branding']}Startup] \033[94mInfo\033[0m Full Path:\033[93m",
base_path / model_path,
"\033[0m",
)
# Download files if they don't exist # Download files if they don't exist
print("[" + params["branding"] + "Startup] Checking Model is Downloaded.") print(f"[{params['branding']}Startup] \033[92mModel is available :\033[93m Checking\033[0m")
for filename, url in files_to_download.items(): for filename, url in files_to_download.items():
if str(base_path) == "models": if str(base_path) == "models":
destination = this_dir / base_path / model_path / filename destination = this_dir / base_path / model_path / filename
else: else:
destination = Path(base_path) / model_path / filename destination = Path(base_path) / model_path / filename
if not destination.exists(): if not destination.exists():
print(f"[{params['branding']}Startup] Downloading " + filename + "...") print(f"[{params['branding']}Startup] \033[92mModel Downloading file : \033[93m" + filename + "\033[0m")
download_file(url, destination) download_file(url, destination)
print(f"[{params['branding']}Startup] \033[92mModel is available :\033[93m Checked\033[0m")
github_site = "erew123"
github_repository = "alltalk_tts"
check_torch_version()
check_tts_version() check_tts_version()
# Define the file path based on your directory structure
sha_file_path = this_dir / "system" / "config" / "at_github_sha.json"
# Read or initialize the SHA (adjusted for handling both SHA and date)
last_known_commit_sha = read_or_initialize_sha(sha_file_path) # Assuming adjustment for date
# Assuming you have fetched the latest commit SHA and date
latest_commit_sha, latest_commit_date = fetch_latest_commit_sha_and_date(github_site, github_repository)
formatted_date = format_datetime(latest_commit_date) if latest_commit_date else "an unknown date"
if latest_commit_sha and latest_commit_sha != last_known_commit_sha:
#print(f"There's an update available for alltalk_tts.")
print(f"[{params['branding']}Startup] \033[92m{params['branding']}Github updated :\033[93m {formatted_date}\033[0m")
# Update the file with the new SHA
update_sha_file(sha_file_path, latest_commit_sha)
elif latest_commit_sha == last_known_commit_sha:
#print(f"Your alltalk_tts software is up to date.")
print(f"[{params['branding']}Startup] \033[92m{params['branding']}Github updated :\033[93m {formatted_date}\033[0m")
+21 -19
View File
@@ -1,20 +1,22 @@
FROM nvidia/cuda:12.1.1-devel-ubuntu22.04 FROM nvidia/cuda:12.1.1-devel-ubuntu22.04
ENV HOST 0.0.0.0 ENV HOST 0.0.0.0
RUN apt-get update && apt-get upgrade -y \ RUN apt-get update && apt-get upgrade -y \
&& apt-get install -y git build-essential portaudio19-dev \ && apt-get install -y git build-essential portaudio19-dev \
python3 python3-pip gcc wget \ python3 python3-pip gcc wget \
ocl-icd-opencl-dev opencl-headers clinfo \ ocl-icd-opencl-dev opencl-headers clinfo \
libclblast-dev libopenblas-dev \ libclblast-dev libopenblas-dev \
&& mkdir -p /etc/OpenCL/vendors && echo "libnvidia-opencl.so.1" > /etc/OpenCL/vendors/nvidia.icd \ && mkdir -p /etc/OpenCL/vendors && echo "libnvidia-opencl.so.1" > /etc/OpenCL/vendors/nvidia.icd \
&& ln -s /usr/bin/python3 /usr/bin/python && ln -s /usr/bin/python3 /usr/bin/python
WORKDIR /app
COPY . . RUN mkdir /app/pip_cache
ENV CUDA_DOCKER_ARCH=all ENV PIP_CACHE_DIR=/app/pip_cache
RUN pip install --no-cache-dir -r requirements_nvidia.txt && \ WORKDIR /app
pip install --no-cache-dir -r requirements_finetune.txt && \ COPY . .
pip install --no-cache-dir -r requirements_other.txt && \ ENV CUDA_DOCKER_ARCH=all
pip install --no-cache-dir deepspeed RUN pip install --no-cache-dir --no-deps -r system/requirements/requirements_standalone.txt --cache-dir=/app/pip_cache && \
EXPOSE 7851 pip install --no-cache-dir deepspeed --cache-dir=/app/pip_cache
RUN chmod +x launch.sh
EXPOSE 7851
RUN chmod +x launch.sh
ENTRYPOINT ["sh", "-c", "./launch.sh"] ENTRYPOINT ["sh", "-c", "./launch.sh"]
+125 -112
View File
@@ -9,12 +9,16 @@ import threading
import signal import signal
import sys import sys
import atexit import atexit
import shutil
from pathlib import Path from pathlib import Path
from datetime import datetime, timedelta from datetime import datetime, timedelta
import re import re
import numpy as np import numpy as np
import soundfile as sf import soundfile as sf
import uuid import uuid
import logging
# Store the current disable level
current_disable_level = logging.getLogger().manager.disable
###################################### ######################################
#### ALLTALK ALLOWED STARTUP TIME #### #### ALLTALK ALLOWED STARTUP TIME ####
@@ -30,43 +34,109 @@ startup_wait_time = 120
# STARTUP VARIABLE - Create "this_dir" variable as the current script directory # STARTUP VARIABLE - Create "this_dir" variable as the current script directory
this_dir = Path(__file__).parent.resolve() this_dir = Path(__file__).parent.resolve()
# load config file in and get settings # load config file in and get settings
def load_config(file_path): def load_config(file_path):
with open(file_path, "r") as config_file: with open(file_path, "r") as config_file:
config = json.load(config_file) config = json.load(config_file)
return config return config
config_file_path = this_dir / "confignew.json" config_file_path = this_dir / "confignew.json"
# Load the params dictionary from the confignew.json file # Load the params dictionary from the confignew.json file
params = load_config(config_file_path) params = load_config(config_file_path)
print(f"[{params['branding']}Startup]\033[94m _ _ _ \033[1;35m_____ _ _ \033[0m _____ _____ ____ ")
print(f"[{params['branding']}Startup]\033[94m / \ | | |\033[1;35m_ _|_ _| | | __ \033[0m |_ _|_ _/ ___| ")
print(f"[{params['branding']}Startup]\033[94m / _ \ | | |\033[1;35m | |/ _` | | |/ / \033[0m | | | | \___ \ ")
print(f"[{params['branding']}Startup]\033[94m / ___ \| | |\033[1;35m | | (_| | | < \033[0m | | | | ___) |")
print(f"[{params['branding']}Startup]\033[94m /_/ \_\_|_|\033[1;35m |_|\__,_|_|_|\_\ \033[0m |_| |_| |____/ ")
print(f"[{params['branding']}Startup]")
##############################################
#### Update any changes to confignew.json ####
##############################################
update_config_path = this_dir / "system" / "config" / "at_configupdate.json"
downgrade_config_path = this_dir / "system" / "config" / "at_configdowngrade.json"
def changes_needed(main_config, update_config, downgrade_config):
"""Check if there are any changes to be made to the main configuration."""
for key in downgrade_config.keys():
if key in main_config:
return True
for key, value in update_config.items():
if key not in main_config:
return True
return False
def update_config(config_file_path, update_config_path, downgrade_config_path):
try:
with open(config_file_path, 'r') as file:
main_config = json.load(file)
with open(update_config_path, 'r') as file:
update_config = json.load(file)
with open(downgrade_config_path, 'r') as file:
downgrade_config = json.load(file)
# Determine if changes are needed
if changes_needed(main_config, update_config, downgrade_config):
# Backup with timestamp to avoid overwriting
timestamp = datetime.now().strftime("%Y%m%d%H%M%S")
backup_path = config_file_path.with_suffix(f".{timestamp}.bak")
logging.info(f"Creating backup of the main config to {backup_path}")
shutil.copy(config_file_path, backup_path)
# Proceed with updates and downgrades
for key, value in update_config.items():
if key not in main_config:
main_config[key] = value
for key in downgrade_config.keys():
if key in main_config:
del main_config[key]
# Save the updated configuration
with open(config_file_path, 'w') as file:
json.dump(main_config, file, indent=4)
print(f"[{params['branding']}Startup] \033[92mConfig file check : \033[91mUpdates applied\033[0m")
else:
print(f"[{params['branding']}Startup] \033[92mConfig file check : \033[93mNo Updates required\033[0m")
except Exception as e:
print(f"[{params['branding']}Startup] \033[92mConfig file check : \033[91mError updating\033[0m")
# Update the configuration
update_config(config_file_path, update_config_path, downgrade_config_path)
# Re-Load the params dictionary from the confignew.json file
params = load_config(config_file_path)
#########################################
#### Continue on with Startup Checks ####
#########################################
# Required for sentence splitting # Required for sentence splitting
try: try:
from TTS.api import TTS from TTS.api import TTS
from TTS.utils.synthesizer import Synthesizer from TTS.utils.synthesizer import Synthesizer
except ModuleNotFoundError: except ModuleNotFoundError:
logger.error( # Inform the user about the missing module and suggest next steps
f"[{params['branding']}]\033[91mWarning\033[0m Could not find the TTS module. Make sure to install the requirements for the {params['branding']} extension." print(f"[{params['branding']}]\033[91mWarning\033[0m Could not find the TTS module. Make sure to install the requirements for the {params['branding']} extension.")
f"[{params['branding']}]\033[91mWarning\033[0m Linux / Mac:\npip install -r /alltalk_tts/requirements.txt\n" print(f"[{params['branding']}]\033[91mWarning\033[0m Please use the ATSetup utility or check the Github installation instructions.")
f"[{params['branding']}]\033[91mWarning\033[0m Windows:\npip install -r \\alltalk_tts\\requirements.txt\n" # Re-raise the ModuleNotFoundError to stop the program and print the traceback
f"[{params['branding']}]\033[91mWarning\033[0m If you used the one-click installer, paste the command above in the terminal window launched after running the cmd_ script. On Windows, that's cmd_windows.bat." raise
)
raise
# IMPORT - Attempt Importing DeepSpeed (required for displaying Deepspeed checkbox in gradio) # Suppress logging
logging.disable(logging.ERROR)
try: try:
import deepspeed import deepspeed
deepspeed_installed = True deepspeed_installed = True
except ImportError: except ImportError:
deepspeed_installed = False deepspeed_installed = False
# Restore previous logging level
logging.disable(current_disable_level)
# Import gradio if being used within text generation webUI # Import gradio if being used within text generation webUI
try: try:
import gradio as gr import gradio as gr
from modules import chat, shared, ui_chat from modules import chat, shared, ui_chat
from modules.logging_colors import logger from modules.logging_colors import logger
from modules.ui import create_refresh_button from modules.ui import create_refresh_button
@@ -75,9 +145,10 @@ try:
# This is set to check if the script is being run within text generation webui or as a standalone script. False is running as part of text gen web ui or a gradio interface # This is set to check if the script is being run within text generation webui or as a standalone script. False is running as part of text gen web ui or a gradio interface
running_in_standalone = False running_in_standalone = False
output_folder_wav = params["output_folder_wav"] output_folder_wav = params["output_folder_wav"]
print(f"[{params['branding']}Startup] \033[92m{params['branding']}startup Mode : \033[93mText-Gen-webui mode\033[0m")
except ModuleNotFoundError: except ModuleNotFoundError:
output_folder_wav = params["output_folder_wav_standalone"] output_folder_wav = params["output_folder_wav_standalone"]
print(f"[{params['branding']}Startup] Running script.py in standalone mode") print(f"[{params['branding']}Startup] \033[92m{params['branding']}startup Mode : \033[93mStandalone mode\033[0m")
# This is set to check if the script is being run within text generation webui or as a standalone script. true means standalone # This is set to check if the script is being run within text generation webui or as a standalone script. true means standalone
running_in_standalone = True running_in_standalone = True
@@ -98,23 +169,12 @@ def get_available_voices():
return sorted([voice.name for voice in Path(f"{this_dir}/voices").glob("*.wav")]) return sorted([voice.name for voice in Path(f"{this_dir}/voices").glob("*.wav")])
#########################
#### LICENSE DISPLAY ####
#########################
# STARTUP Display Licence Information
print(f"[{params['branding']}Startup] \033[94mCoqui Public Model License\033[0m")
print(f"[{params['branding']}Startup] \033[94mhttps://coqui.ai/cpml.txt\033[0m")
############################################ ############################################
#### DELETE OLD OUTPUT WAV FILES IF SET #### #### DELETE OLD OUTPUT WAV FILES IF SET ####
############################################ ############################################
def delete_old_files(folder_path, days_to_keep): def delete_old_files(folder_path, days_to_keep):
current_time = datetime.now() current_time = datetime.now()
print( print(f"[{params['branding']}Startup] \033[92mWAV file deletion :\033[93m", delete_output_wavs_setting,"\033[0m")
f"[{params['branding']}Startup] Deletion of old output folder WAV files is currently enabled and set at",
delete_output_wavs_setting,
)
for file_name in os.listdir(folder_path): for file_name in os.listdir(folder_path):
file_path = os.path.join(folder_path, file_name) file_path = os.path.join(folder_path, file_name)
if os.path.isfile(file_path): if os.path.isfile(file_path):
@@ -130,20 +190,18 @@ output_folder_wav = os.path.normpath(output_folder_wav)
# Check and perform file deletion # Check and perform file deletion
if delete_output_wavs_setting.lower() == "disabled": if delete_output_wavs_setting.lower() == "disabled":
print( print("["+ params["branding"]+"Startup] \033[92mWAV file deletion :\033[93m Disabled\033[0m")
"["
+ params["branding"]
+ "Startup] Old output wav file deletion is set to disabled."
)
else: else:
try: try:
days_to_keep = int(delete_output_wavs_setting.split()[0]) days_to_keep = int(delete_output_wavs_setting.split()[0])
delete_old_files(output_folder_wav, days_to_keep) delete_old_files(output_folder_wav, days_to_keep)
except ValueError: except ValueError:
print( print(f"[{params['branding']}Startup] \033[92mWAV file deletion :\033[93m Invalid setting for deleting old wav files. Please use 'Disabled' or 'X Days' format\033[0m")
f"[{params['branding']}Startup] Invalid setting for deleting old wav files. Please use 'Disabled' or 'X Days' format."
)
if deepspeed_installed:
print(f"[{params['branding']}Startup] \033[92mDeepSpeed version :\033[93m",deepspeed.__version__,"\033[0m")
else:
print(f"[{params['branding']}Startup] \033[92mDeepSpeed version :\033[91m Not Detected\033[0m")
######################## ########################
#### STARTUP CHECKS #### #### STARTUP CHECKS ####
@@ -153,8 +211,6 @@ def check_required_files():
this_dir = Path(__file__).parent.resolve() this_dir = Path(__file__).parent.resolve()
download_script_path = this_dir / "modeldownload.py" download_script_path = this_dir / "modeldownload.py"
subprocess.run(["python", str(download_script_path)]) subprocess.run(["python", str(download_script_path)])
print(f"[{params['branding']}Startup] All required files are present.")
# STARTUP Call Check routine # STARTUP Call Check routine
check_required_files() check_required_files()
@@ -174,7 +230,7 @@ if finetuned_model:
(trained_model_directory / file).exists() for file in required_files (trained_model_directory / file).exists() for file in required_files
) )
if finetuned_model: if finetuned_model:
print(f"[{params['branding']}Startup] Finetuned model \033[93mDetected\033[0m") print(f"[{params['branding']}Startup] \033[92mFinetuned model :\033[93m Detected\033[0m")
#################################################### ####################################################
#### SET GRADIO BUTTONS BASED ON confignew.json #### #### SET GRADIO BUTTONS BASED ON confignew.json ####
@@ -190,7 +246,6 @@ elif params["tts_method_xtts_local"] == True:
# Set the default for Narrated text without asterisk or quotes to be Narrator # Set the default for Narrated text without asterisk or quotes to be Narrator
non_quoted_text_is = True non_quoted_text_is = True
###################### ######################
#### GRADIO STUFF #### #### GRADIO STUFF ####
###################### ######################
@@ -237,9 +292,7 @@ script_path = this_dir / "tts_server.py"
def signal_handler(sig, frame): def signal_handler(sig, frame):
print( print(f"[{params['branding']}Shutdown] \033[94mReceived Ctrl+C, terminating subprocess\033[92m")
f"[{params['branding']}Shutdown] \033[94mReceived Ctrl+C, terminating subprocess\033[92m"
)
if process.poll() is None: if process.poll() is None:
process.terminate() process.terminate()
process.wait() # Wait for the subprocess to finish process.wait() # Wait for the subprocess to finish
@@ -258,33 +311,20 @@ else:
process = subprocess.Popen(["python", script_path]) process = subprocess.Popen(["python", script_path])
# Check if the subprocess has started successfully # Check if the subprocess has started successfully
if process.poll() is None: if process.poll() is None:
print(f"[{params['branding']}Startup] TTS Subprocess starting") print(f"[{params['branding']}Startup] \033[92mTTS Subprocess :\033[93m Starting up\033[0m")
print(f"[{params['branding']}Startup]") print(f"[{params['branding']}Startup]")
print( print(
f"[{params['branding']}Startup] \033[94m {params['branding']}Settings & Documentation:\033[00m", f"[{params['branding']}Startup] \033[94m{params['branding']}Settings & Documentation:\033[00m",
f"\033[92mhttp://{params['ip_address']}:{params['port_number']}\033[00m", f"\033[92mhttp://{params['ip_address']}:{params['port_number']}\033[00m",
) )
print(f"[{params['branding']}Startup]") print(f"[{params['branding']}Startup]")
else: else:
print( print(f"[{params['branding']}Startup] \033[91mWarning\033[0m TTS Subprocess Webserver failing to start process")
f"[{params['branding']}Startup] \033[91mWarning\033[0m TTS Subprocess Webserver failing to start process" print(f"[{params['branding']}Startup] \033[91mWarning\033[0m It could be that you have something on port:",params["port_number"],)
) print(f"[{params['branding']}Startup] \033[91mWarning\033[0m Or you have not started in a Python environement with all the necesssary bits installed")
print( print(f"[{params['branding']}Startup] \033[91mWarning\033[0m Check you are starting Text-generation-webui with either the start_xxxxx file or the Python environment with cmd_xxxxx file.")
f"[{params['branding']}Startup] \033[91mWarning\033[0m It could be that you have something on port:", print(f"[{params['branding']}Startup] \033[91mWarning\033[0m xxxxx is the type of OS you are on e.g. windows, linux or mac.")
params["port_number"], print(f"[{params['branding']}Startup] \033[91mWarning\033[0m Alternatively, you could check no other Python processes are running that shouldnt be e.g. Restart your computer is the simple way.")
)
print(
f"[{params['branding']}Startup] \033[91mWarning\033[0m Or you have not started in a Python environement with all the necesssary bits installed"
)
print(
f"[{params['branding']}Startup] \033[91mWarning\033[0m Check you are starting Text-generation-webui with either the start_xxxxx file or the Python environment with cmd_xxxxx file."
)
print(
f"[{params['branding']}Startup] \033[91mWarning\033[0m xxxxx is the type of OS you are on e.g. windows, linux or mac."
)
print(
f"[{params['branding']}Startup] \033[91mWarning\033[0m Alternatively, you could check no other Python processes are running that shouldnt be e.g. Restart your computer is the simple way."
)
# Cleanly kill off this script, but allow text-generation-webui to keep running, albeit without this alltalk_tts # Cleanly kill off this script, but allow text-generation-webui to keep running, albeit without this alltalk_tts
sys.exit(1) sys.exit(1)
@@ -300,26 +340,14 @@ else:
break break
except requests.RequestException as e: except requests.RequestException as e:
# Print the exception for debugging purposes # Print the exception for debugging purposes
print( print(f"[{params['branding']}Startup] \033[91mWarning\033[0m TTS Subprocess has NOT started up yet, Will keep trying for {timeout} seconds maximum. Please wait.")
f"[{params['branding']}Startup] \033[91mWarning\033[0m TTS Subprocess has NOT started up yet, Will keep trying for {timeout} seconds maximum. Please wait."
)
time.sleep(5) time.sleep(5)
else: else:
print( print(f"\n[{params['branding']}Startup] Startup timed out. Full help available here \033[92mhttps://github.com/erew123/alltalk_tts#-help-with-problems\033[0m")
f"\n[{params['branding']}Startup] Startup timed out. Full help available here \033[92mhttps://github.com/erew123/alltalk_tts#-help-with-problems\033[0m" print(f"[{params['branding']}Startup] On older system you may wish to open and edit \033[94mscript.py\033[0m with a text editor and changing the")
) print(f"[{params['branding']}Startup] \033[94mstartup_wait_time = 120\033[0m setting to something like \033[94mstartup_wait_time = 240\033[0m as this will allow")
print( print(f"[{params['branding']}Startup] AllTalk more time to try load the model into your VRAM. Otherise please visit the Github for")
f"[{params['branding']}Startup] On older system you may wish to open and edit \033[94mscript.py\033[0m with a text editor and changing the" print(f"[{params['branding']}Startup] a list of other possible troubleshooting options.")
)
print(
f"[{params['branding']}Startup] \033[94mstartup_wait_time = 120\033[0m setting to something like \033[94mstartup_wait_time = 240\033[0m as this will allow"
)
print(
f"[{params['branding']}Startup] AllTalk more time to try load the model into your VRAM. Otherise please visit the Github for"
)
print(
f"[{params['branding']}Startup] a list of other possible troubleshooting options."
)
# Cleanly kill off this script, but allow text-generation-webui to keep running, albeit without this alltalk_tts # Cleanly kill off this script, but allow text-generation-webui to keep running, albeit without this alltalk_tts
sys.exit(1) sys.exit(1)
@@ -371,9 +399,7 @@ def send_reload_request(tts_method):
return f'<audio src="file/{audio_path}" controls autoplay></audio>' return f'<audio src="file/{audio_path}" controls autoplay></audio>'
except requests.exceptions.RequestException as e: except requests.exceptions.RequestException as e:
# Handle the HTTP request error # Handle the HTTP request error
print( print(f"[{params['branding']}Server] \033[91mWarning\033[0m Error during request to webserver process: {e}")
f"[{params['branding']}Server] \033[91mWarning\033[0m Error during request to webserver process: {e}"
)
return {"status": "error", "message": str(e)} return {"status": "error", "message": str(e)}
@@ -400,9 +426,7 @@ def send_lowvram_request(low_vram):
return f'<audio src="file/{audio_path}" controls autoplay></audio>' return f'<audio src="file/{audio_path}" controls autoplay></audio>'
except requests.exceptions.RequestException as e: except requests.exceptions.RequestException as e:
# Handle the HTTP request error # Handle the HTTP request error
print( print(f"[{params['branding']}Server] \033[91mWarning\033[0m Error during request to webserver process: {e}")
f"[{params['branding']}Server] \033[91mWarning\033[0m Error during request to webserver process: {e}"
)
return {"status": "error", "message": str(e)} return {"status": "error", "message": str(e)}
@@ -417,7 +441,6 @@ def send_deepspeed_request(deepspeed_param):
audio_path = this_dir / "system" / "at_sounds" / "deepspeedenabled.wav" audio_path = this_dir / "system" / "at_sounds" / "deepspeedenabled.wav"
else: else:
audio_path = this_dir / "system" / "at_sounds" / "deepspeeddisabled.wav" audio_path = this_dir / "system" / "at_sounds" / "deepspeeddisabled.wav"
url = f"{base_url}/api/deepspeed?new_deepspeed_value={deepspeed_param}" url = f"{base_url}/api/deepspeed?new_deepspeed_value={deepspeed_param}"
headers = {"Content-Type": "application/json"} headers = {"Content-Type": "application/json"}
response = requests.post(url, headers=headers) response = requests.post(url, headers=headers)
@@ -430,9 +453,7 @@ def send_deepspeed_request(deepspeed_param):
return f'<audio src="file/{audio_path}" controls autoplay></audio>' return f'<audio src="file/{audio_path}" controls autoplay></audio>'
except requests.exceptions.RequestException as e: except requests.exceptions.RequestException as e:
# Handle the HTTP request error # Handle the HTTP request error
print( print(f"[{params['branding']}Server] \033[91mWarning\033[0m Error during request to webserver process: {e}")
f"[{params['branding']}Server] \033[91mWarning\033[0m Error during request to webserver process: {e}"
)
return {"status": "error", "message": str(e)} return {"status": "error", "message": str(e)}
@@ -465,9 +486,7 @@ Synthesizer.split_into_sentences = new_split_into_sentences
def before_audio_generation(string, params): def before_audio_generation(string, params):
# Check Model is loaded into cuda or cpu and error if not # Check Model is loaded into cuda or cpu and error if not
if not params["tts_model_loaded"]: if not params["tts_model_loaded"]:
print( print(f"[{params['branding']}Model] \033[91mWarning\033[0m Model is still loading, please wait before trying to generate TTS")
f"[{params['branding']}Model] \033[91mWarning\033[0m Model is still loading, please wait before trying to generate TTS"
)
return return
string = html.unescape(string) or random_sentence() string = html.unescape(string) or random_sentence()
if string == "": if string == "":
@@ -491,13 +510,9 @@ def combine(audio_files, output_folder, state):
# Save the combined audio to a file with a specified sample rate # Save the combined audio to a file with a specified sample rate
if "character_menu" in state: if "character_menu" in state:
output_file_path = os.path.join( output_file_path = os.path.join(output_folder, f'{state["character_menu"]}_{int(time.time())}_combined.wav')
output_folder, f'{state["character_menu"]}_{int(time.time())}_combined.wav'
)
else: else:
output_file_path = os.path.join( output_file_path = os.path.join(output_folder, f"TTSOUT_{int(time.time())}_combined.wav")
output_folder, f"TTSOUT_{int(time.time())}_combined.wav"
)
sf.write(output_file_path, audio, samplerate=sample_rate) sf.write(output_file_path, audio, samplerate=sample_rate)
# Clean up unnecessary files # Clean up unnecessary files
for audio_file in audio_files: for audio_file in audio_files:
@@ -544,9 +559,7 @@ def voice_preview(string):
) )
# Check if lock is already acquired # Check if lock is already acquired
if process_lock.locked(): if process_lock.locked():
print( print(f"[{params['branding']}Model] \033[91mWarning\033[0m Audio generation is already in progress. Please wait.")
f"[{params['branding']}Model] \033[91mWarning\033[0m Audio generation is already in progress. Please wait."
)
return return
if generate_response.get("status") == "generate-success": if generate_response.get("status") == "generate-success":
# Handle Gradio and playback # Handle Gradio and playback
@@ -666,14 +679,10 @@ def output_modifier(string, state):
# Determine the voice to use based on the part type # Determine the voice to use based on the part type
if part_type == "narrator": if part_type == "narrator":
voice_to_use = params["narrator_voice"] voice_to_use = params["narrator_voice"]
print( print(f"[{params['branding']}TTSGen] \033[92mNarrator\033[0m") # Green
f"[{params['branding']}TTSGen] \033[92mNarrator\033[0m"
) # Green
elif part_type == "character": elif part_type == "character":
voice_to_use = params["voice"] voice_to_use = params["voice"]
print( print(f"[{params['branding']}TTSGen] \033[36mCharacter\033[0m") # Yellow
f"[{params['branding']}TTSGen] \033[36mCharacter\033[0m"
) # Yellow
else: else:
# Handle ambiguous parts based on user preference # Handle ambiguous parts based on user preference
voice_to_use = ( voice_to_use = (
@@ -689,9 +698,11 @@ def output_modifier(string, state):
print(f"[{params['branding']}TTSGen] {voice_description}") print(f"[{params['branding']}TTSGen] {voice_description}")
# Replace multiple exclamation marks, question marks, or other punctuation with a single instance # Replace multiple exclamation marks, question marks, or other punctuation with a single instance
cleaned_part = re.sub(r"([!?.])\1+", r"\1", part) cleaned_part = re.sub(r"([!?.\u3002\uFF1F\uFF01\uFF0C])\1+", r"\1", part)
# Replace "Chinese ellipsis" with a single dot
cleaned_part = re.sub(r"\u2026{1,2}", ". ", cleaned_part)
# Further clean to remove any other unwanted characters # Further clean to remove any other unwanted characters
cleaned_part = re.sub(r'[^a-zA-Z0-9\s\.,;:!?\-\'"\u0100-\u017F\u0400-\u04FFÀ-ÿ\u0150\u0151\u0170\u0171\u4E00-\u9FFF\u3400-\u4DBF\u20000-\u2A6DF\u3000-\u303F\$]', '', cleaned_part) cleaned_part = re.sub(r'[^a-zA-Z0-9\s.,;:!?\-\'"$\u0400-\u04FF\u00C0-\u00FF\u0150\u0151\u0170\u0171\u0900-\u097F\u2018\u2019\u201C\u201D\u3001\u3002\u3040-\u309F\u30A0-\u30FF\u4E00-\u9FFF\u3400-\u4DBF\uF900-\uFAFF\u0600-\u06FF\u0750-\u077F\uFB50-\uFDFF\uFE70-\uFEFF\uAC00-\uD7A3\u1100-\u11FF\u3130-\u318F\uFF01\uFF0c\uFF1A\uFF1B\uFF1F]', '', cleaned_part)
# Remove all newline characters (single or multiple) # Remove all newline characters (single or multiple)
cleaned_part = re.sub(r"\n+", " ", cleaned_part) cleaned_part = re.sub(r"\n+", " ", cleaned_part)
@@ -716,9 +727,11 @@ def output_modifier(string, state):
# Decode HTML entities first # Decode HTML entities first
cleaned_part = html.unescape(cleaned_string) cleaned_part = html.unescape(cleaned_string)
# Replace multiple instances of certain punctuation marks with a single instance # Replace multiple instances of certain punctuation marks with a single instance
cleaned_part = re.sub(r"([!?.])\1+", r"\1", cleaned_part) cleaned_part = re.sub(r"([!?.\u3002\uFF1F\uFF01\uFF0C])\1+", r"\1", cleaned_part)
# Replace "Chinese ellipsis" with a single dot
cleaned_part = re.sub(r"\u2026{1,2}", ". ", cleaned_part)
# Further clean to remove any other unwanted characters # Further clean to remove any other unwanted characters
cleaned_part = re.sub(r'[^a-zA-Z0-9\s\.,;:!?\-\'"\u0100-\u017F\u0400-\u04FFÀ-ÿ\u0150\u0151\u0170\u0171\u4E00-\u9FFF\u3400-\u4DBF\u20000-\u2A6DF\u3000-\u303F\$]', '', cleaned_part) cleaned_part = re.sub(r'[^a-zA-Z0-9\s.,;:!?\-\'"$\u0400-\u04FF\u00C0-\u00FF\u0150\u0151\u0170\u0171\u0900-\u097F\u2018\u2019\u201C\u201D\u3001\u3002\u3040-\u309F\u30A0-\u30FF\u4E00-\u9FFF\u3400-\u4DBF\uF900-\uFAFF\u0600-\u06FF\u0750-\u077F\uFB50-\uFDFF\uFE70-\uFEFF\uAC00-\uD7A3\u1100-\u11FF\u3130-\u318F\uFF01\uFF0c\uFF1A\uFF1B\uFF1F]', '', cleaned_part)
# Remove all newline characters (single or multiple) # Remove all newline characters (single or multiple)
cleaned_part = re.sub(r"\n+", " ", cleaned_part) cleaned_part = re.sub(r"\n+", " ", cleaned_part)
# Process the part and give it a non-character name if being used vai API or standalone. # Process the part and give it a non-character name if being used vai API or standalone.
+4 -1
View File
@@ -304,6 +304,7 @@
<option value="nl">Dutch</option> <option value="nl">Dutch</option>
<option value="fr">French</option> <option value="fr">French</option>
<option value="de">German</option> <option value="de">German</option>
<option value="hi">Hindi</option>
<option value="hu">Hungarian</option> <option value="hu">Hungarian</option>
<option value="it">Italian</option> <option value="it">Italian</option>
<option value="ja">Japanese</option> <option value="ja">Japanese</option>
@@ -474,7 +475,7 @@
<span style="color: #3366ff;">22050Hz</span>, <span style="color: #3366ff;">Mono</span>, <span <span style="color: #3366ff;">22050Hz</span>, <span style="color: #3366ff;">Mono</span>, <span
style="color: #3366ff;">16 bit</span> and between 6 to 30 seconds long, though 8 to 10 seconds is usually style="color: #3366ff;">16 bit</span> and between 6 to 30 seconds long, though 8 to 10 seconds is usually
good enough. The model can handle up to 30 second samples, however I've not noticed any improvement in voice good enough. The model can handle up to 30 second samples, however I've not noticed any improvement in voice
output from a much longer clips. output from much longer clips.
</p> </p>
<p>You want to find a nice clear selection of audio, so lets say <p>You want to find a nice clear selection of audio, so lets say
you wanted to clone your favourite celebrity. You may go looking for an interview where they are talking. Pay you wanted to clone your favourite celebrity. You may go looking for an interview where they are talking. Pay
@@ -1357,6 +1358,7 @@
<h3 id="🟨-help-with-problems">🟨 Help with problems</h3> <h3 id="🟨-help-with-problems">🟨 Help with problems</h3>
<h4 id="🔄-minor-updatesbug-fixes-list-can-be-found-here">&nbsp;&nbsp;&nbsp;&nbsp; 🔄 <strong>Minor updates/bug <h4 id="🔄-minor-updatesbug-fixes-list-can-be-found-here">&nbsp;&nbsp;&nbsp;&nbsp; 🔄 <strong>Minor updates/bug
fixes list</strong> can be found <a href="https://github.com/erew123/alltalk_tts/issues/25">here</a></h4> fixes list</strong> can be found <a href="https://github.com/erew123/alltalk_tts/issues/25">here</a></h4>
<h4>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Please check Github for the most up to date help list <a href="https://github.com/erew123/alltalk_tts#-help-with-problems">here</a></h4>
<details> <details>
<summary>🟨 How to make a diagnostics report file</summary> <summary>🟨 How to make a diagnostics report file</summary>
@@ -2499,6 +2501,7 @@
<code>en</code> English<br> <code>en</code> English<br>
<code>fr</code> French<br> <code>fr</code> French<br>
<code>de</code> German<br> <code>de</code> German<br>
<code>hi</code> Hindi<br>
<code>hu</code> Hungarian<br> <code>hu</code> Hungarian<br>
<code>it</code> Italian<br> <code>it</code> Italian<br>
<code>ja</code> Japanese<br> <code>ja</code> Japanese<br>
Binary file not shown.

Before

Width:  |  Height:  |  Size: 440 KiB

After

Width:  |  Height:  |  Size: 97 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 25 KiB

After

Width:  |  Height:  |  Size: 11 KiB

+1
View File
@@ -124,6 +124,7 @@
<option value="English" {% if data.language=='English' %}selected{% endif %}>English</option> <option value="English" {% if data.language=='English' %}selected{% endif %}>English</option>
<option value="French" {% if data.language=='French' %}selected{% endif %}>French</option> <option value="French" {% if data.language=='French' %}selected{% endif %}>French</option>
<option value="German" {% if data.language=='German' %}selected{% endif %}>German</option> <option value="German" {% if data.language=='German' %}selected{% endif %}>German</option>
<option value="Hindi" {% if data.language=='Hindi' %}selected{% endif %}>Hindi</option>
<option value="Hungarian" {% if data.language=='Hungarian' %}selected{% endif %}>Hungarian</option> <option value="Hungarian" {% if data.language=='Hungarian' %}selected{% endif %}>Hungarian</option>
<option value="Italian" {% if data.language=='Italian' %}selected{% endif %}>Italian</option> <option value="Italian" {% if data.language=='Italian' %}selected{% endif %}>Italian</option>
<option value="Japanese" {% if data.language=='Japanese' %}selected{% endif %}>Japanese</option> <option value="Japanese" {% if data.language=='Japanese' %}selected{% endif %}>Japanese</option>
Binary file not shown.

Before

Width:  |  Height:  |  Size: 445 KiB

After

Width:  |  Height:  |  Size: 216 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 476 KiB

After

Width:  |  Height:  |  Size: 173 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 547 KiB

After

Width:  |  Height:  |  Size: 57 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 171 KiB

After

Width:  |  Height:  |  Size: 59 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 172 KiB

After

Width:  |  Height:  |  Size: 91 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 156 KiB

After

Width:  |  Height:  |  Size: 52 KiB

+2
View File
@@ -0,0 +1,2 @@
{
}
+2
View File
@@ -0,0 +1,2 @@
{
}
+1
View File
@@ -6,6 +6,7 @@
"English": "en", "English": "en",
"French": "fr", "French": "fr",
"German": "de", "German": "de",
"Hindi": "hi",
"Hungarian": "hu", "Hungarian": "hu",
"Italian": "it", "Italian": "it",
"Japanese": "ja", "Japanese": "ja",
+173
View File
@@ -0,0 +1,173 @@
absl-py==2.1.0
aiofiles==23.2.1
aiohttp==3.9.3
aiosignal==1.3.1
altair==5.2.0
annotated-types==0.6.0
anyascii==0.3.2
anyio==4.3.0
attrs==23.2.0
audioread==3.0.1
av==11.0.0
Babel==2.14.0
bangla==0.0.2
blinker==1.7.0
blis==0.7.11
bnnumerizer==0.0.2
bnunicodenormalizer==0.1.6
catalogue==2.0.10
certifi==2024.2.2
cffi==1.16.0
charset-normalizer==3.3.2
click==8.1.7
cloudpathlib==0.16.0
colorama==0.4.6
coloredlogs==15.0.1
confection==0.1.4
contourpy==1.2.0
coqpit==0.0.17
ctranslate2==4.1.0
cutlet==0.4.0
cycler==0.12.1
cymem==2.0.8
Cython==3.0.9
dateparser==1.1.8
decorator==5.1.1
docopt==0.6.2
einops==0.7.0
encodec==0.1.1
exceptiongroup
fastapi==0.110.0
faster-whisper==1.0.1
ffmpy==0.3.2
filelock==3.13.3
Flask==3.0.2
flatbuffers==24.3.25
fonttools==4.50.0
frozenlist==1.4.1
fsspec==2024.3.1
fugashi==1.3.1
fuzzywuzzy>=0.18.0
g2pkk==0.1.2
gradio==3.50.2
gradio_client==0.6.1
grpcio==1.62.1
gruut==2.2.3
gruut-ipa==0.13.0
gruut-lang-de==2.0.0
gruut-lang-en==2.0.0
gruut-lang-es==2.0.0
gruut-lang-fr==2.0.2
h11==0.14.0
hangul-romanize==0.1.0
httpcore==1.0.4
httpx==0.27.0
huggingface-hub==0.22.1
humanfriendly==10.0
idna==3.6
importlib_metadata==7.1.0
importlib_resources==6.4.0
inflect==7.0.0
itsdangerous==2.1.2
jaconv==0.3.4
jamo==0.4.1
jieba==0.42.1
Jinja2==3.1.3
joblib==1.3.2
jsonlines==1.2.0
jsonschema==4.21.1
jsonschema-specifications==2023.12.1
kiwisolver==1.4.5
langcodes==3.3.0
lazy_loader==0.3
librosa==0.10.1
llvmlite==0.42.0
Markdown==3.6
MarkupSafe==2.1.5
matplotlib==3.8.3
mojimoji==0.0.13
mpmath==1.3.0
msgpack==1.0.8
multidict==6.0.5
murmurhash==1.0.10
networkx==2.8.8
nltk==3.8.1
num2words==0.5.13
numba==0.59.1
numpy==1.26.4
nvidia-cublas-cu11>=11.11.3.6
nvidia-cudnn-cu11>=9.0.0.312
onnxruntime==1.17.1
orjson==3.9.15
packaging==24.0
pandas==1.5.3
pillow==10.2.0
platformdirs==4.2.0
pooch==1.8.1
preshed==3.0.9
protobuf==5.26.0
psutil==5.9.8
pycparser==2.21
pydantic==2.6.4
pydantic_core==2.16.3
pydub==0.25.1
pynndescent==0.5.11
pyparsing==3.1.2
pypinyin==0.51.0
pyreadline3==3.4.1
pysbd==0.3.4
python-crfsuite==0.9.10
python-dateutil==2.9.0.post0
python-Levenshtein>=0.25.0
python-multipart==0.0.9
pytz==2024.1
PyYAML==6.0.1
referencing==0.34.0
regex==2023.12.25
requests==2.31.0
rpds-py==0.18.0
safetensors==0.4.2
scikit-learn==1.4.1.post1
scipy==1.12.0
semantic-version==2.10.0
six==1.16.0
smart-open==6.4.0
sniffio==1.3.1
sounddevice==0.4.6
soundfile==0.12.1
soxr==0.3.7
spacy==3.7.4
spacy-legacy==3.0.12
spacy-loggers==1.0.5
srsly==2.4.8
starlette==0.36.3
SudachiDict-core==20240109
SudachiPy==0.6.8
sympy==1.12
tensorboard==2.16.2
tensorboard-data-server==0.7.2
thinc==8.2.3
threadpoolctl==3.4.0
tokenizers==0.15.2
toolz==0.12.1
torch>=2.2.0
torchaudio>=2.2.0
tqdm==4.66.2
trainer==0.0.36
transformers==4.39.1
TTS==0.22.0
typer==0.9.4
typing_extensions==4.10.0
tzdata==2024.1
tzlocal==5.2
umap-learn==0.5.5
Unidecode==1.3.8
unidic-lite==1.0.8
urllib3==2.2.1
uvicorn==0.29.0
wasabi==1.1.2
weasel==0.3.4
websockets==11.0.3
Werkzeug==3.0.1
yarl==1.9.4
zipp==3.18.1
@@ -0,0 +1,171 @@
absl-py==2.1.0
aiofiles==23.2.1
aiohttp==3.9.3
aiosignal==1.3.1
altair==5.2.0
annotated-types==0.6.0
anyascii==0.3.2
anyio==4.3.0
attrs==23.2.0
audioread==3.0.1
av==11.0.0
Babel==2.14.0
bangla==0.0.2
blinker==1.7.0
blis==0.7.11
bnnumerizer==0.0.2
bnunicodenormalizer==0.1.6
catalogue==2.0.10
certifi==2024.2.2
cffi==1.16.0
charset-normalizer==3.3.2
click==8.1.7
cloudpathlib==0.16.0
colorama==0.4.6
coloredlogs==15.0.1
confection==0.1.4
contourpy==1.2.0
coqpit==0.0.17
ctranslate2==4.1.0
cutlet==0.4.0
cycler==0.12.1
cymem==2.0.8
Cython==3.0.9
dateparser==1.1.8
decorator==5.1.1
docopt==0.6.2
einops==0.7.0
encodec==0.1.1
fastapi==0.110.0
faster-whisper==1.0.1
ffmpy==0.3.2
filelock==3.13.3
Flask==3.0.2
flatbuffers==24.3.25
fonttools==4.50.0
frozenlist==1.4.1
fsspec==2024.3.1
fugashi==1.3.1
fuzzywuzzy>=0.18.0
g2pkk==0.1.2
gradio==3.50.2
gradio_client==0.6.1
grpcio==1.62.1
gruut==2.2.3
gruut-ipa==0.13.0
gruut-lang-de==2.0.0
gruut-lang-en==2.0.0
gruut-lang-es==2.0.0
gruut-lang-fr==2.0.2
h11==0.14.0
hangul-romanize==0.1.0
httpcore==1.0.4
httpx==0.27.0
huggingface-hub==0.22.1
humanfriendly==10.0
idna==3.6
importlib_metadata==7.1.0
importlib_resources==6.4.0
inflect==7.0.0
itsdangerous==2.1.2
jaconv==0.3.4
jamo==0.4.1
jieba==0.42.1
Jinja2==3.1.3
joblib==1.3.2
jsonlines==1.2.0
jsonschema==4.21.1
jsonschema-specifications==2023.12.1
kiwisolver==1.4.5
langcodes==3.3.0
lazy_loader==0.3
librosa==0.10.1
llvmlite==0.42.0
Markdown==3.6
MarkupSafe==2.1.5
matplotlib==3.8.3
mojimoji==0.0.13
mpmath==1.3.0
msgpack==1.0.8
multidict==6.0.5
murmurhash==1.0.10
networkx==2.8.8
nltk==3.8.1
num2words==0.5.13
numba==0.59.1
numpy>=1.17.0,<1.29.0
nvidia-cublas-cu11>=11.11.3.6
nvidia-cudnn-cu11>=9.0.0.312
onnxruntime==1.17.1
orjson==3.9.15
packaging==24.0
pandas==1.5.3
pillow==10.2.0
platformdirs==4.2.0
pooch==1.8.1
preshed==3.0.9
protobuf==5.26.0
psutil==5.9.8
pycparser==2.21
pydantic==2.6.4
pydantic_core==2.16.3
pydub==0.25.1
pynndescent==0.5.11
pyparsing==3.1.2
pypinyin==0.51.0
pyreadline3==3.4.1
pysbd==0.3.4
python-crfsuite==0.9.10
python-dateutil==2.9.0.post0
python-Levenshtein>=0.25.0
python-multipart==0.0.9
pytz==2024.1
PyYAML==6.0.1
referencing==0.34.0
regex==2023.12.25
requests==2.31.0
rpds-py==0.18.0
safetensors==0.4.2
scikit-learn==1.4.1.post1
scipy==1.12.0
semantic-version==2.10.0
six==1.16.0
smart-open==6.4.0
sniffio==1.3.1
sounddevice==0.4.6
soundfile==0.12.1
soxr==0.3.7
spacy==3.7.4
spacy-legacy==3.0.12
spacy-loggers==1.0.5
srsly==2.4.8
starlette==0.36.3
SudachiDict-core==20240109
SudachiPy==0.6.8
sympy==1.12
tensorboard==2.16.2
tensorboard-data-server==0.7.2
thinc==8.2.3
threadpoolctl==3.4.0
tokenizers==0.15.2
toolz==0.12.1
torch>=2.2.0
torchaudio>=2.2.0
tqdm==4.66.2
trainer==0.0.36
transformers==4.39.1
typer==0.9.4
typing_extensions==4.10.0
tzdata==2024.1
tzlocal==5.2
umap-learn==0.5.5
Unidecode==1.3.8
unidic-lite==1.0.8
urllib3==2.2.1
uvicorn==0.29.0
wasabi==1.1.2
weasel==0.3.4
websockets==11.0.3
Werkzeug==3.0.1
yarl==1.9.4
zipp==3.18.1
@@ -22,3 +22,4 @@ nvidia-cublas-cu11>=11.11.3.6
nvidia-cudnn-cu11>=9.0.0.312 nvidia-cudnn-cu11>=9.0.0.312
fuzzywuzzy>=0.18.0 fuzzywuzzy>=0.18.0
python-Levenshtein>=0.25.0 python-Levenshtein>=0.25.0
spacy>=3.7.2
+2 -2
View File
@@ -281,7 +281,7 @@ class AllTalkTtsProvider {
if (narratorVoiceSelect) narratorVoiceSelect.disabled = !this.settings.narrator_enabled; if (narratorVoiceSelect) narratorVoiceSelect.disabled = !this.settings.narrator_enabled;
} }
const modelSelect = document.getElementById('switch_model'); const modelSelect = document.getElementById('switch_model');
if (this.settings.finetuned_model === 'true') { if (this.settings.finetuned_model === true) {
const ftOption = document.createElement('option'); const ftOption = document.createElement('option');
ftOption.value = 'XTTSv2 FT'; ftOption.value = 'XTTSv2 FT';
ftOption.textContent = 'XTTSv2 FT'; ftOption.textContent = 'XTTSv2 FT';
@@ -830,4 +830,4 @@ function updateStatus(message) {
break; break;
} }
} }
} }
+1 -1
View File
@@ -176,4 +176,4 @@ export default class Crunker {
* @internal * @internal
*/ */
private _renderURL; private _renderURL;
} }
+1 -1
View File
@@ -1,2 +1,2 @@
var e={d:(t,n)=>{for(var r in n)e.o(n,r)&&!e.o(t,r)&&Object.defineProperty(t,r,{enumerable:!0,get:n[r]})},o:(e,t)=>Object.prototype.hasOwnProperty.call(e,t)},t={};function n(e){return n="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e},n(e)}function r(e){return function(e){if(Array.isArray(e))return a(e)}(e)||function(e){if("undefined"!=typeof Symbol&&null!=e[Symbol.iterator]||null!=e["@@iterator"])return Array.from(e)}(e)||function(e,t){if(!e)return;if("string"==typeof e)return a(e,t);var n=Object.prototype.toString.call(e).slice(8,-1);"Object"===n&&e.constructor&&(n=e.constructor.name);if("Map"===n||"Set"===n)return Array.from(e);if("Arguments"===n||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n))return a(e,t)}(e)||function(){throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}()}function a(e,t){(null==t||t>e.length)&&(t=e.length);for(var n=0,r=new Array(t);n<t;n++)r[n]=e[n];return r}function o(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}function i(e,t){for(var r=0;r<t.length;r++){var a=t[r];a.enumerable=a.enumerable||!1,a.configurable=!0,"value"in a&&(a.writable=!0),Object.defineProperty(e,(o=a.key,i=void 0,i=function(e,t){if("object"!==n(e)||null===e)return e;var r=e[Symbol.toPrimitive];if(void 0!==r){var a=r.call(e,t||"default");if("object"!==n(a))return a;throw new TypeError("@@toPrimitive must return a primitive value.")}return("string"===t?String:Number)(e)}(o,"string"),"symbol"===n(i)?i:String(i)),a)}var o,i}e.d(t,{Z:()=>u});var u=function(){function e(){var t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{},n=t.sampleRate;o(this,e),this._context=this._createContext(n),n||(n=this._context.sampleRate),this._sampleRate=n}var t,n,a;return t=e,n=[{key:"_createContext",value:function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:44100;return window.AudioContext=window.AudioContext||window.webkitAudioContext||window.mozAudioContext,new AudioContext({sampleRate:e})}},{key:"context",get:function(){return this._context}},{key:"fetchAudio",value:async function(){for(var e=this,t=arguments.length,n=new Array(t),r=0;r<t;r++)n[r]=arguments[r];return await Promise.all(n.map((async function(t){var n;return n=t instanceof File||t instanceof Blob?await t.arrayBuffer():await fetch(t).then((function(e){return e.headers.has("Content-Type")&&!e.headers.get("Content-Type").includes("audio/")&&console.warn("Crunker: Attempted to fetch an audio file, but its MIME type is `".concat(e.headers.get("Content-Type").split(";")[0],"`. We'll try and continue anyway. (file: \"").concat(t,'")')),e.arrayBuffer()})),await e._context.decodeAudioData(n)})))}},{key:"mergeAudio",value:function(e){var t=this._context.createBuffer(this._maxNumberOfChannels(e),this._sampleRate*this._maxDuration(e),this._sampleRate);return e.forEach((function(e){for(var n=0;n<e.numberOfChannels;n++){for(var r=t.getChannelData(n),a=e.getChannelData(n),o=e.getChannelData(n).length-1;o>=0;o--)r[o]+=a[o];t.getChannelData(n).set(r)}})),t}},{key:"concatAudio",value:function(e){var t=this._context.createBuffer(this._maxNumberOfChannels(e),this._totalLength(e),this._sampleRate),n=0;return e.forEach((function(e){for(var r=0;r<e.numberOfChannels;r++)t.getChannelData(r).set(e.getChannelData(r),n);n+=e.length})),t}},{key:"padAudio",value:function(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:0,n=arguments.length>2&&void 0!==arguments[2]?arguments[2]:0;if(0===n)return e;if(t<0)throw new Error('Crunker: Parameter "padStart" in padAudio must be positive');if(n<0)throw new Error('Crunker: Parameter "seconds" in padAudio must be positive');for(var r=this._context.createBuffer(e.numberOfChannels,Math.ceil(e.length+n*e.sampleRate),e.sampleRate),a=0;a<e.numberOfChannels;a++){var o=e.getChannelData(a);r.getChannelData(a).set(o.subarray(0,Math.ceil(t*e.sampleRate)+1),0),r.getChannelData(a).set(o.subarray(Math.ceil(t*e.sampleRate)+2,r.length+1),Math.ceil((t+n)*e.sampleRate))}return r}},{key:"sliceAudio",value:function(e,t,n){var r=arguments.length>3&&void 0!==arguments[3]?arguments[3]:0,a=arguments.length>4&&void 0!==arguments[4]?arguments[4]:0;if(t>=n)throw new Error('Crunker: "start" time should be less than "end" time in sliceAudio method');for(var o=Math.round((n-t)*this._sampleRate),i=Math.round(t*this._sampleRate),u=this._context.createBuffer(e.numberOfChannels,o,this._sampleRate),l=0;l<e.numberOfChannels;l++)for(var s=e.getChannelData(l),c=u.getChannelData(l),f=0;f<o;f++)c[f]=s[i+f],f<r*this._sampleRate&&(c[f]*=f/(r*this._sampleRate)),f>o-a*this._sampleRate&&(c[f]*=(o-f)/(a*this._sampleRate));return u}},{key:"play",value:function(e){var t=this._context.createBufferSource();return t.buffer=e,t.connect(this._context.destination),t.start(),Line truncated var e={d:(t,n)=>{for(var r in n)e.o(n,r)&&!e.o(t,r)&&Object.defineProperty(t,r,{enumerable:!0,get:n[r]})},o:(e,t)=>Object.prototype.hasOwnProperty.call(e,t)},t={};function n(e){return n="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e},n(e)}function r(e){return function(e){if(Array.isArray(e))return a(e)}(e)||function(e){if("undefined"!=typeof Symbol&&null!=e[Symbol.iterator]||null!=e["@@iterator"])return Array.from(e)}(e)||function(e,t){if(!e)return;if("string"==typeof e)return a(e,t);var n=Object.prototype.toString.call(e).slice(8,-1);"Object"===n&&e.constructor&&(n=e.constructor.name);if("Map"===n||"Set"===n)return Array.from(e);if("Arguments"===n||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n))return a(e,t)}(e)||function(){throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}()}function a(e,t){(null==t||t>e.length)&&(t=e.length);for(var n=0,r=new Array(t);n<t;n++)r[n]=e[n];return r}function o(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}function i(e,t){for(var r=0;r<t.length;r++){var a=t[r];a.enumerable=a.enumerable||!1,a.configurable=!0,"value"in a&&(a.writable=!0),Object.defineProperty(e,(o=a.key,i=void 0,i=function(e,t){if("object"!==n(e)||null===e)return e;var r=e[Symbol.toPrimitive];if(void 0!==r){var a=r.call(e,t||"default");if("object"!==n(a))return a;throw new TypeError("@@toPrimitive must return a primitive value.")}return("string"===t?String:Number)(e)}(o,"string"),"symbol"===n(i)?i:String(i)),a)}var o,i}e.d(t,{Z:()=>u});var u=function(){function e(){var t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{},n=t.sampleRate;o(this,e),this._context=this._createContext(n),n||(n=this._context.sampleRate),this._sampleRate=n}var t,n,a;return t=e,n=[{key:"_createContext",value:function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:44100;return window.AudioContext=window.AudioContext||window.webkitAudioContext||window.mozAudioContext,new AudioContext({sampleRate:e})}},{key:"context",get:function(){return this._context}},{key:"fetchAudio",value:async function(){for(var e=this,t=arguments.length,n=new Array(t),r=0;r<t;r++)n[r]=arguments[r];return await Promise.all(n.map((async function(t){var n;return n=t instanceof File||t instanceof Blob?await t.arrayBuffer():await fetch(t).then((function(e){return e.headers.has("Content-Type")&&!e.headers.get("Content-Type").includes("audio/")&&console.warn("Crunker: Attempted to fetch an audio file, but its MIME type is `".concat(e.headers.get("Content-Type").split(";")[0],"`. We'll try and continue anyway. (file: \"").concat(t,'")')),e.arrayBuffer()})),await e._context.decodeAudioData(n)})))}},{key:"mergeAudio",value:function(e){var t=this._context.createBuffer(this._maxNumberOfChannels(e),this._sampleRate*this._maxDuration(e),this._sampleRate);return e.forEach((function(e){for(var n=0;n<e.numberOfChannels;n++){for(var r=t.getChannelData(n),a=e.getChannelData(n),o=e.getChannelData(n).length-1;o>=0;o--)r[o]+=a[o];t.getChannelData(n).set(r)}})),t}},{key:"concatAudio",value:function(e){var t=this._context.createBuffer(this._maxNumberOfChannels(e),this._totalLength(e),this._sampleRate),n=0;return e.forEach((function(e){for(var r=0;r<e.numberOfChannels;r++)t.getChannelData(r).set(e.getChannelData(r),n);n+=e.length})),t}},{key:"padAudio",value:function(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:0,n=arguments.length>2&&void 0!==arguments[2]?arguments[2]:0;if(0===n)return e;if(t<0)throw new Error('Crunker: Parameter "padStart" in padAudio must be positive');if(n<0)throw new Error('Crunker: Parameter "seconds" in padAudio must be positive');for(var r=this._context.createBuffer(e.numberOfChannels,Math.ceil(e.length+n*e.sampleRate),e.sampleRate),a=0;a<e.numberOfChannels;a++){var o=e.getChannelData(a);r.getChannelData(a).set(o.subarray(0,Math.ceil(t*e.sampleRate)+1),0),r.getChannelData(a).set(o.subarray(Math.ceil(t*e.sampleRate)+2,r.length+1),Math.ceil((t+n)*e.sampleRate))}return r}},{key:"sliceAudio",value:function(e,t,n){var r=arguments.length>3&&void 0!==arguments[3]?arguments[3]:0,a=arguments.length>4&&void 0!==arguments[4]?arguments[4]:0;if(t>=n)throw new Error('Crunker: "start" time should be less than "end" time in sliceAudio method');for(var o=Math.round((n-t)*this._sampleRate),i=Math.round(t*this._sampleRate),u=this._context.createBuffer(e.numberOfChannels,o,this._sampleRate),l=0;l<e.numberOfChannels;l++)for(var s=e.getChannelData(l),c=u.getChannelData(l),f=0;f<o;f++)c[f]=s[i+f],f<r*this._sampleRate&&(c[f]*=f/(r*this._sampleRate)),f>o-a*this._sampleRate&&(c[f]*=(o-f)/(a*this._sampleRate));return u}},{key:"play",value:function(e){var t=this._context.createBufferSource();return t.buffer=e,t.connect(this._context.destination),t.start(),Line truncated
//# sourceMappingURL=crunker.esm.js.map //# sourceMappingURL=crunker.esm.js.map
+1 -1
View File
@@ -1 +1 @@
{"version":3,"file":"crunker.esm.js","mappings":"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 truncated {"version":3,"file":"crunker.esm.js","mappings":"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 truncated
+14 -10
View File
@@ -532,6 +532,7 @@
<option value="en" selected>English</option> <option value="en" selected>English</option>
<option value="fr">French</option> <option value="fr">French</option>
<option value="de">German</option> <option value="de">German</option>
<option value="hi">Hindi</option>
<option value="hu">Hungarian</option> <option value="hu">Hungarian</option>
<option value="it">Italian</option> <option value="it">Italian</option>
<option value="ja">Japanese</option> <option value="ja">Japanese</option>
@@ -921,18 +922,21 @@
const playType = document.getElementById('playType').value; // Play type (chunk or stream) const playType = document.getElementById('playType').value; // Play type (chunk or stream)
playModeValue = document.getElementById('playMode').value; playModeValue = document.getElementById('playMode').value;
let cleanedText = textInput.replace(/ \- | \– /g, ' '); let cleanedText = textInput.replace(/ \\- | \\– /g, ' ');
cleanedText = cleanedText.replace(/%/g, " percent"); cleanedText = cleanedText.replace(/%/g, " percent");
cleanedText = cleanedText.replace(/\.\s'/g, ".'"); cleanedText = cleanedText.replace(/\\.\\s'/g, ".'");
cleanedText = cleanedText.replace(/(?<!\w)[']+(?!\w)/g, ''); cleanedText = cleanedText.replace(/(?<!\\w)\['\]+(?!\\w)/g, '');
cleanedText = cleanedText.replace(/"/g, "'"); cleanedText = cleanedText.replace(/"/g, "'");
cleanedText = cleanedText.replace(/[^a-zA-Z0-9\s\.,;:!?\-\'"À-ÿ\u0400-\u04FF\u0150\u0151\u0170\u0171]\$/g, ''); cleanedText = cleanedText.replace(/[^a-zA-Z0-9\s.,;:!?\-'"$À-ÿ\u0400-\u04FF\u0150\u0151\u0170\u0171\u0900-\u097F\u2018\u2019\u201C\u201D\u2026\u3001\u3002\u3040-\u309F\u30A0-\u30FF\u4E00-\u9FFF\u3400-\u4DBF\uF900-\uFAFF\u0600-\u06FF\u0750-\u077F\uFB50-\uFDFF\uFE70-\uFEFF\uAC00-\uD7A3\u1100-\u11FF\u3130-\u318F\uFF01\uFF0c\uFF1A\uFF1B\uFF1F]/g, '');
// Replace dash followed by space with comma and space cleanedText = cleanedText.replace(/-\\s/g, '; ');
cleanedText = cleanedText.replace(/-\s/g, '; ');
// Replace double dash "--" with semicolon "; "
cleanedText = cleanedText.replace(/--/g, '; '); cleanedText = cleanedText.replace(/--/g, '; ');
// Split by Fullwidth/CJK punctuation regardless of if there are spaces behind them
let sentences = cleanedText.split(/(?<=[.!?:])\s+/); // Please test if it works properly. In my test they it should be
// (?<=[.!?:])\s+ instead of
// (?<=\[.!?:\])\\s+
// so I don't know whats going on here
// let sentences = cleanedText.split(/(?<=[\[.!?:\]])\s+|(?<=[\u3002\uFF01\uFF1A\uFF1F])/);
let sentences = cleanedText.split(/(?<=[\[.!?:\]]['"\u2018\u2019\u201C\u201D]*)\s+|(?<=[\u3002\uFF01\uFF1A\uFF1F])/);
if (!sentences) { if (!sentences) {
console.error("No sentences found in the input text."); console.error("No sentences found in the input text.");
@@ -2208,4 +2212,4 @@
</script> </script>
</body> </body>
</html> </html>
File diff suppressed because it is too large. Load diff
+61 -26
View File
@@ -8,7 +8,8 @@ from TTS.tts.configs.xtts_config import XttsConfig
from TTS.tts.models.xtts import Xtts from TTS.tts.models.xtts import Xtts
import io import io
import wave import wave
import logging
logging.disable(logging.WARNING)
########################## ##########################
#### Webserver Imports#### #### Webserver Imports####
########################## ##########################
@@ -120,12 +121,6 @@ try:
deepspeed_available = True deepspeed_available = True
except ImportError: except ImportError:
pass pass
if deepspeed_available:
print(f"[{params['branding']}Startup] DeepSpeed \033[93mDetected\033[0m")
print(f"[{params['branding']}Startup] Activate DeepSpeed in {params['branding']}settings")
else:
print(f"[{params['branding']}Startup] DeepSpeed \033[93mNot Detected\033[0m. See https://github.com/microsoft/DeepSpeed")
@asynccontextmanager @asynccontextmanager
async def startup_shutdown(no_actual_value_it_demanded_something_be_here): async def startup_shutdown(no_actual_value_it_demanded_something_be_here):
@@ -187,9 +182,8 @@ async def setup():
# Calculate start time minus end time # Calculate start time minus end time
generate_elapsed_time = generate_end_time - generate_start_time generate_elapsed_time = generate_end_time - generate_start_time
# Print out the result of the load time # Print out the result of the load time
print( print(f"[{params['branding']}Model] \033[94mModel Loaded in \033[93m{generate_elapsed_time:.2f} seconds.\033[0m")
f"[{params['branding']}Model] \033[94mModel Loaded in \033[93m{generate_elapsed_time:.2f} seconds.\033[0m" print(f"[{params['branding']}Model] Ready")
)
# Set "tts_model_loaded" to true # Set "tts_model_loaded" to true
params["tts_model_loaded"] = True params["tts_model_loaded"] = True
# Set the output path for wav files # Set the output path for wav files
@@ -223,6 +217,8 @@ async def api_manual_load_model():
model_path=modeldownload_base_path / modeldownload_model_path, model_path=modeldownload_base_path / modeldownload_model_path,
config_path=modeldownload_base_path / modeldownload_model_path / "config.json", config_path=modeldownload_base_path / modeldownload_model_path / "config.json",
).to(device) ).to(device)
print(f"[{params['branding']}Model] \033[94mCoqui Public Model License\033[0m")
print(f"[{params['branding']}Model] \033[94mhttps://coqui.ai/cpml.txt\033[0m")
return model return model
@@ -252,6 +248,8 @@ async def xtts_manual_load_model():
use_deepspeed=params["deepspeed_activate"], use_deepspeed=params["deepspeed_activate"],
) )
model.to(device) model.to(device)
print(f"[{params['branding']}Model] \033[94mCoqui Public Model License\033[0m")
print(f"[{params['branding']}Model] \033[94mhttps://coqui.ai/cpml.txt\033[0m")
return model return model
# MODEL LOADER For "XTTSv2 FT" # MODEL LOADER For "XTTSv2 FT"
@@ -270,6 +268,8 @@ async def xtts_ft_manual_load_model():
use_deepspeed=params["deepspeed_activate"], use_deepspeed=params["deepspeed_activate"],
) )
model.to(device) model.to(device)
print(f"[{params['branding']}Model] \033[94mCoqui Public Model License\033[0m")
print(f"[{params['branding']}Model] \033[94mhttps://coqui.ai/cpml.txt\033[0m")
return model return model
# MODEL UNLOADER # MODEL UNLOADER
@@ -476,6 +476,11 @@ async def deepspeed(request: Request, new_deepspeed_value: bool):
######################## ########################
#### TTS GENERATION #### #### TTS GENERATION ####
######################## ########################
debug_generate_audio = False
tts_stop_generation = False # Called to stop generation of the current text at whatever stage its at. currently only set for streaming.
tts_generation_lock = False # Tracks locking of the generaetion process.
tts_narrator_generatingtts = False # Tracks if the current tts processes are narrator based, to avoid moving model on each chunk of text generated.
# Not in use tts_queue_behaviour = False # True is to queue the current generation request. False is to cancel the current generation and start a new one.
# TTS VOICE GENERATION METHODS (called from voice_preview and output_modifer) # TTS VOICE GENERATION METHODS (called from voice_preview and output_modifer)
async def generate_audio(text, voice, language, temperature, repetition_penalty, output_file, streaming=False): async def generate_audio(text, voice, language, temperature, repetition_penalty, output_file, streaming=False):
@@ -486,9 +491,11 @@ async def generate_audio(text, voice, language, temperature, repetition_penalty,
return response return response
async for _ in response: async for _ in response:
pass pass
async def generate_audio_internal(text, voice, language, temperature, repetition_penalty, output_file, streaming): async def generate_audio_internal(text, voice, language, temperature, repetition_penalty, output_file, streaming):
global model global model, tts_stop_generation, tts_generation_lock
tts_generation_lock = True
if params["low_vram"] and device == "cpu": if params["low_vram"] and device == "cpu":
await switch_device() await switch_device()
generate_start_time = time.time() # Record the start time of generating TTS generate_start_time = time.time() # Record the start time of generating TTS
@@ -539,6 +546,12 @@ async def generate_audio_internal(text, voice, language, temperature, repetition
yield wav_buf.read() yield wav_buf.read()
for i, chunk in enumerate(output): for i, chunk in enumerate(output):
if tts_stop_generation:
print(f"[{params['branding']}TTSGen] Stopping audio generation.")
file_chunks.clear() # Clear the file_chunks list
tts_stop_generation = False
tts_generation_lock = False
break
file_chunks.append(chunk) file_chunks.append(chunk)
if isinstance(chunk, list): if isinstance(chunk, list):
chunk = torch.cat(chunk, dim=0) chunk = torch.cat(chunk, dim=0)
@@ -547,6 +560,7 @@ async def generate_audio_internal(text, voice, language, temperature, repetition
chunk = np.clip(chunk, -1, 1) chunk = np.clip(chunk, -1, 1)
chunk = (chunk * 32767).astype(np.int16) chunk = (chunk * 32767).astype(np.int16)
yield chunk.tobytes() yield chunk.tobytes()
print(f"[{params['branding']}Debug] Stream audio generation: Yielded audio chunk {i}.") if debug_generate_audio else None
else: else:
# Non-streaming-specific operation # Non-streaming-specific operation
torchaudio.save(output_file, torch.tensor(output["wav"]).unsqueeze(0), 24000) torchaudio.save(output_file, torch.tensor(output["wav"]).unsqueeze(0), 24000)
@@ -565,9 +579,9 @@ async def generate_audio_internal(text, voice, language, temperature, repetition
file_path=output_file, file_path=output_file,
speaker_wav=[f"{this_dir}/voices/{voice}"], speaker_wav=[f"{this_dir}/voices/{voice}"],
language=language, language=language,
temperature=temperature, temperature=float(temperature),
length_penalty=model.config.length_penalty, length_penalty=float(model.config.length_penalty),
repetition_penalty=repetition_penalty, repetition_penalty=float(repetition_penalty),
top_k=model.config.top_k, top_k=model.config.top_k,
top_p=model.config.top_p, top_p=model.config.top_p,
) )
@@ -589,12 +603,11 @@ async def generate_audio_internal(text, voice, language, temperature, repetition
# Print Generation time and settings # Print Generation time and settings
generate_end_time = time.time() # Record the end time to generate TTS generate_end_time = time.time() # Record the end time to generate TTS
generate_elapsed_time = generate_end_time - generate_start_time generate_elapsed_time = generate_end_time - generate_start_time
print( print(f"[{params['branding']}TTSGen] \033[93m{generate_elapsed_time:.2f} seconds. \033[94mLowVRAM: \033[33m{params['low_vram']} \033[94mDeepSpeed: \033[33m{params['deepspeed_activate']}\033[0m")
f"[{params['branding']}TTSGen] \033[93m{generate_elapsed_time:.2f} seconds. \033[94mLowVRAM: \033[33m{params['low_vram']} \033[94mDeepSpeed: \033[33m{params['deepspeed_activate']}\033[0m"
)
# Move model back to cpu system ram if needed. # Move model back to cpu system ram if needed.
if params["low_vram"] and device == "cuda": if params["low_vram"] and device == "cuda" and tts_narrator_generatingtts == False:
await switch_device() await switch_device()
tts_generation_lock = False
return return
@@ -854,6 +867,13 @@ async def tts_generate_streaming(request: Request, text: str = Form(...), voice:
print(f"An error occurred: {e}") print(f"An error occurred: {e}")
return JSONResponse(content={"error": "An error occurred"}, status_code=500) return JSONResponse(content={"error": "An error occurred"}, status_code=500)
@app.put("/api/stop-generation")
async def stop_generation_endpoint():
global tts_stop_generation, tts_generation_lock
if tts_generation_lock and not tts_stop_generation:
tts_stop_generation = True
return {"message": "Generation stopped"}
############################## ##############################
#### Standard Generation #### #### Standard Generation ####
############################## ##############################
@@ -890,7 +910,7 @@ class JSONInput(BaseModel):
narrator_enabled: bool = Field(..., description="narrator_enabled needs to be true or false.") narrator_enabled: bool = Field(..., description="narrator_enabled needs to be true or false.")
narrator_voice_gen: str = Field(..., pattern="^.*\.wav$", description="narrator_voice_gen needs to be the name of a wav file e.g. mysample.wav.") narrator_voice_gen: str = Field(..., pattern="^.*\.wav$", description="narrator_voice_gen needs to be the name of a wav file e.g. mysample.wav.")
text_not_inside: str = Field(..., pattern="^(character|narrator)$", description="text_not_inside needs to be 'character' or 'narrator'.") text_not_inside: str = Field(..., pattern="^(character|narrator)$", description="text_not_inside needs to be 'character' or 'narrator'.")
language: str = Field(..., pattern="^(ar|zh-cn|cs|nl|en|fr|de|hu|it|ja|ko|pl|pt|ru|es|tr)$", description="language needs to be one of the following ar|zh-cn|cs|nl|en|fr|de|hu|it|ja|ko|pl|pt|ru|es|tr.") language: str = Field(..., pattern="^(ar|zh-cn|cs|nl|en|fr|de|hu|hi|it|ja|ko|pl|pt|ru|es|tr)$", description="language needs to be one of the following ar|zh-cn|cs|nl|en|fr|de|hu|hi|it|ja|ko|pl|pt|ru|es|tr.")
output_file_name: str = Field(..., pattern="^[a-zA-Z0-9_]+$", description="output_file_name needs to be the name without any special characters or file extension e.g. 'filename'") output_file_name: str = Field(..., pattern="^[a-zA-Z0-9_]+$", description="output_file_name needs to be the name without any special characters or file extension e.g. 'filename'")
output_file_timestamp: bool = Field(..., description="output_file_timestamp needs to be true or false.") output_file_timestamp: bool = Field(..., description="output_file_timestamp needs to be true or false.")
autoplay: bool = Field(..., description="autoplay needs to be a true or false value.") autoplay: bool = Field(..., description="autoplay needs to be a true or false value.")
@@ -1029,12 +1049,17 @@ async def tts_generate(
"autoplay_volume": autoplay_volume, "autoplay_volume": autoplay_volume,
"streaming": streaming, "streaming": streaming,
} }
global tts_narrator_generatingtts
JSONresult = TTSGenerator.validate_json_input(json_input_data) JSONresult = TTSGenerator.validate_json_input(json_input_data)
if JSONresult is None: if JSONresult is None:
pass pass
else: else:
print(f"[{params['branding']}API] \033[91mError with API request:\033[0m", JSONresult)
return JSONResponse(content={"error": JSONresult}, status_code=400) return JSONResponse(content={"error": JSONresult}, status_code=400)
if narrator_enabled: if narrator_enabled:
if params["low_vram"] and device == "cpu":
await switch_device()
tts_narrator_generatingtts = True
processed_parts = process_text(text_input) processed_parts = process_text(text_input)
audio_files_all_paragraphs = [] audio_files_all_paragraphs = []
for part_type, part in processed_parts: for part_type, part in processed_parts:
@@ -1054,9 +1079,11 @@ async def tts_generate(
voice_description = "\033[36mCharacter (Text-not-inside)\033[0m" if text_not_inside == "character" else "\033[92mNarrator (Text-not-inside)\033[0m" voice_description = "\033[36mCharacter (Text-not-inside)\033[0m" if text_not_inside == "character" else "\033[92mNarrator (Text-not-inside)\033[0m"
print(f"[{params['branding']}TTSGen] {voice_description}") print(f"[{params['branding']}TTSGen] {voice_description}")
# Replace multiple exclamation marks, question marks, or other punctuation with a single instance # Replace multiple exclamation marks, question marks, or other punctuation with a single instance
cleaned_part = re.sub(r'([!?.])\1+', r'\1', part) cleaned_part = re.sub(r'([!?.\u3002\uFF1F\uFF01\uFF0C])\1+', r'\1', part)
# Replace "Chinese ellipsis" with a single dot
cleaned_part = re.sub(r"\u2026{1,2}", ". ", cleaned_part)
# Further clean to remove any other unwanted characters # Further clean to remove any other unwanted characters
cleaned_part = re.sub(r'[^a-zA-Z0-9\s\.,;:!?\-\'"\u0400-\u04FFÀ-ÿ\u0150\u0151\u0170\u0171]\$', '', cleaned_part) cleaned_part = re.sub(r'[^a-zA-Z0-9\s.,;:!?\-\'"$\u0400-\u04FF\u00C0-\u00FF\u0150\u0151\u0170\u0171\u0900-\u097F\u2018\u2019\u201C\u201D\u3001\u3002\u3040-\u309F\u30A0-\u30FF\u4E00-\u9FFF\u3400-\u4DBF\uF900-\uFAFF\u0600-\u06FF\u0750-\u077F\uFB50-\uFDFF\uFE70-\uFEFF\uAC00-\uD7A3\u1100-\u11FF\u3130-\u318F\uFF01\uFF0c\uFF1A\uFF1B\uFF1F]', '', cleaned_part)
# Remove all newline characters (single or multiple) # Remove all newline characters (single or multiple)
cleaned_part = re.sub(r'\n+', ' ', cleaned_part) cleaned_part = re.sub(r'\n+', ' ', cleaned_part)
output_file = this_dir / "outputs" / f"{output_file_name}_{uuid.uuid4()}_{int(time.time())}.wav" output_file = this_dir / "outputs" / f"{output_file_name}_{uuid.uuid4()}_{int(time.time())}.wav"
@@ -1066,6 +1093,10 @@ async def tts_generate(
audio_files_all_paragraphs.append(audio_path) audio_files_all_paragraphs.append(audio_path)
# Combine audio files across paragraphs # Combine audio files across paragraphs
output_file_path, output_file_url, output_cache_url = combine(output_file_timestamp, output_file_name, audio_files_all_paragraphs) output_file_path, output_file_url, output_cache_url = combine(output_file_timestamp, output_file_name, audio_files_all_paragraphs)
tts_narrator_generatingtts = False
# Move model back to cpu system ram if needed.
if params["low_vram"] and device == "cuda":
await switch_device()
else: else:
if output_file_timestamp: if output_file_timestamp:
timestamp = int(time.time()) timestamp = int(time.time())
@@ -1085,15 +1116,19 @@ async def tts_generate(
output_cache_url = f'http://{params["ip_address"]}:{params["port_number"]}/audiocache/{output_file_name}.wav' output_cache_url = f'http://{params["ip_address"]}:{params["port_number"]}/audiocache/{output_file_name}.wav'
if text_filtering == "html": if text_filtering == "html":
cleaned_string = html.unescape(standard_filtering(text_input)) cleaned_string = html.unescape(standard_filtering(text_input))
cleaned_string = re.sub(r'([!?.])\1+', r'\1', text_input) cleaned_string = re.sub(r'([!?.\u3002\uFF1F\uFF01\uFF0C])\1+', r'\1', text_input)
# Replace "Chinese ellipsis" with a single dot
cleaned_string = re.sub(r"\u2026{1,2}", ". ", cleaned_string)
# Further clean to remove any other unwanted characters # Further clean to remove any other unwanted characters
cleaned_string = re.sub(r'[^a-zA-Z0-9\s\.,;:!?\-\'"\u0400-\u04FFÀ-ÿ\u0150\u0151\u0170\u0171]\$', '', cleaned_string) cleaned_string = re.sub(r'[^a-zA-Z0-9\s.,;:!?\-\'"$\u0400-\u04FF\u00C0-\u00FF\u0150\u0151\u0170\u0171\u0900-\u097F\u2018\u2019\u201C\u201D\u3001\u3002\u3040-\u309F\u30A0-\u30FF\u4E00-\u9FFF\u3400-\u4DBF\uF900-\uFAFF\u0600-\u06FF\u0750-\u077F\uFB50-\uFDFF\uFE70-\uFEFF\uAC00-\uD7A3\u1100-\u11FF\u3130-\u318F\uFF01\uFF0c\uFF1A\uFF1B\uFF1F]', '', cleaned_string)
# Remove all newline characters (single or multiple) # Remove all newline characters (single or multiple)
cleaned_string = re.sub(r'\n+', ' ', cleaned_string) cleaned_string = re.sub(r'\n+', ' ', cleaned_string)
elif text_filtering == "standard": elif text_filtering == "standard":
cleaned_string = re.sub(r'([!?.])\1+', r'\1', text_input) cleaned_string = re.sub(r'([!?.\u3002\uFF1F\uFF01\uFF0C])\1+', r'\1', text_input)
# Replace "Chinese ellipsis" with a single dot
cleaned_string = re.sub(r"\u2026{1,2}", ". ", cleaned_string)
# Further clean to remove any other unwanted characters # Further clean to remove any other unwanted characters
cleaned_string = re.sub(r'[^a-zA-Z0-9\s\.,;:!?\-\'"\u0400-\u04FFÀ-ÿ\u0150\u0151\u0170\u0171]\$', '', cleaned_string) cleaned_string = re.sub(r'[^a-zA-Z0-9\s.,;:!?\-\'"$\u0400-\u04FF\u00C0-\u00FF\u0150\u0151\u0170\u0171\u0900-\u097F\u2018\u2019\u201C\u201D\u3001\u3002\u3040-\u309F\u30A0-\u30FF\u4E00-\u9FFF\u3400-\u4DBF\uF900-\uFAFF\u0600-\u06FF\u0750-\u077F\uFB50-\uFDFF\uFE70-\uFEFF\uAC00-\uD7A3\u1100-\u11FF\u3130-\u318F\uFF01\uFF0c\uFF1A\uFF1B\uFF1F]', '', cleaned_string)
# Remove all newline characters (single or multiple) # Remove all newline characters (single or multiple)
cleaned_string = re.sub(r'\n+', ' ', cleaned_string) cleaned_string = re.sub(r'\n+', ' ', cleaned_string)
else: else: