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.
|
||||
- **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)
|
||||
- 🟪 [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)
|
||||
- 🔴 [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.
|
||||
#### 🔄 **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)
|
||||
For Windows 10/11 and Linux machines there is a quick setup script. Click to expand the correct section below:
|
||||
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.
|
||||
|
||||
**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>
|
||||
<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)
|
||||
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>
|
||||
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`
|
||||
@@ -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)
|
||||
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>
|
||||
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`
|
||||
@@ -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).
|
||||
|
||||
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>
|
||||
</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:
|
||||
<details>
|
||||
<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>
|
||||
|
||||
## 🟩 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:
|
||||
<details>
|
||||
<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
|
||||
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).
|
||||
|
||||
@@ -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)
|
||||
|
||||
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
|
||||
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).
|
||||
|
||||
## 🟪 Updating
|
||||
### 🟪 Updating
|
||||
<details>
|
||||
<summary>UPDATING - I am using Text-Generation-webui</summary><br>
|
||||
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.
|
||||
</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)
|
||||
|
||||
@@ -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>
|
||||
**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.
|
||||
|
||||
@@ -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).
|
||||
</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
|
||||
<details>
|
||||
<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)
|
||||
</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
|
||||
<details>
|
||||
<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)
|
||||
</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.
|
||||
|
||||
<details>
|
||||
@@ -391,7 +432,7 @@ Depending on which of your Nvidia GPU's is the more powerful one, you can change
|
||||
|
||||
</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:
|
||||
|
||||
- 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
|
||||
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
|
||||
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)
|
||||
@@ -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.
|
||||
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
|
||||
|
||||
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.
|
||||
|
||||
## 🔵🟢🟡 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.
|
||||
|
||||
#### 🔵 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>
|
||||
**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>
|
||||
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>
|
||||
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.
|
||||
@@ -645,7 +708,7 @@ def get_shm_size():
|
||||
</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).
|
||||
|
||||
#### ⬜ 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.
|
||||
- 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
|
||||
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"}`
|
||||
|
||||
### 🟠 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>
|
||||
|
||||
@@ -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"}`
|
||||
|
||||
### 🟠 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>
|
||||
|
||||
- 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>
|
||||
|
||||
### 🔴 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).
|
||||
- Tagging support within text.
|
||||
- Have a break!
|
||||
+3
-2
@@ -341,6 +341,7 @@ if "%UserChoice%" == "1" (
|
||||
pip install fsspec>=2023.6.0
|
||||
pip install soundfile==0.12.1
|
||||
pip install uvicorn==0.24.0.post1
|
||||
pip install transformers>=4.37.1
|
||||
pip install TTS==0.21.3
|
||||
pip install fastapi==0.104.1
|
||||
pip install Jinja2==3.1.2
|
||||
@@ -351,6 +352,8 @@ if "%UserChoice%" == "1" (
|
||||
pip install pydantic==1.10.13
|
||||
pip install sounddevice==0.4.6
|
||||
pip install python-multipart==0.0.6
|
||||
pip install cutlet>=0.3.0
|
||||
pip install unidic-lite>=1.0.8
|
||||
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
|
||||
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 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 exit >> start_environment.bat
|
||||
@rem Create start_alltalk.bat to run AllTalk
|
||||
echo @echo off > 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 call "%CONDA_ROOT_PREFIX%\condabin\conda.bat" activate "%INSTALL_ENV_DIR%" >> start_alltalk.bat
|
||||
echo call python script.py >> start_alltalk.bat
|
||||
echo exit >> start_alltalk.bat
|
||||
Echo.
|
||||
Echo start_alltalk.bat has been created.
|
||||
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 soundfile==0.12.1
|
||||
pip install uvicorn==0.24.0.post1
|
||||
pip install transformers>=4.37.1
|
||||
pip install TTS==0.21.3
|
||||
pip install fastapi==0.104.1
|
||||
pip install Jinja2==3.1.2
|
||||
@@ -311,6 +312,8 @@ install_custom_standalone() {
|
||||
pip install pydantic==1.10.13
|
||||
pip install sounddevice==0.4.6
|
||||
pip install python-multipart==0.0.6
|
||||
pip install cutlet>=0.3.0
|
||||
pip install unidic-lite>=1.0.8
|
||||
else
|
||||
pip install -r requirements_other.txt
|
||||
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}"
|
||||
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'))]
|
||||
buffer=0.2
|
||||
eval_percentage=0.15
|
||||
eval_percentage = eval_split_number / 100.0
|
||||
speaker_name="coqui"
|
||||
audio_total_size = 0
|
||||
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[94mCoqui Public Model License\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")
|
||||
# Write the target language to lang.txt in the output directory
|
||||
lang_file_path = os.path.join(out_path, "lang.txt")
|
||||
@@ -968,6 +969,14 @@ if __name__ == "__main__":
|
||||
"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(
|
||||
label="Progress:"
|
||||
)
|
||||
@@ -979,14 +988,14 @@ if __name__ == "__main__":
|
||||
|
||||
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()
|
||||
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:
|
||||
return "I cannot find any mp3, wav or flac files in the folder called 'put-voice-samples-in-here'", "", ""
|
||||
else:
|
||||
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:
|
||||
traceback.print_exc()
|
||||
error = traceback.format_exc()
|
||||
@@ -1274,6 +1283,7 @@ if __name__ == "__main__":
|
||||
lang,
|
||||
whisper_model,
|
||||
out_path,
|
||||
eval_split_number,
|
||||
],
|
||||
outputs=[
|
||||
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
|
||||
soundfile>=0.12.1
|
||||
uvicorn>=0.24.0.post1
|
||||
transformers>=4.37.1
|
||||
TTS>=0.21.3
|
||||
torch>=2.1.0+cu118
|
||||
torchaudio>=2.1.0+cu118
|
||||
@@ -13,3 +14,5 @@ packaging>=23.2
|
||||
pydantic>=1.10.13
|
||||
sounddevice>=0.4.6
|
||||
python-multipart>=0.0.6
|
||||
cutlet>=0.3.0
|
||||
unidic-lite>=1.0.8
|
||||
@@ -1,6 +1,7 @@
|
||||
numpy>=1.22.0
|
||||
soundfile>=0.12.1
|
||||
uvicorn>=0.24.0.post1
|
||||
transformers>=4.37.1
|
||||
TTS>=0.21.3
|
||||
torch>=2.1.0
|
||||
torchaudio>=2.1.0
|
||||
@@ -13,3 +14,5 @@ packaging>=23.2
|
||||
pydantic>=1.10.13
|
||||
sounddevice>=0.4.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 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 ####
|
||||
#################################################################
|
||||
# STARTUP VARIABLE - Create "this_dir" variable as the current script directory
|
||||
this_dir = Path(__file__).parent.resolve()
|
||||
|
||||
|
||||
# load config file in and get settings
|
||||
def load_config(file_path):
|
||||
with open(file_path, "r") as config_file:
|
||||
config = json.load(config_file)
|
||||
return config
|
||||
|
||||
|
||||
config_file_path = this_dir / "confignew.json"
|
||||
# Load the params dictionary from the confignew.json file
|
||||
params = load_config(config_file_path)
|
||||
@@ -48,6 +58,7 @@ except ModuleNotFoundError:
|
||||
# IMPORT - Attempt Importing DeepSpeed (required for displaying Deepspeed checkbox in gradio)
|
||||
try:
|
||||
import deepspeed
|
||||
|
||||
deepspeed_installed = True
|
||||
except ImportError:
|
||||
deepspeed_installed = False
|
||||
@@ -60,6 +71,7 @@ try:
|
||||
from modules.logging_colors import logger
|
||||
from modules.ui import create_refresh_button
|
||||
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
|
||||
running_in_standalone = False
|
||||
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
|
||||
tts_method_xtts_ft = False
|
||||
|
||||
|
||||
# Gather the voice files
|
||||
def get_available_voices():
|
||||
return sorted([voice.name for voice in Path(f"{this_dir}/voices").glob("*.wav")])
|
||||
|
||||
|
||||
#########################
|
||||
#### LICENSE DISPLAY ####
|
||||
#########################
|
||||
@@ -141,6 +155,7 @@ def check_required_files():
|
||||
subprocess.run(["python", str(download_script_path)])
|
||||
print(f"[{params['branding']}Startup] All required files are present.")
|
||||
|
||||
|
||||
# STARTUP Call Check routine
|
||||
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 finetuned_model:
|
||||
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:
|
||||
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
|
||||
non_quoted_text_is = True
|
||||
|
||||
|
||||
######################
|
||||
#### GRADIO STUFF ####
|
||||
######################
|
||||
@@ -230,64 +248,80 @@ def signal_handler(sig, frame):
|
||||
|
||||
# Attach the signal handler to the SIGINT signal (Ctrl+C)
|
||||
signal.signal(signal.SIGINT, signal_handler)
|
||||
|
||||
# Start the subprocess
|
||||
process = subprocess.Popen(["python", script_path])
|
||||
|
||||
# Check if the subprocess has started successfully
|
||||
if process.poll() is None:
|
||||
print(f"[{params['branding']}Startup] TTS Subprocess starting")
|
||||
print(f"[{params['branding']}Startup]")
|
||||
# Check if we're running in docker
|
||||
if os.path.isfile("/.dockerenv"):
|
||||
print(
|
||||
f"[{params['branding']}Startup] \033[94mSettings & Documentation:\033[00m",
|
||||
f"\033[92mhttp://{params['ip_address']}:{params['port_number']}\033[00m",
|
||||
f"[{params['branding']}Startup] \033[94mRunning in Docker. Please wait.\033[0m"
|
||||
)
|
||||
print(f"[{params['branding']}Startup]")
|
||||
else:
|
||||
print(
|
||||
f"[{params['branding']}Startup] \033[91mWarning\033[0m TTS Subprocess Webserver failing to start process"
|
||||
)
|
||||
print(
|
||||
f"[{params['branding']}Startup] \033[91mWarning\033[0m It could be that you have something on port:",
|
||||
params["port_number"],
|
||||
)
|
||||
print(
|
||||
f"[{params['branding']}Startup] \033[91mWarning\033[0m Or you have not started in a Python environement with all the necesssary bits installed"
|
||||
)
|
||||
print(
|
||||
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
|
||||
# Start the subprocess
|
||||
process = subprocess.Popen(["python", script_path])
|
||||
# Check if the subprocess has started successfully
|
||||
if process.poll() is None:
|
||||
print(f"[{params['branding']}Startup] TTS Subprocess starting")
|
||||
print(f"[{params['branding']}Startup]")
|
||||
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)
|
||||
else:
|
||||
print(
|
||||
f"[{params['branding']}Startup] Startup timed out. Check the server logs for more information."
|
||||
)
|
||||
# Cleanly kill off this script, but allow text-generation-webui to keep running, albeit without this alltalk_tts
|
||||
sys.exit(1)
|
||||
print(f"[{params['branding']}Startup]")
|
||||
else:
|
||||
print(
|
||||
f"[{params['branding']}Startup] \033[91mWarning\033[0m TTS Subprocess Webserver failing to start process"
|
||||
)
|
||||
print(
|
||||
f"[{params['branding']}Startup] \033[91mWarning\033[0m It could be that you have something on port:",
|
||||
params["port_number"],
|
||||
)
|
||||
print(
|
||||
f"[{params['branding']}Startup] \033[91mWarning\033[0m Or you have not started in a Python environement with all the necesssary bits installed"
|
||||
)
|
||||
print(
|
||||
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)}
|
||||
|
||||
|
||||
##################
|
||||
#### 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
|
||||
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:
|
||||
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)
|
||||
# Clean up unnecessary 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"
|
||||
# Generate the audio
|
||||
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
|
||||
output_file_str = output_file.as_posix()
|
||||
# Lock before making the generate request
|
||||
with process_lock:
|
||||
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
|
||||
if process_lock.locked():
|
||||
@@ -516,7 +562,7 @@ def process_text(text):
|
||||
text = html.unescape(text)
|
||||
|
||||
# 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
|
||||
combined_pattern = r'(\*[^*"]+\*|"[^"*]+")'
|
||||
@@ -531,22 +577,22 @@ def process_text(text):
|
||||
for match in re.finditer(combined_pattern, text):
|
||||
# Add the text before the match, if any, as ambiguous
|
||||
if start < match.start():
|
||||
ambiguous_text = text[start:match.start()].strip()
|
||||
ambiguous_text = text[start : match.start()].strip()
|
||||
if ambiguous_text:
|
||||
ordered_parts.append(('ambiguous', ambiguous_text))
|
||||
ordered_parts.append(("ambiguous", ambiguous_text))
|
||||
|
||||
# Add the matched part as either narrator or character
|
||||
matched_text = match.group(0)
|
||||
if matched_text.startswith('*') and matched_text.endswith('*'):
|
||||
ordered_parts.append(('narrator', matched_text.strip('*').strip()))
|
||||
if matched_text.startswith("*") and matched_text.endswith("*"):
|
||||
ordered_parts.append(("narrator", matched_text.strip("*").strip()))
|
||||
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:
|
||||
# In case of mixed or improperly formatted parts
|
||||
if '*' in matched_text:
|
||||
ordered_parts.append(('narrator', matched_text.strip('*').strip('"')))
|
||||
if "*" in matched_text:
|
||||
ordered_parts.append(("narrator", matched_text.strip("*").strip('"')))
|
||||
else:
|
||||
ordered_parts.append(('character', matched_text.strip('"').strip('*')))
|
||||
ordered_parts.append(("character", matched_text.strip('"').strip("*")))
|
||||
|
||||
# Update the start of the next segment
|
||||
start = match.end()
|
||||
@@ -555,10 +601,11 @@ def process_text(text):
|
||||
if start < len(text):
|
||||
ambiguous_text = text[start:].strip()
|
||||
if ambiguous_text:
|
||||
ordered_parts.append(('ambiguous', ambiguous_text))
|
||||
ordered_parts.append(("ambiguous", ambiguous_text))
|
||||
|
||||
return ordered_parts
|
||||
|
||||
|
||||
#################################
|
||||
#### TTS STANDARD GENERATION ####
|
||||
#################################
|
||||
@@ -567,12 +614,14 @@ def output_modifier(string, state):
|
||||
if not params["activate"]:
|
||||
return string
|
||||
original_string = string
|
||||
#print("ORIGINAL STRING IS:", original_string)
|
||||
# print("ORIGINAL STRING IS:", original_string)
|
||||
cleaned_string = before_audio_generation(string, params)
|
||||
if cleaned_string is None:
|
||||
return
|
||||
string = cleaned_string
|
||||
language_code = languages.get(params["language"])
|
||||
temperature = params["local_temperature"]
|
||||
repetition_penalty = params["local_repetition_penalty"]
|
||||
# Create a list to store generated audio paths
|
||||
audio_files = []
|
||||
if process_lock.acquire(blocking=False):
|
||||
@@ -587,29 +636,46 @@ def output_modifier(string, state):
|
||||
continue
|
||||
|
||||
# Determine the voice to use based on the part type
|
||||
if part_type == 'narrator':
|
||||
if part_type == "narrator":
|
||||
voice_to_use = params["narrator_voice"]
|
||||
print(f"[{params['branding']}TTSGen] \033[92mNarrator\033[0m") # Green
|
||||
elif part_type == 'character':
|
||||
print(
|
||||
f"[{params['branding']}TTSGen] \033[92mNarrator\033[0m"
|
||||
) # Green
|
||||
elif part_type == "character":
|
||||
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:
|
||||
# Handle ambiguous parts based on user preference
|
||||
voice_to_use = 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"
|
||||
voice_to_use = (
|
||||
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}")
|
||||
|
||||
# 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
|
||||
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)
|
||||
cleaned_part = re.sub(r'\n+', ' ', cleaned_part)
|
||||
cleaned_part = re.sub(r"\n+", " ", cleaned_part)
|
||||
|
||||
# Generate TTS and output to a file
|
||||
output_filename = get_output_filename(state)
|
||||
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_files_all_paragraphs.append(audio_path)
|
||||
@@ -622,20 +688,29 @@ def output_modifier(string, state):
|
||||
# Decode HTML entities first
|
||||
cleaned_part = html.unescape(original_string)
|
||||
# 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
|
||||
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)
|
||||
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.
|
||||
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:
|
||||
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 = get_output_filename(state)
|
||||
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")
|
||||
final_output_file = audio_path
|
||||
@@ -676,20 +751,28 @@ def output_modifier(string, state):
|
||||
|
||||
def get_output_filename(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:
|
||||
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 ####
|
||||
###############################################
|
||||
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"
|
||||
payload = {
|
||||
"text": text,
|
||||
"voice": voice,
|
||||
"language": language,
|
||||
"temperature": temperature,
|
||||
"repetition_penalty": repetition_penalty,
|
||||
"output_file": output_file,
|
||||
}
|
||||
headers = {"Content-Type": "application/json"}
|
||||
@@ -714,12 +797,14 @@ def state_modifier(state):
|
||||
state["stream"] = False
|
||||
return state
|
||||
|
||||
|
||||
def update_narrator_enabled(value):
|
||||
if value == "Enabled":
|
||||
params["narrator_enabled"] = True
|
||||
elif value == "Disabled":
|
||||
params["narrator_enabled"] = False
|
||||
|
||||
|
||||
def update_non_quoted_text_is(value):
|
||||
global non_quoted_text_is
|
||||
if value == "Narrator":
|
||||
@@ -727,6 +812,7 @@ def update_non_quoted_text_is(value):
|
||||
elif value == "Char":
|
||||
non_quoted_text_is = True
|
||||
|
||||
|
||||
def input_modifier(string, state):
|
||||
if not params["activate"]:
|
||||
return string
|
||||
@@ -737,20 +823,31 @@ def input_modifier(string, state):
|
||||
|
||||
def ui():
|
||||
with gr.Accordion(params["branding"] + " TTS (XTTSv2)"):
|
||||
# Activate alltalk_tts, Enable autoplay, Show text
|
||||
with gr.Row():
|
||||
activate = gr.Checkbox(value=params['activate'], label='Enable TTS')
|
||||
autoplay = gr.Checkbox(value=params['autoplay'], label='Autoplay TTS')
|
||||
show_text = gr.Checkbox(value=params['show_text'], label='Show Text')
|
||||
activate = gr.Checkbox(value=params["activate"], label="Enable TTS")
|
||||
autoplay = gr.Checkbox(value=params["autoplay"], label="Autoplay TTS")
|
||||
show_text = gr.Checkbox(value=params["show_text"], label="Show Text")
|
||||
|
||||
# Low vram enable, Deepspeed enable, Remove trailing dots
|
||||
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)
|
||||
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)
|
||||
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():
|
||||
model_loader_choices=["API TTS", "API Local", "XTTSv2 Local"]
|
||||
model_loader_choices = ["API TTS", "API Local", "XTTSv2 Local"]
|
||||
if finetuned_model:
|
||||
model_loader_choices.append("XTTSv2 FT")
|
||||
tts_radio_buttons = gr.Radio(
|
||||
@@ -761,32 +858,88 @@ def ui():
|
||||
tts_radio_buttons_play = gr.HTML(visible=False)
|
||||
with gr.Row():
|
||||
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:
|
||||
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
|
||||
voice = gr.Dropdown(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",)
|
||||
voice = gr.Dropdown(
|
||||
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():
|
||||
language = gr.Dropdown(languages.keys(), label="Language", value=params["language"])
|
||||
language = gr.Dropdown(
|
||||
languages.keys(), label="Language", value=params["language"]
|
||||
)
|
||||
with gr.Row():
|
||||
narrator_voice_gr = gr.Dropdown(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")
|
||||
narrator_voice_gr = gr.Dropdown(
|
||||
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)
|
||||
# with gr.Row():
|
||||
# local_temperature_gr = gr.Slider(minimum=0, maximum=1, step=0.05, label="Temperature", value=params["local_temperature"])
|
||||
# local_repetition_penalty_gr = gr.Slider(minimum=0, maximum=20, step=0.5, label="Repetition Penalty", value=params["local_repetition_penalty"])
|
||||
# Temperature, Repetition Penalty
|
||||
with gr.Row():
|
||||
local_temperature_gr = gr.Slider(
|
||||
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():
|
||||
narrator_enabled_gr = gr.Radio(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")
|
||||
narrator_enabled_gr = gr.Radio(
|
||||
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(
|
||||
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():
|
||||
preview_text = gr.Text(
|
||||
show_label=False,
|
||||
@@ -856,23 +1009,34 @@ def ui():
|
||||
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: 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)
|
||||
deepspeed_checkbox.change(send_deepspeed_request, deepspeed_checkbox, deepspeed_checkbox_play, None)
|
||||
tts_radio_buttons.change(
|
||||
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(
|
||||
lambda x: params.update({"remove_trailing_dots": x}), remove_trailing_dots, None
|
||||
)
|
||||
voice.change(lambda x: params.update({"voice": x}), voice, None)
|
||||
language.change(lambda x: params.update({"language": x}), language, None)
|
||||
|
||||
|
||||
# TS Settings (Not yet parsed to api/implemented)
|
||||
# local_temperature_gr.change(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)
|
||||
# TSS Settings
|
||||
local_temperature_gr.change(
|
||||
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,
|
||||
)
|
||||
|
||||
# Narrator selection actions
|
||||
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)
|
||||
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
|
||||
preview_text.submit(voice_preview, preview_text, preview_audio)
|
||||
|
||||
+104
-209
@@ -3,92 +3,33 @@ import { saveTtsProviderSettings } from './index.js';
|
||||
|
||||
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 {
|
||||
//########//
|
||||
// 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;
|
||||
voices = [];
|
||||
separator = '. ';
|
||||
@@ -114,23 +55,12 @@ class AllTalkTtsProvider {
|
||||
'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() {
|
||||
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>
|
||||
<select id="at_generation_method">
|
||||
<option value="standard_generation">Standard Audio Generation (AT Narrator - Optional)</option>
|
||||
@@ -139,9 +69,9 @@ 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="at_narrator_enabled">AT Narrator</label>
|
||||
<select id="at_narrator_enabled">
|
||||
<option value="true">Enabled</option>
|
||||
@@ -149,7 +79,7 @@ class AllTalkTtsProvider {
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="settings-option">
|
||||
<div class="at-settings-option">
|
||||
<label for="at_narrator_text_not_inside">Text Not Inside * or " is</label>
|
||||
<select id="at_narrator_text_not_inside">
|
||||
<option value="narrator">Narrator</option>
|
||||
@@ -159,8 +89,8 @@ class AllTalkTtsProvider {
|
||||
|
||||
</div>`;
|
||||
|
||||
html += `<div class="settings-row">
|
||||
<div class="settings-option">
|
||||
html += `<div class="at-settings-row">
|
||||
<div class="at-settings-option">
|
||||
<label for="narrator_voice">Narrator Voice</label>
|
||||
<select id="narrator_voice">`;
|
||||
if (this.voices) {
|
||||
@@ -170,7 +100,7 @@ class AllTalkTtsProvider {
|
||||
}
|
||||
html += `</select>
|
||||
</div>
|
||||
<div class="settings-option">
|
||||
<div class="at-settings-option">
|
||||
<label for="language_options">Language</label>
|
||||
<select id="language_options">`;
|
||||
for (let language in this.languageLabels) {
|
||||
@@ -181,8 +111,8 @@ class AllTalkTtsProvider {
|
||||
</div>`;
|
||||
|
||||
|
||||
html += `<div class="model-endpoint-row">
|
||||
<div class="model-option">
|
||||
html += `<div class="at-model-endpoint-row">
|
||||
<div class="at-model-option">
|
||||
<label for="switch_model">Switch Model</label>
|
||||
<select id="switch_model">
|
||||
<option value="api_tts">API TTS</option>
|
||||
@@ -191,49 +121,47 @@ class AllTalkTtsProvider {
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="endpoint-option">
|
||||
<div class="at-endpoint-option">
|
||||
<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>`;
|
||||
|
||||
|
||||
html += `<div class="model-endpoint-row">
|
||||
<div class="settings-option">
|
||||
html += `<div class="at-model-endpoint-row">
|
||||
<div class="at-settings-option">
|
||||
<label for="low_vram">Low VRAM</label>
|
||||
<input id="low_vram" type="checkbox"/>
|
||||
</div>
|
||||
<div class="settings-option">
|
||||
<div class="at-settings-option">
|
||||
<label for="deepspeed">DeepSpeed</label>
|
||||
<input id="deepspeed" type="checkbox"/>
|
||||
</div>
|
||||
<div class="settings-option status-option">
|
||||
<div class="at-settings-option status-option">
|
||||
<span>Status: <span id="status_info">Ready</span></span>
|
||||
</div>
|
||||
<div class="settings-option empty-option">
|
||||
<div class="at-settings-option empty-option">
|
||||
<!-- This div remains empty for spacing -->
|
||||
</div>
|
||||
</div>`;
|
||||
|
||||
|
||||
html += `<div class="website-row">
|
||||
<div class="website-option">
|
||||
<span>AllTalk <a target="_blank" href="${this.Settings.provider_endpoint}">Config & Docs</a>.</span>
|
||||
html += `<div class="at-website-row">
|
||||
<div class="at-website-option">
|
||||
<span>AllTalk <a target="_blank" href="${this.settings.provider_endpoint}">Config & Docs</a>.</span>
|
||||
</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>
|
||||
</div>
|
||||
</div>`;
|
||||
|
||||
html += `<div class="website-row">
|
||||
<div class="website-option">
|
||||
html += `<div class="at-website-row">
|
||||
<div class="at-website-option">
|
||||
<span><strong>Text-generation-webui</strong> users - Uncheck <strong>Enable TTS</strong> in Text-generation-webui.</span>
|
||||
</div>
|
||||
</div>`;
|
||||
|
||||
|
||||
|
||||
return html;
|
||||
}
|
||||
|
||||
@@ -244,14 +172,35 @@ html += `<div class="website-row">
|
||||
|
||||
async loadSettings(settings) {
|
||||
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 {
|
||||
// Check if TTS provider is ready
|
||||
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.updateSettingsFromServer();
|
||||
this.updateNarratorVoicesDropdown();
|
||||
this.updateLanguageDropdown();
|
||||
this.setupEventListeners();
|
||||
@@ -259,29 +208,23 @@ html += `<div class="website-row">
|
||||
updateStatus('Ready');
|
||||
} catch (error) {
|
||||
console.error("Error loading settings:", error);
|
||||
// Set status to Error if there is an issue in loading settings
|
||||
updateStatus('Offline');
|
||||
}
|
||||
}
|
||||
|
||||
applySettingsToHTML() {
|
||||
// Load AllTalk specific settings first
|
||||
const loadedSettings = loadAllTalkSettings();
|
||||
// Apply loaded settings or use defaults
|
||||
this.settings = loadedSettings ? { ...this.Settings, ...loadedSettings } : this.Settings;
|
||||
const narratorVoiceSelect = document.getElementById('narrator_voice');
|
||||
const atNarratorSelect = document.getElementById('at_narrator_enabled');
|
||||
const textNotInsideSelect = document.getElementById('at_narrator_text_not_inside');
|
||||
const generationMethodSelect = document.getElementById('at_generation_method');
|
||||
this.settings.narrator_voice = this.settings.narrator_voice_gen;
|
||||
// Apply settings to Narrator Voice dropdown
|
||||
if (narratorVoiceSelect && this.settings.narrator_voice) {
|
||||
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
|
||||
if (atNarratorSelect) {
|
||||
//console.log(this.settings.narrator_enabled)
|
||||
// Sync the state with the checkbox in 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
|
||||
@@ -314,18 +257,15 @@ html += `<div class="website-row">
|
||||
const languageSelect = document.getElementById('language_options');
|
||||
if (languageSelect && this.settings.language) {
|
||||
languageSelect.value = this.settings.language;
|
||||
//console.log(this.settings.language)
|
||||
}
|
||||
// Apply settings to Text Not Inside dropdown
|
||||
if (textNotInsideSelect && this.settings.text_not_inside) {
|
||||
textNotInsideSelect.value = 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
|
||||
if (generationMethodSelect && 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
|
||||
const isStreamingEnabled = this.settings.at_generation_method === 'streaming_enabled';
|
||||
@@ -347,7 +287,6 @@ html += `<div class="website-row">
|
||||
ftOption.textContent = 'XTTSv2 FT';
|
||||
modelSelect.appendChild(ftOption);
|
||||
}
|
||||
//console.log("Settings applied to HTML.");
|
||||
}
|
||||
|
||||
//##############################//
|
||||
@@ -486,7 +425,7 @@ html += `<div class="website-row">
|
||||
const languageSelect = document.getElementById('language_options');
|
||||
if (languageSelect) {
|
||||
// Ensure default language is set
|
||||
this.settings.language = this.Settings.language;
|
||||
this.settings.language = this.settings.language;
|
||||
|
||||
languageSelect.innerHTML = '';
|
||||
for (let language in this.languageLabels) {
|
||||
@@ -610,7 +549,7 @@ html += `<div class="website-row">
|
||||
if (narratorVoiceSelect) {
|
||||
narratorVoiceSelect.addEventListener('change', (event) => {
|
||||
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) {
|
||||
textNotInsideSelect.addEventListener('change', (event) => {
|
||||
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').trigger('change');
|
||||
}
|
||||
this.onSettingsChangeAllTalk(); // Save the settings after change
|
||||
this.onSettingsChange(); // Save the settings after change
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Event Listener for AT Generation Method Dropdown
|
||||
const atGenerationMethodSelect = document.getElementById('at_generation_method');
|
||||
const atNarratorEnabledSelect = document.getElementById('at_narrator_enabled');
|
||||
@@ -680,7 +618,7 @@ html += `<div class="website-row">
|
||||
atNarratorEnabledSelect.disabled = false;
|
||||
}
|
||||
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) {
|
||||
languageSelect.addEventListener('change', (event) => {
|
||||
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() {
|
||||
// Update settings that SillyTavern will save
|
||||
this.settings.provider_endpoint = $('#at_server').val();
|
||||
// Update settings based on the UI elements
|
||||
//this.settings.provider_endpoint = $('#at_server').val();
|
||||
this.settings.language = $('#language_options').val();
|
||||
saveTtsProviderSettings(); // This function should save settings handled by SillyTavern
|
||||
// Call the function to handle AllTalk specific settings
|
||||
this.onSettingsChangeAllTalk(); // Save the settings after change
|
||||
}
|
||||
|
||||
onSettingsChangeAllTalk() {
|
||||
// Update AllTalk specific settings and save to localStorage
|
||||
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
|
||||
});
|
||||
//this.settings.voiceMap = $('#voicemap').val();
|
||||
this.settings.at_generation_method = $('#at_generation_method').val();
|
||||
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();
|
||||
// Save the updated settings
|
||||
saveTtsProviderSettings();
|
||||
}
|
||||
|
||||
//#########################//
|
||||
@@ -802,38 +736,20 @@ html += `<div class="website-row">
|
||||
async generateTts(inputText, voiceId) {
|
||||
try {
|
||||
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 audioElement = new Audio(streamingUrl);
|
||||
audioElement.play(); // Play the audio stream directly
|
||||
console.log("Streaming URL:", streamingUrl);
|
||||
return new Response(null, {
|
||||
status: 200,
|
||||
statusText: "OK",
|
||||
headers: {
|
||||
"Content-Type": "audio/wav",
|
||||
"Content-Location": streamingUrl
|
||||
}
|
||||
});
|
||||
|
||||
// Return the streaming URL directly
|
||||
return streamingUrl;
|
||||
} else {
|
||||
// For standard method
|
||||
const outputUrl = await this.fetchTtsGeneration(inputText, voiceId);
|
||||
// Fetch the audio data as a blob from the URL
|
||||
const audioResponse = await fetch(outputUrl);
|
||||
if (!audioResponse.ok) {
|
||||
throw new Error(`HTTP ${audioResponse.status}: Failed to fetch audio data`);
|
||||
}
|
||||
const audioBlob = await audioResponse.blob();
|
||||
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
|
||||
}
|
||||
});
|
||||
return audioResponse; // Return the fetch response directly
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error in generateTts:", error);
|
||||
@@ -841,6 +757,7 @@ html += `<div class="website-row">
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//####################//
|
||||
// Generate Standard //
|
||||
//####################//
|
||||
@@ -852,8 +769,8 @@ html += `<div class="website-row">
|
||||
'text_filtering': "standard",
|
||||
'character_voice_gen': voiceId + ".wav",
|
||||
'narrator_enabled': this.settings.narrator_enabled,
|
||||
'narrator_voice_gen': this.settings.narrator_voice_gen,
|
||||
'text_not_inside': this.settings.text_not_inside,
|
||||
'narrator_voice_gen': this.settings.narrator_voice_gen + ".wav",
|
||||
'text_not_inside': this.settings.at_narrator_text_not_inside,
|
||||
'language': this.settings.language,
|
||||
'output_file_name': "st_output",
|
||||
'output_file_timestamp': "true",
|
||||
@@ -877,7 +794,6 @@ html += `<div class="website-row">
|
||||
if (!response.ok) {
|
||||
const errorText = await response.text();
|
||||
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');
|
||||
throw new Error(`HTTP ${response.status}: ${errorText}`);
|
||||
}
|
||||
@@ -912,27 +828,6 @@ function updateStatus(message) {
|
||||
case 'Error':
|
||||
statusElement.style.color = 'red';
|
||||
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 { 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 { ElevenLabsTtsProvider } from './elevenlabs.js';
|
||||
import { SileroTtsProvider } from './silerotts.js';
|
||||
@@ -318,12 +318,14 @@ async function playAudioData(audioBlob) {
|
||||
if (currentAudioJob == null) {
|
||||
console.log('Cancelled TTS playback because currentAudioJob was null');
|
||||
}
|
||||
const reader = new FileReader();
|
||||
reader.onload = function (e) {
|
||||
const srcUrl = e.target.result;
|
||||
if (audioBlob instanceof Blob) {
|
||||
const srcUrl = await getBase64Async(audioBlob);
|
||||
audioElement.src = srcUrl;
|
||||
};
|
||||
reader.readAsDataURL(audioBlob);
|
||||
} else if (typeof audioBlob === 'string') {
|
||||
audioElement.src = audioBlob;
|
||||
} else {
|
||||
throw `TTS received invalid audio data type ${typeof audioBlob}`;
|
||||
}
|
||||
audioElement.addEventListener('ended', completeCurrentAudioJob);
|
||||
audioElement.addEventListener('canplay', () => {
|
||||
console.debug('Starting TTS playback');
|
||||
@@ -419,11 +421,15 @@ function completeCurrentAudioJob() {
|
||||
* @param {Response} response
|
||||
*/
|
||||
async function addAudioJob(response) {
|
||||
const audioData = await response.blob();
|
||||
if (!audioData.type.startsWith('audio/')) {
|
||||
throw `TTS received HTTP response with invalid data format. Expecting audio/*, got ${audioData.type}`;
|
||||
if (typeof response === 'string') {
|
||||
audioJobQueue.push(response);
|
||||
} 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.');
|
||||
}
|
||||
|
||||
@@ -434,7 +440,7 @@ async function processAudioJobQueue() {
|
||||
}
|
||||
try {
|
||||
audioQueueProcessorReady = false;
|
||||
currentAudioJob = audioJobQueue.pop();
|
||||
currentAudioJob = audioJobQueue.shift();
|
||||
playAudioData(currentAudioJob);
|
||||
talkingAnimation(true);
|
||||
} catch (error) {
|
||||
@@ -465,13 +471,25 @@ function saveLastValues() {
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
// RVC injection
|
||||
if (extension_settings.rvc.enabled && typeof window['rvcVoiceConversion'] === 'function')
|
||||
response = await window['rvcVoiceConversion'](response, char, text);
|
||||
// If async generator, process every chunk as it comes in
|
||||
if (typeof response[Symbol.asyncIterator] === 'function') {
|
||||
for await (const chunk of response) {
|
||||
await processResponse(chunk);
|
||||
}
|
||||
} else {
|
||||
await processResponse(response);
|
||||
}
|
||||
|
||||
addAudioJob(response);
|
||||
completeTtsJob();
|
||||
}
|
||||
|
||||
@@ -658,6 +676,7 @@ function onSkipCodeblocksClick() {
|
||||
function onPassAsterisksClick() {
|
||||
extension_settings.tts.pass_asterisks = !!$('#tts_pass_asterisks').prop('checked');
|
||||
saveSettingsDebounced();
|
||||
console.log("setting pass asterisks", extension_settings.tts.pass_asterisks)
|
||||
}
|
||||
|
||||
//##############//
|
||||
@@ -743,7 +762,7 @@ function getCharacters(unrestricted) {
|
||||
if (unrestricted) {
|
||||
const names = context.characters.map(char => char.name);
|
||||
names.unshift(DEFAULT_VOICE_MARKER);
|
||||
return names;
|
||||
return names.filter(onlyUnique);
|
||||
}
|
||||
|
||||
let characters = [];
|
||||
@@ -758,14 +777,13 @@ function getCharacters(unrestricted) {
|
||||
characters.push(context.name1);
|
||||
const group = context.groups.find(group => context.groupId == group.id);
|
||||
for (let member of group.members) {
|
||||
// Remove suffix
|
||||
if (member.endsWith('.png')) {
|
||||
member = member.slice(0, -4);
|
||||
const character = context.characters.find(char => char.avatar == member);
|
||||
if (character) {
|
||||
characters.push(character.name);
|
||||
}
|
||||
characters.push(member);
|
||||
}
|
||||
}
|
||||
return characters;
|
||||
return characters.filter(onlyUnique);
|
||||
}
|
||||
|
||||
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">
|
||||
<link rel="icon" href="./favicon.ico" type="image/x-icon">
|
||||
<title>AllTalk TTS Generator</title>
|
||||
|
||||
<style>
|
||||
body,
|
||||
textarea,
|
||||
@@ -637,51 +636,51 @@
|
||||
import Crunker from './crunker.esm.js';
|
||||
|
||||
async function exportMP3() {
|
||||
const crunker = new Crunker();
|
||||
const fileUrls = ttsList.map(item => item.fileUrl);
|
||||
const splitEvery = parseInt(document.getElementById('splitEvery').value) || 500;
|
||||
const progressIndicator = document.getElementById('progressIndicator');
|
||||
const progressText = document.getElementById('progressText');
|
||||
const crunker = new Crunker();
|
||||
const fileUrls = ttsList.map(item => item.fileUrl);
|
||||
const splitEvery = parseInt(document.getElementById('splitEvery').value) || 500;
|
||||
const progressIndicator = document.getElementById('progressIndicator');
|
||||
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) {
|
||||
progressIndicator.style.display = 'none';
|
||||
progressText.textContent = ''; // Reset progress text
|
||||
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) {
|
||||
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 currentPage = 1;
|
||||
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 ////////
|
||||
@@ -851,7 +853,7 @@ window.exportMP3 = exportMP3;
|
||||
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",
|
||||
headers: {
|
||||
"Content-Type": "application/x-www-form-urlencoded",
|
||||
@@ -921,7 +923,7 @@ window.exportMP3 = exportMP3;
|
||||
autoplay: "false",
|
||||
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",
|
||||
headers: {
|
||||
"Content-Type": "application/x-www-form-urlencoded",
|
||||
@@ -1051,7 +1053,7 @@ window.exportMP3 = exportMP3;
|
||||
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",
|
||||
headers: {
|
||||
"Content-Type": "application/x-www-form-urlencoded",
|
||||
@@ -1081,8 +1083,9 @@ window.exportMP3 = exportMP3;
|
||||
});
|
||||
|
||||
// Reinitialize currentSound every time
|
||||
var srcUrl = baseUrl + "/tts-demo-request?" + queryParams.toString();
|
||||
currentSound = new Howl({
|
||||
src: [`http://127.0.0.1:7851/tts-demo-request?${queryParams.toString()}`],
|
||||
src: [srcUrl],
|
||||
volume: volume,
|
||||
html5: true // Force HTML5 Audio to enable streaming
|
||||
});
|
||||
@@ -1141,7 +1144,7 @@ window.exportMP3 = exportMP3;
|
||||
const formData = new FormData();
|
||||
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",
|
||||
body: formData,
|
||||
});
|
||||
@@ -1531,54 +1534,54 @@ window.exportMP3 = exportMP3;
|
||||
}
|
||||
|
||||
function importFromJSON() {
|
||||
const input = document.createElement('input');
|
||||
input.type = 'file';
|
||||
input.accept = '.json';
|
||||
const input = document.createElement('input');
|
||||
input.type = 'file';
|
||||
input.accept = '.json';
|
||||
|
||||
input.onchange = async (e) => {
|
||||
const file = e.target.files[0];
|
||||
if (file) {
|
||||
const reader = new FileReader();
|
||||
reader.readAsText(file);
|
||||
reader.onload = (e) => {
|
||||
try {
|
||||
const jsonData = JSON.parse(e.target.result);
|
||||
input.onchange = async (e) => {
|
||||
const file = e.target.files[0];
|
||||
if (file) {
|
||||
const reader = new FileReader();
|
||||
reader.readAsText(file);
|
||||
reader.onload = (e) => {
|
||||
try {
|
||||
const jsonData = JSON.parse(e.target.result);
|
||||
|
||||
ttsList = jsonData;
|
||||
currentPage = 1; // Reset to the first page
|
||||
updateUIList();
|
||||
updatePaginationControls(); // Update pagination controls
|
||||
} catch (error) {
|
||||
console.error('Error parsing JSON:', error);
|
||||
}
|
||||
};
|
||||
ttsList = jsonData;
|
||||
currentPage = 1; // Reset to the first page
|
||||
updateUIList();
|
||||
updatePaginationControls(); // Update pagination controls
|
||||
} catch (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 lastPage = Math.ceil(ttsList.length / itemsPerPage);
|
||||
const pageIndicator = document.getElementById('pageIndicator');
|
||||
if (pageIndicator) {
|
||||
pageIndicator.textContent = `Page ${currentPage} of ${lastPage}`;
|
||||
const nextPageButton = document.getElementById('nextPage');
|
||||
if (nextPageButton) {
|
||||
nextPageButton.disabled = (currentPage === 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() {
|
||||
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 characterVoiceSelect = document.getElementById("character_voice_gen");
|
||||
|
||||
@@ -1985,9 +1988,11 @@ function updatePaginationControls() {
|
||||
window.onload = function () {
|
||||
setupPage();
|
||||
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>
|
||||
</body>
|
||||
|
||||
|
||||
+22
-16
@@ -57,6 +57,10 @@ params = load_config(configfile_path)
|
||||
# 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"]
|
||||
|
||||
# Load values for temperature and repetition_penalty
|
||||
temperature = params["local_temperature"]
|
||||
repetition_penalty = params["local_repetition_penalty"]
|
||||
|
||||
# Define the path to the JSON file
|
||||
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)
|
||||
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
|
||||
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:
|
||||
return response
|
||||
async for _ in response:
|
||||
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
|
||||
if params["low_vram"] and device == "cpu":
|
||||
await switch_device()
|
||||
@@ -504,9 +508,9 @@ async def generate_audio_internal(text, voice, language, output_file, streaming)
|
||||
"language": language,
|
||||
"gpt_cond_latent": gpt_cond_latent,
|
||||
"speaker_embedding": speaker_embedding,
|
||||
"temperature": float(params["local_temperature"]),
|
||||
"temperature": float(temperature),
|
||||
"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_p": float(model.config.top_p),
|
||||
"enable_text_splitting": True
|
||||
@@ -560,9 +564,9 @@ async def generate_audio_internal(text, voice, language, output_file, streaming)
|
||||
file_path=output_file,
|
||||
speaker_wav=[f"{this_dir}/voices/{voice}"],
|
||||
language=language,
|
||||
temperature=float(params["local_temperature"]),
|
||||
temperature=temperature,
|
||||
length_penalty=model.config.length_penalty,
|
||||
repetition_penalty=float(params["local_repetition_penalty"]),
|
||||
repetition_penalty=repetition_penalty,
|
||||
top_k=model.config.top_k,
|
||||
top_p=model.config.top_p,
|
||||
)
|
||||
@@ -602,10 +606,12 @@ async def generate(request: Request):
|
||||
text = data["text"]
|
||||
voice = data["voice"]
|
||||
language = data["language"]
|
||||
temperature = data["temperature"]
|
||||
repetition_penalty = data["repetition_penalty"]
|
||||
output_file = data["output_file"]
|
||||
streaming = False
|
||||
# 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:
|
||||
return StreamingResponse(response, media_type="audio/wav")
|
||||
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):
|
||||
try:
|
||||
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")
|
||||
except Exception as 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(...)):
|
||||
try:
|
||||
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)
|
||||
except Exception as e:
|
||||
print(f"An error occurred: {e}")
|
||||
@@ -794,7 +800,7 @@ async def preview_voice(request: Request, voice: str = Form(...)):
|
||||
|
||||
# Generate the audio
|
||||
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
|
||||
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):
|
||||
try:
|
||||
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")
|
||||
except Exception as 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(...)):
|
||||
try:
|
||||
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)
|
||||
except Exception as e:
|
||||
print(f"An error occurred: {e}")
|
||||
@@ -877,7 +883,7 @@ class Request(BaseModel):
|
||||
pass
|
||||
|
||||
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'.")
|
||||
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.")
|
||||
@@ -1054,7 +1060,7 @@ async def tts_generate(
|
||||
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_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_files_all_paragraphs.append(audio_path)
|
||||
# Combine audio files across paragraphs
|
||||
@@ -1091,7 +1097,7 @@ async def tts_generate(
|
||||
cleaned_string = re.sub(r'\n+', ' ', cleaned_string)
|
||||
else:
|
||||
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:
|
||||
autoplay = False
|
||||
if autoplay:
|
||||
|
||||
Reference in new issue
Block a user