Compare commits

..
84 Commits
Author SHA1 Message Date
erew123 9bb0d1fa58 Update README.md 2024-03-04 09:34:55 +00:00
erew123 015b9033ec Additional updates re chinese
CJK Symbols and Punctuation range u3000 > u303F
2024-02-21 20:58:57 +00:00
erew123 7f261bb5e9 Attempt to allow Chinese character set through 2024-02-21 12:24:11 +00:00
erew123 00b93cfe1b explicit allowance for ć, ś, ń, ł etc 2024-02-07 12:44:42 +00:00
erew123 c0d01ee931 Update tts_server.py 2024-02-05 14:05:49 +00:00
erew123 d0ceb2bb65 Update atsetup.sh 2024-01-30 11:37:23 +00:00
erew123 3231e61d56 Bumped transformers requirements >=4.37.1
Now transformers is fixed, bumped requirements up to skip 4.37.0
2024-01-30 11:36:37 +00:00
erew123 32f3b721c2 Update README.md 2024-01-24 17:06:26 +00:00
erew123 8c588939d2 Merge pull request #86 from erew123/dev
merge main > dev
2024-01-23 14:24:57 +00:00
erew123 ff1077f282 Update docker-compose-cuda.yml 2024-01-23 13:25:37 +00:00
erew123 36d0e92ed9 Update docker-compose.yml 2024-01-23 13:25:09 +00:00
erew123 8dfb84d7e2 Merge pull request #83 from Josh-XT/main
Standalone Docker Container
2024-01-23 13:05:38 +00:00
erew123 4caecdc5eb Merge pull request #85 from erew123/main
main to dev merge
2024-01-23 13:01:56 +00:00
Josh XT 40f2fbf54e Merge branch 'erew123:main' into main 2024-01-23 06:57:54 -05:00
erew123 ab37dd8849 Update README.md 2024-01-23 11:45:29 +00:00
erew123 03b02e0e4c Update README.md 2024-01-23 11:18:42 +00:00
Josh XT 351f46c7b5 Merge branch 'erew123:main' into main 2024-01-22 21:20:17 -05:00
Josh XT 71ba584e7c Add voices 2024-01-22 21:15:29 -05:00
Josh XT b3f53e93cc fix typo 2024-01-22 20:46:53 -05:00
Josh XT 0ed3cb75b5 Add modeldownload 2024-01-22 20:44:18 -05:00
Josh XT c29de18ad4 add chmod 2024-01-22 19:41:18 -05:00
Josh XT ec1d0e8c5d Check if running in docker 2024-01-22 19:26:23 -05:00
Josh XT 12bf008b3f Run back end without subprocess 2024-01-22 19:15:54 -05:00
erew123 ecdc10ece5 Update atsetup.bat 2024-01-22 23:27:55 +00:00
Josh XT 360f70cb0a Disable deepspeed 2024-01-22 18:10:20 -05:00
Josh XT 51c91c3d70 Fix config 2024-01-22 17:35:21 -05:00
Josh XT 81a9f4c847 Persist config 2024-01-22 17:33:02 -05:00
Josh XT cdce7511dc Use deepspeed 2024-01-22 17:31:41 -05:00
erew123 6d4b6d336b Update README.md 2024-01-22 22:30:47 +00:00
erew123 6c4a9b19c7 Update README.md 2024-01-22 22:21:21 +00:00
erew123 82ecf3eecc Update README.md 2024-01-22 22:18:16 +00:00
Josh XT d750959f52 Add docker-compose 2024-01-22 17:04:28 -05:00
Josh XT bd74de53da Add portaudio19-dev 2024-01-22 16:53:08 -05:00
Josh XT fcec9d8f5b Fix python alias 2024-01-22 16:32:30 -05:00
Josh XT 85e5e6ab86 Merge branch 'erew123:main' into main 2024-01-22 16:06:30 -05:00
Josh XT d15931bdda 3 2024-01-22 15:54:46 -05:00
erew123 abc9cf1fb7 Update README.md 2024-01-22 20:45:11 +00:00
erew123 2b60f5b0da Update requirements_other.txt 2024-01-22 20:01:16 +00:00
erew123 e524063ec2 Update requirements_nvidia.txt 2024-01-22 20:01:04 +00:00
erew123 d93906a75f Update atsetup.sh 2024-01-22 20:00:47 +00:00
erew123 ad57deed20 Update atsetup.bat 2024-01-22 20:00:21 +00:00
erew123 d0866e3c6c Update README.md 2024-01-22 19:16:54 +00:00
erew123 af907a299d Update requirements_other.txt 2024-01-22 18:56:50 +00:00
erew123 7928af1bb7 Update requirements_nvidia.txt 2024-01-22 18:56:39 +00:00
erew123 4d837a0219 Update atsetup.sh
force     pip install transformers==4.36.2
2024-01-22 18:56:11 +00:00
erew123 e50817a234 Update atsetup.bat
force install of transformers pip install transformers==4.36.2
2024-01-22 18:55:45 +00:00
Josh XT c3bb58d2af Update dockerhub 2024-01-22 12:15:10 -05:00
Josh XT c17959f142 Add dockerhub publish 2024-01-22 12:12:22 -05:00
Josh XT def2d51c5f Use ubuntu 2024-01-22 11:54:29 -05:00
Josh XT 6166d4f751 No arm support 2024-01-22 11:52:41 -05:00
Josh XT cd164ae258 Updates 2024-01-22 11:49:10 -05:00
Josh XT f089e3e85b Add multi build 2024-01-22 11:45:55 -05:00
Josh XT d008c7ab0a Move install 2024-01-22 11:27:35 -05:00
Josh XT a199adfbd6 Add deps 2024-01-22 11:24:54 -05:00
Josh XT 61998b4d39 Set to nvidia 2024-01-22 11:21:15 -05:00
Josh XT 55799b2de8 Add workflow 2024-01-22 10:54:14 -05:00
Josh XT ece55ffe06 Create Dockerfile 2024-01-22 10:46:10 -05:00
erew123 dbc2582fa6 Update README.md 2024-01-21 13:05:35 +00:00
erew123 535c9b652a Update README.md 2024-01-21 12:53:04 +00:00
erew123 6c28d58b4a Update README.md 2024-01-21 12:23:53 +00:00
erew123 c9a222932c Update README.md 2024-01-21 12:22:14 +00:00
erew123 7010369966 Added startup_wait_time to top of script
Making it simple for people to adjust the allowed start-up time
2024-01-21 12:20:29 +00:00
erew123 525a337835 Update README.md 2024-01-20 13:47:58 +00:00
erew123 2bd1ef843e Dynamic IP support 2024-01-20 00:18:06 +00:00
erew123 a9d95fc563 Update script.py 2024-01-18 14:15:34 +00:00
erew123 1a37ae4ba5 Merge pull request #76 from S95Sedan/main
Temperature and Repetition_penalty
2024-01-18 14:14:04 +00:00
erew123 8c6fc4853d Updated ST files 2024-01-17 22:01:51 +00:00
erew123 f34ebbd675 Update README.md 2024-01-16 01:00:48 +00:00
S95Sedan 64cffcdf80 Updated tts_server.py
With changes in: https://github.com/S95Sedan/alltalk_tts/commit/cefd1e045baf15c68fbdcac15fb1978850f8987f#commitcomment-137124105
2024-01-16 01:47:29 +01:00
erew123 2731a559f4 Update README.md 2024-01-16 00:12:54 +00:00
erew123 a91a83268f eval % option 2024-01-15 23:55:54 +00:00
S95Sedan 5d406fbbfb Load variables fix
Load temp/repetition variables in tts_server incase of API only usage.
2024-01-16 00:37:10 +01:00
S95Sedan cefd1e045b Temperature and Repetition_penalty modification
Modified script.py to enable temperature and repetition_penalty to be parsed and tts_server.py to recieve those variables.
2024-01-15 21:51:00 +01:00
erew123 bec4e905d3 Push JS mimetype on /static
Push JS files with mimetype of application/javascript
2024-01-15 16:38:37 +00:00
erew123 f5e99a5e72 Update README.md 2024-01-15 13:38:28 +00:00
erew123 631ea6dd8a Update README.md 2024-01-15 13:38:00 +00:00
erew123 08a04ba506 Added warning about starting from disk location 2024-01-15 11:36:29 +00:00
erew123 c78927b80a Create FND 2024-01-14 12:51:41 +00:00
erew123 04e08c58b8 Delete .github/ISSUE_TEMPLATE/FUNDING.yml 2024-01-14 12:50:57 +00:00
erew123 893747c9a9 Add files via upload 2024-01-14 12:48:49 +00:00
erew123 d52dcf86d5 Update README.md 2024-01-13 23:43:15 +00:00
erew123 ad0eab1224 Update atsetup.sh
added cutlet and unidic-lite
2024-01-13 22:45:01 +00:00
erew123 62d1cc05d3 Added cutlet and unidic-lite for Japanese 2024-01-13 22:43:41 +00:00
erew123 b8292da92f Update README.md 2024-01-13 21:47:49 +00:00
21 changed files with 880 additions and 362 deletions

No files matched your search

+1
View File
@@ -0,0 +1 @@
ko_fi: erew123
+67
View File
@@ -0,0 +1,67 @@
name: Publish Docker image
on:
push:
branches: [main]
release:
types: [published]
workflow_dispatch:
jobs:
build_and_push:
runs-on: ubuntu-latest
strategy:
matrix:
include:
- dockerfile: "Dockerfile"
platforms: "linux/amd64"
tag_name: "cpu"
- dockerfile: "nvidia.Dockerfile"
platforms: "linux/amd64"
tag_name: "cuda"
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Lowercase Actor and Extract Repo Name
run: |
echo "ACTOR_LOWER=$(echo ${{ github.actor }} | tr '[:upper:]' '[:lower:]')" >> $GITHUB_ENV
echo "REPO_NAME=$(echo ${{ github.repository }} | cut -d'/' -f 2)" >> $GITHUB_ENV
- name: Login to GitHub Container Registry
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ env.ACTOR_LOWER }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Login to DockerHub
uses: docker/login-action@v3
with:
username: ${{ vars.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Build and push Docker image
uses: docker/build-push-action@v5
with:
context: .
file: ./${{ matrix.dockerfile }}
platforms: ${{ matrix.platforms }}
push: true
tags: |
ghcr.io/${{ env.ACTOR_LOWER }}/${{ env.REPO_NAME }}:${{ matrix.tag_name }}-${{ github.sha }}
ghcr.io/${{ env.ACTOR_LOWER }}/${{ env.REPO_NAME }}:${{ matrix.tag_name }}
ghcr.io/${{ env.ACTOR_LOWER }}/${{ env.REPO_NAME }}:latest
${{ vars.DOCKERHUB_USERNAME }}/${{ env.REPO_NAME }}:${{ matrix.tag_name }}-${{ github.sha }}
${{ vars.DOCKERHUB_USERNAME }}/${{ env.REPO_NAME }}:${{ matrix.tag_name }}
${{ vars.DOCKERHUB_USERNAME }}/${{ env.REPO_NAME }}:latest
- name: Docker Hub Description
uses: peter-evans/dockerhub-description@v3.4.2
with:
username: ${{ vars.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
repository: ${{ vars.DOCKERHUB_USERNAME }}/${{ env.REPO_NAME }}
+12
View File
@@ -0,0 +1,12 @@
FROM ubuntu:22.04
ENV HOST 0.0.0.0
RUN apt-get update && apt-get upgrade -y \
&& apt-get install -y git build-essential portaudio19-dev \
python3 python3-pip gcc wget \
&& ln -s /usr/bin/python3 /usr/bin/python
WORKDIR /app
COPY . .
RUN pip install --no-cache-dir -r requirements_other.txt
EXPOSE 7851
RUN chmod +x launch.sh
ENTRYPOINT ["sh", "-c", "./launch.sh"]
+84 -20
View File
@@ -15,7 +15,7 @@ AllTalk is an updated version of the Coqui_tts extension for Text Generation web
- **API Suite and 3rd Party support via JSON calls:** Can be used with 3rd party applications via JSON calls. - **API Suite and 3rd Party support via JSON calls:** Can be used with 3rd party applications via JSON calls.
- **SillyTavern integration:** Full integration with SillyTavern. [Screenshot](https://github.com/erew123/screenshots/raw/main/sillytavern.jpg) - **SillyTavern integration:** Full integration with SillyTavern. [Screenshot](https://github.com/erew123/screenshots/raw/main/sillytavern.jpg)
## Index ### Index
- 🟩 [Installation](https://github.com/erew123/alltalk_tts/#-quick-setup-text-generation-webui--standalone-installation) - 🟩 [Installation](https://github.com/erew123/alltalk_tts/#-quick-setup-text-generation-webui--standalone-installation)
- 🟪 [Updating & problems with updating](https://github.com/erew123/alltalk_tts?#-updating) - 🟪 [Updating & problems with updating](https://github.com/erew123/alltalk_tts?#-updating)
@@ -27,18 +27,25 @@ AllTalk is an updated version of the Coqui_tts extension for Text Generation web
- 🟠 [API Suite and JSON-CURL](https://github.com/erew123/alltalk_tts?#-api-suite-and-json-curl) - 🟠 [API Suite and JSON-CURL](https://github.com/erew123/alltalk_tts?#-api-suite-and-json-curl)
- 🔴 [Future to-do list & Upcoming updates](https://github.com/erew123/alltalk_tts?#-future-to-do-list) - 🔴 [Future to-do list & Upcoming updates](https://github.com/erew123/alltalk_tts?#-future-to-do-list)
## 🔄 Updates & Bug fixes ### 🔄 Feature requests, Updates & Bug fixes
Please check the below link to find a list of all recent updates and changes. Please check the below link to find a list of all recent updates and changes.
####     🔄 **Updates list & bug fixes list** can be found [here](https://github.com/erew123/alltalk_tts/issues/25) ####     🔄 **Updates list & bug fixes list** can be found [here](https://github.com/erew123/alltalk_tts/issues/25)
####     🔄 **Current Feature request list** can be found [here](https://github.com/erew123/alltalk_tts/discussions/74)
## 🟩 Quick Setup (Text-generation-webui & Standalone Installation) I welcome your input and ideas for new features, suggestions, and improvements. Feel free to share your thoughts and collaborate in the discussions area. If you find this project valuable and would like to show your appreciation, you can make a donation on my [Ko-fi](https://ko-fi.com/erew123) page. Your support goes a long way in ensuring that I can continue to deliver even better features and experiences.
For Windows 10/11 and Linux machines there is a quick setup script. Click to expand the correct section below:
**ERROR** `ImportError: cannot import name 'SampleOutput' from 'transformers.generation.utils'` please see this issue [here](https://github.com/erew123/alltalk_tts/issues/82)
### 🟩 Quick Setup (Text-generation-webui & Standalone Installation)
For Windows 10/11 and Linux machines there is a quick setup script. Please note, Python on Windows requires you install the C++ development [tools](https://wiki.python.org/moin/WindowsCompilers) to compile packages, further details can be found in the help section.
Click to expand the correct section below:
<details> <details>
<summary>QUICK SETUP - Text-Generation-webui</summary><br> <summary>QUICK SETUP - Text-Generation-webui</summary><br>
If you wish to see this as a video, please go [here](https://www.youtube.com/watch?v=icn2XS5rUH8) If you wish to see this as a video, please go [here](https://www.youtube.com/watch?v=icn2XS5rUH8)
1) To download the AllTalk setup you can either: 1) To download the AllTalk setup you can either:
- A) On this page select **CODE** > **Download ZIP** then extract it to the text-generation-webui extensions folder<br>e.g. `\text-generation-webui\extensions\alltalk_tts\`<br><br> - A) Go to the [Releases page](https://github.com/erew123/alltalk_tts/releases) and download the latest `alltalk_tts.zip` then extract it to the text-generation-webui extensions folder<br>e.g. `\text-generation-webui\extensions\alltalk_tts\`.<br><br>
- B) Go to a terminal/console, move into the `\text-generation-webui\extensions\` folder<br>and `git clone https://github.com/erew123/alltalk_tts`<br><br> - B) Go to a terminal/console, move into the `\text-generation-webui\extensions\` folder<br>and `git clone https://github.com/erew123/alltalk_tts`<br><br>
3) In a terminal/command prompt, in the text-generation-webui folder you will start its Python environment with either `cmd_windows.bat` or `./cmd_linux.sh` 3) In a terminal/command prompt, in the text-generation-webui folder you will start its Python environment with either `cmd_windows.bat` or `./cmd_linux.sh`
4) Move into the AllTalk folder e.g. `cd extensions` then `cd alltalk_tts` 4) Move into the AllTalk folder e.g. `cd extensions` then `cd alltalk_tts`
@@ -56,7 +63,7 @@ For Windows 10/11 and Linux machines there is a quick setup script. Click to exp
If you wish to see this as a video, please go [here](https://www.youtube.com/watch?v=AQYCccDRbaY) If you wish to see this as a video, please go [here](https://www.youtube.com/watch?v=AQYCccDRbaY)
1) To download the AllTalk setup you can either: 1) To download the AllTalk setup you can either:
- A) On this page select **CODE** > **Download ZIP** then extract it to the folder of your choice<br>e.g. `C:\myfiles\alltalk_tts\`<br><br> - A) Go to the [Releases page](https://github.com/erew123/alltalk_tts/releases) and download the latest `alltalk_tts.zip` and extract it to the folder of your choice<br>e.g. `C:\myfiles\alltalk_tts\`.<br><br>
- B) Go to a terminal/console, move into the folder of your choice e.g `C:\myfiles\` folder<br>and `git clone https://github.com/erew123/alltalk_tts`<br><br> - B) Go to a terminal/console, move into the folder of your choice e.g `C:\myfiles\` folder<br>and `git clone https://github.com/erew123/alltalk_tts`<br><br>
4) In a terminal/command prompt, move into the AllTalk folder e.g. `cd alltalk_tts` 4) In a terminal/command prompt, move into the AllTalk folder e.g. `cd alltalk_tts`
5) Start the AllTalk setup script `atsetup.bat` or `./atsetup.sh` 5) Start the AllTalk setup script `atsetup.bat` or `./atsetup.sh`
@@ -66,10 +73,12 @@ For Windows 10/11 and Linux machines there is a quick setup script. Click to exp
Please read the `🟩 Other installation notes` (also additional voices are available there). Please read the `🟩 Other installation notes` (also additional voices are available there).
You cannot have a dash in your folder path e.g. `c:\myfiles\alltalk_tts-main` so please ensure you remove any `-` from your folder path. This is a conda specific requirement and will cause AllTalk not to start at all.
Finetuning has other installation requirements so please read any instructions in the setup utility and refer back here to this page for detailed instructions.<br><br> Finetuning has other installation requirements so please read any instructions in the setup utility and refer back here to this page for detailed instructions.<br><br>
</details> </details>
## 🟩 Manual Installation - As part of Text generation web UI ### 🟩 Manual Installation - As part of Text generation web UI
On Mac's or if you wish to perform a manual installation. Click to expand the correct section below: On Mac's or if you wish to perform a manual installation. Click to expand the correct section below:
<details> <details>
<summary>MANUAL INSTALLATION - Text-Generation-webui</summary><br> <summary>MANUAL INSTALLATION - Text-Generation-webui</summary><br>
@@ -109,7 +118,7 @@ This has been tested on the current Dec 2023 release of Text generation webUI. I
</details> </details>
## 🟩 Manual Installation - As a Standalone Application ### 🟩 Manual Installation - As a Standalone Application
On Mac's or if you wish to perform a manual installation. Click to expand the correct section below: On Mac's or if you wish to perform a manual installation. Click to expand the correct section below:
<details> <details>
<summary>MANUAL INSTALLATION - I want to run AllTalk as a standalone when installed with Text-generation-webui</summary><br> <summary>MANUAL INSTALLATION - I want to run AllTalk as a standalone when installed with Text-generation-webui</summary><br>
@@ -160,7 +169,7 @@ Deepspeed and other such things can be installed. Please read the relevant instr
#### 🟩 Other installation notes #### 🟩 Other installation notes
On first startup, AllTalk will download the Coqui XTTSv2 2.0.2 model to its **models** folder (1.8GB space required). Check the command prompt/terminal window if you want to know what its doing. After it says "Model Loaded" the Text generation webUI is usually available on its IP address a few seconds later, for you to connect to in your browser. On first startup, AllTalk will download the Coqui XTTSv2 2.0.2 model to its **models** folder (1.8GB space required). Check the command prompt/terminal window if you want to know what its doing. After it says "Model Loaded" the Text generation webUI is usually available on its IP address a few seconds later, for you to connect to in your browser. If you are running a headless system and need to change the IP, please see the Help with problems section down below.
Once the extension is loaded, please find all documentation and settings on the link provided in the interface (as shown in the screenshot below). Once the extension is loaded, please find all documentation and settings on the link provided in the interface (as shown in the screenshot below).
@@ -168,7 +177,7 @@ Once the extension is loaded, please find all documentation and settings on the
Please read the note below about start-up times and also the note about ensuring your character cards are set up [correctly](https://github.com/erew123/alltalk_tts#-a-note-on-character-cards--greeting-messages) Please read the note below about start-up times and also the note about ensuring your character cards are set up [correctly](https://github.com/erew123/alltalk_tts#-a-note-on-character-cards--greeting-messages)
Some extra voices for AllTalk are downloadable [here](https://drive.google.com/file/d/1bYdZdr3L69kmzUN3vSiqZmLRD7-A3M47/view?usp=drive_link) Some extra voices for AllTalk are downloadable [here](https://drive.google.com/file/d/1bYdZdr3L69kmzUN3vSiqZmLRD7-A3M47/view?usp=drive_link) and [here](https://drive.google.com/file/d/1CPnx1rpkuKvVj5fGr9OiUJHZ_e8DfTzP/view)
#### 🟩 A note on Character Cards & Greeting Messages #### 🟩 A note on Character Cards & Greeting Messages
Messages intended for the Narrator should be enclosed in asterisks `*` and those for the character inside quotation marks `"`. However, AI systems often deviate from these rules, resulting in text that is neither in quotes nor asterisks. Sometimes, text may appear with only a single asterisk, and AI models may vary their formatting mid-conversation. For example, they might use asterisks initially and then switch to unmarked text. A properly formatted line should look like this: Messages intended for the Narrator should be enclosed in asterisks `*` and those for the character inside quotation marks `"`. However, AI systems often deviate from these rules, resulting in text that is neither in quotes nor asterisks. Sometimes, text may appear with only a single asterisk, and AI models may vary their formatting mid-conversation. For example, they might use asterisks initially and then switch to unmarked text. A properly formatted line should look like this:
@@ -179,7 +188,7 @@ Most narrator/character systems switch voices upon encountering an asterisk or q
With improvements to the splitter/processor, I'm confident it's functioning well. You can monitor what AllTalk identifies as Narrator lines on the command line and adjust its behavior if needed (Text Not Inside - Function). With improvements to the splitter/processor, I'm confident it's functioning well. You can monitor what AllTalk identifies as Narrator lines on the command line and adjust its behavior if needed (Text Not Inside - Function).
## 🟪 Updating ### 🟪 Updating
<details> <details>
<summary>UPDATING - I am using Text-Generation-webui</summary><br> <summary>UPDATING - I am using Text-Generation-webui</summary><br>
This is pretty much a repeat of the installation process. This is pretty much a repeat of the installation process.
@@ -258,12 +267,12 @@ You can now start text-generation-webui or AllTalk (standalone) and it should st
Assuming its all working fine and you are happy, you can delete the old alltalk_tts.old folder. Assuming its all working fine and you are happy, you can delete the old alltalk_tts.old folder.
</details> </details>
## 🟫 Screenshots ### 🟫 Screenshots
|![image](https://github.com/erew123/screenshots/raw/main/textgensettings.jpg) | ![image](https://github.com/erew123/screenshots/raw/main/setuputilitys.jpg) | ![image](https://github.com/erew123/screenshots/raw/main/deepspeed.jpg) |![image](https://github.com/erew123/screenshots/raw/main/textgen.jpg) | |![image](https://github.com/erew123/screenshots/raw/main/textgensettings.jpg) | ![image](https://github.com/erew123/screenshots/raw/main/setuputilitys.jpg) | ![image](https://github.com/erew123/screenshots/raw/main/deepspeed.jpg) |![image](https://github.com/erew123/screenshots/raw/main/textgen.jpg) |
|:---:|:---:|:---:|:---:| |:---:|:---:|:---:|:---:|
|![image](https://github.com/erew123/screenshots/raw/main/settingsanddocs.jpg) | ![image](https://github.com/erew123/screenshots/raw/main/finetune1.jpg) | ![image](https://github.com/erew123/screenshots/raw/main/finetune2.jpg) |![image](https://github.com/erew123/screenshots/raw/main/sillytavern.jpg)| |![image](https://github.com/erew123/screenshots/raw/main/settingsanddocs.jpg) | ![image](https://github.com/erew123/screenshots/raw/main/finetune1.jpg) | ![image](https://github.com/erew123/screenshots/raw/main/finetune2.jpg) |![image](https://github.com/erew123/screenshots/raw/main/sillytavern.jpg)|
## 🟨 Help with problems ### 🟨 Help with problems
#### &nbsp;&nbsp;&nbsp;&nbsp; 🔄 **Minor updates/bug fixes list** can be found [here](https://github.com/erew123/alltalk_tts/issues/25) #### &nbsp;&nbsp;&nbsp;&nbsp; 🔄 **Minor updates/bug fixes list** can be found [here](https://github.com/erew123/alltalk_tts/issues/25)
@@ -296,7 +305,7 @@ If you are on a Windows machine or a Linux machine, you should be able to use th
**A)** Its trying to load the voice model into your graphics card VRAM (assuming you have a Nvidia Graphics card, otherwise its your system RAM)<br> **A)** Its trying to load the voice model into your graphics card VRAM (assuming you have a Nvidia Graphics card, otherwise its your system RAM)<br>
**B)** Its trying to start up the mini-webserver and send the "ready" signal back to the main process. **B)** Its trying to start up the mini-webserver and send the "ready" signal back to the main process.
Before giving other possibilities a go, some people with **old machines** are finding their startup times are **very** slow 2-3 minutes. Ive extended the allowed time within the script from 1 minute to 2 minutes. **If you have an older machine** and wish to try extending this further, you can do so by editing `script.py` and changing line 251 `timeout = 120` changing the timeout to a larger value e.g `timeout = 240` (4 minutes). Before giving other possibilities a go, some people with **old machines** are finding their startup times are **very** slow 2-3 minutes. Ive extended the allowed time within the script from 1 minute to 2 minutes. **If you have an older machine** and wish to try extending this further, you can do so by editing `script.py` and changing `startup_wait_time = 120` (120 seconds, aka 2 minutes) at the top of the script.py file, to a larger value e.g `startup_wait_time = 240` (240 seconds aka 4 minutes).
**Note:** If you need to create a support ticket, please create a `diagnostics.log` report file to submit with a support request. Details on doing this are above. **Note:** If you need to create a support ticket, please create a `diagnostics.log` report file to submit with a support request. Details on doing this are above.
@@ -325,6 +334,21 @@ Other possibilities for this issue are:
11) You have built yourself a custom Python environment and something is funky with it. This is very hard to diagnose as its not a standard environment. You may want to updating text-generation-webui and re installing its requirements file (whichever one you use that comes down with text-generation-webui). 11) You have built yourself a custom Python environment and something is funky with it. This is very hard to diagnose as its not a standard environment. You may want to updating text-generation-webui and re installing its requirements file (whichever one you use that comes down with text-generation-webui).
</details> </details>
#### 🟨 Windows & Python requirements for compiling packages
`ERROR: Microsoft Visual C++ 14.0 or greater is required` or `ERROR: Could not build wheels for TTS.` or `ModuleNotFoundError: No module named 'TTS`
<details>
<summary>Click to expand</summary><br>
Python requires that you install C++ development tools on Windows. This is detailed on the [Python site here](https://wiki.python.org/moin/WindowsCompilers). You would need to install `MSVCv142 - VS 2019 C++ x64/x86 build tools` and `Windows 10/11 SDK` from the C++ Build tools section.
You can get hold of the **Community** edition [here](https://visualstudio.microsoft.com/downloads/) the during installation, selecting `C++ Build tools` and then `MSVCv142 - VS 2019 C++ x64/x86 build tools` and `Windows 10/11 SDK`.
![image](https://github.com/erew123/screenshots/raw/main/pythonrequirementswindows.jpg)
</details>
#### 🟨 I think AllTalks requirements file has installed something another extension doesn't like #### 🟨 I think AllTalks requirements file has installed something another extension doesn't like
<details> <details>
<summary>Click to expand</summary><br> <summary>Click to expand</summary><br>
@@ -347,6 +371,15 @@ You can either follow the [Problems Updating](https://github.com/erew123/alltalk
If you want to use DeepSpeed, you need an Nvidia Graphics card and to install DeepSpeed on your system. Instructions are [here](https://github.com/erew123/alltalk_tts#-deepspeed-installation-options) If you want to use DeepSpeed, you need an Nvidia Graphics card and to install DeepSpeed on your system. Instructions are [here](https://github.com/erew123/alltalk_tts#-deepspeed-installation-options)
</details> </details>
#### 🟨 I am running a Headless system and need to change the IP Address manually as I cannot reach the config page.
<details>
<summary>Click to expand</summary><br>
To do this you can edit the `confignew.json` file within the `alltalk_tts` folder. You would look for `"ip_address": "127.0.0.1",` and change the `127.0.0.1` to your chosen IP address,then save the file and start AllTalk.<br><br>
When doing this, be careful not to impact the formatting of the JSON file. Worst case, you can re-download a fresh copy of `confignew.json` from this website and that will put you back to a factory setting.
</details>
#### 🟨 I am having problems updating/some other issue where it wont start up/Im sure this is a bug #### 🟨 I am having problems updating/some other issue where it wont start up/Im sure this is a bug
<details> <details>
<summary>Click to expand</summary><br> <summary>Click to expand</summary><br>
@@ -356,6 +389,14 @@ Please see [Problems Updating](https://github.com/erew123/alltalk_tts#-problems-
Also, is your text-generation-webui up to date? [instructions here](https://github.com/oobabooga/text-generation-webui?tab=readme-ov-file#how-to-install) Also, is your text-generation-webui up to date? [instructions here](https://github.com/oobabooga/text-generation-webui?tab=readme-ov-file#how-to-install)
</details> </details>
#### 🟨 Standalone Install - start_{youros}.xx opens and closes instantly and AllTalk doesnt start
<details>
<summary>Click to expand</summary><br>
This is more than likely caused by having a `-` in your folder path e.g. `c:\myfiles\alltalk_tts-main`. In this circumstance you would be best renaming the folder to remove the `-` from its name e.g. `c:\myfiles\alltalk_tts`, delete the `alltalk_environment` folder and `start_alltalk.bat` or `start_alltalk.sh` and then re-run `atsetup` to re-create the environment and startup files.
</details>
#### 🟨 I am having problems getting AllTalk to start after changing settings or making a custom setup/model setup. #### 🟨 I am having problems getting AllTalk to start after changing settings or making a custom setup/model setup.
<details> <details>
@@ -391,7 +432,7 @@ Depending on which of your Nvidia GPU's is the more powerful one, you can change
</details> </details>
## ⚫ Finetuning a model ### ⚫ Finetuning a model
If you have a voice that the model doesnt quite reproduce correctly, or indeed you just want to improve the reproduced voice, then finetuning is a way to train your "XTTSv2 local" model **(stored in `/alltalk_tts/models/xxxxx/`)** on a specific voice. For this you will need: If you have a voice that the model doesnt quite reproduce correctly, or indeed you just want to improve the reproduced voice, then finetuning is a way to train your "XTTSv2 local" model **(stored in `/alltalk_tts/models/xxxxx/`)** on a specific voice. For this you will need:
- An Nvidia graphics card. (Please see this [note](https://github.com/erew123/alltalk_tts#-i-have-multiple-gpus-and-i-have-problems-running-finetuning) if you have multiple Nvidia GPU's). - An Nvidia graphics card. (Please see this [note](https://github.com/erew123/alltalk_tts#-i-have-multiple-gpus-and-i-have-problems-running-finetuning) if you have multiple Nvidia GPU's).
@@ -406,6 +447,14 @@ Everything has been done to make this as simple as possible. At its simplest, yo
#### ⚫ The audio you will use #### ⚫ The audio you will use
I would suggest that if its in an interview format, you cut out the interviewer speaking in audacity or your chosen audio editing package. You dont have to worry about being perfect with your cuts, the finetuning Step 1 will go and find spoken audio and cut it out for you. Is there is music over the spoken parts, for best quality you would cut out those parts, though its not 100% necessary. As always, try to avoid bad quality audio with noises in it (humming sounds, hiss etc). You can try something like [Audioenhancer](https://audioenhancer.ai/) to try clean up noisier audio. There is no need to down-sample any of the audio, all of that is handled for you. Just give the finetuning some good quality audio to work with. I would suggest that if its in an interview format, you cut out the interviewer speaking in audacity or your chosen audio editing package. You dont have to worry about being perfect with your cuts, the finetuning Step 1 will go and find spoken audio and cut it out for you. Is there is music over the spoken parts, for best quality you would cut out those parts, though its not 100% necessary. As always, try to avoid bad quality audio with noises in it (humming sounds, hiss etc). You can try something like [Audioenhancer](https://audioenhancer.ai/) to try clean up noisier audio. There is no need to down-sample any of the audio, all of that is handled for you. Just give the finetuning some good quality audio to work with.
#### ⚫ A note about anonymous training Telemetry information & disabling it
Portions of Coqui's TTS trainer scripts gather anonymous training information which you can disable. Their statement on this is listed [here](https://github.com/coqui-ai/Trainer?tab=readme-ov-file#anonymized-telemetry). Although I have tried to pass `TRAINER_TELEMETRY=0` through AllTalk, it appears you will have to set this manually if you wish to finetune on a non-internet enabled computer or disable the anonymous data being sent. You can do this by:
- On Windows by typing `set TRAINER_TELEMETRY=0`
- On Linux & Mac by typing `export TRAINER_TELEMETRY=0`
Before you start `finetune.py`. You will now be able to finetune offline and no anonymous training data will be sent.
#### ⚫ Important requirements CUDA 11.8 #### ⚫ Important requirements CUDA 11.8
As mentioned you must have a small portion of the Nvidia CUDA Toolkit **11.8** installed. Not higher or lower versions. Specifically **11.8**. You do not have to uninstall any other versions, change any graphics drivers, reinstall torch or anything like that. This requirement is for Step1 of Finetuning. To keep the download+install as small as possible, you will need to: As mentioned you must have a small portion of the Nvidia CUDA Toolkit **11.8** installed. Not higher or lower versions. Specifically **11.8**. You do not have to uninstall any other versions, change any graphics drivers, reinstall torch or anything like that. This requirement is for Step1 of Finetuning. To keep the download+install as small as possible, you will need to:
- Download the **xxx (network)** install of the Nvidia CUDA Toolkit 11.8 from [here](https://developer.nvidia.com/cuda-11-8-0-download-archive) - Download the **xxx (network)** install of the Nvidia CUDA Toolkit 11.8 from [here](https://developer.nvidia.com/cuda-11-8-0-download-archive)
@@ -455,6 +504,19 @@ As mentioned you must have a small portion of the Nvidia CUDA Toolkit **11.8** i
11) Follow the on-screen instructions when the web interface starts up. 11) Follow the on-screen instructions when the web interface starts up.
12) When you have finished finetuning, the final tab will tell you what to do with your files and how to move your newly trained model to the correct location on disk. 12) When you have finished finetuning, the final tab will tell you what to do with your files and how to move your newly trained model to the correct location on disk.
#### ⚫ Evaluation Data Percentage
In the process of finetuning, it's crucial to balance the data used for training the model against the data reserved for evaluating its performance. Typically, a portion of the dataset is set aside as an 'evaluation set' to assess the model's capabilities in dealing with unseen data. On Step 1 of finetuning you have the option to adjust this evaluation data percentage, offering more control over your model training process.<br><br>
**Why Adjust the Evaluation Percentage?**<br><br>
Adjusting the evaluation percentage **can** be beneficial in scenarios with limited voice samples. When dealing with a smaller dataset, allocating a slightly larger portion to training could enhance the model's ability to learn from these scarce samples. Conversely, with abundant data, a higher evaluation percentage might be more appropriate to rigorously test the model's performance. There are currently no absolutely optimal split percentages as it varies by dataset.
- **Default Setting:** The default evaluation percentage is set at 15%, which is a balanced choice for most datasets.
- **Adjustable Range:** Users can now adjust this percentage, but it’s generally recommend keeping it between 5% and 30%.
- **Lower Bound:** A minimum of 5% ensures that there's enough data to evaluate model performance.
- **Upper Bound:** Its suggested not exceeding 30% for evaluation to avoid limiting the amount of data available for training.
- **Understanding the Impact:** Before adjusting this setting, it's important to understand its impact on model training and evaluation. Incorrect adjustments can lead to suboptimal model performance.
- **Gradual Adjustments:** For those unfamiliar with the process, we recommend reading up on training data and training sets, then making small, incremental changes and observing their effects.
- **Data Quality:** Regardless of the split, the quality of the audio data is paramount. Ensure that your datasets are built from good quality audio with enough data within them.
#### ⚫ Using a Finetuned model in Text-generation-webui #### ⚫ Using a Finetuned model in Text-generation-webui
At the end of the finetune process, you will have an option to `Compact and move model to /trainedmodel/` this will compact the raw training file and move it to `/model/trainedmodel/`. When AllTalk starts up within Text-generation-webui, if it finds a model in this location a new loader will appear in the interface for `XTTSv2 FT` and you can use this to load your finetuned model. <br><br>**Be careful** not to train a new model from the base model, then overwrite your current `/model/trainedmodel/` **if** you want a seperately trained model. This is why there is an `OPTION B` to move your just trained model to `/models/lastfinetuned/`. At the end of the finetune process, you will have an option to `Compact and move model to /trainedmodel/` this will compact the raw training file and move it to `/model/trainedmodel/`. When AllTalk starts up within Text-generation-webui, if it finds a model in this location a new loader will appear in the interface for `XTTSv2 FT` and you can use this to load your finetuned model. <br><br>**Be careful** not to train a new model from the base model, then overwrite your current `/model/trainedmodel/` **if** you want a seperately trained model. This is why there is an `OPTION B` to move your just trained model to `/models/lastfinetuned/`.
@@ -467,7 +529,7 @@ At the end of the finetune process, you will have an option to `Compact and move
If you've compacted and moved your model, its highly unlikely you would want to keep that data, however the choice is there to keep it if you wish. It will be between 5-10GB in size, so most people will want to delete it. If you've compacted and moved your model, its highly unlikely you would want to keep that data, however the choice is there to keep it if you wish. It will be between 5-10GB in size, so most people will want to delete it.
## 🔵🟢🟡 DeepSpeed Installation Options ### 🔵🟢🟡 DeepSpeed Installation Options
**NOTE**: You **DO NOT** need to set Text-generation-webUI's **--deepspeed** setting for AllTalk to be able to use DeepSpeed. These are two completely separate things and incorrectly setting that on Text-generation-webUI may cause other complications. **NOTE**: You **DO NOT** need to set Text-generation-webUI's **--deepspeed** setting for AllTalk to be able to use DeepSpeed. These are two completely separate things and incorrectly setting that on Text-generation-webUI may cause other complications.
#### 🔵 Linux Installation #### 🔵 Linux Installation
@@ -489,6 +551,7 @@ If you've compacted and moved your model, its highly unlikely you would want to
`export CUDA_HOME=/etc/alternatives/cuda`<br><br> `export CUDA_HOME=/etc/alternatives/cuda`<br><br>
**every** time you run `./cmd_linux.sh`.<br> <br> **every** time you run `./cmd_linux.sh`.<br> <br>
If you try to start DeepSpeed with the CUDA_HOME path set incorrectly, expect an error similar to `[Errno 2] No such file or directory: /home/yourname/text-generation-webui/installer_files/env/bin/nvcc`<br> <br> If you try to start DeepSpeed with the CUDA_HOME path set incorrectly, expect an error similar to `[Errno 2] No such file or directory: /home/yourname/text-generation-webui/installer_files/env/bin/nvcc`<br> <br>
You can also add the `export` line into the start scripts just below the `conda active .......` line, to avoid running the command each time. <br> <br>
8) Now install deepspeed with pip install deepspeed<br><br> 8) Now install deepspeed with pip install deepspeed<br><br>
9) You can now start Text generation webUI `python server.py` ensuring to activate your extensions.<br><br> 9) You can now start Text generation webUI `python server.py` ensuring to activate your extensions.<br><br>
Just to reiterate, starting Text-generation-webUI with `./start_linux.sh` will overwrite the CUDA_HOME variable unless you have permanently changed it, hence always starting it with `./cmd_linux.sh` **then** setting the environment variable manually (step 7) and **then** `python server.py`, which is how you would need to run it each time, unless you permanently set the environment variable for CUDA_HOME within Text-generation-webUI's standard Python environment. Just to reiterate, starting Text-generation-webUI with `./start_linux.sh` will overwrite the CUDA_HOME variable unless you have permanently changed it, hence always starting it with `./cmd_linux.sh` **then** setting the environment variable manually (step 7) and **then** `python server.py`, which is how you would need to run it each time, unless you permanently set the environment variable for CUDA_HOME within Text-generation-webUI's standard Python environment.
@@ -645,7 +708,7 @@ def get_shm_size():
</details> </details>
</details> </details>
## ⬜ AllTalk TTS Generator ### ⬜ AllTalk TTS Generator
AllTalk TTS Generator is the solution for converting large volumes of text into speech using the voice of your choice. Whether you're creating audio content or just want to hear text read aloud, the TTS Generator is equipped to handle it all efficiently. Please see here for a quick [demo](https://www.youtube.com/watch?v=hunvXn0mLzc)<br><br>The link to open the TTS generator can be found on the built-in Settings and Documentation page.<br><br>**DeepSpeed** is **highly** recommended to speed up generation. **Low VRAM** would be best turned off and your LLM model unloaded from your GPU VRAM (unload your model). **No Playback** will reduce memory overhead on very large generations (15,000 words or more). Splitting **Export to Wav** into smaller groups will also reduce memory overhead at the point of exporting your wav files (so good for low memory systems). AllTalk TTS Generator is the solution for converting large volumes of text into speech using the voice of your choice. Whether you're creating audio content or just want to hear text read aloud, the TTS Generator is equipped to handle it all efficiently. Please see here for a quick [demo](https://www.youtube.com/watch?v=hunvXn0mLzc)<br><br>The link to open the TTS generator can be found on the built-in Settings and Documentation page.<br><br>**DeepSpeed** is **highly** recommended to speed up generation. **Low VRAM** would be best turned off and your LLM model unloaded from your GPU VRAM (unload your model). **No Playback** will reduce memory overhead on very large generations (15,000 words or more). Splitting **Export to Wav** into smaller groups will also reduce memory overhead at the point of exporting your wav files (so good for low memory systems).
#### ⬜ Estimated Throughput #### ⬜ Estimated Throughput
@@ -682,7 +745,7 @@ For generating **large amounts of TTS**, it's recommended to select the **No Pla
- Generated audio can be played back from the list, which also highlights the currently playing chunk. - Generated audio can be played back from the list, which also highlights the currently playing chunk.
- The TTS Generator remembers your settings, so you can pick up where you left off even after refreshing the page. - The TTS Generator remembers your settings, so you can pick up where you left off even after refreshing the page.
## 🟠 API Suite and JSON-CURL ### 🟠 API Suite and JSON-CURL
### 🟠Overview ### 🟠Overview
The Text-to-Speech (TTS) Generation API allows you to generate speech from text input using various configuration options. This API supports both character and narrator voices, providing flexibility for creating dynamic and engaging audio content. The Text-to-Speech (TTS) Generation API allows you to generate speech from text input using various configuration options. This API supports both character and narrator voices, providing flexibility for creating dynamic and engaging audio content.
@@ -765,6 +828,7 @@ Generate a preview of a specified voice with hardcoded settings.
JSON return `{"status": "lowvram-success"}` JSON return `{"status": "lowvram-success"}`
### 🟠 TTS Generation Endpoint (Standard Generation) ### 🟠 TTS Generation Endpoint (Standard Generation)
Streaming endpoint details are further down the page.
- URL: `http://127.0.0.1:7851/api/tts-generate`<br> - Method: `POST`<br> - Content-Type: `application/x-www-form-urlencoded`<br> - URL: `http://127.0.0.1:7851/api/tts-generate`<br> - Method: `POST`<br> - Content-Type: `application/x-www-form-urlencoded`<br>
@@ -872,7 +936,7 @@ Example JSON TTS Generation Response:
`{"status":"generate-success","output_file_path":"C:\\text-generation-webui\\extensions\\alltalk_tts\\outputs\\myoutputfile_1704141936.wav","output_file_url":"http://127.0.0.1:7851/audio/myoutputfile_1704141936.wav","output_cache_url":"http://127.0.0.1:7851/audiocache/myoutputfile_1704141936.wav"}` `{"status":"generate-success","output_file_path":"C:\\text-generation-webui\\extensions\\alltalk_tts\\outputs\\myoutputfile_1704141936.wav","output_file_url":"http://127.0.0.1:7851/audio/myoutputfile_1704141936.wav","output_cache_url":"http://127.0.0.1:7851/audiocache/myoutputfile_1704141936.wav"}`
### 🟠 Example command lines (Streaming Generation) ### 🟠 TTS Generation Endpoint (Streaming Generation)
Streaming TTS generation does NOT support Narration and will generate an audio stream. Streaming TTS speech JavaScript Example:<br> Streaming TTS generation does NOT support Narration and will generate an audio stream. Streaming TTS speech JavaScript Example:<br>
- URL: `http://localhost:7851/api/tts-generate-streaming`<br> - Method: `POST`<br> - Content-Type: `application/x-www-form-urlencoded`<br><br> - URL: `http://localhost:7851/api/tts-generate-streaming`<br> - Method: `POST`<br> - Content-Type: `application/x-www-form-urlencoded`<br><br>
@@ -898,6 +962,6 @@ audioElement.play(); // Play the audio stream directly
<br> <br>
### 🔴 Future to-do list ### 🔴 Future to-do list
- I am maintaining a list of things people request [here](https://github.com/erew123/alltalk_tts/discussions/74)
- Possibly add some additional TTS engines (TBD). - Possibly add some additional TTS engines (TBD).
- Tagging support within text.
- Have a break! - Have a break!
+3 -2
View File
@@ -341,6 +341,7 @@ if "%UserChoice%" == "1" (
pip install fsspec>=2023.6.0 pip install fsspec>=2023.6.0
pip install soundfile==0.12.1 pip install soundfile==0.12.1
pip install uvicorn==0.24.0.post1 pip install uvicorn==0.24.0.post1
pip install transformers>=4.37.1
pip install TTS==0.21.3 pip install TTS==0.21.3
pip install fastapi==0.104.1 pip install fastapi==0.104.1
pip install Jinja2==3.1.2 pip install Jinja2==3.1.2
@@ -351,6 +352,8 @@ if "%UserChoice%" == "1" (
pip install pydantic==1.10.13 pip install pydantic==1.10.13
pip install sounddevice==0.4.6 pip install sounddevice==0.4.6
pip install python-multipart==0.0.6 pip install python-multipart==0.0.6
pip install cutlet>=0.3.0
pip install unidic-lite>=1.0.8
echo Downloading DeepSpeed... echo Downloading DeepSpeed...
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-12.7/deepspeed-0.12.7+d058d4b-cp311-cp311-win_amd64.whl
echo Installing DeepSpeed... echo Installing DeepSpeed...
@@ -367,7 +370,6 @@ echo cd /D "%~dp0" >> start_environment.bat
echo set CONDA_ROOT_PREFIX=%cd%\alltalk_environment\conda >> start_environment.bat echo set CONDA_ROOT_PREFIX=%cd%\alltalk_environment\conda >> start_environment.bat
echo set INSTALL_ENV_DIR=%cd%\alltalk_environment\env >> start_environment.bat echo set INSTALL_ENV_DIR=%cd%\alltalk_environment\env >> start_environment.bat
echo call "%CONDA_ROOT_PREFIX%\condabin\conda.bat" activate "%INSTALL_ENV_DIR%" >> start_environment.bat echo call "%CONDA_ROOT_PREFIX%\condabin\conda.bat" activate "%INSTALL_ENV_DIR%" >> start_environment.bat
echo exit >> start_environment.bat
@rem Create start_alltalk.bat to run AllTalk @rem Create start_alltalk.bat to run AllTalk
echo @echo off > start_alltalk.bat echo @echo off > start_alltalk.bat
echo cd /D "%~dp0" >> start_alltalk.bat echo cd /D "%~dp0" >> start_alltalk.bat
@@ -375,7 +377,6 @@ echo set CONDA_ROOT_PREFIX=%cd%\alltalk_environment\conda >> start_alltalk.bat
echo set INSTALL_ENV_DIR=%cd%\alltalk_environment\env >> start_alltalk.bat echo set INSTALL_ENV_DIR=%cd%\alltalk_environment\env >> start_alltalk.bat
echo call "%CONDA_ROOT_PREFIX%\condabin\conda.bat" activate "%INSTALL_ENV_DIR%" >> start_alltalk.bat echo call "%CONDA_ROOT_PREFIX%\condabin\conda.bat" activate "%INSTALL_ENV_DIR%" >> start_alltalk.bat
echo call python script.py >> start_alltalk.bat echo call python script.py >> start_alltalk.bat
echo exit >> start_alltalk.bat
Echo. Echo.
Echo start_alltalk.bat has been created. Echo start_alltalk.bat has been created.
Echo You can now start AllTalk with %L_YELLOW%start_alltalk.bat%RESET% Echo You can now start AllTalk with %L_YELLOW%start_alltalk.bat%RESET%
+3
View File
@@ -301,6 +301,7 @@ install_custom_standalone() {
pip install fsspec>=2023.6.0 pip install fsspec>=2023.6.0
pip install soundfile==0.12.1 pip install soundfile==0.12.1
pip install uvicorn==0.24.0.post1 pip install uvicorn==0.24.0.post1
pip install transformers>=4.37.1
pip install TTS==0.21.3 pip install TTS==0.21.3
pip install fastapi==0.104.1 pip install fastapi==0.104.1
pip install Jinja2==3.1.2 pip install Jinja2==3.1.2
@@ -311,6 +312,8 @@ install_custom_standalone() {
pip install pydantic==1.10.13 pip install pydantic==1.10.13
pip install sounddevice==0.4.6 pip install sounddevice==0.4.6
pip install python-multipart==0.0.6 pip install python-multipart==0.0.6
pip install cutlet>=0.3.0
pip install unidic-lite>=1.0.8
else else
pip install -r requirements_other.txt pip install -r requirements_other.txt
fi fi
+25 -1
View File
@@ -1 +1,25 @@
{"activate": true, "autoplay": true, "branding": "AllTalk ", "narrator_enabled": false, "deepspeed_activate": false, "delete_output_wavs": "Disabled", "ip_address": "127.0.0.1", "language": "English", "low_vram": false, "local_temperature": "0.7", "local_repetition_penalty": "10.0", "tts_model_loaded": true, "tts_model_name": "tts_models/multilingual/multi-dataset/xtts_v2", "narrator_voice": "female_01.wav", "output_folder_wav": "extensions/alltalk_tts/outputs/", "output_folder_wav_standalone": "outputs/", "port_number": "7851", "remove_trailing_dots": false, "show_text": true, "tts_method_api_local": false, "tts_method_api_tts": false, "tts_method_xtts_local": true, "voice": "female_01.wav"} {
"activate": true,
"autoplay": true,
"branding": "AllTalk ",
"narrator_enabled": false,
"deepspeed_activate": false,
"delete_output_wavs": "Disabled",
"ip_address": "127.0.0.1",
"language": "English",
"low_vram": false,
"local_temperature": "0.7",
"local_repetition_penalty": "10.0",
"tts_model_loaded": true,
"tts_model_name": "tts_models/multilingual/multi-dataset/xtts_v2",
"narrator_voice": "female_01.wav",
"output_folder_wav": "extensions/alltalk_tts/outputs/",
"output_folder_wav_standalone": "outputs/",
"port_number": "7851",
"remove_trailing_dots": false,
"show_text": true,
"tts_method_api_local": false,
"tts_method_api_tts": false,
"tts_method_xtts_local": true,
"voice": "female_01.wav"
}
+20
View File
@@ -0,0 +1,20 @@
version: '3.8'
services:
alltalk-tts:
image: erew123/alltalk_tts:cuda
restart: unless-stopped
ports:
- "7851:7851"
volumes:
- ./outputs:/app/outputs/
- ./models:/app/models/
- ./voices:/app/voices/
- ./finetune/put-voice-samples-in-here:/app/finetune/put-voice-samples-in-here
- ./dockerconfig.json:/app/confignew.json
deploy:
resources:
reservations:
devices:
- driver: nvidia
count: all
capabilities: [ gpu ]
+13
View File
@@ -0,0 +1,13 @@
version: '3.8'
services:
alltalk-tts:
image: erew123/alltalk_tts:cpu
restart: unless-stopped
ports:
- "7851:7851"
volumes:
- ./outputs:/app/outputs/
- ./models:/app/models/
- ./voices:/app/voices/
- ./finetune/put-voice-samples-in-here:/app/finetune/put-voice-samples-in-here
- ./dockerconfig.json:/app/confignew.json
+25
View File
@@ -0,0 +1,25 @@
{
"activate": true,
"autoplay": true,
"branding": "AllTalk ",
"narrator_enabled": false,
"deepspeed_activate": true,
"delete_output_wavs": "Disabled",
"ip_address": "127.0.0.1",
"language": "English",
"low_vram": false,
"local_temperature": "0.7",
"local_repetition_penalty": "10.0",
"tts_model_loaded": true,
"tts_model_name": "tts_models/multilingual/multi-dataset/xtts_v2",
"narrator_voice": "female_01.wav",
"output_folder_wav": "extensions/alltalk_tts/outputs/",
"output_folder_wav_standalone": "outputs/",
"port_number": "7851",
"remove_trailing_dots": false,
"show_text": true,
"tts_method_api_local": false,
"tts_method_api_tts": false,
"tts_method_xtts_local": true,
"voice": "female_01.wav"
}
+14 -4
View File
@@ -75,10 +75,10 @@ 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, gradio_progress=progress): def format_audio_list(target_language, whisper_model, out_path, eval_split_number, gradio_progress=progress):
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=0.15 eval_percentage = eval_split_number / 100.0
speaker_name="coqui" speaker_name="coqui"
audio_total_size = 0 audio_total_size = 0
os.makedirs(out_path, exist_ok=True) os.makedirs(out_path, exist_ok=True)
@@ -87,6 +87,7 @@ def format_audio_list(target_language, whisper_model, out_path, gradio_progress=
print("[FINETUNE] \033[94mPart of AllTalk\033[0m https://github.com/erew123/alltalk_tts/") print("[FINETUNE] \033[94mPart of AllTalk\033[0m https://github.com/erew123/alltalk_tts/")
print("[FINETUNE] \033[94mCoqui Public Model License\033[0m") print("[FINETUNE] \033[94mCoqui Public Model License\033[0m")
print("[FINETUNE] \033[94mhttps://coqui.ai/cpml.txt\033[0m") print("[FINETUNE] \033[94mhttps://coqui.ai/cpml.txt\033[0m")
print(f"[FINETUNE] \033[94mWhisper model: \033[92m{whisper_model} \033[94mLanguage: \033[92m{target_language} \033[94mEvaluation data percentage: \033[92m{eval_split_number}%\033[0m")
print("[FINETUNE] \033[94mStarting Step 1\033[0m - Preparing Audio/Generating the dataset") print("[FINETUNE] \033[94mStarting Step 1\033[0m - Preparing Audio/Generating the dataset")
# Write the target language to lang.txt in the output directory # Write the target language to lang.txt in the output directory
lang_file_path = os.path.join(out_path, "lang.txt") lang_file_path = os.path.join(out_path, "lang.txt")
@@ -968,6 +969,14 @@ if __name__ == "__main__":
"ja" "ja"
], ],
) )
eval_split_number = gr.Number(
label="Evaluation data Split (the % to use for Evaluation data)",
value=15, # Default value
minimum=5, # Minimum value
maximum=95, # Maximum value
step=1, # Increment step
)
progress_data = gr.Label( progress_data = gr.Label(
label="Progress:" label="Progress:"
) )
@@ -979,14 +988,14 @@ 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, progress=gr.Progress(track_tqdm=True)): def preprocess_dataset(language, whisper_model, out_path, eval_split_number, 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, 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, gradio_progress=progress)
except: except:
traceback.print_exc() traceback.print_exc()
error = traceback.format_exc() error = traceback.format_exc()
@@ -1274,6 +1283,7 @@ if __name__ == "__main__":
lang, lang,
whisper_model, whisper_model,
out_path, out_path,
eval_split_number,
], ],
outputs=[ outputs=[
progress_data, progress_data,
+5
View File
@@ -0,0 +1,5 @@
#!/bin/sh
python modeldownload.py
uvicorn tts_server:app --host 0.0.0.0 --port 7851 --workers 1 --proxy-headers &
sleep 5
python script.py
+20
View File
@@ -0,0 +1,20 @@
FROM nvidia/cuda:12.1.1-devel-ubuntu22.04
ENV HOST 0.0.0.0
RUN apt-get update && apt-get upgrade -y \
&& apt-get install -y git build-essential portaudio19-dev \
python3 python3-pip gcc wget \
ocl-icd-opencl-dev opencl-headers clinfo \
libclblast-dev libopenblas-dev \
&& mkdir -p /etc/OpenCL/vendors && echo "libnvidia-opencl.so.1" > /etc/OpenCL/vendors/nvidia.icd \
&& ln -s /usr/bin/python3 /usr/bin/python
WORKDIR /app
COPY . .
ENV CUDA_DOCKER_ARCH=all
RUN pip install --no-cache-dir -r requirements_nvidia.txt && \
pip install --no-cache-dir -r requirements_finetune.txt && \
pip install --no-cache-dir -r requirements_other.txt && \
pip install --no-cache-dir deepspeed
EXPOSE 7851
RUN chmod +x launch.sh
ENTRYPOINT ["sh", "-c", "./launch.sh"]
+3
View File
@@ -1,6 +1,7 @@
numpy>=1.22.0 numpy>=1.22.0
soundfile>=0.12.1 soundfile>=0.12.1
uvicorn>=0.24.0.post1 uvicorn>=0.24.0.post1
transformers>=4.37.1
TTS>=0.21.3 TTS>=0.21.3
torch>=2.1.0+cu118 torch>=2.1.0+cu118
torchaudio>=2.1.0+cu118 torchaudio>=2.1.0+cu118
@@ -13,3 +14,5 @@ packaging>=23.2
pydantic>=1.10.13 pydantic>=1.10.13
sounddevice>=0.4.6 sounddevice>=0.4.6
python-multipart>=0.0.6 python-multipart>=0.0.6
cutlet>=0.3.0
unidic-lite>=1.0.8
+3
View File
@@ -1,6 +1,7 @@
numpy>=1.22.0 numpy>=1.22.0
soundfile>=0.12.1 soundfile>=0.12.1
uvicorn>=0.24.0.post1 uvicorn>=0.24.0.post1
transformers>=4.37.1
TTS>=0.21.3 TTS>=0.21.3
torch>=2.1.0 torch>=2.1.0
torchaudio>=2.1.0 torchaudio>=2.1.0
@@ -13,3 +14,5 @@ packaging>=23.2
pydantic>=1.10.13 pydantic>=1.10.13
sounddevice>=0.4.6 sounddevice>=0.4.6
python-multipart>=0.0.6 python-multipart>=0.0.6
cutlet>=0.3.0
unidic-lite>=1.0.8
+243 -79
View File
@@ -16,18 +16,28 @@ import numpy as np
import soundfile as sf import soundfile as sf
import uuid import uuid
######################################
#### ALLTALK ALLOWED STARTUP TIME ####
######################################
startup_wait_time = 120
# You can change the above setting to a larger number to allow AllTAlk more time to start up. The default setting is 120 seconds (2 minutes).
# On some older systems you may need to allow AllTalk more time. So you could set it to 240 (4 minutes) which will give AllTalk more to load.
################################################################# #################################################################
#### LOAD PARAMS FROM confignew.json - REQUIRED FOR BRANDING #### #### LOAD PARAMS FROM confignew.json - REQUIRED FOR BRANDING ####
################################################################# #################################################################
# 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)
@@ -48,6 +58,7 @@ except ModuleNotFoundError:
# IMPORT - Attempt Importing DeepSpeed (required for displaying Deepspeed checkbox in gradio) # IMPORT - Attempt Importing DeepSpeed (required for displaying Deepspeed checkbox in gradio)
try: try:
import deepspeed import deepspeed
deepspeed_installed = True deepspeed_installed = True
except ImportError: except ImportError:
deepspeed_installed = False deepspeed_installed = False
@@ -60,6 +71,7 @@ try:
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
from modules.utils import gradio from modules.utils import gradio
# 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"]
@@ -80,10 +92,12 @@ process_lock = threading.Lock()
# Base setting for a possible FineTuned model existing and the loader being available # Base setting for a possible FineTuned model existing and the loader being available
tts_method_xtts_ft = False tts_method_xtts_ft = False
# Gather the voice files # Gather the voice files
def get_available_voices(): 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 #### #### LICENSE DISPLAY ####
######################### #########################
@@ -141,6 +155,7 @@ def check_required_files():
subprocess.run(["python", str(download_script_path)]) subprocess.run(["python", str(download_script_path)])
print(f"[{params['branding']}Startup] All required files are present.") print(f"[{params['branding']}Startup] All required files are present.")
# STARTUP Call Check routine # STARTUP Call Check routine
check_required_files() check_required_files()
@@ -155,7 +170,9 @@ finetuned_model = trained_model_directory.exists()
# If true, this will add a extra option in the Gradio interface for loading Xttsv2 FT # If true, this will add a extra option in the Gradio interface for loading Xttsv2 FT
if finetuned_model: if finetuned_model:
required_files = ["model.pth", "config.json", "vocab.json"] required_files = ["model.pth", "config.json", "vocab.json"]
finetuned_model = all((trained_model_directory / file).exists() for file in required_files) finetuned_model = all(
(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] Finetuned model \033[93mDetected\033[0m")
@@ -173,6 +190,7 @@ 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 ####
###################### ######################
@@ -230,20 +248,24 @@ def signal_handler(sig, frame):
# Attach the signal handler to the SIGINT signal (Ctrl+C) # Attach the signal handler to the SIGINT signal (Ctrl+C)
signal.signal(signal.SIGINT, signal_handler) signal.signal(signal.SIGINT, signal_handler)
# Check if we're running in docker
# Start the subprocess if os.path.isfile("/.dockerenv"):
process = subprocess.Popen(["python", script_path]) print(
f"[{params['branding']}Startup] \033[94mRunning in Docker. Please wait.\033[0m"
# Check if the subprocess has started successfully )
if process.poll() is None: else:
# Start the subprocess
process = subprocess.Popen(["python", script_path])
# Check if the subprocess has started successfully
if process.poll() is None:
print(f"[{params['branding']}Startup] TTS Subprocess starting") print(f"[{params['branding']}Startup] TTS Subprocess starting")
print(f"[{params['branding']}Startup]") print(f"[{params['branding']}Startup]")
print( print(
f"[{params['branding']}Startup] \033[94mSettings & 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"
) )
@@ -266,12 +288,12 @@ else:
# 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)
timeout = 120 # Adjust the timeout as needed timeout = startup_wait_time # Gather timeout setting from startup_wait_time
# Introduce a delay before starting the check loop # Introduce a delay before starting the check loop
time.sleep(26) # Wait 26 secs before checking if the tts_server.py has started up. time.sleep(26) # Wait 26 secs before checking if the tts_server.py has started up.
start_time = time.time() start_time = time.time()
while time.time() - start_time < timeout: while time.time() - start_time < timeout:
try: try:
response = requests.get(f"{base_url}/ready") response = requests.get(f"{base_url}/ready")
if response.status_code == 200: if response.status_code == 200:
@@ -279,12 +301,24 @@ while time.time() - start_time < timeout:
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 120 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(4) time.sleep(5)
else: else:
print( print(
f"[{params['branding']}Startup] Startup timed out. Check the server logs for more information." 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(
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)
@@ -342,6 +376,7 @@ def send_reload_request(tts_method):
) )
return {"status": "error", "message": str(e)} return {"status": "error", "message": str(e)}
################## ##################
#### LOW VRAM #### #### LOW VRAM ####
################## ##################
@@ -456,9 +491,13 @@ 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_folder, f'{state["character_menu"]}_{int(time.time())}_combined.wav') output_file_path = os.path.join(
output_folder, f'{state["character_menu"]}_{int(time.time())}_combined.wav'
)
else: else:
output_file_path = os.path.join(output_folder, f'TTSOUT_{int(time.time())}_combined.wav') output_file_path = os.path.join(
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:
@@ -489,12 +528,19 @@ def voice_preview(string):
output_file = Path(params["output_folder_wav"]) / "voice_preview.wav" output_file = Path(params["output_folder_wav"]) / "voice_preview.wav"
# Generate the audio # Generate the audio
language_code = languages.get(params["language"]) language_code = languages.get(params["language"])
temperature = params["local_temperature"]
repetition_penalty = params["local_repetition_penalty"]
# Convert the WindowsPath object to a string before using it in JSON payload # Convert the WindowsPath object to a string before using it in JSON payload
output_file_str = output_file.as_posix() output_file_str = output_file.as_posix()
# Lock before making the generate request # Lock before making the generate request
with process_lock: with process_lock:
generate_response = send_generate_request( generate_response = send_generate_request(
string, params["voice"], language_code, output_file_str string,
params["voice"],
language_code,
temperature,
repetition_penalty,
output_file_str,
) )
# Check if lock is already acquired # Check if lock is already acquired
if process_lock.locked(): if process_lock.locked():
@@ -516,7 +562,7 @@ def process_text(text):
text = html.unescape(text) text = html.unescape(text)
# Replace ellipsis with a single dot # Replace ellipsis with a single dot
text = re.sub(r'\.{3,}', '.', text) text = re.sub(r"\.{3,}", ".", text)
# Pattern to identify combined narrator and character speech # Pattern to identify combined narrator and character speech
combined_pattern = r'(\*[^*"]+\*|"[^"*]+")' combined_pattern = r'(\*[^*"]+\*|"[^"*]+")'
@@ -531,22 +577,22 @@ def process_text(text):
for match in re.finditer(combined_pattern, text): for match in re.finditer(combined_pattern, text):
# Add the text before the match, if any, as ambiguous # Add the text before the match, if any, as ambiguous
if start < match.start(): if start < match.start():
ambiguous_text = text[start:match.start()].strip() ambiguous_text = text[start : match.start()].strip()
if ambiguous_text: if ambiguous_text:
ordered_parts.append(('ambiguous', ambiguous_text)) ordered_parts.append(("ambiguous", ambiguous_text))
# Add the matched part as either narrator or character # Add the matched part as either narrator or character
matched_text = match.group(0) matched_text = match.group(0)
if matched_text.startswith('*') and matched_text.endswith('*'): if matched_text.startswith("*") and matched_text.endswith("*"):
ordered_parts.append(('narrator', matched_text.strip('*').strip())) ordered_parts.append(("narrator", matched_text.strip("*").strip()))
elif matched_text.startswith('"') and matched_text.endswith('"'): elif matched_text.startswith('"') and matched_text.endswith('"'):
ordered_parts.append(('character', matched_text.strip('"').strip())) ordered_parts.append(("character", matched_text.strip('"').strip()))
else: else:
# In case of mixed or improperly formatted parts # In case of mixed or improperly formatted parts
if '*' in matched_text: if "*" in matched_text:
ordered_parts.append(('narrator', matched_text.strip('*').strip('"'))) ordered_parts.append(("narrator", matched_text.strip("*").strip('"')))
else: else:
ordered_parts.append(('character', matched_text.strip('"').strip('*'))) ordered_parts.append(("character", matched_text.strip('"').strip("*")))
# Update the start of the next segment # Update the start of the next segment
start = match.end() start = match.end()
@@ -555,10 +601,11 @@ def process_text(text):
if start < len(text): if start < len(text):
ambiguous_text = text[start:].strip() ambiguous_text = text[start:].strip()
if ambiguous_text: if ambiguous_text:
ordered_parts.append(('ambiguous', ambiguous_text)) ordered_parts.append(("ambiguous", ambiguous_text))
return ordered_parts return ordered_parts
################################# #################################
#### TTS STANDARD GENERATION #### #### TTS STANDARD GENERATION ####
################################# #################################
@@ -567,12 +614,14 @@ def output_modifier(string, state):
if not params["activate"]: if not params["activate"]:
return string return string
original_string = string original_string = string
#print("ORIGINAL STRING IS:", original_string) # print("ORIGINAL STRING IS:", original_string)
cleaned_string = before_audio_generation(string, params) cleaned_string = before_audio_generation(string, params)
if cleaned_string is None: if cleaned_string is None:
return return
string = cleaned_string string = cleaned_string
language_code = languages.get(params["language"]) language_code = languages.get(params["language"])
temperature = params["local_temperature"]
repetition_penalty = params["local_repetition_penalty"]
# Create a list to store generated audio paths # Create a list to store generated audio paths
audio_files = [] audio_files = []
if process_lock.acquire(blocking=False): if process_lock.acquire(blocking=False):
@@ -587,29 +636,46 @@ def output_modifier(string, state):
continue continue
# 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(f"[{params['branding']}TTSGen] \033[92mNarrator\033[0m") # Green print(
elif part_type == 'character': f"[{params['branding']}TTSGen] \033[92mNarrator\033[0m"
) # Green
elif part_type == "character":
voice_to_use = params["voice"] voice_to_use = params["voice"]
print(f"[{params['branding']}TTSGen] \033[36mCharacter\033[0m") # Yellow print(
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 = params["voice"] if non_quoted_text_is else params["narrator_voice"] voice_to_use = (
voice_description = "\033[36mCharacter (Text-not-inside)\033[0m" if non_quoted_text_is else "\033[92mNarrator (Text-not-inside)\033[0m" params["voice"]
if non_quoted_text_is
else params["narrator_voice"]
)
voice_description = (
"\033[36mCharacter (Text-not-inside)\033[0m"
if non_quoted_text_is
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"([!?.])\1+", r"\1", 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\.,;:!?\-\'"\u0100-\u017F\u0400-\u04FFÀ-ÿ\u0150\u0151\u0170\u0171\u4E00-\u9FFF\u3400-\u4DBF\u20000-\u2A6DF\u3000-\u303F\$]', '', 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)
# Generate TTS and output to a file # Generate TTS and output to a file
output_filename = get_output_filename(state) output_filename = get_output_filename(state)
generate_response = send_generate_request( generate_response = send_generate_request(
cleaned_part, voice_to_use, language_code, output_filename cleaned_part,
voice_to_use,
language_code,
temperature,
repetition_penalty,
output_filename,
) )
audio_path = generate_response.get("data", {}).get("audio_path") audio_path = generate_response.get("data", {}).get("audio_path")
audio_files_all_paragraphs.append(audio_path) audio_files_all_paragraphs.append(audio_path)
@@ -622,20 +688,29 @@ def output_modifier(string, state):
# Decode HTML entities first # Decode HTML entities first
cleaned_part = html.unescape(original_string) cleaned_part = html.unescape(original_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"([!?.])\1+", r"\1", 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\.,;:!?\-\'"\u0100-\u017F\u0400-\u04FFÀ-ÿ\u0150\u0151\u0170\u0171\u4E00-\u9FFF\u3400-\u4DBF\u20000-\u2A6DF\u3000-\u303F\$]', '', 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.
if "character_menu" in state: if "character_menu" in state:
output_file = Path(f'{params["output_folder_wav"]}/{state["character_menu"]}_{int(time.time())}.wav') output_file = Path(
f'{params["output_folder_wav"]}/{state["character_menu"]}_{int(time.time())}.wav'
)
else: else:
output_file = Path(f'{params["output_folder_wav"]}/TTSOUT_{int(time.time())}.wav') output_file = Path(
f'{params["output_folder_wav"]}/TTSOUT_{int(time.time())}.wav'
)
output_file_str = output_file.as_posix() output_file_str = output_file.as_posix()
output_file = get_output_filename(state) output_file = get_output_filename(state)
generate_response = send_generate_request( generate_response = send_generate_request(
cleaned_part, params["voice"], language_code, output_file_str cleaned_part,
params["voice"],
language_code,
temperature,
repetition_penalty,
output_file_str,
) )
audio_path = generate_response.get("data", {}).get("audio_path") audio_path = generate_response.get("data", {}).get("audio_path")
final_output_file = audio_path final_output_file = audio_path
@@ -676,20 +751,28 @@ def output_modifier(string, state):
def get_output_filename(state): def get_output_filename(state):
if "character_menu" in state: if "character_menu" in state:
return Path(f'{params["output_folder_wav"]}/{state["character_menu"]}_{str(uuid.uuid4())[:8]}.wav').as_posix() return Path(
f'{params["output_folder_wav"]}/{state["character_menu"]}_{str(uuid.uuid4())[:8]}.wav'
).as_posix()
else: else:
return Path(f'{params["output_folder_wav"]}/TTSOUT_{str(uuid.uuid4())[:8]}.wav').as_posix() return Path(
f'{params["output_folder_wav"]}/TTSOUT_{str(uuid.uuid4())[:8]}.wav'
).as_posix()
############################################### ###############################################
#### SEND GENERATION REQUEST TO TTS ENGINE #### #### SEND GENERATION REQUEST TO TTS ENGINE ####
############################################### ###############################################
def send_generate_request(text, voice, language, output_file): def send_generate_request(
text, voice, language, temperature, repetition_penalty, output_file
):
url = f"{base_url}/api/generate" url = f"{base_url}/api/generate"
payload = { payload = {
"text": text, "text": text,
"voice": voice, "voice": voice,
"language": language, "language": language,
"temperature": temperature,
"repetition_penalty": repetition_penalty,
"output_file": output_file, "output_file": output_file,
} }
headers = {"Content-Type": "application/json"} headers = {"Content-Type": "application/json"}
@@ -714,12 +797,14 @@ def state_modifier(state):
state["stream"] = False state["stream"] = False
return state return state
def update_narrator_enabled(value): def update_narrator_enabled(value):
if value == "Enabled": if value == "Enabled":
params["narrator_enabled"] = True params["narrator_enabled"] = True
elif value == "Disabled": elif value == "Disabled":
params["narrator_enabled"] = False params["narrator_enabled"] = False
def update_non_quoted_text_is(value): def update_non_quoted_text_is(value):
global non_quoted_text_is global non_quoted_text_is
if value == "Narrator": if value == "Narrator":
@@ -727,6 +812,7 @@ def update_non_quoted_text_is(value):
elif value == "Char": elif value == "Char":
non_quoted_text_is = True non_quoted_text_is = True
def input_modifier(string, state): def input_modifier(string, state):
if not params["activate"]: if not params["activate"]:
return string return string
@@ -737,20 +823,31 @@ def input_modifier(string, state):
def ui(): def ui():
with gr.Accordion(params["branding"] + " TTS (XTTSv2)"): with gr.Accordion(params["branding"] + " TTS (XTTSv2)"):
# Activate alltalk_tts, Enable autoplay, Show text
with gr.Row(): with gr.Row():
activate = gr.Checkbox(value=params['activate'], label='Enable TTS') activate = gr.Checkbox(value=params["activate"], label="Enable TTS")
autoplay = gr.Checkbox(value=params['autoplay'], label='Autoplay TTS') autoplay = gr.Checkbox(value=params["autoplay"], label="Autoplay TTS")
show_text = gr.Checkbox(value=params['show_text'], label='Show Text') show_text = gr.Checkbox(value=params["show_text"], label="Show Text")
# Low vram enable, Deepspeed enable, Remove trailing dots
with gr.Row(): with gr.Row():
low_vram = gr.Checkbox(value=params['low_vram'], label='Enable Low VRAM Mode') low_vram = gr.Checkbox(
value=params["low_vram"], label="Enable Low VRAM Mode"
)
low_vram_play = gr.HTML(visible=False) low_vram_play = gr.HTML(visible=False)
deepspeed_checkbox = gr.Checkbox(value=params['deepspeed_activate'], label='Enable DeepSpeed', visible=deepspeed_installed) deepspeed_checkbox = gr.Checkbox(
value=params["deepspeed_activate"],
label="Enable DeepSpeed",
visible=deepspeed_installed,
)
deepspeed_checkbox_play = gr.HTML(visible=False) deepspeed_checkbox_play = gr.HTML(visible=False)
remove_trailing_dots = gr.Checkbox(value=params['remove_trailing_dots'], label='Remove trailing "."') remove_trailing_dots = gr.Checkbox(
value=params["remove_trailing_dots"], label='Remove trailing "."'
)
# TTS method, Character voice selection
with gr.Row(): with gr.Row():
model_loader_choices=["API TTS", "API Local", "XTTSv2 Local"] model_loader_choices = ["API TTS", "API Local", "XTTSv2 Local"]
if finetuned_model: if finetuned_model:
model_loader_choices.append("XTTSv2 FT") model_loader_choices.append("XTTSv2 FT")
tts_radio_buttons = gr.Radio( tts_radio_buttons = gr.Radio(
@@ -761,32 +858,88 @@ def ui():
tts_radio_buttons_play = gr.HTML(visible=False) tts_radio_buttons_play = gr.HTML(visible=False)
with gr.Row(): with gr.Row():
available_voices = get_available_voices() available_voices = get_available_voices()
default_voice = params["voice"] # Check if the default voice is in the list of available voices default_voice = params[
"voice"
] # Check if the default voice is in the list of available voices
if default_voice not in available_voices: if default_voice not in available_voices:
default_voice = available_voices[ 0 ] # Choose the first available voice as the default default_voice = available_voices[
0
] # Choose the first available voice as the default
# Add allow_custom_value=True to the Dropdown # Add allow_custom_value=True to the Dropdown
voice = gr.Dropdown(available_voices, label="Character Voice", value=default_voice, allow_custom_value=True, ) voice = gr.Dropdown(
create_refresh_button(voice, lambda: None, lambda: {"choices": get_available_voices(), "value": params["voice"],}, "refresh-button",) available_voices,
label="Character Voice",
value=default_voice,
allow_custom_value=True,
)
create_refresh_button(
voice,
lambda: None,
lambda: {
"choices": get_available_voices(),
"value": params["voice"],
},
"refresh-button",
)
# Language, Narrator voice
with gr.Row(): with gr.Row():
language = gr.Dropdown(languages.keys(), label="Language", value=params["language"]) language = gr.Dropdown(
languages.keys(), label="Language", value=params["language"]
)
with gr.Row(): with gr.Row():
narrator_voice_gr = gr.Dropdown(get_available_voices(), label="Narrator Voice", allow_custom_value=True, value=params["narrator_voice"]) narrator_voice_gr = gr.Dropdown(
create_refresh_button(narrator_voice_gr, lambda: None, lambda: {"choices": get_available_voices(), "value": params["narrator_voice"],}, "refresh-button") get_available_voices(),
label="Narrator Voice",
allow_custom_value=True,
value=params["narrator_voice"],
)
create_refresh_button(
narrator_voice_gr,
lambda: None,
lambda: {
"choices": get_available_voices(),
"value": params["narrator_voice"],
},
"refresh-button",
)
# Temperature and Repetition Penalty (Not yet parsed to api/implemented) # Temperature, Repetition Penalty
# with gr.Row(): with gr.Row():
# local_temperature_gr = gr.Slider(minimum=0, maximum=1, step=0.05, label="Temperature", value=params["local_temperature"]) local_temperature_gr = gr.Slider(
# local_repetition_penalty_gr = gr.Slider(minimum=0, maximum=20, step=0.5, label="Repetition Penalty", value=params["local_repetition_penalty"]) minimum=0.05,
maximum=1,
step=0.05,
label="Temperature",
value=params["local_temperature"],
)
local_repetition_penalty_gr = gr.Slider(
minimum=0.5,
maximum=20,
step=0.5,
label="Repetition Penalty",
value=params["local_repetition_penalty"],
)
# Narrator enable, Non quoted text, Explanation text
with gr.Row(): with gr.Row():
with gr.Row(): with gr.Row():
narrator_enabled_gr = gr.Radio(choices={"Enabled": "true", "Disabled": "false"}, label="Narrator", value="Enabled" if params.get("narrator_enabled") else "Disabled") narrator_enabled_gr = gr.Radio(
non_quoted_text_is_gr = gr.Radio(choices={"Character": "true", "Narrator": "false"}, label='Unmarked text NOT inside of * or " is', value="Character" if non_quoted_text_is else "Narrator") choices={"Enabled": "true", "Disabled": "false"},
label="Narrator",
value="Enabled" if params.get("narrator_enabled") else "Disabled",
)
non_quoted_text_is_gr = gr.Radio(
choices={"Character": "true", "Narrator": "false"},
label='Unmarked text NOT inside of * or " is',
value="Character" if non_quoted_text_is else "Narrator",
)
explanation_text = gr.HTML( explanation_text = gr.HTML(
f"<p>⚙️ <a href='http://{params['ip_address']}:{params['port_number']}'>Settings and Documentation Page</a><a href='http://{params['ip_address']}:{params['port_number']}'></a>⚙️<br>- Low VRAM Mode and Deepspeed take 15 seconds to be enabled or disabled.<br>- The DeepSpeed checkbox is only visible if DeepSpeed is present.</p>") f"<p>⚙️ <a href='http://{params['ip_address']}:{params['port_number']}'>Settings and Documentation Page</a><a href='http://{params['ip_address']}:{params['port_number']}'></a>⚙️<br>- Low VRAM Mode and Deepspeed take 15 seconds to be enabled or disabled.<br>- The DeepSpeed checkbox is only visible if DeepSpeed is present.</p>"
)
# Preview speech
with gr.Row(): with gr.Row():
preview_text = gr.Text( preview_text = gr.Text(
show_label=False, show_label=False,
@@ -856,23 +1009,34 @@ def ui():
autoplay.change(lambda x: params.update({"autoplay": x}), autoplay, None) autoplay.change(lambda x: params.update({"autoplay": x}), autoplay, None)
low_vram.change(lambda x: params.update({"low_vram": x}), low_vram, None) low_vram.change(lambda x: params.update({"low_vram": x}), low_vram, None)
low_vram.change(lambda x: send_lowvram_request(x), low_vram, low_vram_play, None) low_vram.change(lambda x: send_lowvram_request(x), low_vram, low_vram_play, None)
tts_radio_buttons.change(send_reload_request, tts_radio_buttons, tts_radio_buttons_play, None) tts_radio_buttons.change(
deepspeed_checkbox.change(send_deepspeed_request, deepspeed_checkbox, deepspeed_checkbox_play, None) send_reload_request, tts_radio_buttons, tts_radio_buttons_play, None
)
deepspeed_checkbox.change(
send_deepspeed_request, deepspeed_checkbox, deepspeed_checkbox_play, None
)
remove_trailing_dots.change( remove_trailing_dots.change(
lambda x: params.update({"remove_trailing_dots": x}), remove_trailing_dots, None lambda x: params.update({"remove_trailing_dots": x}), remove_trailing_dots, None
) )
voice.change(lambda x: params.update({"voice": x}), voice, None) voice.change(lambda x: params.update({"voice": x}), voice, None)
language.change(lambda x: params.update({"language": x}), language, None) language.change(lambda x: params.update({"language": x}), language, None)
# TSS Settings
# TS Settings (Not yet parsed to api/implemented) local_temperature_gr.change(
# local_temperature_gr.change(lambda x: params.update({"local_temperature": x}), local_temperature_gr, None) lambda x: params.update({"local_temperature": x}), local_temperature_gr, None
# local_repetition_penalty_gr.change(lambda x: params.update({"local_repetition_penalty": x}), local_repetition_penalty_gr, None) )
local_repetition_penalty_gr.change(
lambda x: params.update({"local_repetition_penalty": x}),
local_repetition_penalty_gr,
None,
)
# Narrator selection actions # Narrator selection actions
narrator_enabled_gr.change(update_narrator_enabled, narrator_enabled_gr, None) narrator_enabled_gr.change(update_narrator_enabled, narrator_enabled_gr, None)
non_quoted_text_is_gr.change(update_non_quoted_text_is, non_quoted_text_is_gr, None) non_quoted_text_is_gr.change(update_non_quoted_text_is, non_quoted_text_is_gr, None)
narrator_voice_gr.change(lambda x: params.update({"narrator_voice": x}), narrator_voice_gr, None) narrator_voice_gr.change(
lambda x: params.update({"narrator_voice": x}), narrator_voice_gr, None
)
# Play preview # Play preview
preview_text.submit(voice_preview, preview_text, preview_audio) preview_text.submit(voice_preview, preview_text, preview_audio)
+104 -209
View File
@@ -3,92 +3,33 @@ import { saveTtsProviderSettings } from './index.js';
export { AllTalkTtsProvider }; export { AllTalkTtsProvider };
const css = `
.settings-row {
display: flex;
justify-content: space-between; /* Distribute space evenly between the children */
align-items: center;
width: 100%; /* Full width to spread out the children */
}
.settings-option {
flex: 1; /* Each child will take equal space */
margin: 0 10px; /* Adjust spacing as needed */
}
.endpoint-option {
flex: 1; /* Each child will take equal space */
margin: 0 10px; /* Adjust spacing as needed */
margin-right: 25px;
width: 38%; /* Full width to spread out the children */
}
.website-row {
display: flex;
justify-content: start; /* Aligns items to the start of the row */
align-items: center;
margin-top: 10px; /* Top margin */
margin-bottom: 10px; /* Bottom margin */
}
.website-option {
flex: 1; /* Allows each option to take equal space */
margin-right: 10px; /* Spacing between the two options, adjust as needed */
margin-left: 10px; /* Adjust this value to align as desired */
/* Remove the individual margin top and bottom declarations if they are set globally on the .settings-row */
}
.settings-separator {
margin-top: 10px;
margin-bottom: 10px;
padding: 18x;
font-weight: bold;
border-top: 1px solid #e1e1e1; /* Grey line */
border-bottom: 1px solid #e1e1e1; /* Grey line */
text-align: center;
}
.status-message {
flex: 1; /* Adjust as needed */
margin: 0 10px;
/* Additional styling */
}
.model-endpoint-row {
display: flex;
justify-content: space-between;
align-items: center;
width: 100%;
}
.model-option, .endpoint-option {
flex: 1;
margin: 0 10px;
margin-left: 10px; /* Adjust this value to align as desired */
/* Adjust width as needed to fit in one line */
}
.endpoint-option {
/* Adjust width to align with model dropdown */
width: 38%;
}
#status_info {
color: lightgreen; /* Or any specific shade of green you prefer */
}
`;
const style = document.createElement('style');
style.type = 'text/css';
style.appendChild(document.createTextNode(css));
document.head.appendChild(style);
class AllTalkTtsProvider { class AllTalkTtsProvider {
//########// //########//
// Config // // Config //
//########// //########//
settings; settings = {};
constructor() {
// Initialize with default settings if they are not already set
this.settings = {
provider_endpoint: this.settings.provider_endpoint || 'http://localhost:7851',
language: this.settings.language || 'en',
voiceMap: this.settings.voiceMap || {},
at_generation_method: this.settings.at_generation_method || 'standard_generation',
narrator_enabled: this.settings.narrator_enabled || 'false',
at_narrator_text_not_inside: this.settings.at_narrator_text_not_inside || 'narrator',
narrator_voice_gen: this.settings.narrator_voice_gen || 'female_01.wav',
finetuned_model: this.settings.finetuned_model || 'false'
};
// Separate property for dynamically updated settings from the server
this.dynamicSettings = {
modelsAvailable: [],
currentModel: '',
deepspeed_available: false,
deepSpeedEnabled: false,
lowVramEnabled: false,
};
}
ready = false; ready = false;
voices = []; voices = [];
separator = '. '; separator = '. ';
@@ -114,23 +55,12 @@ class AllTalkTtsProvider {
'Hindi': 'hi', 'Hindi': 'hi',
}; };
Settings = {
provider_endpoint: 'http://localhost:7851',
language: 'en',
voiceMap: {},
at_generation_method: 'standard_generation',
narrator_enabled: 'false',
at_narrator_text_not_inside: 'narrator',
narrator_voice_gen: 'female_01.wav',
finetuned_model: 'false'
};
get settingsHtml() { get settingsHtml() {
let html = `<div class="settings-separator">AllTalk Settings</div>`; let html = `<div class="at-settings-separator">AllTalk Settings</div>`;
html += `<div class="settings-row"> html += `<div class="at-settings-row">
<div class="settings-option"> <div class="at-settings-option">
<label for="at_generation_method">AllTalk TTS Generation Method</label> <label for="at_generation_method">AllTalk TTS Generation Method</label>
<select id="at_generation_method"> <select id="at_generation_method">
<option value="standard_generation">Standard Audio Generation (AT Narrator - Optional)</option> <option value="standard_generation">Standard Audio Generation (AT Narrator - Optional)</option>
@@ -139,9 +69,9 @@ class AllTalkTtsProvider {
</div> </div>
</div>`; </div>`;
html += `<div class="settings-row"> html += `<div class="at-settings-row">
<div class="settings-option"> <div class="at-settings-option">
<label for="at_narrator_enabled">AT Narrator</label> <label for="at_narrator_enabled">AT Narrator</label>
<select id="at_narrator_enabled"> <select id="at_narrator_enabled">
<option value="true">Enabled</option> <option value="true">Enabled</option>
@@ -149,7 +79,7 @@ class AllTalkTtsProvider {
</select> </select>
</div> </div>
<div class="settings-option"> <div class="at-settings-option">
<label for="at_narrator_text_not_inside">Text Not Inside * or " is</label> <label for="at_narrator_text_not_inside">Text Not Inside * or " is</label>
<select id="at_narrator_text_not_inside"> <select id="at_narrator_text_not_inside">
<option value="narrator">Narrator</option> <option value="narrator">Narrator</option>
@@ -159,8 +89,8 @@ class AllTalkTtsProvider {
</div>`; </div>`;
html += `<div class="settings-row"> html += `<div class="at-settings-row">
<div class="settings-option"> <div class="at-settings-option">
<label for="narrator_voice">Narrator Voice</label> <label for="narrator_voice">Narrator Voice</label>
<select id="narrator_voice">`; <select id="narrator_voice">`;
if (this.voices) { if (this.voices) {
@@ -170,7 +100,7 @@ class AllTalkTtsProvider {
} }
html += `</select> html += `</select>
</div> </div>
<div class="settings-option"> <div class="at-settings-option">
<label for="language_options">Language</label> <label for="language_options">Language</label>
<select id="language_options">`; <select id="language_options">`;
for (let language in this.languageLabels) { for (let language in this.languageLabels) {
@@ -181,8 +111,8 @@ class AllTalkTtsProvider {
</div>`; </div>`;
html += `<div class="model-endpoint-row"> html += `<div class="at-model-endpoint-row">
<div class="model-option"> <div class="at-model-option">
<label for="switch_model">Switch Model</label> <label for="switch_model">Switch Model</label>
<select id="switch_model"> <select id="switch_model">
<option value="api_tts">API TTS</option> <option value="api_tts">API TTS</option>
@@ -191,49 +121,47 @@ class AllTalkTtsProvider {
</select> </select>
</div> </div>
<div class="endpoint-option"> <div class="at-endpoint-option">
<label for="at_server">AllTalk Endpoint:</label> <label for="at_server">AllTalk Endpoint:</label>
<input id="at_server" type="text" class="text_pole" maxlength="80" value="${this.Settings.provider_endpoint}"/> <input id="at_server" type="text" class="text_pole" maxlength="80" value="${this.settings.provider_endpoint}"/>
</div> </div>
</div>`; </div>`;
html += `<div class="model-endpoint-row"> html += `<div class="at-model-endpoint-row">
<div class="settings-option"> <div class="at-settings-option">
<label for="low_vram">Low VRAM</label> <label for="low_vram">Low VRAM</label>
<input id="low_vram" type="checkbox"/> <input id="low_vram" type="checkbox"/>
</div> </div>
<div class="settings-option"> <div class="at-settings-option">
<label for="deepspeed">DeepSpeed</label> <label for="deepspeed">DeepSpeed</label>
<input id="deepspeed" type="checkbox"/> <input id="deepspeed" type="checkbox"/>
</div> </div>
<div class="settings-option status-option"> <div class="at-settings-option status-option">
<span>Status: <span id="status_info">Ready</span></span> <span>Status: <span id="status_info">Ready</span></span>
</div> </div>
<div class="settings-option empty-option"> <div class="at-settings-option empty-option">
<!-- This div remains empty for spacing --> <!-- This div remains empty for spacing -->
</div> </div>
</div>`; </div>`;
html += `<div class="website-row"> html += `<div class="at-website-row">
<div class="website-option"> <div class="at-website-option">
<span>AllTalk <a target="_blank" href="${this.Settings.provider_endpoint}">Config & Docs</a>.</span> <span>AllTalk <a target="_blank" href="${this.settings.provider_endpoint}">Config & Docs</a>.</span>
</div> </div>
<div class="website-option"> <div class="at-website-option">
<span>AllTalk <a target="_blank" href="https://github.com/erew123/alltalk_tts/">Website</a>.</span> <span>AllTalk <a target="_blank" href="https://github.com/erew123/alltalk_tts/">Website</a>.</span>
</div> </div>
</div>`; </div>`;
html += `<div class="website-row"> html += `<div class="at-website-row">
<div class="website-option"> <div class="at-website-option">
<span><strong>Text-generation-webui</strong> users - Uncheck <strong>Enable TTS</strong> in Text-generation-webui.</span> <span><strong>Text-generation-webui</strong> users - Uncheck <strong>Enable TTS</strong> in Text-generation-webui.</span>
</div> </div>
</div>`; </div>`;
return html; return html;
} }
@@ -244,14 +172,35 @@ html += `<div class="website-row">
async loadSettings(settings) { async loadSettings(settings) {
updateStatus('Offline'); updateStatus('Offline');
// Use default settings if no settings are provided
this.settings = Object.keys(settings).length === 0 ? this.Settings : settings;
if (Object.keys(settings).length === 0) {
console.info('Using default AllTalk TTS Provider settings');
} else {
// Populate settings with provided values, ignoring server-provided settings
for (const key in settings) {
if (key in this.settings) {
this.settings[key] = settings[key];
} else {
console.debug(`Ignoring non-user-configurable setting: ${key}`);
}
}
}
// Update UI elements to reflect the loaded settings
$('#at_server').val(this.settings.provider_endpoint);
$('#language_options').val(this.settings.language);
//$('#voicemap').val(this.settings.voiceMap);
$('#at_generation_method').val(this.settings.at_generation_method);
$('#at_narrator_enabled').val(this.settings.narrator_enabled);
$('#at_narrator_text_not_inside').val(this.settings.at_narrator_text_not_inside);
$('#narrator_voice').val(this.settings.narrator_voice_gen);
console.debug('AllTalkTTS: Settings loaded');
try { try {
// Check if TTS provider is ready // Check if TTS provider is ready
await this.checkReady(); await this.checkReady();
await this.updateSettingsFromServer(); // Fetch dynamic settings from the TTS server
await this.fetchTtsVoiceObjects(); // Fetch voices only if service is ready await this.fetchTtsVoiceObjects(); // Fetch voices only if service is ready
await this.updateSettingsFromServer();
this.updateNarratorVoicesDropdown(); this.updateNarratorVoicesDropdown();
this.updateLanguageDropdown(); this.updateLanguageDropdown();
this.setupEventListeners(); this.setupEventListeners();
@@ -259,29 +208,23 @@ html += `<div class="website-row">
updateStatus('Ready'); updateStatus('Ready');
} catch (error) { } catch (error) {
console.error("Error loading settings:", error); console.error("Error loading settings:", error);
// Set status to Error if there is an issue in loading settings
updateStatus('Offline'); updateStatus('Offline');
} }
} }
applySettingsToHTML() { applySettingsToHTML() {
// Load AllTalk specific settings first
const loadedSettings = loadAllTalkSettings();
// Apply loaded settings or use defaults // Apply loaded settings or use defaults
this.settings = loadedSettings ? { ...this.Settings, ...loadedSettings } : this.Settings;
const narratorVoiceSelect = document.getElementById('narrator_voice'); const narratorVoiceSelect = document.getElementById('narrator_voice');
const atNarratorSelect = document.getElementById('at_narrator_enabled'); const atNarratorSelect = document.getElementById('at_narrator_enabled');
const textNotInsideSelect = document.getElementById('at_narrator_text_not_inside'); const textNotInsideSelect = document.getElementById('at_narrator_text_not_inside');
const generationMethodSelect = document.getElementById('at_generation_method'); const generationMethodSelect = document.getElementById('at_generation_method');
this.settings.narrator_voice = this.settings.narrator_voice_gen;
// Apply settings to Narrator Voice dropdown // Apply settings to Narrator Voice dropdown
if (narratorVoiceSelect && this.settings.narrator_voice) { if (narratorVoiceSelect && this.settings.narrator_voice) {
narratorVoiceSelect.value = this.settings.narrator_voice.replace('.wav', ''); narratorVoiceSelect.value = this.settings.narrator_voice.replace('.wav', '');
this.settings.narrator_voice_gen = this.settings.narrator_voice;
//console.log(this.settings.narrator_voice_gen)
} }
// Apply settings to AT Narrator Enabled dropdown // Apply settings to AT Narrator Enabled dropdown
if (atNarratorSelect) { if (atNarratorSelect) {
//console.log(this.settings.narrator_enabled)
// Sync the state with the checkbox in index.js // Sync the state with the checkbox in index.js
const ttsPassAsterisksCheckbox = document.getElementById('tts_pass_asterisks'); // Access the checkbox from index.js const ttsPassAsterisksCheckbox = document.getElementById('tts_pass_asterisks'); // Access the checkbox from index.js
const ttsNarrateQuotedCheckbox = document.getElementById('tts_narrate_quoted'); // Access the checkbox from index.js const ttsNarrateQuotedCheckbox = document.getElementById('tts_narrate_quoted'); // Access the checkbox from index.js
@@ -314,18 +257,15 @@ html += `<div class="website-row">
const languageSelect = document.getElementById('language_options'); const languageSelect = document.getElementById('language_options');
if (languageSelect && this.settings.language) { if (languageSelect && this.settings.language) {
languageSelect.value = this.settings.language; languageSelect.value = this.settings.language;
//console.log(this.settings.language)
} }
// Apply settings to Text Not Inside dropdown // Apply settings to Text Not Inside dropdown
if (textNotInsideSelect && this.settings.text_not_inside) { if (textNotInsideSelect && this.settings.text_not_inside) {
textNotInsideSelect.value = this.settings.text_not_inside; textNotInsideSelect.value = this.settings.text_not_inside;
this.settings.at_narrator_text_not_inside = this.settings.text_not_inside; this.settings.at_narrator_text_not_inside = this.settings.text_not_inside;
//console.log(this.settings.at_narrator_text_not_inside)
} }
// Apply settings to Generation Method dropdown // Apply settings to Generation Method dropdown
if (generationMethodSelect && this.settings.at_generation_method) { if (generationMethodSelect && this.settings.at_generation_method) {
generationMethodSelect.value = this.settings.at_generation_method; generationMethodSelect.value = this.settings.at_generation_method;
//console.log(this.settings.at_generation_method)
} }
// Additional logic to disable/enable dropdowns based on the selected generation method // Additional logic to disable/enable dropdowns based on the selected generation method
const isStreamingEnabled = this.settings.at_generation_method === 'streaming_enabled'; const isStreamingEnabled = this.settings.at_generation_method === 'streaming_enabled';
@@ -347,7 +287,6 @@ html += `<div class="website-row">
ftOption.textContent = 'XTTSv2 FT'; ftOption.textContent = 'XTTSv2 FT';
modelSelect.appendChild(ftOption); modelSelect.appendChild(ftOption);
} }
//console.log("Settings applied to HTML.");
} }
//##############################// //##############################//
@@ -486,7 +425,7 @@ html += `<div class="website-row">
const languageSelect = document.getElementById('language_options'); const languageSelect = document.getElementById('language_options');
if (languageSelect) { if (languageSelect) {
// Ensure default language is set // Ensure default language is set
this.settings.language = this.Settings.language; this.settings.language = this.settings.language;
languageSelect.innerHTML = ''; languageSelect.innerHTML = '';
for (let language in this.languageLabels) { for (let language in this.languageLabels) {
@@ -610,7 +549,7 @@ html += `<div class="website-row">
if (narratorVoiceSelect) { if (narratorVoiceSelect) {
narratorVoiceSelect.addEventListener('change', (event) => { narratorVoiceSelect.addEventListener('change', (event) => {
this.settings.narrator_voice_gen = `${event.target.value}.wav`; this.settings.narrator_voice_gen = `${event.target.value}.wav`;
this.onSettingsChangeAllTalk(); // Save the settings after change this.onSettingsChange(); // Save the settings after change
}); });
} }
@@ -618,7 +557,7 @@ html += `<div class="website-row">
if (textNotInsideSelect) { if (textNotInsideSelect) {
textNotInsideSelect.addEventListener('change', (event) => { textNotInsideSelect.addEventListener('change', (event) => {
this.settings.text_not_inside = event.target.value; this.settings.text_not_inside = event.target.value;
this.onSettingsChangeAllTalk(); // Save the settings after change this.onSettingsChange(); // Save the settings after change
}); });
} }
@@ -656,12 +595,11 @@ html += `<div class="website-row">
$('#tts_narrate_dialogues').click(); $('#tts_narrate_dialogues').click();
$('#tts_narrate_dialogues').trigger('change'); $('#tts_narrate_dialogues').trigger('change');
} }
this.onSettingsChangeAllTalk(); // Save the settings after change this.onSettingsChange(); // Save the settings after change
}); });
} }
// Event Listener for AT Generation Method Dropdown // Event Listener for AT Generation Method Dropdown
const atGenerationMethodSelect = document.getElementById('at_generation_method'); const atGenerationMethodSelect = document.getElementById('at_generation_method');
const atNarratorEnabledSelect = document.getElementById('at_narrator_enabled'); const atNarratorEnabledSelect = document.getElementById('at_narrator_enabled');
@@ -680,7 +618,7 @@ html += `<div class="website-row">
atNarratorEnabledSelect.disabled = false; atNarratorEnabledSelect.disabled = false;
} }
this.settings.at_generation_method = selectedMethod; // Update the setting here this.settings.at_generation_method = selectedMethod; // Update the setting here
this.onSettingsChangeAllTalk(); // Save the settings after change this.onSettingsChange(); // Save the settings after change
}); });
} }
@@ -689,9 +627,19 @@ html += `<div class="website-row">
if (languageSelect) { if (languageSelect) {
languageSelect.addEventListener('change', (event) => { languageSelect.addEventListener('change', (event) => {
this.settings.language = event.target.value; this.settings.language = event.target.value;
this.onSettingsChangeAllTalk(); // Save the settings after change this.onSettingsChange(); // Save the settings after change
}); });
} }
// Listener for AllTalk Endpoint Input
const atServerInput = document.getElementById('at_server');
if (atServerInput) {
atServerInput.addEventListener('input', (event) => {
this.settings.provider_endpoint = event.target.value;
this.onSettingsChange(); // Save the settings after change
});
}
} }
//#############################// //#############################//
@@ -699,30 +647,16 @@ html += `<div class="website-row">
//#############################// //#############################//
onSettingsChange() { onSettingsChange() {
// Update settings that SillyTavern will save // Update settings based on the UI elements
this.settings.provider_endpoint = $('#at_server').val(); //this.settings.provider_endpoint = $('#at_server').val();
this.settings.language = $('#language_options').val(); this.settings.language = $('#language_options').val();
saveTtsProviderSettings(); // This function should save settings handled by SillyTavern //this.settings.voiceMap = $('#voicemap').val();
// Call the function to handle AllTalk specific settings this.settings.at_generation_method = $('#at_generation_method').val();
this.onSettingsChangeAllTalk(); // Save the settings after change this.settings.narrator_enabled = $('#at_narrator_enabled').val();
} this.settings.at_narrator_text_not_inside = $('#at_narrator_text_not_inside').val();
this.settings.narrator_voice_gen = $('#narrator_voice').val();
onSettingsChangeAllTalk() { // Save the updated settings
// Update AllTalk specific settings and save to localStorage saveTtsProviderSettings();
this.settings.narrator_enabled = $('#at_narrator_enabled').val() === 'true';
this.settings.narrator_voice = $('#narrator_voice').val() + '.wav'; // assuming you need to append .wav
this.settings.text_not_inside = $('#at_narrator_text_not_inside').val(); // "character" or "narrator"
this.settings.at_generation_method = $('#at_generation_method').val(); // Streaming or standard
this.settings.language = $('#language_options').val(); // Streaming or standard
// Call the save function with the current settings
saveAllTalkSettings({
narrator_voice: this.settings.narrator_voice,
narrator_enabled: this.settings.narrator_enabled,
text_not_inside: this.settings.text_not_inside,
at_generation_method: this.settings.at_generation_method,
language: this.settings.language
});
} }
//#########################// //#########################//
@@ -802,38 +736,20 @@ html += `<div class="website-row">
async generateTts(inputText, voiceId) { async generateTts(inputText, voiceId) {
try { try {
if (this.settings.at_generation_method === 'streaming_enabled') { if (this.settings.at_generation_method === 'streaming_enabled') {
// For streaming method // Construct the streaming URL
const streamingUrl = `${this.settings.provider_endpoint}/api/tts-generate-streaming?text=${encodeURIComponent(inputText)}&voice=${encodeURIComponent(voiceId)}.wav&language=${encodeURIComponent(this.settings.language)}&output_file=stream_output.wav`; const streamingUrl = `${this.settings.provider_endpoint}/api/tts-generate-streaming?text=${encodeURIComponent(inputText)}&voice=${encodeURIComponent(voiceId)}.wav&language=${encodeURIComponent(this.settings.language)}&output_file=stream_output.wav`;
const audioElement = new Audio(streamingUrl);
audioElement.play(); // Play the audio stream directly
console.log("Streaming URL:", streamingUrl); console.log("Streaming URL:", streamingUrl);
return new Response(null, {
status: 200, // Return the streaming URL directly
statusText: "OK", return streamingUrl;
headers: {
"Content-Type": "audio/wav",
"Content-Location": streamingUrl
}
});
} else { } else {
// For standard method // For standard method
const outputUrl = await this.fetchTtsGeneration(inputText, voiceId); const outputUrl = await this.fetchTtsGeneration(inputText, voiceId);
// Fetch the audio data as a blob from the URL
const audioResponse = await fetch(outputUrl); const audioResponse = await fetch(outputUrl);
if (!audioResponse.ok) { if (!audioResponse.ok) {
throw new Error(`HTTP ${audioResponse.status}: Failed to fetch audio data`); throw new Error(`HTTP ${audioResponse.status}: Failed to fetch audio data`);
} }
const audioBlob = await audioResponse.blob(); return audioResponse; // Return the fetch response directly
if (!audioBlob.type.startsWith('audio/')) {
throw new Error(`Invalid audio data format. Expecting audio/*, got ${audioBlob.type}`);
}
return new Response(audioBlob, {
status: 200,
statusText: "OK",
headers: {
"Content-Type": audioBlob.type
}
});
} }
} catch (error) { } catch (error) {
console.error("Error in generateTts:", error); console.error("Error in generateTts:", error);
@@ -841,6 +757,7 @@ html += `<div class="website-row">
} }
} }
//####################// //####################//
// Generate Standard // // Generate Standard //
//####################// //####################//
@@ -852,8 +769,8 @@ html += `<div class="website-row">
'text_filtering': "standard", 'text_filtering': "standard",
'character_voice_gen': voiceId + ".wav", 'character_voice_gen': voiceId + ".wav",
'narrator_enabled': this.settings.narrator_enabled, 'narrator_enabled': this.settings.narrator_enabled,
'narrator_voice_gen': this.settings.narrator_voice_gen, 'narrator_voice_gen': this.settings.narrator_voice_gen + ".wav",
'text_not_inside': this.settings.text_not_inside, 'text_not_inside': this.settings.at_narrator_text_not_inside,
'language': this.settings.language, 'language': this.settings.language,
'output_file_name': "st_output", 'output_file_name': "st_output",
'output_file_timestamp': "true", 'output_file_timestamp': "true",
@@ -877,7 +794,6 @@ html += `<div class="website-row">
if (!response.ok) { if (!response.ok) {
const errorText = await response.text(); const errorText = await response.text();
console.error(`[fetchTtsGeneration] Error Response Text:`, errorText); console.error(`[fetchTtsGeneration] Error Response Text:`, errorText);
// Uncomment the following line if you have a UI element for displaying errors
// toastr.error(response.statusText, 'TTS Generation Failed'); // toastr.error(response.statusText, 'TTS Generation Failed');
throw new Error(`HTTP ${response.status}: ${errorText}`); throw new Error(`HTTP ${response.status}: ${errorText}`);
} }
@@ -912,27 +828,6 @@ function updateStatus(message) {
case 'Error': case 'Error':
statusElement.style.color = 'red'; statusElement.style.color = 'red';
break; break;
// Add more cases as needed
} }
} }
} }
//########################//
// Save/load AT Settings //
//########################//
function saveAllTalkSettings(settingsToSave) {
// Save the specific settings to localStorage
console.log("settings", settingsToSave)
localStorage.setItem('AllTalkSettings', JSON.stringify(settingsToSave));
}
function loadAllTalkSettings() {
// Retrieve the settings from localStorage
const settings = localStorage.getItem('AllTalkSettings');
// If settings exist, parse them back into an object
if (settings) {
return JSON.parse(settings);
}
return null;
}
+35 -17
View File
@@ -1,6 +1,6 @@
import { callPopup, cancelTtsPlay, eventSource, event_types, name2, saveSettingsDebounced } from '../../../script.js'; import { callPopup, cancelTtsPlay, eventSource, event_types, name2, saveSettingsDebounced } from '../../../script.js';
import { ModuleWorkerWrapper, doExtrasFetch, extension_settings, getApiUrl, getContext, modules } from '../../extensions.js'; import { ModuleWorkerWrapper, doExtrasFetch, extension_settings, getApiUrl, getContext, modules } from '../../extensions.js';
import { delay, escapeRegex, getStringHash } from '../../utils.js'; import { delay, escapeRegex, getBase64Async, getStringHash, onlyUnique } from '../../utils.js';
import { EdgeTtsProvider } from './edge.js'; import { EdgeTtsProvider } from './edge.js';
import { ElevenLabsTtsProvider } from './elevenlabs.js'; import { ElevenLabsTtsProvider } from './elevenlabs.js';
import { SileroTtsProvider } from './silerotts.js'; import { SileroTtsProvider } from './silerotts.js';
@@ -318,12 +318,14 @@ async function playAudioData(audioBlob) {
if (currentAudioJob == null) { if (currentAudioJob == null) {
console.log('Cancelled TTS playback because currentAudioJob was null'); console.log('Cancelled TTS playback because currentAudioJob was null');
} }
const reader = new FileReader(); if (audioBlob instanceof Blob) {
reader.onload = function (e) { const srcUrl = await getBase64Async(audioBlob);
const srcUrl = e.target.result;
audioElement.src = srcUrl; audioElement.src = srcUrl;
}; } else if (typeof audioBlob === 'string') {
reader.readAsDataURL(audioBlob); audioElement.src = audioBlob;
} else {
throw `TTS received invalid audio data type ${typeof audioBlob}`;
}
audioElement.addEventListener('ended', completeCurrentAudioJob); audioElement.addEventListener('ended', completeCurrentAudioJob);
audioElement.addEventListener('canplay', () => { audioElement.addEventListener('canplay', () => {
console.debug('Starting TTS playback'); console.debug('Starting TTS playback');
@@ -419,11 +421,15 @@ function completeCurrentAudioJob() {
* @param {Response} response * @param {Response} response
*/ */
async function addAudioJob(response) { async function addAudioJob(response) {
if (typeof response === 'string') {
audioJobQueue.push(response);
} else {
const audioData = await response.blob(); const audioData = await response.blob();
if (!audioData.type.startsWith('audio/')) { if (!audioData.type.startsWith('audio/')) {
throw `TTS received HTTP response with invalid data format. Expecting audio/*, got ${audioData.type}`; throw `TTS received HTTP response with invalid data format. Expecting audio/*, got ${audioData.type}`;
} }
audioJobQueue.push(audioData); audioJobQueue.push(audioData);
}
console.debug('Pushed audio job to queue.'); console.debug('Pushed audio job to queue.');
} }
@@ -434,7 +440,7 @@ async function processAudioJobQueue() {
} }
try { try {
audioQueueProcessorReady = false; audioQueueProcessorReady = false;
currentAudioJob = audioJobQueue.pop(); currentAudioJob = audioJobQueue.shift();
playAudioData(currentAudioJob); playAudioData(currentAudioJob);
talkingAnimation(true); talkingAnimation(true);
} catch (error) { } catch (error) {
@@ -465,13 +471,25 @@ function saveLastValues() {
} }
async function tts(text, voiceId, char) { async function tts(text, voiceId, char) {
let response = await ttsProvider.generateTts(text, voiceId); async function processResponse(response) {
// RVC injection // RVC injection
if (extension_settings.rvc.enabled && typeof window['rvcVoiceConversion'] === 'function') if (extension_settings.rvc.enabled && typeof window['rvcVoiceConversion'] === 'function')
response = await window['rvcVoiceConversion'](response, char, text); response = await window['rvcVoiceConversion'](response, char, text);
addAudioJob(response); await addAudioJob(response);
}
let response = await ttsProvider.generateTts(text, voiceId);
// If async generator, process every chunk as it comes in
if (typeof response[Symbol.asyncIterator] === 'function') {
for await (const chunk of response) {
await processResponse(chunk);
}
} else {
await processResponse(response);
}
completeTtsJob(); completeTtsJob();
} }
@@ -658,6 +676,7 @@ function onSkipCodeblocksClick() {
function onPassAsterisksClick() { function onPassAsterisksClick() {
extension_settings.tts.pass_asterisks = !!$('#tts_pass_asterisks').prop('checked'); extension_settings.tts.pass_asterisks = !!$('#tts_pass_asterisks').prop('checked');
saveSettingsDebounced(); saveSettingsDebounced();
console.log("setting pass asterisks", extension_settings.tts.pass_asterisks)
} }
//##############// //##############//
@@ -743,7 +762,7 @@ function getCharacters(unrestricted) {
if (unrestricted) { if (unrestricted) {
const names = context.characters.map(char => char.name); const names = context.characters.map(char => char.name);
names.unshift(DEFAULT_VOICE_MARKER); names.unshift(DEFAULT_VOICE_MARKER);
return names; return names.filter(onlyUnique);
} }
let characters = []; let characters = [];
@@ -758,14 +777,13 @@ function getCharacters(unrestricted) {
characters.push(context.name1); characters.push(context.name1);
const group = context.groups.find(group => context.groupId == group.id); const group = context.groups.find(group => context.groupId == group.id);
for (let member of group.members) { for (let member of group.members) {
// Remove suffix const character = context.characters.find(char => char.avatar == member);
if (member.endsWith('.png')) { if (character) {
member = member.slice(0, -4); characters.push(character.name);
}
characters.push(member);
} }
} }
return characters; }
return characters.filter(onlyUnique);
} }
function sanitizeId(input) { function sanitizeId(input) {
+159
View File
@@ -0,0 +1,159 @@
#tts_media_control {
/* order: 100; */
/* width: 40px;
height: 40px;
margin: 0;
padding: 1px; */
outline: none;
border: none;
cursor: pointer;
/* transition: 0.3s;
opacity: 0.7; */
display: flex;
align-items: center;
/* justify-content: center; */
}
#ttsExtensionMenuItem {
transition: 0.3s;
opacity: 0.7;
}
#ttsExtensionMenuItem:hover {
opacity: 1;
filter: brightness(1.2);
}
#tts_media_control:hover {
opacity: 1;
filter: brightness(1.2);
}
.voice_preview {
margin: 0.25rem 0.5rem;
display: flex;
justify-content: space-between;
align-items: center;
gap: 0.5rem;
}
.voice_preview .voice_name {
text-align: left;
flex: 1;
}
.voice_preview .voice_lang {
width: 4rem;
text-align: left;
}
.voice_preview .fa-play {
cursor: pointer;
}
.tts-button {
margin: 0;
outline: none;
border: none;
cursor: pointer;
transition: 0.3s;
opacity: 0.7;
align-items: center;
justify-content: center;
}
.tts-button:hover {
opacity: 1;
}
.tts_block {
display: flex;
align-items: baseline;
column-gap: 5px;
flex-wrap: wrap;
}
.tts_custom_voices {
display: flex;
align-items: baseline;
gap: 5px;
}
.novel_tts_hints {
font-size: calc(0.9 * var(--mainFontSize));
display: flex;
flex-direction: column;
gap: 5px;
margin-bottom: 5px;
}
.at-settings-row {
display: flex;
justify-content: space-between;
align-items: center;
width: 100%;
}
.at-settings-option {
flex: 1;
margin: 0 10px;
}
.at-endpoint-option {
flex: 1;
margin: 0 10px;
margin-right: 25px;
width: 38%;
}
.at-website-row {
display: flex;
justify-content: start;
align-items: center;
margin-top: 10px;
margin-bottom: 10px;
}
.at-website-option {
flex: 1;
margin-right: 10px;
margin-left: 10px;
}
.at-settings-separator {
margin-top: 10px;
margin-bottom: 10px;
padding: 18x;
font-weight: bold;
border-top: 1px solid #e1e1e1; /* Grey line */
border-bottom: 1px solid #e1e1e1; /* Grey line */
text-align: center;
}
.at-status-message {
flex: 1;
margin: 0 10px;
}
.at-model-endpoint-row {
display: flex;
justify-content: space-between;
align-items: center;
width: 100%;
}
.at-model-option, .endpoint-option {
flex: 1;
margin: 0 10px;
margin-left: 10px;
}
.at-endpoint-option {
width: 38%;
}
#at-status_info {
color: lightgreen;
}
@@ -6,7 +6,6 @@
<meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="icon" href="./favicon.ico" type="image/x-icon"> <link rel="icon" href="./favicon.ico" type="image/x-icon">
<title>AllTalk TTS Generator</title> <title>AllTalk TTS Generator</title>
<style> <style>
body, body,
textarea, textarea,
@@ -679,9 +678,9 @@
progressText.textContent = ''; // Reset progress text progressText.textContent = ''; // Reset progress text
} }
} }
} }
window.exportMP3 = exportMP3; window.exportMP3 = exportMP3;
@@ -710,7 +709,10 @@ window.exportMP3 = exportMP3;
let listPlayIndex = 0; let listPlayIndex = 0;
let currentPage = 1; let currentPage = 1;
let itemsPerPage = 100; // Adjust as needed let itemsPerPage = 100; // Adjust as needed
var protocol = window.location.protocol;
var host = window.location.hostname;
var port = window.location.port;
var baseUrl = protocol + "//" + host + (port ? ':' + port : '');
//////////////////////////////////// ////////////////////////////////////
//////// Progress Indicator //////// //////// Progress Indicator ////////
@@ -851,7 +853,7 @@ window.exportMP3 = exportMP3;
autoplay_volume: volume autoplay_volume: volume
}); });
const response = await fetch("http://127.0.0.1:7851/api/tts-generate", { const response = await fetch(baseUrl + "/api/tts-generate", {
method: "POST", method: "POST",
headers: { headers: {
"Content-Type": "application/x-www-form-urlencoded", "Content-Type": "application/x-www-form-urlencoded",
@@ -921,7 +923,7 @@ window.exportMP3 = exportMP3;
autoplay: "false", autoplay: "false",
autoplay_volume: "0.1" autoplay_volume: "0.1"
}); });
const response = await fetch("http://127.0.0.1:7851/api/tts-generate", { const response = await fetch(baseUrl + "/api/tts-generate", {
method: "POST", method: "POST",
headers: { headers: {
"Content-Type": "application/x-www-form-urlencoded", "Content-Type": "application/x-www-form-urlencoded",
@@ -1051,7 +1053,7 @@ window.exportMP3 = exportMP3;
autoplay_volume: volume, autoplay_volume: volume,
}); });
const response = await fetch("http://127.0.0.1:7851/api/tts-generate", { const response = await fetch(baseUrl + "/api/tts-generate", {
method: "POST", method: "POST",
headers: { headers: {
"Content-Type": "application/x-www-form-urlencoded", "Content-Type": "application/x-www-form-urlencoded",
@@ -1081,8 +1083,9 @@ window.exportMP3 = exportMP3;
}); });
// Reinitialize currentSound every time // Reinitialize currentSound every time
var srcUrl = baseUrl + "/tts-demo-request?" + queryParams.toString();
currentSound = new Howl({ currentSound = new Howl({
src: [`http://127.0.0.1:7851/tts-demo-request?${queryParams.toString()}`], src: [srcUrl],
volume: volume, volume: volume,
html5: true // Force HTML5 Audio to enable streaming html5: true // Force HTML5 Audio to enable streaming
}); });
@@ -1141,7 +1144,7 @@ window.exportMP3 = exportMP3;
const formData = new FormData(); const formData = new FormData();
formData.append("voice", characterVoice); formData.append("voice", characterVoice);
const response = await fetch("http://127.0.0.1:7851/api/previewvoice/", { const response = await fetch(baseUrl + "/api/previewvoice/", {
method: "POST", method: "POST",
body: formData, body: formData,
}); });
@@ -1556,9 +1559,9 @@ window.exportMP3 = exportMP3;
}; };
input.click(); input.click();
} }
function updatePaginationControls() { function updatePaginationControls() {
const lastPage = Math.ceil(ttsList.length / itemsPerPage); const lastPage = Math.ceil(ttsList.length / itemsPerPage);
const pageIndicator = document.getElementById('pageIndicator'); const pageIndicator = document.getElementById('pageIndicator');
if (pageIndicator) { if (pageIndicator) {
@@ -1577,7 +1580,7 @@ function updatePaginationControls() {
// Similar updates for any additional pagination controls if you have them // Similar updates for any additional pagination controls if you have them
// ... // ...
} }
@@ -1633,7 +1636,7 @@ function updatePaginationControls() {
async function fetchVoices() { async function fetchVoices() {
try { try {
const response = await fetch("http://127.0.0.1:7851/api/voices"); const response = await fetch(baseUrl + "/api/voices");
const result = await response.json(); const result = await response.json();
const characterVoiceSelect = document.getElementById("character_voice_gen"); const characterVoiceSelect = document.getElementById("character_voice_gen");
@@ -1985,9 +1988,11 @@ function updatePaginationControls() {
window.onload = function () { window.onload = function () {
setupPage(); setupPage();
hideProgress(); // Ensure the progress indicator is hidden on page load hideProgress(); // Ensure the progress indicator is hidden on page load
if (window.location.protocol !== 'http:' && window.location.protocol !== 'https:') {
alert('AllTalk TTS Generator is not designed to be run from your disk folder. Please run it from the correct URL e.g. http://127.0.0.1:7851/static/tts_chunk_player/tts_chunk_player.html');
}
}; };
</script> </script>
</body> </body>
+22 -16
View File
@@ -57,6 +57,10 @@ params = load_config(configfile_path)
# check someone hasnt enabled lowvram on a system thats not cuda enabled # check someone hasnt enabled lowvram on a system thats not cuda enabled
params["low_vram"] = "false" if not torch.cuda.is_available() else params["low_vram"] params["low_vram"] = "false" if not torch.cuda.is_available() else params["low_vram"]
# Load values for temperature and repetition_penalty
temperature = params["local_temperature"]
repetition_penalty = params["local_repetition_penalty"]
# Define the path to the JSON file # Define the path to the JSON file
config_file_path = this_dir / "modeldownload.json" config_file_path = this_dir / "modeldownload.json"
@@ -473,16 +477,16 @@ async def deepspeed(request: Request, new_deepspeed_value: bool):
######################## ########################
# 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, output_file, streaming=False): async def generate_audio(text, voice, language, temperature, repetition_penalty, output_file, streaming=False):
# Get the async generator from the internal function # Get the async generator from the internal function
response = generate_audio_internal(text, voice, language, output_file, streaming) response = generate_audio_internal(text, voice, language, temperature, repetition_penalty, output_file, streaming)
# If streaming, then return the generator as-is, otherwise just exhaust it and return # If streaming, then return the generator as-is, otherwise just exhaust it and return
if streaming: if streaming:
return response return response
async for _ in response: async for _ in response:
pass pass
async def generate_audio_internal(text, voice, language, output_file, streaming): async def generate_audio_internal(text, voice, language, temperature, repetition_penalty, output_file, streaming):
global model global model
if params["low_vram"] and device == "cpu": if params["low_vram"] and device == "cpu":
await switch_device() await switch_device()
@@ -504,9 +508,9 @@ async def generate_audio_internal(text, voice, language, output_file, streaming)
"language": language, "language": language,
"gpt_cond_latent": gpt_cond_latent, "gpt_cond_latent": gpt_cond_latent,
"speaker_embedding": speaker_embedding, "speaker_embedding": speaker_embedding,
"temperature": float(params["local_temperature"]), "temperature": float(temperature),
"length_penalty": float(model.config.length_penalty), "length_penalty": float(model.config.length_penalty),
"repetition_penalty": float(params["local_repetition_penalty"]), "repetition_penalty": float(repetition_penalty),
"top_k": int(model.config.top_k), "top_k": int(model.config.top_k),
"top_p": float(model.config.top_p), "top_p": float(model.config.top_p),
"enable_text_splitting": True "enable_text_splitting": True
@@ -560,9 +564,9 @@ async def generate_audio_internal(text, voice, language, output_file, streaming)
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=float(params["local_temperature"]), temperature=temperature,
length_penalty=model.config.length_penalty, length_penalty=model.config.length_penalty,
repetition_penalty=float(params["local_repetition_penalty"]), repetition_penalty=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,
) )
@@ -602,10 +606,12 @@ async def generate(request: Request):
text = data["text"] text = data["text"]
voice = data["voice"] voice = data["voice"]
language = data["language"] language = data["language"]
temperature = data["temperature"]
repetition_penalty = data["repetition_penalty"]
output_file = data["output_file"] output_file = data["output_file"]
streaming = False streaming = False
# Generation logic # Generation logic
response = await generate_audio(text, voice, language, output_file, streaming) response = await generate_audio(text, voice, language, temperature, repetition_penalty, output_file, streaming)
if streaming: if streaming:
return StreamingResponse(response, media_type="audio/wav") return StreamingResponse(response, media_type="audio/wav")
return JSONResponse( return JSONResponse(
@@ -722,7 +728,7 @@ async def update_settings(
async def tts_demo_request_streaming(text: str, voice: str, language: str, output_file: str): async def tts_demo_request_streaming(text: str, voice: str, language: str, output_file: str):
try: try:
output_file_path = this_dir / "outputs" / output_file output_file_path = this_dir / "outputs" / output_file
stream = await generate_audio(text, voice, language, output_file_path, streaming=True) stream = await generate_audio(text, voice, language, temperature, repetition_penalty, output_file_path, streaming=True)
return StreamingResponse(stream, media_type="audio/wav") return StreamingResponse(stream, media_type="audio/wav")
except Exception as e: except Exception as e:
print(f"An error occurred: {e}") print(f"An error occurred: {e}")
@@ -732,7 +738,7 @@ async def tts_demo_request_streaming(text: str, voice: str, language: str, outpu
async def tts_demo_request(request: Request, text: str = Form(...), voice: str = Form(...), language: str = Form(...), output_file: str = Form(...)): async def tts_demo_request(request: Request, text: str = Form(...), voice: str = Form(...), language: str = Form(...), output_file: str = Form(...)):
try: try:
output_file_path = this_dir / "outputs" / output_file output_file_path = this_dir / "outputs" / output_file
await generate_audio(text, voice, language, output_file_path, streaming=False) await generate_audio(text, voice, language, temperature, repetition_penalty, output_file_path, streaming=False)
return JSONResponse(content={"output_file_path": str(output_file)}, status_code=200) return JSONResponse(content={"output_file_path": str(output_file)}, status_code=200)
except Exception as e: except Exception as e:
print(f"An error occurred: {e}") print(f"An error occurred: {e}")
@@ -794,7 +800,7 @@ async def preview_voice(request: Request, voice: str = Form(...)):
# Generate the audio # Generate the audio
output_file_path = this_dir / "outputs" / f"{output_file_name}.wav" output_file_path = this_dir / "outputs" / f"{output_file_name}.wav"
await generate_audio(text, voice, language, output_file_path, streaming=False) await generate_audio(text, voice, language, temperature, repetition_penalty, output_file_path, streaming=False)
# Generate the URL # Generate the URL
output_file_url = f'http://{params["ip_address"]}:{params["port_number"]}/audio/{output_file_name}.wav' output_file_url = f'http://{params["ip_address"]}:{params["port_number"]}/audio/{output_file_name}.wav'
@@ -831,7 +837,7 @@ import hashlib
async def tts_generate_streaming(text: str, voice: str, language: str, output_file: str): async def tts_generate_streaming(text: str, voice: str, language: str, output_file: str):
try: try:
output_file_path = this_dir / "outputs" / output_file output_file_path = this_dir / "outputs" / output_file
stream = await generate_audio(text, voice, language, output_file_path, streaming=True) stream = await generate_audio(text, voice, language, temperature, repetition_penalty, output_file_path, streaming=True)
return StreamingResponse(stream, media_type="audio/wav") return StreamingResponse(stream, media_type="audio/wav")
except Exception as e: except Exception as e:
print(f"An error occurred: {e}") print(f"An error occurred: {e}")
@@ -841,7 +847,7 @@ async def tts_generate_streaming(text: str, voice: str, language: str, output_fi
async def tts_generate_streaming(request: Request, text: str = Form(...), voice: str = Form(...), language: str = Form(...), output_file: str = Form(...)): async def tts_generate_streaming(request: Request, text: str = Form(...), voice: str = Form(...), language: str = Form(...), output_file: str = Form(...)):
try: try:
output_file_path = this_dir / "outputs" / output_file output_file_path = this_dir / "outputs" / output_file
await generate_audio(text, voice, language, output_file_path, streaming=False) await generate_audio(text, voice, language, temperature, repetition_penalty, output_file_path, streaming=False)
return JSONResponse(content={"output_file_path": str(output_file)}, status_code=200) return JSONResponse(content={"output_file_path": str(output_file)}, status_code=200)
except Exception as e: except Exception as e:
print(f"An error occurred: {e}") print(f"An error occurred: {e}")
@@ -877,7 +883,7 @@ class Request(BaseModel):
pass pass
class JSONInput(BaseModel): class JSONInput(BaseModel):
text_input: str = Field(..., max_length=1000, description="text_input needs to be 1000 characters or less.") text_input: str = Field(..., max_length=2000, description="text_input needs to be 2000 characters or less.")
text_filtering: str = Field(..., pattern="^(none|standard|html)$", description="text_filtering needs to be 'none', 'standard' or 'html'.") text_filtering: str = Field(..., pattern="^(none|standard|html)$", description="text_filtering needs to be 'none', 'standard' or 'html'.")
character_voice_gen: str = Field(..., pattern="^.*\.wav$", description="character_voice_gen needs to be the name of a wav file e.g. mysample.wav.") character_voice_gen: str = Field(..., pattern="^.*\.wav$", description="character_voice_gen needs to be the name of a wav file e.g. mysample.wav.")
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.")
@@ -1054,7 +1060,7 @@ async def tts_generate(
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"
output_file_str = output_file.as_posix() output_file_str = output_file.as_posix()
response = await generate_audio(cleaned_part, voice_to_use, language, output_file_str, streaming) response = await generate_audio(cleaned_part, voice_to_use, language,temperature, repetition_penalty, output_file_str, streaming)
audio_path = output_file_str audio_path = output_file_str
audio_files_all_paragraphs.append(audio_path) audio_files_all_paragraphs.append(audio_path)
# Combine audio files across paragraphs # Combine audio files across paragraphs
@@ -1091,7 +1097,7 @@ async def tts_generate(
cleaned_string = re.sub(r'\n+', ' ', cleaned_string) cleaned_string = re.sub(r'\n+', ' ', cleaned_string)
else: else:
cleaned_string = text_input cleaned_string = text_input
response = await generate_audio(cleaned_string, character_voice_gen, language, output_file_path, streaming) response = await generate_audio(cleaned_string, character_voice_gen, language, temperature, repetition_penalty, output_file_path, streaming)
if sounddevice_installed == False or streaming == True: if sounddevice_installed == False or streaming == True:
autoplay = False autoplay = False
if autoplay: if autoplay: