Compare commits
84
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9bb0d1fa58 | ||
|
|
015b9033ec | ||
|
|
7f261bb5e9 | ||
|
|
00b93cfe1b | ||
|
|
c0d01ee931 | ||
|
|
d0ceb2bb65 | ||
|
|
3231e61d56 | ||
|
|
32f3b721c2 | ||
|
|
8c588939d2 | ||
|
|
ff1077f282 | ||
|
|
36d0e92ed9 | ||
|
|
8dfb84d7e2 | ||
|
|
4caecdc5eb | ||
|
|
40f2fbf54e | ||
|
|
ab37dd8849 | ||
|
|
03b02e0e4c | ||
|
|
351f46c7b5 | ||
|
|
71ba584e7c | ||
|
|
b3f53e93cc | ||
|
|
0ed3cb75b5 | ||
|
|
c29de18ad4 | ||
|
|
ec1d0e8c5d | ||
|
|
12bf008b3f | ||
|
|
ecdc10ece5 | ||
|
|
360f70cb0a | ||
|
|
51c91c3d70 | ||
|
|
81a9f4c847 | ||
|
|
cdce7511dc | ||
|
|
6d4b6d336b | ||
|
|
6c4a9b19c7 | ||
|
|
82ecf3eecc | ||
|
|
d750959f52 | ||
|
|
bd74de53da | ||
|
|
fcec9d8f5b | ||
|
|
85e5e6ab86 | ||
|
|
d15931bdda | ||
|
|
abc9cf1fb7 | ||
|
|
2b60f5b0da | ||
|
|
e524063ec2 | ||
|
|
d93906a75f | ||
|
|
ad57deed20 | ||
|
|
d0866e3c6c | ||
|
|
af907a299d | ||
|
|
7928af1bb7 | ||
|
|
4d837a0219 | ||
|
|
e50817a234 | ||
|
|
c3bb58d2af | ||
|
|
c17959f142 | ||
|
|
def2d51c5f | ||
|
|
6166d4f751 | ||
|
|
cd164ae258 | ||
|
|
f089e3e85b | ||
|
|
d008c7ab0a | ||
|
|
a199adfbd6 | ||
|
|
61998b4d39 | ||
|
|
55799b2de8 | ||
|
|
ece55ffe06 | ||
|
|
dbc2582fa6 | ||
|
|
535c9b652a | ||
|
|
6c28d58b4a | ||
|
|
c9a222932c | ||
|
|
7010369966 | ||
|
|
525a337835 | ||
|
|
2bd1ef843e | ||
|
|
a9d95fc563 | ||
|
|
1a37ae4ba5 | ||
|
|
8c6fc4853d | ||
|
|
f34ebbd675 | ||
|
|
64cffcdf80 | ||
|
|
2731a559f4 | ||
|
|
a91a83268f | ||
|
|
5d406fbbfb | ||
|
|
cefd1e045b | ||
|
|
bec4e905d3 | ||
|
|
f5e99a5e72 | ||
|
|
631ea6dd8a | ||
|
|
08a04ba506 | ||
|
|
c78927b80a | ||
|
|
04e08c58b8 | ||
|
|
893747c9a9 | ||
|
|
d52dcf86d5 | ||
|
|
ad0eab1224 | ||
|
|
62d1cc05d3 | ||
|
|
b8292da92f |
No files matched your search
@@ -0,0 +1 @@
|
|||||||
|
ko_fi: erew123
|
||||||
@@ -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
@@ -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"]
|
||||||
@@ -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
|
||||||
| |  |  | |
|
| |  |  | |
|
||||||
|:---:|:---:|:---:|:---:|
|
|:---:|:---:|:---:|:---:|
|
||||||
| |  |  ||
|
| |  |  ||
|
||||||
|
|
||||||
## 🟨 Help with problems
|
### 🟨 Help with problems
|
||||||
|
|
||||||
#### 🔄 **Minor updates/bug fixes list** can be found [here](https://github.com/erew123/alltalk_tts/issues/25)
|
#### 🔄 **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`.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
</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
@@ -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%
|
||||||
|
|||||||
@@ -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
@@ -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"
|
||||||
|
}
|
||||||
@@ -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 ]
|
||||||
@@ -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
|
||||||
@@ -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
@@ -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,
|
||||||
|
|||||||
@@ -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
|
||||||
@@ -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"]
|
||||||
@@ -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
|
||||||
@@ -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
|
||||||
@@ -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,64 +248,80 @@ 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])
|
|
||||||
|
|
||||||
# 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]")
|
|
||||||
print(
|
print(
|
||||||
f"[{params['branding']}Startup] \033[94mSettings & Documentation:\033[00m",
|
f"[{params['branding']}Startup] \033[94mRunning in Docker. Please wait.\033[0m"
|
||||||
f"\033[92mhttp://{params['ip_address']}:{params['port_number']}\033[00m",
|
|
||||||
)
|
)
|
||||||
print(f"[{params['branding']}Startup]")
|
|
||||||
else:
|
else:
|
||||||
print(
|
# Start the subprocess
|
||||||
f"[{params['branding']}Startup] \033[91mWarning\033[0m TTS Subprocess Webserver failing to start process"
|
process = subprocess.Popen(["python", script_path])
|
||||||
)
|
# Check if the subprocess has started successfully
|
||||||
print(
|
if process.poll() is None:
|
||||||
f"[{params['branding']}Startup] \033[91mWarning\033[0m It could be that you have something on port:",
|
print(f"[{params['branding']}Startup] TTS Subprocess starting")
|
||||||
params["port_number"],
|
print(f"[{params['branding']}Startup]")
|
||||||
)
|
|
||||||
print(
|
|
||||||
f"[{params['branding']}Startup] \033[91mWarning\033[0m Or you have not started in a Python environement with all the necesssary bits installed"
|
|
||||||
)
|
|
||||||
print(
|
|
||||||
f"[{params['branding']}Startup] \033[91mWarning\033[0m Check you are starting Text-generation-webui with either the start_xxxxx file or the Python environment with cmd_xxxxx file."
|
|
||||||
)
|
|
||||||
print(
|
|
||||||
f"[{params['branding']}Startup] \033[91mWarning\033[0m xxxxx is the type of OS you are on e.g. windows, linux or mac."
|
|
||||||
)
|
|
||||||
print(
|
|
||||||
f"[{params['branding']}Startup] \033[91mWarning\033[0m Alternatively, you could check no other Python processes are running that shouldnt be e.g. Restart your computer is the simple way."
|
|
||||||
)
|
|
||||||
# Cleanly kill off this script, but allow text-generation-webui to keep running, albeit without this alltalk_tts
|
|
||||||
sys.exit(1)
|
|
||||||
|
|
||||||
timeout = 120 # Adjust the timeout as needed
|
|
||||||
|
|
||||||
# Introduce a delay before starting the check loop
|
|
||||||
time.sleep(26) # Wait 26 secs before checking if the tts_server.py has started up.
|
|
||||||
start_time = time.time()
|
|
||||||
while time.time() - start_time < timeout:
|
|
||||||
try:
|
|
||||||
response = requests.get(f"{base_url}/ready")
|
|
||||||
if response.status_code == 200:
|
|
||||||
break
|
|
||||||
except requests.RequestException as e:
|
|
||||||
# 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[94m {params['branding']}Settings & Documentation:\033[00m",
|
||||||
|
f"\033[92mhttp://{params['ip_address']}:{params['port_number']}\033[00m",
|
||||||
)
|
)
|
||||||
time.sleep(4)
|
print(f"[{params['branding']}Startup]")
|
||||||
else:
|
else:
|
||||||
print(
|
print(
|
||||||
f"[{params['branding']}Startup] Startup timed out. Check the server logs for more information."
|
f"[{params['branding']}Startup] \033[91mWarning\033[0m TTS Subprocess Webserver failing to start process"
|
||||||
)
|
)
|
||||||
# Cleanly kill off this script, but allow text-generation-webui to keep running, albeit without this alltalk_tts
|
print(
|
||||||
sys.exit(1)
|
f"[{params['branding']}Startup] \033[91mWarning\033[0m It could be that you have something on port:",
|
||||||
|
params["port_number"],
|
||||||
|
)
|
||||||
|
print(
|
||||||
|
f"[{params['branding']}Startup] \033[91mWarning\033[0m Or you have not started in a Python environement with all the necesssary bits installed"
|
||||||
|
)
|
||||||
|
print(
|
||||||
|
f"[{params['branding']}Startup] \033[91mWarning\033[0m Check you are starting Text-generation-webui with either the start_xxxxx file or the Python environment with cmd_xxxxx file."
|
||||||
|
)
|
||||||
|
print(
|
||||||
|
f"[{params['branding']}Startup] \033[91mWarning\033[0m xxxxx is the type of OS you are on e.g. windows, linux or mac."
|
||||||
|
)
|
||||||
|
print(
|
||||||
|
f"[{params['branding']}Startup] \033[91mWarning\033[0m Alternatively, you could check no other Python processes are running that shouldnt be e.g. Restart your computer is the simple way."
|
||||||
|
)
|
||||||
|
# Cleanly kill off this script, but allow text-generation-webui to keep running, albeit without this alltalk_tts
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
timeout = startup_wait_time # Gather timeout setting from startup_wait_time
|
||||||
|
|
||||||
|
# Introduce a delay before starting the check loop
|
||||||
|
time.sleep(26) # Wait 26 secs before checking if the tts_server.py has started up.
|
||||||
|
start_time = time.time()
|
||||||
|
while time.time() - start_time < timeout:
|
||||||
|
try:
|
||||||
|
response = requests.get(f"{base_url}/ready")
|
||||||
|
if response.status_code == 200:
|
||||||
|
break
|
||||||
|
except requests.RequestException as e:
|
||||||
|
# Print the exception for debugging purposes
|
||||||
|
print(
|
||||||
|
f"[{params['branding']}Startup] \033[91mWarning\033[0m TTS Subprocess has NOT started up yet, Will keep trying for {timeout} seconds maximum. Please wait."
|
||||||
|
)
|
||||||
|
time.sleep(5)
|
||||||
|
else:
|
||||||
|
print(
|
||||||
|
f"\n[{params['branding']}Startup] Startup timed out. Full help available here \033[92mhttps://github.com/erew123/alltalk_tts#-help-with-problems\033[0m"
|
||||||
|
)
|
||||||
|
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
|
||||||
|
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
@@ -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;
|
|
||||||
}
|
|
||||||
+39
-21
@@ -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) {
|
||||||
const audioData = await response.blob();
|
if (typeof response === 'string') {
|
||||||
if (!audioData.type.startsWith('audio/')) {
|
audioJobQueue.push(response);
|
||||||
throw `TTS received HTTP response with invalid data format. Expecting audio/*, got ${audioData.type}`;
|
} else {
|
||||||
|
const audioData = await response.blob();
|
||||||
|
if (!audioData.type.startsWith('audio/')) {
|
||||||
|
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) {
|
||||||
|
async function processResponse(response) {
|
||||||
|
// RVC injection
|
||||||
|
if (extension_settings.rvc.enabled && typeof window['rvcVoiceConversion'] === 'function')
|
||||||
|
response = await window['rvcVoiceConversion'](response, char, text);
|
||||||
|
|
||||||
|
await addAudioJob(response);
|
||||||
|
}
|
||||||
|
|
||||||
let response = await ttsProvider.generateTts(text, voiceId);
|
let response = await ttsProvider.generateTts(text, voiceId);
|
||||||
|
|
||||||
// RVC injection
|
// If async generator, process every chunk as it comes in
|
||||||
if (extension_settings.rvc.enabled && typeof window['rvcVoiceConversion'] === 'function')
|
if (typeof response[Symbol.asyncIterator] === 'function') {
|
||||||
response = await window['rvcVoiceConversion'](response, char, text);
|
for await (const chunk of response) {
|
||||||
|
await processResponse(chunk);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
await processResponse(response);
|
||||||
|
}
|
||||||
|
|
||||||
addAudioJob(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) {
|
||||||
|
|||||||
@@ -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,
|
||||||
@@ -637,51 +636,51 @@
|
|||||||
import Crunker from './crunker.esm.js';
|
import Crunker from './crunker.esm.js';
|
||||||
|
|
||||||
async function exportMP3() {
|
async function exportMP3() {
|
||||||
const crunker = new Crunker();
|
const crunker = new Crunker();
|
||||||
const fileUrls = ttsList.map(item => item.fileUrl);
|
const fileUrls = ttsList.map(item => item.fileUrl);
|
||||||
const splitEvery = parseInt(document.getElementById('splitEvery').value) || 500;
|
const splitEvery = parseInt(document.getElementById('splitEvery').value) || 500;
|
||||||
const progressIndicator = document.getElementById('progressIndicator');
|
const progressIndicator = document.getElementById('progressIndicator');
|
||||||
const progressText = document.getElementById('progressText');
|
const progressText = document.getElementById('progressText');
|
||||||
|
|
||||||
if (progressIndicator && progressText) {
|
|
||||||
progressIndicator.style.display = 'block';
|
|
||||||
progressText.textContent = 'Starting export...';
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
for (let i = 0; i < fileUrls.length; i += splitEvery) {
|
|
||||||
const currentBatch = Math.floor(i / splitEvery) + 1;
|
|
||||||
const totalBatches = Math.ceil(fileUrls.length / splitEvery);
|
|
||||||
if (progressText) {
|
|
||||||
progressText.textContent = `Processing batch ${currentBatch} of ${totalBatches}`;
|
|
||||||
progressText.style.color = 'black'; // Set the text color to black
|
|
||||||
}
|
|
||||||
|
|
||||||
const batchUrls = fileUrls.slice(i, Math.min(i + splitEvery, fileUrls.length));
|
|
||||||
const audioBuffers = await crunker.fetchAudio(...batchUrls);
|
|
||||||
const combined = crunker.concatAudio(audioBuffers);
|
|
||||||
const output = crunker.export(combined, "audio/wav");
|
|
||||||
crunker.download(output.blob, `combined_audio_part_${currentBatch}.wav`);
|
|
||||||
|
|
||||||
// Clear audioBuffers array to free up memory
|
|
||||||
audioBuffers.length = 0;
|
|
||||||
|
|
||||||
console.log(`Exported batch ${currentBatch} of ${totalBatches}`);
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
console.error('Error combining files:', error);
|
|
||||||
if (progressText) {
|
|
||||||
progressText.textContent = 'Error during export!';
|
|
||||||
}
|
|
||||||
} finally {
|
|
||||||
if (progressIndicator && progressText) {
|
if (progressIndicator && progressText) {
|
||||||
progressIndicator.style.display = 'none';
|
progressIndicator.style.display = 'block';
|
||||||
progressText.textContent = ''; // Reset progress text
|
progressText.textContent = 'Starting export...';
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
for (let i = 0; i < fileUrls.length; i += splitEvery) {
|
||||||
|
const currentBatch = Math.floor(i / splitEvery) + 1;
|
||||||
|
const totalBatches = Math.ceil(fileUrls.length / splitEvery);
|
||||||
|
if (progressText) {
|
||||||
|
progressText.textContent = `Processing batch ${currentBatch} of ${totalBatches}`;
|
||||||
|
progressText.style.color = 'black'; // Set the text color to black
|
||||||
|
}
|
||||||
|
|
||||||
|
const batchUrls = fileUrls.slice(i, Math.min(i + splitEvery, fileUrls.length));
|
||||||
|
const audioBuffers = await crunker.fetchAudio(...batchUrls);
|
||||||
|
const combined = crunker.concatAudio(audioBuffers);
|
||||||
|
const output = crunker.export(combined, "audio/wav");
|
||||||
|
crunker.download(output.blob, `combined_audio_part_${currentBatch}.wav`);
|
||||||
|
|
||||||
|
// Clear audioBuffers array to free up memory
|
||||||
|
audioBuffers.length = 0;
|
||||||
|
|
||||||
|
console.log(`Exported batch ${currentBatch} of ${totalBatches}`);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error combining files:', error);
|
||||||
|
if (progressText) {
|
||||||
|
progressText.textContent = 'Error during export!';
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
if (progressIndicator && progressText) {
|
||||||
|
progressIndicator.style.display = 'none';
|
||||||
|
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,
|
||||||
});
|
});
|
||||||
@@ -1531,54 +1534,54 @@ window.exportMP3 = exportMP3;
|
|||||||
}
|
}
|
||||||
|
|
||||||
function importFromJSON() {
|
function importFromJSON() {
|
||||||
const input = document.createElement('input');
|
const input = document.createElement('input');
|
||||||
input.type = 'file';
|
input.type = 'file';
|
||||||
input.accept = '.json';
|
input.accept = '.json';
|
||||||
|
|
||||||
input.onchange = async (e) => {
|
input.onchange = async (e) => {
|
||||||
const file = e.target.files[0];
|
const file = e.target.files[0];
|
||||||
if (file) {
|
if (file) {
|
||||||
const reader = new FileReader();
|
const reader = new FileReader();
|
||||||
reader.readAsText(file);
|
reader.readAsText(file);
|
||||||
reader.onload = (e) => {
|
reader.onload = (e) => {
|
||||||
try {
|
try {
|
||||||
const jsonData = JSON.parse(e.target.result);
|
const jsonData = JSON.parse(e.target.result);
|
||||||
|
|
||||||
ttsList = jsonData;
|
ttsList = jsonData;
|
||||||
currentPage = 1; // Reset to the first page
|
currentPage = 1; // Reset to the first page
|
||||||
updateUIList();
|
updateUIList();
|
||||||
updatePaginationControls(); // Update pagination controls
|
updatePaginationControls(); // Update pagination controls
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Error parsing JSON:', error);
|
console.error('Error parsing JSON:', error);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
input.click();
|
||||||
|
}
|
||||||
|
|
||||||
|
function updatePaginationControls() {
|
||||||
|
const lastPage = Math.ceil(ttsList.length / itemsPerPage);
|
||||||
|
const pageIndicator = document.getElementById('pageIndicator');
|
||||||
|
if (pageIndicator) {
|
||||||
|
pageIndicator.textContent = `Page ${currentPage} of ${lastPage}`;
|
||||||
}
|
}
|
||||||
};
|
|
||||||
|
|
||||||
input.click();
|
const prevPageButton = document.getElementById('prevPage');
|
||||||
}
|
if (prevPageButton) {
|
||||||
|
prevPageButton.disabled = (currentPage === 1);
|
||||||
|
}
|
||||||
|
|
||||||
function updatePaginationControls() {
|
const nextPageButton = document.getElementById('nextPage');
|
||||||
const lastPage = Math.ceil(ttsList.length / itemsPerPage);
|
if (nextPageButton) {
|
||||||
const pageIndicator = document.getElementById('pageIndicator');
|
nextPageButton.disabled = (currentPage === lastPage);
|
||||||
if (pageIndicator) {
|
}
|
||||||
pageIndicator.textContent = `Page ${currentPage} of ${lastPage}`;
|
|
||||||
|
// Similar updates for any additional pagination controls if you have them
|
||||||
|
// ...
|
||||||
}
|
}
|
||||||
|
|
||||||
const prevPageButton = document.getElementById('prevPage');
|
|
||||||
if (prevPageButton) {
|
|
||||||
prevPageButton.disabled = (currentPage === 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
const nextPageButton = document.getElementById('nextPage');
|
|
||||||
if (nextPageButton) {
|
|
||||||
nextPageButton.disabled = (currentPage === lastPage);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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
@@ -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:
|
||||||
|
|||||||
Reference in new issue
Block a user