Compare commits

...
Author SHA1 Message Date
Paladinium f16117e95b Merge pull request #655 from Paladinium/fix_disk_space
Make free disk space to also consider large packages and swap storage
2026-01-09 09:58:54 +01:00
ekoc d499b24315 Make free disk space to also consider large packages and swap storage 2026-01-09 09:55:07 +01:00
Paladinium 46cc0d1e06 Merge pull request #623 from Hamakaze1s/alltalkbeta
Fix: Corrected conditional check in docker-start.sh for port mapping
2026-01-09 09:33:38 +01:00
Hamakaze1s a33771e51c Fix: Corrected conditional check in docker-start.sh for port mapping 2025-07-17 12:08:18 +09:00
Paladinium 56c859a8dc Merge pull request #620 from Paladinium/docker_cuda_12_8
Docker: Updating to CUDA 12.8, pytorch 2.7 and DeepSpeed 0.17.2
2025-07-15 07:10:48 +02:00
ekoc 7a539f65b9 Fix download URL for pre-built deepspeed wheel 2025-07-12 10:32:57 +02:00
ekoc 446c2f6814 Updating to CUDA 12.8, pytorch 2.7 and deepseepd 0.17.2 2025-07-11 20:33:20 +02:00
Paladinium ee4477e221 Merge pull request #609 from Paladinium/fix_pytorch_version
Fix pytorch version to 2.6.0
2025-06-09 13:51:20 +02:00
ekoc d33dde7fc8 Fix pytorch version to 2.6.0 2025-06-09 13:41:55 +02:00
erew123 38683ae9c8 Fix Error Invalid input error: I/O operation on closed file.
Thanks to @petermg

https://github.com/erew123/alltalk_tts/issues/501#issuecomment-2829142969
2025-04-25 07:44:37 +01:00
erew123 3b296bcd3c Merge pull request #582 from zappityzap/pr-pydantic
Pin pydantic due to bug in 2.11
2025-04-25 07:25:01 +01:00
erew123 35dcd9710c Update F5 to use new models & Inference
Adds the new F5 models
Updates inference code
Sets speed to 1 (when not specified)
2025-04-09 09:23:38 +01:00
zappityzap c9e2007526 pin pydantic due to bug in 2.11 2025-04-06 14:05:40 -07:00
Paladinium 947c4343d7 Merge pull request #568 from Paladinium/alltalkbeta_improve_github_actions
Improve paths-filter for Docker github action
2025-03-19 21:51:58 +01:00
ekoc 403ffdef3e Improve paths-filter for Docker github action 2025-03-19 21:51:34 +01:00
Paladinium b130ca30d5 Merge pull request #566 from Paladinium/alltalkbeta_rvc_cache_size
Provide config setting for RVC model cache size
2025-03-19 21:27:31 +01:00
ekoc 5c017f8752 Provide config setting for RVC model cache size 2025-03-19 21:23:56 +01:00
Paladinium 5a99f8b203 Merge pull request #565 from S-T-K/alltalkbeta
maxsize 3 for LRU cache for RVC
2025-03-18 21:09:46 +01:00
S-T-K f8516e5a14 maxsize 3 for LRU cache for RVC
Feature was introduced in https://github.com/erew123/alltalk_tts/pull/383 but without maxsize, which meant rvc models would never get unloaded
2025-03-18 09:33:39 +01:00
Paladinium 9317203f5a Merge pull request #561 from Paladinium/alltalkbeta_auto_cleanup
Periodic cleanup of generated WAV files
2025-03-14 22:29:20 +01:00
ekoc 3a9fc4554a Periodic cleanup of generated WAV files 2025-03-14 22:26:02 +01:00
Paladinium 64081dcabd Merge pull request #558 from Paladinium/alltalkbeta_dockerfile_improvements
Improve docker files and use a non-root user
2025-03-14 14:21:20 +01:00
ekoc 4041defb8d Improve docker files and use a non-root user 2025-03-14 14:14:18 +01:00
Paladinium 2bd1a5b43b Merge pull request #557 from Paladinium/alltalkbeta_docker_env_variables
Introduce ENV variables for Docker
2025-03-14 09:20:50 +01:00
ekoc 239524ea7d Introduce ENV variables for Docker 2025-03-12 22:02:42 +01:00
erew123 08dbeb334d Merge pull request #552 from Paladinium/alltalkbeta_improve_docker_build
Improving docker build time
2025-03-10 07:03:15 +00:00
ekoc 8a5db3a687 Improving docker build time by downloading the TTS model during build time, thereby enhancing firstrun.py 2025-03-09 16:07:05 +01:00
erew123 81fea73df4 Merge pull request #545 from Paladinium/alltalkbeta_multi_engine_manager
Enabling multi engine manager in Docker
2025-03-09 13:36:43 +00:00
erew123 230319c34e Merge pull request #547 from galemwendar/alltalkbeta
prevent variable "model_name" from UnboundLocalError
2025-03-08 06:11:02 +00:00
Андрей Петров 90af3c2ec5 prevent variable "model_name" from UnboundLocalError 2025-03-03 19:11:09 +03:00
ekoc 1dc8f807d2 Enabling multi engine manager in Docker 2025-03-01 14:41:17 +01:00
Paladinium 7a59e9c20e Merge pull request #544 from Paladinium/alltalkbeta
Bugfixes for docker build and start scripts + readme
2025-02-27 22:30:51 +01:00
ekoc 13db476dd0 Bugfixes for docker build and start scripts + readme 2025-02-27 22:28:13 +01:00
Paladinium cef6d8cf36 Merge pull request #534 from RenNagasaki/alltalkbeta_fix_docker_for_vastai
Fixing Dockerfile change I proposed myself, which doesn't work + Fix for Finetune.py
2025-02-27 21:48:07 +01:00
erew123 be1805e993 Merge pull request #535 from RenNagasaki/alltalkbeta_silent_installer
Silent installer for linux
2025-02-27 06:51:00 +00:00
erew123 cb47f763f9 Merge pull request #542 from Paladinium/alltalkbeta
Improvements to the docker build + update readme
2025-02-27 06:38:35 +00:00
j.katzwinkel@posteo.de 990d9b2d9a Revert error level changes 2025-02-26 21:53:57 +01:00
ekoc 385b5629b6 Improvements to the docker build + update readme 2025-02-26 21:21:53 +01:00
j.katzwinkel@posteo.de b07697b466 Move to correct position + fix bash syntax 2025-02-25 14:26:45 +01:00
j.katzwinkel@posteo.de 9fa61bfe18 Add -silent to bash script. 2025-02-25 14:09:50 +01:00
j.katzwinkel@posteo.de dac938476a Errors should always be displayed. 2025-02-25 13:35:49 +01:00
j.katzwinkel@posteo.de 9ff23888e0 -m trainer.distribute not working currently since coqui doesn't seem to be able to run multiple gpus
changing finetune from 127.0.0.1 to 0.0.0.0 so it's accessible.
2025-02-25 11:01:01 +01:00
erew123 91bf7a220a Merge pull request #531 from Paladinium/alltalkbeta
Adding Github actions for building the docker images
2025-02-25 06:09:09 +00:00
ekoc ce0e5c08bf Adding Github actions for building the docker images 2025-02-24 21:35:39 +01:00
erew123 698efdcdec Update RVC Hubert Loader - add_safe_globals fix 2025-02-24 15:06:05 +00:00
35 changed files with 1065 additions and 538 deletions

No files matched your search

+73
View File
@@ -0,0 +1,73 @@
name: 'Setting up docker'
inputs:
DOCKERHUB_USERNAME:
description: "Docker hub username"
required: true
DOCKERHUB_REPO_NAME:
description: "Docker hub repo name"
required: true
DOCKERHUB_TOKEN:
description: "Docker hub token"
required: true
runs:
using: "composite"
steps:
- name: Free Disk Space (Ubuntu)
uses: jlumbroso/free-disk-space@v1.3.1
with:
tool-cache: true
swap-storage: true
large-packages: true
- name: Read variables
shell: bash
run: |
source ./docker/variables.sh
for key in "${!ALLTALK_VARS[@]}"
do
echo "${key}=${ALLTALK_VARS[${key}]}"
echo "${key}=${ALLTALK_VARS[${key}]}" >> $GITHUB_ENV
done
- name: Extract version from Git Tag
shell: bash {0}
run: |
# Use the git tag if existing, otherwise 'latest':
DOCKER_TAG=$( echo "${{ github.ref }}" | grep -q tags | cut -d'/' -f 3 )
if [ -z "${DOCKER_TAG}" ]; then
DOCKER_TAG="latest"
fi
echo "Using DOCKER_TAG=${DOCKER_TAG}"
echo "DOCKER_TAG=${DOCKER_TAG}" >> $GITHUB_ENV
- name: Extract Docker Hub User and Repo Name
shell: bash
env:
DOCKERHUB_USERNAME: ${{ inputs.DOCKERHUB_USERNAME }}
DOCKERHUB_REPO_NAME: ${{ inputs.DOCKERHUB_REPO_NAME }}
run: |
if [ -n "${DOCKERHUB_USERNAME}" ]; then
echo "Using Github repository variable 'DOCKERHUB_USERNAME' as Docker hub username with value: '${DOCKERHUB_USERNAME}'"
echo "DOCKERHUB_USERNAME=${DOCKERHUB_USERNAME}" >> $GITHUB_ENV
else
echo "Using Github repository name as Docker hub username"
echo "DOCKERHUB_USERNAME=$(echo ${{ github.repository }} | cut -d'/' -f 1)" >> $GITHUB_ENV
fi
if [ -n "${DOCKERHUB_REPO_NAME}" ]; then
echo "Using Github repository variable 'DOCKERHUB_REPO_NAME' as Docker hub repo name with value: '${DOCKERHUB_REPO_NAME}'"
echo "DOCKERHUB_REPO_NAME=${DOCKERHUB_REPO_NAME}" >> $GITHUB_ENV
else
echo "Using Github repository name as Docker hub repo name"
echo "DOCKERHUB_REPO_NAME=$(echo ${{ github.repository }} | cut -d'/' -f 2)" >> $GITHUB_ENV
fi
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Login to DockerHub
uses: docker/login-action@v3
with:
username: ${{ inputs.DOCKERHUB_USERNAME }}
password: ${{ inputs.DOCKERHUB_TOKEN }}
+124
View File
@@ -0,0 +1,124 @@
name: Publish Docker v2
on:
workflow_dispatch:
release:
types: [ published ]
push:
branches:
- 'alltalkbeta'
tags:
- 'v*'
# Limit the concurrency of entire workflow runs for a specific branch:
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
jobs:
build_base_environment_docker_image:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Setup docker
uses: ./.github/actions/docker-setup
with:
DOCKERHUB_USERNAME: ${{ vars.DOCKERHUB_USERNAME }}
DOCKERHUB_REPO_NAME: ${{ vars.DOCKERHUB_REPO_NAME }}
DOCKERHUB_TOKEN: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Docker meta
id: meta
uses: docker/metadata-action@v5
with:
images: |
${{ env.DOCKERHUB_USERNAME }}/${{ env.DOCKERHUB_REPO_NAME }}_environment
tags: |
# Use sematic versioning on push tag event:
type=semver,pattern={{version}}
# set latest tag for main and alltalkbeta branch:
type=raw,value=latest,enable=${{ github.ref == format('refs/heads/{0}', 'main') || github.ref == format('refs/heads/{0}', 'alltalkbeta') }}
- uses: dorny/paths-filter@v3
id: changes
with:
base: 'alltalkbeta'
filters: |
docker-base:
- 'docker/variables.sh'
- 'docker/base/Dockerfile'
docker-workflow:
- '.github/workflows/publish-docker*.yml'
- name: Build and push base Docker image
if: |
steps.changes.outputs.docker-base == 'true' ||
steps.changes.outputs.docker-workflow == 'true' ||
github.event_name == 'release' ||
(github.event_name == 'push' && contains(github.ref, 'refs/tags/'))
uses: docker/build-push-action@v6
with:
context: ./docker/base
file: ./docker/base/Dockerfile
platforms: "linux/amd64"
push: true
build-args: |
CUDA_VERSION=${{ env.CUDA_VERSION }}
PYTHON_VERSION=${{ env.PYTHON_VERSION }}
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
cache-from: type=registry,ref=${{ env.DOCKERHUB_USERNAME }}/${{ env.DOCKERHUB_REPO_NAME }}_environment:buildcache
cache-to: type=registry,ref=${{ env.DOCKERHUB_USERNAME }}/${{ env.DOCKERHUB_REPO_NAME }}_environment:buildcache,mode=max
build_main_docker_image:
needs: build_base_environment_docker_image
runs-on: ubuntu-latest
strategy:
matrix:
tts: [ xtts, vits, piper ]
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Setup docker
uses: ./.github/actions/docker-setup
with:
DOCKERHUB_USERNAME: ${{ vars.DOCKERHUB_USERNAME }}
DOCKERHUB_REPO_NAME: ${{ vars.DOCKERHUB_REPO_NAME }}
DOCKERHUB_TOKEN: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Docker meta
id: meta
uses: docker/metadata-action@v5
with:
images: |
${{ env.DOCKERHUB_USERNAME }}/${{ env.DOCKERHUB_REPO_NAME }}
flavor: |
suffix=-${{ matrix.tts }},onlatest=true
tags: |
# Use sematic versioning on push tag event:
type=semver,pattern={{version}}
# set latest tag for main and alltalkbeta branch:
type=raw,value=latest,enable=${{ github.ref == format('refs/heads/{0}', 'main') || github.ref == format('refs/heads/{0}', 'alltalkbeta') }}
- name: Build and push base Docker image
uses: docker/build-push-action@v6
with:
context: .
file: ./Dockerfile
platforms: "linux/amd64"
push: true
build-args: |
TTS_MODEL=${{ matrix.tts }}
ALLTALK_DIR=${{ env.ALLTALK_DIR }}
DEEPSPEED_VERSION=${{ env.DEEPSPEED_VERSION }}
DOCKER_TAG=${{ env.DOCKER_TAG }}
DOCKER_REPOSITORY=${{ env.DOCKERHUB_USERNAME }}/
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
cache-from: type=registry,ref=${{ env.DOCKERHUB_USERNAME }}/${{ env.DOCKERHUB_REPO_NAME }}:buildcache-${{ matrix.tts }}
cache-to: type=registry,ref=${{ env.DOCKERHUB_USERNAME }}/${{ env.DOCKERHUB_REPO_NAME }}:buildcache-${{ matrix.tts }},mode=max
+84 -47
View File
@@ -1,21 +1,19 @@
# Docker # Docker
The Docker image currently works on Windows and Linux, optionally supporting NVIDIA GPUs. The Docker image currently works on Windows and Linux supporting NVIDIA GPUs.
## General Remarks ## System requirements
- The resulting Docker image is 21 GB in size. Building might require even more disk space temporarily. - AMD compatible Windows or Linux system
- Another 15 GB is required for building DeepSpeed - CUDA 12.8
- Build time depends on your hardware and internet connection. Expect at least 20-30min to be normal for a full build. - Nvidia Docker Container Toolkit
- This includes building the conda environment as well as DeepSpeed, which is the basis for the alltalk Docker image. - At least 25GB of free disk space
- The Docker build for alltalk:
- Downloads XTTS as default TTS engine
- Enables RVC by default
- Downloads all supported RVC models
- Enables deepspeed by default
- Starting the Docker image should only a few seconds due to all the steps that were already executed during build.
## Docker for Linux ## Quickstart
Use `docker-start.sh` to pull the latest docker image using the XTTS model from Docker Hub and
visit `http://localhost:7851/`. See instructions below for passing more arguments to the start script.
### Ubuntu Specific Setup for GPUs ## Installing Prerequisites
### Docker for Linux
#### Ubuntu Specific Setup for GPUs
1. Make sure the latest nvidia drivers are installed: `sudo ubuntu-drivers install` 1. Make sure the latest nvidia drivers are installed: `sudo ubuntu-drivers install`
1. Install Docker your preferred way. One way to do it is to follow the official documentation [here](https://docs.docker.com/engine/install/ubuntu/#uninstall-old-versions). 1. Install Docker your preferred way. One way to do it is to follow the official documentation [here](https://docs.docker.com/engine/install/ubuntu/#uninstall-old-versions).
- Start by uninstalling the old versions - Start by uninstalling the old versions
@@ -27,8 +25,8 @@ The Docker image currently works on Windows and Linux, optionally supporting NVI
```sudo nvidia-ctk runtime configure --runtime=docker``` ```sudo nvidia-ctk runtime configure --runtime=docker```
- Restart docker - Restart docker
## Docker for Windows (WSL2) ### Docker for Windows (WSL2)
### Windows Specific Setup for GPUs #### Windows Specific Setup for GPUs
> Make sure your Nvidia drivers are up to date: https://www.nvidia.com/download/index.aspx > Make sure your Nvidia drivers are up to date: https://www.nvidia.com/download/index.aspx
1. Install WSL2 in PowerShell with `wsl --install` and restart 1. Install WSL2 in PowerShell with `wsl --install` and restart
2. Open PowerShell, type and enter ```ubuntu```. It should now load you into wsl2 2. Open PowerShell, type and enter ```ubuntu```. It should now load you into wsl2
@@ -36,43 +34,82 @@ The Docker image currently works on Windows and Linux, optionally supporting NVI
4. Download CUDA toolkit keyring: `wget https://developer.download.nvidia.com/compute/cuda/repos/wsl-ubuntu/x86_64/cuda-keyring_1.1-1_all.deb` 4. Download CUDA toolkit keyring: `wget https://developer.download.nvidia.com/compute/cuda/repos/wsl-ubuntu/x86_64/cuda-keyring_1.1-1_all.deb`
5. Install keyring: `sudo dpkg -i cuda-keyring_1.1-1_all.deb` 5. Install keyring: `sudo dpkg -i cuda-keyring_1.1-1_all.deb`
6. Update package list: `sudo apt-get update` 6. Update package list: `sudo apt-get update`
7. Install CUDA toolkit: `sudo apt-get -y install cuda-toolkit-12-6` 7. Install CUDA toolkit: `sudo apt-get -y install cuda-toolkit-12-8`
8. Install Docker Desktop using WSL2 as the backend 8. Install Docker Desktop using WSL2 as the backend
9. Restart 9. Restart
10. If you wish to monitor the terminal remotely via SSH, follow [this guide](https://www.hanselman.com/blog/how-to-ssh-into-wsl2-on-windows-10-from-an-external-machine). 10. If you wish to monitor the terminal remotely via SSH, follow [this guide](https://www.hanselman.com/blog/how-to-ssh-into-wsl2-on-windows-10-from-an-external-machine).
11. Open PowerShell, type ```ubuntu```, [then follow below](#building-and-running-in-docker) 11. Open PowerShell, type ```ubuntu```, [then follow below](#quickstart)
## Building and Running in Docker ## Arguments for starting the Docker container
To make it as simple as possible to use Docker, the script `docker-start.sh` can be used. It provides the
following optional arguments:
1. Open a terminal (or Ubuntu WSL) and go where you cloned the repo - `--config` lets you choose a config JSON file which can subset of `confignew.json`. This allows you to change only
3. Build the image with `./docker-build.sh`
4. Start the container with `./docker-start.sh`
5. Visit `http://localhost:7851/` or remotely with `http://<ip>:7851`
## Arguments for building and starting docker
There are various arguments to customize the build and start of the docker image.
### Arguments for `docker-build.sh`
- `--tts_model` allows to choose the TTS model that is used by default. Valid values are `piper`, `vits`, `xtts`. Defaults to `xtts`.
- Example: `docker-build.sh --tts_model piper`
- `--tag` allows to choose the docker tag. Defaults to `latest`.
- Example: `docker-build.sh --tag mytag`
- `--clean` allows remove existing dependency build like conda environment or DeepSpeed.
- Example: `docker-build.sh --clean`
### Arguments for `docker-start.sh`
- `--config` lets you choose a config JSON file which can subset of `confignew.json`. This allows you to change only
few values and leave the rest as defined in the default `confignew.json` file. few values and leave the rest as defined in the default `confignew.json` file.
- Example: `docker-start.sh --config /my/config/file.json` with content `{"branding": "My Brand "}` will just change - Example: `docker-start.sh --config /my/config/file.json` with content `{"branding": "My Brand "}` will just change
the branding in `confignew.json`. the branding in `confignew.json`.
- `--voices` lets you add voices for the TTS engine in WAV format. You have to specify the folder containing all - `--voices` lets you add voices for the TTS engine in WAV format. You have to specify the folder containing all
voice files. voice files.
- Example: `docker-start.sh --voices /my/voices/dir` - Example: `docker-start.sh --voices /my/voices/dir`
- `--rvc_voices` similar to voices, this option lets you pick the folder containing the RVC models. - `--rvc-voices` similar to voices, this option lets you pick the folder containing the [RVC models](https://github.com/erew123/alltalk_tts/wiki/RVC-(Retrieval%E2%80%90based-Voice-Conversion)).
- Example: `docker-start.sh --rvc_voices /my/rvc/voices/dir` - Example: `docker-start.sh --rvc_voices /my/rvc/voices/dir`
- `--no_ui` allows you to not expose port 7852 for the gradio interface. Note that you still have to set `launch_gradio` - `--no-ui` allows you to not expose port 7852 for the gradio interface and sets `launch_gradio` and `gradio_interface` to `false`.
to `false` via JSON file passed to `--config`. - `--auto-cleanup` periodically deletes all generated WAV files that are older than 1min.
- `--tag` allows to choose the docker tag of the image to run. Defaults to `latest`. - `--with-multi-engine-manager` enables the use of the [multi engine manager (MEM)](https://github.com/erew123/alltalk_tts/wiki/Multi-Engine-Manager)
which allows for more parallel requests. By default, one TTS engine is started. Optionally, you can pass the
file path of a JSON file which can be a subset of `mem_config.json` with more fine-grained configuration options.
- Example: `docker-start.sh --with-multi_engine_manager` to use MEM with default settings or
`docker-start.sh --with-multi_engine_manager /my/config/file.json` to pass a JSON file containing more settings.
- `--tag` allows to choose the docker tag of the image to run. Defaults to `latest-xtts`.
- Example: `docker-start.sh --tag mytag` - Example: `docker-start.sh --tag mytag`
- Since the above commands only address the most important options, you might pass additional arbitrary docker commands - `--docker-repository` allows to choose another Docker repository for pulling the image from. Use an empty
to the `docker-start.sh`. string for the local repo.
- Since the above commands only address the most important options, you might pass additional arbitrary docker arguments
to the `docker-start.sh`.
Of course, like for any other Docker image, you can also directly use `docker run` directly and use
[docker-start.sh](https://github.com/erew123/alltalk_tts/blob/alltalkbeta/docker-start.sh) as an inspiration.
### Using environment variables for setting config values
Instead of using config JSON files, there are config settings that can be directly set using environment (ENV) variables
when starting the application. This is also what is used internally in `docker-start.sh`.
Since `docker-start.sh` allows to pass arbitrary params, it can also be used to set env variables, e.g:
```
./docker-start.sh -e ALLTALK_BRANDING="My Brand " -e ALLTALK_API_PORT_NUMBER=9876
```
or when using Docker directly:
```
docker run -e ALLTALK_BRANDING="My Brand " -e ALLTALK_API_PORT_NUMBER=9876 ...
```
Consult [docker_default_confignew.json](https://github.com/erew123/alltalk_tts/blob/alltalkbeta/docker_default_confignew.json)
and [docker_default_mem_config.json](https://github.com/erew123/alltalk_tts/blob/alltalkbeta/docker_default_mem_config.json)
for a list of all available env variables.
## Building your own images (advanced)
> Be aware that building the images requires good hardware and bandwidth. Expect this process to require >
> 40 GB of disk space and > 30min execution time depending on your hardware.
Under normal circumstances, there should be no need to build the Docker images locally. However, if needed for some
reason, you may want to use `docker-build.sh` with the following arguments:
- `--tts_model` allows to choose the TTS model that is used by default. Valid values are `piper`, `vits`, `xtts`. Defaults to `xtts`.
- Example: `docker-build.sh --tts_model piper`
- `--tag` allows to choose the docker tag. Defaults to `latest-xtts`.
- Example: `docker-build.sh --tag mytag`
- `--docker-repository` allows to choose another Docker repository for tagging the image from. Use an empty
string for the local repo.
- `--local-deepspeed-build` should only be used in rare cases where DeepSpeed needs to be rebuilt. Defaults to `false`.
- `--clean` allows remove existing dependency build like conda environment or DeepSpeed. Defaults to `false`.
- Example: `docker-build.sh --clean`
# Older images
- CUDA 12.6:
- XTTS Dockerhub hash: sha256:fd4741714af0c82e4e7a66bf9ae47c98f3e51ebefdb3af5795b47d41ca5fb159
- VITS Dockerhub hash: sha256:9f411e8fdb997486bc217fcd316bf6b163c39736655ea3400028ad4fcc5c60f9
- Piper: Dockerhub hash: sha256:6ae2520051bb00a0356dc4ed80a33bf6784cb567b59759af46046ddcf356b765
+129 -36
View File
@@ -1,32 +1,81 @@
ARG GITHUB_REPOSITORY ARG DOCKER_REPOSITORY
FROM ${GITHUB_REPOSITORY}alltalk_environment:latest ARG DOCKER_TAG=latest
FROM ${DOCKER_REPOSITORY}alltalk_tts_environment:${DOCKER_TAG}
# Argument to choose the model: piper, vits, xtts # Argument to choose the model: piper, vits, xtts
ARG TTS_MODEL="xtts" ARG TTS_MODEL="xtts"
ENV TTS_MODEL=$TTS_MODEL ENV TTS_MODEL=$TTS_MODEL
ARG DEEPSPEED_VERSION=0.16.2 ARG DEEPSPEED_VERSION=0.17.2
ENV DEEPSPEED_VERSION=$DEEPSPEED_VERSION ENV DEEPSPEED_VERSION=$DEEPSPEED_VERSION
ARG ALLTALK_DIR=/opt/alltalk
ENV GRADIO_SERVER_NAME="0.0.0.0" ENV GRADIO_SERVER_NAME="0.0.0.0"
# Switch for using the multi engine manager:
ENV ALLTALK_ENABLE_MULTI_ENGINE_MANAGER=false
# Default settings used in docker_default_confignew.json:
ENV ALLTALK_DELETE_OUTPUT_WAVS="1"
ENV ALLTALK_GRADIO_INTERFACE=true
ENV ALLTALK_RVC_ENABLED=true
ENV ALLTALK_RVC_F0METHOD="rmvpe"
# Default settings used in docker_default_mem_config.json:
ENV ALLTALK_MEM_AUTO_START_ENGINES=1
ENV ALLTALK_MEM_MAX_INSTANCES=1
WORKDIR ${ALLTALK_DIR} WORKDIR ${ALLTALK_DIR}
##############################################################################
# Download TTS models:
##############################################################################
COPY system/tts_engines/piper/available_models.json system/tts_engines/piper/
COPY system/tts_engines/vits/available_models.json system/tts_engines/vits/
COPY system/tts_engines/xtts/available_models.json system/tts_engines/xtts/
RUN <<EOR
available_models="system/tts_engines/${TTS_MODEL}/available_models.json"
first_start_model=$(cat ${available_models} | jq -r '.first_start_model')
model=$(cat ${available_models} | jq ".models[] | select(.model_name==\"${first_start_model}\")")
folder_path=$( echo "${model}" | jq -r '.folder_path // empty' )
files_to_download=$( echo "${model}" | jq '.files_to_download[]?' )
github_rls_url=$( echo "${model}" | jq '.github_rls_url // empty' )
# Special case for vits: firstrun.py expects the ZIP download directly in the model folder.
# When firstrun.py unzips the file, the correct folder will be created automatically.
if [[ "$TTS_MODEL" == "vits" ]]; then
folder_path=""
fi
# Merging both fields:
files_to_download="${files_to_download} ${github_rls_url}"
target_dir="models/${TTS_MODEL}/${folder_path}"
echo "$files_to_download" | xargs -n 1 curl --create-dirs --output-dir "${target_dir}" -LO
EOR
##############################################################################
# Download all RVC models:
##############################################################################
COPY system/tts_engines/rvc_files.json system/tts_engines/
RUN jq -r '.[]' system/tts_engines/rvc_files.json | xargs -n 1 curl --create-dirs --output-dir models/rvc_base -LO
############################################################################## ##############################################################################
# Install python dependencies (cannot use --no-deps because requirements are not complete) # Install python dependencies (cannot use --no-deps because requirements are not complete)
############################################################################## ##############################################################################
COPY system/config system/config COPY system/config/*.whl system/config/
COPY system/requirements/requirements_standalone.txt system/requirements/requirements_standalone.txt COPY system/requirements/requirements_standalone.txt system/requirements/requirements_standalone.txt
COPY system/requirements/requirements_parler.txt system/requirements/requirements_parler.txt COPY system/requirements/requirements_parler.txt system/requirements/requirements_parler.txt
ENV PIP_CACHE_DIR=${ALLTALK_DIR}/pip_cache ENV PIP_CACHE_DIR=${ALLTALK_DIR}/pip_cache
RUN <<EOR RUN <<EOR
conda activate alltalk conda activate alltalk
mkdir ${ALLTALK_DIR}/pip_cache mkdir -p ${ALLTALK_DIR}/pip_cache
pip install --no-cache-dir --cache-dir=${ALLTALK_DIR}/pip_cache -r system/requirements/requirements_standalone.txt pip install --no-cache-dir --cache-dir=${ALLTALK_DIR}/pip_cache -r system/requirements/requirements_standalone.txt
pip install --no-cache-dir --cache-dir=${ALLTALK_DIR}/pip_cache --upgrade gradio==4.32.2 pip install --no-cache-dir --cache-dir=${ALLTALK_DIR}/pip_cache --upgrade gradio==4.32.2
# By default, version 1.9.10 is used causing this warning on startup: 'FutureWarning: `torch.cuda.amp.autocast(args...)` is deprecated'
pip install --no-cache-dir --cache-dir=${ALLTALK_DIR}/pip_cache local-attention==1.11.1
# Parler: # Parler:
pip install --no-cache-dir --cache-dir=${ALLTALK_DIR}/pip_cache -r system/requirements/requirements_parler.txt pip install --no-cache-dir --cache-dir=${ALLTALK_DIR}/pip_cache -r system/requirements/requirements_parler.txt
@@ -36,12 +85,23 @@ EOR
############################################################################## ##############################################################################
# Install DeepSpeed # Install DeepSpeed
############################################################################## ##############################################################################
RUN mkdir -p /tmp/deepseped RUN mkdir -p /tmp/deepspeed
COPY docker/deepspeed/build/*.whl /tmp/deepspeed/ COPY docker/deepspeed/build*/*.whl /tmp/deepspeed/
RUN <<EOR RUN <<EOR
DEEPSPEED_WHEEL=$(realpath /tmp/deepspeed/*.whl) DEEPSPEED_WHEEL=$(realpath -q /tmp/deepspeed/*.whl)
conda activate alltalk conda activate alltalk
# Download DeepSpeed wheel if it was not built locally:
if [ -z "${DEEPSPEED_WHEEL}" ] || [ ! -f $DEEPSPEED_WHEEL ] ; then
echo "Downloading pre-built DeepSpeed wheel"
CURL_ERROR=$( { curl --output-dir /tmp/deepspeed -fLO "https://github.com/erew123/alltalk_tts/releases/download/DeepSpeed-for-docker/deepspeed-0.17.2+15f054d9-cp311-cp311-linux_x86_64.whl" ; } 2>&1 )
if [ $? -ne 0 ] ; then
echo "Failed to download DeepSpeed: $CURL_ERROR"
exit 1
fi
DEEPSPEED_WHEEL=$(realpath -q /tmp/deepspeed/*.whl)
fi
echo "Using precompiled DeepSpeed wheel at ${DEEPSPEED_WHEEL}" echo "Using precompiled DeepSpeed wheel at ${DEEPSPEED_WHEEL}"
CFLAGS="-I$CONDA_PREFIX/include/" LDFLAGS="-L$CONDA_PREFIX/lib/" \ CFLAGS="-I$CONDA_PREFIX/include/" LDFLAGS="-L$CONDA_PREFIX/lib/" \
pip install --no-cache-dir ${DEEPSPEED_WHEEL} pip install --no-cache-dir ${DEEPSPEED_WHEEL}
@@ -63,24 +123,55 @@ RUN <<EOR
#!/usr/bin/env bash #!/usr/bin/env bash
source ~/.bashrc source ~/.bashrc
# Merging config from docker_confignew.json into confignew.json: replace_env_vars() {
jq -s '.[0] * .[1] * .[2]' confignew.json docker_default_config.json docker_confignew.json > confignew.json.tmp echo "{}" > /tmp/empty.json
mv confignew.json.tmp confignew.json jq "\$( cat \$1 ) | del(.. | select(. == null))" /tmp/empty.json > \$1.tmp
mv \$1.tmp \$1
rm -f /tmp/empty.json
}
conda activate alltalk merge_json_files() {
python script.py # Merging JSON config with docker default values followed by values set on startup:
jq -s '.[0] * .[1] * .[2]' \$1 docker_default_\$1 docker_\$1 > \$1.tmp
mv \$1.tmp \$1
}
replace_env_vars docker_default_confignew.json
merge_json_files confignew.json
# Script for deleting WAV files that are older than 1 minute:
cat << EOF2 > ${ALLTALK_DIR}/cleanup-wavs.sh
#!/usr/bin/env bash
[ \"\$ALLTALK_AUTO_CLEANUP\" = \"true\" ] && find ${ALLTALK_DIR}/outputs -type f -name "*.wav" -mmin +1 -exec rm {} \;
EOF2
chmod u+x ${ALLTALK_DIR}/cleanup-wavs.sh
# Starting the cron job:
cron
source ${ALLTALK_DIR}/conda_env.sh
if [ "\$ALLTALK_ENABLE_MULTI_ENGINE_MANAGER" = "true" ] ; then
echo "Starting alltalk using multi engine manager"
replace_env_vars docker_default_mem_config.json
merge_json_files mem_config.json
python tts_mem.py
else
echo "Starting alltalk"
python script.py
fi
EOF EOF
cat << EOF > start_finetune.sh cat << EOF > start_finetune.sh
#!/usr/bin/env bash #!/usr/bin/env bash
source ~/.bashrc source ~/.bashrc
export TRAINER_TELEMETRY=0 export TRAINER_TELEMETRY=0
conda activate alltalk source ${ALLTALK_DIR}/conda_env.sh
python finetune.py python finetune.py
EOF EOF
cat << EOF > start_diagnostics.sh cat << EOF > start_diagnostics.sh
#!/usr/bin/env bash #!/usr/bin/env bash
source ~/.bashrc source ~/.bashrc
conda activate alltalk source ${ALLTALK_DIR}/conda_env.sh
python diagnostics.py python diagnostics.py
EOF EOF
chmod +x start_alltalk.sh chmod +x start_alltalk.sh
@@ -88,21 +179,9 @@ EOF
chmod +x start_diagnostics.sh chmod +x start_diagnostics.sh
EOR EOR
COPY . . COPY --chown=alltalk:alltalk . .
############################################################################## RUN mkdir -p ${ALLTALK_DIR}/outputs ${ALLTALK_DIR}/.triton/autotune
# Create script to execute firstrun.py and run it:
##############################################################################
RUN echo $'#!/usr/bin/env bash \n\
source ~/.bashrc \n\
conda activate alltalk \n\
python ./system/config/firstrun.py $@' > ./start_firstrun.sh
RUN chmod +x start_firstrun.sh
RUN ./start_firstrun.sh --tts_model $TTS_MODEL
RUN mkdir -p ${ALLTALK_DIR}/outputs
RUN mkdir -p /root/.triton/autotune
############################################################################## ##############################################################################
# Enable deepspeed for all models: # Enable deepspeed for all models:
@@ -110,15 +189,29 @@ RUN mkdir -p /root/.triton/autotune
RUN find . -name model_settings.json -exec sed -i -e 's/"deepspeed_enabled": false/"deepspeed_enabled": true/g' {} \; RUN find . -name model_settings.json -exec sed -i -e 's/"deepspeed_enabled": false/"deepspeed_enabled": true/g' {} \;
############################################################################## ##############################################################################
# Download all RVC models: # Cronjob running every minute to delete output WAV files:
############################################################################## ##############################################################################
USER root
RUN <<EOR RUN <<EOR
jq -r '.[]' system/tts_engines/rvc_files.json > /tmp/rvc_files.txt echo "* * * * * ${ALLTALK_DIR}/cleanup-wavs.sh" > /etc/cron.d/cleanup-wavs
xargs -n 1 curl --create-dirs --output-dir models/rvc_base -LO < /tmp/rvc_files.txt crontab -u alltalk /etc/cron.d/cleanup-wavs
rm -f /tmp/rvc_files.txt chmod u+s /usr/sbin/cron
EOR EOR
USER alltalk
##############################################################################
# Create script to execute firstrun.py and run it:
##############################################################################
RUN echo $'#!/usr/bin/env bash \n\
source ~/.bashrc \n\
source ${ALLTALK_DIR}/conda_env.sh \n\
python ./system/config/firstrun.py $@' > ./start_firstrun.sh
RUN chmod +x start_firstrun.sh
RUN ./start_firstrun.sh --tts_model $TTS_MODEL
############################################################################## ##############################################################################
# Start alltalk: # Start alltalk:
############################################################################## ##############################################################################
ENTRYPOINT ["sh", "-c", "./start_alltalk.sh"] ENTRYPOINT ["bash", "-c", "./start_alltalk.sh"]
+7 -3
View File
@@ -553,6 +553,10 @@ reapply_standalone() {
echo echo
} }
# Check for "-silent" install command-line argument
# Start the main menu if [[ $1 == "-silent" ]]; then
main_menu install_custom_standalone
else
# Start the main menu
main_menu
fi
+44 -1
View File
@@ -38,6 +38,7 @@ class AlltalkConfigRvcSettings(BaseModel):
f0method: str = "fcpe" f0method: str = "fcpe"
embedder_model: str = "hubert" embedder_model: str = "hubert"
training_data_size: int = 45000 training_data_size: int = 45000
model_cache_size: int = 1
class AlltalkConfigTgwUi(BaseModel): class AlltalkConfigTgwUi(BaseModel):
tgwui_activate_tts: bool = True tgwui_activate_tts: bool = True
@@ -134,7 +135,7 @@ class AbstractJsonConfig(ABC):
def _reload_on_change(self): def _reload_on_change(self):
# Check if config file has been modified and reload if needed # Check if config file has been modified and reload if needed
if time.time() - self.__last_read_time >= self.__file_check_interval: if self.__file_check_interval != 0 and time.time() - self.__last_read_time >= self.__file_check_interval:
try: try:
most_recent_modification = self.get_config_path().stat().st_mtime most_recent_modification = self.get_config_path().stat().st_mtime
if most_recent_modification > self.__last_read_time: if most_recent_modification > self.__last_read_time:
@@ -369,3 +370,45 @@ class AlltalkConfig(AbstractJsonConfig, AlltalkConfigFields):
model = AlltalkConfigModel.model_validate_json(json_string) model = AlltalkConfigModel.model_validate_json(json_string)
model.get_output_directory().mkdir(parents=True, exist_ok=True) model.get_output_directory().mkdir(parents=True, exist_ok=True)
return model return model
class AlltalkMultiEngineManagerConfigFields:
base_port: int = 7001
api_server_port: int = 7851
auto_start_engines: int = 0
max_instances: int = 8
gradio_interface_port: int = 7500
max_retries: int = 12
initial_wait: float = 2
backoff_factor: float = 1.2
debug_mode: bool = False
max_queue_time: int = 60, # Maximum time a request can wait in the queue (in seconds)
queue_check_interval: float = 0.1, # Time between checks for available instances (in seconds)
tts_request_timeout: int = 30, # Timeout for individual TTS requests (in seconds)
text_length_factor: float = 0.2, # Increase timeout by 20% per 100 characters
concurrent_request_factor: float = 0.5, # Increase timeout by 50% per concurrent request
diminishing_factor: float = 0.5, # Reduce additional time for long-running requests by 50%
queue_position_factor: float = 1.0 # Add 100% of base timeout for each queue position
class AlltalkMultiEngineManagerConfigModel(BaseModel, AlltalkMultiEngineManagerConfigFields):
pass
class AlltalkMultiEngineManagerConfig(AbstractJsonConfig, AlltalkMultiEngineManagerConfigFields):
__instance = None
__this_dir = Path(__file__).parent.resolve()
def __init__(self, config_path: Path | str = os.path.join(__this_dir, "mem_config.json")):
super().__init__(config_path, 0)
self._load_config()
@staticmethod
def get_instance():
if AlltalkMultiEngineManagerConfig.__instance is None:
AlltalkMultiEngineManagerConfig.__instance = AlltalkMultiEngineManagerConfig()
AlltalkMultiEngineManagerConfig.__instance._reload_on_change()
return AlltalkMultiEngineManagerConfig.__instance
def _handle_loaded_config(self, json_string: str):
return AlltalkMultiEngineManagerConfigModel.model_validate_json(json_string)
def save(self, path: Path | str | None = None):
self._save_file(path)
+2 -1
View File
@@ -26,7 +26,8 @@
"hop_length": 130, "hop_length": 130,
"f0method": "fcpe", "f0method": "fcpe",
"embedder_model": "hubert", "embedder_model": "hubert",
"training_data_size": 45000 "training_data_size": 45000,
"model_cache_size": 1
}, },
"tgwui": { "tgwui": {
"tgwui_activate_tts": true, "tgwui_activate_tts": true,
+28 -15
View File
@@ -6,8 +6,10 @@ cd $SCRIPT_DIR
. ${SCRIPT_DIR=}/docker/variables.sh . ${SCRIPT_DIR=}/docker/variables.sh
TTS_MODEL=xtts TTS_MODEL=xtts
DOCKER_TAG=latest DOCKER_TAG=latest-${TTS_MODEL}
CLEAN=false CLEAN=false
LOCAL_DEEPSPEED_BUILD=false
DOCKER_REPOSITORY=erew123
# Create required build directories if they don't exist # Create required build directories if they don't exist
mkdir -p ${SCRIPT_DIR=}/docker/deepspeed/build mkdir -p ${SCRIPT_DIR=}/docker/deepspeed/build
@@ -36,12 +38,17 @@ while [ "$#" -gt 0 ]; do
DOCKER_TAG="$2" DOCKER_TAG="$2"
shift shift
;; ;;
--github-repository) --docker-repository)
if [ -n "${GITHUB_REPOSITORY}" ] && ! [[ $GITHUB_REPOSITORY =~ ^--.* ]]; then if [ -n "$2" ] && ! [[ $2 =~ ^--.* ]]; then
GITHUB_REPOSITORY="$2" DOCKER_REPOSITORY="$2"
shift shift
else
DOCKER_REPOSITORY=""
fi fi
;; ;;
--local-deepspeed-build)
LOCAL_DEEPSPEED_BUILD=true
;;
--clean) --clean)
CLEAN=true CLEAN=true
;; ;;
@@ -53,6 +60,9 @@ while [ "$#" -gt 0 ]; do
shift shift
done done
# Append slash if missing:
DOCKER_REPOSITORY=$(echo "$DOCKER_REPOSITORY" | sed 's![^/]$!&/!')
if [ "$CLEAN" = true ]; then if [ "$CLEAN" = true ]; then
rm -rf ${SCRIPT_DIR=}/docker/deepspeed/build rm -rf ${SCRIPT_DIR=}/docker/deepspeed/build
# Recreate directories after clean # Recreate directories after clean
@@ -64,7 +74,7 @@ echo "Building base environment image"
$SCRIPT_DIR/docker/base/build-base-env.sh \ $SCRIPT_DIR/docker/base/build-base-env.sh \
--cuda-version ${CUDA_VERSION} \ --cuda-version ${CUDA_VERSION} \
--python-version ${PYTHON_VERSION} \ --python-version ${PYTHON_VERSION} \
--github-repository ${GITHUB_REPOSITORY} \ --docker-repository ${DOCKER_REPOSITORY} \
--tag ${DOCKER_TAG} --tag ${DOCKER_TAG}
if [ $? -ne 0 ]; then if [ $? -ne 0 ]; then
@@ -72,15 +82,17 @@ if [ $? -ne 0 ]; then
exit 1 exit 1
fi fi
echo "Building DeepSpeed" if [ "$LOCAL_DEEPSPEED_BUILD" = true ]; then
$SCRIPT_DIR/docker/deepspeed/build-deepspeed.sh \ echo "Building DeepSpeed"
--python-version ${PYTHON_VERSION} \ $SCRIPT_DIR/docker/deepspeed/build-deepspeed.sh \
--github-repository ${GITHUB_REPOSITORY} \ --python-version ${PYTHON_VERSION} \
--tag ${DOCKER_TAG} --docker-repository ${DOCKER_REPOSITORY} \
--tag ${DOCKER_TAG}
if [ $? -ne 0 ]; then if [ $? -ne 0 ]; then
echo "Failed to build DeepSpeed" echo "Failed to build DeepSpeed"
exit 1 exit 1
fi
fi fi
echo "Starting docker build process using TTS model '${TTS_MODEL}' and docker tag '${DOCKER_TAG}'" echo "Starting docker build process using TTS model '${TTS_MODEL}' and docker tag '${DOCKER_TAG}'"
@@ -90,9 +102,10 @@ docker buildx \
build \ build \
--progress=plain \ --progress=plain \
--build-arg TTS_MODEL=$TTS_MODEL \ --build-arg TTS_MODEL=$TTS_MODEL \
--build-arg ALLTALK_DIR=$ALLTALK_DIR \
--build-arg DEEPSPEED_VERSION=$DEEPSPEED_VERSION \ --build-arg DEEPSPEED_VERSION=$DEEPSPEED_VERSION \
-t ${GITHUB_REPOSITORY}alltalk_beta:${DOCKER_TAG} \ --build-arg DOCKER_TAG=$DOCKER_TAG \
--build-arg DOCKER_REPOSITORY=$DOCKER_REPOSITORY \
-t ${DOCKER_REPOSITORY}alltalk_tts:${DOCKER_TAG} \
. .
echo "Docker build process finished. Use docker-start.sh to start the container." echo "Docker build process finished. Use docker-start.sh to start the container."
+46 -11
View File
@@ -3,8 +3,13 @@
SCRIPT_DIR=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd ) SCRIPT_DIR=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )
. ${SCRIPT_DIR=}/docker/variables.sh . ${SCRIPT_DIR=}/docker/variables.sh
WITH_UI=true ALLTALK_GRADIO_INTERFACE=true
DOCKER_TAG=latest ALLTALK_ENABLE_MULTI_ENGINE_MANAGER=false
MULTI_ENGINE_MANAGER_CONFIG=
DOCKER_TAG=latest-xtts
DOCKER_REPOSITORY=erew123
ALLTALK_DIR=/home/alltalk
ALLTALK_AUTO_CLEANUP=false
declare -a ADDITIONAL_ARGS=() declare -a ADDITIONAL_ARGS=()
# Parse arguments # Parse arguments
@@ -18,21 +23,33 @@ while [ "$#" -gt 0 ]; do
VOICES="$2" VOICES="$2"
shift shift
;; ;;
--rvc_voices) --rvc-voices)
RVC_VOICES="$2" RVC_VOICES="$2"
shift shift
;; ;;
--no_ui) --no-ui)
WITH_UI=false ALLTALK_GRADIO_INTERFACE=false
;;
--auto-cleanup)
ALLTALK_AUTO_CLEANUP=true
;;
--with-multi-engine-manager)
ALLTALK_ENABLE_MULTI_ENGINE_MANAGER=true
if [ -n "$2" ] && ! [[ $2 =~ ^--.* ]]; then
MULTI_ENGINE_MANAGER_CONFIG=$2
shift
fi
;; ;;
--tag) --tag)
DOCKER_TAG="$2" DOCKER_TAG="$2"
shift shift
;; ;;
--github-repository) --docker-repository)
if [ -n "${GITHUB_REPOSITORY}" ] && ! [[ $GITHUB_REPOSITORY =~ ^--.* ]]; then if [ -n "$2" ] && ! [[ $2 =~ ^--.* ]]; then
GITHUB_REPOSITORY="$2" DOCKER_REPOSITORY="$2"
shift shift
else
DOCKER_REPOSITORY=""
fi fi
;; ;;
*) *)
@@ -43,6 +60,9 @@ while [ "$#" -gt 0 ]; do
shift shift
done done
# Append slash if missing:
DOCKER_REPOSITORY=$(echo "$DOCKER_REPOSITORY" | sed 's![^/]$!&/!')
# Compose docker arguments based on user input to the script: # Compose docker arguments based on user input to the script:
declare -a DOCKER_ARGS=() declare -a DOCKER_ARGS=()
@@ -59,10 +79,25 @@ if [[ -n $RVC_VOICES ]]; then
DOCKER_ARGS+=( -v ${RVC_VOICES}:${ALLTALK_DIR}/models/rvc_voices ) DOCKER_ARGS+=( -v ${RVC_VOICES}:${ALLTALK_DIR}/models/rvc_voices )
fi fi
if [ "$WITH_UI" = true ] ; then if [ "$ALLTALK_GRADIO_INTERFACE" = true ] ; then
DOCKER_ARGS+=( -p 7852:7852 ) if [ "$ALLTALK_ENABLE_MULTI_ENGINE_MANAGER" = true ] ; then
DOCKER_ARGS+=( -p 7500:7500 )
else
DOCKER_ARGS+=( -p 7852:7852 )
fi
fi fi
if [[ -n $MULTI_ENGINE_MANAGER_CONFIG ]]; then
DOCKER_ARGS+=( -v ${MULTI_ENGINE_MANAGER_CONFIG}:${ALLTALK_DIR}/docker_mem_config.json )
fi
# Pass env variables:
DOCKER_ARGS+=( -e ALLTALK_ENABLE_MULTI_ENGINE_MANAGER=${ALLTALK_ENABLE_MULTI_ENGINE_MANAGER} )
DOCKER_ARGS+=( -e ALLTALK_GRADIO_INTERFACE=${ALLTALK_GRADIO_INTERFACE} )
DOCKER_ARGS+=( -e ALLTALK_LAUNCH_GRADIO=${ALLTALK_GRADIO_INTERFACE} )
DOCKER_ARGS+=( -e ALLTALK_AUTO_CLEANUP=${ALLTALK_AUTO_CLEANUP} )
docker run \ docker run \
--rm \ --rm \
-it \ -it \
@@ -71,4 +106,4 @@ docker run \
--name alltalk \ --name alltalk \
"${DOCKER_ARGS[@]}" \ "${DOCKER_ARGS[@]}" \
"${ADDITIONAL_ARGS[@]}" \ "${ADDITIONAL_ARGS[@]}" \
${GITHUB_REPOSITORY}alltalk_beta:${DOCKER_TAG} &> /dev/stdout ${DOCKER_REPOSITORY}alltalk_tts:${DOCKER_TAG} &> /dev/stdout
+23 -4
View File
@@ -1,6 +1,6 @@
FROM continuumio/miniconda3:24.7.1-0 AS build FROM continuumio/miniconda3:24.7.1-0 AS build
ARG CUDA_VERSION=12.6.0 ARG CUDA_VERSION=12.8.1
ENV CUDA_VERSION=$CUDA_VERSION ENV CUDA_VERSION=$CUDA_VERSION
ARG PYTHON_VERSION=3.11.11 ARG PYTHON_VERSION=3.11.11
@@ -14,7 +14,7 @@ ENV CUDA_DOCKER_ARCH=all
ENV NVIDIA_VISIBLE_DEVICES=all ENV NVIDIA_VISIBLE_DEVICES=all
ENV CONDA_AUTO_UPDATE_CONDA="false" ENV CONDA_AUTO_UPDATE_CONDA="false"
ENV ALLTALK_DIR=/opt/alltalk ENV ALLTALK_DIR=/home/alltalk
############################################################################## ##############################################################################
# Installation/Basic Utilities # Installation/Basic Utilities
@@ -24,6 +24,7 @@ RUN <<EOR
apt-get upgrade -y apt-get upgrade -y
apt-get install --no-install-recommends -y \ apt-get install --no-install-recommends -y \
espeak-ng \ espeak-ng \
cron \
curl \ curl \
wget \ wget \
jq \ jq \
@@ -32,6 +33,10 @@ RUN <<EOR
apt-get clean && rm -rf /var/lib/apt/lists/* apt-get clean && rm -rf /var/lib/apt/lists/*
EOR EOR
# Create a non-root user and switch to it for security
RUN useradd -ms /bin/bash alltalk
USER alltalk
############################################################################## ##############################################################################
# Create a conda environment and install dependencies: # Create a conda environment and install dependencies:
############################################################################## ##############################################################################
@@ -48,7 +53,8 @@ RUN <<EOR
cuda-command-line-tools=${CUDA_VERSION} cuda-compiler=${CUDA_VERSION} cuda-runtime=${CUDA_VERSION} \ cuda-command-line-tools=${CUDA_VERSION} cuda-compiler=${CUDA_VERSION} cuda-runtime=${CUDA_VERSION} \
cuda-libraries=${CUDA_VERSION} cuda-libraries-dev=${CUDA_VERSION} cuda-libraries-static=${CUDA_VERSION} \ cuda-libraries=${CUDA_VERSION} cuda-libraries-dev=${CUDA_VERSION} cuda-libraries-static=${CUDA_VERSION} \
cuda-cudart=${CUDA_SHORT_VERSION} cuda-cudart_linux-64=${CUDA_SHORT_VERSION} \ cuda-cudart=${CUDA_SHORT_VERSION} cuda-cudart_linux-64=${CUDA_SHORT_VERSION} \
cudnn=9.3 \ conda-forge::nccl=2.27.5.1 \
cudnn=9.10 \
py-cpuinfo=9 \ py-cpuinfo=9 \
conda-forge::ffmpeg=7.1.0 \ conda-forge::ffmpeg=7.1.0 \
conda-forge::portaudio=19.7.0 \ conda-forge::portaudio=19.7.0 \
@@ -71,10 +77,23 @@ RUN <<EOR
conda activate alltalk conda activate alltalk
mkdir -p ${ALLTALK_DIR}/pip_cache mkdir -p ${ALLTALK_DIR}/pip_cache
pip install --no-cache-dir --cache-dir=${ALLTALK_DIR}/pip_cache \ pip install --no-cache-dir --cache-dir=${ALLTALK_DIR}/pip_cache \
torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cu${CUDA_SHORT_VERSION_NO_DOT} torch==2.7.1 torchvision==0.22.1 torchaudio==2.7.1 --index-url https://download.pytorch.org/whl/cu${CUDA_SHORT_VERSION_NO_DOT}
if [ $? -ne 0 ] ; then if [ $? -ne 0 ] ; then
echo "Failed to install pip dependencies: $RESULT" echo "Failed to install pip dependencies: $RESULT"
exit 1 exit 1
fi fi
EOR
RUN <<EOR
# See https://gist.github.com/xkortex/5ae49d7e6e969405bd2c3152a949c1f1
cat << EOF > ${ALLTALK_DIR}/conda_env.sh
#!/usr/bin/env bash
source ~/.bashrc
__conda_setup="\$('/opt/conda/bin/conda' 'shell.bash' 'hook' 2> /dev/null)"
eval "\$__conda_setup"
unset __conda_setup
conda activate alltalk
EOF
EOR EOR
+10 -4
View File
@@ -6,6 +6,7 @@ cd $SCRIPT_DIR
. ${SCRIPT_DIR=}/../variables.sh . ${SCRIPT_DIR=}/../variables.sh
DOCKER_TAG=latest DOCKER_TAG=latest
DOCKER_REPOSITORY=erew123
# Parse arguments # Parse arguments
while [ "$#" -gt 0 ]; do while [ "$#" -gt 0 ]; do
@@ -22,10 +23,12 @@ while [ "$#" -gt 0 ]; do
DOCKER_TAG="$2" DOCKER_TAG="$2"
shift shift
;; ;;
--github-repository) --docker-repository)
if [ -n "${GITHUB_REPOSITORY}" ] && ! [[ $GITHUB_REPOSITORY =~ ^--.* ]]; then if [ -n "$2" ] && ! [[ $2 =~ ^--.* ]]; then
GITHUB_REPOSITORY="$2" DOCKER_REPOSITORY="$2"
shift shift
else
DOCKER_REPOSITORY=""
fi fi
;; ;;
*) *)
@@ -36,6 +39,9 @@ while [ "$#" -gt 0 ]; do
shift shift
done done
# Append slash if missing:
DOCKER_REPOSITORY=$(echo "$DOCKER_REPOSITORY" | sed 's![^/]$!&/!')
echo "Building conda environment using python ${PYTHON_VERSION} with CUDA ${CUDA_VERSION}" echo "Building conda environment using python ${PYTHON_VERSION} with CUDA ${CUDA_VERSION}"
docker buildx \ docker buildx \
@@ -43,5 +49,5 @@ docker buildx \
--progress=plain \ --progress=plain \
--build-arg CUDA_VERSION=$CUDA_VERSION \ --build-arg CUDA_VERSION=$CUDA_VERSION \
--build-arg PYTHON_VERSION=$PYTHON_VERSION \ --build-arg PYTHON_VERSION=$PYTHON_VERSION \
-t ${GITHUB_REPOSITORY}alltalk_environment:${DOCKER_TAG} \ -t ${DOCKER_REPOSITORY}alltalk_tts_environment:${DOCKER_TAG} \
. .
+14 -27
View File
@@ -1,14 +1,11 @@
ARG GITHUB_REPOSITORY ARG DOCKER_REPOSITORY
FROM ${GITHUB_REPOSITORY}alltalk_environment:latest ARG DOCKER_TAG=latest
FROM ${DOCKER_REPOSITORY}alltalk_tts_environment:${DOCKER_TAG}
ARG DEEPSPEED_VERSION=0.16.3 ARG DEEPSPEED_VERSION=0.17.2
ENV DEEPSPEED_VERSION=$DEEPSPEED_VERSION ENV DEEPSPEED_VERSION=$DEEPSPEED_VERSION
############################################################################## WORKDIR ${ALLTALK_DIR}
# Directories:
##############################################################################
ENV STAGE_DIR=/tmp
RUN mkdir -p ${STAGE_DIR}
############################################################################## ##############################################################################
# Install python dependencies # Install python dependencies
@@ -26,40 +23,30 @@ EOR
# Clone DeepSpeed sources # Clone DeepSpeed sources
############################################################################## ##############################################################################
RUN <<EOR RUN <<EOR
git clone https://github.com/microsoft/DeepSpeed.git ${STAGE_DIR}/DeepSpeed git clone https://github.com/microsoft/DeepSpeed.git ${ALLTALK_DIR}/DeepSpeed
cd ${STAGE_DIR}/DeepSpeed cd ${ALLTALK_DIR}/DeepSpeed
git checkout . git checkout .
git checkout "tags/v${DEEPSPEED_VERSION}" -b "v${DEEPSPEED_VERSION}" git checkout "tags/v${DEEPSPEED_VERSION}" -b "v${DEEPSPEED_VERSION}"
# Patching some files due to bug https://github.com/microsoft/DeepSpeed/issues/6709
START_LINE=`awk '/#ifndef BF16_AVAILABLE/{ print NR; exit }' csrc/transformer/inference/csrc/gelu.cu`
TO_LINE=$((START_LINE + 2))
sed -i "${START_LINE},${TO_LINE}d" ./csrc/transformer/inference/csrc/gelu.cu
START_LINE=`awk '/#ifndef BF16_AVAILABLE/{ print NR; exit }' csrc/transformer/inference/csrc/transform.cu`
TO_LINE=$((START_LINE + 2))
sed -i "${START_LINE},${TO_LINE}d" ./csrc/transformer/inference/csrc/transform.cu
EOR EOR
############################################################################## ##############################################################################
# Create DeepSpeed build file # Create DeepSpeed build file
############################################################################## ##############################################################################
RUN <<EOR RUN <<EOR
cat << EOF > build_deepspeed.sh cat << EOF > ${ALLTALK_DIR}/build_deepspeed.sh
#!/usr/bin/env bash #!/usr/bin/env bash
source ~/.bashrc source ${ALLTALK_DIR}/conda_env.sh
mkdir -p /deepspeed mkdir -p ${ALLTALK_DIR}/build
conda activate alltalk cd ${ALLTALK_DIR}/DeepSpeed
cd ${STAGE_DIR}/DeepSpeed
DS_BUILD_OPS=1 python setup.py build_ext -j8 bdist_wheel DS_BUILD_OPS=1 python setup.py build_ext -j8 bdist_wheel
mv ${STAGE_DIR}/DeepSpeed/dist/*.whl /deepspeed/ mv ${ALLTALK_DIR}/DeepSpeed/dist/*.whl ${ALLTALK_DIR}/build
EOF EOF
EOR EOR
RUN chmod +x /build_deepspeed.sh RUN chmod +x ${ALLTALK_DIR}/build_deepspeed.sh
############################################################################## ##############################################################################
# Build DeepSpeed: # Build DeepSpeed:
############################################################################## ##############################################################################
ENTRYPOINT ["/build_deepspeed.sh"] ENTRYPOINT ["bash", "-c", "-l", "$ALLTALK_DIR/build_deepspeed.sh"]
+14 -7
View File
@@ -6,6 +6,8 @@ cd $SCRIPT_DIR
. ${SCRIPT_DIR=}/../variables.sh . ${SCRIPT_DIR=}/../variables.sh
DOCKER_TAG=latest DOCKER_TAG=latest
DOCKER_REPOSITORY=erew123
ALLTALK_DIR=/home/alltalk
# Parse arguments # Parse arguments
while [ "$#" -gt 0 ]; do while [ "$#" -gt 0 ]; do
@@ -22,10 +24,12 @@ while [ "$#" -gt 0 ]; do
DOCKER_TAG="$2" DOCKER_TAG="$2"
shift shift
;; ;;
--github-repository) --docker-repository)
if [ -n "${GITHUB_REPOSITORY}" ] && ! [[ $GITHUB_REPOSITORY =~ ^--.* ]]; then if [ -n "$2" ] && ! [[ $2 =~ ^--.* ]]; then
GITHUB_REPOSITORY="$2" DOCKER_REPOSITORY="$2"
shift shift
else
DOCKER_REPOSITORY=""
fi fi
;; ;;
*) *)
@@ -36,6 +40,9 @@ while [ "$#" -gt 0 ]; do
shift shift
done done
# Append slash if missing:
DOCKER_REPOSITORY=$(echo "$DOCKER_REPOSITORY" | sed 's![^/]$!&/!')
PYTHON_SHORT_VERSION=${PYTHON_VERSION%.*} PYTHON_SHORT_VERSION=${PYTHON_VERSION%.*}
PYTHON_VERSION_NO_DOT=${PYTHON_SHORT_VERSION//./} PYTHON_VERSION_NO_DOT=${PYTHON_SHORT_VERSION//./}
if [[ -n $(find build -name "deepspeed-${DEEPSPEED_VERSION}*-cp${PYTHON_VERSION_NO_DOT}-cp${PYTHON_VERSION_NO_DOT}-*.whl") ]] if [[ -n $(find build -name "deepspeed-${DEEPSPEED_VERSION}*-cp${PYTHON_VERSION_NO_DOT}-cp${PYTHON_VERSION_NO_DOT}-*.whl") ]]
@@ -51,9 +58,9 @@ mkdir -p build
docker buildx \ docker buildx \
build \ build \
--build-arg GITHUB_REPOSITORY=$GITHUB_REPOSITORY \ --build-arg DOCKER_REPOSITORY=$DOCKER_REPOSITORY \
--build-arg DEEPSPEED_VERSION=$DEEPSPEED_VERSION \ --build-arg DEEPSPEED_VERSION=$DEEPSPEED_VERSION \
-t ${GITHUB_REPOSITORY}alltalk_deepspeed:${DOCKER_TAG} \ -t ${DOCKER_REPOSITORY}alltalk_deepspeed:${DOCKER_TAG} \
. .
docker run \ docker run \
@@ -61,5 +68,5 @@ docker run \
-it \ -it \
--gpus=all \ --gpus=all \
--name deepspeed \ --name deepspeed \
-v $SCRIPT_DIR/build:/deepspeed \ -v $SCRIPT_DIR/build:${ALLTALK_DIR}/build \
${GITHUB_REPOSITORY}alltalk_deepspeed:${DOCKER_TAG} ${DOCKER_REPOSITORY}alltalk_deepspeed:${DOCKER_TAG}
+13 -5
View File
@@ -1,7 +1,15 @@
#!/usr/bin/env bash #!/usr/bin/env bash
export CUDA_VERSION=12.6.0 declare -A ALLTALK_VARS
export PYTHON_VERSION=3.11.11 ALLTALK_VARS["CUDA_VERSION"]=12.8.1
export DEEPSPEED_VERSION=0.16.2 ALLTALK_VARS["PYTHON_VERSION"]=3.11.11
export ALLTALK_DIR=/opt/alltalk ALLTALK_VARS["DEEPSPEED_VERSION"]=0.17.2
export GITHUB_REPOSITORY=
# Export single variables (needed by Docker locally)
for key in "${!ALLTALK_VARS[@]}"
do
export "${key}=${ALLTALK_VARS[${key}]}"
done
# Export the entire associative array (needed by Github action)
export ALLTALK_VARS
-7
View File
@@ -1,7 +0,0 @@
{
"delete_output_wavs": "1",
"rvc_settings": {
"rvc_enabled": true,
"f0method": "rmvpe"
}
}
+41
View File
@@ -0,0 +1,41 @@
{
"branding": $ENV.ALLTALK_BRANDING,
"delete_output_wavs": $ENV.ALLTALK_DELETE_OUTPUT_WAVS,
"gradio_interface": $ENV.ALLTALK_GRADIO_INTERFACE,
"output_folder": $ENV.ALLTALK_OUTPUT_FOLDER,
"gradio_port_number": $ENV.ALLTALK_GRADIO_PORT_NUMBER,
"launch_gradio": $ENV.ALLTALK_LAUNCH_GRADIO,
"transcode_audio_format": $ENV.ALLTALK_TRANSCODE_AUDIO_FORMAT,
"rvc_settings": {
"rvc_enabled": $ENV.ALLTALK_RVC_ENABLED,
"rvc_char_model_file": $ENV.ALLTALK_RVC_CHAR_MODEL_FILE,
"rvc_narr_model_file": $ENV.ALLTALK_RVC_NARR_MODEL_FILE,
"split_audio": $ENV.ALLTALK_RVC_SPLIT_AUDIO,
"autotune": $ENV.ALLTALK_RVC_AUTOTUNE,
"pitch": $ENV.ALLTALK_RVC_PITCH,
"filter_radius": $ENV.ALLTALK_RVC_FILTER_RADIUS,
"index_rate": $ENV.ALLTALK_RVC_INDEX_RATE,
"rms_mix_rate": $ENV.ALLTALK_RVC_RMS_MIX_RATE,
"protect": $ENV.ALLTALK_RVC_PROTECT,
"hop_length": $ENV.ALLTALK_RVC_HOP_LENGTH,
"f0method": $ENV.ALLTALK_RVC_F0METHOD,
"embedder_model": $ENV.ALLTALK_RVC_EMBEDDER_MODEL,
"training_data_size": $ENV.ALLTALK_RVC_TRAINING_DATA_SIZE,
"model_cache_size": $ENV.ALLTALK_RVC_MODEL_CACHE_SIZE
},
"api_def": {
"api_port_number": $ENV.ALLTALK_API_PORT_NUMBER,
"api_allowed_filter": $ENV.ALLTALK_API_ALLOWED_FILTER,
"api_length_stripping": $ENV.ALLTALK_API_LENGTH_STRIPPING,
"api_max_characters": $ENV.ALLTALK_API_MAX_CHARACTERS,
"api_use_legacy_api": $ENV.ALLTALK_API_USE_LEGACY_API,
"api_text_filtering": $ENV.ALLTALK_API_TEXT_FILTERING,
"api_narrator_enabled": $ENV.ALLTALK_API_NARRATOR_ENABLED,
"api_text_not_inside": $ENV.ALLTALK_API_TEXT_NOT_INSIDE,
"api_language": $ENV.ALLTALK_API_LANGUAGE,
"api_output_file_name": $ENV.ALLTALK_API_OUTPUT_FILE_NAME,
"api_output_file_timestamp": $ENV.ALLTALK_API_OUTPUT_FILE_TIMESTAMP,
"api_autoplay": $ENV.ALLTALK_API_AUTOPLAY,
"api_autoplay_volume": $ENV.ALLTALK_API_AUTOPLAY_VOLUME
}
}
+18
View File
@@ -0,0 +1,18 @@
{
"base_port": $ENV.ALLTALK_MEM_BASE_PORT,
"api_server_port": $ENV.ALLTALK_MEM_API_SERVER_PORT,
"auto_start_engines": $ENV.ALLTALK_MEM_AUTO_START_ENGINES,
"max_instances": $ENV.ALLTALK_MEM_MAX_INSTANCES,
"gradio_interface_port": $ENV.ALLTALK_MEM_GRADIO_INTERFACE_PORT,
"max_retries": $ENV.ALLTALK_MEM_MAX_RETRIES,
"initial_wait": $ENV.ALLTALK_MEM_INITIAL_WAIT,
"backoff_factor": $ENV.ALLTALK_MEM_BACKOFF_FACTOR,
"debug_mode": $ENV.ALLTALK_MEM_DEBUG_MODE,
"max_queue_time": $ENV.ALLTALK_MEM_MAX_QUEUE_TIME,
"queue_check_interval": $ENV.ALLTALK_MEM_QUEUE_CHECK_INTERVAL,
"tts_request_timeout": $ENV.ALLTALK_MEM_TTS_REQUEST_TIMEOUT,
"text_length_factor": $ENV.ALLTALK_MEM_TEXT_LENGTH_FACTOR,
"concurrent_request_factor": $ENV.ALLTALK_MEM_CONCURRENT_REQUEST_FACTOR,
"diminishing_factor": $ENV.ALLTALK_MEM_DIMINISHING_FACTOR,
"queue_position_factor": $ENV.ALLTALK_MEM_QUEUE_POSITION_FACTOR
}
+1
View File
@@ -0,0 +1 @@
{}
+1 -1
View File
@@ -4805,5 +4805,5 @@ if __name__ == "__main__":
share=False, share=False,
debug=False, debug=False,
server_port=7052, server_port=7052,
server_name="127.0.0.1", server_name="0.0.0.0",
) )
+18
View File
@@ -0,0 +1,18 @@
{
"base_port": 7001,
"api_server_port": 7851,
"auto_start_engines": 0,
"max_instances": 8,
"gradio_interface_port": 7500,
"max_retries": 12,
"initial_wait": 2.0,
"backoff_factor": 1.2,
"debug_mode": false,
"max_queue_time": 60,
"queue_check_interval": 0.1,
"tts_request_timeout": 30,
"text_length_factor": 0.2,
"concurrent_request_factor": 0.5,
"diminishing_factor": 0.5,
"queue_position_factor": 1.0
}
+12 -11
View File
@@ -1227,16 +1227,17 @@ else:
+ str(config.api_def.api_port_number) + str(config.api_def.api_port_number)
+ "\033[00m" + "\033[00m"
) )
print_message( if config.launch_gradio:
"\033[94mGradio Light:\033[00m \033[92mhttp://127.0.0.1:" print_message(
+ str(config.gradio_port_number) "\033[94mGradio Light:\033[00m \033[92mhttp://127.0.0.1:"
+ "\033[00m" + str(config.gradio_port_number)
) + "\033[00m"
print_message( )
"\033[94mGradio Dark :\033[00m \033[92mhttp://127.0.0.1:" print_message(
+ str(config.gradio_port_number) "\033[94mGradio Dark :\033[00m \033[92mhttp://127.0.0.1:"
+ "?__theme=dark\033[00m" + str(config.gradio_port_number)
) + "?__theme=dark\033[00m"
)
print_message("") print_message("")
print_message( print_message(
@@ -2038,7 +2039,7 @@ def process_audio_files(
"processing_date": datetime.now().isoformat(), "processing_date": datetime.now().isoformat(),
"validation_messages": validation_messages, "validation_messages": validation_messages,
}, },
f, file,
indent=4, indent=4,
) )
+53 -66
View File
@@ -41,9 +41,8 @@ Functions:
- **is_running_in_colab**: Detects if the script is running in Google Colab. - **is_running_in_colab**: Detects if the script is running in Google Colab.
- **is_running_in_docker**: Detects if the script is running in a Docker container. - **is_running_in_docker**: Detects if the script is running in a Docker container.
- **download_file**: Downloads a file with a progress bar and retry logic. - **download_file**: Downloads a file with a progress bar and retry logic.
- **setup_piper**: Sets up the Piper TTS engine and downloads associated files. - **setup_tts**: Sets up the TTS engine passed as argument.
- **setup_vits**: Sets up the VITS TTS engine and downloads associated files. - **download_tts_model**: Downloads the TTS engine's associated files.
- **setup_xtts**: Sets up the XTTS TTS engine and downloads associated files.
- **update_tts_engines**: Updates the configuration to reflect the selected TTS engine. - **update_tts_engines**: Updates the configuration to reflect the selected TTS engine.
- **set_firstrun_model_false**: Marks the initial setup as complete in the configuration. - **set_firstrun_model_false**: Marks the initial setup as complete in the configuration.
- **warning_message**: Displays post-setup warnings or tips for the user. - **warning_message**: Displays post-setup warnings or tips for the user.
@@ -65,6 +64,7 @@ import zipfile
import requests import requests
from tqdm import tqdm from tqdm import tqdm
from inputimeout import inputimeout, TimeoutOccurred from inputimeout import inputimeout, TimeoutOccurred
from urllib.parse import urlsplit
# Setting the module search path: # Setting the module search path:
sys.path.insert(0, str(Path(__file__).parent.parent.parent.resolve())) sys.path.insert(0, str(Path(__file__).parent.parent.parent.resolve()))
@@ -103,6 +103,11 @@ def download_file(url: str, dest_path: str, timeout: int = 30, retries: int = 3)
Raises: Raises:
ValueError: If the downloaded file size does not match the expected size. ValueError: If the downloaded file size does not match the expected size.
""" """
# Don't re-download if file already exists (which is the case when using Docker):
if Path(dest_path).exists():
print(f"File already exists at: {dest_path}. Skipping download.")
return True
progress_bar = None progress_bar = None
for attempt in range(1, retries + 1): for attempt in range(1, retries + 1):
try: try:
@@ -166,41 +171,44 @@ def download_file(url: str, dest_path: str, timeout: int = 30, retries: int = 3)
# Return False if all attempts fail # Return False if all attempts fail
return False return False
def setup_piper(): def unzip_compressed_files(downloaded_files: []):
"""Download base piper files""" for file in downloaded_files:
os.makedirs(this_dir / 'models/piper', exist_ok=True) _, file_extension = os.path.splitext(file)
os.makedirs(this_dir / 'models/xtts', exist_ok=True) if file_extension == '.zip':
download_file("https://huggingface.co/rhasspy/piper-voices/resolve/v1.0.0/en/en_US/ljspeech/high/en_US-ljspeech-high.onnx?download=true", with zipfile.ZipFile(file, 'r') as zip_ref:
this_dir / "models/piper/en_US-ljspeech-high.onnx") zip_ref.extractall(file.parent)
download_file("https://huggingface.co/rhasspy/piper-voices/resolve/v1.0.0/en/en_US/ljspeech/high/en_US-ljspeech-high.onnx.json?download=true.json", os.remove(file)
this_dir / "models/piper/en_US-ljspeech-high.onnx.json")
def setup_vits(): def setup_tts(tts_model: str, on_download_completed = unzip_compressed_files):
"""Download base vits files""" download_tts_model(tts_model, on_download_completed)
os.makedirs(this_dir / 'models/vits', exist_ok=True) update_tts_engines(tts_model)
os.makedirs(this_dir / 'models/xtts', exist_ok=True) set_firstrun_model_false()
zip_path = this_dir / 'models/vits/vits.zip'
download_file("https://coqui.gateway.scarf.sh/v0.6.1_models/tts_models--en--vctk--vits.zip", zip_path)
with zipfile.ZipFile(zip_path, 'r') as zip_ref:
zip_ref.extractall(this_dir / 'models/vits')
os.remove(zip_path)
def setup_xtts(): def download_tts_model(tts_model: str, on_download_completed = None):
"""Download base xtts files""" with open(os.path.join(this_dir, "system", "tts_engines", tts_model, "available_models.json")) as f:
os.makedirs(this_dir / 'models/xtts/xttsv2_2.0.3', exist_ok=True) available_models = json.load(f)
base_url = "https://huggingface.co/coqui/XTTS-v2/resolve/v2.0.3/" first_start_model = available_models["first_start_model"]
files = [ model = next(m for m in available_models["models"] if m["model_name"] == first_start_model)
"LICENSE.txt",
"README.md", # folder_path: Special case for vits: firstrun.py expects the ZIP download directly in the model folder.
"config.json", # When firstrun.py unzips the file, the correct folder will be created automatically.
"model.pth", folder_path = "" if tts_model == "vits" else model.get("folder_path", "")
"dvae.pth", files_to_download = model.get("files_to_download", []) or [model.get("github_rls_url", None)]
"mel_stats.pth", target_dir = this_dir / f"models/{tts_model}/{folder_path}"
"speakers_xtts.pth",
"vocab.json" if type(files_to_download) == dict:
] files_to_download = list(files_to_download.values())
for each_file in files:
download_file(base_url + each_file + "?download=true", this_dir / f"models/xtts/xttsv2_2.0.3/{each_file}") downloaded_files = []
for file_download in files_to_download:
urlpath = urlsplit(file_download).path
file_name = os.path.basename(urlpath)
target_file = target_dir / file_name
download_file(file_download, target_file)
downloaded_files.append(target_file)
if on_download_completed:
on_download_completed(downloaded_files)
def update_tts_engines(engine): def update_tts_engines(engine):
"""Set engine to users choice that matches the download""" """Set engine to users choice that matches the download"""
@@ -282,18 +290,9 @@ args = parser.parse_args()
if config.firstrun_model: if config.firstrun_model:
# Handle command-line argument for tts_model # Handle command-line argument for tts_model
if args.tts_model: if args.tts_model:
if args.tts_model == 'piper': if args.tts_model in {'piper', 'vits', 'xtts'}:
setup_piper() print(f"[{branding}TTS] Setting up model '{args.tts_model}'.")
update_tts_engines('piper') setup_tts(args.tts_model)
set_firstrun_model_false()
elif args.tts_model == 'vits':
setup_vits()
update_tts_engines('vits')
set_firstrun_model_false()
elif args.tts_model == 'xtts':
setup_xtts()
update_tts_engines('xtts')
set_firstrun_model_false()
elif args.tts_model == 'none': elif args.tts_model == 'none':
print(f"[{branding}TTS] No TTS model setup requested.") print(f"[{branding}TTS] No TTS model setup requested.")
print(f"[{branding}TTS] Setup completed for {args.tts_model}. Exiting.") print(f"[{branding}TTS] Setup completed for {args.tts_model}. Exiting.")
@@ -302,9 +301,7 @@ if config.firstrun_model:
# Check for Colab or Docker environment first # Check for Colab or Docker environment first
if is_running_in_colab() or is_running_in_docker(): if is_running_in_colab() or is_running_in_docker():
print(f"[{branding}TTS] Detected Colab/Docker environment - automatically selecting Piper") print(f"[{branding}TTS] Detected Colab/Docker environment - automatically selecting Piper")
setup_piper() setup_tts('piper')
update_tts_engines('piper')
set_firstrun_model_false()
warning_message() warning_message()
sys.exit() sys.exit()
@@ -363,20 +360,10 @@ if config.firstrun_model:
warning_message() warning_message()
if user_choice is None or user_choice.lower() != 'own': if user_choice is None or user_choice.lower() != 'own':
if selected_model['name'] == 'piper': if selected_model['name'] in {'piper', 'vits', 'xtts'}:
setup_piper() setup_tts(selected_model['name'])
update_tts_engines('piper') print(f"[{branding}TTS] {selected_model['name']} model downloaded and configuration updated successfully.")
elif selected_model['name'] == 'vits': warning_message()
setup_vits() sys.exit()
update_tts_engines('vits')
elif selected_model['name'] == 'xtts':
setup_xtts()
update_tts_engines('xtts')
print(f"[{branding}TTS] {selected_model['name']} model downloaded and configuration updated successfully.")
# Set firstrun_model to false
set_firstrun_model_false()
warning_message()
sys.exit()
else: else:
sys.exit() sys.exit()
+1 -1
View File
@@ -10,7 +10,7 @@ Jinja2>=3.1.0
librosa>=0.10.2.post1 librosa>=0.10.2.post1
local_attention==1.9.10 local_attention==1.9.10
onnxruntime-gpu>=1.18.0 onnxruntime-gpu>=1.18.0
pydantic>=2.7.0 pydantic==2.10.6
python-Levenshtein>=0.25.1 python-Levenshtein>=0.25.1
praat-parselmouth>=0.4.0 praat-parselmouth>=0.4.0
pyworld>=0.3.0 pyworld>=0.3.0
@@ -1,5 +1,7 @@
coqui-tts==0.24.3 coqui-tts==0.24.3
filelock>=3.13.4 filelock>=3.13.4
flask[async]==3.1.0
flask-cors==5.0.1
fuzzywuzzy>=0.18.0 fuzzywuzzy>=0.18.0
ffmpeg-python>=0.2.0 ffmpeg-python>=0.2.0
gradio==4.44.1 gradio==4.44.1
@@ -10,7 +12,7 @@ librosa>=0.10.2.post1
local_attention==1.9.10 local_attention==1.9.10
openai-whisper openai-whisper
onnxruntime-gpu>=1.18.1 onnxruntime-gpu>=1.18.1
pydantic>=2.8.2 pydantic==2.10.6
python-Levenshtein>=0.25.1 python-Levenshtein>=0.25.1
praat-parselmouth>=0.4.4 praat-parselmouth>=0.4.4
pyworld>=0.3.4 pyworld>=0.3.4
+1 -1
View File
@@ -9,7 +9,7 @@ librosa>=0.10.2.post1
local_attention>=1.9.0 local_attention>=1.9.0
onnxruntime-gpu>=1.18.0 onnxruntime-gpu>=1.18.0
openai-whisper openai-whisper
pydantic>=2.7.0 pydantic==2.10.6
python-Levenshtein>=0.25.1 python-Levenshtein>=0.25.1
praat-parselmouth>=0.4.0 praat-parselmouth>=0.4.0
pyworld>=0.3.0 pyworld>=0.3.0
@@ -1,4 +1,4 @@
filelock>=3.16.1 filelock>=3.16.1
pydantic>=2.7.0 pydantic==2.10.6
pytest>=8.3.3 pytest>=8.3.3
pytest-cov>=6.0.0 pytest-cov>=6.0.0
@@ -1,19 +1,19 @@
{ {
"first_start_model": "f5tts_v1", "first_start_model": "f5tts_v1a",
"models": [ "models": [
{ {
"model_name": "f5tts_v1", "model_name": "f5tts_v1a",
"folder_path": "f5tts_v1", "folder_path": "f5tts_v1a",
"files_to_download": { "files_to_download": {
"model_1200000.safetensors": "https://huggingface.co/SWivid/F5-TTS/resolve/main/F5TTS_Base/model_1200000.safetensors", "model_1250000.safetensors": "https://huggingface.co/SWivid/F5-TTS/resolve/main/F5TTS_v1_Base/model_1250000.safetensors",
"vocab.txt": "https://raw.githubusercontent.com/SWivid/F5-TTS/refs/heads/main/src/f5_tts/infer/examples/vocab.txt", "vocab.txt": "https://raw.githubusercontent.com/SWivid/F5-TTS/refs/heads/main/src/f5_tts/infer/examples/vocab.txt",
"vocos/config.yaml": "https://huggingface.co/charactr/vocos-mel-24khz/resolve/main/config.yaml", "vocos/config.yaml": "https://huggingface.co/charactr/vocos-mel-24khz/resolve/main/config.yaml",
"vocos/pytorch_model.bin": "https://huggingface.co/charactr/vocos-mel-24khz/resolve/main/pytorch_model.bin" "vocos/pytorch_model.bin": "https://huggingface.co/charactr/vocos-mel-24khz/resolve/main/pytorch_model.bin"
} }
}, },
{ {
"model_name": "e2tts_v1", "model_name": "e2tts_v1a",
"folder_path": "e2tts_v1", "folder_path": "e2tts_v1a",
"files_to_download": { "files_to_download": {
"model_1200000.safetensors": "https://huggingface.co/SWivid/E2-TTS/resolve/main/E2TTS_Base/model_1200000.safetensors", "model_1200000.safetensors": "https://huggingface.co/SWivid/E2-TTS/resolve/main/E2TTS_Base/model_1200000.safetensors",
"vocab.txt": "https://raw.githubusercontent.com/SWivid/F5-TTS/refs/heads/main/src/f5_tts/infer/examples/vocab.txt", "vocab.txt": "https://raw.githubusercontent.com/SWivid/F5-TTS/refs/heads/main/src/f5_tts/infer/examples/vocab.txt",
+104 -46
View File
@@ -722,34 +722,36 @@ class tts_class:
if clip_short: if clip_short:
# 1. try to find long silence for clipping # 1. try to find long silence for clipping
non_silent_segs = silence.split_on_silence( non_silent_segs = silence.split_on_silence(
aseg, min_silence_len=1000, silence_thresh=-50, keep_silence=1000 aseg, min_silence_len=1000, silence_thresh=-50, keep_silence=1000, seek_step=10
) )
non_silent_wave = AudioSegment.silent(duration=0) non_silent_wave = AudioSegment.silent(duration=0)
for non_silent_seg in non_silent_segs: for non_silent_seg in non_silent_segs:
if len(non_silent_wave) > 6000 and len(non_silent_wave + non_silent_seg) > 15000: if len(non_silent_wave) > 6000 and len(non_silent_wave + non_silent_seg) > 12000:
print(f"[{self.branding}ENG] Audio is over 15s, clipping short. (1)") if self.debug_tts else None print(f"[{self.branding}ENG] Audio is over 12s, clipping short. (1)") if self.debug_tts else None
break break
non_silent_wave += non_silent_seg non_silent_wave += non_silent_seg
# 2. try to find short silence for clipping if 1. failed # 2. try to find short silence for clipping if 1. failed
if len(non_silent_wave) > 15000: if len(non_silent_wave) > 12000:
non_silent_segs = silence.split_on_silence( non_silent_segs = silence.split_on_silence(
aseg, min_silence_len=100, silence_thresh=-40, keep_silence=1000 aseg, min_silence_len=100, silence_thresh=-40, keep_silence=1000, seek_step=10
) )
non_silent_wave = AudioSegment.silent(duration=0) non_silent_wave = AudioSegment.silent(duration=0)
for non_silent_seg in non_silent_segs: for non_silent_seg in non_silent_segs:
if len(non_silent_wave) > 6000 and len(non_silent_wave + non_silent_seg) > 15000: if len(non_silent_wave) > 6000 and len(non_silent_wave + non_silent_seg) > 12000:
print(f"[{self.branding}ENG] Audio is over 15s, clipping short. (2)") if self.debug_tts else None print(f"[{self.branding}ENG] Audio is over 12s, clipping short. (2)") if self.debug_tts else None
break break
non_silent_wave += non_silent_seg non_silent_wave += non_silent_seg
aseg = non_silent_wave aseg = non_silent_wave
# 3. if no proper silence found for clipping # 3. if no proper silence found for clipping
if len(aseg) > 15000: if len(aseg) > 12000:
aseg = aseg[:15000] aseg = aseg[:12000]
print(f"[{self.branding}ENG] Audio is over 15s, clipping short. (3)") if self.debug_tts else None print(f"[{self.branding}ENG] Audio is over 12s, clipping short. (3)") if self.debug_tts else None
# Add the silence removal function
aseg = self.remove_silence_edges(aseg) + AudioSegment.silent(duration=50)
aseg.export(f.name, format="wav") aseg.export(f.name, format="wav")
ref_audio = f.name ref_audio = f.name
@@ -762,6 +764,36 @@ class tts_class:
return ref_audio, ref_text return ref_audio, ref_text
def remove_silence_edges(self, audio, silence_threshold=-42):
try:
# Remove silence from the start
non_silent_start_idx = silence.detect_leading_silence(audio, silence_threshold=silence_threshold)
audio = audio[non_silent_start_idx:]
# Remove silence from the end
non_silent_end_duration = audio.duration_seconds
for ms in reversed(audio):
if ms.dBFS > silence_threshold:
break
non_silent_end_duration -= 0.001
trimmed_audio = audio[: int(non_silent_end_duration * 1000)]
return trimmed_audio
except Exception as e:
print(f"[{self.branding}ENG] Error in remove_silence_edges: {str(e)}") if self.debug_tts else None
return audio # Return original audio if there's an error
def remove_silence_for_generated_wav(self, filename):
aseg = AudioSegment.from_file(filename)
non_silent_segs = silence.split_on_silence(
aseg, min_silence_len=1000, silence_thresh=-50, keep_silence=500, seek_step=10
)
non_silent_wave = AudioSegment.silent(duration=0)
for non_silent_seg in non_silent_segs:
non_silent_wave += non_silent_seg
aseg = non_silent_wave
aseg.export(filename, format="wav")
async def infer_process( async def infer_process(
self, self,
ref_audio, ref_audio,
@@ -779,31 +811,44 @@ class tts_class:
device=None device=None
): ):
"""Process text and prepare for batch inference""" """Process text and prepare for batch inference"""
# Split the input text into batches try:
audio, sr = torchaudio.load(str(ref_audio)) # Split the input text into batches
max_chars = int(len(ref_text.encode("utf-8")) / (audio.shape[-1] / sr) * (25 - audio.shape[-1] / sr)) audio, sr = torchaudio.load(str(ref_audio))
gen_text_batches = self.chunk_text(gen_text, max_chars=max_chars) max_chars = int(len(ref_text.encode("utf-8")) / (audio.shape[-1] / sr) * (22 - audio.shape[-1] / sr))
gen_text_batches = self.chunk_text(gen_text, max_chars=max_chars)
for i, gen_text_batch in enumerate(gen_text_batches):
print(f"[{self.branding}ENG] gen_text {i}", gen_text_batch) if self.debug_tts else None for i, gen_text_batch in enumerate(gen_text_batches):
print(f"[{self.branding}ENG] gen_text {i}", gen_text_batch) if self.debug_tts else None
print(f"[{self.branding}ENG] Generating audio in {len(gen_text_batches)} batches...") if self.debug_tts else None print(f"[{self.branding}ENG] Generating audio in {len(gen_text_batches)} batches...") if self.debug_tts else None
return await self.infer_batch_process( result = await self.infer_batch_process(
(audio, sr), (audio, sr),
ref_text, ref_text,
gen_text_batches, gen_text_batches,
model_obj, model_obj,
vocoder, vocoder,
target_rms=target_rms, target_rms=target_rms,
cross_fade_duration=cross_fade_duration, cross_fade_duration=cross_fade_duration,
nfe_step=nfe_step, nfe_step=nfe_step,
cfg_strength=cfg_strength, cfg_strength=cfg_strength,
sway_sampling_coef=sway_sampling_coef, sway_sampling_coef=sway_sampling_coef,
speed=speed, speed=speed,
fix_duration=fix_duration, fix_duration=fix_duration,
device=device device=device
) )
if result is None:
print(f"[{self.branding}ENG] Error: No audio was generated") if self.debug_tts else None
# Return some default values to avoid unpacking errors
return np.zeros(1000), self.target_sample_rate, None
return result
except Exception as e:
print(f"[{self.branding}ENG] Error in infer_process: {str(e)}") if self.debug_tts else None
# Return some default values to avoid unpacking errors
return np.zeros(1000), self.target_sample_rate, None
async def infer_batch_process( async def infer_batch_process(
self, self,
@@ -853,6 +898,14 @@ class tts_class:
ref_text = ref_text + " " ref_text = ref_text + " "
for gen_text in gen_text_batches: for gen_text in gen_text_batches:
local_speed = speed
if len(gen_text.encode("utf-8")) < 10:
local_speed = 0.3
print(f"[{self.branding}Debug] Short text detected, using speed={local_speed}") if self.debug_tts else None
else:
print(f"[{self.branding}Debug] Using speed={local_speed}") if self.debug_tts else None
# Prepare the text # Prepare the text
text_list = [ref_text + gen_text] text_list = [ref_text + gen_text]
final_text_list = convert_char_to_pinyin(text_list) final_text_list = convert_char_to_pinyin(text_list)
@@ -861,10 +914,14 @@ class tts_class:
if fix_duration is not None: if fix_duration is not None:
duration = int(fix_duration * self.target_sample_rate / self.hop_length) duration = int(fix_duration * self.target_sample_rate / self.hop_length)
else: else:
# Calculate duration
ref_text_len = len(ref_text.encode("utf-8")) ref_text_len = len(ref_text.encode("utf-8"))
gen_text_len = len(gen_text.encode("utf-8")) gen_text_len = len(gen_text.encode("utf-8"))
duration = ref_audio_len + int(ref_audio_len / ref_text_len * gen_text_len / speed) print(f"[{self.branding}Debug] ref_text_len={ref_text_len}, gen_text_len={gen_text_len}") if self.debug_tts else None
print(f"[{self.branding}Debug] ref_audio_len={ref_audio_len}") if self.debug_tts else None
estimated_duration = ref_audio_len + int(ref_audio_len / ref_text_len * gen_text_len / local_speed)
print(f"[{self.branding}Debug] Estimated duration={estimated_duration} frames") if self.debug_tts else None
duration = ref_audio_len + int(ref_audio_len / ref_text_len * gen_text_len / local_speed)
# inference # inference
with torch.inference_mode(): with torch.inference_mode():
@@ -878,19 +935,13 @@ class tts_class:
) )
# Handle precision for post-processing # Handle precision for post-processing
if self.device == 'cuda': generated = generated.to(torch.float32)
generated = generated.half()
else:
generated = generated.float()
generated = generated[:, ref_audio_len:, :] generated = generated[:, ref_audio_len:, :]
generated_mel_spec = generated.permute(0, 2, 1) generated_mel_spec = generated.permute(0, 2, 1)
# Convert to the correct precision for vocoder # Convert to the correct precision for vocoder
if self.device == 'cuda': if device == 'cuda':
generated_mel_spec = generated_mel_spec.half() generated_mel_spec = generated_mel_spec.half()
else:
generated_mel_spec = generated_mel_spec.float()
generated_wave = vocoder.decode(generated_mel_spec) generated_wave = vocoder.decode(generated_mel_spec)
if rms < target_rms: if rms < target_rms:
@@ -903,6 +954,11 @@ class tts_class:
spectrograms.append(generated_mel_spec[0].cpu().numpy()) spectrograms.append(generated_mel_spec[0].cpu().numpy())
# Combine all generated waves with cross-fading # Combine all generated waves with cross-fading
if not generated_waves:
# Handle the case where no waves were generated
print(f"[{self.branding}ENG] Warning: No audio was generated") if self.debug_tts else None
return None, self.target_sample_rate, None
if cross_fade_duration <= 0: if cross_fade_duration <= 0:
final_wave = np.concatenate(generated_waves) final_wave = np.concatenate(generated_waves)
else: else:
@@ -938,7 +994,7 @@ class tts_class:
final_wave = new_wave final_wave = new_wave
# Create a combined spectrogram # Create a combined spectrogram
combined_spectrogram = np.concatenate(spectrograms, axis=1) combined_spectrogram = np.concatenate(spectrograms, axis=1) if spectrograms else None
return final_wave, self.target_sample_rate, combined_spectrogram return final_wave, self.target_sample_rate, combined_spectrogram
@@ -1083,6 +1139,8 @@ class tts_class:
) )
text = self.process_text_for_tts(text) text = self.process_text_for_tts(text)
speed_value = float(speed)
print(f"[{self.branding}Debug] Using speed setting: {speed_value}") if self.debug_tts else None
# Generate the audio using infer_process # Generate the audio using infer_process
final_wave, final_sample_rate, _ = await self.infer_process( final_wave, final_sample_rate, _ = await self.infer_process(
@@ -1096,7 +1154,7 @@ class tts_class:
nfe_step=self.nfe_step, nfe_step=self.nfe_step,
cfg_strength=self.cfg_strength, cfg_strength=self.cfg_strength,
sway_sampling_coef=self.sway_sampling_coef, sway_sampling_coef=self.sway_sampling_coef,
speed=float(speed), speed=speed_value,
fix_duration=None, fix_duration=None,
device=self.device device=self.device
) )
+1 -1
View File
@@ -25,7 +25,7 @@
"def_narrator_voice": "female_01.wav", "def_narrator_voice": "female_01.wav",
"deepspeed_enabled": false, "deepspeed_enabled": false,
"engine_installed": true, "engine_installed": true,
"generationspeed_set": 0.9, "generationspeed_set": 1,
"lowvram_enabled": true, "lowvram_enabled": true,
"pitch_set": 0, "pitch_set": 0,
"repetitionpenalty_set": 10, "repetitionpenalty_set": 10,
@@ -1,5 +1,5 @@
{ {
"first_start_model": "en_GB-jenny_dioco-medium", "first_start_model": "en_US-ljspeech-high",
"models": [ "models": [
{ {
"model_name": "ar_JO-kareem-low", "model_name": "ar_JO-kareem-low",
+3 -1
View File
@@ -9,6 +9,8 @@ import soundfile as sf
import librosa import librosa
from functools import lru_cache from functools import lru_cache
from config import AlltalkConfig
now_dir = os.getcwd() now_dir = os.getcwd()
sys.path.append(now_dir) sys.path.append(now_dir)
@@ -171,7 +173,7 @@ def voice_conversion(
return None, None return None, None
@lru_cache @lru_cache(maxsize=AlltalkConfig.get_instance().rvc_settings.model_cache_size)
def get_vc(weight_root, sid, file_index=None, training_data_size=10000, debug_rvc=False): def get_vc(weight_root, sid, file_index=None, training_data_size=10000, debug_rvc=False):
net_g = None net_g = None
branding="AllTalk " branding="AllTalk "
+21 -12
View File
@@ -1,20 +1,20 @@
import numpy as np import numpy as np
import re import re
import unicodedata import unicodedata
from fairseq import checkpoint_utils
from fairseq.data import Dictionary
import torch
# Next 2 lines are a fix for fairseq to be compatible with pytorch > 2.6
import sys
sys.path = [str(p) for p in sys.path]
from fairseq import checkpoint_utils
import torch
import logging import logging
logging.getLogger("fairseq").setLevel(logging.WARNING) logging.getLogger("fairseq").setLevel(logging.WARNING)
import sys import sys
import os
import numpy as np import numpy as np
import asyncio
import ffmpeg import ffmpeg
from pathlib import Path from pathlib import Path
import subprocess
# Get the parent directory of the current file # Get the parent directory of the current file
this_dir = Path(__file__).resolve().parent.parent.parent.parent.parent this_dir = Path(__file__).resolve().parent.parent.parent.parent.parent
@@ -56,7 +56,6 @@ def format_title(title):
def load_embedding(embedder_model): def load_embedding(embedder_model):
#print("EMBEDDER MODEL IS", embedder_model)
embedding_list = { embedding_list = {
"contentvec": "contentvec_base.pt", "contentvec": "contentvec_base.pt",
"hubert": "hubert_base.pt", "hubert": "hubert_base.pt",
@@ -65,20 +64,30 @@ def load_embedding(embedder_model):
try: try:
this_dir = Path(__file__).resolve().parent.parent.parent.parent.parent this_dir = Path(__file__).resolve().parent.parent.parent.parent.parent
model_path = this_dir / "models" / "rvc_base" / embedding_list[embedder_model] model_path = this_dir / "models" / "rvc_base" / embedding_list[embedder_model]
#print("MODEL PATH IS", model_path)
# Load model ensemble and task # Import Dictionary class from fairseq
torch.serialization.add_safe_globals([Dictionary]) from fairseq.data.dictionary import Dictionary
# For PyTorch 2.2+, try using add_safe_globals directly but with proper import
# of the Dictionary class from fairseq
try:
torch.serialization.add_safe_globals([Dictionary])
except (AttributeError, ImportError) as e:
# If add_safe_globals doesn't exist, we'll need to load with weights_only=False
# But that's a security risk with untrusted models
logging.warning("Could not use add_safe_globals, loading model with reduced security")
pass
# Load model
models = checkpoint_utils.load_model_ensemble_and_task( models = checkpoint_utils.load_model_ensemble_and_task(
[f"{model_path}"], [f"{model_path}"],
suffix="", suffix="",
) )
#print(f"Embedding model {embedder_model} loaded successfully.")
return models return models
except KeyError as e: except KeyError as e:
logging.error(f"Invalid embedder model name: {embedder_model}") logging.error(f"Invalid embedder model name: {embedder_model}")
raise ValueError(f"Invalid embedder model name: {embedder_model}") from e raise ValueError(f"Invalid embedder model name: {embedder_model}") from e
except Exception as e: except Exception as e:
logging.error(f"Error loading embedding model: {e}") logging.error(f"Error loading embedding model: {e}")
raise RuntimeError(f"Error loading embedding model: {e}") from e raise RuntimeError(f"Error loading embedding model: {e}") from e
+1
View File
@@ -267,6 +267,7 @@ class tts_class:
model_files = ["model_file.pth", "model.pth"] model_files = ["model_file.pth", "model.pth"]
found_any_models = False found_any_models = False
model_name = None
for subfolder in models_folder.iterdir(): for subfolder in models_folder.iterdir():
if subfolder.is_dir(): if subfolder.is_dir():
model_name = subfolder.name model_name = subfolder.name
+25 -1
View File
@@ -2,7 +2,7 @@ import os.path
import tempfile import tempfile
from unittest import TestCase from unittest import TestCase
from pathlib import Path from pathlib import Path
from config import AlltalkConfig, AlltalkTTSEnginesConfig, AlltalkNewEnginesConfig from config import AlltalkConfig, AlltalkTTSEnginesConfig, AlltalkNewEnginesConfig, AlltalkMultiEngineManagerConfig
class TestAlltalkConfig(TestCase): class TestAlltalkConfig(TestCase):
@@ -242,3 +242,27 @@ class TestAlltalkNewEnginesConfig(TestCase):
def test_no_private_fields(self): def test_no_private_fields(self):
for attr in self.new_engines_config.to_dict().keys(): for attr in self.new_engines_config.to_dict().keys():
self.assertTrue(not attr.startswith("_")) self.assertTrue(not attr.startswith("_"))
class TestAlltalkMultiEngineManagerConfig(TestCase):
def setUp(self):
self.mem_config = AlltalkMultiEngineManagerConfig.get_instance()
self.mem_config.reload()
def test_defaults(self):
self.assertEqual(self.mem_config.base_port, 7001)
self.assertEqual(self.mem_config.api_server_port, 7851)
self.assertEqual(self.mem_config.auto_start_engines, 0)
self.assertEqual(self.mem_config.max_instances, 8)
self.assertEqual(self.mem_config.gradio_interface_port, 7500)
self.assertEqual(self.mem_config.max_retries, 12)
self.assertEqual(self.mem_config.initial_wait, 2.0)
self.assertEqual(self.mem_config.backoff_factor, 1.2)
self.assertFalse(self.mem_config.debug_mode)
self.assertEqual(self.mem_config.max_queue_time, 60)
self.assertEqual(self.mem_config.queue_check_interval, 0.1)
self.assertEqual(self.mem_config.tts_request_timeout, 30)
self.assertEqual(self.mem_config.text_length_factor, 0.2)
self.assertEqual(self.mem_config.concurrent_request_factor, 0.5)
self.assertEqual(self.mem_config.diminishing_factor, 0.5)
self.assertEqual(self.mem_config.queue_position_factor, 1.0)
+142 -220
View File
@@ -40,15 +40,12 @@ import os
import io import io
import sys import sys
import time import time
import json
import queue import queue
import socket import socket
import random
import signal import signal
import asyncio import asyncio
import aiohttp import aiohttp
import requests import requests
import importlib
import threading import threading
import subprocess import subprocess
import gradio as gr import gradio as gr
@@ -58,43 +55,7 @@ from collections import deque
from datetime import datetime, timedelta from datetime import datetime, timedelta
from requests.exceptions import RequestException from requests.exceptions import RequestException
from werkzeug.serving import make_server, WSGIRequestHandler from werkzeug.serving import make_server, WSGIRequestHandler
from concurrent.futures import ThreadPoolExecutor from config import AlltalkMultiEngineManagerConfig, AlltalkConfig
def ensure_flask_dependencies():
# Check for Flask async support
try:
import flask.json
if not hasattr(flask.json, 'loads'):
raise ImportError("Flask async support not found")
except ImportError:
print("Flask async support not found. Installing...")
try:
subprocess.check_call([sys.executable, "-m", "pip", "install", "flask[async]"])
print("Flask async support installed successfully.")
import flask
importlib.reload(flask) # Reload the module to ensure fresh state after install
except subprocess.CalledProcessError as e:
print(f"Failed to install Flask async support. Error: {e}")
print("Please run 'pip install flask[async]' manually.")
sys.exit(1)
# Check for Flask-CORS
try:
import flask_cors
except ImportError:
print("Flask-CORS not found. Installing...")
try:
subprocess.check_call([sys.executable, "-m", "pip", "install", "flask-cors"])
print("Flask-CORS installed successfully.")
except subprocess.CalledProcessError as e:
print(f"Failed to install Flask-CORS. Error: {e}")
print("Please run 'pip install flask-cors' manually.")
sys.exit(1)
# Check Flask Dependencies
ensure_flask_dependencies()
# Flask app setup
from flask_cors import CORS, cross_origin from flask_cors import CORS, cross_origin
from flask import Flask, request, jsonify, send_from_directory, send_file from flask import Flask, request, jsonify, send_from_directory, send_file
@@ -120,47 +81,9 @@ flask_app = Flask(__name__)
CORS(flask_app) CORS(flask_app)
flask_app.config['OUTPUT_FOLDER'] = str(Path(os.getcwd()) / 'outputs') flask_app.config['OUTPUT_FOLDER'] = str(Path(os.getcwd()) / 'outputs')
# Default configuration
default_config = {
"base_port": 7001,
"api_server_port": 7851,
"auto_start_engines": 0,
"max_instances": 8,
"gradio_interface_port": 7500,
"max_retries": 12,
"initial_wait": 2,
"backoff_factor": 1.2,
"debug_mode": False,
"max_queue_time": 60, # Maximum time a request can wait in the queue (in seconds)
"queue_check_interval": 0.1, # Time between checks for available instances (in seconds)
"tts_request_timeout": 30, # Timeout for individual TTS requests (in seconds)
"text_length_factor": 0.2, # Increase timeout by 20% per 100 characters
"concurrent_request_factor": 0.5, # Increase timeout by 50% per concurrent request
"diminishing_factor": 0.5, # Reduce additional time for long-running requests by 50%
"queue_position_factor": 1.0 # Add 100% of base timeout for each queue position
}
def load_config():
if os.path.exists(CONFIG_FILE):
with open(CONFIG_FILE, 'r') as f:
loaded_config = json.load(f)
# Update loaded_config with any missing keys from default_config
for key, value in default_config.items():
if key not in loaded_config:
loaded_config[key] = value
# Ensure debug_mode is a boolean
loaded_config['debug_mode'] = bool(loaded_config['debug_mode'])
return loaded_config
return default_config.copy()
def save_config(config):
with open(CONFIG_FILE, 'w') as f:
json.dump(config, f, indent=2)
# Load configuration at the start of the script # Load configuration at the start of the script
config = load_config() config = AlltalkMultiEngineManagerConfig.get_instance()
current_base_port = config['base_port'] USE_GRADIO = AlltalkConfig.get_instance().gradio_interface
max_instances = config['max_instances']
def signal_handler(signum, frame): def signal_handler(signum, frame):
print("[AllTalk MEM] Interrupt received, shutting down...") print("[AllTalk MEM] Interrupt received, shutting down...")
@@ -201,9 +124,10 @@ print(f"[AllTalk MEM]")
print(f"[AllTalk MEM] \033[93m MEM is not intended for production use and\033[00m") print(f"[AllTalk MEM] \033[93m MEM is not intended for production use and\033[00m")
print(f"[AllTalk MEM] \033[93m there is NO support being offered on MEM\033[00m") print(f"[AllTalk MEM] \033[93m there is NO support being offered on MEM\033[00m")
print(f"[AllTalk MEM]") print(f"[AllTalk MEM]")
print(f"[AllTalk MEM] \033[94mAPI/Queue :\033[00m \033[92mhttp://127.0.0.1:{config['api_server_port']}/api/tts-generate\033[00m") print(f"[AllTalk MEM] \033[94mAPI/Queue :\033[00m \033[92mhttp://127.0.0.1:{config.api_server_port}/api/tts-generate\033[00m")
print(f"[AllTalk MEM] \033[94mGradio Light:\033[00m \033[92mhttp://127.0.0.1:{config['gradio_interface_port']}\033[00m") if USE_GRADIO:
print(f"[AllTalk MEM] \033[94mGradio Dark :\033[00m \033[92mhttp://127.0.0.1:{config['gradio_interface_port']}?__theme=dark\033[00m") print(f"[AllTalk MEM] \033[94mGradio Light:\033[00m \033[92mhttp://127.0.0.1:{config.gradio_interface_port}\033[00m")
print(f"[AllTalk MEM] \033[94mGradio Dark :\033[00m \033[92mhttp://127.0.0.1:{config.gradio_interface_port}?__theme=dark\033[00m")
print(f"[AllTalk MEM]") print(f"[AllTalk MEM]")
def is_port_in_use(port): def is_port_in_use(port):
@@ -267,21 +191,21 @@ def check_subprocess_status(instance_id, port):
# to allow for longer startup times of the TTS engine. # to allow for longer startup times of the TTS engine.
def retry_with_backoff(func): def retry_with_backoff(func):
retries = 0 retries = 0
wait_time = config['initial_wait'] wait_time = config.initial_wait
while retries < config['max_retries']: while retries < config.max_retries:
try: try:
result = func() result = func()
print(f"[AllTalk MEM] Operation successful on attempt {retries + 1}") if config['debug_mode'] else None print(f"[AllTalk MEM] Operation successful on attempt {retries + 1}") if config.debug_mode else None
return result return result
except RequestException as e: except RequestException as e:
retries += 1 retries += 1
print(f"[AllTalk MEM] Attempt {retries}") if config['debug_mode'] else None print(f"[AllTalk MEM] Attempt {retries}") if config.debug_mode else None
if retries == config['max_retries']: if retries == config.max_retries:
print(f"[AllTalk MEM] All {max_retries} attempts failed waiting for this instance of the Engine to load.") if config['debug_mode'] else None print(f"[AllTalk MEM] All {config.max_retries} attempts failed waiting for this instance of the Engine to load.") if config.debug_mode else None
return False return False
print(f"[AllTalk MEM] Retrying in {wait_time} seconds...") if config['debug_mode'] else None print(f"[AllTalk MEM] Retrying in {wait_time} seconds...") if config.debug_mode else None
time.sleep(wait_time) time.sleep(wait_time)
wait_time = round(wait_time * config['backoff_factor'], 1) wait_time = round(wait_time * config.backoff_factor, 1)
return False return False
def is_server_ready(port, timeout=3): def is_server_ready(port, timeout=3):
@@ -294,14 +218,14 @@ def is_server_ready(port, timeout=3):
def stop_all_instances(): def stop_all_instances():
instance_ids = list(processes.keys()) instance_ids = list(processes.keys())
results = ["❌ Not running"] * max_instances results = ["❌ Not running"] * config.max_instances
for instance_id in instance_ids: for instance_id in instance_ids:
stop_subprocess(instance_id) stop_subprocess(instance_id)
results[instance_id-1] = "❌ Not running" results[instance_id-1] = "❌ Not running"
return results return results
def update_all_statuses(base_port): def update_all_statuses():
return [check_subprocess_status(i, base_port + i - 1) for i in range(1, max_instances + 1)] return [check_subprocess_status(i, config.base_port + i - 1) for i in range(1, config.max_instances + 1)]
def count_running_instances(): def count_running_instances():
return sum(1 for p in processes.values() if p.poll() is None) return sum(1 for p in processes.values() if p.poll() is None)
@@ -336,7 +260,7 @@ def test_tts(engine, voice, text):
return None, "Please select both an engine and a voice" return None, "Please select both an engine and a voice"
try: try:
engine_num = int(engine.split()[1]) engine_num = int(engine.split()[1])
port = current_base_port + engine_num - 1 port = config.base_port + engine_num - 1
formatted_text = text.format(engine=engine_num, port=port, voice=voice) formatted_text = text.format(engine=engine_num, port=port, voice=voice)
audio_url, debug_msg = generate_tts(port, engine_num, formatted_text, voice) audio_url, debug_msg = generate_tts(port, engine_num, formatted_text, voice)
@@ -351,7 +275,7 @@ def test_tts(engine, voice, text):
# Gradio Code and functions # # Gradio Code and functions #
############################# #############################
def create_gradio_interface(): def create_gradio_interface():
global current_base_port, max_instances, config, monitor global monitor
monitor = MonitoringControl() monitor = MonitoringControl()
with gr.Blocks(title="AllTalk Multi Engine Manager", theme=gr.themes.Base()) as interface: with gr.Blocks(title="AllTalk Multi Engine Manager", theme=gr.themes.Base()) as interface:
with gr.Row(): with gr.Row():
@@ -371,17 +295,17 @@ def create_gradio_interface():
}""", show_api=False) }""", show_api=False)
with gr.Tab("Engine Management"): with gr.Tab("Engine Management"):
with gr.Row(): with gr.Row():
num_instances_input = gr.Slider(minimum=1, maximum=max_instances, step=1, value=1, label="Number of Instances to Start") num_instances_input = gr.Slider(minimum=1, maximum=config.max_instances, step=1, value=1, label="Number of Instances to Start")
start_MEMple_button = gr.Button("Start Instances") start_MEMple_button = gr.Button("Start Instances")
stop_all_button = gr.Button("Stop All Instances") stop_all_button = gr.Button("Stop All Instances")
refresh_button = gr.Button("Refresh Status") refresh_button = gr.Button("Refresh Status")
error_box = gr.Textbox(label="Error Messages", visible=False) error_box = gr.Textbox(label="Error Messages", visible=False)
instance_statuses = [] instance_statuses = []
for i in range(0, max_instances, 8): # Create rows with 8 instances each for i in range(0, config.max_instances, 8): # Create rows with 8 instances each
with gr.Row(): with gr.Row():
for j in range(8): for j in range(8):
if i + j < max_instances: if i + j < config.max_instances:
instance_id = i + j + 1 instance_id = i + j + 1
# Use a column for each engine instance to control layout # Use a column for each engine instance to control layout
@@ -396,15 +320,15 @@ def create_gradio_interface():
restart_btn = gr.Button("♻️ Re-St", size="sm", scale=1, min_width=10) restart_btn = gr.Button("♻️ Re-St", size="sm", scale=1, min_width=10)
# Define click behavior for each button # Define click behavior for each button
start_btn.click(lambda id=instance_id: start_and_update(id, current_base_port + id - 1), outputs=status) start_btn.click(lambda id=instance_id: start_and_update(id, config.base_port + id - 1), outputs=status)
stop_btn.click(lambda id=instance_id: stop_and_update(id), outputs=status) stop_btn.click(lambda id=instance_id: stop_and_update(id), outputs=status)
restart_btn.click(lambda id=instance_id: restart_and_update(id, current_base_port + id - 1), outputs=status) restart_btn.click(lambda id=instance_id: restart_and_update(id, config.base_port + id - 1), outputs=status)
instance_statuses.append(status) instance_statuses.append(status)
def refresh_and_update_slider(): def refresh_and_update_slider():
running_count = count_running_instances() running_count = count_running_instances()
statuses = update_all_statuses(current_base_port) statuses = update_all_statuses()
return [gr.update(value=running_count)] + statuses return [gr.update(value=running_count)] + statuses
refresh_button.click( refresh_button.click(
@@ -414,9 +338,9 @@ def create_gradio_interface():
def validate_and_start(num_instances): def validate_and_start(num_instances):
if num_instances < 1: if num_instances < 1:
return [gr.update(value=1)] + ["❌ Not running"] * max_instances + ["Invalid number of instances"] return [gr.update(value=1)] + ["❌ Not running"] * config.max_instances + ["Invalid number of instances"]
results = start_MEMple_instances(num_instances, current_base_port) results = start_MEMple_instances(num_instances)
running_count = count_running_instances() running_count = count_running_instances()
return [gr.update(value=running_count)] + results + [""] return [gr.update(value=running_count)] + results + [""]
@@ -463,7 +387,7 @@ def create_gradio_interface():
async def update_voice_selector(engine): async def update_voice_selector(engine):
if engine: if engine:
engine_num = int(engine.split()[1]) engine_num = int(engine.split()[1])
port = current_base_port + engine_num - 1 port = config.base_port + engine_num - 1
voices = await get_available_voices(port) voices = await get_available_voices(port)
debug_str = f"Fetched voices for Engine {engine_num} (Port {port}): {voices}" debug_str = f"Fetched voices for Engine {engine_num} (Port {port}): {voices}"
return gr.update(choices=voices, value=voices[0] if voices else None), debug_str return gr.update(choices=voices, value=voices[0] if voices else None), debug_str
@@ -489,17 +413,17 @@ def create_gradio_interface():
gr.Markdown("### 🟦 Engine Instance Management") gr.Markdown("### 🟦 Engine Instance Management")
with gr.Row(): with gr.Row():
base_port_input = gr.Number( base_port_input = gr.Number(
value=config['base_port'], value=config.base_port,
label=f"Starting Port Number (Current: {config['base_port']})", label=f"Starting Port Number (Current: {config.base_port})",
step=1 step=1
) )
max_instances_input = gr.Number( max_instances_input = gr.Number(
value=config['max_instances'], value=config.max_instances,
label=f"Maximum Engine Instances (Current: {config['max_instances']})", label=f"Maximum Engine Instances (Current: {config.max_instances})",
step=1 step=1
) )
auto_start_engines = gr.Number( auto_start_engines = gr.Number(
value=config['auto_start_engines'], value=config.auto_start_engines,
label="Auto-start Engine Instances (0 to disable)", label="Auto-start Engine Instances (0 to disable)",
step=1 step=1
) )
@@ -508,13 +432,13 @@ def create_gradio_interface():
gr.Markdown("### 🟨 Gradio Port & API Port") gr.Markdown("### 🟨 Gradio Port & API Port")
with gr.Row(): with gr.Row():
gradio_port_input = gr.Number( gradio_port_input = gr.Number(
value=config['gradio_interface_port'], value=config.gradio_interface_port,
label=f"Gradio Interface Port (Current: {config['gradio_interface_port']})", label=f"Gradio Interface Port (Current: {config.gradio_interface_port})",
step=1 step=1
) )
api_server_port_input = gr.Number( api_server_port_input = gr.Number(
value=config['api_server_port'], value=config.api_server_port,
label=f"API Server Port (Current: {config['api_server_port']})", label=f"API Server Port (Current: {config.api_server_port})",
step=1 step=1
) )
@@ -523,18 +447,18 @@ def create_gradio_interface():
gr.Markdown("### 🟥 Engine Start-up Time Contol") gr.Markdown("### 🟥 Engine Start-up Time Contol")
with gr.Row(): with gr.Row():
max_retries_input = gr.Number( max_retries_input = gr.Number(
value=config['max_retries'], value=config.max_retries,
label=f"Max Retries (Current: {config['max_retries']})", label=f"Max Retries (Current: {config.max_retries})",
step=1 step=1
) )
initial_wait_input = gr.Number( initial_wait_input = gr.Number(
value=config['initial_wait'], value=config.initial_wait,
label=f"Initial Wait (Current: {config['initial_wait']})", label=f"Initial Wait (Current: {config.initial_wait})",
step=0.1 step=0.1
) )
backoff_factor_input = gr.Number( backoff_factor_input = gr.Number(
value=config['backoff_factor'], value=config.backoff_factor,
label=f"Backoff Factor (Current: {config['backoff_factor']})", label=f"Backoff Factor (Current: {config.backoff_factor})",
step=0.1 step=0.1
) )
with gr.Column(scale=1): with gr.Column(scale=1):
@@ -542,7 +466,7 @@ def create_gradio_interface():
with gr.Row(): with gr.Row():
debug_mode_input = gr.Dropdown( debug_mode_input = gr.Dropdown(
choices=["Disabled", "Enabled"], choices=["Disabled", "Enabled"],
value="Enabled" if config['debug_mode'] else "Disabled", value="Enabled" if config.debug_mode else "Disabled",
label="Debug Mode" label="Debug Mode"
) )
@@ -551,41 +475,41 @@ def create_gradio_interface():
gr.Markdown("### 🟩 Core Queue Management") gr.Markdown("### 🟩 Core Queue Management")
with gr.Row(): with gr.Row():
max_queue_time_input = gr.Number( max_queue_time_input = gr.Number(
value=config['max_queue_time'], value=config.max_queue_time,
label=f"Max Queue Time (seconds) (Current: {config['max_queue_time']})", label=f"Max Queue Time (seconds) (Current: {config.max_queue_time})",
step=1 step=1
) )
queue_check_interval_input = gr.Number( queue_check_interval_input = gr.Number(
value=config['queue_check_interval'], value=config.queue_check_interval,
label=f"Queue Check Interval (seconds) (Current: {config['queue_check_interval']})", label=f"Queue Check Interval (seconds) (Current: {config.queue_check_interval})",
step=0.1 step=0.1
) )
with gr.Column(scale=3): with gr.Column(scale=3):
gr.Markdown("### 🟩 TTS Request Dynamic Timeout Factors") gr.Markdown("### 🟩 TTS Request Dynamic Timeout Factors")
with gr.Row(): with gr.Row():
tts_request_timeout_input = gr.Number( tts_request_timeout_input = gr.Number(
value=config['tts_request_timeout'], value=config.tts_request_timeout,
label=f"TTS Request Timeout (seconds) (Current: {config['tts_request_timeout']})", label=f"TTS Request Timeout (seconds) (Current: {config.tts_request_timeout})",
step=1 step=1
) )
text_length_factor_input = gr.Number( text_length_factor_input = gr.Number(
value=config['text_length_factor'], value=config.text_length_factor,
label=f"Text Length Factor Per 100 chrs (Current: {config['text_length_factor']})", label=f"Text Length Factor Per 100 chrs (Current: {config.text_length_factor})",
step=0.1 step=0.1
) )
concurrent_request_factor_input = gr.Number( concurrent_request_factor_input = gr.Number(
value=config['concurrent_request_factor'], value=config.concurrent_request_factor,
label=f"Concurrent Request Factor (Current: {config['concurrent_request_factor']})", label=f"Concurrent Request Factor (Current: {config.concurrent_request_factor})",
step=0.1 step=0.1
) )
diminishing_factor_input = gr.Number( diminishing_factor_input = gr.Number(
value=config['diminishing_factor'], value=config.diminishing_factor,
label=f"Diminishing Factor Calc (Current: {config['diminishing_factor']})", label=f"Diminishing Factor Calc (Current: {config.diminishing_factor})",
step=0.1 step=0.1
) )
queue_position_factor_input = gr.Number( queue_position_factor_input = gr.Number(
value=config['queue_position_factor'], value=config.queue_position_factor,
label=f"Queue Position Factor (Current: {config['queue_position_factor']})", label=f"Queue Position Factor (Current: {config.queue_position_factor})",
step=0.1 step=0.1
) )
@@ -599,69 +523,63 @@ def create_gradio_interface():
new_queue_check_interval, new_tts_request_timeout, new_queue_check_interval, new_tts_request_timeout,
new_text_length_factor, new_concurrent_request_factor, new_text_length_factor, new_concurrent_request_factor,
new_diminishing_factor, new_queue_position_factor): new_diminishing_factor, new_queue_position_factor):
global current_base_port, max_instances, config, server_port
if new_port < 1024: if new_port < 1024:
new_port = 7001 new_port = 7001
new_auto_start = min(int(new_auto_start), int(new_max_instances)) new_auto_start = min(int(new_auto_start), int(new_max_instances))
current_base_port = int(new_port) config.base_port = int(new_port)
max_instances = int(new_max_instances) config.max_instances = int(new_max_instances)
server_port = int(new_gradio_port) config.auto_start_engines = new_auto_start
config.gradio_interface_port = int(new_gradio_port)
config.api_server_port = int(new_api_server_port)
config.max_retries = int(new_max_retries)
config.initial_wait = float(new_initial_wait)
config.backoff_factor = float(new_backoff_factor)
config.debug_mode = (new_debug_mode == "Enabled")
config.max_queue_time = int(new_max_queue_time)
config.queue_check_interval = float(new_queue_check_interval)
config.tts_request_timeout = int(new_tts_request_timeout)
config.text_length_factor = float(new_text_length_factor)
config.concurrent_request_factor = float(new_concurrent_request_factor)
config.diminishing_factor = float(new_diminishing_factor)
config.queue_position_factor = float(new_queue_position_factor)
config['base_port'] = current_base_port config.save()
config['max_instances'] = max_instances
config['auto_start_engines'] = new_auto_start
config['gradio_interface_port'] = server_port
config['api_server_port'] = int(new_api_server_port)
config['max_retries'] = int(new_max_retries)
config['initial_wait'] = float(new_initial_wait)
config['backoff_factor'] = float(new_backoff_factor)
config['debug_mode'] = (new_debug_mode == "Enabled")
config['max_queue_time'] = int(new_max_queue_time)
config['queue_check_interval'] = float(new_queue_check_interval)
config['tts_request_timeout'] = int(new_tts_request_timeout)
config['text_length_factor'] = float(new_text_length_factor)
config['concurrent_request_factor'] = float(new_concurrent_request_factor)
config['diminishing_factor'] = float(new_diminishing_factor)
config['queue_position_factor'] = float(new_queue_position_factor)
save_config(config)
status_message = ( status_message = (
f"Settings updated: Start port: {current_base_port}, Max instances: {max_instances}, " f"Settings updated: Start port: {config.base_port}, Max instances: {config.max_instances}, "
f"Auto-start engines: {config['auto_start_engines']}, Gradio port: {server_port}, " f"Auto-start engines: {config.auto_start_engines}, Gradio port: {config.gradio_interface_port}, "
f"API Server Port: {config['api_server_port']}, " f"API Server Port: {config.api_server_port}, "
f"Max retries: {config['max_retries']}, Initial wait: {config['initial_wait']}, " f"Max retries: {config.max_retries}, Initial wait: {config.initial_wait}, "
f"Backoff factor: {config['backoff_factor']}, Debug mode: {config['debug_mode']}, " f"Backoff factor: {config.backoff_factor}, Debug mode: {config.debug_mode}, "
f"Max queue time: {config['max_queue_time']}, " f"Max queue time: {config.max_queue_time}, "
f"Queue check interval: {config['queue_check_interval']}, " f"Queue check interval: {config.queue_check_interval}, "
f"TTS request timeout: {config['tts_request_timeout']}, " f"TTS request timeout: {config.tts_request_timeout}, "
f"Text length factor: {config['text_length_factor']}, " f"Text length factor: {config.text_length_factor}, "
f"Concurrent request factor: {config['concurrent_request_factor']}, " f"Concurrent request factor: {config.concurrent_request_factor}, "
f"Diminishing factor: {config['diminishing_factor']}, " f"Diminishing factor: {config.diminishing_factor}, "
f"Queue position factor: {config['queue_position_factor']}" f"Queue position factor: {config.queue_position_factor}"
) )
return ( return (
status_message, status_message,
gr.update(value=current_base_port, label=f"Starting Port Number (Current: {current_base_port})"), gr.update(value=config.base_port, label=f"Starting Port Number (Current: {config.base_port})"),
gr.update(value=max_instances, label=f"Maximum Instances (Current: {max_instances})"), gr.update(value=config.max_instances, label=f"Maximum Instances (Current: {config.max_instances})"),
gr.update(value=config['auto_start_engines']), gr.update(value=config.auto_start_engines),
gr.update(value=server_port, label=f"Gradio Interface Port (Current: {server_port})"), gr.update(value=config.gradio_interface_port, label=f"Gradio Interface Port (Current: {config.gradio_interface_port})"),
gr.update(value=config['api_server_port'], label=f"API Server Port (Current: {config['api_server_port']})"), gr.update(value=config.api_server_port, label=f"API Server Port (Current: {config.api_server_port})"),
gr.update(value=config['max_retries']), gr.update(value=config.max_retries),
gr.update(value=config['initial_wait']), gr.update(value=config.initial_wait),
gr.update(value=config['backoff_factor']), gr.update(value=config.backoff_factor),
gr.update(value="Enabled" if config['debug_mode'] else "Disabled"), gr.update(value="Enabled" if config.debug_mode else "Disabled"),
gr.update(value=config['max_queue_time'], label=f"Max Queue Time (seconds) (Current: {config['max_queue_time']})"), gr.update(value=config.max_queue_time, label=f"Max Queue Time (seconds) (Current: {config.max_queue_time})"),
gr.update(value=config['queue_check_interval'], label=f"Queue Check Interval (seconds) (Current: {config['queue_check_interval']})"), gr.update(value=config.queue_check_interval, label=f"Queue Check Interval (seconds) (Current: {config.queue_check_interval})"),
gr.update(value=config['tts_request_timeout'], label=f"TTS Request Timeout (seconds) (Current: {config['tts_request_timeout']})"), gr.update(value=config.tts_request_timeout, label=f"TTS Request Timeout (seconds) (Current: {config.tts_request_timeout})"),
gr.update(value=config['text_length_factor'], label=f"Text Length Factor (Current: {config['text_length_factor']})"), gr.update(value=config.text_length_factor, label=f"Text Length Factor (Current: {config.text_length_factor})"),
gr.update(value=config['concurrent_request_factor'], label=f"Concurrent Request Factor (Current: {config['concurrent_request_factor']})"), gr.update(value=config.concurrent_request_factor, label=f"Concurrent Request Factor (Current: {config.concurrent_request_factor})"),
gr.update(value=config['diminishing_factor'], label=f"Diminishing Factor (Current: {config['diminishing_factor']})"), gr.update(value=config.diminishing_factor, label=f"Diminishing Factor (Current: {config.diminishing_factor})"),
gr.update(value=config['queue_position_factor'], label=f"Queue Position Factor (Current: {config['queue_position_factor']})") gr.update(value=config.queue_position_factor, label=f"Queue Position Factor (Current: {config.queue_position_factor})")
) )
set_settings_button.click( set_settings_button.click(
@@ -866,7 +784,7 @@ def create_gradio_interface():
queue_data = [] queue_data = []
for i, item in enumerate(list(queue_items)[:10]): for i, item in enumerate(list(queue_items)[:10]):
wait_time = datetime.now() - item.start_time wait_time = datetime.now() - item.start_time
timeout = timedelta(seconds=config['max_queue_time']) - wait_time timeout = timedelta(seconds=config.max_queue_time) - wait_time
queue_data.append([ queue_data.append([
i+1, i+1,
str(wait_time).split(".")[0], str(wait_time).split(".")[0],
@@ -1114,19 +1032,19 @@ def restart_and_update(instance_id, port):
result = restart_subprocess(instance_id, port) result = restart_subprocess(instance_id, port)
return check_subprocess_status(instance_id, port) return check_subprocess_status(instance_id, port)
def start_MEMple_instances(num_instances, base_port): def start_MEMple_instances(num_instances):
currently_running = count_running_instances() currently_running = count_running_instances()
if currently_running >= num_instances: if currently_running >= num_instances:
return update_all_statuses(base_port) # All requested instances are already running return update_all_statuses() # All requested instances are already running
instances_to_start = num_instances - currently_running instances_to_start = num_instances - currently_running
start_port, available_ports = find_available_port(base_port + currently_running, instances_to_start) start_port, available_ports = find_available_port(config.base_port + currently_running, instances_to_start)
if len(available_ports) < instances_to_start: if len(available_ports) < instances_to_start:
return ["Not enough available ports"] * max_instances return ["Not enough available ports"] * config.max_instances
results = update_all_statuses(base_port) results = update_all_statuses()
for i, port in enumerate(available_ports[:instances_to_start], start=currently_running+1): for i, port in enumerate(available_ports[:instances_to_start], start=currently_running+1):
result = start_subprocess(i, port) result = start_subprocess(i, port)
results[i-1] = result results[i-1] = result
@@ -1152,8 +1070,8 @@ def initialize_instances():
global tts_instances global tts_instances
with lock: with lock:
tts_instances.clear() tts_instances.clear()
for i in range(1, max_instances + 1): for i in range(1, config.max_instances + 1):
port = current_base_port + i - 1 port = config.base_port + i - 1
tts_instances[i] = {"port": port, "locked": False} tts_instances[i] = {"port": port, "locked": False}
def get_available_instance(): def get_available_instance():
@@ -1183,23 +1101,23 @@ def update_tts_instances():
tts_instances.clear() tts_instances.clear()
for i, process in processes.items(): for i, process in processes.items():
if process.poll() is None: # Check if the process is running if process.poll() is None: # Check if the process is running
port = current_base_port + i - 1 port = config.base_port + i - 1
tts_instances[i] = {"port": port, "locked": False} tts_instances[i] = {"port": port, "locked": False}
def calculate_dynamic_timeout(text, concurrent_requests, queue_position, total_queue_length, request_start_time): def calculate_dynamic_timeout(text, concurrent_requests, queue_position, total_queue_length, request_start_time):
base_timeout = config['tts_request_timeout'] * (1 + (concurrent_requests * 0.5)) base_timeout = config.tts_request_timeout * (1 + (concurrent_requests * 0.5))
text_length_factor = len(text) / 100 text_length_factor = len(text) / 100
adjusted_timeout = base_timeout * (1 + (text_length_factor * 0.2)) adjusted_timeout = base_timeout * (1 + (text_length_factor * 0.2))
time_in_process = time.time() - request_start_time time_in_process = time.time() - request_start_time
diminishing_factor = max(0, 1 - (time_in_process / base_timeout)) diminishing_factor = max(0, 1 - (time_in_process / base_timeout))
timeout_with_diminishing = adjusted_timeout * (1 + (diminishing_factor * config['diminishing_factor'])) timeout_with_diminishing = adjusted_timeout * (1 + (diminishing_factor * config.diminishing_factor))
queue_position_factor = queue_position / total_queue_length queue_position_factor = queue_position / total_queue_length
final_timeout = timeout_with_diminishing + (config['tts_request_timeout'] * queue_position_factor) final_timeout = timeout_with_diminishing + (config.tts_request_timeout * queue_position_factor)
return min(final_timeout, config['max_queue_time']) # Ensure we don't exceed max queue time return min(final_timeout, config.max_queue_time) # Ensure we don't exceed max queue time
######################################## ########################################
## WEBSERVER # Client Facing Endpoints # ## WEBSERVER # Client Facing Endpoints #
@@ -1207,7 +1125,7 @@ def calculate_dynamic_timeout(text, concurrent_requests, queue_position, total_q
@flask_app.route('/api/tts-generate', methods=['POST']) @flask_app.route('/api/tts-generate', methods=['POST'])
@cross_origin(origins='*', methods=['POST', 'OPTIONS'], allow_headers=['Content-Type']) @cross_origin(origins='*', methods=['POST', 'OPTIONS'], allow_headers=['Content-Type'])
def tts_generate(): def tts_generate():
print("[AllTalk MEM] Received TTS generate request") if config['debug_mode'] else None print("[AllTalk MEM] Received TTS generate request") if config.debug_mode else None
start_time = datetime.now() start_time = datetime.now()
request_data = request.form.to_dict() request_data = request.form.to_dict()
@@ -1215,11 +1133,11 @@ def tts_generate():
queue_item = QueueItem(request_data['text_input'], start_time) queue_item = QueueItem(request_data['text_input'], start_time)
queue_items.append(queue_item) queue_items.append(queue_item)
while (datetime.now() - start_time).total_seconds() < config['max_queue_time']: while (datetime.now() - start_time).total_seconds() < config.max_queue_time:
instance = get_available_instance() instance = get_available_instance()
if instance: if instance:
try: try:
print(f"[AllTalk MEM] Processing request with instance {instance}") if config['debug_mode'] else None print(f"[AllTalk MEM] Processing request with instance {instance}") if config.debug_mode else None
# Update engine status # Update engine status
engine_statuses[instance] = EngineStatus() engine_statuses[instance] = EngineStatus()
@@ -1236,7 +1154,7 @@ def tts_generate():
) )
result = process_tts_request(instance, request_data, dynamic_timeout) result = process_tts_request(instance, request_data, dynamic_timeout)
print(f"[AllTalk MEM] Request processed, result: {result}") if config['debug_mode'] else None print(f"[AllTalk MEM] Request processed, result: {result}") if config.debug_mode else None
# Remove request from queue # Remove request from queue
queue_items.popleft() queue_items.popleft()
@@ -1249,7 +1167,7 @@ def tts_generate():
print(f"[AllTalk MEM] Error processing request: {str(e)}") print(f"[AllTalk MEM] Error processing request: {str(e)}")
release_instance(instance) release_instance(instance)
return jsonify({"status": "error", "message": str(e)}) return jsonify({"status": "error", "message": str(e)})
time.sleep(config['queue_check_interval']) time.sleep(config.queue_check_interval)
# Remove request from queue if it times out # Remove request from queue if it times out
queue_items.popleft() queue_items.popleft()
@@ -1270,7 +1188,7 @@ def process_tts_request(instance, data, timeout):
@flask_app.route('/v1/audio/speech', methods=['POST']) @flask_app.route('/v1/audio/speech', methods=['POST'])
@cross_origin(origins='*', methods=['POST', 'OPTIONS'], allow_headers=['Content-Type']) @cross_origin(origins='*', methods=['POST', 'OPTIONS'], allow_headers=['Content-Type'])
def openai_speech(): def openai_speech():
print("[AllTalk MEM] Received OpenAI-style TTS generate request") if config['debug_mode'] else None print("[AllTalk MEM] Received OpenAI-style TTS generate request") if config.debug_mode else None
start_time = datetime.now() start_time = datetime.now()
request_data = request.json request_data = request.json
@@ -1278,11 +1196,11 @@ def openai_speech():
queue_item = QueueItem(request_data['input'], start_time) queue_item = QueueItem(request_data['input'], start_time)
queue_items.append(queue_item) queue_items.append(queue_item)
while (datetime.now() - start_time).total_seconds() < config['max_queue_time']: while (datetime.now() - start_time).total_seconds() < config.max_queue_time:
instance = get_available_instance() instance = get_available_instance()
if instance: if instance:
try: try:
print(f"[AllTalk MEM] Processing OpenAI-style request with instance {instance}") if config['debug_mode'] else None print(f"[AllTalk MEM] Processing OpenAI-style request with instance {instance}") if config.debug_mode else None
# Update engine status # Update engine status
engine_statuses[instance] = EngineStatus() engine_statuses[instance] = EngineStatus()
@@ -1299,7 +1217,7 @@ def openai_speech():
) )
result = process_openai_tts_request(instance, request_data, dynamic_timeout) result = process_openai_tts_request(instance, request_data, dynamic_timeout)
print(f"[AllTalk MEM] OpenAI-style request processed, result: {result}") if config['debug_mode'] else None print(f"[AllTalk MEM] OpenAI-style request processed, result: {result}") if config.debug_mode else None
# Remove request from queue # Remove request from queue
queue_items.popleft() queue_items.popleft()
@@ -1322,7 +1240,7 @@ def openai_speech():
release_instance(instance) release_instance(instance)
return jsonify({"error": str(e)}), 500 return jsonify({"error": str(e)}), 500
time.sleep(config['queue_check_interval']) time.sleep(config.queue_check_interval)
# Remove request from queue if it times out # Remove request from queue if it times out
queue_items.popleft() queue_items.popleft()
@@ -1427,7 +1345,7 @@ async def get_available_voices(port):
async def update_voice_selector(engine): async def update_voice_selector(engine):
if engine: if engine:
engine_num = int(engine.split()[1]) engine_num = int(engine.split()[1])
port = current_base_port + engine_num - 1 port = config.base_port + engine_num - 1
voices = await get_available_voices(port) voices = await get_available_voices(port)
debug_str = f"Fetched voices for Engine {engine_num} (Port {port}): {voices}" debug_str = f"Fetched voices for Engine {engine_num} (Port {port}): {voices}"
return gr.update(choices=voices, value=voices[0] if voices else None), debug_str return gr.update(choices=voices, value=voices[0] if voices else None), debug_str
@@ -1437,7 +1355,7 @@ async def update_voice_selector(engine):
## WEBSERVER # Load Testing # ## WEBSERVER # Load Testing #
############################# #############################
async def run_load_test_gradio(num_requests, base_text, voice): async def run_load_test_gradio(num_requests, base_text, voice):
api_url = f"http://127.0.0.1:{config['api_server_port']}/api/tts-generate" api_url = f"http://127.0.0.1:{config.api_server_port}/api/tts-generate"
start_time = time.time() start_time = time.time()
async with aiohttp.ClientSession() as session: async with aiohttp.ClientSession() as session:
@@ -1455,7 +1373,7 @@ async def run_load_test_gradio(num_requests, base_text, voice):
results = [] results = []
for i, response in enumerate(responses): for i, response in enumerate(responses):
if response and response.get("status") == "generate-success": if response and response.get("status") == "generate-success":
audio_url = f"http://127.0.0.1:{config['api_server_port']}{response['output_file_url']}" audio_url = f"http://127.0.0.1:{config.api_server_port}{response['output_file_url']}"
# Round time to 1 decimal place # Round time to 1 decimal place
rounded_time = round(response['time'], 1) rounded_time = round(response['time'], 1)
results.append([f"Request {i+1}", rounded_time, "Success", audio_url]) results.append([f"Request {i+1}", rounded_time, "Success", audio_url])
@@ -1505,12 +1423,12 @@ class SilentWSGIRequestHandler(WSGIRequestHandler):
class ServerThread(threading.Thread): class ServerThread(threading.Thread):
def __init__(self, app): def __init__(self, app):
threading.Thread.__init__(self) threading.Thread.__init__(self)
self.server = make_server('0.0.0.0', config['api_server_port'], app, threaded=True, request_handler=SilentWSGIRequestHandler) self.server = make_server('0.0.0.0', config.api_server_port, app, threaded=True, request_handler=SilentWSGIRequestHandler)
self.ctx = app.app_context() self.ctx = app.app_context()
self.ctx.push() self.ctx.push()
def run(self): def run(self):
print(f"[AllTalk MEM] Starting API server on port {config['api_server_port']}") if config['debug_mode'] else None print(f"[AllTalk MEM] Starting API server on port {config.api_server_port}") if config.debug_mode else None
self.server.serve_forever() self.server.serve_forever()
def shutdown(self): def shutdown(self):
@@ -1520,7 +1438,7 @@ def start_api_server():
global server_thread global server_thread
server_thread = ServerThread(flask_app) # Use flask_app here server_thread = ServerThread(flask_app) # Use flask_app here
server_thread.start() server_thread.start()
print(f"[AllTalk MEM] API server thread started on port {config['api_server_port']}") if config['debug_mode'] else None print(f"[AllTalk MEM] API server thread started on port {config.api_server_port}") if config.debug_mode else None
def stop_api_server(): def stop_api_server():
global server_thread global server_thread
@@ -1563,10 +1481,10 @@ print(f"[AllTalk MEM] ")
print(f"[AllTalk MEM] MEM Server Ready") print(f"[AllTalk MEM] MEM Server Ready")
def auto_start_engines(): def auto_start_engines():
num_engines = config['auto_start_engines'] num_engines = config.auto_start_engines
if num_engines > 0: if num_engines > 0:
print(f"[AllTalk MEM] Auto-starting {num_engines} engines...") print(f"[AllTalk MEM] Auto-starting {num_engines} engines...")
results = start_MEMple_instances(num_engines, current_base_port) results = start_MEMple_instances(num_engines)
update_tts_instances() update_tts_instances()
print(f"[AllTalk MEM] Auto-start complete.") print(f"[AllTalk MEM] Auto-start complete.")
print(f"[AllTalk MEM] {results}") print(f"[AllTalk MEM] {results}")
@@ -1574,9 +1492,13 @@ def auto_start_engines():
# Main execution # Main execution
if __name__ == "__main__": if __name__ == "__main__":
signal.signal(signal.SIGINT, signal_handler) signal.signal(signal.SIGINT, signal_handler)
interface = create_gradio_interface() if USE_GRADIO:
# Start the Gradio interface print("[AllTalk MEM] Starting gradio...")
interface.launch(quiet=True, server_port=config['gradio_interface_port'], prevent_thread_lock=True) interface = create_gradio_interface()
# Start the Gradio interface
interface.launch(quiet=True, server_port=config.gradio_interface_port, prevent_thread_lock=True)
else:
print("[AllTalk MEM] Skipping gradio start...")
# Start the API server # Start the API server
start_api_server() start_api_server()
initialize_instances() initialize_instances()