Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7c7cb72c13 | ||
|
|
3a105ce922 | ||
|
|
690018c4ab | ||
|
|
6808439ca1 | ||
|
|
8cf56faad2 | ||
|
|
cb7d486288 | ||
|
|
086999fc1b | ||
|
|
a1b889108f | ||
|
|
9f18b24465 | ||
|
|
a3f157249c | ||
|
|
eb08d31509 | ||
|
|
1e1b7fdd8c | ||
|
|
5aad7bcc45 | ||
|
|
dd52597f49 | ||
|
|
5f836e08b8 | ||
|
|
b89b6cdc1d | ||
|
|
558aed1888 | ||
|
|
5eab3d28df | ||
|
|
fd76f496a0 | ||
|
|
4d9b44d9a0 | ||
|
|
951df5a239 | ||
|
|
0a35ee2310 | ||
|
|
efeb2e67e0 | ||
|
|
131f14de76 | ||
|
|
4e67931fe9 | ||
|
|
785525327d | ||
|
|
d4647573cd | ||
|
|
c3895b602c | ||
|
|
f18eb9a4a0 | ||
|
|
2834778e8f | ||
|
|
2f42c69d25 | ||
|
|
8c65f026e6 | ||
|
|
fff01107c8 | ||
|
|
0bdc66f6e5 | ||
|
|
1c3fc86085 | ||
|
|
d9eb0876a6 | ||
|
|
9b4130a8eb | ||
|
|
8fb5b4f695 | ||
|
|
65e306a198 | ||
|
|
3dabb63656 | ||
|
|
241fa91473 | ||
|
|
2a5c8dd58a | ||
|
|
2bfa10d7be | ||
|
|
3501e2e2f3 | ||
|
|
679e3a0982 | ||
|
|
35d05c7689 | ||
|
|
084cc2d2a2 | ||
|
|
a96c82ff01 | ||
|
|
aa7ff1940a | ||
|
|
58465ae14e | ||
|
|
2f49f61645 | ||
|
|
58a70caebc | ||
|
|
d0d470db0f | ||
|
|
068fc72346 | ||
|
|
fc5c8c748f | ||
|
|
24f86d08b7 | ||
|
|
99e402664c | ||
|
|
24cf9eae59 | ||
|
|
18b1582d2a | ||
|
|
96d9df9438 | ||
|
|
4ca156a032 | ||
|
|
5aecb5b7fd | ||
|
|
63c308ff05 | ||
|
|
e6e4b0b47f | ||
|
|
af4c63f1ee | ||
|
|
bedc8148ad | ||
|
|
21cd171719 | ||
|
|
856c0c1baa | ||
|
|
09977956f5 | ||
|
|
2c7feb31e5 | ||
|
|
cead53a808 | ||
|
|
e7996ebc7a | ||
|
|
6815ba0f2e | ||
|
|
51fa4ce9e9 | ||
|
|
043648e24a | ||
|
|
b76ff028f8 | ||
|
|
14214cc141 | ||
|
|
872638a198 | ||
|
|
5d2720c118 | ||
|
|
460216819a | ||
|
|
412d1442a6 | ||
|
|
5a1dd31ab6 | ||
|
|
ce84f153ca | ||
|
|
ab3570c899 | ||
|
|
42f08cd19c | ||
|
|
21fb1b58d9 | ||
|
|
d4741edf94 | ||
|
|
e3c7144272 | ||
|
|
27a4fe2338 | ||
|
|
a0913db3c9 | ||
|
|
645c0d3a67 | ||
|
|
e3878b595b | ||
|
|
825e4a7d12 | ||
|
|
6c20f45154 | ||
|
|
97cda27c7e | ||
|
|
a075495a0a | ||
|
|
b205657be3 | ||
|
|
483d6c8e79 | ||
|
|
9bd4665e6b | ||
|
|
62e1ab7e89 | ||
|
|
740c54db1c | ||
|
|
e188467730 | ||
|
|
743f23240e | ||
|
|
66dadbca25 | ||
|
|
8125b46dbe | ||
|
|
d4bd46f203 | ||
|
|
f13d6d336b | ||
|
|
1fa2f2e110 | ||
|
|
9ad4005e52 | ||
|
|
6d6b92d4b2 | ||
|
|
bf5d913cae | ||
|
|
f52ccd3931 | ||
|
|
37e7c8002d | ||
|
|
ad7382ee68 | ||
|
|
2428bfe357 | ||
|
|
74f3acc51f | ||
|
|
90a98f140b | ||
|
|
885311ecd8 | ||
|
|
c2952e9758 | ||
|
|
7e6e18c6d9 | ||
|
|
47eafafeda | ||
|
|
5edaab1356 | ||
|
|
7bde34d2ce | ||
|
|
0aa4638f68 | ||
|
|
22dd9c741c | ||
|
|
e000b8bb87 | ||
|
|
2d26f309df | ||
|
|
22d3e53a28 | ||
|
|
0ea646dbf3 | ||
|
|
282e3ce84c | ||
|
|
772afd47a1 | ||
|
|
1612835c5d | ||
|
|
ea551d3a84 | ||
|
|
47001203cd | ||
|
|
27ff5a12b0 | ||
|
|
0a89407e4d | ||
|
|
a579c5e6d2 | ||
|
|
5a21d14672 | ||
|
|
f4f5f82bb0 | ||
|
|
7b03804f9e | ||
|
|
f46493e6d8 | ||
|
|
2a64f6ad2b | ||
|
|
3368f8b016 | ||
|
|
121585ae84 | ||
|
|
3e6c11740d | ||
|
|
6b111494b3 | ||
|
|
b376a27da2 | ||
|
|
eeec4de703 | ||
|
|
49f98033b3 | ||
|
|
c1470930f4 | ||
|
|
1dec4f027b | ||
|
|
a9ef8edcb4 | ||
|
|
299238be7d | ||
|
|
29c7ea1526 | ||
|
|
e974e14a10 | ||
|
|
59c654bacd | ||
|
|
969b4e3f46 | ||
|
|
86c64fc9ba | ||
|
|
b8c0db6f56 | ||
|
|
57982cb810 | ||
|
|
6387007f56 | ||
|
|
cdf8d695e2 | ||
|
|
833bf3da4f | ||
|
|
f68086db0b | ||
|
|
1f7842ef5a | ||
|
|
9bb0d1fa58 | ||
|
|
015b9033ec | ||
|
|
7f261bb5e9 | ||
|
|
00b93cfe1b | ||
|
|
c0d01ee931 | ||
|
|
d0ceb2bb65 | ||
|
|
3231e61d56 | ||
|
|
32f3b721c2 | ||
|
|
8c588939d2 | ||
|
|
ff1077f282 | ||
|
|
36d0e92ed9 | ||
|
|
8dfb84d7e2 | ||
|
|
4caecdc5eb | ||
|
|
40f2fbf54e | ||
|
|
ab37dd8849 | ||
|
|
03b02e0e4c | ||
|
|
351f46c7b5 | ||
|
|
71ba584e7c | ||
|
|
b3f53e93cc | ||
|
|
0ed3cb75b5 | ||
|
|
c29de18ad4 | ||
|
|
ec1d0e8c5d | ||
|
|
12bf008b3f | ||
|
|
ecdc10ece5 | ||
|
|
360f70cb0a | ||
|
|
51c91c3d70 | ||
|
|
81a9f4c847 | ||
|
|
cdce7511dc | ||
|
|
6d4b6d336b | ||
|
|
6c4a9b19c7 | ||
|
|
82ecf3eecc | ||
|
|
d750959f52 | ||
|
|
bd74de53da | ||
|
|
fcec9d8f5b | ||
|
|
85e5e6ab86 | ||
|
|
d15931bdda | ||
|
|
abc9cf1fb7 | ||
|
|
2b60f5b0da | ||
|
|
e524063ec2 | ||
|
|
d93906a75f | ||
|
|
ad57deed20 | ||
|
|
d0866e3c6c | ||
|
|
af907a299d | ||
|
|
7928af1bb7 | ||
|
|
4d837a0219 | ||
|
|
e50817a234 | ||
|
|
c3bb58d2af | ||
|
|
c17959f142 | ||
|
|
def2d51c5f | ||
|
|
6166d4f751 | ||
|
|
cd164ae258 | ||
|
|
f089e3e85b | ||
|
|
d008c7ab0a | ||
|
|
a199adfbd6 | ||
|
|
61998b4d39 | ||
|
|
55799b2de8 | ||
|
|
ece55ffe06 | ||
|
|
dbc2582fa6 | ||
|
|
535c9b652a | ||
|
|
6c28d58b4a | ||
|
|
c9a222932c | ||
|
|
7010369966 | ||
|
|
525a337835 | ||
|
|
2bd1ef843e | ||
|
|
a9d95fc563 | ||
|
|
1a37ae4ba5 | ||
|
|
8c6fc4853d | ||
|
|
f34ebbd675 | ||
|
|
64cffcdf80 | ||
|
|
2731a559f4 | ||
|
|
a91a83268f | ||
|
|
5d406fbbfb | ||
|
|
cefd1e045b | ||
|
|
bec4e905d3 | ||
|
|
f5e99a5e72 | ||
|
|
631ea6dd8a | ||
|
|
08a04ba506 | ||
|
|
c78927b80a | ||
|
|
04e08c58b8 | ||
|
|
893747c9a9 | ||
|
|
d52dcf86d5 | ||
|
|
ad0eab1224 | ||
|
|
62d1cc05d3 | ||
|
|
b8292da92f | ||
|
|
180adb45dd | ||
|
|
5c0f08d49e | ||
|
|
571872dbe2 | ||
|
|
467bfbea55 | ||
|
|
5a3cbb6366 | ||
|
|
2351e418fd | ||
|
|
b987ff38a7 | ||
|
|
43899028e3 | ||
|
|
2c1ca8b2a3 | ||
|
|
139162c4e6 | ||
|
|
7da78ec045 | ||
|
|
11c70c7432 | ||
|
|
c2822edb1b | ||
|
|
cba7ed7b88 | ||
|
|
471a26f2a4 | ||
|
|
34d1b81ae4 | ||
|
|
f1b4d4b8b6 | ||
|
|
c05237143b | ||
|
|
c5846b832a | ||
|
|
7a38730c56 | ||
|
|
0b1e6f8711 | ||
|
|
8c38e925dd | ||
|
|
df70481934 | ||
|
|
e43c58a56e | ||
|
|
8dc9304a4c | ||
|
|
102424669b | ||
|
|
5210d075c1 | ||
|
|
30db974439 | ||
|
|
c2a558110b | ||
|
|
83ac18b83f | ||
|
|
6e36415746 | ||
|
|
fe7ffb535c | ||
|
|
80b7fccb50 | ||
|
|
2790ee9860 | ||
|
|
74ce2584cc | ||
|
|
8dd11541b4 | ||
|
|
d1317b49f0 | ||
|
|
a50d5a5028 | ||
|
|
33264554cd | ||
|
|
4a116e359a | ||
|
|
36bfb17eda | ||
|
|
697fb098c3 | ||
|
|
51a8e93df7 | ||
|
|
257fa59e70 | ||
|
|
7dbad85fc3 | ||
|
|
510894c793 | ||
|
|
a85058a3ac | ||
|
|
e54c7e4362 | ||
|
|
1f2ad303a0 | ||
|
|
d4a04096c8 | ||
|
|
dd45008e88 | ||
|
|
e9d9f54de6 | ||
|
|
3230397a82 | ||
|
|
840889ac9a | ||
|
|
d3e061b127 | ||
|
|
a428f67a2e | ||
|
|
b223e7b247 | ||
|
|
9296843508 | ||
|
|
f0c9e3e6bb | ||
|
|
22de1bf0c6 | ||
|
|
621f5434a7 | ||
|
|
81c8354cf8 | ||
|
|
3bc55a91e5 | ||
|
|
fc857f464c | ||
|
|
50aa6de96c | ||
|
|
10299194f8 | ||
|
|
d80d1ace3c | ||
|
|
f19f4e9a29 | ||
|
|
7e4ceef0e8 | ||
|
|
c1bc1f26cd | ||
|
|
70d385133d | ||
|
|
13b2eb5fe3 | ||
|
|
ff4b9d9e99 | ||
|
|
a22830f700 | ||
|
|
374c794fa9 | ||
|
|
d2adbd9c1e | ||
|
|
1dc4b8bf50 | ||
|
|
0eba72fc47 | ||
|
|
f2c87dcdab | ||
|
|
4a727ce14a | ||
|
|
6e15d43e63 | ||
|
|
dedc13b3b6 | ||
|
|
7cb206e531 | ||
|
|
d5fa6bb46d | ||
|
|
e5acc8bdbd | ||
|
|
56a7ea1d8f | ||
|
|
863a7cd841 | ||
|
|
2e1709ab58 | ||
|
|
9c4efdc135 | ||
|
|
28675c976c | ||
|
|
afe3b9fe0b | ||
|
|
2e4d52170d | ||
|
|
0e2cc25571 | ||
|
|
8a2b669da9 | ||
|
|
b65291d44d |
No files matched your search
@@ -0,0 +1 @@
|
||||
ko_fi: erew123
|
||||
@@ -1,13 +1,14 @@
|
||||
---
|
||||
name: Bug report/Support request
|
||||
about: Create a report to help us improve
|
||||
name: Support Request/Issue
|
||||
about: Use this to create a support request or report a bug/issue
|
||||
title: ''
|
||||
labels: ''
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
🔴 If you have installed AllTalk in a custom Python environment, I will only be able to provide limited assistance/support. AllTalk draws on a variety of scripts and libraries that are not written or managed by myself, and they may fail, error or give strange results in custom built python environments.
|
||||
|
||||
Please generate a diagnostics report and upload the "diagnostics.log".
|
||||
🔴 Please generate a diagnostics report and upload the "diagnostics.log" as this helps me understand your configuration.
|
||||
|
||||
https://github.com/erew123/alltalk_tts/tree/main?#-how-to-make-a-diagnostics-report-file
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
---
|
||||
name: Feature request
|
||||
about: Suggest an idea for this project
|
||||
about: Use this to create a Feature request
|
||||
title: ''
|
||||
labels: ''
|
||||
assignees: ''
|
||||
|
||||
@@ -0,0 +1,67 @@
|
||||
name: Publish Docker image
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [main]
|
||||
release:
|
||||
types: [published]
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
build_and_push:
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
include:
|
||||
- dockerfile: "Dockerfile"
|
||||
platforms: "linux/amd64"
|
||||
tag_name: "cpu"
|
||||
- dockerfile: "nvidia.Dockerfile"
|
||||
platforms: "linux/amd64"
|
||||
tag_name: "cuda"
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
|
||||
- name: Lowercase Actor and Extract Repo Name
|
||||
run: |
|
||||
echo "ACTOR_LOWER=$(echo ${{ github.actor }} | tr '[:upper:]' '[:lower:]')" >> $GITHUB_ENV
|
||||
echo "REPO_NAME=$(echo ${{ github.repository }} | cut -d'/' -f 2)" >> $GITHUB_ENV
|
||||
|
||||
- name: Login to GitHub Container Registry
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: ${{ env.ACTOR_LOWER }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Login to DockerHub
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
username: ${{ vars.DOCKERHUB_USERNAME }}
|
||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||
|
||||
- name: Build and push Docker image
|
||||
uses: docker/build-push-action@v5
|
||||
with:
|
||||
context: .
|
||||
file: ./${{ matrix.dockerfile }}
|
||||
platforms: ${{ matrix.platforms }}
|
||||
push: true
|
||||
tags: |
|
||||
ghcr.io/${{ env.ACTOR_LOWER }}/${{ env.REPO_NAME }}:${{ matrix.tag_name }}-${{ github.sha }}
|
||||
ghcr.io/${{ env.ACTOR_LOWER }}/${{ env.REPO_NAME }}:${{ matrix.tag_name }}
|
||||
ghcr.io/${{ env.ACTOR_LOWER }}/${{ env.REPO_NAME }}:latest
|
||||
${{ vars.DOCKERHUB_USERNAME }}/${{ env.REPO_NAME }}:${{ matrix.tag_name }}-${{ github.sha }}
|
||||
${{ vars.DOCKERHUB_USERNAME }}/${{ env.REPO_NAME }}:${{ matrix.tag_name }}
|
||||
${{ vars.DOCKERHUB_USERNAME }}/${{ env.REPO_NAME }}:latest
|
||||
|
||||
- name: Docker Hub Description
|
||||
uses: peter-evans/dockerhub-description@v3.4.2
|
||||
with:
|
||||
username: ${{ vars.DOCKERHUB_USERNAME }}
|
||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||
repository: ${{ vars.DOCKERHUB_USERNAME }}/${{ env.REPO_NAME }}
|
||||
@@ -1,285 +0,0 @@
|
||||
// Copyright (c) Microsoft Corporation.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// DeepSpeed Team
|
||||
|
||||
#include <ATen/cuda/CUDAContext.h>
|
||||
#include <torch/extension.h>
|
||||
#include <cassert>
|
||||
#include <vector>
|
||||
#include "quantization.h"
|
||||
|
||||
template <typename T>
|
||||
at::Tensor ds_quantize(at::Tensor& vals, int groups, int bits)
|
||||
{
|
||||
auto t_size = vals.sizes();
|
||||
int size = 1;
|
||||
for (auto dim : t_size) size *= dim;
|
||||
|
||||
if ((((size / groups) - 1) / 4096 + 1) <= 256) {
|
||||
launch_fake_quantize_kernel(
|
||||
(T*)vals.data_ptr(), size, groups, bits, at::cuda::getCurrentCUDAStream());
|
||||
}
|
||||
return vals;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
at::Tensor ds_sr_quantize(at::Tensor& vals, int groups, int bits)
|
||||
{
|
||||
auto t_size = vals.sizes();
|
||||
int size = 1;
|
||||
for (auto dim : t_size) size *= dim;
|
||||
|
||||
if (((size / groups) / 4 / 1024) <= 256) {
|
||||
launch_sr_fake_quantize_kernel(
|
||||
(T*)vals.data_ptr(), size, groups, bits, at::cuda::getCurrentCUDAStream());
|
||||
}
|
||||
return vals;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
at::Tensor ds_quantize_asym(at::Tensor& vals, int groups, int bits)
|
||||
{
|
||||
auto t_size = vals.sizes();
|
||||
int size = 1;
|
||||
for (auto dim : t_size) size *= dim;
|
||||
|
||||
if ((((size / groups) - 1) / 4096 + 1) <= 256) {
|
||||
launch_fake_quantize_kernel_asym(
|
||||
(T*)vals.data_ptr(), size, groups, bits, at::cuda::getCurrentCUDAStream());
|
||||
}
|
||||
return vals;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
at::Tensor ds_sr_quantize_asym(at::Tensor& vals, int groups, int bits)
|
||||
{
|
||||
auto t_size = vals.sizes();
|
||||
int size = 1;
|
||||
for (auto dim : t_size) size *= dim;
|
||||
|
||||
if (((size / groups) / 4 / 1024) <= 256) {
|
||||
launch_sr_fake_quantize_kernel_asym(
|
||||
(T*)vals.data_ptr(), size, groups, bits, at::cuda::getCurrentCUDAStream());
|
||||
}
|
||||
return vals;
|
||||
}
|
||||
|
||||
std::vector<at::Tensor> quantize_kernel(at::Tensor& input_vals,
|
||||
int groups,
|
||||
int numBits,
|
||||
quantize::Type quantType)
|
||||
{
|
||||
auto dtype = at::kFloat;
|
||||
auto params_options = at::TensorOptions()
|
||||
.dtype(dtype)
|
||||
.layout(at::kStrided)
|
||||
.device(at::kCUDA)
|
||||
.requires_grad(false);
|
||||
const int param_elems = (quantize::requires_offset(quantType)) ? 2 : 1;
|
||||
auto params = torch::empty({groups, param_elems}, params_options);
|
||||
|
||||
auto output_options = at::TensorOptions()
|
||||
.dtype(at::kChar)
|
||||
.layout(at::kStrided)
|
||||
.device(at::kCUDA)
|
||||
.requires_grad(false);
|
||||
|
||||
auto output_sizes = input_vals.sizes().vec();
|
||||
output_sizes[output_sizes.size() - 1] /= numBits == 8 ? 1 : 2;
|
||||
auto output = torch::empty(output_sizes, output_options);
|
||||
|
||||
const int elems_per_group = at::numel(input_vals) / groups;
|
||||
|
||||
launch_quant((int8_t*)output.data_ptr(),
|
||||
(float*)params.data_ptr(),
|
||||
(__half*)input_vals.data_ptr(),
|
||||
groups,
|
||||
elems_per_group,
|
||||
numBits,
|
||||
quantType,
|
||||
at::cuda::getCurrentCUDAStream());
|
||||
|
||||
return {output, params};
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
at::Tensor dequantize(at::Tensor& quantized_data,
|
||||
at::Tensor& params,
|
||||
int groups,
|
||||
int num_bits,
|
||||
quantize::Type quant_type)
|
||||
{
|
||||
auto dtype = (std::is_same<T, float>::value) ? torch::kFloat32 : torch::kFloat16;
|
||||
auto output_options = at::TensorOptions()
|
||||
.dtype(dtype)
|
||||
.layout(at::kStrided)
|
||||
.device(at::kCUDA)
|
||||
.requires_grad(false);
|
||||
|
||||
auto output_sizes = quantized_data.sizes().vec();
|
||||
output_sizes[output_sizes.size() - 1] *= num_bits == 8 ? 1 : 2;
|
||||
auto output = torch::empty(output_sizes, output_options);
|
||||
|
||||
const int total_elems = at::numel(output);
|
||||
const int elems_per_group = total_elems / groups;
|
||||
|
||||
launch_dequantize_kernel((T*)output.data_ptr(),
|
||||
(const int8_t*)quantized_data.data_ptr(),
|
||||
(const float*)params.data_ptr(),
|
||||
quant_type,
|
||||
num_bits,
|
||||
elems_per_group,
|
||||
total_elems,
|
||||
at::cuda::getCurrentCUDAStream());
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
at::Tensor dequantize_int4_to_half_experimental(at::Tensor& data_in,
|
||||
at::Tensor& scale_buffer,
|
||||
at::Tensor& min_val_buffer,
|
||||
int num_group,
|
||||
int group_size)
|
||||
{
|
||||
auto output_options = at::TensorOptions().dtype(at::kHalf).device(at::kCUDA);
|
||||
auto output = torch::empty({num_group, group_size}, output_options);
|
||||
|
||||
launch_dequantize_int4_to_half_experimental((uint8_t*)data_in.data_ptr(),
|
||||
(half*)output.data_ptr(),
|
||||
(half*)scale_buffer.data_ptr(),
|
||||
(half*)min_val_buffer.data_ptr(),
|
||||
num_group,
|
||||
group_size,
|
||||
at::cuda::getCurrentCUDAStream());
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
std::vector<at::Tensor> ds_swizzle_quant(at::Tensor& input_vals,
|
||||
int groups,
|
||||
int num_bits,
|
||||
quantize::Type quant_type,
|
||||
int pipeline_size,
|
||||
int nodes,
|
||||
int devices_per_node)
|
||||
{
|
||||
auto scales_options = at::TensorOptions()
|
||||
.dtype(at::kFloat)
|
||||
.layout(at::kStrided)
|
||||
.device(at::kCUDA)
|
||||
.requires_grad(false);
|
||||
const int scales_elems = (quantize::requires_offset(quant_type)) ? 2 : 1;
|
||||
auto scales = torch::empty({groups, scales_elems}, scales_options);
|
||||
|
||||
auto output_options = at::TensorOptions()
|
||||
.dtype(at::kChar)
|
||||
.layout(at::kStrided)
|
||||
.device(at::kCUDA)
|
||||
.requires_grad(false);
|
||||
|
||||
const int quantization_scalar = 8 / num_bits;
|
||||
const int compressed_vals = at::numel(input_vals) / quantization_scalar;
|
||||
|
||||
auto output = torch::empty({compressed_vals}, output_options);
|
||||
const int elems_per_group = at::numel(input_vals) / groups;
|
||||
|
||||
launch_swizzled_quant((int8_t*)output.data_ptr(),
|
||||
(float*)scales.data_ptr(),
|
||||
(__half*)input_vals.data_ptr(),
|
||||
num_bits,
|
||||
quant_type,
|
||||
groups,
|
||||
elems_per_group,
|
||||
pipeline_size,
|
||||
nodes,
|
||||
devices_per_node,
|
||||
at::cuda::getCurrentCUDAStream());
|
||||
|
||||
return {output, scales};
|
||||
}
|
||||
|
||||
std::vector<at::Tensor> quantized_reduction(at::Tensor& input_vals,
|
||||
at::Tensor& input_scales,
|
||||
int in_groups,
|
||||
int out_groups,
|
||||
int num_bits,
|
||||
quantize::Type quant_type,
|
||||
int devices_per_node)
|
||||
{
|
||||
auto scales_options = at::TensorOptions()
|
||||
.dtype(at::kFloat)
|
||||
.layout(at::kStrided)
|
||||
.device(at::kCUDA)
|
||||
.requires_grad(false);
|
||||
const int scales_elems = (quantize::requires_offset(quant_type)) ? 2 : 1;
|
||||
auto scales = torch::empty({out_groups, scales_elems}, scales_options);
|
||||
|
||||
auto output_options = at::TensorOptions()
|
||||
.dtype(at::kChar)
|
||||
.layout(at::kStrided)
|
||||
.device(at::kCUDA)
|
||||
.requires_grad(false);
|
||||
|
||||
std::vector<int64_t> sz_vector(input_vals.sizes().begin(), input_vals.sizes().end());
|
||||
sz_vector[sz_vector.size() - 1] = sz_vector.back() / devices_per_node; // num of GPU per nodes
|
||||
at::IntArrayRef sz(sz_vector);
|
||||
auto output = torch::empty(sz, output_options);
|
||||
|
||||
const int elems_per_in_tensor = at::numel(input_vals) / devices_per_node;
|
||||
const int elems_per_in_group = elems_per_in_tensor / (in_groups / devices_per_node);
|
||||
const int elems_per_out_group = elems_per_in_tensor / out_groups;
|
||||
|
||||
// old code:
|
||||
// std::vector<long int> sz(input_vals.sizes().begin(), input_vals.sizes().end());
|
||||
// sz[sz.size() - 1] = sz.back() / devices_per_node; // num of GPU per nodes
|
||||
// const int elems_per_in_tensor = at::numel(input_vals) / devices_per_node;
|
||||
// auto output = torch::empty(sz, output_options);
|
||||
|
||||
// const int elems_per_in_group = elems_per_in_tensor / (in_groups / devices_per_node);
|
||||
// const int elems_per_out_group = elems_per_in_tensor / out_groups;
|
||||
|
||||
launch_dequant_reduce((int8_t*)output.data_ptr(),
|
||||
(float*)scales.data_ptr(),
|
||||
(const int8_t*)input_vals.data_ptr(),
|
||||
(const float*)input_scales.data_ptr(),
|
||||
devices_per_node,
|
||||
num_bits,
|
||||
quant_type,
|
||||
out_groups,
|
||||
elems_per_out_group,
|
||||
elems_per_in_tensor,
|
||||
in_groups / devices_per_node,
|
||||
elems_per_in_group,
|
||||
at::cuda::getCurrentCUDAStream());
|
||||
return {output, scales};
|
||||
}
|
||||
|
||||
PYBIND11_MODULE(TORCH_EXTENSION_NAME, m)
|
||||
{
|
||||
m.def("ds_quantize_fp32", &ds_quantize<float>, "DeepSpeed Quantize with fp32 (CUDA)");
|
||||
m.def("ds_quantize_fp16", &ds_quantize<__half>, "DeepSpeed Quantize with fp16 (CUDA)");
|
||||
m.def("ds_sr_quantize_fp32", &ds_sr_quantize<float>, "DeepSpeed Quantize with fp32 (CUDA)");
|
||||
m.def("ds_sr_quantize_fp16", &ds_sr_quantize<__half>, "DeepSpeed Quantize with fp16 (CUDA)");
|
||||
m.def("ds_quantize_asym_fp32", &ds_quantize_asym<float>, "DeepSpeed Quantize with fp32 (CUDA)");
|
||||
m.def(
|
||||
"ds_quantize_asym_fp16", &ds_quantize_asym<__half>, "DeepSpeed Quantize with fp16 (CUDA)");
|
||||
m.def("ds_sr_quantize_asym_fp32",
|
||||
&ds_sr_quantize_asym<float>,
|
||||
"DeepSpeed Quantize with fp32 (CUDA)");
|
||||
m.def("ds_sr_quantize_asym_fp16",
|
||||
&ds_sr_quantize_asym<__half>,
|
||||
"DeepSpeed Quantize with fp16 (CUDA)");
|
||||
pybind11::enum_<quantize::Type>(m, "QuantizationType")
|
||||
.value("Symmetric", quantize::Type::Symmetric)
|
||||
.value("Asymmetric", quantize::Type::Asymmetric)
|
||||
.export_values();
|
||||
m.def("quantize", &quantize_kernel);
|
||||
m.def("dequantize", &dequantize<__half>);
|
||||
m.def("dequantize_fp32", &dequantize<float>);
|
||||
m.def("dequantize_int4_to_half_experimental",
|
||||
&dequantize_int4_to_half_experimental,
|
||||
"Dequantize int4 to half (experimental)");
|
||||
m.def("swizzle_quant", &ds_swizzle_quant);
|
||||
m.def("quantized_reduction", &quantized_reduction);
|
||||
}
|
||||
@@ -1,160 +0,0 @@
|
||||
# Copyright (c) Microsoft Corporation.
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
# DeepSpeed Team
|
||||
|
||||
import os
|
||||
import torch
|
||||
import deepspeed
|
||||
import subprocess
|
||||
import argparse
|
||||
import psutil
|
||||
from .ops.op_builder.all_ops import ALL_OPS
|
||||
from .git_version_info import installed_ops, torch_info
|
||||
from deepspeed.accelerator import get_accelerator
|
||||
|
||||
GREEN = '\033[92m'
|
||||
RED = '\033[91m'
|
||||
YELLOW = '\033[93m'
|
||||
END = '\033[0m'
|
||||
SUCCESS = f"{GREEN} [SUCCESS] {END}"
|
||||
OKAY = f"{GREEN}[OKAY]{END}"
|
||||
WARNING = f"{YELLOW}[WARNING]{END}"
|
||||
FAIL = f'{RED}[FAIL]{END}'
|
||||
INFO = '[INFO]'
|
||||
|
||||
color_len = len(GREEN) + len(END)
|
||||
okay = f"{GREEN}[OKAY]{END}"
|
||||
warning = f"{YELLOW}[WARNING]{END}"
|
||||
|
||||
|
||||
def op_report(verbose=True):
|
||||
max_dots = 23
|
||||
max_dots2 = 11
|
||||
h = ["op name", "installed", "compatible"]
|
||||
print("-" * (max_dots + max_dots2 + len(h[0]) + len(h[1])))
|
||||
print("DeepSpeed C++/CUDA extension op report")
|
||||
print("-" * (max_dots + max_dots2 + len(h[0]) + len(h[1])))
|
||||
|
||||
print("NOTE: Ops not installed will be just-in-time (JIT) compiled at\n"
|
||||
" runtime if needed. Op compatibility means that your system\n"
|
||||
" meet the required dependencies to JIT install the op.")
|
||||
|
||||
print("-" * (max_dots + max_dots2 + len(h[0]) + len(h[1])))
|
||||
print("JIT compiled ops requires ninja")
|
||||
ninja_status = OKAY if ninja_installed() else FAIL
|
||||
print('ninja', "." * (max_dots - 5), ninja_status)
|
||||
print("-" * (max_dots + max_dots2 + len(h[0]) + len(h[1])))
|
||||
print(h[0], "." * (max_dots - len(h[0])), h[1], "." * (max_dots2 - len(h[1])), h[2])
|
||||
print("-" * (max_dots + max_dots2 + len(h[0]) + len(h[1])))
|
||||
installed = f"{GREEN}[YES]{END}"
|
||||
no = f"{YELLOW}[NO]{END}"
|
||||
for op_name, builder in ALL_OPS.items():
|
||||
dots = "." * (max_dots - len(op_name))
|
||||
is_compatible = OKAY if builder.is_compatible(verbose) else no
|
||||
is_installed = installed if installed_ops.get(op_name, False) else no
|
||||
dots2 = '.' * ((len(h[1]) + (max_dots2 - len(h[1]))) - (len(is_installed) - color_len))
|
||||
print(op_name, dots, is_installed, dots2, is_compatible)
|
||||
print("-" * (max_dots + max_dots2 + len(h[0]) + len(h[1])))
|
||||
|
||||
|
||||
def ninja_installed():
|
||||
try:
|
||||
import ninja # noqa: F401 # type: ignore
|
||||
except ImportError:
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
def nvcc_version():
|
||||
import torch.utils.cpp_extension
|
||||
cuda_home = torch.utils.cpp_extension.CUDA_HOME
|
||||
if cuda_home is None:
|
||||
return f"{RED} [FAIL] cannot find CUDA_HOME via torch.utils.cpp_extension.CUDA_HOME={torch.utils.cpp_extension.CUDA_HOME} {END}"
|
||||
try:
|
||||
output = subprocess.check_output([cuda_home + "/bin/nvcc", "-V"], universal_newlines=True)
|
||||
except FileNotFoundError:
|
||||
return f"{RED} [FAIL] nvcc missing {END}"
|
||||
output_split = output.split()
|
||||
release_idx = output_split.index("release")
|
||||
release = output_split[release_idx + 1].replace(',', '').split(".")
|
||||
return ".".join(release)
|
||||
|
||||
|
||||
def get_shm_size():
|
||||
try:
|
||||
temp_dir = os.getenv('TEMP') or os.getenv('TMP') or os.path.join(os.path.expanduser('~'), 'tmp')
|
||||
shm_stats = psutil.disk_usage(temp_dir)
|
||||
shm_size = shm_stats.total
|
||||
shm_hbytes = human_readable_size(shm_size)
|
||||
warn = []
|
||||
if shm_size < 512 * 1024**2:
|
||||
warn.append(
|
||||
f" {YELLOW} [WARNING] Shared memory size might be too small, consider increasing it. {END}"
|
||||
)
|
||||
# Add additional warnings specific to your use case if needed.
|
||||
return shm_hbytes, warn
|
||||
except Exception as e:
|
||||
return "UNKNOWN", [f"Error getting shared memory size: {e}"]
|
||||
|
||||
def human_readable_size(size):
|
||||
units = ['B', 'KB', 'MB', 'GB', 'TB']
|
||||
i = 0
|
||||
while size >= 1024 and i < len(units) - 1:
|
||||
size /= 1024
|
||||
i += 1
|
||||
return f'{size:.2f} {units[i]}'
|
||||
|
||||
|
||||
def debug_report():
|
||||
max_dots = 33
|
||||
|
||||
report = [("torch install path", torch.__path__), ("torch version", torch.__version__),
|
||||
("deepspeed install path", deepspeed.__path__),
|
||||
("deepspeed info", f"{deepspeed.__version__}, {deepspeed.__git_hash__}, {deepspeed.__git_branch__}")]
|
||||
if get_accelerator().device_name() == 'cuda':
|
||||
hip_version = getattr(torch.version, "hip", None)
|
||||
report.extend([("torch cuda version", torch.version.cuda), ("torch hip version", hip_version),
|
||||
("nvcc version", (None if hip_version else nvcc_version())),
|
||||
("deepspeed wheel compiled w.", f"torch {torch_info['version']}, " +
|
||||
(f"hip {torch_info['hip_version']}" if hip_version else f"cuda {torch_info['cuda_version']}"))
|
||||
])
|
||||
else:
|
||||
report.extend([("deepspeed wheel compiled w.", f"torch {torch_info['version']} ")])
|
||||
|
||||
report.append(("shared memory (/dev/shm) size", get_shm_size()))
|
||||
|
||||
print("DeepSpeed general environment info:")
|
||||
for name, value in report:
|
||||
warns = []
|
||||
if isinstance(value, tuple):
|
||||
value, warns = value
|
||||
print(name, "." * (max_dots - len(name)), value)
|
||||
if warns:
|
||||
for warn in warns:
|
||||
print(warn)
|
||||
|
||||
|
||||
def parse_arguments():
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument('--hide_operator_status',
|
||||
action='store_true',
|
||||
help='Suppress display of installation and compatibility statuses of DeepSpeed operators. ')
|
||||
parser.add_argument('--hide_errors_and_warnings', action='store_true', help='Suppress warning and error messages.')
|
||||
args = parser.parse_args()
|
||||
return args
|
||||
|
||||
|
||||
def main(hide_operator_status=False, hide_errors_and_warnings=False):
|
||||
if not hide_operator_status:
|
||||
op_report(verbose=not hide_errors_and_warnings)
|
||||
debug_report()
|
||||
|
||||
|
||||
def cli_main():
|
||||
args = parse_arguments()
|
||||
main(hide_operator_status=args.hide_operator_status, hide_errors_and_warnings=args.hide_errors_and_warnings)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -1,299 +0,0 @@
|
||||
// Copyright (c) Microsoft Corporation.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// DeepSpeed Team
|
||||
|
||||
#include <ATen/cuda/CUDAContext.h>
|
||||
#include <torch/extension.h>
|
||||
#include <cassert>
|
||||
#include <vector>
|
||||
#include "quantization.h"
|
||||
|
||||
template <typename T>
|
||||
at::Tensor ds_quantize(at::Tensor& vals, int groups, int bits)
|
||||
{
|
||||
auto t_size = vals.sizes();
|
||||
int size = 1;
|
||||
for (auto dim : t_size) size *= dim;
|
||||
|
||||
if ((((size / groups) - 1) / 4096 + 1) <= 256) {
|
||||
launch_fake_quantize_kernel(
|
||||
(T*)vals.data_ptr(), size, groups, bits, at::cuda::getCurrentCUDAStream());
|
||||
}
|
||||
return vals;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
at::Tensor ds_sr_quantize(at::Tensor& vals, int groups, int bits)
|
||||
{
|
||||
auto t_size = vals.sizes();
|
||||
int size = 1;
|
||||
for (auto dim : t_size) size *= dim;
|
||||
|
||||
if (((size / groups) / 4 / 1024) <= 256) {
|
||||
launch_sr_fake_quantize_kernel(
|
||||
(T*)vals.data_ptr(), size, groups, bits, at::cuda::getCurrentCUDAStream());
|
||||
}
|
||||
return vals;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
at::Tensor ds_quantize_asym(at::Tensor& vals, int groups, int bits)
|
||||
{
|
||||
auto t_size = vals.sizes();
|
||||
int size = 1;
|
||||
for (auto dim : t_size) size *= dim;
|
||||
|
||||
if ((((size / groups) - 1) / 4096 + 1) <= 256) {
|
||||
launch_fake_quantize_kernel_asym(
|
||||
(T*)vals.data_ptr(), size, groups, bits, at::cuda::getCurrentCUDAStream());
|
||||
}
|
||||
return vals;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
at::Tensor ds_sr_quantize_asym(at::Tensor& vals, int groups, int bits)
|
||||
{
|
||||
auto t_size = vals.sizes();
|
||||
int size = 1;
|
||||
for (auto dim : t_size) size *= dim;
|
||||
|
||||
if (((size / groups) / 4 / 1024) <= 256) {
|
||||
launch_sr_fake_quantize_kernel_asym(
|
||||
(T*)vals.data_ptr(), size, groups, bits, at::cuda::getCurrentCUDAStream());
|
||||
}
|
||||
return vals;
|
||||
}
|
||||
|
||||
std::vector<at::Tensor> quantize_kernel(at::Tensor& input_vals,
|
||||
int groups,
|
||||
int numBits,
|
||||
quantize::Type quantType)
|
||||
{
|
||||
auto dtype = at::kFloat;
|
||||
auto params_options = at::TensorOptions()
|
||||
.dtype(dtype)
|
||||
.layout(at::kStrided)
|
||||
.device(at::kCUDA)
|
||||
.requires_grad(false);
|
||||
const int param_elems = (quantize::requires_offset(quantType)) ? 2 : 1;
|
||||
auto params = torch::empty({groups, param_elems}, params_options);
|
||||
|
||||
auto output_options = at::TensorOptions()
|
||||
.dtype(at::kChar)
|
||||
.layout(at::kStrided)
|
||||
.device(at::kCUDA)
|
||||
.requires_grad(false);
|
||||
|
||||
auto output_sizes = input_vals.sizes().vec();
|
||||
output_sizes[output_sizes.size() - 1] /= numBits == 8 ? 1 : 2;
|
||||
auto output = torch::empty(output_sizes, output_options);
|
||||
|
||||
const int elems_per_group = at::numel(input_vals) / groups;
|
||||
|
||||
launch_quant((int8_t*)output.data_ptr(),
|
||||
(float*)params.data_ptr(),
|
||||
(__half*)input_vals.data_ptr(),
|
||||
groups,
|
||||
elems_per_group,
|
||||
numBits,
|
||||
quantType,
|
||||
at::cuda::getCurrentCUDAStream());
|
||||
|
||||
return {output, params};
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
at::Tensor dequantize(at::Tensor& quantized_data,
|
||||
at::Tensor& params,
|
||||
int groups,
|
||||
int num_bits,
|
||||
quantize::Type quant_type)
|
||||
{
|
||||
auto dtype = (std::is_same<T, float>::value) ? torch::kFloat32 : torch::kFloat16;
|
||||
auto output_options = at::TensorOptions()
|
||||
.dtype(dtype)
|
||||
.layout(at::kStrided)
|
||||
.device(at::kCUDA)
|
||||
.requires_grad(false);
|
||||
|
||||
auto output_sizes = quantized_data.sizes().vec();
|
||||
output_sizes[output_sizes.size() - 1] *= num_bits == 8 ? 1 : 2;
|
||||
auto output = torch::empty(output_sizes, output_options);
|
||||
|
||||
const int total_elems = at::numel(output);
|
||||
const int elems_per_group = total_elems / groups;
|
||||
|
||||
launch_dequantize_kernel((T*)output.data_ptr(),
|
||||
(const int8_t*)quantized_data.data_ptr(),
|
||||
(const float*)params.data_ptr(),
|
||||
quant_type,
|
||||
num_bits,
|
||||
elems_per_group,
|
||||
total_elems,
|
||||
at::cuda::getCurrentCUDAStream());
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
at::Tensor dequantize_int4_to_half_experimental(at::Tensor& data_in,
|
||||
at::Tensor& scale_buffer,
|
||||
at::Tensor& min_val_buffer,
|
||||
int num_group,
|
||||
int group_size)
|
||||
{
|
||||
auto output_options = at::TensorOptions().dtype(at::kHalf).device(at::kCUDA);
|
||||
auto output = torch::empty({num_group, group_size}, output_options);
|
||||
|
||||
launch_dequantize_int4_to_half_experimental((uint8_t*)data_in.data_ptr(),
|
||||
(half*)output.data_ptr(),
|
||||
(half*)scale_buffer.data_ptr(),
|
||||
(half*)min_val_buffer.data_ptr(),
|
||||
num_group,
|
||||
group_size,
|
||||
at::cuda::getCurrentCUDAStream());
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
at::Tensor dequantize_int8_to_half_experimental(at::Tensor& data_in,
|
||||
at::Tensor& scale_buffer,
|
||||
at::Tensor& min_val_buffer,
|
||||
int num_group,
|
||||
int group_size)
|
||||
{
|
||||
auto output_options = at::TensorOptions().dtype(at::kHalf).device(at::kCUDA);
|
||||
auto output = torch::empty({num_group, group_size}, output_options);
|
||||
|
||||
launch_dequantize_int8_to_half_experimental((uint8_t*)data_in.data_ptr(),
|
||||
(half*)output.data_ptr(),
|
||||
(half*)scale_buffer.data_ptr(),
|
||||
(half*)min_val_buffer.data_ptr(),
|
||||
num_group,
|
||||
group_size,
|
||||
at::cuda::getCurrentCUDAStream());
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
std::vector<at::Tensor> ds_swizzle_quant(at::Tensor& input_vals,
|
||||
int groups,
|
||||
int num_bits,
|
||||
quantize::Type quant_type,
|
||||
int pipeline_size,
|
||||
int nodes,
|
||||
int devices_per_node)
|
||||
{
|
||||
auto scales_options = at::TensorOptions()
|
||||
.dtype(at::kFloat)
|
||||
.layout(at::kStrided)
|
||||
.device(at::kCUDA)
|
||||
.requires_grad(false);
|
||||
const int scales_elems = (quantize::requires_offset(quant_type)) ? 2 : 1;
|
||||
auto scales = torch::empty({groups, scales_elems}, scales_options);
|
||||
|
||||
auto output_options = at::TensorOptions()
|
||||
.dtype(at::kChar)
|
||||
.layout(at::kStrided)
|
||||
.device(at::kCUDA)
|
||||
.requires_grad(false);
|
||||
|
||||
const int quantization_scalar = 8 / num_bits;
|
||||
const int compressed_vals = at::numel(input_vals) / quantization_scalar;
|
||||
|
||||
auto output = torch::empty({compressed_vals}, output_options);
|
||||
const int elems_per_group = at::numel(input_vals) / groups;
|
||||
|
||||
launch_swizzled_quant((int8_t*)output.data_ptr(),
|
||||
(float*)scales.data_ptr(),
|
||||
(__half*)input_vals.data_ptr(),
|
||||
num_bits,
|
||||
quant_type,
|
||||
groups,
|
||||
elems_per_group,
|
||||
pipeline_size,
|
||||
nodes,
|
||||
devices_per_node,
|
||||
at::cuda::getCurrentCUDAStream());
|
||||
|
||||
return {output, scales};
|
||||
}
|
||||
|
||||
std::vector<at::Tensor> quantized_reduction(at::Tensor& input_vals,
|
||||
at::Tensor& input_scales,
|
||||
int in_groups,
|
||||
int out_groups,
|
||||
int num_bits,
|
||||
quantize::Type quant_type,
|
||||
int devices_per_node)
|
||||
{
|
||||
auto scales_options = at::TensorOptions()
|
||||
.dtype(at::kFloat)
|
||||
.layout(at::kStrided)
|
||||
.device(at::kCUDA)
|
||||
.requires_grad(false);
|
||||
const int scales_elems = (quantize::requires_offset(quant_type)) ? 2 : 1;
|
||||
auto scales = torch::empty({out_groups, scales_elems}, scales_options);
|
||||
|
||||
auto output_options = at::TensorOptions()
|
||||
.dtype(at::kChar)
|
||||
.layout(at::kStrided)
|
||||
.device(at::kCUDA)
|
||||
.requires_grad(false);
|
||||
|
||||
std::vector<int64_t> sz_vector(input_vals.sizes().begin(), input_vals.sizes().end());
|
||||
sz_vector[sz_vector.size() - 1] = sz_vector.back() / devices_per_node; // num of GPU per nodes
|
||||
at::IntArrayRef sz(sz_vector);
|
||||
auto output = torch::empty(sz, output_options);
|
||||
|
||||
const int elems_per_in_tensor = at::numel(input_vals) / devices_per_node;
|
||||
const int elems_per_in_group = elems_per_in_tensor / (in_groups / devices_per_node);
|
||||
const int elems_per_out_group = elems_per_in_tensor / out_groups;
|
||||
|
||||
launch_dequant_reduce((int8_t*)output.data_ptr(),
|
||||
(float*)scales.data_ptr(),
|
||||
(const int8_t*)input_vals.data_ptr(),
|
||||
(const float*)input_scales.data_ptr(),
|
||||
devices_per_node,
|
||||
num_bits,
|
||||
quant_type,
|
||||
out_groups,
|
||||
elems_per_out_group,
|
||||
elems_per_in_tensor,
|
||||
in_groups / devices_per_node,
|
||||
elems_per_in_group,
|
||||
at::cuda::getCurrentCUDAStream());
|
||||
return {output, scales};
|
||||
}
|
||||
|
||||
PYBIND11_MODULE(TORCH_EXTENSION_NAME, m)
|
||||
{
|
||||
m.def("ds_quantize_fp32", &ds_quantize<float>, "DeepSpeed Quantize with fp32 (CUDA)");
|
||||
m.def("ds_quantize_fp16", &ds_quantize<__half>, "DeepSpeed Quantize with fp16 (CUDA)");
|
||||
m.def("ds_sr_quantize_fp32", &ds_sr_quantize<float>, "DeepSpeed Quantize with fp32 (CUDA)");
|
||||
m.def("ds_sr_quantize_fp16", &ds_sr_quantize<__half>, "DeepSpeed Quantize with fp16 (CUDA)");
|
||||
m.def("ds_quantize_asym_fp32", &ds_quantize_asym<float>, "DeepSpeed Quantize with fp32 (CUDA)");
|
||||
m.def(
|
||||
"ds_quantize_asym_fp16", &ds_quantize_asym<__half>, "DeepSpeed Quantize with fp16 (CUDA)");
|
||||
m.def("ds_sr_quantize_asym_fp32",
|
||||
&ds_sr_quantize_asym<float>,
|
||||
"DeepSpeed Quantize with fp32 (CUDA)");
|
||||
m.def("ds_sr_quantize_asym_fp16",
|
||||
&ds_sr_quantize_asym<__half>,
|
||||
"DeepSpeed Quantize with fp16 (CUDA)");
|
||||
pybind11::enum_<quantize::Type>(m, "QuantizationType")
|
||||
.value("Symmetric", quantize::Type::Symmetric)
|
||||
.value("Asymmetric", quantize::Type::Asymmetric)
|
||||
.export_values();
|
||||
m.def("quantize", &quantize_kernel);
|
||||
m.def("dequantize", &dequantize<__half>);
|
||||
m.def("dequantize_fp32", &dequantize<float>);
|
||||
m.def("dequantize_int4_to_half_experimental",
|
||||
&dequantize_int4_to_half_experimental,
|
||||
"Dequantize int4 to half (experimental)");
|
||||
m.def("dequantize_int8_to_half_experimental",
|
||||
&dequantize_int8_to_half_experimental,
|
||||
"Dequantize int8 to half (experimental)");
|
||||
m.def("swizzle_quant", &ds_swizzle_quant);
|
||||
m.def("quantized_reduction", &quantized_reduction);
|
||||
}
|
||||
@@ -1,161 +0,0 @@
|
||||
# Copyright (c) Microsoft Corporation.
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
# DeepSpeed Team
|
||||
|
||||
import os
|
||||
import torch
|
||||
import deepspeed
|
||||
import subprocess
|
||||
import argparse
|
||||
import psutil
|
||||
from .ops.op_builder.all_ops import ALL_OPS
|
||||
from .git_version_info import installed_ops, torch_info
|
||||
from deepspeed.accelerator import get_accelerator
|
||||
|
||||
GREEN = '\033[92m'
|
||||
RED = '\033[91m'
|
||||
YELLOW = '\033[93m'
|
||||
END = '\033[0m'
|
||||
SUCCESS = f"{GREEN} [SUCCESS] {END}"
|
||||
OKAY = f"{GREEN}[OKAY]{END}"
|
||||
WARNING = f"{YELLOW}[WARNING]{END}"
|
||||
FAIL = f'{RED}[FAIL]{END}'
|
||||
INFO = '[INFO]'
|
||||
|
||||
color_len = len(GREEN) + len(END)
|
||||
okay = f"{GREEN}[OKAY]{END}"
|
||||
warning = f"{YELLOW}[WARNING]{END}"
|
||||
|
||||
|
||||
def op_report(verbose=True):
|
||||
max_dots = 23
|
||||
max_dots2 = 11
|
||||
h = ["op name", "installed", "compatible"]
|
||||
print("-" * (max_dots + max_dots2 + len(h[0]) + len(h[1])))
|
||||
print("DeepSpeed C++/CUDA extension op report")
|
||||
print("-" * (max_dots + max_dots2 + len(h[0]) + len(h[1])))
|
||||
|
||||
print("NOTE: Ops not installed will be just-in-time (JIT) compiled at\n"
|
||||
" runtime if needed. Op compatibility means that your system\n"
|
||||
" meet the required dependencies to JIT install the op.")
|
||||
|
||||
print("-" * (max_dots + max_dots2 + len(h[0]) + len(h[1])))
|
||||
print("JIT compiled ops requires ninja")
|
||||
ninja_status = OKAY if ninja_installed() else FAIL
|
||||
print('ninja', "." * (max_dots - 5), ninja_status)
|
||||
print("-" * (max_dots + max_dots2 + len(h[0]) + len(h[1])))
|
||||
print(h[0], "." * (max_dots - len(h[0])), h[1], "." * (max_dots2 - len(h[1])), h[2])
|
||||
print("-" * (max_dots + max_dots2 + len(h[0]) + len(h[1])))
|
||||
installed = f"{GREEN}[YES]{END}"
|
||||
no = f"{YELLOW}[NO]{END}"
|
||||
for op_name, builder in ALL_OPS.items():
|
||||
dots = "." * (max_dots - len(op_name))
|
||||
is_compatible = OKAY if builder.is_compatible(verbose) else no
|
||||
is_installed = installed if installed_ops.get(op_name, False) else no
|
||||
dots2 = '.' * ((len(h[1]) + (max_dots2 - len(h[1]))) - (len(is_installed) - color_len))
|
||||
print(op_name, dots, is_installed, dots2, is_compatible)
|
||||
print("-" * (max_dots + max_dots2 + len(h[0]) + len(h[1])))
|
||||
|
||||
|
||||
def ninja_installed():
|
||||
try:
|
||||
import ninja # noqa: F401 # type: ignore
|
||||
except ImportError:
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
def nvcc_version():
|
||||
import torch.utils.cpp_extension
|
||||
cuda_home = torch.utils.cpp_extension.CUDA_HOME
|
||||
if cuda_home is None:
|
||||
return f"{RED} [FAIL] cannot find CUDA_HOME via torch.utils.cpp_extension.CUDA_HOME={torch.utils.cpp_extension.CUDA_HOME} {END}"
|
||||
try:
|
||||
output = subprocess.check_output([cuda_home + "/bin/nvcc", "-V"], universal_newlines=True)
|
||||
except FileNotFoundError:
|
||||
return f"{RED} [FAIL] nvcc missing {END}"
|
||||
output_split = output.split()
|
||||
release_idx = output_split.index("release")
|
||||
release = output_split[release_idx + 1].replace(',', '').split(".")
|
||||
return ".".join(release)
|
||||
|
||||
|
||||
def get_shm_size():
|
||||
try:
|
||||
temp_dir = os.getenv('TEMP') or os.getenv('TMP') or os.path.join(os.path.expanduser('~'), 'tmp')
|
||||
shm_stats = psutil.disk_usage(temp_dir)
|
||||
shm_size = shm_stats.total
|
||||
shm_hbytes = human_readable_size(shm_size)
|
||||
warn = []
|
||||
if shm_size < 512 * 1024**2:
|
||||
warn.append(
|
||||
f" {YELLOW} [WARNING] Shared memory size might be too small, consider increasing it. {END}"
|
||||
)
|
||||
# Add additional warnings specific to your use case if needed.
|
||||
return shm_hbytes, warn
|
||||
except Exception as e:
|
||||
return "UNKNOWN", [f"Error getting shared memory size: {e}"]
|
||||
|
||||
|
||||
def human_readable_size(size):
|
||||
units = ['B', 'KB', 'MB', 'GB', 'TB']
|
||||
i = 0
|
||||
while size >= 1024 and i < len(units) - 1:
|
||||
size /= 1024
|
||||
i += 1
|
||||
return f'{size:.2f} {units[i]}'
|
||||
|
||||
|
||||
def debug_report():
|
||||
max_dots = 33
|
||||
|
||||
report = [("torch install path", torch.__path__), ("torch version", torch.__version__),
|
||||
("deepspeed install path", deepspeed.__path__),
|
||||
("deepspeed info", f"{deepspeed.__version__}, {deepspeed.__git_hash__}, {deepspeed.__git_branch__}")]
|
||||
if get_accelerator().device_name() == 'cuda':
|
||||
hip_version = getattr(torch.version, "hip", None)
|
||||
report.extend([("torch cuda version", torch.version.cuda), ("torch hip version", hip_version),
|
||||
("nvcc version", (None if hip_version else nvcc_version())),
|
||||
("deepspeed wheel compiled w.", f"torch {torch_info['version']}, " +
|
||||
(f"hip {torch_info['hip_version']}" if hip_version else f"cuda {torch_info['cuda_version']}"))
|
||||
])
|
||||
else:
|
||||
report.extend([("deepspeed wheel compiled w.", f"torch {torch_info['version']} ")])
|
||||
|
||||
report.append(("shared memory (/dev/shm) size", get_shm_size()))
|
||||
|
||||
print("DeepSpeed general environment info:")
|
||||
for name, value in report:
|
||||
warns = []
|
||||
if isinstance(value, tuple):
|
||||
value, warns = value
|
||||
print(name, "." * (max_dots - len(name)), value)
|
||||
if warns:
|
||||
for warn in warns:
|
||||
print(warn)
|
||||
|
||||
|
||||
def parse_arguments():
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument('--hide_operator_status',
|
||||
action='store_true',
|
||||
help='Suppress display of installation and compatibility statuses of DeepSpeed operators. ')
|
||||
parser.add_argument('--hide_errors_and_warnings', action='store_true', help='Suppress warning and error messages.')
|
||||
args = parser.parse_args()
|
||||
return args
|
||||
|
||||
|
||||
def main(hide_operator_status=False, hide_errors_and_warnings=False):
|
||||
if not hide_operator_status:
|
||||
op_report(verbose=not hide_errors_and_warnings)
|
||||
debug_report()
|
||||
|
||||
|
||||
def cli_main():
|
||||
args = parse_arguments()
|
||||
main(hide_operator_status=args.hide_operator_status, hide_errors_and_warnings=args.hide_errors_and_warnings)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -1,18 +0,0 @@
|
||||
These are the modified files you would need for a MANUAL build of DeepSpeed 11.1 or 11.2.
|
||||
|
||||
They were created by a mix of instructions from erew123 and S95Sedan.
|
||||
|
||||
|
||||
- You still have to download the DeepSpeed v11.1 or v11.2 installation files from Microsoft to use these files.
|
||||
|
||||
https://github.com/microsoft/DeepSpeed/releases/tag/v0.11.2
|
||||
|
||||
|
||||
- These files were provided by S95Sedan here:
|
||||
|
||||
https://github.com/S95Sedan/Deepspeed-Windows
|
||||
|
||||
|
||||
- They are related to the manual build of DeepSpeed for AllTalk, which is linked here:
|
||||
|
||||
https://github.com/erew123/alltalk_tts/tree/main?tab=readme-ov-file#-option-2---a-bit-more-complicated
|
||||
@@ -0,0 +1,19 @@
|
||||
FROM nvidia/cuda:12.1.1-devel-ubuntu22.04
|
||||
ENV HOST 0.0.0.0
|
||||
RUN apt-get update && apt-get upgrade -y \
|
||||
&& apt-get install -y git build-essential portaudio19-dev \
|
||||
python3 python3-pip gcc wget \
|
||||
ocl-icd-opencl-dev opencl-headers clinfo \
|
||||
libclblast-dev libopenblas-dev libaio-dev \
|
||||
&& mkdir -p /etc/OpenCL/vendors && echo "libnvidia-opencl.so.1" > /etc/OpenCL/vendors/nvidia.icd \
|
||||
&& ln -s /usr/bin/python3 /usr/bin/python
|
||||
|
||||
WORKDIR /app
|
||||
COPY . .
|
||||
ENV CUDA_DOCKER_ARCH=all
|
||||
RUN pip install --no-cache-dir --no-deps -r system/requirements/requirements_docker.txt && \
|
||||
pip install --no-cache-dir deepspeed
|
||||
|
||||
EXPOSE 7851 7852
|
||||
RUN chmod +x launch.sh
|
||||
ENTRYPOINT ["sh", "-c", "./launch.sh"]
|
||||
@@ -0,0 +1,659 @@
|
||||
@echo off
|
||||
cd /D "%~dp0"
|
||||
setlocal enabledelayedexpansion
|
||||
@set "PATH=%PATH%;%SystemRoot%\system32"
|
||||
|
||||
:: Generate the ESC character
|
||||
for /F %%a in ('echo prompt $E ^| cmd') do set "ESC=%%a"
|
||||
|
||||
:: Standard Colors
|
||||
set "BLACK=%ESC%[30m"
|
||||
set "RED=%ESC%[31m"
|
||||
set "GREEN=%ESC%[32m"
|
||||
set "YELLOW=%ESC%[33m"
|
||||
set "BLUE=%ESC%[34m"
|
||||
set "MAGENTA=%ESC%[35m"
|
||||
set "CYAN=%ESC%[36m"
|
||||
set "WHITE=%ESC%[37m"
|
||||
set "L_BLACK=%ESC%[90m"
|
||||
set "L_RED=%ESC%[91m"
|
||||
set "L_GREEN=%ESC%[92m"
|
||||
set "L_YELLOW=%ESC%[93m"
|
||||
set "L_BLUE=%ESC%[94m"
|
||||
set "L_MAGENTA=%ESC%[95m"
|
||||
set "L_CYAN=%ESC%[96m"
|
||||
set "L_WHITE=%ESC%[97m"
|
||||
set "RESET=%ESC%[0m"
|
||||
|
||||
echo "%CD%"| findstr /C:" " >nul && echo. && echo %L_BLUE%ALLTALK WINDOWS SETUP UTILITY%RESET% && echo. && echo. && echo You are trying to install AllTalk in a folder that has a space in the folder path e.g. && echo. && echo C:\%L_RED%program files%RESET%\alltalk_tts && echo. && echo This causes errors with Conda and Python scripts. Please follow this link for reference: && echo. && echo %L_CYAN%https://docs.anaconda.com/free/working-with-conda/reference/faq/#installing-anaconda%RESET% && echo. && echo Please use a folder path that has no spaces in it e.g. && echo. && echo C:\myfiles\alltalk_tts\ && echo. && echo. && pause && goto :end
|
||||
|
||||
@rem Check for special characters in installation path
|
||||
set "SPCHARMESSAGE="WARNING: Special characters were detected in the installation path!" " This can cause the installation to fail!""
|
||||
echo "%CD%"| findstr /R /C:"[!#\$%&()\*+,;<=>?@\[\]\^`{|}~]" >nul && (
|
||||
call :PrintBigMessage %SPCHARMESSAGE%
|
||||
)
|
||||
set SPCHARMESSAGE=
|
||||
|
||||
@rem Check if curl is available
|
||||
curl --version >nul 2>&1
|
||||
if "%ERRORLEVEL%" NEQ "0" (
|
||||
echo curl is not available on this system. Please install curl then re-run the script https://curl.se/
|
||||
echo or perform a manual installation of a Conda Python environment.
|
||||
goto end
|
||||
)
|
||||
|
||||
:MainMenu
|
||||
cls
|
||||
echo.
|
||||
echo %L_BLUE%ALLTALK WINDOWS SETUP UTILITY%RESET%
|
||||
echo.
|
||||
echo INSTALLATION TYPE
|
||||
echo 1) I am using AllTalk as part of %L_GREEN%Text-generation-webui%RESET%
|
||||
echo 2) I am using AllTalk as a %L_GREEN%Standalone Application%RESET%
|
||||
echo.
|
||||
echo 9) %L_RED%Exit/Quit%RESET%
|
||||
echo.
|
||||
set /p UserOption=" Enter your choice: "
|
||||
|
||||
if "%UserOption%"=="1" goto WebUIMenu
|
||||
if "%UserOption%"=="2" goto StandaloneMenu
|
||||
if "%UserOption%"=="9" goto End
|
||||
goto MainMenu
|
||||
|
||||
:WebUIMenu
|
||||
cls
|
||||
echo.
|
||||
echo %L_BLUE%TEXT-GENERATION-WEBUI SETUP%RESET%
|
||||
echo.
|
||||
echo Please ensure you have started your Text-generation-webui Python
|
||||
echo environment. If you have NOT done this, please run %L_GREEN%cmd_windows.bat%RESET%
|
||||
echo in the %L_GREEN%text-generation-webui%RESET% folder and then re-run this script.
|
||||
echo.
|
||||
echo BASE REQUIREMENTS
|
||||
echo 1) Apply/Re-Apply the requirements for %L_GREEN%Text-generation-webui%RESET%.
|
||||
echo.
|
||||
echo OPTIONAL
|
||||
echo 2) Git Pull the latest AllTalk updates from Github
|
||||
echo.
|
||||
echo DEEPSPEED FOR %L_YELLOW=%PyTorch 2.1.x%RESET%
|
||||
echo 4) Install DeepSpeed v11.2 for CUDA %L_GREEN%11.8%RESET% and Python-3.11.x and %L_YELLOW%PyTorch 2.1.x%RESET%.
|
||||
echo 5) Install DeepSpeed v11.2 for CUDA %L_GREEN%12.1%RESET% and Python-3.11.x and %L_YELLOW%PyTorch 2.1.x%RESET%.
|
||||
echo.
|
||||
echo DEEPSPEED FOR %L_YELLOW=%PyTorch 2.2.x%RESET% (March 2024 builds of Text-gen-webui and later)
|
||||
echo 6) Install DeepSpeed v14.0 for CUDA %L_GREEN%12.1%RESET% and Python-3.11.x and %L_YELLOW%PyTorch 2.2.x%RESET%.
|
||||
echo 7) Install DeepSpeed v14.0 for CUDA %L_GREEN%11.8%RESET% and Python-3.11.x and %L_YELLOW%PyTorch 2.2.x%RESET%.
|
||||
echo.
|
||||
echo U) Uninstall DeepSpeed.
|
||||
echo.
|
||||
echo. OTHER
|
||||
echo 8) Generate a diagnostics file.
|
||||
echo.
|
||||
echo 9) %L_RED%Exit/Quit%RESET%
|
||||
echo.
|
||||
set /p WebUIOption="Enter your choice: "
|
||||
if "%WebUIOption%"=="1" goto InstallNvidiaTextGen
|
||||
if "%WebUIOption%"=="2" goto TGGitpull
|
||||
if "%WebUIOption%"=="4" goto InstallDeepSpeed118TextGen
|
||||
if "%WebUIOption%"=="5" goto InstallDeepSpeed121TextGen
|
||||
if "%WebUIOption%"=="6" goto InstallDeepSpeed121TextGenPytorch221
|
||||
if "%WebUIOption%"=="7" goto InstallDeepSpeed118TextGenPytorch221
|
||||
if "%WebUIOption%"=="U" goto UnInstallDeepSpeed
|
||||
if "%WebUIOption%"=="u" goto UnInstallDeepSpeed
|
||||
if "%WebUIOption%"=="8" goto GenerateDiagsTextGen
|
||||
if "%WebUIOption%"=="9" goto End
|
||||
goto WebUIMenu
|
||||
|
||||
:StandaloneMenu
|
||||
cls
|
||||
echo.
|
||||
echo %L_BLUE%ALLTALK STANDALONE APPLICATION SETUP%RESET%
|
||||
echo.
|
||||
echo BASE REQUIREMENTS
|
||||
echo 1) Install AllTalk as a Standalone Application
|
||||
echo.
|
||||
echo OPTIONAL
|
||||
echo 2) Git Pull the latest AllTalk updates from Github
|
||||
echo 3) Re-Apply/Update the requirements file
|
||||
echo 4) Delete AllTalk's custom Python environment
|
||||
echo 5) Purge the PIP cache
|
||||
echo.
|
||||
echo. OTHER
|
||||
echo 8) Generate a diagnostics file
|
||||
echo.
|
||||
echo 9) %L_RED%Exit/Quit%RESET%
|
||||
echo.
|
||||
set /p StandaloneOption="Enter your choice: "
|
||||
if "%StandaloneOption%"=="1" goto InstallCustomStandalone
|
||||
if "%StandaloneOption%"=="2" goto STGitpull
|
||||
if "%StandaloneOption%"=="3" goto STReapplyrequirements
|
||||
if "%StandaloneOption%"=="4" goto STDeleteCustomStandalone
|
||||
if "%StandaloneOption%"=="5" goto STPurgepipcache
|
||||
if "%StandaloneOption%"=="8" goto GenerateDiagsStandalone
|
||||
if "%StandaloneOption%"=="9" goto EndStandalone
|
||||
goto StandaloneMenu
|
||||
|
||||
:InstallNvidiaTextGen
|
||||
pip install -r system\requirements\requirements_textgen.txt
|
||||
if %ERRORLEVEL% neq 0 (
|
||||
echo.
|
||||
echo There was an error installing the requirements.
|
||||
echo Have you started your Text-gen-webui Python environment
|
||||
echo with cmd_{yourOS} before running atsetup.bat?
|
||||
echo Press any key to return to the menu.
|
||||
echo.
|
||||
pause
|
||||
goto WebUIMenu
|
||||
)
|
||||
Echo.
|
||||
echo.
|
||||
Echo Requirements installed successfully.
|
||||
Echo.
|
||||
pause
|
||||
goto WebUIMenu
|
||||
|
||||
:TGGitpull
|
||||
git pull
|
||||
if %ERRORLEVEL% neq 0 (
|
||||
echo.
|
||||
echo There was an error pulling from Github.
|
||||
echo Press any key to return to the menu.
|
||||
echo.
|
||||
pause
|
||||
goto WebUIMenu
|
||||
)
|
||||
Echo.
|
||||
echo AllTalk Updated from Github. Please re-apply
|
||||
echo the latest requirements file. Option 1
|
||||
Echo.
|
||||
pause
|
||||
goto WebUIMenu
|
||||
|
||||
:InstallDeepSpeed118TextGen
|
||||
echo Downloading DeepSpeed...
|
||||
curl -LO https://github.com/erew123/alltalk_tts/releases/download/deepspeed/deepspeed-0.11.2+cuda118-cp311-cp311-win_amd64.whl
|
||||
if %ERRORLEVEL% neq 0 (
|
||||
echo.
|
||||
echo Failed to download DeepSpeed wheel file.
|
||||
echo Please check your internet connection or try again later.
|
||||
echo Press any key to return to the menu.
|
||||
echo.
|
||||
pause
|
||||
goto WebUIMenu
|
||||
)
|
||||
echo DeepSpeed wheel file downloaded successfully.
|
||||
echo Installing DeepSpeed...
|
||||
pip install deepspeed-0.11.2+cuda118-cp311-cp311-win_amd64.whl
|
||||
if %ERRORLEVEL% neq 0 (
|
||||
echo.
|
||||
echo Failed to install DeepSpeed.
|
||||
echo Please check if the wheel file is compatible with your system.
|
||||
echo Press any key to return to the menu.
|
||||
echo.
|
||||
pause
|
||||
goto WebUIMenu
|
||||
)
|
||||
Echo.
|
||||
echo.
|
||||
Echo DeepSpeed installed successfully.
|
||||
Echo.
|
||||
del deepspeed-0.11.2+cuda118-cp311-cp311-win_amd64.whl
|
||||
pause
|
||||
goto WebUIMenu
|
||||
|
||||
:InstallDeepSpeed121TextGen
|
||||
echo Downloading DeepSpeed...
|
||||
curl -LO https://github.com/erew123/alltalk_tts/releases/download/deepspeed/deepspeed-0.11.2+cuda121-cp311-cp311-win_amd64.whl
|
||||
if %ERRORLEVEL% neq 0 (
|
||||
echo.
|
||||
echo Failed to download DeepSpeed wheel file.
|
||||
echo Please check your internet connection or try again later.
|
||||
echo Press any key to return to the menu.
|
||||
echo.
|
||||
pause
|
||||
goto WebUIMenu
|
||||
)
|
||||
echo DeepSpeed wheel file downloaded successfully.
|
||||
echo Installing DeepSpeed...
|
||||
pip install deepspeed-0.11.2+cuda121-cp311-cp311-win_amd64.whl
|
||||
if %ERRORLEVEL% neq 0 (
|
||||
echo.
|
||||
echo Failed to install DeepSpeed.
|
||||
echo Please check if the wheel file is compatible with your system.
|
||||
echo Press any key to return to the menu.
|
||||
echo.
|
||||
pause
|
||||
goto WebUIMenu
|
||||
)
|
||||
Echo.
|
||||
echo.
|
||||
Echo DeepSpeed installed successfully.
|
||||
Echo.
|
||||
del deepspeed-0.11.2+cuda121-cp311-cp311-win_amd64.whl
|
||||
pause
|
||||
goto WebUIMenu
|
||||
|
||||
:InstallDeepSpeed121TextGenPytorch221
|
||||
echo Downloading DeepSpeed...
|
||||
curl -LO https://github.com/erew123/alltalk_tts/releases/download/DeepSpeed-14.0/deepspeed-0.14.0+ce78a63-cp311-cp311-win_amd64.whl
|
||||
if %ERRORLEVEL% neq 0 (
|
||||
echo.
|
||||
echo Failed to download DeepSpeed wheel file.
|
||||
echo Please check your internet connection or try again later.
|
||||
echo Press any key to return to the menu.
|
||||
echo.
|
||||
pause
|
||||
goto WebUIMenu
|
||||
)
|
||||
echo DeepSpeed wheel file downloaded successfully.
|
||||
echo Installing DeepSpeed...
|
||||
pip install deepspeed-0.14.0+ce78a63-cp311-cp311-win_amd64.whl
|
||||
if %ERRORLEVEL% neq 0 (
|
||||
echo.
|
||||
echo Failed to install DeepSpeed.
|
||||
echo Please check if the wheel file is compatible with your system.
|
||||
echo Press any key to return to the menu.
|
||||
echo.
|
||||
pause
|
||||
goto WebUIMenu
|
||||
)
|
||||
Echo.
|
||||
echo.
|
||||
Echo DeepSpeed installed successfully.
|
||||
Echo.
|
||||
del deepspeed-0.14.0+ce78a63-cp311-cp311-win_amd64.whl
|
||||
pause
|
||||
goto WebUIMenu
|
||||
|
||||
:InstallDeepSpeed118TextGenPytorch221
|
||||
echo Downloading DeepSpeed...
|
||||
curl -LO https://github.com/erew123/alltalk_tts/releases/download/DeepSpeed-14.0/deepspeed-0.14.0+cu118-cp311-cp311-win_amd64.whl
|
||||
if %ERRORLEVEL% neq 0 (
|
||||
echo.
|
||||
echo Failed to download DeepSpeed wheel file.
|
||||
echo Please check your internet connection or try again later.
|
||||
echo Press any key to return to the menu.
|
||||
echo.
|
||||
pause
|
||||
goto WebUIMenu
|
||||
)
|
||||
echo DeepSpeed wheel file downloaded successfully.
|
||||
echo Installing DeepSpeed...
|
||||
pip install deepspeed-0.14.0+cu118-cp311-cp311-win_amd64.whl
|
||||
if %ERRORLEVEL% neq 0 (
|
||||
echo.
|
||||
echo Failed to install DeepSpeed.
|
||||
echo Please check if the wheel file is compatible with your system.
|
||||
echo Press any key to return to the menu.
|
||||
echo.
|
||||
pause
|
||||
goto WebUIMenu
|
||||
)
|
||||
Echo.
|
||||
echo.
|
||||
Echo DeepSpeed installed successfully.
|
||||
Echo.
|
||||
del deepspeed-0.14.0+cu118-cp311-cp311-win_amd64.whl
|
||||
pause
|
||||
goto WebUIMenu
|
||||
|
||||
:UnInstallDeepSpeed
|
||||
pip uninstall deepspeed
|
||||
if %ERRORLEVEL% neq 0 (
|
||||
echo.
|
||||
echo There was an error uninstalling DeepSpeed.
|
||||
echo Press any key to return to the menu.
|
||||
echo.
|
||||
pause
|
||||
goto WebUIMenu
|
||||
)
|
||||
echo.
|
||||
Echo DeepSpeed uninstalled successfully.
|
||||
Echo.
|
||||
pause
|
||||
goto WebUIMenu
|
||||
|
||||
:GenerateDiagsTextGen
|
||||
Python diagnostics.py
|
||||
if %ERRORLEVEL% neq 0 (
|
||||
echo.
|
||||
echo There was an error running diagnostics. Have you correctly started your
|
||||
echo Text-generation-webui Python environment with %L_GREEN%cmd_windows.bat%RESET%?
|
||||
echo.
|
||||
pause
|
||||
goto WebUIMenu
|
||||
)
|
||||
Echo.
|
||||
echo.
|
||||
Echo Diagnostics.log generated. Please scroll up to look over the log.
|
||||
Echo.
|
||||
pause
|
||||
goto WebUIMenu
|
||||
|
||||
:InstallCustomStandalone
|
||||
set PATH=%PATH%;%SystemRoot%\system32
|
||||
|
||||
@rem Check if curl is available
|
||||
curl --version >nul 2>&1
|
||||
if "%ERRORLEVEL%" NEQ "0" (
|
||||
echo curl is not available on this system. Please install curl then re-run the script https://curl.se/
|
||||
echo or perform a manual installation of a Conda Python environment.
|
||||
goto end
|
||||
)
|
||||
echo "%CD%"| findstr /C:" " >nul && echo This script relies on Miniconda which can not be silently installed under a path with spaces. Please correct your folder names and re-try installation. && goto exit
|
||||
|
||||
@rem Check for special characters in installation path
|
||||
set "SPCHARMESSAGE=WARNING: Special characters were detected in the installation path! This can cause the installation to fail!"
|
||||
echo "%CD%"| findstr /R /C:"[!#\$%&()\*+,;<=>?@\[\]\^`{|}~]" >nul && (
|
||||
call :PrintBigMessage %SPCHARMESSAGE%
|
||||
)
|
||||
set SPCHARMESSAGE=
|
||||
|
||||
@rem fix failed install when installing to a separate drive
|
||||
set TMP=%cd%\alltalk_environment
|
||||
set TEMP=%cd%\alltalk_environment
|
||||
|
||||
@rem deactivate existing conda envs as needed to avoid conflicts
|
||||
(call conda deactivate && call conda deactivate && call conda deactivate) 2>nul
|
||||
|
||||
@rem config
|
||||
set INSTALL_DIR=%cd%\alltalk_environment
|
||||
set CONDA_ROOT_PREFIX=%cd%\alltalk_environment\conda
|
||||
set INSTALL_ENV_DIR=%cd%\alltalk_environment\env
|
||||
set MINICONDA_DOWNLOAD_URL=https://repo.anaconda.com/miniconda/Miniconda3-py310_23.3.1-0-Windows-x86_64.exe
|
||||
set conda_exists=F
|
||||
|
||||
@rem figure out whether git and conda need to be installed
|
||||
call "%CONDA_ROOT_PREFIX%\_conda.exe" --version >nul 2>&1
|
||||
if "%ERRORLEVEL%" EQU "0" (
|
||||
set conda_exists=T
|
||||
call "%CONDA_ROOT_PREFIX%\condabin\conda.bat" activate "%INSTALL_ENV_DIR%"
|
||||
goto RunScript
|
||||
)
|
||||
|
||||
@rem download and install conda if not installed
|
||||
echo Downloading Miniconda from %MINICONDA_DOWNLOAD_URL% to %INSTALL_DIR%\miniconda_installer.exe
|
||||
mkdir "%INSTALL_DIR%"
|
||||
call curl -Lk "%MINICONDA_DOWNLOAD_URL%" > "%INSTALL_DIR%\miniconda_installer.exe" || ( echo. && echo Miniconda failed to download. && goto end )
|
||||
echo Installing Miniconda to %CONDA_ROOT_PREFIX%
|
||||
start /wait "" "%INSTALL_DIR%\miniconda_installer.exe" /InstallationType=JustMe /NoShortcuts=1 /AddToPath=0 /RegisterPython=0 /NoRegistry=1 /S /D=%CONDA_ROOT_PREFIX%
|
||||
echo Miniconda version:
|
||||
call "%CONDA_ROOT_PREFIX%\_conda.exe" --version || ( echo. && echo Miniconda not found. && goto end )
|
||||
|
||||
@rem create the installer env
|
||||
echo Packages to install: %PACKAGES_TO_INSTALL%
|
||||
call "%CONDA_ROOT_PREFIX%\_conda.exe" create --no-shortcuts -y -k --prefix "%INSTALL_ENV_DIR%" python=3.11 || ( echo. && echo Conda environment creation failed. && goto end )
|
||||
|
||||
@rem check if conda environment was actually created
|
||||
if not exist "%INSTALL_ENV_DIR%\python.exe" ( echo. && echo Conda environment is empty. && goto end )
|
||||
|
||||
@rem activate installer env
|
||||
call "%CONDA_ROOT_PREFIX%\condabin\conda.bat" activate "%INSTALL_ENV_DIR%" || ( echo. && echo Miniconda hook not found. && goto end )
|
||||
|
||||
echo.
|
||||
echo Downloading and installing PyTorch. This step can take a long time
|
||||
echo depending on your internet connection and hard drive speed. Please
|
||||
echo be patient.
|
||||
echo.
|
||||
pip install torch==2.2.2+cu121 torchaudio>=2.2.2+cu121 --upgrade --force-reinstall --extra-index-url https://download.pytorch.org/whl/cu121
|
||||
echo Installing other requirements.
|
||||
echo.
|
||||
pip install -r system\requirements\requirements_standalone.txt
|
||||
curl -LO https://github.com/erew123/alltalk_tts/releases/download/DeepSpeed-14.0/deepspeed-0.14.0+ce78a63-cp311-cp311-win_amd64.whl
|
||||
echo Installing DeepSpeed...
|
||||
pip install deepspeed-0.14.0+ce78a63-cp311-cp311-win_amd64.whl
|
||||
del deepspeed-0.14.0+ce78a63-cp311-cp311-win_amd64.whl
|
||||
|
||||
|
||||
@rem Create start_environment.bat to run AllTalk environment
|
||||
echo @echo off > start_environment.bat
|
||||
echo cd /D "%~dp0" >> start_environment.bat
|
||||
echo set CONDA_ROOT_PREFIX=%cd%\alltalk_environment\conda >> start_environment.bat
|
||||
echo set INSTALL_ENV_DIR=%cd%\alltalk_environment\env >> start_environment.bat
|
||||
echo call "%CONDA_ROOT_PREFIX%\condabin\conda.bat" activate "%INSTALL_ENV_DIR%" >> start_environment.bat
|
||||
@rem Create start_alltalk.bat to run AllTalk
|
||||
echo @echo off > start_alltalk.bat
|
||||
echo cd /D "%~dp0" >> start_alltalk.bat
|
||||
echo set CONDA_ROOT_PREFIX=%cd%\alltalk_environment\conda >> start_alltalk.bat
|
||||
echo set INSTALL_ENV_DIR=%cd%\alltalk_environment\env >> start_alltalk.bat
|
||||
echo call "%CONDA_ROOT_PREFIX%\condabin\conda.bat" activate "%INSTALL_ENV_DIR%" >> start_alltalk.bat
|
||||
echo call python script.py >> start_alltalk.bat
|
||||
@rem Create start_finetune.bat to run AllTalk
|
||||
echo @echo off > start_finetune.bat
|
||||
echo cd /D "%~dp0" >> start_finetune.bat
|
||||
echo set CONDA_ROOT_PREFIX=%cd%\alltalk_environment\conda >> start_finetune.bat
|
||||
echo set INSTALL_ENV_DIR=%cd%\alltalk_environment\env >> start_finetune.bat
|
||||
echo call "%CONDA_ROOT_PREFIX%\condabin\conda.bat" activate "%INSTALL_ENV_DIR%" >> start_finetune.bat
|
||||
echo call python finetune.py >> start_finetune.bat
|
||||
Echo.
|
||||
Echo Run %L_YELLOW%start_alltalk.bat%RESET% to start AllTalk.
|
||||
Echo Run %L_YELLOW%start_finetune.bat%RESET% to start Finetuning.
|
||||
Echo Run %L_YELLOW%start_environment.bat%RESET% to start the AllTalk Python environment.
|
||||
Echo.
|
||||
pause
|
||||
goto StandaloneMenu
|
||||
|
||||
:STDeleteCustomStandalone
|
||||
@rem Check if the alltalk_environment directory exists
|
||||
if not exist "%cd%\alltalk_environment\" (
|
||||
echo.
|
||||
echo %L_GREEN%alltalk_environment%RESET% directory does not exist. No need to delete.
|
||||
echo.
|
||||
pause
|
||||
goto StandaloneMenu
|
||||
)
|
||||
@rem Check if a Conda environment is active
|
||||
if not defined CONDA_PREFIX goto NoCondaEnvDeleteCustomStandalone
|
||||
@rem Deactivate Conda environment if it's active
|
||||
Echo Exiting the Conda Environment. Please run %L_GREEN%atsetup.bat%RESET% again and delete the environment.
|
||||
conda deactivate
|
||||
:NoCondaEnvDeleteCustomStandalone
|
||||
echo Deleting "alltalk_environment". Please wait.
|
||||
rd /s /q "alltalk_environment"
|
||||
del start_alltalk.bat
|
||||
del start_environment.bat
|
||||
if %ERRORLEVEL% neq 0 (
|
||||
echo.
|
||||
echo Failed to delete alltalk_environment folder.
|
||||
echo Please make sure it is not in use and try again.
|
||||
echo.
|
||||
pause
|
||||
goto StandaloneMenu
|
||||
)
|
||||
echo.
|
||||
Echo.
|
||||
echo Environment %L_GREEN%alltalk_environment%RESET% deleted. Please set up the environment again.
|
||||
Echo.
|
||||
pause
|
||||
goto StandaloneMenu
|
||||
|
||||
:GenerateDiagsStandalone
|
||||
cd /D "%~dp0"
|
||||
set CONDA_ROOT_PREFIX=%cd%\alltalk_environment\conda
|
||||
set INSTALL_ENV_DIR=%cd%\alltalk_environment\env
|
||||
@rem Check if the Conda environment exists
|
||||
if not exist "%INSTALL_ENV_DIR%\python.exe" (
|
||||
echo.
|
||||
echo The Conda environment at "%INSTALL_ENV_DIR%" does not exist.
|
||||
echo Please install the environment before proceeding.
|
||||
echo.
|
||||
pause
|
||||
goto StandaloneMenu
|
||||
)
|
||||
@rem Attempt to activate the Conda environment
|
||||
call "%CONDA_ROOT_PREFIX%\condabin\conda.bat" activate "%INSTALL_ENV_DIR%"
|
||||
if errorlevel 1 (
|
||||
echo.
|
||||
echo Failed to activate the Conda environment.
|
||||
echo Please check your installation and try again.
|
||||
echo.
|
||||
pause
|
||||
goto StandaloneMenu
|
||||
)
|
||||
@rem Run diagnostics
|
||||
Python diagnostics.py
|
||||
if %ERRORLEVEL% neq 0 (
|
||||
echo.
|
||||
echo There was an error running diagnostics.
|
||||
echo Press any key to return to the menu.
|
||||
echo.
|
||||
pause
|
||||
goto StandaloneMenu
|
||||
)
|
||||
Echo.
|
||||
Echo.
|
||||
Echo Diagnostics.log generated. Please scroll up to look over the log.
|
||||
Echo.
|
||||
pause
|
||||
goto StandaloneMenu
|
||||
|
||||
:STReapplyrequirements
|
||||
cd /D "%~dp0"
|
||||
set CONDA_ROOT_PREFIX=%cd%\alltalk_environment\conda
|
||||
set INSTALL_ENV_DIR=%cd%\alltalk_environment\env
|
||||
@rem Check if the Conda environment exists
|
||||
if not exist "%INSTALL_ENV_DIR%\python.exe" (
|
||||
echo.
|
||||
echo The Conda environment at "%INSTALL_ENV_DIR%" does not exist.
|
||||
echo Please install the environment before proceeding.
|
||||
echo.
|
||||
pause
|
||||
goto StandaloneMenu
|
||||
)
|
||||
@rem Attempt to activate the Conda environment
|
||||
call "%CONDA_ROOT_PREFIX%\condabin\conda.bat" activate "%INSTALL_ENV_DIR%"
|
||||
if errorlevel 1 (
|
||||
echo.
|
||||
echo Failed to activate the Conda environment.
|
||||
echo Please check your installation and try again.
|
||||
echo.
|
||||
pause
|
||||
goto StandaloneMenu
|
||||
)
|
||||
@rem Run Reapply requirements
|
||||
echo.
|
||||
echo Downloading and installing PyTorch. This step can take a long time
|
||||
echo depending on your internet connection and hard drive speed. Please
|
||||
echo be patient.
|
||||
echo.
|
||||
pip install torch==2.2.2+cu121 torchaudio>=2.2.2+cu121 --upgrade --force-reinstall --extra-index-url https://download.pytorch.org/whl/cu121
|
||||
echo Installing other requirements.
|
||||
echo.
|
||||
pip install -r system\requirements\requirements_standalone.txt
|
||||
C:/Windows/system32/curl.exe -LO https://github.com/erew123/alltalk_tts/releases/download/DeepSpeed-14.0/deepspeed-0.14.0+ce78a63-cp311-cp311-win_amd64.whl
|
||||
echo Installing DeepSpeed...
|
||||
pip install deepspeed-0.14.0+ce78a63-cp311-cp311-win_amd64.whl
|
||||
del deepspeed-0.14.0+ce78a63-cp311-cp311-win_amd64.whl
|
||||
if %ERRORLEVEL% neq 0 (
|
||||
echo.
|
||||
echo There was an error.
|
||||
echo Press any key to return to the menu.
|
||||
echo.
|
||||
pause
|
||||
goto StandaloneMenu
|
||||
)
|
||||
Echo.
|
||||
Echo.
|
||||
Echo Requirements have been re-applied/updated.
|
||||
Echo.
|
||||
pause
|
||||
goto StandaloneMenu
|
||||
|
||||
:STPurgepipcache
|
||||
cd /D "%~dp0"
|
||||
set CONDA_ROOT_PREFIX=%cd%\alltalk_environment\conda
|
||||
set INSTALL_ENV_DIR=%cd%\alltalk_environment\env
|
||||
@rem Check if the Conda environment exists
|
||||
if not exist "%INSTALL_ENV_DIR%\python.exe" (
|
||||
echo.
|
||||
echo The Conda environment at "%INSTALL_ENV_DIR%" does not exist.
|
||||
echo Please install the environment before proceeding.
|
||||
echo.
|
||||
pause
|
||||
goto StandaloneMenu
|
||||
)
|
||||
@rem Attempt to activate the Conda environment
|
||||
call "%CONDA_ROOT_PREFIX%\condabin\conda.bat" activate "%INSTALL_ENV_DIR%"
|
||||
if errorlevel 1 (
|
||||
echo.
|
||||
echo Failed to activate the Conda environment.
|
||||
echo Please check your installation and try again.
|
||||
echo.
|
||||
pause
|
||||
goto StandaloneMenu
|
||||
)
|
||||
@rem Clear the PIP cache
|
||||
echo.
|
||||
echo Purging the PIP cache of downloaded files.
|
||||
echo.
|
||||
pip cache purge
|
||||
if %ERRORLEVEL% neq 0 (
|
||||
echo.
|
||||
echo There was an error.
|
||||
echo Press any key to return to the menu.
|
||||
echo.
|
||||
pause
|
||||
goto StandaloneMenu
|
||||
)
|
||||
Echo.
|
||||
Echo The PIP cache has been purged.
|
||||
Echo.
|
||||
pause
|
||||
goto StandaloneMenu
|
||||
|
||||
:STGitpull
|
||||
cd /D "%~dp0"
|
||||
set CONDA_ROOT_PREFIX=%cd%\alltalk_environment\conda
|
||||
set INSTALL_ENV_DIR=%cd%\alltalk_environment\env
|
||||
@rem Check if the Conda environment exists
|
||||
if not exist "%INSTALL_ENV_DIR%\python.exe" (
|
||||
echo.
|
||||
echo The Conda environment at "%INSTALL_ENV_DIR%" does not exist.
|
||||
echo Please install the environment before proceeding.
|
||||
echo.
|
||||
pause
|
||||
goto StandaloneMenu
|
||||
)
|
||||
@rem Attempt to activate the Conda environment
|
||||
call "%CONDA_ROOT_PREFIX%\condabin\conda.bat" activate "%INSTALL_ENV_DIR%"
|
||||
if errorlevel 1 (
|
||||
echo.
|
||||
echo Failed to activate the Conda environment.
|
||||
echo Please check your installation and try again.
|
||||
echo.
|
||||
pause
|
||||
goto StandaloneMenu
|
||||
)
|
||||
@rem Pull from Github
|
||||
echo.
|
||||
echo Pulling the latest updates. Please re-apply
|
||||
echo the latest requirements file. Option 3
|
||||
echo.
|
||||
git pull
|
||||
if %ERRORLEVEL% neq 0 (
|
||||
echo.
|
||||
echo There was an error pulling from Github.
|
||||
echo Press any key to return to the menu.
|
||||
echo.
|
||||
pause
|
||||
goto StandaloneMenu
|
||||
)
|
||||
Echo.
|
||||
echo AllTalk Updated from Github. Please re-apply
|
||||
echo the latest requirements file. Option 3
|
||||
Echo.
|
||||
pause
|
||||
goto StandaloneMenu
|
||||
|
||||
:EndStandalone
|
||||
echo Exiting AllTalk Setup Utility...
|
||||
echo.
|
||||
Echo Remember, after installation you can....
|
||||
Echo Run %L_YELLOW%start_alltalk.bat%RESET% to start AllTalk.
|
||||
Echo Run %L_YELLOW%start_finetune.bat%RESET% to start Finetuning.
|
||||
Echo Run %L_YELLOW%start_environment.bat%RESET% to start the AllTalk Python environment.
|
||||
Echo.
|
||||
exit /b
|
||||
|
||||
:End
|
||||
echo Exiting AllTalk Setup Utility...
|
||||
exit /b
|
||||
@@ -0,0 +1,520 @@
|
||||
#!/bin/bash
|
||||
|
||||
# ANSI color codes
|
||||
RED='\033[1;31m'
|
||||
GREEN='\033[1;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
BLUE='\033[1;34m'
|
||||
CYAN='\033[1;36m'
|
||||
MAGENTA='\033[1;35m'
|
||||
WHITE='\033[1;37m'
|
||||
L_RED='\033[1;91m'
|
||||
L_GREEN='\033[1;92m'
|
||||
L_YELLOW='\033[1;93m'
|
||||
L_BLUE='\033[1;94m'
|
||||
L_CYAN='\033[1;96m'
|
||||
L_MAGENTA='\033[1;95m'
|
||||
NC='\033[0m' # No Color
|
||||
|
||||
# Navigate to the script's directory
|
||||
cd "$(dirname "$0")"
|
||||
|
||||
# Function to check if curl is installed
|
||||
check_curl() {
|
||||
if ! command -v curl &> /dev/null; then
|
||||
echo "curl is not available on this system. Please install curl then re-run the script https://curl.se/"
|
||||
echo "or perform a manual installation of a Conda Python environment."
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
# Check if the current directory path contains a space
|
||||
containsSpace=false
|
||||
currentPath=$(pwd)
|
||||
if echo "$currentPath" | grep -q ' '; then
|
||||
containsSpace=true
|
||||
fi
|
||||
|
||||
if [ "$containsSpace" = true ]; then
|
||||
echo
|
||||
echo -e " ${L_BLUE}ALLTALK LINUX SETUP UTILITY${NC}"
|
||||
echo
|
||||
echo
|
||||
echo -e " You are trying to install AllTalk in a folder that has a space in the"
|
||||
echo -e " folder path e.g."
|
||||
echo
|
||||
echo -e " /home/${L_RED}program files${NC}/alltalk_tts"
|
||||
echo
|
||||
echo -e " This causes errors with Conda and Python scripts. Please follow this"
|
||||
echo -e " link for reference:"
|
||||
echo
|
||||
echo -e " ${L_CYAN}https://docs.anaconda.com/free/working-with-conda/reference/faq/#installing-anaconda${NC}"
|
||||
echo
|
||||
echo -e " Please use a folder path that has no spaces in it e.g."
|
||||
echo
|
||||
echo -e " /home/myfiles/alltalk_tts/"
|
||||
echo
|
||||
echo
|
||||
read -p "Press Enter to continue..."
|
||||
exit 1
|
||||
else
|
||||
# Continue with the main menu
|
||||
echo "Continue with the main menu."
|
||||
fi
|
||||
|
||||
# Main Menu
|
||||
main_menu() {
|
||||
while true; do
|
||||
clear
|
||||
echo
|
||||
echo -e " ${L_BLUE}ALLTALK LINUX SETUP UTILITY${NC}"
|
||||
echo
|
||||
echo " INSTALLATION TYPE"
|
||||
echo -e " 1) I am using AllTalk as part of ${L_GREEN}Text-generation-webui${NC}"
|
||||
echo -e " 2) I am using AllTalk as a ${L_GREEN}Standalone Application${NC}"
|
||||
echo
|
||||
echo -e " 9)${L_RED} Exit/Quit${NC}"
|
||||
echo
|
||||
read -p " Enter your choice: " user_option
|
||||
|
||||
case $user_option in
|
||||
1) webui_menu ;;
|
||||
2) standalone_menu ;;
|
||||
9) exit 0 ;;
|
||||
*) echo "Invalid option"; sleep 2 ;;
|
||||
esac
|
||||
done
|
||||
}
|
||||
|
||||
# Text-generation-webui Menu
|
||||
webui_menu() {
|
||||
while true; do
|
||||
clear
|
||||
echo
|
||||
echo -e " ${L_BLUE}TEXT-GENERATION-WEBUI SETUP${NC}"
|
||||
echo
|
||||
echo -e " Please ensure you have started your Text-generation-webui Python"
|
||||
echo -e " environment. If you have NOT done this, please run ${L_GREEN}/cmd_linux.sh${NC}"
|
||||
echo -e " in the ${L_GREEN}text-generation-webui${NC} folder and then re-run this script."
|
||||
echo
|
||||
echo " BASE REQUIREMENTS"
|
||||
echo -e " 1) Apply/Re-Apply the requirements for an ${L_GREEN}Text-generation-webui${NC}"
|
||||
echo
|
||||
echo " OPTIONAL"
|
||||
echo " 2) Git Pull the latest AllTalk updates from Github"
|
||||
echo
|
||||
echo " DEEPSPEED"
|
||||
echo " 4) Install DeepSpeed."
|
||||
echo " 5) Uninstall DeepSpeed."
|
||||
echo
|
||||
echo " OTHER"
|
||||
echo " 6) Generate a diagnostics file."
|
||||
echo
|
||||
echo -e " 9)${L_RED} Exit/Quit${NC}"
|
||||
echo
|
||||
read -p " Enter your choice: " webui_option
|
||||
|
||||
case $webui_option in
|
||||
1) install_nvidia_textgen ;;
|
||||
2) tg_gitpull ;;
|
||||
4) install_deepspeed ;;
|
||||
5) uninstall_deepspeed ;;
|
||||
6) generate_diagnostics_textgen ;;
|
||||
9) exit 0 ;;
|
||||
*) echo "Invalid option"; sleep 2 ;;
|
||||
esac
|
||||
done
|
||||
}
|
||||
|
||||
install_nvidia_textgen() {
|
||||
local requirements_file="system/requirements/requirements_textgen.txt"
|
||||
echo " Installing Requirements from $requirements_file..."
|
||||
if ! pip install -r "$requirements_file"; then
|
||||
echo
|
||||
echo " There was an error pulling from Github."
|
||||
echo " Please check the output for details."
|
||||
read -p " Press any key to return to the menu. " -n 1
|
||||
echo
|
||||
return
|
||||
fi
|
||||
echo
|
||||
echo " Requirements installed successfully."
|
||||
echo
|
||||
echo -e " To install ${L_YELLOW}DeepSpeed${NC} on Linux, there are additional"
|
||||
echo -e " steps required. Please see the Github or documentation on DeepSeed."
|
||||
echo
|
||||
read -p " Press any key to continue. " -n 1
|
||||
echo
|
||||
}
|
||||
|
||||
tg_gitpull() {
|
||||
echo
|
||||
if ! git pull; then
|
||||
echo
|
||||
echo " There was an error installing the requirements."
|
||||
echo " Please check the output for details."
|
||||
echo
|
||||
read -p " Press any key to return to the menu. " -n 1
|
||||
echo
|
||||
return
|
||||
fi
|
||||
echo
|
||||
echo " AllTalk Updated from Github. Please re-apply"
|
||||
echo " the latest requirements file. (Option 1)"
|
||||
echo
|
||||
read -p " Press any key to continue. " -n 1
|
||||
echo
|
||||
}
|
||||
|
||||
# Function to install DeepSpeed
|
||||
install_deepspeed() {
|
||||
clear
|
||||
echo
|
||||
echo -e " ${L_BLUE}DEEPSPEED INSTALLATION REQUIREMENTS${NC}"
|
||||
echo
|
||||
echo -e " - NVIDIA CUDA Toolkit must be installed from your ${L_GREEN}Linux package manager${NC}"
|
||||
echo -e " or ${L_GREEN}https://developer.nvidia.com/cuda-toolkit-archive${NC}"
|
||||
echo -e " - The environment variable ${L_GREEN}CUDA_HOME${NC} must be set in your Python env"
|
||||
echo -e " e.g. export CUDA_HOME=/usr/local/cuda."
|
||||
echo -e " - ${L_GREEN}libaio-dev${NC} must be installed."
|
||||
echo
|
||||
echo -e " See the following link for full instructions:"
|
||||
echo -e " ${L_GREEN}https://github.com/erew123/alltalk_tts?#-deepspeed-installation-options${NC}"
|
||||
echo
|
||||
echo -e " ${L_BLUE}Please ${L_RED}confirm${L_BLUE} that you have completed these steps before continuing"
|
||||
echo -e " with the DeepSpeed installation, or the install ${L_RED}will fail.${NC}"
|
||||
echo
|
||||
read -p " Have you completed all the above steps? (y/n): " confirm
|
||||
|
||||
if [ "$confirm" != "y" ]; then
|
||||
echo -e " ${RED}DeepSpeed installation cannot proceed without completing the prerequisites.${NC}"
|
||||
return
|
||||
fi
|
||||
|
||||
echo -e "\n ${GREEN}Proceeding with DeepSpeed installation...${NC}"
|
||||
pip install deepspeed
|
||||
if [ $? -ne 0 ]; then
|
||||
echo -e " ${RED}There was an error installing DeepSpeed.${NC}"
|
||||
return
|
||||
fi
|
||||
|
||||
echo -e " ${GREEN}DeepSpeed installed successfully.${NC}"
|
||||
read -p " Press any key to continue. " -n 1
|
||||
echo
|
||||
}
|
||||
|
||||
uninstall_deepspeed() {
|
||||
echo "Uninstalling DeepSpeed..."
|
||||
pip uninstall -y deepspeed
|
||||
if [ $? -ne 0 ]; then
|
||||
echo
|
||||
echo " There was an error uninstalling DeepSpeed."
|
||||
echo
|
||||
echo " Press any key to return to the menu."
|
||||
read -n 1
|
||||
return
|
||||
fi
|
||||
echo
|
||||
echo " DeepSpeed uninstalled successfully."
|
||||
echo
|
||||
echo " Press any key to continue."
|
||||
read -n 1
|
||||
}
|
||||
|
||||
generate_diagnostics_textgen() {
|
||||
# Run diagnostics
|
||||
if ! python diagnostics.py; then
|
||||
echo
|
||||
echo " There was an error running diagnostics."
|
||||
echo
|
||||
read -p " Press any key to return to the menu. " -n 1
|
||||
echo
|
||||
return
|
||||
fi
|
||||
echo
|
||||
echo " Diagnostics log file generated successfully."
|
||||
echo " Please see diagnostics.log"
|
||||
echo
|
||||
read -p " Press any key to continue. " -n 1
|
||||
echo
|
||||
}
|
||||
|
||||
# Standalone Menu
|
||||
standalone_menu() {
|
||||
while true; do
|
||||
clear
|
||||
echo
|
||||
echo -e " ${L_BLUE}ALLTALK STANDALONE APPLICATION SETUP${NC}"
|
||||
echo
|
||||
echo " BASE REQUIREMENTS"
|
||||
echo " 1) Install AllTalk as a Standalone Application"
|
||||
echo
|
||||
echo " OPTIONAL"
|
||||
echo " 2) Git Pull the latest AllTalk updates from Github"
|
||||
echo " 3) Re-Apply/Update the requirements file"
|
||||
echo " 4) Delete AllTalk's custom Python environment"
|
||||
echo " 5) Purge the PIP cache"
|
||||
echo
|
||||
echo " DEEPSPEED"
|
||||
echo " 6) DeepSpeed Instructions/Install"
|
||||
echo
|
||||
echo " OTHER"
|
||||
echo " 8) Generate a diagnostics file"
|
||||
echo
|
||||
echo -e " 9)${L_RED} Exit/Quit${NC}"
|
||||
echo
|
||||
read -p " Enter your choice: " standalone_option
|
||||
|
||||
case $standalone_option in
|
||||
1) install_custom_standalone ;;
|
||||
2) gitpull_standalone ;;
|
||||
3) reapply_standalone ;;
|
||||
4) delete_custom_standalone ;;
|
||||
5) pippurge_standalone ;;
|
||||
6) install_deepspeed ;;
|
||||
8) generate_diagnostics_standalone ;;
|
||||
9) exit 0 ;;
|
||||
*) echo "Invalid option"; sleep 2 ;;
|
||||
esac
|
||||
done
|
||||
}
|
||||
|
||||
|
||||
install_custom_standalone() {
|
||||
cd "$(dirname "${BASH_SOURCE[0]}")"
|
||||
|
||||
if [[ "$(pwd)" =~ " " ]]; then
|
||||
echo "This script relies on Miniconda which can not be silently installed under a path with spaces."
|
||||
exit
|
||||
fi
|
||||
|
||||
# Deactivate existing conda envs as needed to avoid conflicts
|
||||
{ conda deactivate && conda deactivate && conda deactivate; } 2> /dev/null
|
||||
|
||||
OS_ARCH=$(uname -m)
|
||||
case "${OS_ARCH}" in
|
||||
x86_64*) OS_ARCH="x86_64" ;;
|
||||
arm64* | aarch64*) OS_ARCH="aarch64" ;;
|
||||
*) echo "Unknown system architecture: $OS_ARCH! This script runs only on x86_64 or arm64" && exit ;;
|
||||
esac
|
||||
|
||||
# Config
|
||||
INSTALL_DIR="$(pwd)/alltalk_environment"
|
||||
CONDA_ROOT_PREFIX="${INSTALL_DIR}/conda"
|
||||
INSTALL_ENV_DIR="${INSTALL_DIR}/env"
|
||||
MINICONDA_DOWNLOAD_URL="https://repo.anaconda.com/miniconda/Miniconda3-py310_23.3.1-0-Linux-${OS_ARCH}.sh"
|
||||
|
||||
if [ ! -x "${CONDA_ROOT_PREFIX}/bin/conda" ]; then
|
||||
echo "Downloading Miniconda from $MINICONDA_DOWNLOAD_URL to ${INSTALL_DIR}/miniconda_installer.sh"
|
||||
mkdir -p "${INSTALL_DIR}"
|
||||
curl -L "${MINICONDA_DOWNLOAD_URL}" -o "${INSTALL_DIR}/miniconda_installer.sh"
|
||||
chmod +x "${INSTALL_DIR}/miniconda_installer.sh"
|
||||
bash "${INSTALL_DIR}/miniconda_installer.sh" -b -p "${CONDA_ROOT_PREFIX}"
|
||||
echo "Miniconda installed."
|
||||
fi
|
||||
|
||||
if [ ! -d "${INSTALL_ENV_DIR}" ]; then
|
||||
"${CONDA_ROOT_PREFIX}/bin/conda" create -y --prefix "${INSTALL_ENV_DIR}" python=3.11
|
||||
echo "Conda environment created at ${INSTALL_ENV_DIR}."
|
||||
fi
|
||||
|
||||
# Activate the environment and install requirements
|
||||
source "${CONDA_ROOT_PREFIX}/etc/profile.d/conda.sh"
|
||||
conda activate "${INSTALL_ENV_DIR}"
|
||||
echo
|
||||
echo " Downloading and installing PyTorch. This step can take a long time"
|
||||
echo " depending on your internet connection and hard drive speed. Please"
|
||||
echo " be patient."
|
||||
pip install torch>=2.2.1+cu121 torchaudio>=2.2.1+cu121 --upgrade --force-reinstall --extra-index-url https://download.pytorch.org/whl/cu121
|
||||
echo
|
||||
echo " Installing additional requirements."
|
||||
echo
|
||||
pip install -r system/requirements/requirements_standalone.txt
|
||||
# Create start_environment.sh to run AllTalk
|
||||
cat << EOF > start_environment.sh
|
||||
#!/bin/bash
|
||||
cd "$(dirname "${BASH_SOURCE[0]}")"
|
||||
if [[ "$(pwd)" =~ " " ]]; then echo This script relies on Miniconda which can not be silently installed under a path with spaces. && exit; fi
|
||||
# deactivate existing conda envs as needed to avoid conflicts
|
||||
{ conda deactivate && conda deactivate && conda deactivate; } 2> /dev/null
|
||||
# config
|
||||
CONDA_ROOT_PREFIX="$(pwd)/alltalk_environment/conda"
|
||||
INSTALL_ENV_DIR="$(pwd)/alltalk_environment/env"
|
||||
# environment isolation
|
||||
export PYTHONNOUSERSITE=1
|
||||
unset PYTHONPATH
|
||||
unset PYTHONHOME
|
||||
export CUDA_PATH="$INSTALL_ENV_DIR"
|
||||
export CUDA_HOME="$CUDA_PATH"
|
||||
export LD_LIBRARY_PATH=`python3 -c 'import os; import nvidia.cublas.lib; import nvidia.cudnn.lib; print(os.path.dirname(nvidia.cublas.lib.__file__) + ":" + os.path.dirname(nvidia.cudnn.lib.__file__))'`
|
||||
# activate env
|
||||
bash --init-file <(echo "source \"$CONDA_ROOT_PREFIX/etc/profile.d/conda.sh\" && conda activate \"$INSTALL_ENV_DIR\"")
|
||||
EOF
|
||||
# Create start_alltalk.sh to run AllTalk
|
||||
cat << EOF > start_alltalk.sh
|
||||
#!/bin/bash
|
||||
export LD_LIBRARY_PATH=`python3 -c 'import os; import nvidia.cublas.lib; import nvidia.cudnn.lib; print(os.path.dirname(nvidia.cublas.lib.__file__) + ":" + os.path.dirname(nvidia.cudnn.lib.__file__))'`
|
||||
source "${CONDA_ROOT_PREFIX}/etc/profile.d/conda.sh"
|
||||
conda activate "${INSTALL_ENV_DIR}"
|
||||
python script.py
|
||||
EOF
|
||||
# Create start_finetune.sh to run AllTalk
|
||||
cat << EOF > start_finetune.sh
|
||||
#!/bin/bash
|
||||
export TRAINER_TELEMETRY=0
|
||||
export LD_LIBRARY_PATH=`python3 -c 'import os; import nvidia.cublas.lib; import nvidia.cudnn.lib; print(os.path.dirname(nvidia.cublas.lib.__file__) + ":" + os.path.dirname(nvidia.cudnn.lib.__file__))'`
|
||||
source "${CONDA_ROOT_PREFIX}/etc/profile.d/conda.sh"
|
||||
conda activate "${INSTALL_ENV_DIR}"
|
||||
python finetune.py
|
||||
EOF
|
||||
chmod +x start_alltalk.sh
|
||||
chmod +x start_environment.sh
|
||||
chmod +x start_finetune.sh
|
||||
echo
|
||||
echo
|
||||
echo -e " Run ${L_YELLOW}./start_alltalk.sh${NC} to start AllTalk."
|
||||
echo -e " Run ${L_YELLOW}./start_finetune.sh${NC} to start Finetuning."
|
||||
echo -e " Run ${L_YELLOW}./start_environment.sh${NC} to start the AllTalk Python environment."
|
||||
echo
|
||||
echo -e " To install ${L_YELLOW}DeepSpeed${NC} on Linux, there are additional"
|
||||
echo -e " steps required. Please see the Github or documentation on DeepSeed."
|
||||
echo
|
||||
read -p " Press any key to continue. " -n 1
|
||||
}
|
||||
|
||||
delete_custom_standalone() {
|
||||
local env_dir="$PWD/alltalk_environment"
|
||||
# Check if the alltalk_environment directory exists
|
||||
if [ ! -d "$env_dir" ]; then
|
||||
echo " \"$env_dir\" directory does not exist. No need to delete."
|
||||
read -p " Press any key to continue. " -n 1
|
||||
echo
|
||||
return
|
||||
fi
|
||||
# Check if a Conda environment is active and deactivate it
|
||||
if [ -n "$CONDA_PREFIX" ]; then
|
||||
echo " Exiting the Conda environment. You may need to start ./atstart.sh again"
|
||||
conda deactivate
|
||||
fi
|
||||
echo "Deleting \"$env_dir\". Please wait."
|
||||
rm -rf "$env_dir"
|
||||
if [ -d "$env_dir" ]; then
|
||||
echo " Failed to delete \"$env_dir\" folder."
|
||||
echo " Please make sure it is not in use and try again."
|
||||
else
|
||||
echo " Environment \"$env_dir\" deleted successfully."
|
||||
fi
|
||||
read -p " Press any key to continue. " -n 1
|
||||
echo
|
||||
}
|
||||
|
||||
generate_diagnostics_standalone() {
|
||||
local env_dir="$PWD/alltalk_environment"
|
||||
local conda_root_prefix="${env_dir}/conda"
|
||||
local install_env_dir="${env_dir}/env"
|
||||
if [ ! -d "${install_env_dir}" ]; then
|
||||
echo
|
||||
echo " The Conda environment at '${install_env_dir}' does not exist."
|
||||
echo " Please install the environment before proceeding."
|
||||
echo
|
||||
read -p " Press any key to return to the menu. " -n 1
|
||||
echo
|
||||
return
|
||||
fi
|
||||
source "${conda_root_prefix}/etc/profile.d/conda.sh"
|
||||
conda activate "${install_env_dir}"
|
||||
if ! python diagnostics.py; then
|
||||
echo
|
||||
echo " There was an error running diagnostics."
|
||||
echo
|
||||
read -p " Press any key to return to the menu. " -n 1
|
||||
echo
|
||||
return
|
||||
fi
|
||||
echo
|
||||
echo " Diagnostics completed successfully."
|
||||
read -p " Press any key to continue. " -n 1
|
||||
echo
|
||||
}
|
||||
|
||||
gitpull_standalone() {
|
||||
local env_dir="$PWD/alltalk_environment"
|
||||
local conda_root_prefix="${env_dir}/conda"
|
||||
local install_env_dir="${env_dir}/env"
|
||||
if [ ! -d "${install_env_dir}" ]; then
|
||||
echo
|
||||
echo " The Conda environment at '${install_env_dir}' does not exist."
|
||||
echo " Please install the environment before proceeding."
|
||||
echo
|
||||
read -p " Press any key to return to the menu. " -n 1
|
||||
echo
|
||||
return
|
||||
fi
|
||||
source "${conda_root_prefix}/etc/profile.d/conda.sh"
|
||||
conda activate "${install_env_dir}"
|
||||
if ! git pull; then
|
||||
echo
|
||||
echo " There was an error pulling from Github."
|
||||
echo
|
||||
read -p " Press any key to return to the menu. " -n 1
|
||||
echo
|
||||
return
|
||||
fi
|
||||
echo
|
||||
echo " AllTalk Updated from Github. Please re-apply."
|
||||
echo " the latest requirements file. (Option 3)"
|
||||
echo
|
||||
read -p " Press any key to continue. " -n 1
|
||||
echo
|
||||
}
|
||||
|
||||
pippurge_standalone() {
|
||||
if ! pip cache purge; then
|
||||
echo
|
||||
echo " There was an error."
|
||||
echo
|
||||
read -p " Press any key to return to the menu. " -n 1
|
||||
echo
|
||||
return
|
||||
fi
|
||||
echo
|
||||
echo " The PIP cache has been purged."
|
||||
echo
|
||||
read -p " Press any key to continue. " -n 1
|
||||
echo
|
||||
}
|
||||
|
||||
reapply_standalone() {
|
||||
local env_dir="$PWD/alltalk_environment"
|
||||
local conda_root_prefix="${env_dir}/conda"
|
||||
local install_env_dir="${env_dir}/env"
|
||||
if [ ! -d "${install_env_dir}" ]; then
|
||||
echo
|
||||
echo " The Conda environment at '${install_env_dir}' does not exist."
|
||||
echo " Please install the environment before proceeding."
|
||||
echo
|
||||
read -p " Press any key to return to the menu. " -n 1
|
||||
echo
|
||||
return
|
||||
fi
|
||||
source "${conda_root_prefix}/etc/profile.d/conda.sh"
|
||||
conda activate "${install_env_dir}"
|
||||
echo
|
||||
echo " Downloading and installing PyTorch. This step can take a long time"
|
||||
echo " depending on your internet connection and hard drive speed. Please"
|
||||
echo " be patient."
|
||||
pip install torch>=2.2.1+cu121 torchaudio>=2.2.1+cu121 --upgrade --force-reinstall --extra-index-url https://download.pytorch.org/whl/cu121
|
||||
echo
|
||||
echo " Installing additional requirements."
|
||||
echo
|
||||
pip install -r system/requirements/requirements_standalone.txt
|
||||
echo
|
||||
echo " Requirements have been re-applied/updated."
|
||||
echo
|
||||
read -p " Press any key to continue. " -n 1
|
||||
echo
|
||||
}
|
||||
|
||||
|
||||
# Start the main menu
|
||||
main_menu
|
||||
@@ -1 +1,25 @@
|
||||
{"activate": true, "autoplay": true, "branding": "AllTalk ", "narrator_enabled": false, "deepspeed_activate": false, "delete_output_wavs": "Disabled", "ip_address": "127.0.0.1", "language": "English", "low_vram": false, "local_temperature": "0.7", "local_repetition_penalty": "10.0", "tts_model_loaded": true, "tts_model_name": "tts_models/multilingual/multi-dataset/xtts_v2", "narrator_voice": "female_01.wav", "output_folder_wav": "extensions/alltalk_tts/outputs/", "output_folder_wav_standalone": "outputs/", "port_number": "7851", "remove_trailing_dots": false, "show_text": true, "tts_method_api_local": false, "tts_method_api_tts": false, "tts_method_xtts_local": true, "voice": "female_01.wav"}
|
||||
{
|
||||
"activate": true,
|
||||
"autoplay": true,
|
||||
"branding": "AllTalk ",
|
||||
"narrator_enabled": false,
|
||||
"deepspeed_activate": false,
|
||||
"delete_output_wavs": "Disabled",
|
||||
"ip_address": "127.0.0.1",
|
||||
"language": "English",
|
||||
"low_vram": false,
|
||||
"local_temperature": "0.7",
|
||||
"local_repetition_penalty": "10.0",
|
||||
"tts_model_loaded": true,
|
||||
"tts_model_name": "tts_models/multilingual/multi-dataset/xtts_v2",
|
||||
"narrator_voice": "female_01.wav",
|
||||
"output_folder_wav": "extensions/alltalk_tts/outputs/",
|
||||
"output_folder_wav_standalone": "outputs/",
|
||||
"port_number": "7851",
|
||||
"remove_trailing_dots": false,
|
||||
"show_text": true,
|
||||
"tts_method_api_local": false,
|
||||
"tts_method_api_tts": false,
|
||||
"tts_method_xtts_local": true,
|
||||
"voice": "female_01.wav"
|
||||
}
|
||||
@@ -8,12 +8,15 @@ try:
|
||||
import re
|
||||
import sys
|
||||
import glob
|
||||
import site
|
||||
import textwrap
|
||||
import torch
|
||||
import packaging.version
|
||||
import packaging.specifiers
|
||||
from packaging.specifiers import SpecifierSet
|
||||
from packaging.specifiers import SpecifierSet
|
||||
from packaging.version import parse as parse_version
|
||||
from pathlib import Path
|
||||
except ImportError as e:
|
||||
print(f"\033[91mError importing module: {e}\033[0m\n")
|
||||
print("\033[94mPlease ensure you started the Text-generation-webUI Python environment with either\033[0m")
|
||||
@@ -21,6 +24,8 @@ except ImportError as e:
|
||||
print("\033[94mfrom the text-generation-webui directory, and then try running the diagnostics again.\033[0m")
|
||||
exit(1)
|
||||
|
||||
this_dir = Path(__file__).parent
|
||||
|
||||
try:
|
||||
import psutil
|
||||
except ImportError:
|
||||
@@ -29,24 +34,30 @@ except ImportError:
|
||||
import psutil
|
||||
|
||||
def get_requirements_file():
|
||||
requirements_files = glob.glob("requirements*.txt")
|
||||
this_dir = Path(__file__).parent # Assuming 'this_dir' is defined as the script's directory
|
||||
requirements_dir = this_dir / 'system' / 'requirements'
|
||||
requirements_files = list(requirements_dir.glob('requirements*.txt')) # Using pathlib for globbing
|
||||
|
||||
if not requirements_files:
|
||||
print("\033[91mNo requirements files found.\033[0m")
|
||||
return None
|
||||
|
||||
print("\033[94m\nSelect a requirements file to check against (or press Enter for default 'requirements.txt'):\033[0m\n")
|
||||
for i, file in enumerate(requirements_files, start=1):
|
||||
print(f"{i}. {file}")
|
||||
for i, file_path in enumerate(requirements_files, start=1):
|
||||
print(f" {i}. {file_path.name}") # Only print the filename
|
||||
|
||||
choice = input("\nEnter the number of your choice: ")
|
||||
try:
|
||||
choice = int(choice) - 1
|
||||
return requirements_files[choice]
|
||||
return str(requirements_files[choice]) # Return the full path as a string
|
||||
except (ValueError, IndexError):
|
||||
return "requirements.txt"
|
||||
return str(requirements_dir / "requirements.txt") # Return the default path as a string
|
||||
|
||||
# Set up logging with filemode='w'
|
||||
logging.basicConfig(filename='diagnostics.log', filemode='w', level=logging.INFO)
|
||||
logging.basicConfig(filename='diagnostics.log',
|
||||
filemode='w',
|
||||
level=logging.INFO,
|
||||
format='%(message)s') # Custom format
|
||||
|
||||
# Function to get GPU information using subprocess
|
||||
def get_gpu_info():
|
||||
@@ -56,6 +67,22 @@ def get_gpu_info():
|
||||
except FileNotFoundError:
|
||||
return "NVIDIA GPU information not available"
|
||||
|
||||
def get_cpu_info():
|
||||
cpu_info = {
|
||||
'physical_cores': psutil.cpu_count(logical=False),
|
||||
'total_cores': psutil.cpu_count(logical=True),
|
||||
'max_frequency': psutil.cpu_freq().max
|
||||
}
|
||||
return cpu_info
|
||||
|
||||
def get_disk_info():
|
||||
disk_info = []
|
||||
partitions = psutil.disk_partitions()
|
||||
for p in partitions:
|
||||
usage = psutil.disk_usage(p.mountpoint)
|
||||
disk_info.append(f" Drive: {p.device} | Total: {usage.total / (1024 ** 3):.2f} GB | Used: {usage.used / (1024 ** 3):.2f} GB | Free: {usage.free / (1024 ** 3):.2f} GB | Type: {p.fstype}")
|
||||
return disk_info
|
||||
|
||||
# Function to check if a port is in use
|
||||
def is_port_in_use(port):
|
||||
for conn in psutil.net_connections():
|
||||
@@ -73,6 +100,38 @@ def satisfies_wildcard(installed_version, required_version):
|
||||
return True
|
||||
return False
|
||||
|
||||
def test_cuda():
|
||||
cuda_available = torch.cuda.is_available()
|
||||
if cuda_available:
|
||||
try:
|
||||
# Attempt to create a tensor on GPU
|
||||
torch.tensor([1.0, 2.0]).cuda()
|
||||
return "Success - CUDA is available and working."
|
||||
except Exception as e:
|
||||
return f"Fail - CUDA is available but not working. Error: {e}"
|
||||
else:
|
||||
return "Fail - CUDA is not available."
|
||||
|
||||
def find_files_in_path_with_wildcard(pattern):
|
||||
# Get the site-packages directory of the current Python environment
|
||||
site_packages_path = site.getsitepackages()
|
||||
found_paths = []
|
||||
# Adjust the sub-directory based on the operating system
|
||||
sub_directory = "nvidia/cublas"
|
||||
if platform.system() == "Linux":
|
||||
sub_directory = os.path.join(sub_directory, "lib")
|
||||
else:
|
||||
sub_directory = os.path.join(sub_directory, "bin")
|
||||
# Iterate over each site-packages directory (there can be more than one)
|
||||
for directory in site_packages_path:
|
||||
# Construct the search directory path
|
||||
search_directory = os.path.join(directory, sub_directory)
|
||||
# Use glob to find all files matching the pattern in this directory
|
||||
for file_path in glob.glob(os.path.join(search_directory, pattern)):
|
||||
if os.path.isfile(file_path): # Ensure it's a file
|
||||
found_paths.append(file_path)
|
||||
return found_paths
|
||||
|
||||
# Function to log and print system information
|
||||
def log_system_info():
|
||||
# System information
|
||||
@@ -88,6 +147,9 @@ def log_system_info():
|
||||
|
||||
# Torch version
|
||||
torch_version = torch.__version__
|
||||
cuda_test_result = test_cuda()
|
||||
cpu_info = get_cpu_info()
|
||||
disk_info = get_disk_info()
|
||||
|
||||
# System RAM using psutil
|
||||
try:
|
||||
@@ -123,6 +185,10 @@ def log_system_info():
|
||||
search_path = sys.path
|
||||
path_env = os.environ.get('PATH', 'N/A')
|
||||
|
||||
# Check for cublas
|
||||
file_name = 'cublas64_11.*' if platform.system() == "Windows" else 'libcublas.so.11*'
|
||||
found_paths = find_files_in_path_with_wildcard(file_name)
|
||||
|
||||
# Compare with requirements file
|
||||
requirements_file = get_requirements_file()
|
||||
if requirements_file:
|
||||
@@ -146,52 +212,72 @@ def log_system_info():
|
||||
logging.info(f"NOTE {requirements_file} not found. Skipping version checks.")
|
||||
|
||||
# Log and print information
|
||||
logging.info(f"OS Version: {os_version}")
|
||||
logging.info(f"Note: Windows 11 will list as build is 10.x.22xxx")
|
||||
logging.info(f"Torch Version: {torch_version}")
|
||||
logging.info(f"System RAM: {system_ram}")
|
||||
logging.info(f"CUDA_HOME: {cuda_home}")
|
||||
logging.info(f"Port Status: {port_status}")
|
||||
logging.info(f"Python Version: {platform.python_version()}")
|
||||
logging.info(f"Python Version Info: {python_version_info}")
|
||||
logging.info(f"Python Executable: {python_executable}")
|
||||
logging.info(f"Python Virtual Environment: {python_virtual_env} (Should be N/A when in Text-generation-webui Conda Python environment)")
|
||||
logging.info(f"Conda Environment: {conda_env}")
|
||||
logging.info(f"OPERATING SYSTEM:")
|
||||
logging.info(f" OS Version: {os_version}")
|
||||
logging.info(f" Note: Windows 11 will list as build is 10.x.22xxx")
|
||||
logging.info(f"\nHARDWARE ENVIRONMENT:")
|
||||
logging.info(f" CPU: Physical Cores: {cpu_info['physical_cores']}, Total Cores: {cpu_info['total_cores']}, Max Frequency: {cpu_info['max_frequency']} MHz")
|
||||
logging.info(f" System RAM: {system_ram}")
|
||||
logging.info(f"\nGPU INFORMATION:{gpu_info}")
|
||||
logging.info("DISK INFORMATION:")
|
||||
for disk in disk_info:
|
||||
logging.info(disk)
|
||||
logging.info("\nNETWORK PORT:")
|
||||
logging.info(f" Port Status: {port_status}")
|
||||
logging.info("\nCUDA:")
|
||||
logging.info(f" CUDA Working: {cuda_test_result}")
|
||||
logging.info(f" CUDA_HOME: {cuda_home}")
|
||||
if found_paths:
|
||||
logging.info(f" Cublas64_11 Path: {', '.join(found_paths)}")
|
||||
else:
|
||||
logging.info(f" Cublas64_11 Path: Not found in any search path directories.")
|
||||
logging.info("\nPYTHON & PYTORCH:")
|
||||
logging.info(f" Torch Version: {torch_version}")
|
||||
logging.info(f" Python Version: {platform.python_version()}")
|
||||
logging.info(f" Python Version Info: {python_version_info}")
|
||||
logging.info(f" Python Executable: {python_executable}")
|
||||
logging.info(f" Python Virtual Environment: {python_virtual_env} (Should be N/A when in Text-generation-webui Conda Python environment)")
|
||||
logging.info(f" Conda Environment: {conda_env}")
|
||||
logging.info("\nPython Search Path:")
|
||||
for path in search_path:
|
||||
logging.info(f" {path}")
|
||||
logging.info("\nOS PATH Environment Variable:")
|
||||
logging.info("\nOS SEARCHPATH ENVIRONMENT:")
|
||||
for path in path_env.split(';'):
|
||||
logging.info(f" {path}")
|
||||
if required_packages: # Check if the dictionary is not empty
|
||||
logging.info("\nPackage Versions:")
|
||||
logging.info("\nPACKAGE VERSIONS vs REQUIREMENTS FILE:")
|
||||
max_package_length = max(len(package) for package in required_packages.keys())
|
||||
for package_name, (operator, required_version) in required_packages.items():
|
||||
installed_version = installed_packages.get(package_name, 'Not installed')
|
||||
logging.info(f"{package_name.ljust(max_package_length)} Required: {operator} {required_version.ljust(12)} Installed: {installed_version}")
|
||||
logging.info(f"GPU Information:\n{gpu_info}")
|
||||
logging.info("Package Versions:")
|
||||
logging.info(f" {package_name.ljust(max_package_length)} Required: {operator} {required_version.ljust(12)} Installed: {installed_version}")
|
||||
logging.info("\nPYTHON PACKAGES:")
|
||||
for package, version in package_versions.items():
|
||||
logging.info(f"{package}>= {version}")
|
||||
logging.info(f" {package}>= {version}")
|
||||
|
||||
# Print to screen
|
||||
print(f"\n\033[94mOS Version:\033[0m \033[92m{os_version}\033[0m")
|
||||
print(f"\033[94mOS Ver note:\033[0m \033[92m(Windows 11 will say build is 10.x.22xxx)\033[0m")
|
||||
print(f"\033[94mCUDA_HOME:\033[0m \033[92m{cuda_home}\033[0m")
|
||||
print(f"\033[94mSystem RAM:\033[0m \033[92m{system_ram}\033[0m")
|
||||
print(f"\033[94mPort Status:\033[0m \033[92m{port_status}\033[0m")
|
||||
print(f"\033[94mTorch Version:\033[0m \033[92m{torch_version}\033[0m")
|
||||
for disk in disk_info:
|
||||
print(f"\033[94mDisk:\033[0m \033[92m{disk}\033[0m")
|
||||
print(f"\033[94m\nGPU Information:\033[0m {gpu_info}")
|
||||
print(f"\033[94mPort Status:\033[0m \033[92m{port_status}\033[0m\n")
|
||||
if "Fail" in cuda_test_result:
|
||||
print(f"\033[91mCUDA Working:\033[0m \033[91m{cuda_test_result}\033[0m")
|
||||
else:
|
||||
print(f"\033[94mCUDA Working:\033[0m \033[92m{cuda_test_result}\033[0m")
|
||||
print(f"\033[94mCUDA_HOME:\033[0m \033[92m{cuda_home}\033[0m")
|
||||
if found_paths:
|
||||
print(f"\033[94mCublas64_11 Path:\033[0m \033[92m{', '.join(found_paths)}\033[0m")
|
||||
else:
|
||||
print(f"\033[94mCublas64_11 Path:\033[0m \033[91mNot found in any search path directories.\033[0m")
|
||||
print(f"\033[94m\nTorch Version:\033[0m \033[92m{torch_version}\033[0m")
|
||||
print(f"\033[94mPython Version:\033[0m \033[92m{platform.python_version()}\033[0m")
|
||||
print(f"\033[94mPython Version Info:\033[0m \033[92m{python_version_info}\033[0m")
|
||||
print(f"\033[94mPython Executable:\033[0m \033[92m{python_executable}\033[0m")
|
||||
print(f"\033[94mPython Virtual Environment:\033[0m \033[92m{python_virtual_env}\033[0m (Should be N/A when in Text-generation-webui Conda Python environment)")
|
||||
print(f"\033[94mConda Environment:\033[0m \033[92m{conda_env}\033[0m")
|
||||
print(f"\n\033[94mPython Search Path:\033[0m")
|
||||
for path in search_path:
|
||||
print(f" {path}")
|
||||
print(f"\n\033[94mOS Search PATH Environment Variable:\033[0m")
|
||||
for path in path_env.split(';'):
|
||||
print(f" {path}")
|
||||
if required_packages: # Check if the dictionary is not empty
|
||||
print("\033[94m\nRequirements file package comparison:\033[0m")
|
||||
max_package_length = max(len(package) for package in required_packages.keys())
|
||||
@@ -214,7 +300,14 @@ def log_system_info():
|
||||
color_installed = "\033[92m" if condition_met else "\033[91m"
|
||||
|
||||
# Print colored output
|
||||
print(f"{package_name.ljust(max_package_length)} Required: {color_required}{operator} {required_version.ljust(12)}\033[0m Installed: {color_installed}{installed_version}\033[0m")
|
||||
print(f" {package_name.ljust(max_package_length)} Required: {color_required}{operator} {required_version.ljust(12)}\033[0m Installed: {color_installed}{installed_version}\033[0m")
|
||||
|
||||
print("\nOn Nvidia Graphics cards machines, if your \033[92mInstalled\033[0m version of \033[92mTorch\033[0m and \033[92mTorchaudio\033[0m does")
|
||||
print("not have \033[92m+cu118\033[0m (Cuda 11.8) or \033[92m+cu121\033[0m (Cuda 12.1) listed after them, you do not have CUDA")
|
||||
print("installed for Torch or Torchaudio in this Python environment. This will cause you problems")
|
||||
print("with \033[94mAllTalk\033[0m and \033[94mFinetuning.\033[0m You may have to 'pip install' a new version of torch and")
|
||||
print("torchaudio, using '\033[94m--upgrade --force-reinstall\033[0m' with the correct version of PyTorch for\033[0m")
|
||||
print("your Python environment.\033[0m")
|
||||
print("\033[94m\nRequirements file specifier meanings:\033[0m")
|
||||
explanation = textwrap.dedent("""
|
||||
== Exact version != Any version except < Less than
|
||||
@@ -224,9 +317,9 @@ def log_system_info():
|
||||
""")
|
||||
print(explanation.strip())
|
||||
print("")
|
||||
print(f"GPU Information:{gpu_info}")
|
||||
print(f"\033[94mDiagnostic log created:\033[0m \033[92mdiagnostics.log\033[0m")
|
||||
print(f"\033[94mPlease upload the log file with any support ticket.\033[0m")
|
||||
print(f"\033[94mDiagnostic log created:\033[0m \033[92mdiagnostics.log. \033[94mA brief summary of results is displayed above on\033[0m")
|
||||
print(f"\033[94mscreen. Please see the log file for more detail.\033[0m")
|
||||
print(f"\033[94m\nPlease upload the log file with any support ticket.\033[0m")
|
||||
|
||||
if __name__ == "__main__":
|
||||
log_system_info()
|
||||
@@ -0,0 +1,20 @@
|
||||
version: '3.8'
|
||||
services:
|
||||
alltalk-tts:
|
||||
image: erew123/alltalk_tts:cuda
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
- "7851:7851"
|
||||
volumes:
|
||||
- ./outputs:/app/outputs/
|
||||
- ./models:/app/models/
|
||||
- ./voices:/app/voices/
|
||||
- ./finetune/put-voice-samples-in-here:/app/finetune/put-voice-samples-in-here
|
||||
- ./dockerconfig.json:/app/confignew.json
|
||||
deploy:
|
||||
resources:
|
||||
reservations:
|
||||
devices:
|
||||
- driver: nvidia
|
||||
count: all
|
||||
capabilities: [ gpu ]
|
||||
@@ -0,0 +1,21 @@
|
||||
version: '3.8'
|
||||
services:
|
||||
alltalk-tts:
|
||||
image: alltalk:v1-9c
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
- "7851:7851"
|
||||
- "7852:7852"
|
||||
volumes:
|
||||
- ./outputs:/app/outputs/
|
||||
- ./models:/app/models/
|
||||
- ./voices:/app/voices/
|
||||
- ./finetune/put-voice-samples-in-here:/app/finetune/put-voice-samples-in-here
|
||||
- ./dockerconfig.json:/app/confignew.json
|
||||
deploy:
|
||||
resources:
|
||||
reservations:
|
||||
devices:
|
||||
- driver: nvidia
|
||||
count: all
|
||||
capabilities: [ gpu ]
|
||||
@@ -0,0 +1,25 @@
|
||||
{
|
||||
"activate": true,
|
||||
"autoplay": true,
|
||||
"branding": "AllTalk ",
|
||||
"narrator_enabled": false,
|
||||
"deepspeed_activate": true,
|
||||
"delete_output_wavs": "Disabled",
|
||||
"ip_address": "127.0.0.1",
|
||||
"language": "English",
|
||||
"low_vram": false,
|
||||
"local_temperature": "0.7",
|
||||
"local_repetition_penalty": "10.0",
|
||||
"tts_model_loaded": true,
|
||||
"tts_model_name": "tts_models/multilingual/multi-dataset/xtts_v2",
|
||||
"narrator_voice": "female_01.wav",
|
||||
"output_folder_wav": "extensions/alltalk_tts/outputs/",
|
||||
"output_folder_wav_standalone": "outputs/",
|
||||
"port_number": "7851",
|
||||
"remove_trailing_dots": false,
|
||||
"show_text": true,
|
||||
"tts_method_api_local": false,
|
||||
"tts_method_api_tts": false,
|
||||
"tts_method_xtts_local": true,
|
||||
"voice": "female_01.wav"
|
||||
}
|
||||
@@ -1,26 +0,0 @@
|
||||
import torch
|
||||
import os
|
||||
MODEL_PATH = "best_model.pth"
|
||||
# Check if best_model.pth exists
|
||||
if not os.path.exists(MODEL_PATH):
|
||||
print(f"\nCannot find \033[94m{MODEL_PATH}\033[0m in the current folder.")
|
||||
|
||||
# Check if model.pth exists as an alternative
|
||||
ALTERNATIVE_PATH = "model.pth"
|
||||
if os.path.exists(ALTERNATIVE_PATH):
|
||||
print(f"\nIf your model is currently called \033[93m{ALTERNATIVE_PATH}\033[0m please rename it to \033[94mbest_model.pth\033[0m.")
|
||||
else:
|
||||
print(f"Please put \033[91mbest_model.pth\033[0m in the current folder or rename your model file to \033[94mbest_model.pth\033[0m.")
|
||||
exit(1)
|
||||
# Attempt to load the model
|
||||
try:
|
||||
checkpoint = torch.load(MODEL_PATH, map_location=torch.device("cpu"))
|
||||
except Exception as e:
|
||||
print("Error loading checkpoint:", e)
|
||||
raise
|
||||
del checkpoint["optimizer"]
|
||||
for key in list(checkpoint["model"].keys()):
|
||||
if "dvae" in key:
|
||||
del checkpoint["model"][key]
|
||||
torch.save(checkpoint, "model.pth")
|
||||
print(f"\nYour compacted model is created \033[93mmodel.pth\033[0m.")
|
||||
@@ -0,0 +1,5 @@
|
||||
#!/bin/sh
|
||||
python modeldownload.py
|
||||
uvicorn tts_server:app --host 0.0.0.0 --port 7851 --workers 1 --proxy-headers &
|
||||
sleep 5
|
||||
python script.py
|
||||
@@ -1,10 +1,13 @@
|
||||
import os
|
||||
import sys
|
||||
from pathlib import Path
|
||||
import requests
|
||||
from requests.exceptions import ConnectionError
|
||||
from tqdm import tqdm
|
||||
import importlib.metadata as metadata
|
||||
import json
|
||||
from packaging import version
|
||||
from datetime import datetime
|
||||
|
||||
#################################################################
|
||||
#### LOAD PARAMS FROM confignew.json - REQUIRED FOR BRANDING ####
|
||||
@@ -12,14 +15,12 @@ from packaging import version
|
||||
# STARTUP VARIABLE - Create "this_dir" variable as the current script directory
|
||||
this_dir = Path(__file__).parent.resolve()
|
||||
|
||||
|
||||
# load standard config file in and get settings
|
||||
def load_config(file_path):
|
||||
with open(file_path, "r") as config_file:
|
||||
config = json.load(config_file)
|
||||
return config
|
||||
|
||||
|
||||
config_file_path = this_dir / "confignew.json"
|
||||
params = load_config(config_file_path)
|
||||
|
||||
@@ -37,34 +38,26 @@ if modeldownload_config_file_path.exists():
|
||||
files_to_download = settings.get("files_to_download", {})
|
||||
else:
|
||||
# Default settings if the JSON file doesn't exist or is empty
|
||||
print(
|
||||
f"[{params['branding']}Startup] \033[91mWarning\033[0m modeldownload.json is missing so please re-download it and save it in the coquii_tts main folder."
|
||||
)
|
||||
print(
|
||||
f"[{params['branding']}Startup] \033[91mWarning\033[0m API Local and XTTSv2 Local will error unless this is corrected."
|
||||
)
|
||||
print(f"[{params['branding']}Startup] \033[91mWarning\033[0m modeldownload.json is missing so please re-download it and save it in the coquii_tts main folder.")
|
||||
print(f"[{params['branding']}Startup] \033[91mWarning\033[0m API Local and XTTSv2 Local will error unless this is corrected.")
|
||||
|
||||
# Read the version specifier from requirements_nvidia.txt
|
||||
with open(this_dir / "requirements_nvidia.txt", "r") as req_file:
|
||||
with open(this_dir / "system" / "requirements" / "requirements_standalone.txt", "r") as req_file:
|
||||
requirements = req_file.readlines()
|
||||
|
||||
tts_version_required = None
|
||||
for req in requirements:
|
||||
if req.startswith("TTS>="):
|
||||
tts_version_required = req.strip().split(">=")[1]
|
||||
if req.startswith("TTS=="):
|
||||
tts_version_required = req.strip().split("==")[1]
|
||||
break
|
||||
|
||||
if tts_version_required is None:
|
||||
raise ValueError(
|
||||
f"[{params['branding']}Startup] \033[91mWarning\033[0m Could not find TTS version specifier in requirements_nvidia.txt"
|
||||
)
|
||||
|
||||
raise ValueError(f"[{params['branding']}Startup] \033[91mWarning\033[0m Could not find TTS version specifier in requirements file")
|
||||
|
||||
def create_directory_if_not_exists(directory):
|
||||
if not directory.exists():
|
||||
directory.mkdir(parents=True)
|
||||
|
||||
|
||||
def download_file(url, destination):
|
||||
response = requests.get(url, stream=True)
|
||||
total_size_in_bytes = int(response.headers.get("content-length", 0))
|
||||
@@ -83,63 +76,118 @@ def download_file(url, destination):
|
||||
def check_tts_version():
|
||||
try:
|
||||
tts_version = metadata.version("tts")
|
||||
print(
|
||||
f"[{params['branding']}Startup] TTS version installed: \033[93m"
|
||||
+ tts_version
|
||||
+ "\033[0m"
|
||||
)
|
||||
print(f"[{params['branding']}Startup] \033[92mCurrent TTS Version : \033[93m"+tts_version+"\033[0m")
|
||||
|
||||
if version.parse(tts_version) < version.parse(tts_version_required):
|
||||
print(
|
||||
f"[{params['branding']}Startup] \033[91mWarning\033[0m TTS version is too old. Please upgrade to version \033[93m"
|
||||
+ tts_version_required
|
||||
+ "\033[0m or later.\033[0m"
|
||||
)
|
||||
print(
|
||||
f"[{params['branding']}Startup] \033[91mWarning\033[0m At your terminal/command prompt \033[94mpip install --upgrade tts\033[0m"
|
||||
)
|
||||
print(f"[{params['branding']}Startup] \033[91mWarning\033[0m TTS version is too old. Please upgrade to version \033[93m"+tts_version_required+"\033[0m or later.\033[0m")
|
||||
print(f"[{params['branding']}Startup] \033[91mWarning\033[0m At your terminal/command prompt \033[94mpip install --upgrade tts\033[0m")
|
||||
else:
|
||||
print(f"[{params['branding']}Startup] TTS version is up to date.")
|
||||
print(f"[{params['branding']}Startup] \033[92mCurrent TTS Version is :\033[93m Up to date\033[0m")
|
||||
except metadata.PackageNotFoundError:
|
||||
print(
|
||||
f"[{params['branding']}Startup] \033[91mWarning\033[0m TTS is not installed."
|
||||
)
|
||||
print(f"[{params['branding']}Startup] \033[91mWarning\033[0m TTS is not installed")
|
||||
|
||||
def check_torch_version():
|
||||
import torch
|
||||
pytorch_version = torch.__version__
|
||||
cuda_version = torch.version.cuda
|
||||
major, minor, micro = sys.version_info[:3]
|
||||
python_version = f"{major}.{minor}.{micro}"
|
||||
print(f"[{params['branding']}Startup] \033[92mCurrent Python Version :\033[93m {python_version}\033[0m")
|
||||
print(f"[{params['branding']}Startup] \033[92mCurrent PyTorch Version:\033[93m {pytorch_version}\033[0m")
|
||||
if cuda_version is None:
|
||||
print(f"[{params['branding']}Startup] \033[92mCurrent CUDA Version :\033[91m CUDA is not available\033[0m")
|
||||
else:
|
||||
print(f"[{params['branding']}Startup] \033[92mCurrent CUDA Version :\033[93m {cuda_version}\033[0m")
|
||||
|
||||
def ordinal(n):
|
||||
return "%d%s" % (n, "th" if 4 <= n % 100 <= 20 else {1: "st", 2: "nd", 3: "rd"}.get(n % 10, "th"))
|
||||
|
||||
def format_datetime(iso_str):
|
||||
# Parse the ISO 8601 string to datetime
|
||||
dt = datetime.strptime(iso_str, "%Y-%m-%dT%H:%M:%SZ")
|
||||
# Format the datetime into a more readable string
|
||||
return dt.strftime(f"{ordinal(dt.day)} %B %Y at %H:%M")
|
||||
|
||||
def fetch_latest_commit_sha_and_date(owner, repo):
|
||||
url = f"https://api.github.com/repos/{owner}/{repo}/commits"
|
||||
try:
|
||||
response = requests.get(url)
|
||||
if response.status_code == 200:
|
||||
latest_commit = response.json()[0]
|
||||
latest_commit_sha = latest_commit['sha']
|
||||
latest_commit_date = latest_commit['commit']['committer']['date']
|
||||
return latest_commit_sha, latest_commit_date
|
||||
else:
|
||||
print(f"[{params['branding']}Startup] \033[92m{params['branding']}Github updated :\033[91m Failed to fetch the latest commits due to an unexpected response from GitHub")
|
||||
return None, None
|
||||
except ConnectionError:
|
||||
# This block is executed when a connection error occurs
|
||||
print(f"[{params['branding']}Startup] \033[92m{params['branding']}Github updated :\033[91m Could not reach GitHub to check for updates\033[0m")
|
||||
return None, None
|
||||
|
||||
def read_or_initialize_sha(file_path):
|
||||
if os.path.exists(file_path):
|
||||
with open(file_path, 'r') as file:
|
||||
data = json.load(file)
|
||||
return data.get("last_known_commit_sha")
|
||||
else:
|
||||
# File doesn't exist, fetch the latest SHA and create the file
|
||||
latest_commit_sha = fetch_latest_commit_sha_and_date(github_site, github_repository)
|
||||
if latest_commit_sha:
|
||||
with open(file_path, 'w') as file:
|
||||
json.dump({"last_known_commit_sha": latest_commit_sha}, file)
|
||||
return latest_commit_sha
|
||||
else:
|
||||
# Handle the case where GitHub couldn't be reached
|
||||
return None
|
||||
|
||||
def update_sha_file(file_path, new_sha):
|
||||
with open(file_path, 'w') as file:
|
||||
json.dump({"last_known_commit_sha": new_sha}, file)
|
||||
|
||||
# Check and create directories
|
||||
if str(base_path) == "models":
|
||||
create_directory_if_not_exists(this_dir / base_path / model_path)
|
||||
else:
|
||||
create_directory_if_not_exists(base_path / model_path)
|
||||
print(
|
||||
f"[{params['branding']}Startup] \033[94mInfo\033[0m Custom path set in \033[93mmodeldownload.json\033[0m. Using the following settings:"
|
||||
)
|
||||
print(
|
||||
f"[{params['branding']}Startup] \033[94mInfo\033[0m Base folder Path:\033[93m",
|
||||
base_path,
|
||||
"\033[0m",
|
||||
)
|
||||
print(
|
||||
f"[{params['branding']}Startup] \033[94mInfo\033[0m Model folder Path:\033[93m",
|
||||
model_path,
|
||||
"\033[0m",
|
||||
)
|
||||
print(
|
||||
f"[{params['branding']}Startup] \033[94mInfo\033[0m Full Path:\033[93m",
|
||||
base_path / model_path,
|
||||
"\033[0m",
|
||||
)
|
||||
print(f"[{params['branding']}Startup] \033[94mInfo\033[0m Custom path set in \033[93mmodeldownload.json\033[0m. Using the following settings:")
|
||||
print(f"[{params['branding']}Startup] \033[94mInfo\033[0m Base folder Path:\033[93m",base_path,"\033[0m",)
|
||||
print(f"[{params['branding']}Startup] \033[94mInfo\033[0m Model folder Path:\033[93m",model_path,"\033[0m",)
|
||||
print(f"[{params['branding']}Startup] \033[94mInfo\033[0m Full Path:\033[93m",base_path / model_path,"\033[0m",)
|
||||
|
||||
# Download files if they don't exist
|
||||
print("[" + params["branding"] + "Startup] Checking Model is Downloaded.")
|
||||
print(f"[{params['branding']}Startup] \033[92mModel is available :\033[93m Checking\033[0m")
|
||||
for filename, url in files_to_download.items():
|
||||
if str(base_path) == "models":
|
||||
destination = this_dir / base_path / model_path / filename
|
||||
else:
|
||||
destination = Path(base_path) / model_path / filename
|
||||
|
||||
if not destination.exists():
|
||||
print(f"[{params['branding']}Startup] Downloading " + filename + "...")
|
||||
print(f"[{params['branding']}Startup] \033[92mModel Downloading file : \033[93m" + filename + "\033[0m")
|
||||
download_file(url, destination)
|
||||
print(f"[{params['branding']}Startup] \033[92mModel is available :\033[93m Checked\033[0m")
|
||||
|
||||
github_site = "erew123"
|
||||
github_repository = "alltalk_tts"
|
||||
|
||||
check_torch_version()
|
||||
check_tts_version()
|
||||
# Define the file path based on your directory structure
|
||||
sha_file_path = this_dir / "system" / "config" / "at_github_sha.json"
|
||||
|
||||
# Read or initialize the SHA (adjusted for handling both SHA and date)
|
||||
last_known_commit_sha = read_or_initialize_sha(sha_file_path) # Assuming adjustment for date
|
||||
|
||||
# Assuming you have fetched the latest commit SHA and date
|
||||
latest_commit_sha, latest_commit_date = fetch_latest_commit_sha_and_date(github_site, github_repository)
|
||||
|
||||
formatted_date = format_datetime(latest_commit_date) if latest_commit_date else "an unknown date"
|
||||
|
||||
if latest_commit_sha and latest_commit_sha != last_known_commit_sha:
|
||||
#print(f"There's an update available for alltalk_tts.")
|
||||
print(f"[{params['branding']}Startup] \033[92m{params['branding']}Github updated :\033[93m {formatted_date}\033[0m")
|
||||
# Update the file with the new SHA
|
||||
update_sha_file(sha_file_path, latest_commit_sha)
|
||||
elif latest_commit_sha == last_known_commit_sha:
|
||||
#print(f"Your alltalk_tts software is up to date.")
|
||||
print(f"[{params['branding']}Startup] \033[92m{params['branding']}Github updated :\033[93m {formatted_date}\033[0m")
|
||||
@@ -0,0 +1,22 @@
|
||||
FROM nvidia/cuda:12.1.1-devel-ubuntu22.04
|
||||
ENV HOST 0.0.0.0
|
||||
RUN apt-get update && apt-get upgrade -y \
|
||||
&& apt-get install -y git build-essential portaudio19-dev \
|
||||
python3 python3-pip gcc wget \
|
||||
ocl-icd-opencl-dev opencl-headers clinfo \
|
||||
libclblast-dev libopenblas-dev \
|
||||
&& mkdir -p /etc/OpenCL/vendors && echo "libnvidia-opencl.so.1" > /etc/OpenCL/vendors/nvidia.icd \
|
||||
&& ln -s /usr/bin/python3 /usr/bin/python
|
||||
|
||||
RUN mkdir /app/pip_cache
|
||||
ENV PIP_CACHE_DIR=/app/pip_cache
|
||||
|
||||
WORKDIR /app
|
||||
COPY . .
|
||||
ENV CUDA_DOCKER_ARCH=all
|
||||
RUN pip install --no-cache-dir --no-deps -r system/requirements/requirements_standalone.txt --cache-dir=/app/pip_cache && \
|
||||
pip install --no-cache-dir deepspeed --cache-dir=/app/pip_cache
|
||||
|
||||
EXPOSE 7851
|
||||
RUN chmod +x launch.sh
|
||||
ENTRYPOINT ["sh", "-c", "./launch.sh"]
|
||||
@@ -1,7 +0,0 @@
|
||||
gradio>=3.50.2
|
||||
torch>=2.1.0+cu118
|
||||
torchaudio>=2.1.0+cu118
|
||||
TTS==0.21.3
|
||||
faster-whisper>=0.10.0
|
||||
tqdm>=4.66.0
|
||||
pandas>=1.5.0
|
||||
@@ -1,15 +0,0 @@
|
||||
numpy>=1.22.0
|
||||
soundfile>=0.12.1
|
||||
uvicorn>=0.24.0.post1
|
||||
TTS>=0.21.3
|
||||
torch>=2.1.0
|
||||
torchaudio>=2.1.0
|
||||
fastapi>=0.104.1
|
||||
Jinja2>=3.1.2
|
||||
requests>=2.31.0
|
||||
tqdm>=4.66.1
|
||||
importlib-metadata>=4.8.1
|
||||
packaging>=23.2
|
||||
pydantic>=1.10.13
|
||||
sounddevice>=0.4.6
|
||||
python-multipart>=0.0.6
|
||||
@@ -9,12 +9,24 @@ import threading
|
||||
import signal
|
||||
import sys
|
||||
import atexit
|
||||
import shutil
|
||||
from pathlib import Path
|
||||
from datetime import datetime, timedelta
|
||||
import re
|
||||
import numpy as np
|
||||
import soundfile as sf
|
||||
import uuid
|
||||
import logging
|
||||
# Store the current disable level
|
||||
current_disable_level = logging.getLogger().manager.disable
|
||||
|
||||
######################################
|
||||
#### ALLTALK ALLOWED STARTUP TIME ####
|
||||
######################################
|
||||
startup_wait_time = 120
|
||||
|
||||
# You can change the above setting to a larger number to allow AllTAlk more time to start up. The default setting is 120 seconds (2 minutes).
|
||||
# On some older systems you may need to allow AllTalk more time. So you could set it to 240 (4 minutes) which will give AllTalk more to load.
|
||||
|
||||
#################################################################
|
||||
#### LOAD PARAMS FROM confignew.json - REQUIRED FOR BRANDING ####
|
||||
@@ -32,40 +44,111 @@ config_file_path = this_dir / "confignew.json"
|
||||
# Load the params dictionary from the confignew.json file
|
||||
params = load_config(config_file_path)
|
||||
|
||||
print(f"[{params['branding']}Startup]\033[94m _ _ _ \033[1;35m_____ _ _ \033[0m _____ _____ ____ ")
|
||||
print(f"[{params['branding']}Startup]\033[94m / \ | | |\033[1;35m_ _|_ _| | | __ \033[0m |_ _|_ _/ ___| ")
|
||||
print(f"[{params['branding']}Startup]\033[94m / _ \ | | |\033[1;35m | |/ _` | | |/ / \033[0m | | | | \___ \ ")
|
||||
print(f"[{params['branding']}Startup]\033[94m / ___ \| | |\033[1;35m | | (_| | | < \033[0m | | | | ___) |")
|
||||
print(f"[{params['branding']}Startup]\033[94m /_/ \_\_|_|\033[1;35m |_|\__,_|_|_|\_\ \033[0m |_| |_| |____/ ")
|
||||
print(f"[{params['branding']}Startup]")
|
||||
|
||||
##############################################
|
||||
#### Update any changes to confignew.json ####
|
||||
##############################################
|
||||
|
||||
update_config_path = this_dir / "system" / "config" / "at_configupdate.json"
|
||||
downgrade_config_path = this_dir / "system" / "config" / "at_configdowngrade.json"
|
||||
|
||||
def changes_needed(main_config, update_config, downgrade_config):
|
||||
"""Check if there are any changes to be made to the main configuration."""
|
||||
for key in downgrade_config.keys():
|
||||
if key in main_config:
|
||||
return True
|
||||
for key, value in update_config.items():
|
||||
if key not in main_config:
|
||||
return True
|
||||
return False
|
||||
|
||||
def update_config(config_file_path, update_config_path, downgrade_config_path):
|
||||
try:
|
||||
with open(config_file_path, 'r') as file:
|
||||
main_config = json.load(file)
|
||||
with open(update_config_path, 'r') as file:
|
||||
update_config = json.load(file)
|
||||
with open(downgrade_config_path, 'r') as file:
|
||||
downgrade_config = json.load(file)
|
||||
|
||||
# Determine if changes are needed
|
||||
if changes_needed(main_config, update_config, downgrade_config):
|
||||
# Backup with timestamp to avoid overwriting
|
||||
timestamp = datetime.now().strftime("%Y%m%d%H%M%S")
|
||||
backup_path = config_file_path.with_suffix(f".{timestamp}.bak")
|
||||
logging.info(f"Creating backup of the main config to {backup_path}")
|
||||
shutil.copy(config_file_path, backup_path)
|
||||
|
||||
# Proceed with updates and downgrades
|
||||
for key, value in update_config.items():
|
||||
if key not in main_config:
|
||||
main_config[key] = value
|
||||
for key in downgrade_config.keys():
|
||||
if key in main_config:
|
||||
del main_config[key]
|
||||
|
||||
# Save the updated configuration
|
||||
with open(config_file_path, 'w') as file:
|
||||
json.dump(main_config, file, indent=4)
|
||||
|
||||
print(f"[{params['branding']}Startup] \033[92mConfig file check : \033[91mUpdates applied\033[0m")
|
||||
else:
|
||||
print(f"[{params['branding']}Startup] \033[92mConfig file check : \033[93mNo Updates required\033[0m")
|
||||
|
||||
except Exception as e:
|
||||
print(f"[{params['branding']}Startup] \033[92mConfig file check : \033[91mError updating\033[0m")
|
||||
|
||||
# Update the configuration
|
||||
update_config(config_file_path, update_config_path, downgrade_config_path)
|
||||
# Re-Load the params dictionary from the confignew.json file
|
||||
params = load_config(config_file_path)
|
||||
|
||||
#########################################
|
||||
#### Continue on with Startup Checks ####
|
||||
#########################################
|
||||
|
||||
# Required for sentence splitting
|
||||
try:
|
||||
from TTS.api import TTS
|
||||
from TTS.utils.synthesizer import Synthesizer
|
||||
except ModuleNotFoundError:
|
||||
logger.error(
|
||||
f"[{params['branding']}]\033[91mWarning\033[0m Could not find the TTS module. Make sure to install the requirements for the {params['branding']} extension."
|
||||
f"[{params['branding']}]\033[91mWarning\033[0m Linux / Mac:\npip install -r /alltalk_tts/requirements.txt\n"
|
||||
f"[{params['branding']}]\033[91mWarning\033[0m Windows:\npip install -r \\alltalk_tts\\requirements.txt\n"
|
||||
f"[{params['branding']}]\033[91mWarning\033[0m If you used the one-click installer, paste the command above in the terminal window launched after running the cmd_ script. On Windows, that's cmd_windows.bat."
|
||||
)
|
||||
# Inform the user about the missing module and suggest next steps
|
||||
print(f"[{params['branding']}]\033[91mWarning\033[0m Could not find the TTS module. Make sure to install the requirements for the {params['branding']} extension.")
|
||||
print(f"[{params['branding']}]\033[91mWarning\033[0m Please use the ATSetup utility or check the Github installation instructions.")
|
||||
# Re-raise the ModuleNotFoundError to stop the program and print the traceback
|
||||
raise
|
||||
|
||||
# IMPORT - Attempt Importing DeepSpeed (required for displaying Deepspeed checkbox in gradio)
|
||||
# Suppress logging
|
||||
logging.disable(logging.ERROR)
|
||||
try:
|
||||
import deepspeed
|
||||
deepspeed_installed = True
|
||||
except ImportError:
|
||||
deepspeed_installed = False
|
||||
# Restore previous logging level
|
||||
logging.disable(current_disable_level)
|
||||
|
||||
# Import gradio if being used within text generation webUI
|
||||
try:
|
||||
import gradio as gr
|
||||
|
||||
from modules import chat, shared, ui_chat
|
||||
from modules.logging_colors import logger
|
||||
from modules.ui import create_refresh_button
|
||||
from modules.utils import gradio
|
||||
|
||||
# This is set to check if the script is being run within text generation webui or as a standalone script. False is running as part of text gen web ui or a gradio interface
|
||||
running_in_standalone = False
|
||||
output_folder_wav = params["output_folder_wav"]
|
||||
print(f"[{params['branding']}Startup] \033[92m{params['branding']}startup Mode : \033[93mText-Gen-webui mode\033[0m")
|
||||
except ModuleNotFoundError:
|
||||
output_folder_wav = params["output_folder_wav_standalone"]
|
||||
print(f"[{params['branding']}Startup] Running script.py in standalone mode")
|
||||
print(f"[{params['branding']}Startup] \033[92m{params['branding']}startup Mode : \033[93mStandalone mode\033[0m")
|
||||
# This is set to check if the script is being run within text generation webui or as a standalone script. true means standalone
|
||||
running_in_standalone = True
|
||||
|
||||
@@ -73,34 +156,25 @@ except ModuleNotFoundError:
|
||||
#### STARTUP VARIABLES ####
|
||||
###########################
|
||||
# STARTUP VARIABLE - Import languges file for Gradio to be able to display them in the interface
|
||||
with open(this_dir / "languages.json", encoding="utf8") as f:
|
||||
with open(this_dir / "system" / "config" / "languages.json", encoding="utf8") as f:
|
||||
languages = json.load(f)
|
||||
# Create a global lock
|
||||
process_lock = threading.Lock()
|
||||
# Base setting for a possible FineTuned model existing and the loader being available
|
||||
tts_method_xtts_ft = False
|
||||
|
||||
|
||||
# Gather the voice files
|
||||
def get_available_voices():
|
||||
return sorted([voice.name for voice in Path(f"{this_dir}/voices").glob("*.wav")])
|
||||
|
||||
#########################
|
||||
#### LICENSE DISPLAY ####
|
||||
#########################
|
||||
# STARTUP Display Licence Information
|
||||
print(f"[{params['branding']}Startup] \033[94mCoqui Public Model License\033[0m")
|
||||
print(f"[{params['branding']}Startup] \033[94mhttps://coqui.ai/cpml.txt\033[0m")
|
||||
|
||||
|
||||
############################################
|
||||
#### DELETE OLD OUTPUT WAV FILES IF SET ####
|
||||
############################################
|
||||
def delete_old_files(folder_path, days_to_keep):
|
||||
current_time = datetime.now()
|
||||
print(
|
||||
f"[{params['branding']}Startup] Deletion of old output folder WAV files is currently enabled and set at",
|
||||
delete_output_wavs_setting,
|
||||
)
|
||||
print(f"[{params['branding']}Startup] \033[92mWAV file deletion :\033[93m", delete_output_wavs_setting,"\033[0m")
|
||||
for file_name in os.listdir(folder_path):
|
||||
file_path = os.path.join(folder_path, file_name)
|
||||
if os.path.isfile(file_path):
|
||||
@@ -116,20 +190,18 @@ output_folder_wav = os.path.normpath(output_folder_wav)
|
||||
|
||||
# Check and perform file deletion
|
||||
if delete_output_wavs_setting.lower() == "disabled":
|
||||
print(
|
||||
"["
|
||||
+ params["branding"]
|
||||
+ "Startup] Old output wav file deletion is set to disabled."
|
||||
)
|
||||
print("["+ params["branding"]+"Startup] \033[92mWAV file deletion :\033[93m Disabled\033[0m")
|
||||
else:
|
||||
try:
|
||||
days_to_keep = int(delete_output_wavs_setting.split()[0])
|
||||
delete_old_files(output_folder_wav, days_to_keep)
|
||||
except ValueError:
|
||||
print(
|
||||
f"[{params['branding']}Startup] Invalid setting for deleting old wav files. Please use 'Disabled' or 'X Days' format."
|
||||
)
|
||||
print(f"[{params['branding']}Startup] \033[92mWAV file deletion :\033[93m Invalid setting for deleting old wav files. Please use 'Disabled' or 'X Days' format\033[0m")
|
||||
|
||||
if deepspeed_installed:
|
||||
print(f"[{params['branding']}Startup] \033[92mDeepSpeed version :\033[93m",deepspeed.__version__,"\033[0m")
|
||||
else:
|
||||
print(f"[{params['branding']}Startup] \033[92mDeepSpeed version :\033[91m Not Detected\033[0m")
|
||||
|
||||
########################
|
||||
#### STARTUP CHECKS ####
|
||||
@@ -139,7 +211,6 @@ def check_required_files():
|
||||
this_dir = Path(__file__).parent.resolve()
|
||||
download_script_path = this_dir / "modeldownload.py"
|
||||
subprocess.run(["python", str(download_script_path)])
|
||||
print(f"[{params['branding']}Startup] All required files are present.")
|
||||
|
||||
# STARTUP Call Check routine
|
||||
check_required_files()
|
||||
@@ -155,9 +226,11 @@ finetuned_model = trained_model_directory.exists()
|
||||
# If true, this will add a extra option in the Gradio interface for loading Xttsv2 FT
|
||||
if finetuned_model:
|
||||
required_files = ["model.pth", "config.json", "vocab.json"]
|
||||
finetuned_model = all((trained_model_directory / file).exists() for file in required_files)
|
||||
finetuned_model = all(
|
||||
(trained_model_directory / file).exists() for file in required_files
|
||||
)
|
||||
if finetuned_model:
|
||||
print(f"[{params['branding']}Startup] Finetuned model \033[93mDetected\033[0m")
|
||||
print(f"[{params['branding']}Startup] \033[92mFinetuned model :\033[93m Detected\033[0m")
|
||||
|
||||
####################################################
|
||||
#### SET GRADIO BUTTONS BASED ON confignew.json ####
|
||||
@@ -219,9 +292,7 @@ script_path = this_dir / "tts_server.py"
|
||||
|
||||
|
||||
def signal_handler(sig, frame):
|
||||
print(
|
||||
f"[{params['branding']}Shutdown] \033[94mReceived Ctrl+C, terminating subprocess\033[92m"
|
||||
)
|
||||
print(f"[{params['branding']}Shutdown] \033[94mReceived Ctrl+C, terminating subprocess\033[92m")
|
||||
if process.poll() is None:
|
||||
process.terminate()
|
||||
process.wait() # Wait for the subprocess to finish
|
||||
@@ -230,60 +301,53 @@ def signal_handler(sig, frame):
|
||||
|
||||
# Attach the signal handler to the SIGINT signal (Ctrl+C)
|
||||
signal.signal(signal.SIGINT, signal_handler)
|
||||
|
||||
# Start the subprocess
|
||||
process = subprocess.Popen(["python", script_path])
|
||||
|
||||
# Check if the subprocess has started successfully
|
||||
if process.poll() is None:
|
||||
print(f"[{params['branding']}Startup] TTS Subprocess starting")
|
||||
# Check if we're running in docker
|
||||
if os.path.isfile("/.dockerenv"):
|
||||
print(
|
||||
f"[{params['branding']}Startup] Readme available here:",
|
||||
f"http://{params['ip_address']}:{params['port_number']}",
|
||||
f"[{params['branding']}Startup] \033[94mRunning in Docker. Please wait.\033[0m"
|
||||
)
|
||||
else:
|
||||
# Start the subprocess
|
||||
process = subprocess.Popen(["python", script_path])
|
||||
# Check if the subprocess has started successfully
|
||||
if process.poll() is None:
|
||||
print(f"[{params['branding']}Startup] \033[92mTTS Subprocess :\033[93m Starting up\033[0m")
|
||||
print(f"[{params['branding']}Startup]")
|
||||
print(
|
||||
f"[{params['branding']}Startup] \033[91mWarning\033[0m TTS Subprocess Webserver failing to start process"
|
||||
)
|
||||
print(
|
||||
f"[{params['branding']}Startup] \033[91mWarning\033[0m It could be that you have something on port:",
|
||||
params["port_number"],
|
||||
)
|
||||
print(
|
||||
f"[{params['branding']}Startup] \033[91mWarning\033[0m Or you have not started in a Python environement with all the necesssary bits installed"
|
||||
)
|
||||
print(
|
||||
f"[{params['branding']}Startup] \033[91mWarning\033[0m Check you are starting Text-generation-webui with either the start_xxxxx file or the Python environment with cmd_xxxxx file."
|
||||
)
|
||||
print(
|
||||
f"[{params['branding']}Startup] \033[91mWarning\033[0m xxxxx is the type of OS you are on e.g. windows, linux or mac."
|
||||
)
|
||||
print(
|
||||
f"[{params['branding']}Startup] \033[91mWarning\033[0m Alternatively, you could check no other Python processes are running that shouldnt be e.g. Restart your computer is the simple way."
|
||||
f"[{params['branding']}Startup] \033[94m{params['branding']}Settings & Documentation:\033[00m",
|
||||
f"\033[92mhttp://{params['ip_address']}:{params['port_number']}\033[00m",
|
||||
)
|
||||
print(f"[{params['branding']}Startup]")
|
||||
else:
|
||||
print(f"[{params['branding']}Startup] \033[91mWarning\033[0m TTS Subprocess Webserver failing to start process")
|
||||
print(f"[{params['branding']}Startup] \033[91mWarning\033[0m It could be that you have something on port:",params["port_number"],)
|
||||
print(f"[{params['branding']}Startup] \033[91mWarning\033[0m Or you have not started in a Python environement with all the necesssary bits installed")
|
||||
print(f"[{params['branding']}Startup] \033[91mWarning\033[0m Check you are starting Text-generation-webui with either the start_xxxxx file or the Python environment with cmd_xxxxx file.")
|
||||
print(f"[{params['branding']}Startup] \033[91mWarning\033[0m xxxxx is the type of OS you are on e.g. windows, linux or mac.")
|
||||
print(f"[{params['branding']}Startup] \033[91mWarning\033[0m Alternatively, you could check no other Python processes are running that shouldnt be e.g. Restart your computer is the simple way.")
|
||||
# Cleanly kill off this script, but allow text-generation-webui to keep running, albeit without this alltalk_tts
|
||||
sys.exit(1)
|
||||
|
||||
timeout = 120 # Adjust the timeout as needed
|
||||
timeout = startup_wait_time # Gather timeout setting from startup_wait_time
|
||||
|
||||
# Introduce a delay before starting the check loop
|
||||
time.sleep(26) # Wait 26 secs before checking if the tts_server.py has started up.
|
||||
start_time = time.time()
|
||||
while time.time() - start_time < timeout:
|
||||
# Introduce a delay before starting the check loop
|
||||
time.sleep(26) # Wait 26 secs before checking if the tts_server.py has started up.
|
||||
start_time = time.time()
|
||||
while time.time() - start_time < timeout:
|
||||
try:
|
||||
response = requests.get(f"{base_url}/ready")
|
||||
if response.status_code == 200:
|
||||
break
|
||||
except requests.RequestException as e:
|
||||
# Print the exception for debugging purposes
|
||||
print(
|
||||
f"[{params['branding']}Startup] \033[91mWarning\033[0m TTS Subprocess has NOT started up yet, Will keep trying for 120 seconds maximum. Please wait."
|
||||
)
|
||||
time.sleep(4)
|
||||
else:
|
||||
print(
|
||||
f"[{params['branding']}Startup] Startup timed out. Check the server logs for more information."
|
||||
)
|
||||
print(f"[{params['branding']}Startup] \033[91mWarning\033[0m TTS Subprocess has NOT started up yet, Will keep trying for {timeout} seconds maximum. Please wait.")
|
||||
time.sleep(5)
|
||||
else:
|
||||
print(f"\n[{params['branding']}Startup] Startup timed out. Full help available here \033[92mhttps://github.com/erew123/alltalk_tts#-help-with-problems\033[0m")
|
||||
print(f"[{params['branding']}Startup] On older system you may wish to open and edit \033[94mscript.py\033[0m with a text editor and changing the")
|
||||
print(f"[{params['branding']}Startup] \033[94mstartup_wait_time = 120\033[0m setting to something like \033[94mstartup_wait_time = 240\033[0m as this will allow")
|
||||
print(f"[{params['branding']}Startup] AllTalk more time to try load the model into your VRAM. Otherise please visit the Github for")
|
||||
print(f"[{params['branding']}Startup] a list of other possible troubleshooting options.")
|
||||
# Cleanly kill off this script, but allow text-generation-webui to keep running, albeit without this alltalk_tts
|
||||
sys.exit(1)
|
||||
|
||||
@@ -311,35 +375,34 @@ def send_reload_request(tts_method):
|
||||
params["tts_method_xtts_local"] = False
|
||||
params["tts_method_api_tts"] = True
|
||||
params["deepspeed_activate"] = False
|
||||
audio_path = this_dir / "templates" / "apitts.wav"
|
||||
audio_path = this_dir / "system" / "at_sounds" / "apitts.wav"
|
||||
tts_method_xtts_ft = False
|
||||
elif tts_method == "API Local":
|
||||
params["tts_method_api_tts"] = False
|
||||
params["tts_method_xtts_local"] = False
|
||||
params["tts_method_api_local"] = True
|
||||
params["deepspeed_activate"] = False
|
||||
audio_path = this_dir / "templates" / "apilocal.wav"
|
||||
audio_path = this_dir / "system" / "at_sounds" / "apilocal.wav"
|
||||
tts_method_xtts_ft = False
|
||||
elif tts_method == "XTTSv2 Local":
|
||||
params["tts_method_api_tts"] = False
|
||||
params["tts_method_api_local"] = False
|
||||
params["tts_method_xtts_local"] = True
|
||||
audio_path = this_dir / "templates" / "xttslocal.wav"
|
||||
audio_path = this_dir / "system" / "at_sounds" / "xttslocal.wav"
|
||||
tts_method_xtts_ft = False
|
||||
elif tts_method == "XTTSv2 FT":
|
||||
params["tts_method_api_tts"] = False
|
||||
params["tts_method_api_local"] = False
|
||||
params["tts_method_xtts_local"] = False
|
||||
audio_path = this_dir / "templates" / "xttsfinetuned.wav"
|
||||
audio_path = this_dir / "system" / "at_sounds" / "xttsfinetuned.wav"
|
||||
tts_method_xtts_ft = True
|
||||
return f'<audio src="file/{audio_path}" controls autoplay></audio>'
|
||||
except requests.exceptions.RequestException as e:
|
||||
# Handle the HTTP request error
|
||||
print(
|
||||
f"[{params['branding']}Server] \033[91mWarning\033[0m Error during request to webserver process: {e}"
|
||||
)
|
||||
print(f"[{params['branding']}Server] \033[91mWarning\033[0m Error during request to webserver process: {e}")
|
||||
return {"status": "error", "message": str(e)}
|
||||
|
||||
|
||||
##################
|
||||
#### LOW VRAM ####
|
||||
##################
|
||||
@@ -348,9 +411,9 @@ def send_lowvram_request(low_vram):
|
||||
try:
|
||||
params["tts_model_loaded"] = False
|
||||
if low_vram:
|
||||
audio_path = this_dir / "templates" / "lowvramenabled.wav"
|
||||
audio_path = this_dir / "system" / "at_sounds" / "lowvramenabled.wav"
|
||||
else:
|
||||
audio_path = this_dir / "templates" / "lowvramdisabled.wav"
|
||||
audio_path = this_dir / "system" / "at_sounds" / "lowvramdisabled.wav"
|
||||
url = f"{base_url}/api/lowvramsetting?new_low_vram_value={low_vram}"
|
||||
headers = {"Content-Type": "application/json"}
|
||||
response = requests.post(url, headers=headers)
|
||||
@@ -363,9 +426,7 @@ def send_lowvram_request(low_vram):
|
||||
return f'<audio src="file/{audio_path}" controls autoplay></audio>'
|
||||
except requests.exceptions.RequestException as e:
|
||||
# Handle the HTTP request error
|
||||
print(
|
||||
f"[{params['branding']}Server] \033[91mWarning\033[0m Error during request to webserver process: {e}"
|
||||
)
|
||||
print(f"[{params['branding']}Server] \033[91mWarning\033[0m Error during request to webserver process: {e}")
|
||||
return {"status": "error", "message": str(e)}
|
||||
|
||||
|
||||
@@ -377,10 +438,9 @@ def send_deepspeed_request(deepspeed_param):
|
||||
try:
|
||||
params["tts_model_loaded"] = False
|
||||
if deepspeed_param:
|
||||
audio_path = this_dir / "templates" / "deepspeedenabled.wav"
|
||||
audio_path = this_dir / "system" / "at_sounds" / "deepspeedenabled.wav"
|
||||
else:
|
||||
audio_path = this_dir / "templates" / "deepspeeddisabled.wav"
|
||||
|
||||
audio_path = this_dir / "system" / "at_sounds" / "deepspeeddisabled.wav"
|
||||
url = f"{base_url}/api/deepspeed?new_deepspeed_value={deepspeed_param}"
|
||||
headers = {"Content-Type": "application/json"}
|
||||
response = requests.post(url, headers=headers)
|
||||
@@ -393,9 +453,7 @@ def send_deepspeed_request(deepspeed_param):
|
||||
return f'<audio src="file/{audio_path}" controls autoplay></audio>'
|
||||
except requests.exceptions.RequestException as e:
|
||||
# Handle the HTTP request error
|
||||
print(
|
||||
f"[{params['branding']}Server] \033[91mWarning\033[0m Error during request to webserver process: {e}"
|
||||
)
|
||||
print(f"[{params['branding']}Server] \033[91mWarning\033[0m Error during request to webserver process: {e}")
|
||||
return {"status": "error", "message": str(e)}
|
||||
|
||||
|
||||
@@ -428,9 +486,7 @@ Synthesizer.split_into_sentences = new_split_into_sentences
|
||||
def before_audio_generation(string, params):
|
||||
# Check Model is loaded into cuda or cpu and error if not
|
||||
if not params["tts_model_loaded"]:
|
||||
print(
|
||||
f"[{params['branding']}Model] \033[91mWarning\033[0m Model is still loading, please wait before trying to generate TTS"
|
||||
)
|
||||
print(f"[{params['branding']}Model] \033[91mWarning\033[0m Model is still loading, please wait before trying to generate TTS")
|
||||
return
|
||||
string = html.unescape(string) or random_sentence()
|
||||
if string == "":
|
||||
@@ -456,7 +512,7 @@ def combine(audio_files, output_folder, state):
|
||||
if "character_menu" in state:
|
||||
output_file_path = os.path.join(output_folder, f'{state["character_menu"]}_{int(time.time())}_combined.wav')
|
||||
else:
|
||||
output_file_path = os.path.join(output_folder, f'TTSOUT_{int(time.time())}_combined.wav')
|
||||
output_file_path = os.path.join(output_folder, f"TTSOUT_{int(time.time())}_combined.wav")
|
||||
sf.write(output_file_path, audio, samplerate=sample_rate)
|
||||
# Clean up unnecessary files
|
||||
for audio_file in audio_files:
|
||||
@@ -470,7 +526,7 @@ def combine(audio_files, output_folder, state):
|
||||
################################
|
||||
# PREVIEW VOICE - Generate Random Sentence if Voice Preview box is empty
|
||||
def random_sentence():
|
||||
with open(this_dir / "harvard_sentences.txt") as f:
|
||||
with open(this_dir / "system" / "config" / "harvard_sentences.txt") as f:
|
||||
return random.choice(list(f))
|
||||
|
||||
|
||||
@@ -487,18 +543,23 @@ def voice_preview(string):
|
||||
output_file = Path(params["output_folder_wav"]) / "voice_preview.wav"
|
||||
# Generate the audio
|
||||
language_code = languages.get(params["language"])
|
||||
temperature = params["local_temperature"]
|
||||
repetition_penalty = params["local_repetition_penalty"]
|
||||
# Convert the WindowsPath object to a string before using it in JSON payload
|
||||
output_file_str = output_file.as_posix()
|
||||
# Lock before making the generate request
|
||||
with process_lock:
|
||||
generate_response = send_generate_request(
|
||||
string, params["voice"], language_code, output_file_str
|
||||
string,
|
||||
params["voice"],
|
||||
language_code,
|
||||
temperature,
|
||||
repetition_penalty,
|
||||
output_file_str,
|
||||
)
|
||||
# Check if lock is already acquired
|
||||
if process_lock.locked():
|
||||
print(
|
||||
f"[{params['branding']}Model] \033[91mWarning\033[0m Audio generation is already in progress. Please wait."
|
||||
)
|
||||
print(f"[{params['branding']}Model] \033[91mWarning\033[0m Audio generation is already in progress. Please wait.")
|
||||
return
|
||||
if generate_response.get("status") == "generate-success":
|
||||
# Handle Gradio and playback
|
||||
@@ -509,12 +570,16 @@ def voice_preview(string):
|
||||
return f"[{params['branding']}Server] Audio generation failed. Status: {generate_response.get('status')}"
|
||||
|
||||
|
||||
#######################
|
||||
#### TEXT CLEANING ####
|
||||
#######################
|
||||
|
||||
def process_text(text):
|
||||
# Normalize HTML encoded quotes
|
||||
text = html.unescape(text)
|
||||
|
||||
# Replace ellipsis with a single dot
|
||||
text = re.sub(r'\.{3,}', '.', text)
|
||||
text = re.sub(r"\.{3,}", ".", text)
|
||||
|
||||
# Pattern to identify combined narrator and character speech
|
||||
combined_pattern = r'(\*[^*"]+\*|"[^"*]+")'
|
||||
@@ -529,22 +594,22 @@ def process_text(text):
|
||||
for match in re.finditer(combined_pattern, text):
|
||||
# Add the text before the match, if any, as ambiguous
|
||||
if start < match.start():
|
||||
ambiguous_text = text[start:match.start()].strip()
|
||||
ambiguous_text = text[start : match.start()].strip()
|
||||
if ambiguous_text:
|
||||
ordered_parts.append(('ambiguous', ambiguous_text))
|
||||
ordered_parts.append(("ambiguous", ambiguous_text))
|
||||
|
||||
# Add the matched part as either narrator or character
|
||||
matched_text = match.group(0)
|
||||
if matched_text.startswith('*') and matched_text.endswith('*'):
|
||||
ordered_parts.append(('narrator', matched_text.strip('*').strip()))
|
||||
if matched_text.startswith("*") and matched_text.endswith("*"):
|
||||
ordered_parts.append(("narrator", matched_text.strip("*").strip()))
|
||||
elif matched_text.startswith('"') and matched_text.endswith('"'):
|
||||
ordered_parts.append(('character', matched_text.strip('"').strip()))
|
||||
ordered_parts.append(("character", matched_text.strip('"').strip()))
|
||||
else:
|
||||
# In case of mixed or improperly formatted parts
|
||||
if '*' in matched_text:
|
||||
ordered_parts.append(('narrator', matched_text.strip('*').strip('"')))
|
||||
if "*" in matched_text:
|
||||
ordered_parts.append(("narrator", matched_text.strip("*").strip('"')))
|
||||
else:
|
||||
ordered_parts.append(('character', matched_text.strip('"').strip('*')))
|
||||
ordered_parts.append(("character", matched_text.strip('"').strip("*")))
|
||||
|
||||
# Update the start of the next segment
|
||||
start = match.end()
|
||||
@@ -553,10 +618,35 @@ def process_text(text):
|
||||
if start < len(text):
|
||||
ambiguous_text = text[start:].strip()
|
||||
if ambiguous_text:
|
||||
ordered_parts.append(('ambiguous', ambiguous_text))
|
||||
ordered_parts.append(("ambiguous", ambiguous_text))
|
||||
|
||||
return ordered_parts
|
||||
|
||||
|
||||
########################
|
||||
#### IMAGE CLEANING ####
|
||||
########################
|
||||
|
||||
img_pattern = r'<img[^>]*src\s*=\s*["\'][^"\'>]+["\'][^>]*>'
|
||||
|
||||
def extract_and_remove_images(text):
|
||||
"""
|
||||
Extracts all image data from the text and removes it for clean TTS processing.
|
||||
Returns the cleaned text and the extracted image data.
|
||||
"""
|
||||
img_matches = re.findall(img_pattern, text)
|
||||
img_info = "\n".join(img_matches) # Store extracted image data
|
||||
cleaned_text = re.sub(img_pattern, '', text) # Remove images from text
|
||||
return cleaned_text, img_info
|
||||
|
||||
def reinsert_images(text, img_info):
|
||||
"""
|
||||
Reinserts the previously extracted image data back into the text.
|
||||
"""
|
||||
if img_info: # Check if there are images to reinsert
|
||||
text += f"\n\n{img_info}"
|
||||
return text
|
||||
|
||||
#################################
|
||||
#### TTS STANDARD GENERATION ####
|
||||
#################################
|
||||
@@ -564,19 +654,21 @@ def process_text(text):
|
||||
def output_modifier(string, state):
|
||||
if not params["activate"]:
|
||||
return string
|
||||
original_string = string
|
||||
#print("ORIGINAL STRING IS:", original_string)
|
||||
cleaned_string = before_audio_generation(string, params)
|
||||
img_info = ""
|
||||
cleaned_text, img_info = extract_and_remove_images(string)
|
||||
# print("Cleaned STRING IS:", cleaned_text)
|
||||
cleaned_string = before_audio_generation(cleaned_text, params)
|
||||
if cleaned_string is None:
|
||||
return
|
||||
string = cleaned_string
|
||||
language_code = languages.get(params["language"])
|
||||
temperature = params["local_temperature"]
|
||||
repetition_penalty = params["local_repetition_penalty"]
|
||||
# Create a list to store generated audio paths
|
||||
audio_files = []
|
||||
if process_lock.acquire(blocking=False):
|
||||
try:
|
||||
if params["narrator_enabled"]:
|
||||
processed_parts = process_text(original_string)
|
||||
processed_parts = process_text(cleaned_string)
|
||||
|
||||
audio_files_all_paragraphs = []
|
||||
for part_type, part in processed_parts:
|
||||
@@ -585,29 +677,44 @@ def output_modifier(string, state):
|
||||
continue
|
||||
|
||||
# Determine the voice to use based on the part type
|
||||
if part_type == 'narrator':
|
||||
if part_type == "narrator":
|
||||
voice_to_use = params["narrator_voice"]
|
||||
print(f"[{params['branding']}TTSGen] \033[92mNarrator\033[0m") # Green
|
||||
elif part_type == 'character':
|
||||
elif part_type == "character":
|
||||
voice_to_use = params["voice"]
|
||||
print(f"[{params['branding']}TTSGen] \033[36mCharacter\033[0m") # Yellow
|
||||
else:
|
||||
# Handle ambiguous parts based on user preference
|
||||
voice_to_use = params["voice"] if non_quoted_text_is else params["narrator_voice"]
|
||||
voice_description = "\033[36mCharacter (Text-not-inside)\033[0m" if non_quoted_text_is else "\033[92mNarrator (Text-not-inside)\033[0m"
|
||||
voice_to_use = (
|
||||
params["voice"]
|
||||
if non_quoted_text_is
|
||||
else params["narrator_voice"]
|
||||
)
|
||||
voice_description = (
|
||||
"\033[36mCharacter (Text-not-inside)\033[0m"
|
||||
if non_quoted_text_is
|
||||
else "\033[92mNarrator (Text-not-inside)\033[0m"
|
||||
)
|
||||
print(f"[{params['branding']}TTSGen] {voice_description}")
|
||||
|
||||
# Replace multiple exclamation marks, question marks, or other punctuation with a single instance
|
||||
cleaned_part = re.sub(r'([!?.])\1+', r'\1', part)
|
||||
cleaned_part = re.sub(r"([!?.\u3002\uFF1F\uFF01\uFF0C])\1+", r"\1", part)
|
||||
# Replace "Chinese ellipsis" with a single dot
|
||||
cleaned_part = re.sub(r"\u2026{1,2}", ". ", cleaned_part)
|
||||
# Further clean to remove any other unwanted characters
|
||||
cleaned_part = re.sub(r'[^a-zA-Z0-9\s\.,;:!?\-\'"\u0400-\u04FFÀ-ÿ\u0150\u0151\u0170\u0171\$]', '', cleaned_part)
|
||||
cleaned_part = re.sub(r'[^a-zA-Z0-9\s.,;:!?\-\'"$\u0400-\u04FF\u00C0-\u00FF\u0150\u0151\u0170\u0171\u0900-\u097F\u2018\u2019\u201C\u201D\u3001\u3002\u3040-\u309F\u30A0-\u30FF\u4E00-\u9FFF\u3400-\u4DBF\uF900-\uFAFF\u0600-\u06FF\u0750-\u077F\uFB50-\uFDFF\uFE70-\uFEFF\uAC00-\uD7A3\u1100-\u11FF\u3130-\u318F\uFF01\uFF0c\uFF1A\uFF1B\uFF1F]', '', cleaned_part)
|
||||
# Remove all newline characters (single or multiple)
|
||||
cleaned_part = re.sub(r'\n+', ' ', cleaned_part)
|
||||
cleaned_part = re.sub(r"\n+", " ", cleaned_part)
|
||||
|
||||
# Generate TTS and output to a file
|
||||
output_filename = get_output_filename(state)
|
||||
generate_response = send_generate_request(
|
||||
cleaned_part, voice_to_use, language_code, output_filename
|
||||
cleaned_part,
|
||||
voice_to_use,
|
||||
language_code,
|
||||
temperature,
|
||||
repetition_penalty,
|
||||
output_filename,
|
||||
)
|
||||
audio_path = generate_response.get("data", {}).get("audio_path")
|
||||
audio_files_all_paragraphs.append(audio_path)
|
||||
@@ -618,22 +725,33 @@ def output_modifier(string, state):
|
||||
)
|
||||
else:
|
||||
# Decode HTML entities first
|
||||
cleaned_part = html.unescape(original_string)
|
||||
cleaned_part = html.unescape(cleaned_string)
|
||||
# Replace multiple instances of certain punctuation marks with a single instance
|
||||
cleaned_part = re.sub(r'([!?.])\1+', r'\1', cleaned_part)
|
||||
cleaned_part = re.sub(r"([!?.\u3002\uFF1F\uFF01\uFF0C])\1+", r"\1", cleaned_part)
|
||||
# Replace "Chinese ellipsis" with a single dot
|
||||
cleaned_part = re.sub(r"\u2026{1,2}", ". ", cleaned_part)
|
||||
# Further clean to remove any other unwanted characters
|
||||
cleaned_part = re.sub(r'[^a-zA-Z0-9\s\.,;:!?\-\'"\u0400-\u04FFÀ-ÿ\u0150\u0151\u0170\u0171\$]', '', cleaned_part)
|
||||
cleaned_part = re.sub(r'[^a-zA-Z0-9\s.,;:!?\-\'"$\u0400-\u04FF\u00C0-\u00FF\u0150\u0151\u0170\u0171\u0900-\u097F\u2018\u2019\u201C\u201D\u3001\u3002\u3040-\u309F\u30A0-\u30FF\u4E00-\u9FFF\u3400-\u4DBF\uF900-\uFAFF\u0600-\u06FF\u0750-\u077F\uFB50-\uFDFF\uFE70-\uFEFF\uAC00-\uD7A3\u1100-\u11FF\u3130-\u318F\uFF01\uFF0c\uFF1A\uFF1B\uFF1F]', '', cleaned_part)
|
||||
# Remove all newline characters (single or multiple)
|
||||
cleaned_part = re.sub(r'\n+', ' ', cleaned_part)
|
||||
cleaned_part = re.sub(r"\n+", " ", cleaned_part)
|
||||
# Process the part and give it a non-character name if being used vai API or standalone.
|
||||
if "character_menu" in state:
|
||||
output_file = Path(f'{params["output_folder_wav"]}/{state["character_menu"]}_{int(time.time())}.wav')
|
||||
output_file = Path(
|
||||
f'{params["output_folder_wav"]}/{state["character_menu"]}_{int(time.time())}.wav'
|
||||
)
|
||||
else:
|
||||
output_file = Path(f'{params["output_folder_wav"]}/TTSOUT_{int(time.time())}.wav')
|
||||
output_file = Path(
|
||||
f'{params["output_folder_wav"]}/TTSOUT_{int(time.time())}.wav'
|
||||
)
|
||||
output_file_str = output_file.as_posix()
|
||||
output_file = get_output_filename(state)
|
||||
generate_response = send_generate_request(
|
||||
cleaned_part, params["voice"], language_code, output_file_str
|
||||
cleaned_part,
|
||||
params["voice"],
|
||||
language_code,
|
||||
temperature,
|
||||
repetition_penalty,
|
||||
output_file_str,
|
||||
)
|
||||
audio_path = generate_response.get("data", {}).get("audio_path")
|
||||
final_output_file = audio_path
|
||||
@@ -652,42 +770,40 @@ def output_modifier(string, state):
|
||||
if audio_path:
|
||||
# Handle Gradio and playback
|
||||
autoplay = "autoplay" if params["autoplay"] else ""
|
||||
string = (
|
||||
f'<audio src="file/{final_output_file}" controls {autoplay}></audio>'
|
||||
)
|
||||
|
||||
string = (f'<audio src="file/{final_output_file}" controls {autoplay}></audio>')
|
||||
if params["show_text"]:
|
||||
string += f"\n\n{original_string}"
|
||||
string += reinsert_images(cleaned_string, img_info)
|
||||
shared.processing_message = "*Is typing...*"
|
||||
|
||||
return string
|
||||
else:
|
||||
print(
|
||||
f"[{params['branding']}Server] \033[91mWarning\033[0m No audio path in the response."
|
||||
)
|
||||
print(f"[{params['branding']}Server] \033[91mWarning\033[0m No audio path in the response.")
|
||||
else:
|
||||
print(
|
||||
f"[{params['branding']}Server] \033[91mWarning\033[0m Audio generation failed. Status:",
|
||||
generate_response.get("message"),
|
||||
)
|
||||
|
||||
print(f"[{params['branding']}Server] \033[91mWarning\033[0m Audio generation failed. Status:", generate_response.get("message"),)
|
||||
|
||||
def get_output_filename(state):
|
||||
if "character_menu" in state:
|
||||
return Path(f'{params["output_folder_wav"]}/{state["character_menu"]}_{str(uuid.uuid4())[:8]}.wav').as_posix()
|
||||
return Path(
|
||||
f'{params["output_folder_wav"]}/{state["character_menu"]}_{str(uuid.uuid4())[:8]}.wav'
|
||||
).as_posix()
|
||||
else:
|
||||
return Path(f'{params["output_folder_wav"]}/TTSOUT_{str(uuid.uuid4())[:8]}.wav').as_posix()
|
||||
return Path(
|
||||
f'{params["output_folder_wav"]}/TTSOUT_{str(uuid.uuid4())[:8]}.wav'
|
||||
).as_posix()
|
||||
|
||||
|
||||
###############################################
|
||||
#### SEND GENERATION REQUEST TO TTS ENGINE ####
|
||||
###############################################
|
||||
def send_generate_request(text, voice, language, output_file):
|
||||
def send_generate_request(
|
||||
text, voice, language, temperature, repetition_penalty, output_file
|
||||
):
|
||||
url = f"{base_url}/api/generate"
|
||||
payload = {
|
||||
"text": text,
|
||||
"voice": voice,
|
||||
"language": language,
|
||||
"temperature": temperature,
|
||||
"repetition_penalty": repetition_penalty,
|
||||
"output_file": output_file,
|
||||
}
|
||||
headers = {"Content-Type": "application/json"}
|
||||
@@ -712,12 +828,14 @@ def state_modifier(state):
|
||||
state["stream"] = False
|
||||
return state
|
||||
|
||||
|
||||
def update_narrator_enabled(value):
|
||||
if value == "Enabled":
|
||||
params["narrator_enabled"] = True
|
||||
elif value == "Disabled":
|
||||
params["narrator_enabled"] = False
|
||||
|
||||
|
||||
def update_non_quoted_text_is(value):
|
||||
global non_quoted_text_is
|
||||
if value == "Narrator":
|
||||
@@ -725,6 +843,7 @@ def update_non_quoted_text_is(value):
|
||||
elif value == "Char":
|
||||
non_quoted_text_is = True
|
||||
|
||||
|
||||
def input_modifier(string, state):
|
||||
if not params["activate"]:
|
||||
return string
|
||||
@@ -735,35 +854,53 @@ def input_modifier(string, state):
|
||||
|
||||
def ui():
|
||||
with gr.Accordion(params["branding"] + " TTS (XTTSv2)"):
|
||||
# Activate alltalk_tts, Enable autoplay, Show text
|
||||
with gr.Row():
|
||||
activate = gr.Checkbox(value=params["activate"], label="Activate TTS")
|
||||
autoplay = gr.Checkbox(
|
||||
value=params["autoplay"], label="Play TTS automatically"
|
||||
)
|
||||
activate = gr.Checkbox(value=params["activate"], label="Enable TTS")
|
||||
autoplay = gr.Checkbox(value=params["autoplay"], label="Autoplay TTS")
|
||||
show_text = gr.Checkbox(value=params["show_text"], label="Show Text")
|
||||
|
||||
# Low vram enable, Deepspeed enable, Remove trailing dots
|
||||
with gr.Row():
|
||||
show_text = gr.Checkbox(
|
||||
value=params["show_text"], label="Show message text under audio player"
|
||||
low_vram = gr.Checkbox(
|
||||
value=params["low_vram"], label="Enable Low VRAM Mode"
|
||||
)
|
||||
low_vram_play = gr.HTML(visible=False)
|
||||
deepspeed_checkbox = gr.Checkbox(
|
||||
value=params["deepspeed_activate"],
|
||||
label="Enable DeepSpeed",
|
||||
visible=deepspeed_installed,
|
||||
)
|
||||
deepspeed_checkbox_play = gr.HTML(visible=False)
|
||||
remove_trailing_dots = gr.Checkbox(
|
||||
value=params["remove_trailing_dots"],
|
||||
label='Remove trailing "." from text segments before generation',
|
||||
value=params["remove_trailing_dots"], label='Remove trailing "."'
|
||||
)
|
||||
|
||||
# TTS method, Character voice selection
|
||||
with gr.Row():
|
||||
model_loader_choices = ["API TTS", "API Local", "XTTSv2 Local"]
|
||||
if finetuned_model:
|
||||
model_loader_choices.append("XTTSv2 FT")
|
||||
tts_radio_buttons = gr.Radio(
|
||||
choices=model_loader_choices,
|
||||
label="TTS Method (Each method sounds slightly different)",
|
||||
value=gr_modelchoice, # Set the default value
|
||||
)
|
||||
tts_radio_buttons_play = gr.HTML(visible=False)
|
||||
with gr.Row():
|
||||
available_voices = get_available_voices()
|
||||
default_voice = params["voice"]
|
||||
# Check if the default voice is in the list of available voices
|
||||
default_voice = params[
|
||||
"voice"
|
||||
] # Check if the default voice is in the list of available voices
|
||||
|
||||
if default_voice not in available_voices:
|
||||
# Handle the case where the default voice is not in the list (choose a default value or update it)
|
||||
default_voice = available_voices[
|
||||
0
|
||||
] # Choose the first available voice as the default
|
||||
# Add allow_custom_value=True to the Dropdown
|
||||
voice = gr.Dropdown(
|
||||
available_voices,
|
||||
label="Default Voice",
|
||||
label="Character Voice",
|
||||
value=default_voice,
|
||||
allow_custom_value=True,
|
||||
)
|
||||
@@ -777,14 +914,11 @@ def ui():
|
||||
"refresh-button",
|
||||
)
|
||||
|
||||
language = gr.Dropdown(
|
||||
languages.keys(),
|
||||
label="Language",
|
||||
allow_custom_value=True,
|
||||
value=params["language"],
|
||||
)
|
||||
|
||||
# Language, Narrator voice
|
||||
with gr.Row():
|
||||
language = gr.Dropdown(
|
||||
languages.keys(), label="Language", value=params["language"]
|
||||
)
|
||||
with gr.Row():
|
||||
narrator_voice_gr = gr.Dropdown(
|
||||
get_available_voices(),
|
||||
@@ -801,43 +935,42 @@ def ui():
|
||||
},
|
||||
"refresh-button",
|
||||
)
|
||||
|
||||
# Temperature, Repetition Penalty
|
||||
with gr.Row():
|
||||
local_temperature_gr = gr.Slider(
|
||||
minimum=0.05,
|
||||
maximum=1,
|
||||
step=0.05,
|
||||
label="Temperature",
|
||||
value=params["local_temperature"],
|
||||
)
|
||||
local_repetition_penalty_gr = gr.Slider(
|
||||
minimum=0.5,
|
||||
maximum=20,
|
||||
step=0.5,
|
||||
label="Repetition Penalty",
|
||||
value=params["local_repetition_penalty"],
|
||||
)
|
||||
|
||||
# Narrator enable, Non quoted text, Explanation text
|
||||
with gr.Row():
|
||||
with gr.Row():
|
||||
narrator_enabled_gr = gr.Radio(
|
||||
choices={"Enabled": "true", "Disabled": "false"},
|
||||
label="Narrator Activation",
|
||||
label="Narrator",
|
||||
value="Enabled" if params.get("narrator_enabled") else "Disabled",
|
||||
)
|
||||
non_quoted_text_is_gr = gr.Radio(
|
||||
choices={"Char": "true", "Narrator": "false"},
|
||||
label='Text NOT inside of * or " is',
|
||||
value="Char" if non_quoted_text_is else "Narrator",
|
||||
choices={"Character": "true", "Narrator": "false"},
|
||||
label='Unmarked text NOT inside of * or " is',
|
||||
value="Character" if non_quoted_text_is else "Narrator",
|
||||
)
|
||||
|
||||
with gr.Row():
|
||||
low_vram = gr.Checkbox(
|
||||
value=params["low_vram"], label="Low VRAM mode (Read NOTE)"
|
||||
)
|
||||
low_vram_play = gr.HTML(visible=False)
|
||||
deepspeed_checkbox = gr.Checkbox(
|
||||
value=params["deepspeed_activate"],
|
||||
label="Activate DeepSpeed (Read NOTE)",
|
||||
visible=deepspeed_installed,
|
||||
)
|
||||
deepspeed_checkbox_play = gr.HTML(visible=False)
|
||||
|
||||
with gr.Row():
|
||||
model_loader_choices=["API TTS", "API Local", "XTTSv2 Local"]
|
||||
if finetuned_model:
|
||||
model_loader_choices.append("XTTSv2 FT")
|
||||
tts_radio_buttons = gr.Radio(
|
||||
choices=model_loader_choices,
|
||||
label="Select TTS Generation Method (Read NOTE)",
|
||||
value=gr_modelchoice, # Set the default value
|
||||
)
|
||||
tts_radio_buttons_play = gr.HTML(visible=False)
|
||||
explanation_text = gr.HTML(
|
||||
f"<p>⚙️ <a href='http://{params['ip_address']}:{params['port_number']}'>Click here for the Settings page, help & instructions</a><a href='http://{params['ip_address']}:{params['port_number']}'></a>⚙️<br>NOTE: Switching Model Type, Low VRAM & DeepSpeed, each takes 15 seconds. Each TTS generation method has a slightly different sound. DeepSpeed checkbox is only visible if DeepSpeed is installed on your system."
|
||||
f"<p>⚙️ <a href='http://{params['ip_address']}:{params['port_number']}'>Settings and Documentation Page</a><a href='http://{params['ip_address']}:{params['port_number']}'></a>⚙️<br>- Low VRAM Mode and Deepspeed take 15 seconds to be enabled or disabled.<br>- The DeepSpeed checkbox is only visible if DeepSpeed is present.</p>"
|
||||
)
|
||||
|
||||
# Preview speech
|
||||
with gr.Row():
|
||||
preview_text = gr.Text(
|
||||
show_label=False,
|
||||
@@ -907,18 +1040,34 @@ def ui():
|
||||
autoplay.change(lambda x: params.update({"autoplay": x}), autoplay, None)
|
||||
low_vram.change(lambda x: params.update({"low_vram": x}), low_vram, None)
|
||||
low_vram.change(lambda x: send_lowvram_request(x), low_vram, low_vram_play, None)
|
||||
tts_radio_buttons.change(send_reload_request, tts_radio_buttons, tts_radio_buttons_play, None)
|
||||
deepspeed_checkbox.change(send_deepspeed_request, deepspeed_checkbox, deepspeed_checkbox_play, None)
|
||||
tts_radio_buttons.change(
|
||||
send_reload_request, tts_radio_buttons, tts_radio_buttons_play, None
|
||||
)
|
||||
deepspeed_checkbox.change(
|
||||
send_deepspeed_request, deepspeed_checkbox, deepspeed_checkbox_play, None
|
||||
)
|
||||
remove_trailing_dots.change(
|
||||
lambda x: params.update({"remove_trailing_dots": x}), remove_trailing_dots, None
|
||||
)
|
||||
voice.change(lambda x: params.update({"voice": x}), voice, None)
|
||||
language.change(lambda x: params.update({"language": x}), language, None)
|
||||
|
||||
# TSS Settings
|
||||
local_temperature_gr.change(
|
||||
lambda x: params.update({"local_temperature": x}), local_temperature_gr, None
|
||||
)
|
||||
local_repetition_penalty_gr.change(
|
||||
lambda x: params.update({"local_repetition_penalty": x}),
|
||||
local_repetition_penalty_gr,
|
||||
None,
|
||||
)
|
||||
|
||||
# Narrator selection actions
|
||||
narrator_enabled_gr.change(update_narrator_enabled, narrator_enabled_gr, None)
|
||||
non_quoted_text_is_gr.change(update_non_quoted_text_is, non_quoted_text_is_gr, None)
|
||||
narrator_voice_gr.change(lambda x: params.update({"narrator_voice": x}), narrator_voice_gr, None)
|
||||
narrator_voice_gr.change(
|
||||
lambda x: params.update({"narrator_voice": x}), narrator_voice_gr, None
|
||||
)
|
||||
|
||||
# Play preview
|
||||
preview_text.submit(voice_preview, preview_text, preview_audio)
|
||||
|
||||
|
After Width: | Height: | Size: 97 KiB |
|
After Width: | Height: | Size: 11 KiB |
@@ -89,25 +89,25 @@
|
||||
<h3>AllTalk Startup Settings</h3><br>
|
||||
<form action="/update-settings" method="post">
|
||||
|
||||
<label for="activate" style="color: #33cc33;">Activate TTS:</label>
|
||||
<label for="activate" style="color: #b273fa;">TG-webui Activate TTS:</label>
|
||||
<select id="activate" name="activate" required="">
|
||||
<option {% if data.activate %}selected{% endif %} value="true">True</option>
|
||||
<option {% if not data.activate %}selected{% endif %} value="false">False</option>
|
||||
</select>
|
||||
|
||||
<label for="autoplay" style="color: #33cc33;"> Autoplay TTS output:</label>
|
||||
<label for="autoplay" style="color: #b273fa;"> TG-webui Autoplay TTS:</label>
|
||||
<select id="autoplay" name="autoplay" required="">
|
||||
<option {% if data.autoplay %}selected{% endif %} value="true">True</option>
|
||||
<option {% if not data.autoplay %}selected{% endif %} value="false">False</option>
|
||||
</select>
|
||||
|
||||
<label for="show_text" style="color: #33cc33;"> Show Text on generation:</label>
|
||||
<label for="show_text" style="color: #b273fa;">TG-webui Show Text:</label>
|
||||
<select id="show_text" name="show_text" required="">
|
||||
<option {% if data.show_text %}selected{% endif %} value="true">True</option>
|
||||
<option {% if not data.show_text %}selected{% endif %} value="false">False</option>
|
||||
</select>
|
||||
|
||||
<label for="voice" style="color: #33cc33;"> Default voice:</label>
|
||||
<label for="voice" style="color: #b273fa;"> TG-webui Default voice:</label>
|
||||
<select id="voice" name="voice" required="">
|
||||
{% for file in wav_files %}
|
||||
<option value="{{ file }}" {% if data.voice==file %}selected{% endif %}>{{ file }}</option>
|
||||
@@ -115,7 +115,7 @@
|
||||
</select>
|
||||
|
||||
|
||||
<label for="language" style="color: #33cc33;">Default Language:</label>
|
||||
<label for="language" style="color: #b273fa;">TG-webui Default Language:</label>
|
||||
<select id="language" name="language" required="">
|
||||
<option value="Arabic" {% if data.language=='Arabic' %}selected{% endif %}>Arabic</option>
|
||||
<option value="Chinese" {% if data.language=='Chinese' %}selected{% endif %}>Chinese</option>
|
||||
@@ -124,6 +124,7 @@
|
||||
<option value="English" {% if data.language=='English' %}selected{% endif %}>English</option>
|
||||
<option value="French" {% if data.language=='French' %}selected{% endif %}>French</option>
|
||||
<option value="German" {% if data.language=='German' %}selected{% endif %}>German</option>
|
||||
<option value="Hindi" {% if data.language=='Hindi' %}selected{% endif %}>Hindi</option>
|
||||
<option value="Hungarian" {% if data.language=='Hungarian' %}selected{% endif %}>Hungarian</option>
|
||||
<option value="Italian" {% if data.language=='Italian' %}selected{% endif %}>Italian</option>
|
||||
<option value="Japanese" {% if data.language=='Japanese' %}selected{% endif %}>Japanese</option>
|
||||
@@ -135,20 +136,20 @@
|
||||
<option value="Turkish" {% if data.language=='Turkish' %}selected{% endif %}>Turkish</option>
|
||||
</select>
|
||||
|
||||
<label for="narrator_voice" style="color: #33cc33;"> Narrator voice:</label>
|
||||
<label for="narrator_voice" style="color: #b273fa;"> TG-webui Narrator voice:</label>
|
||||
<select id="narrator_voice" name="narrator_voice" required="">
|
||||
{% for file in wav_files %}
|
||||
<option value="{{ file }}" {% if data.narrator_voice==file %}selected{% endif %}>{{ file }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
|
||||
<label for="remove_trailing_dots" style="color: #33cc33;"> Remove Trailing Dots:</label>
|
||||
<label for="remove_trailing_dots" style="color: #b273fa;">TG-webui Remove Trailing Dots:</label>
|
||||
<select id="remove_trailing_dots" name="remove_trailing_dots" required="">
|
||||
<option {% if data.remove_trailing_dots %}selected{% endif %} value="true">True</option>
|
||||
<option {% if not data.remove_trailing_dots %}selected{% endif %} value="false">False</option>
|
||||
</select>
|
||||
|
||||
<label for="narrator_enabled" style="color: #33cc33;"> Narrator enabled:</label>
|
||||
<label for="narrator_enabled" style="color: #b273fa;"> TG-webui Narrator enabled:</label>
|
||||
<select id="narrator_enabled" name="narrator_enabled" required="">
|
||||
<option {% if data.narrator_enabled %}selected{% endif %} value="true">True</option>
|
||||
<option {% if not data.narrator_enabled %}selected{% endif %} value="false">False</option>
|
||||
|
After Width: | Height: | Size: 216 KiB |
|
After Width: | Height: | Size: 173 KiB |
|
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 1.1 KiB |
|
After Width: | Height: | Size: 57 KiB |
|
After Width: | Height: | Size: 59 KiB |
|
After Width: | Height: | Size: 91 KiB |
|
After Width: | Height: | Size: 52 KiB |
@@ -0,0 +1,2 @@
|
||||
{
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
{
|
||||
}
|
||||
@@ -6,6 +6,7 @@
|
||||
"English": "en",
|
||||
"French": "fr",
|
||||
"German": "de",
|
||||
"Hindi": "hi",
|
||||
"Hungarian": "hu",
|
||||
"Italian": "it",
|
||||
"Japanese": "ja",
|
||||
|
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 1.1 KiB |
@@ -0,0 +1,869 @@
|
||||
import os
|
||||
import re
|
||||
import textwrap
|
||||
from functools import cached_property
|
||||
|
||||
import pypinyin
|
||||
import torch
|
||||
from hangul_romanize import Transliter
|
||||
from hangul_romanize.rule import academic
|
||||
from num2words import num2words
|
||||
from spacy.lang.ar import Arabic
|
||||
from spacy.lang.en import English
|
||||
from spacy.lang.es import Spanish
|
||||
from spacy.lang.ja import Japanese
|
||||
from spacy.lang.zh import Chinese
|
||||
from tokenizers import Tokenizer
|
||||
|
||||
from TTS.tts.layers.xtts.zh_num2words import TextNorm as zh_num2words
|
||||
|
||||
|
||||
def get_spacy_lang(lang):
|
||||
if lang == "zh":
|
||||
return Chinese()
|
||||
elif lang == "ja":
|
||||
return Japanese()
|
||||
elif lang == "ar":
|
||||
return Arabic()
|
||||
elif lang == "es":
|
||||
return Spanish()
|
||||
else:
|
||||
# For most languages, Enlish does the job
|
||||
return English()
|
||||
|
||||
|
||||
def split_sentence(text, lang, text_split_length=250):
|
||||
"""Preprocess the input text"""
|
||||
text_splits = []
|
||||
if text_split_length is not None and len(text) >= text_split_length:
|
||||
text_splits.append("")
|
||||
nlp = get_spacy_lang(lang)
|
||||
nlp.add_pipe("sentencizer")
|
||||
doc = nlp(text)
|
||||
for sentence in doc.sents:
|
||||
if len(text_splits[-1]) + len(str(sentence)) <= text_split_length:
|
||||
# if the last sentence + the current sentence is less than the text_split_length
|
||||
# then add the current sentence to the last sentence
|
||||
text_splits[-1] += " " + str(sentence)
|
||||
text_splits[-1] = text_splits[-1].lstrip()
|
||||
elif len(str(sentence)) > text_split_length:
|
||||
# if the current sentence is greater than the text_split_length
|
||||
for line in textwrap.wrap(
|
||||
str(sentence),
|
||||
width=text_split_length,
|
||||
drop_whitespace=True,
|
||||
break_on_hyphens=False,
|
||||
tabsize=1,
|
||||
):
|
||||
text_splits.append(str(line))
|
||||
else:
|
||||
text_splits.append(str(sentence))
|
||||
|
||||
if len(text_splits) > 1:
|
||||
if text_splits[0] == "":
|
||||
del text_splits[0]
|
||||
else:
|
||||
text_splits = [text.lstrip()]
|
||||
|
||||
return text_splits
|
||||
|
||||
|
||||
_whitespace_re = re.compile(r"\s+")
|
||||
|
||||
# List of (regular expression, replacement) pairs for abbreviations:
|
||||
_abbreviations = {
|
||||
"en": [
|
||||
(re.compile("\\b%s\\." % x[0], re.IGNORECASE), x[1])
|
||||
for x in [
|
||||
("mrs", "misess"),
|
||||
("mr", "mister"),
|
||||
("dr", "doctor"),
|
||||
("st", "saint"),
|
||||
("co", "company"),
|
||||
("jr", "junior"),
|
||||
("maj", "major"),
|
||||
("gen", "general"),
|
||||
("drs", "doctors"),
|
||||
("rev", "reverend"),
|
||||
("lt", "lieutenant"),
|
||||
("hon", "honorable"),
|
||||
("sgt", "sergeant"),
|
||||
("capt", "captain"),
|
||||
("esq", "esquire"),
|
||||
("ltd", "limited"),
|
||||
("col", "colonel"),
|
||||
("ft", "fort"),
|
||||
]
|
||||
],
|
||||
"es": [
|
||||
(re.compile("\\b%s\\." % x[0], re.IGNORECASE), x[1])
|
||||
for x in [
|
||||
("sra", "señora"),
|
||||
("sr", "señor"),
|
||||
("dr", "doctor"),
|
||||
("dra", "doctora"),
|
||||
("st", "santo"),
|
||||
("co", "compañía"),
|
||||
("jr", "junior"),
|
||||
("ltd", "limitada"),
|
||||
]
|
||||
],
|
||||
"fr": [
|
||||
(re.compile("\\b%s\\." % x[0], re.IGNORECASE), x[1])
|
||||
for x in [
|
||||
("mme", "madame"),
|
||||
("mr", "monsieur"),
|
||||
("dr", "docteur"),
|
||||
("st", "saint"),
|
||||
("co", "compagnie"),
|
||||
("jr", "junior"),
|
||||
("ltd", "limitée"),
|
||||
]
|
||||
],
|
||||
"de": [
|
||||
(re.compile("\\b%s\\." % x[0], re.IGNORECASE), x[1])
|
||||
for x in [
|
||||
("fr", "frau"),
|
||||
("dr", "doktor"),
|
||||
("st", "sankt"),
|
||||
("co", "firma"),
|
||||
("jr", "junior"),
|
||||
]
|
||||
],
|
||||
"pt": [
|
||||
(re.compile("\\b%s\\." % x[0], re.IGNORECASE), x[1])
|
||||
for x in [
|
||||
("sra", "senhora"),
|
||||
("sr", "senhor"),
|
||||
("dr", "doutor"),
|
||||
("dra", "doutora"),
|
||||
("st", "santo"),
|
||||
("co", "companhia"),
|
||||
("jr", "júnior"),
|
||||
("ltd", "limitada"),
|
||||
]
|
||||
],
|
||||
"it": [
|
||||
(re.compile("\\b%s\\." % x[0], re.IGNORECASE), x[1])
|
||||
for x in [
|
||||
# ("sig.ra", "signora"),
|
||||
("sig", "signore"),
|
||||
("dr", "dottore"),
|
||||
("st", "santo"),
|
||||
("co", "compagnia"),
|
||||
("jr", "junior"),
|
||||
("ltd", "limitata"),
|
||||
]
|
||||
],
|
||||
"pl": [
|
||||
(re.compile("\\b%s\\." % x[0], re.IGNORECASE), x[1])
|
||||
for x in [
|
||||
("p", "pani"),
|
||||
("m", "pan"),
|
||||
("dr", "doktor"),
|
||||
("sw", "święty"),
|
||||
("jr", "junior"),
|
||||
]
|
||||
],
|
||||
"ar": [
|
||||
(re.compile("\\b%s\\." % x[0], re.IGNORECASE), x[1])
|
||||
for x in [
|
||||
# There are not many common abbreviations in Arabic as in English.
|
||||
]
|
||||
],
|
||||
"zh": [
|
||||
(re.compile("\\b%s\\." % x[0], re.IGNORECASE), x[1])
|
||||
for x in [
|
||||
# Chinese doesn't typically use abbreviations in the same way as Latin-based scripts.
|
||||
]
|
||||
],
|
||||
"cs": [
|
||||
(re.compile("\\b%s\\." % x[0], re.IGNORECASE), x[1])
|
||||
for x in [
|
||||
("dr", "doktor"), # doctor
|
||||
("ing", "inženýr"), # engineer
|
||||
("p", "pan"), # Could also map to pani for woman but no easy way to do it
|
||||
# Other abbreviations would be specialized and not as common.
|
||||
]
|
||||
],
|
||||
"ru": [
|
||||
(re.compile("\\b%s\\b" % x[0], re.IGNORECASE), x[1])
|
||||
for x in [
|
||||
("г-жа", "госпожа"), # Mrs.
|
||||
("г-н", "господин"), # Mr.
|
||||
("д-р", "доктор"), # doctor
|
||||
# Other abbreviations are less common or specialized.
|
||||
]
|
||||
],
|
||||
"nl": [
|
||||
(re.compile("\\b%s\\." % x[0], re.IGNORECASE), x[1])
|
||||
for x in [
|
||||
("dhr", "de heer"), # Mr.
|
||||
("mevr", "mevrouw"), # Mrs.
|
||||
("dr", "dokter"), # doctor
|
||||
("jhr", "jonkheer"), # young lord or nobleman
|
||||
# Dutch uses more abbreviations, but these are the most common ones.
|
||||
]
|
||||
],
|
||||
"tr": [
|
||||
(re.compile("\\b%s\\." % x[0], re.IGNORECASE), x[1])
|
||||
for x in [
|
||||
("b", "bay"), # Mr.
|
||||
("byk", "büyük"), # büyük
|
||||
("dr", "doktor"), # doctor
|
||||
# Add other Turkish abbreviations here if needed.
|
||||
]
|
||||
],
|
||||
"hu": [
|
||||
(re.compile("\\b%s\\." % x[0], re.IGNORECASE), x[1])
|
||||
for x in [
|
||||
("dr", "doktor"), # doctor
|
||||
("b", "bácsi"), # Mr.
|
||||
("nőv", "nővér"), # nurse
|
||||
# Add other Hungarian abbreviations here if needed.
|
||||
]
|
||||
],
|
||||
"ko": [
|
||||
(re.compile("\\b%s\\." % x[0], re.IGNORECASE), x[1])
|
||||
for x in [
|
||||
# Korean doesn't typically use abbreviations in the same way as Latin-based scripts.
|
||||
]
|
||||
],
|
||||
"ja": [
|
||||
(re.compile("\\b%s\\b" % x[0]), x[1])
|
||||
for x in [
|
||||
("氏", "さん"), # Mr.
|
||||
("夫人", "おんなのひと"), # Mrs.
|
||||
("博士", "はかせ"), # Doctor or PhD
|
||||
("株", "株式会社"), # Corporation
|
||||
("有", "有限会社"), # Limited company
|
||||
("大学", "だいがく"), # University
|
||||
("先生", "せんせい"), # Teacher/Professor/Master
|
||||
("君", "くん") # Used at the end of boys' names to express familiarity or affection.
|
||||
]
|
||||
],
|
||||
}
|
||||
|
||||
|
||||
def expand_abbreviations_multilingual(text, lang="en"):
|
||||
for regex, replacement in _abbreviations[lang]:
|
||||
text = re.sub(regex, replacement, text)
|
||||
return text
|
||||
|
||||
|
||||
_symbols_multilingual = {
|
||||
"en": [
|
||||
(re.compile(r"%s" % re.escape(x[0]), re.IGNORECASE), x[1])
|
||||
for x in [
|
||||
("&", " and "),
|
||||
("@", " at "),
|
||||
("%", " percent "),
|
||||
("#", " hash "),
|
||||
("$", " dollar "),
|
||||
("£", " pound "),
|
||||
("°", " degree "),
|
||||
]
|
||||
],
|
||||
"es": [
|
||||
(re.compile(r"%s" % re.escape(x[0]), re.IGNORECASE), x[1])
|
||||
for x in [
|
||||
("&", " y "),
|
||||
("@", " arroba "),
|
||||
("%", " por ciento "),
|
||||
("#", " numeral "),
|
||||
("$", " dolar "),
|
||||
("£", " libra "),
|
||||
("°", " grados "),
|
||||
]
|
||||
],
|
||||
"fr": [
|
||||
(re.compile(r"%s" % re.escape(x[0]), re.IGNORECASE), x[1])
|
||||
for x in [
|
||||
("&", " et "),
|
||||
("@", " arobase "),
|
||||
("%", " pour cent "),
|
||||
("#", " dièse "),
|
||||
("$", " dollar "),
|
||||
("£", " livre "),
|
||||
("°", " degrés "),
|
||||
]
|
||||
],
|
||||
"de": [
|
||||
(re.compile(r"%s" % re.escape(x[0]), re.IGNORECASE), x[1])
|
||||
for x in [
|
||||
("&", " und "),
|
||||
("@", " at "),
|
||||
("%", " prozent "),
|
||||
("#", " raute "),
|
||||
("$", " dollar "),
|
||||
("£", " pfund "),
|
||||
("°", " grad "),
|
||||
]
|
||||
],
|
||||
"pt": [
|
||||
(re.compile(r"%s" % re.escape(x[0]), re.IGNORECASE), x[1])
|
||||
for x in [
|
||||
("&", " e "),
|
||||
("@", " arroba "),
|
||||
("%", " por cento "),
|
||||
("#", " cardinal "),
|
||||
("$", " dólar "),
|
||||
("£", " libra "),
|
||||
("°", " graus "),
|
||||
]
|
||||
],
|
||||
"it": [
|
||||
(re.compile(r"%s" % re.escape(x[0]), re.IGNORECASE), x[1])
|
||||
for x in [
|
||||
("&", " e "),
|
||||
("@", " chiocciola "),
|
||||
("%", " per cento "),
|
||||
("#", " cancelletto "),
|
||||
("$", " dollaro "),
|
||||
("£", " sterlina "),
|
||||
("°", " gradi "),
|
||||
]
|
||||
],
|
||||
"pl": [
|
||||
(re.compile(r"%s" % re.escape(x[0]), re.IGNORECASE), x[1])
|
||||
for x in [
|
||||
("&", " i "),
|
||||
("@", " małpa "),
|
||||
("%", " procent "),
|
||||
("#", " krzyżyk "),
|
||||
("$", " dolar "),
|
||||
("£", " funt "),
|
||||
("°", " stopnie "),
|
||||
]
|
||||
],
|
||||
"ar": [
|
||||
# Arabic
|
||||
(re.compile(r"%s" % re.escape(x[0]), re.IGNORECASE), x[1])
|
||||
for x in [
|
||||
("&", " و "),
|
||||
("@", " على "),
|
||||
("%", " في المئة "),
|
||||
("#", " رقم "),
|
||||
("$", " دولار "),
|
||||
("£", " جنيه "),
|
||||
("°", " درجة "),
|
||||
]
|
||||
],
|
||||
"zh": [
|
||||
# Chinese
|
||||
(re.compile(r"%s" % re.escape(x[0]), re.IGNORECASE), x[1])
|
||||
for x in [
|
||||
("&", " 和 "),
|
||||
("@", " 在 "),
|
||||
("%", " 百分之 "),
|
||||
("#", " 号 "),
|
||||
("$", " 美元 "),
|
||||
("£", " 英镑 "),
|
||||
("°", " 度 "),
|
||||
]
|
||||
],
|
||||
"cs": [
|
||||
# Czech
|
||||
(re.compile(r"%s" % re.escape(x[0]), re.IGNORECASE), x[1])
|
||||
for x in [
|
||||
("&", " a "),
|
||||
("@", " na "),
|
||||
("%", " procento "),
|
||||
("#", " křížek "),
|
||||
("$", " dolar "),
|
||||
("£", " libra "),
|
||||
("°", " stupně "),
|
||||
]
|
||||
],
|
||||
"ru": [
|
||||
# Russian
|
||||
(re.compile(r"%s" % re.escape(x[0]), re.IGNORECASE), x[1])
|
||||
for x in [
|
||||
("&", " и "),
|
||||
("@", " собака "),
|
||||
("%", " процентов "),
|
||||
("#", " номер "),
|
||||
("$", " доллар "),
|
||||
("£", " фунт "),
|
||||
("°", " градус "),
|
||||
]
|
||||
],
|
||||
"nl": [
|
||||
# Dutch
|
||||
(re.compile(r"%s" % re.escape(x[0]), re.IGNORECASE), x[1])
|
||||
for x in [
|
||||
("&", " en "),
|
||||
("@", " bij "),
|
||||
("%", " procent "),
|
||||
("#", " hekje "),
|
||||
("$", " dollar "),
|
||||
("£", " pond "),
|
||||
("°", " graden "),
|
||||
]
|
||||
],
|
||||
"tr": [
|
||||
(re.compile(r"%s" % re.escape(x[0]), re.IGNORECASE), x[1])
|
||||
for x in [
|
||||
("&", " ve "),
|
||||
("@", " at "),
|
||||
("%", " yüzde "),
|
||||
("#", " diyez "),
|
||||
("$", " dolar "),
|
||||
("£", " sterlin "),
|
||||
("°", " derece "),
|
||||
]
|
||||
],
|
||||
"hu": [
|
||||
(re.compile(r"%s" % re.escape(x[0]), re.IGNORECASE), x[1])
|
||||
for x in [
|
||||
("&", " és "),
|
||||
("@", " kukac "),
|
||||
("%", " százalék "),
|
||||
("#", " kettőskereszt "),
|
||||
("$", " dollár "),
|
||||
("£", " font "),
|
||||
("°", " fok "),
|
||||
]
|
||||
],
|
||||
"ko": [
|
||||
# Korean
|
||||
(re.compile(r"%s" % re.escape(x[0]), re.IGNORECASE), x[1])
|
||||
for x in [
|
||||
("&", " 그리고 "),
|
||||
("@", " 에 "),
|
||||
("%", " 퍼센트 "),
|
||||
("#", " 번호 "),
|
||||
("$", " 달러 "),
|
||||
("£", " 파운드 "),
|
||||
("°", " 도 "),
|
||||
]
|
||||
],
|
||||
"ja": [
|
||||
(re.compile(r"%s" % re.escape(x[0])), x[1])
|
||||
for x in [
|
||||
("&", " と "),
|
||||
("@", " アットマーク "),
|
||||
("%", " パーセント "),
|
||||
("#", " ナンバー "),
|
||||
("$", " ドル "),
|
||||
("£", " ポンド "),
|
||||
("°", " 度"),
|
||||
]
|
||||
],
|
||||
}
|
||||
|
||||
|
||||
def expand_symbols_multilingual(text, lang="en"):
|
||||
for regex, replacement in _symbols_multilingual[lang]:
|
||||
text = re.sub(regex, replacement, text)
|
||||
text = text.replace(" ", " ") # Ensure there are no double spaces
|
||||
return text.strip()
|
||||
|
||||
|
||||
_ordinal_re = {
|
||||
"en": re.compile(r"([0-9]+)(st|nd|rd|th)"),
|
||||
"es": re.compile(r"([0-9]+)(º|ª|er|o|a|os|as)"),
|
||||
"fr": re.compile(r"([0-9]+)(º|ª|er|re|e|ème)"),
|
||||
"de": re.compile(r"([0-9]+)(st|nd|rd|th|º|ª|\.(?=\s|$))"),
|
||||
"pt": re.compile(r"([0-9]+)(º|ª|o|a|os|as)"),
|
||||
"it": re.compile(r"([0-9]+)(º|°|ª|o|a|i|e)"),
|
||||
"pl": re.compile(r"([0-9]+)(º|ª|st|nd|rd|th)"),
|
||||
"ar": re.compile(r"([0-9]+)(ون|ين|ث|ر|ى)"),
|
||||
"cs": re.compile(r"([0-9]+)\.(?=\s|$)"), # In Czech, a dot is often used after the number to indicate ordinals.
|
||||
"ru": re.compile(r"([0-9]+)(-й|-я|-е|-ое|-ье|-го)"),
|
||||
"nl": re.compile(r"([0-9]+)(de|ste|e)"),
|
||||
"tr": re.compile(r"([0-9]+)(\.|inci|nci|uncu|üncü|\.)"),
|
||||
"hu": re.compile(r"([0-9]+)(\.|adik|edik|odik|edik|ödik|ödike|ik)"),
|
||||
"ko": re.compile(r"([0-9]+)(번째|번|차|째)"),
|
||||
"ja": re.compile(r"([0-9]+)(番|回|つ|目|等|位)")
|
||||
}
|
||||
_number_re = re.compile(r"[0-9]+")
|
||||
_currency_re = {
|
||||
"USD": re.compile(r"((\$[0-9\.\,]*[0-9]+)|([0-9\.\,]*[0-9]+\$))"),
|
||||
"GBP": re.compile(r"((£[0-9\.\,]*[0-9]+)|([0-9\.\,]*[0-9]+£))"),
|
||||
"EUR": re.compile(r"(([0-9\.\,]*[0-9]+€)|((€[0-9\.\,]*[0-9]+)))"),
|
||||
}
|
||||
|
||||
_comma_number_re = re.compile(r"\b\d{1,3}(,\d{3})*(\.\d+)?\b")
|
||||
_dot_number_re = re.compile(r"\b\d{1,3}(.\d{3})*(\,\d+)?\b")
|
||||
_decimal_number_re = re.compile(r"([0-9]+[.,][0-9]+)")
|
||||
|
||||
|
||||
def _remove_commas(m):
|
||||
text = m.group(0)
|
||||
if "," in text:
|
||||
text = text.replace(",", "")
|
||||
return text
|
||||
|
||||
|
||||
def _remove_dots(m):
|
||||
text = m.group(0)
|
||||
if "." in text:
|
||||
text = text.replace(".", "")
|
||||
return text
|
||||
|
||||
|
||||
def _expand_decimal_point(m, lang="en"):
|
||||
amount = m.group(1).replace(",", ".")
|
||||
return num2words(float(amount), lang=lang if lang != "cs" else "cz")
|
||||
|
||||
|
||||
def _expand_currency(m, lang="en", currency="USD"):
|
||||
amount = float((re.sub(r"[^\d.]", "", m.group(0).replace(",", "."))))
|
||||
full_amount = num2words(amount, to="currency", currency=currency, lang=lang if lang != "cs" else "cz")
|
||||
|
||||
and_equivalents = {
|
||||
"en": ", ",
|
||||
"es": " con ",
|
||||
"fr": " et ",
|
||||
"de": " und ",
|
||||
"pt": " e ",
|
||||
"it": " e ",
|
||||
"pl": ", ",
|
||||
"cs": ", ",
|
||||
"ru": ", ",
|
||||
"nl": ", ",
|
||||
"ar": ", ",
|
||||
"tr": ", ",
|
||||
"hu": ", ",
|
||||
"ko": ", ",
|
||||
}
|
||||
|
||||
if amount.is_integer():
|
||||
last_and = full_amount.rfind(and_equivalents[lang])
|
||||
if last_and != -1:
|
||||
full_amount = full_amount[:last_and]
|
||||
|
||||
return full_amount
|
||||
|
||||
|
||||
def _expand_ordinal(m, lang="en"):
|
||||
return num2words(int(m.group(1)), ordinal=True, lang=lang if lang != "cs" else "cz")
|
||||
|
||||
|
||||
def _expand_number(m, lang="en"):
|
||||
return num2words(int(m.group(0)), lang=lang if lang != "cs" else "cz")
|
||||
|
||||
|
||||
def expand_numbers_multilingual(text, lang="en"):
|
||||
if lang == "zh":
|
||||
text = zh_num2words()(text)
|
||||
else:
|
||||
if lang in ["en", "ru"]:
|
||||
text = re.sub(_comma_number_re, _remove_commas, text)
|
||||
else:
|
||||
text = re.sub(_dot_number_re, _remove_dots, text)
|
||||
try:
|
||||
text = re.sub(_currency_re["GBP"], lambda m: _expand_currency(m, lang, "GBP"), text)
|
||||
text = re.sub(_currency_re["USD"], lambda m: _expand_currency(m, lang, "USD"), text)
|
||||
text = re.sub(_currency_re["EUR"], lambda m: _expand_currency(m, lang, "EUR"), text)
|
||||
except:
|
||||
pass
|
||||
if lang != "tr":
|
||||
text = re.sub(_decimal_number_re, lambda m: _expand_decimal_point(m, lang), text)
|
||||
text = re.sub(_ordinal_re[lang], lambda m: _expand_ordinal(m, lang), text)
|
||||
text = re.sub(_number_re, lambda m: _expand_number(m, lang), text)
|
||||
return text
|
||||
|
||||
|
||||
def lowercase(text):
|
||||
return text.lower()
|
||||
|
||||
|
||||
def collapse_whitespace(text):
|
||||
return re.sub(_whitespace_re, " ", text)
|
||||
|
||||
|
||||
def multilingual_cleaners(text, lang):
|
||||
text = text.replace('"', "")
|
||||
if lang == "tr":
|
||||
text = text.replace("İ", "i")
|
||||
text = text.replace("Ö", "ö")
|
||||
text = text.replace("Ü", "ü")
|
||||
text = lowercase(text)
|
||||
text = expand_numbers_multilingual(text, lang)
|
||||
text = expand_abbreviations_multilingual(text, lang)
|
||||
text = expand_symbols_multilingual(text, lang=lang)
|
||||
text = collapse_whitespace(text)
|
||||
return text
|
||||
|
||||
|
||||
def basic_cleaners(text):
|
||||
"""Basic pipeline that lowercases and collapses whitespace without transliteration."""
|
||||
text = lowercase(text)
|
||||
text = collapse_whitespace(text)
|
||||
return text
|
||||
|
||||
|
||||
def chinese_transliterate(text):
|
||||
return "".join(
|
||||
[p[0] for p in pypinyin.pinyin(text, style=pypinyin.Style.TONE3, heteronym=False, neutral_tone_with_five=True)]
|
||||
)
|
||||
|
||||
|
||||
def japanese_cleaners(text, katsu):
|
||||
text = katsu.romaji(text)
|
||||
text = lowercase(text)
|
||||
return text
|
||||
|
||||
|
||||
def korean_transliterate(text):
|
||||
r = Transliter(academic)
|
||||
return r.translit(text)
|
||||
|
||||
|
||||
DEFAULT_VOCAB_FILE = os.path.join(os.path.dirname(os.path.realpath(__file__)), "../data/tokenizer.json")
|
||||
|
||||
|
||||
class VoiceBpeTokenizer:
|
||||
def __init__(self, vocab_file=None):
|
||||
self.tokenizer = None
|
||||
if vocab_file is not None:
|
||||
self.tokenizer = Tokenizer.from_file(vocab_file)
|
||||
self.char_limits = {
|
||||
"en": 250,
|
||||
"de": 253,
|
||||
"fr": 273,
|
||||
"es": 239,
|
||||
"it": 213,
|
||||
"pt": 203,
|
||||
"pl": 224,
|
||||
"zh": 82,
|
||||
"ar": 166,
|
||||
"cs": 186,
|
||||
"ru": 182,
|
||||
"nl": 251,
|
||||
"tr": 226,
|
||||
"ja": 71,
|
||||
"hu": 224,
|
||||
"ko": 95,
|
||||
}
|
||||
|
||||
@cached_property
|
||||
def katsu(self):
|
||||
import cutlet
|
||||
|
||||
return cutlet.Cutlet()
|
||||
|
||||
def check_input_length(self, txt, lang):
|
||||
lang = lang.split("-")[0] # remove the region
|
||||
limit = self.char_limits.get(lang, 250)
|
||||
if len(txt) > limit:
|
||||
print(
|
||||
f"[!] Warning: The text length exceeds the character limit of {limit} for language '{lang}', this might cause truncated audio."
|
||||
)
|
||||
|
||||
def preprocess_text(self, txt, lang):
|
||||
if lang in {"ar", "cs", "de", "en", "es", "fr", "hu", "it", "nl", "pl", "pt", "ru", "tr", "zh", "ko"}:
|
||||
txt = multilingual_cleaners(txt, lang)
|
||||
if lang == "zh":
|
||||
txt = chinese_transliterate(txt)
|
||||
if lang == "ko":
|
||||
txt = korean_transliterate(txt)
|
||||
elif lang == "ja":
|
||||
txt = japanese_cleaners(txt, self.katsu)
|
||||
elif lang == "hi":
|
||||
# @manmay will implement this
|
||||
txt = basic_cleaners(txt)
|
||||
else:
|
||||
raise NotImplementedError(f"Language '{lang}' is not supported.")
|
||||
return txt
|
||||
|
||||
def encode(self, txt, lang):
|
||||
lang = lang.split("-")[0] # remove the region
|
||||
self.check_input_length(txt, lang)
|
||||
txt = self.preprocess_text(txt, lang)
|
||||
lang = "zh-cn" if lang == "zh" else lang
|
||||
txt = f"[{lang}]{txt}"
|
||||
txt = txt.replace(" ", "[SPACE]")
|
||||
return self.tokenizer.encode(txt).ids
|
||||
|
||||
def decode(self, seq):
|
||||
if isinstance(seq, torch.Tensor):
|
||||
seq = seq.cpu().numpy()
|
||||
txt = self.tokenizer.decode(seq, skip_special_tokens=False).replace(" ", "")
|
||||
txt = txt.replace("[SPACE]", " ")
|
||||
txt = txt.replace("[STOP]", "")
|
||||
txt = txt.replace("[UNK]", "")
|
||||
return txt
|
||||
|
||||
def __len__(self):
|
||||
return self.tokenizer.get_vocab_size()
|
||||
|
||||
def get_number_tokens(self):
|
||||
return max(self.tokenizer.get_vocab().values()) + 1
|
||||
|
||||
|
||||
def test_expand_numbers_multilingual():
|
||||
test_cases = [
|
||||
# English
|
||||
("In 12.5 seconds.", "In twelve point five seconds.", "en"),
|
||||
("There were 50 soldiers.", "There were fifty soldiers.", "en"),
|
||||
("This is a 1st test", "This is a first test", "en"),
|
||||
("That will be $20 sir.", "That will be twenty dollars sir.", "en"),
|
||||
("That will be 20€ sir.", "That will be twenty euro sir.", "en"),
|
||||
("That will be 20.15€ sir.", "That will be twenty euro, fifteen cents sir.", "en"),
|
||||
("That's 100,000.5.", "That's one hundred thousand point five.", "en"),
|
||||
# French
|
||||
("En 12,5 secondes.", "En douze virgule cinq secondes.", "fr"),
|
||||
("Il y avait 50 soldats.", "Il y avait cinquante soldats.", "fr"),
|
||||
("Ceci est un 1er test", "Ceci est un premier test", "fr"),
|
||||
("Cela vous fera $20 monsieur.", "Cela vous fera vingt dollars monsieur.", "fr"),
|
||||
("Cela vous fera 20€ monsieur.", "Cela vous fera vingt euros monsieur.", "fr"),
|
||||
("Cela vous fera 20,15€ monsieur.", "Cela vous fera vingt euros et quinze centimes monsieur.", "fr"),
|
||||
("Ce sera 100.000,5.", "Ce sera cent mille virgule cinq.", "fr"),
|
||||
# German
|
||||
("In 12,5 Sekunden.", "In zwölf Komma fünf Sekunden.", "de"),
|
||||
("Es gab 50 Soldaten.", "Es gab fünfzig Soldaten.", "de"),
|
||||
("Dies ist ein 1. Test", "Dies ist ein erste Test", "de"), # Issue with gender
|
||||
("Das macht $20 Herr.", "Das macht zwanzig Dollar Herr.", "de"),
|
||||
("Das macht 20€ Herr.", "Das macht zwanzig Euro Herr.", "de"),
|
||||
("Das macht 20,15€ Herr.", "Das macht zwanzig Euro und fünfzehn Cent Herr.", "de"),
|
||||
# Spanish
|
||||
("En 12,5 segundos.", "En doce punto cinco segundos.", "es"),
|
||||
("Había 50 soldados.", "Había cincuenta soldados.", "es"),
|
||||
("Este es un 1er test", "Este es un primero test", "es"),
|
||||
("Eso le costará $20 señor.", "Eso le costará veinte dólares señor.", "es"),
|
||||
("Eso le costará 20€ señor.", "Eso le costará veinte euros señor.", "es"),
|
||||
("Eso le costará 20,15€ señor.", "Eso le costará veinte euros con quince céntimos señor.", "es"),
|
||||
# Italian
|
||||
("In 12,5 secondi.", "In dodici virgola cinque secondi.", "it"),
|
||||
("C'erano 50 soldati.", "C'erano cinquanta soldati.", "it"),
|
||||
("Questo è un 1° test", "Questo è un primo test", "it"),
|
||||
("Ti costerà $20 signore.", "Ti costerà venti dollari signore.", "it"),
|
||||
("Ti costerà 20€ signore.", "Ti costerà venti euro signore.", "it"),
|
||||
("Ti costerà 20,15€ signore.", "Ti costerà venti euro e quindici centesimi signore.", "it"),
|
||||
# Portuguese
|
||||
("Em 12,5 segundos.", "Em doze vírgula cinco segundos.", "pt"),
|
||||
("Havia 50 soldados.", "Havia cinquenta soldados.", "pt"),
|
||||
("Este é um 1º teste", "Este é um primeiro teste", "pt"),
|
||||
("Isso custará $20 senhor.", "Isso custará vinte dólares senhor.", "pt"),
|
||||
("Isso custará 20€ senhor.", "Isso custará vinte euros senhor.", "pt"),
|
||||
(
|
||||
"Isso custará 20,15€ senhor.",
|
||||
"Isso custará vinte euros e quinze cêntimos senhor.",
|
||||
"pt",
|
||||
), # "cêntimos" should be "centavos" num2words issue
|
||||
# Polish
|
||||
("W 12,5 sekundy.", "W dwanaście przecinek pięć sekundy.", "pl"),
|
||||
("Było 50 żołnierzy.", "Było pięćdziesiąt żołnierzy.", "pl"),
|
||||
("To będzie kosztować 20€ panie.", "To będzie kosztować dwadzieścia euro panie.", "pl"),
|
||||
("To będzie kosztować 20,15€ panie.", "To będzie kosztować dwadzieścia euro, piętnaście centów panie.", "pl"),
|
||||
# Arabic
|
||||
("في الـ 12,5 ثانية.", "في الـ اثنا عشر , خمسون ثانية.", "ar"),
|
||||
("كان هناك 50 جنديًا.", "كان هناك خمسون جنديًا.", "ar"),
|
||||
# ("ستكون النتيجة $20 يا سيد.", 'ستكون النتيجة عشرون دولار يا سيد.', 'ar'), # $ and € are mising from num2words
|
||||
# ("ستكون النتيجة 20€ يا سيد.", 'ستكون النتيجة عشرون يورو يا سيد.', 'ar'),
|
||||
# Czech
|
||||
("Za 12,5 vteřiny.", "Za dvanáct celá pět vteřiny.", "cs"),
|
||||
("Bylo tam 50 vojáků.", "Bylo tam padesát vojáků.", "cs"),
|
||||
("To bude stát 20€ pane.", "To bude stát dvacet euro pane.", "cs"),
|
||||
("To bude 20.15€ pane.", "To bude dvacet euro, patnáct centů pane.", "cs"),
|
||||
# Russian
|
||||
("Через 12.5 секунды.", "Через двенадцать запятая пять секунды.", "ru"),
|
||||
("Там было 50 солдат.", "Там было пятьдесят солдат.", "ru"),
|
||||
("Это будет 20.15€ сэр.", "Это будет двадцать евро, пятнадцать центов сэр.", "ru"),
|
||||
("Это будет стоить 20€ господин.", "Это будет стоить двадцать евро господин.", "ru"),
|
||||
# Dutch
|
||||
("In 12,5 seconden.", "In twaalf komma vijf seconden.", "nl"),
|
||||
("Er waren 50 soldaten.", "Er waren vijftig soldaten.", "nl"),
|
||||
("Dat wordt dan $20 meneer.", "Dat wordt dan twintig dollar meneer.", "nl"),
|
||||
("Dat wordt dan 20€ meneer.", "Dat wordt dan twintig euro meneer.", "nl"),
|
||||
# Chinese (Simplified)
|
||||
("在12.5秒内", "在十二点五秒内", "zh"),
|
||||
("有50名士兵", "有五十名士兵", "zh"),
|
||||
# ("那将是$20先生", '那将是二十美元先生', 'zh'), currency doesn't work
|
||||
# ("那将是20€先生", '那将是二十欧元先生', 'zh'),
|
||||
# Turkish
|
||||
# ("12,5 saniye içinde.", 'On iki virgül beş saniye içinde.', 'tr'), # decimal doesn't work for TR
|
||||
("50 asker vardı.", "elli asker vardı.", "tr"),
|
||||
("Bu 1. test", "Bu birinci test", "tr"),
|
||||
# ("Bu 100.000,5.", 'Bu yüz bin virgül beş.', 'tr'),
|
||||
# Hungarian
|
||||
("12,5 másodperc alatt.", "tizenkettő egész öt tized másodperc alatt.", "hu"),
|
||||
("50 katona volt.", "ötven katona volt.", "hu"),
|
||||
("Ez az 1. teszt", "Ez az első teszt", "hu"),
|
||||
# Korean
|
||||
("12.5 초 안에.", "십이 점 다섯 초 안에.", "ko"),
|
||||
("50 명의 병사가 있었다.", "오십 명의 병사가 있었다.", "ko"),
|
||||
("이것은 1 번째 테스트입니다", "이것은 첫 번째 테스트입니다", "ko"),
|
||||
]
|
||||
for a, b, lang in test_cases:
|
||||
out = expand_numbers_multilingual(a, lang=lang)
|
||||
assert out == b, f"'{out}' vs '{b}'"
|
||||
|
||||
|
||||
def test_abbreviations_multilingual():
|
||||
test_cases = [
|
||||
# English
|
||||
("Hello Mr. Smith.", "Hello mister Smith.", "en"),
|
||||
("Dr. Jones is here.", "doctor Jones is here.", "en"),
|
||||
# Spanish
|
||||
("Hola Sr. Garcia.", "Hola señor Garcia.", "es"),
|
||||
("La Dra. Martinez es muy buena.", "La doctora Martinez es muy buena.", "es"),
|
||||
# French
|
||||
("Bonjour Mr. Dupond.", "Bonjour monsieur Dupond.", "fr"),
|
||||
("Mme. Moreau est absente aujourd'hui.", "madame Moreau est absente aujourd'hui.", "fr"),
|
||||
# German
|
||||
("Frau Dr. Müller ist sehr klug.", "Frau doktor Müller ist sehr klug.", "de"),
|
||||
# Portuguese
|
||||
("Olá Sr. Silva.", "Olá senhor Silva.", "pt"),
|
||||
("Dra. Costa, você está disponível?", "doutora Costa, você está disponível?", "pt"),
|
||||
# Italian
|
||||
("Buongiorno, Sig. Rossi.", "Buongiorno, signore Rossi.", "it"),
|
||||
# ("Sig.ra Bianchi, posso aiutarti?", 'signora Bianchi, posso aiutarti?', 'it'), # Issue with matching that pattern
|
||||
# Polish
|
||||
("Dzień dobry, P. Kowalski.", "Dzień dobry, pani Kowalski.", "pl"),
|
||||
("M. Nowak, czy mogę zadać pytanie?", "pan Nowak, czy mogę zadać pytanie?", "pl"),
|
||||
# Czech
|
||||
("P. Novák", "pan Novák", "cs"),
|
||||
("Dr. Vojtěch", "doktor Vojtěch", "cs"),
|
||||
# Dutch
|
||||
("Dhr. Jansen", "de heer Jansen", "nl"),
|
||||
("Mevr. de Vries", "mevrouw de Vries", "nl"),
|
||||
# Russian
|
||||
("Здравствуйте Г-н Иванов.", "Здравствуйте господин Иванов.", "ru"),
|
||||
("Д-р Смирнов здесь, чтобы увидеть вас.", "доктор Смирнов здесь, чтобы увидеть вас.", "ru"),
|
||||
# Turkish
|
||||
("Merhaba B. Yılmaz.", "Merhaba bay Yılmaz.", "tr"),
|
||||
("Dr. Ayşe burada.", "doktor Ayşe burada.", "tr"),
|
||||
# Hungarian
|
||||
("Dr. Szabó itt van.", "doktor Szabó itt van.", "hu"),
|
||||
]
|
||||
|
||||
for a, b, lang in test_cases:
|
||||
out = expand_abbreviations_multilingual(a, lang=lang)
|
||||
assert out == b, f"'{out}' vs '{b}'"
|
||||
|
||||
|
||||
def test_symbols_multilingual():
|
||||
test_cases = [
|
||||
("I have 14% battery", "I have 14 percent battery", "en"),
|
||||
("Te veo @ la fiesta", "Te veo arroba la fiesta", "es"),
|
||||
("J'ai 14° de fièvre", "J'ai 14 degrés de fièvre", "fr"),
|
||||
("Die Rechnung beträgt £ 20", "Die Rechnung beträgt pfund 20", "de"),
|
||||
("O meu email é ana&joao@gmail.com", "O meu email é ana e joao arroba gmail.com", "pt"),
|
||||
("linguaggio di programmazione C#", "linguaggio di programmazione C cancelletto", "it"),
|
||||
("Moja temperatura to 36.6°", "Moja temperatura to 36.6 stopnie", "pl"),
|
||||
("Mám 14% baterie", "Mám 14 procento baterie", "cs"),
|
||||
("Těším se na tebe @ party", "Těším se na tebe na party", "cs"),
|
||||
("У меня 14% заряда", "У меня 14 процентов заряда", "ru"),
|
||||
("Я буду @ дома", "Я буду собака дома", "ru"),
|
||||
("Ik heb 14% batterij", "Ik heb 14 procent batterij", "nl"),
|
||||
("Ik zie je @ het feest", "Ik zie je bij het feest", "nl"),
|
||||
("لدي 14% في البطارية", "لدي 14 في المئة في البطارية", "ar"),
|
||||
("我的电量为 14%", "我的电量为 14 百分之", "zh"),
|
||||
("Pilim %14 dolu.", "Pilim yüzde 14 dolu.", "tr"),
|
||||
("Az akkumulátorom töltöttsége 14%", "Az akkumulátorom töltöttsége 14 százalék", "hu"),
|
||||
("배터리 잔량이 14%입니다.", "배터리 잔량이 14 퍼센트입니다.", "ko"),
|
||||
]
|
||||
|
||||
for a, b, lang in test_cases:
|
||||
out = expand_symbols_multilingual(a, lang=lang)
|
||||
assert out == b, f"'{out}' vs '{b}'"
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
test_expand_numbers_multilingual()
|
||||
test_abbreviations_multilingual()
|
||||
test_symbols_multilingual()
|
||||
@@ -0,0 +1,173 @@
|
||||
absl-py==2.1.0
|
||||
aiofiles==23.2.1
|
||||
aiohttp==3.9.3
|
||||
aiosignal==1.3.1
|
||||
altair==5.2.0
|
||||
annotated-types==0.6.0
|
||||
anyascii==0.3.2
|
||||
anyio==4.3.0
|
||||
attrs==23.2.0
|
||||
audioread==3.0.1
|
||||
av==11.0.0
|
||||
Babel==2.14.0
|
||||
bangla==0.0.2
|
||||
blinker==1.7.0
|
||||
blis==0.7.11
|
||||
bnnumerizer==0.0.2
|
||||
bnunicodenormalizer==0.1.6
|
||||
catalogue==2.0.10
|
||||
certifi==2024.2.2
|
||||
cffi==1.16.0
|
||||
charset-normalizer==3.3.2
|
||||
click==8.1.7
|
||||
cloudpathlib==0.16.0
|
||||
colorama==0.4.6
|
||||
coloredlogs==15.0.1
|
||||
confection==0.1.4
|
||||
contourpy==1.2.0
|
||||
coqpit==0.0.17
|
||||
ctranslate2==4.1.0
|
||||
cutlet==0.4.0
|
||||
cycler==0.12.1
|
||||
cymem==2.0.8
|
||||
Cython==3.0.9
|
||||
dateparser==1.1.8
|
||||
decorator==5.1.1
|
||||
docopt==0.6.2
|
||||
einops==0.7.0
|
||||
encodec==0.1.1
|
||||
exceptiongroup
|
||||
fastapi==0.110.0
|
||||
faster-whisper==1.0.1
|
||||
ffmpy==0.3.2
|
||||
filelock==3.13.3
|
||||
Flask==3.0.2
|
||||
flatbuffers==24.3.25
|
||||
fonttools==4.50.0
|
||||
frozenlist==1.4.1
|
||||
fsspec==2024.3.1
|
||||
fugashi==1.3.1
|
||||
fuzzywuzzy>=0.18.0
|
||||
g2pkk==0.1.2
|
||||
gradio==3.50.2
|
||||
gradio_client==0.6.1
|
||||
grpcio==1.62.1
|
||||
gruut==2.2.3
|
||||
gruut-ipa==0.13.0
|
||||
gruut-lang-de==2.0.0
|
||||
gruut-lang-en==2.0.0
|
||||
gruut-lang-es==2.0.0
|
||||
gruut-lang-fr==2.0.2
|
||||
h11==0.14.0
|
||||
hangul-romanize==0.1.0
|
||||
httpcore==1.0.4
|
||||
httpx==0.27.0
|
||||
huggingface-hub==0.22.1
|
||||
humanfriendly==10.0
|
||||
idna==3.6
|
||||
importlib_metadata==7.1.0
|
||||
importlib_resources==6.4.0
|
||||
inflect==7.0.0
|
||||
itsdangerous==2.1.2
|
||||
jaconv==0.3.4
|
||||
jamo==0.4.1
|
||||
jieba==0.42.1
|
||||
Jinja2==3.1.3
|
||||
joblib==1.3.2
|
||||
jsonlines==1.2.0
|
||||
jsonschema==4.21.1
|
||||
jsonschema-specifications==2023.12.1
|
||||
kiwisolver==1.4.5
|
||||
langcodes==3.3.0
|
||||
lazy_loader==0.3
|
||||
librosa==0.10.1
|
||||
llvmlite==0.42.0
|
||||
Markdown==3.6
|
||||
MarkupSafe==2.1.5
|
||||
matplotlib==3.8.3
|
||||
mojimoji==0.0.13
|
||||
mpmath==1.3.0
|
||||
msgpack==1.0.8
|
||||
multidict==6.0.5
|
||||
murmurhash==1.0.10
|
||||
networkx==2.8.8
|
||||
nltk==3.8.1
|
||||
num2words==0.5.13
|
||||
numba==0.59.1
|
||||
numpy==1.26.4
|
||||
nvidia-cublas-cu11>=11.11.3.6
|
||||
nvidia-cudnn-cu11>=9.0.0.312
|
||||
onnxruntime==1.17.1
|
||||
orjson==3.9.15
|
||||
packaging==24.0
|
||||
pandas==1.5.3
|
||||
pillow==10.2.0
|
||||
platformdirs==4.2.0
|
||||
pooch==1.8.1
|
||||
preshed==3.0.9
|
||||
protobuf==5.26.0
|
||||
psutil==5.9.8
|
||||
pycparser==2.21
|
||||
pydantic==2.6.4
|
||||
pydantic_core==2.16.3
|
||||
pydub==0.25.1
|
||||
pynndescent==0.5.11
|
||||
pyparsing==3.1.2
|
||||
pypinyin==0.51.0
|
||||
pyreadline3==3.4.1
|
||||
pysbd==0.3.4
|
||||
python-crfsuite==0.9.10
|
||||
python-dateutil==2.9.0.post0
|
||||
python-Levenshtein>=0.25.0
|
||||
python-multipart==0.0.9
|
||||
pytz==2024.1
|
||||
PyYAML==6.0.1
|
||||
referencing==0.34.0
|
||||
regex==2023.12.25
|
||||
requests==2.31.0
|
||||
rpds-py==0.18.0
|
||||
safetensors==0.4.2
|
||||
scikit-learn==1.4.1.post1
|
||||
scipy==1.12.0
|
||||
semantic-version==2.10.0
|
||||
six==1.16.0
|
||||
smart-open==6.4.0
|
||||
sniffio==1.3.1
|
||||
sounddevice==0.4.6
|
||||
soundfile==0.12.1
|
||||
soxr==0.3.7
|
||||
spacy==3.7.4
|
||||
spacy-legacy==3.0.12
|
||||
spacy-loggers==1.0.5
|
||||
srsly==2.4.8
|
||||
starlette==0.36.3
|
||||
SudachiDict-core==20240109
|
||||
SudachiPy==0.6.8
|
||||
sympy==1.12
|
||||
tensorboard==2.16.2
|
||||
tensorboard-data-server==0.7.2
|
||||
thinc==8.2.3
|
||||
threadpoolctl==3.4.0
|
||||
tokenizers==0.15.2
|
||||
toolz==0.12.1
|
||||
torch>=2.2.0
|
||||
torchaudio>=2.2.0
|
||||
tqdm==4.66.2
|
||||
trainer==0.0.36
|
||||
transformers==4.39.1
|
||||
TTS==0.22.0
|
||||
typer==0.9.4
|
||||
typing_extensions==4.10.0
|
||||
tzdata==2024.1
|
||||
tzlocal==5.2
|
||||
umap-learn==0.5.5
|
||||
Unidecode==1.3.8
|
||||
unidic-lite==1.0.8
|
||||
urllib3==2.2.1
|
||||
uvicorn==0.29.0
|
||||
wasabi==1.1.2
|
||||
weasel==0.3.4
|
||||
websockets==11.0.3
|
||||
Werkzeug==3.0.1
|
||||
yarl==1.9.4
|
||||
zipp==3.18.1
|
||||
@@ -0,0 +1,171 @@
|
||||
absl-py==2.1.0
|
||||
aiofiles==23.2.1
|
||||
aiohttp==3.9.3
|
||||
aiosignal==1.3.1
|
||||
altair==5.2.0
|
||||
annotated-types==0.6.0
|
||||
anyascii==0.3.2
|
||||
anyio==4.3.0
|
||||
attrs==23.2.0
|
||||
audioread==3.0.1
|
||||
av==11.0.0
|
||||
Babel==2.14.0
|
||||
bangla==0.0.2
|
||||
blinker==1.7.0
|
||||
blis==0.7.11
|
||||
bnnumerizer==0.0.2
|
||||
bnunicodenormalizer==0.1.6
|
||||
catalogue==2.0.10
|
||||
certifi==2024.2.2
|
||||
cffi==1.16.0
|
||||
charset-normalizer==3.3.2
|
||||
click==8.1.7
|
||||
cloudpathlib==0.16.0
|
||||
colorama==0.4.6
|
||||
coloredlogs==15.0.1
|
||||
confection==0.1.4
|
||||
contourpy==1.2.0
|
||||
coqpit==0.0.17
|
||||
ctranslate2==4.1.0
|
||||
cutlet==0.4.0
|
||||
cycler==0.12.1
|
||||
cymem==2.0.8
|
||||
Cython==3.0.9
|
||||
dateparser==1.1.8
|
||||
decorator==5.1.1
|
||||
docopt==0.6.2
|
||||
einops==0.7.0
|
||||
encodec==0.1.1
|
||||
fastapi==0.110.0
|
||||
faster-whisper==1.0.1
|
||||
ffmpy==0.3.2
|
||||
filelock==3.13.3
|
||||
Flask==3.0.2
|
||||
flatbuffers==24.3.25
|
||||
fonttools==4.50.0
|
||||
frozenlist==1.4.1
|
||||
fsspec==2024.3.1
|
||||
fugashi==1.3.1
|
||||
fuzzywuzzy>=0.18.0
|
||||
g2pkk==0.1.2
|
||||
gradio==3.50.2
|
||||
gradio_client==0.6.1
|
||||
grpcio==1.62.1
|
||||
gruut==2.2.3
|
||||
gruut-ipa==0.13.0
|
||||
gruut-lang-de==2.0.0
|
||||
gruut-lang-en==2.0.0
|
||||
gruut-lang-es==2.0.0
|
||||
gruut-lang-fr==2.0.2
|
||||
h11==0.14.0
|
||||
hangul-romanize==0.1.0
|
||||
httpcore==1.0.4
|
||||
httpx==0.27.0
|
||||
huggingface-hub==0.22.1
|
||||
humanfriendly==10.0
|
||||
idna==3.6
|
||||
importlib_metadata==7.1.0
|
||||
importlib_resources==6.4.0
|
||||
inflect==7.0.0
|
||||
itsdangerous==2.1.2
|
||||
jaconv==0.3.4
|
||||
jamo==0.4.1
|
||||
jieba==0.42.1
|
||||
Jinja2==3.1.3
|
||||
joblib==1.3.2
|
||||
jsonlines==1.2.0
|
||||
jsonschema==4.21.1
|
||||
jsonschema-specifications==2023.12.1
|
||||
kiwisolver==1.4.5
|
||||
langcodes==3.3.0
|
||||
lazy_loader==0.3
|
||||
librosa==0.10.1
|
||||
llvmlite==0.42.0
|
||||
Markdown==3.6
|
||||
MarkupSafe==2.1.5
|
||||
matplotlib==3.8.3
|
||||
mojimoji==0.0.13
|
||||
mpmath==1.3.0
|
||||
msgpack==1.0.8
|
||||
multidict==6.0.5
|
||||
murmurhash==1.0.10
|
||||
networkx==2.8.8
|
||||
nltk==3.8.1
|
||||
num2words==0.5.13
|
||||
numba==0.59.1
|
||||
numpy>=1.17.0,<1.29.0
|
||||
nvidia-cublas-cu11>=11.11.3.6
|
||||
nvidia-cudnn-cu11>=9.0.0.312
|
||||
onnxruntime==1.17.1
|
||||
orjson==3.9.15
|
||||
packaging==24.0
|
||||
pandas==1.5.3
|
||||
pillow==10.2.0
|
||||
platformdirs==4.2.0
|
||||
pooch==1.8.1
|
||||
preshed==3.0.9
|
||||
protobuf==5.26.0
|
||||
psutil==5.9.8
|
||||
pycparser==2.21
|
||||
pydantic==2.6.4
|
||||
pydantic_core==2.16.3
|
||||
pydub==0.25.1
|
||||
pynndescent==0.5.11
|
||||
pyparsing==3.1.2
|
||||
pypinyin==0.51.0
|
||||
pyreadline3==3.4.1
|
||||
pysbd==0.3.4
|
||||
python-crfsuite==0.9.10
|
||||
python-dateutil==2.9.0.post0
|
||||
python-Levenshtein>=0.25.0
|
||||
python-multipart==0.0.9
|
||||
pytz==2024.1
|
||||
PyYAML==6.0.1
|
||||
referencing==0.34.0
|
||||
regex==2023.12.25
|
||||
requests==2.31.0
|
||||
rpds-py==0.18.0
|
||||
safetensors==0.4.2
|
||||
scikit-learn==1.4.1.post1
|
||||
scipy==1.12.0
|
||||
semantic-version==2.10.0
|
||||
six==1.16.0
|
||||
smart-open==6.4.0
|
||||
sniffio==1.3.1
|
||||
sounddevice==0.4.6
|
||||
soundfile==0.12.1
|
||||
soxr==0.3.7
|
||||
spacy==3.7.4
|
||||
spacy-legacy==3.0.12
|
||||
spacy-loggers==1.0.5
|
||||
srsly==2.4.8
|
||||
starlette==0.36.3
|
||||
SudachiDict-core==20240109
|
||||
SudachiPy==0.6.8
|
||||
sympy==1.12
|
||||
tensorboard==2.16.2
|
||||
tensorboard-data-server==0.7.2
|
||||
thinc==8.2.3
|
||||
threadpoolctl==3.4.0
|
||||
tokenizers==0.15.2
|
||||
toolz==0.12.1
|
||||
torch>=2.2.0
|
||||
torchaudio>=2.2.0
|
||||
tqdm==4.66.2
|
||||
trainer==0.0.36
|
||||
transformers==4.39.1
|
||||
typer==0.9.4
|
||||
typing_extensions==4.10.0
|
||||
tzdata==2024.1
|
||||
tzlocal==5.2
|
||||
umap-learn==0.5.5
|
||||
Unidecode==1.3.8
|
||||
unidic-lite==1.0.8
|
||||
urllib3==2.2.1
|
||||
uvicorn==0.29.0
|
||||
wasabi==1.1.2
|
||||
weasel==0.3.4
|
||||
websockets==11.0.3
|
||||
Werkzeug==3.0.1
|
||||
yarl==1.9.4
|
||||
zipp==3.18.1
|
||||
@@ -0,0 +1,172 @@
|
||||
absl-py==2.1.0
|
||||
aiofiles==23.2.1
|
||||
aiohttp==3.9.3
|
||||
aiosignal==1.3.1
|
||||
altair==5.2.0
|
||||
annotated-types==0.6.0
|
||||
anyascii==0.3.2
|
||||
anyio==4.3.0
|
||||
attrs==23.2.0
|
||||
audioread==3.0.1
|
||||
av==11.0.0
|
||||
Babel==2.14.0
|
||||
bangla==0.0.2
|
||||
blinker==1.7.0
|
||||
blis==0.7.11
|
||||
bnnumerizer==0.0.2
|
||||
bnunicodenormalizer==0.1.6
|
||||
catalogue==2.0.10
|
||||
certifi==2024.2.2
|
||||
cffi==1.16.0
|
||||
charset-normalizer==3.3.2
|
||||
click==8.1.7
|
||||
cloudpathlib==0.16.0
|
||||
colorama==0.4.6
|
||||
coloredlogs==15.0.1
|
||||
confection==0.1.4
|
||||
contourpy==1.2.0
|
||||
coqpit==0.0.17
|
||||
ctranslate2==4.1.0
|
||||
cutlet==0.4.0
|
||||
cycler==0.12.1
|
||||
cymem==2.0.8
|
||||
Cython==3.0.9
|
||||
dateparser==1.1.8
|
||||
decorator==5.1.1
|
||||
docopt==0.6.2
|
||||
einops==0.7.0
|
||||
encodec==0.1.1
|
||||
fastapi==0.110.0
|
||||
faster-whisper==1.0.1
|
||||
ffmpy==0.3.2
|
||||
filelock==3.13.3
|
||||
Flask==3.0.2
|
||||
flatbuffers==24.3.25
|
||||
fonttools==4.50.0
|
||||
frozenlist==1.4.1
|
||||
fsspec==2024.3.1
|
||||
fugashi==1.3.1
|
||||
fuzzywuzzy>=0.18.0
|
||||
g2pkk==0.1.2
|
||||
gradio==3.50.2
|
||||
gradio_client==0.6.1
|
||||
grpcio==1.62.1
|
||||
gruut==2.2.3
|
||||
gruut-ipa==0.13.0
|
||||
gruut-lang-de==2.0.0
|
||||
gruut-lang-en==2.0.0
|
||||
gruut-lang-es==2.0.0
|
||||
gruut-lang-fr==2.0.2
|
||||
h11==0.14.0
|
||||
hangul-romanize==0.1.0
|
||||
httpcore==1.0.4
|
||||
httpx==0.27.0
|
||||
huggingface-hub==0.22.1
|
||||
humanfriendly==10.0
|
||||
idna==3.6
|
||||
importlib_metadata==7.1.0
|
||||
importlib_resources==6.4.0
|
||||
inflect==7.0.0
|
||||
itsdangerous==2.1.2
|
||||
jaconv==0.3.4
|
||||
jamo==0.4.1
|
||||
jieba==0.42.1
|
||||
Jinja2==3.1.3
|
||||
joblib==1.3.2
|
||||
jsonlines==1.2.0
|
||||
jsonschema==4.21.1
|
||||
jsonschema-specifications==2023.12.1
|
||||
kiwisolver==1.4.5
|
||||
langcodes==3.3.0
|
||||
lazy_loader==0.3
|
||||
librosa==0.10.1
|
||||
llvmlite==0.42.0
|
||||
Markdown==3.6
|
||||
MarkupSafe==2.1.5
|
||||
matplotlib==3.8.3
|
||||
mojimoji==0.0.13
|
||||
mpmath==1.3.0
|
||||
msgpack==1.0.8
|
||||
multidict==6.0.5
|
||||
murmurhash==1.0.10
|
||||
networkx==2.8.8
|
||||
nltk==3.8.1
|
||||
num2words==0.5.13
|
||||
numba==0.59.1
|
||||
numpy==1.26.4
|
||||
nvidia-cublas-cu11>=11.11.3.6
|
||||
nvidia-cudnn-cu11>=9.0.0.312
|
||||
onnxruntime==1.17.1
|
||||
orjson==3.9.15
|
||||
packaging==24.0
|
||||
pandas==1.5.3
|
||||
pillow==10.2.0
|
||||
platformdirs==4.2.0
|
||||
pooch==1.8.1
|
||||
preshed==3.0.9
|
||||
protobuf==5.26.0
|
||||
psutil==5.9.8
|
||||
pycparser==2.21
|
||||
pydantic==2.6.4
|
||||
pydantic_core==2.16.3
|
||||
pydub==0.25.1
|
||||
pynndescent==0.5.11
|
||||
pyparsing==3.1.2
|
||||
pypinyin==0.51.0
|
||||
pyreadline3==3.4.1
|
||||
pysbd==0.3.4
|
||||
python-crfsuite==0.9.10
|
||||
python-dateutil==2.9.0.post0
|
||||
python-Levenshtein>=0.25.0
|
||||
python-multipart==0.0.9
|
||||
pytz==2024.1
|
||||
PyYAML==6.0.1
|
||||
referencing==0.34.0
|
||||
regex==2023.12.25
|
||||
requests==2.31.0
|
||||
rpds-py==0.18.0
|
||||
safetensors==0.4.2
|
||||
scikit-learn==1.4.1.post1
|
||||
scipy==1.12.0
|
||||
semantic-version==2.10.0
|
||||
six==1.16.0
|
||||
smart-open==6.4.0
|
||||
sniffio==1.3.1
|
||||
sounddevice==0.4.6
|
||||
soundfile==0.12.1
|
||||
soxr==0.3.7
|
||||
spacy==3.7.4
|
||||
spacy-legacy==3.0.12
|
||||
spacy-loggers==1.0.5
|
||||
srsly==2.4.8
|
||||
starlette==0.36.3
|
||||
SudachiDict-core==20240109
|
||||
SudachiPy==0.6.8
|
||||
sympy==1.12
|
||||
tensorboard==2.16.2
|
||||
tensorboard-data-server==0.7.2
|
||||
thinc==8.2.3
|
||||
threadpoolctl==3.4.0
|
||||
tokenizers==0.15.2
|
||||
toolz==0.12.1
|
||||
torch>=2.2.0
|
||||
torchaudio>=2.2.0
|
||||
tqdm==4.66.2
|
||||
trainer==0.0.36
|
||||
transformers==4.39.1
|
||||
TTS==0.22.0
|
||||
typer==0.9.4
|
||||
typing_extensions==4.10.0
|
||||
tzdata==2024.1
|
||||
tzlocal==5.2
|
||||
umap-learn==0.5.5
|
||||
Unidecode==1.3.8
|
||||
unidic-lite==1.0.8
|
||||
urllib3==2.2.1
|
||||
uvicorn==0.29.0
|
||||
wasabi==1.1.2
|
||||
weasel==0.3.4
|
||||
websockets==11.0.3
|
||||
Werkzeug==3.0.1
|
||||
yarl==1.9.4
|
||||
zipp==3.18.1
|
||||
@@ -1,7 +1,8 @@
|
||||
numpy>=1.22.0
|
||||
soundfile>=0.12.1
|
||||
uvicorn>=0.24.0.post1
|
||||
TTS>=0.21.3
|
||||
transformers>=4.37.1
|
||||
TTS>=0.22.0
|
||||
torch>=2.1.0+cu118
|
||||
torchaudio>=2.1.0+cu118
|
||||
fastapi>=0.104.1
|
||||
@@ -13,3 +14,12 @@ packaging>=23.2
|
||||
pydantic>=1.10.13
|
||||
sounddevice>=0.4.6
|
||||
python-multipart>=0.0.6
|
||||
cutlet>=0.3.0
|
||||
unidic-lite>=1.0.8
|
||||
faster-whisper>=0.10.0
|
||||
pandas>=1.5.0
|
||||
nvidia-cublas-cu11>=11.11.3.6
|
||||
nvidia-cudnn-cu11>=9.0.0.312
|
||||
fuzzywuzzy>=0.18.0
|
||||
python-Levenshtein>=0.25.0
|
||||
spacy>=3.7.2
|
||||
@@ -0,0 +1,833 @@
|
||||
import { doExtrasFetch, getApiUrl, modules } from '../../extensions.js';
|
||||
import { saveTtsProviderSettings } from './index.js';
|
||||
|
||||
export { AllTalkTtsProvider };
|
||||
|
||||
class AllTalkTtsProvider {
|
||||
//########//
|
||||
// Config //
|
||||
//########//
|
||||
|
||||
settings = {};
|
||||
constructor() {
|
||||
// Initialize with default settings if they are not already set
|
||||
this.settings = {
|
||||
provider_endpoint: this.settings.provider_endpoint || 'http://localhost:7851',
|
||||
language: this.settings.language || 'en',
|
||||
voiceMap: this.settings.voiceMap || {},
|
||||
at_generation_method: this.settings.at_generation_method || 'standard_generation',
|
||||
narrator_enabled: this.settings.narrator_enabled || 'false',
|
||||
at_narrator_text_not_inside: this.settings.at_narrator_text_not_inside || 'narrator',
|
||||
narrator_voice_gen: this.settings.narrator_voice_gen || 'female_01.wav',
|
||||
finetuned_model: this.settings.finetuned_model || 'false'
|
||||
};
|
||||
// Separate property for dynamically updated settings from the server
|
||||
this.dynamicSettings = {
|
||||
modelsAvailable: [],
|
||||
currentModel: '',
|
||||
deepspeed_available: false,
|
||||
deepSpeedEnabled: false,
|
||||
lowVramEnabled: false,
|
||||
};
|
||||
}
|
||||
ready = false;
|
||||
voices = [];
|
||||
separator = '. ';
|
||||
audioElement = document.createElement('audio');
|
||||
|
||||
languageLabels = {
|
||||
'Arabic': 'ar',
|
||||
'Brazilian Portuguese': 'pt',
|
||||
'Chinese': 'zh-cn',
|
||||
'Czech': 'cs',
|
||||
'Dutch': 'nl',
|
||||
'English': 'en',
|
||||
'French': 'fr',
|
||||
'German': 'de',
|
||||
'Italian': 'it',
|
||||
'Polish': 'pl',
|
||||
'Russian': 'ru',
|
||||
'Spanish': 'es',
|
||||
'Turkish': 'tr',
|
||||
'Japanese': 'ja',
|
||||
'Korean': 'ko',
|
||||
'Hungarian': 'hu',
|
||||
'Hindi': 'hi',
|
||||
};
|
||||
|
||||
get settingsHtml() {
|
||||
let html = `<div class="at-settings-separator">AllTalk Settings</div>`;
|
||||
|
||||
html += `<div class="at-settings-row">
|
||||
|
||||
<div class="at-settings-option">
|
||||
<label for="at_generation_method">AllTalk TTS Generation Method</label>
|
||||
<select id="at_generation_method">
|
||||
<option value="standard_generation">Standard Audio Generation (AT Narrator - Optional)</option>
|
||||
<option value="streaming_enabled">Streaming Audio Generation (AT Narrator - Disabled)</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>`;
|
||||
|
||||
html += `<div class="at-settings-row">
|
||||
|
||||
<div class="at-settings-option">
|
||||
<label for="at_narrator_enabled">AT Narrator</label>
|
||||
<select id="at_narrator_enabled">
|
||||
<option value="true">Enabled</option>
|
||||
<option value="false">Disabled</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="at-settings-option">
|
||||
<label for="at_narrator_text_not_inside">Text Not Inside * or " is</label>
|
||||
<select id="at_narrator_text_not_inside">
|
||||
<option value="narrator">Narrator</option>
|
||||
<option value="character">Character</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
</div>`;
|
||||
|
||||
html += `<div class="at-settings-row">
|
||||
<div class="at-settings-option">
|
||||
<label for="narrator_voice">Narrator Voice</label>
|
||||
<select id="narrator_voice">`;
|
||||
if (this.voices) {
|
||||
for (let voice of this.voices) {
|
||||
html += `<option value="${voice.voice_id}">${voice.name}</option>`;
|
||||
}
|
||||
}
|
||||
html += `</select>
|
||||
</div>
|
||||
<div class="at-settings-option">
|
||||
<label for="language_options">Language</label>
|
||||
<select id="language_options">`;
|
||||
for (let language in this.languageLabels) {
|
||||
html += `<option value="${this.languageLabels[language]}" ${this.languageLabels[language] === this.settings?.language ? 'selected="selected"' : ''}>${language}</option>`;
|
||||
}
|
||||
html += `</select>
|
||||
</div>
|
||||
</div>`;
|
||||
|
||||
|
||||
html += `<div class="at-model-endpoint-row">
|
||||
<div class="at-model-option">
|
||||
<label for="switch_model">Switch Model</label>
|
||||
<select id="switch_model">
|
||||
<option value="api_tts">API TTS</option>
|
||||
<option value="api_local">API Local</option>
|
||||
<option value="xttsv2_local">XTTSv2 Local</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="at-endpoint-option">
|
||||
<label for="at_server">AllTalk Endpoint:</label>
|
||||
<input id="at_server" type="text" class="text_pole" maxlength="80" value="${this.settings.provider_endpoint}"/>
|
||||
</div>
|
||||
</div>`;
|
||||
|
||||
|
||||
html += `<div class="at-model-endpoint-row">
|
||||
<div class="at-settings-option">
|
||||
<label for="low_vram">Low VRAM</label>
|
||||
<input id="low_vram" type="checkbox"/>
|
||||
</div>
|
||||
<div class="at-settings-option">
|
||||
<label for="deepspeed">DeepSpeed</label>
|
||||
<input id="deepspeed" type="checkbox"/>
|
||||
</div>
|
||||
<div class="at-settings-option status-option">
|
||||
<span>Status: <span id="status_info">Ready</span></span>
|
||||
</div>
|
||||
<div class="at-settings-option empty-option">
|
||||
<!-- This div remains empty for spacing -->
|
||||
</div>
|
||||
</div>`;
|
||||
|
||||
|
||||
html += `<div class="at-website-row">
|
||||
<div class="at-website-option">
|
||||
<span>AllTalk <a target="_blank" href="${this.settings.provider_endpoint}">Config & Docs</a>.</span>
|
||||
</div>
|
||||
|
||||
<div class="at-website-option">
|
||||
<span>AllTalk <a target="_blank" href="https://github.com/erew123/alltalk_tts/">Website</a>.</span>
|
||||
</div>
|
||||
</div>`;
|
||||
|
||||
html += `<div class="at-website-row">
|
||||
<div class="at-website-option">
|
||||
<span><strong>Text-generation-webui</strong> users - Uncheck <strong>Enable TTS</strong> in Text-generation-webui.</span>
|
||||
</div>
|
||||
</div>`;
|
||||
|
||||
return html;
|
||||
}
|
||||
|
||||
|
||||
//#################//
|
||||
// Startup ST & AT //
|
||||
//#################//
|
||||
|
||||
async loadSettings(settings) {
|
||||
updateStatus('Offline');
|
||||
|
||||
if (Object.keys(settings).length === 0) {
|
||||
console.info('Using default AllTalk TTS Provider settings');
|
||||
} else {
|
||||
// Populate settings with provided values, ignoring server-provided settings
|
||||
for (const key in settings) {
|
||||
if (key in this.settings) {
|
||||
this.settings[key] = settings[key];
|
||||
} else {
|
||||
console.debug(`Ignoring non-user-configurable setting: ${key}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Update UI elements to reflect the loaded settings
|
||||
$('#at_server').val(this.settings.provider_endpoint);
|
||||
$('#language_options').val(this.settings.language);
|
||||
//$('#voicemap').val(this.settings.voiceMap);
|
||||
$('#at_generation_method').val(this.settings.at_generation_method);
|
||||
$('#at_narrator_enabled').val(this.settings.narrator_enabled);
|
||||
$('#at_narrator_text_not_inside').val(this.settings.at_narrator_text_not_inside);
|
||||
$('#narrator_voice').val(this.settings.narrator_voice_gen);
|
||||
|
||||
console.debug('AllTalkTTS: Settings loaded');
|
||||
try {
|
||||
// Check if TTS provider is ready
|
||||
await this.checkReady();
|
||||
await this.updateSettingsFromServer(); // Fetch dynamic settings from the TTS server
|
||||
await this.fetchTtsVoiceObjects(); // Fetch voices only if service is ready
|
||||
this.updateNarratorVoicesDropdown();
|
||||
this.updateLanguageDropdown();
|
||||
this.setupEventListeners();
|
||||
this.applySettingsToHTML();
|
||||
updateStatus('Ready');
|
||||
} catch (error) {
|
||||
console.error("Error loading settings:", error);
|
||||
updateStatus('Offline');
|
||||
}
|
||||
}
|
||||
|
||||
applySettingsToHTML() {
|
||||
// Apply loaded settings or use defaults
|
||||
const narratorVoiceSelect = document.getElementById('narrator_voice');
|
||||
const atNarratorSelect = document.getElementById('at_narrator_enabled');
|
||||
const textNotInsideSelect = document.getElementById('at_narrator_text_not_inside');
|
||||
const generationMethodSelect = document.getElementById('at_generation_method');
|
||||
this.settings.narrator_voice = this.settings.narrator_voice_gen;
|
||||
// Apply settings to Narrator Voice dropdown
|
||||
if (narratorVoiceSelect && this.settings.narrator_voice) {
|
||||
narratorVoiceSelect.value = this.settings.narrator_voice.replace('.wav', '');
|
||||
}
|
||||
// Apply settings to AT Narrator Enabled dropdown
|
||||
if (atNarratorSelect) {
|
||||
// Sync the state with the checkbox in index.js
|
||||
const ttsPassAsterisksCheckbox = document.getElementById('tts_pass_asterisks'); // Access the checkbox from index.js
|
||||
const ttsNarrateQuotedCheckbox = document.getElementById('tts_narrate_quoted'); // Access the checkbox from index.js
|
||||
const ttsNarrateDialoguesCheckbox = document.getElementById('tts_narrate_dialogues'); // Access the checkbox from index.js
|
||||
// Sync the state with the checkbox in index.js
|
||||
if (this.settings.narrator_enabled) {
|
||||
ttsPassAsterisksCheckbox.checked = false;
|
||||
$('#tts_pass_asterisks').click(); // Simulate a click event
|
||||
$('#tts_pass_asterisks').trigger('change');
|
||||
}
|
||||
if (!this.settings.narrator_enabled) {
|
||||
ttsPassAsterisksCheckbox.checked = true;
|
||||
$('#tts_pass_asterisks').click(); // Simulate a click event
|
||||
$('#tts_pass_asterisks').trigger('change');
|
||||
}
|
||||
// Uncheck and set tts_narrate_quoted to false if narrator is enabled
|
||||
if (this.settings.narrator_enabledd) {
|
||||
ttsNarrateQuotedCheckbox.checked = true;
|
||||
ttsNarrateDialoguesCheckbox.checked = true;
|
||||
// Trigger click events instead of change events
|
||||
$('#tts_narrate_quoted').click();
|
||||
$('#tts_narrate_quoted').trigger('change');
|
||||
$('#tts_narrate_dialogues').click();
|
||||
$('#tts_narrate_dialogues').trigger('change');
|
||||
}
|
||||
atNarratorSelect.value = this.settings.narrator_enabled.toString();
|
||||
this.settings.narrator_enabled = this.settings.narrator_enabled.toString();
|
||||
}
|
||||
// Apply settings to the Language dropdown
|
||||
const languageSelect = document.getElementById('language_options');
|
||||
if (languageSelect && this.settings.language) {
|
||||
languageSelect.value = this.settings.language;
|
||||
}
|
||||
// Apply settings to Text Not Inside dropdown
|
||||
if (textNotInsideSelect && this.settings.text_not_inside) {
|
||||
textNotInsideSelect.value = this.settings.text_not_inside;
|
||||
this.settings.at_narrator_text_not_inside = this.settings.text_not_inside;
|
||||
}
|
||||
// Apply settings to Generation Method dropdown
|
||||
if (generationMethodSelect && this.settings.at_generation_method) {
|
||||
generationMethodSelect.value = this.settings.at_generation_method;
|
||||
}
|
||||
// Additional logic to disable/enable dropdowns based on the selected generation method
|
||||
const isStreamingEnabled = this.settings.at_generation_method === 'streaming_enabled';
|
||||
if (isStreamingEnabled) {
|
||||
// Disable certain dropdowns when streaming is enabled
|
||||
if (atNarratorSelect) atNarratorSelect.disabled = true;
|
||||
if (textNotInsideSelect) textNotInsideSelect.disabled = true;
|
||||
if (narratorVoiceSelect) narratorVoiceSelect.disabled = true;
|
||||
} else {
|
||||
// Enable dropdowns for standard generation
|
||||
if (atNarratorSelect) atNarratorSelect.disabled = false;
|
||||
if (textNotInsideSelect) textNotInsideSelect.disabled = !this.settings.narrator_enabled;
|
||||
if (narratorVoiceSelect) narratorVoiceSelect.disabled = !this.settings.narrator_enabled;
|
||||
}
|
||||
const modelSelect = document.getElementById('switch_model');
|
||||
if (this.settings.finetuned_model === true) {
|
||||
const ftOption = document.createElement('option');
|
||||
ftOption.value = 'XTTSv2 FT';
|
||||
ftOption.textContent = 'XTTSv2 FT';
|
||||
modelSelect.appendChild(ftOption);
|
||||
}
|
||||
}
|
||||
|
||||
//##############################//
|
||||
// Check AT Server is Available //
|
||||
//##############################//
|
||||
|
||||
async checkReady() {
|
||||
try {
|
||||
const response = await fetch(`${this.settings.provider_endpoint}/api/ready`);
|
||||
// Check if the HTTP request was successful
|
||||
if (!response.ok) {
|
||||
throw new Error(`HTTP Error Response: ${response.status} ${response.statusText}`);
|
||||
}
|
||||
const statusText = await response.text();
|
||||
// Check if the response is 'Ready'
|
||||
if (statusText === 'Ready') {
|
||||
this.ready = true; // Set the ready flag to true
|
||||
console.log('TTS service is ready.');
|
||||
} else {
|
||||
this.ready = false;
|
||||
console.log('TTS service is not ready.');
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error checking TTS service readiness:', error);
|
||||
this.ready = false; // Ensure ready flag is set to false in case of error
|
||||
}
|
||||
}
|
||||
|
||||
//######################//
|
||||
// Get Available Voices //
|
||||
//######################//
|
||||
|
||||
async fetchTtsVoiceObjects() {
|
||||
const response = await fetch(`${this.settings.provider_endpoint}/api/voices`);
|
||||
if (!response.ok) {
|
||||
const errorText = await response.text();
|
||||
throw new Error(`HTTP ${response.status}: ${errorText}`);
|
||||
}
|
||||
const data = await response.json();
|
||||
const voices = data.voices.map(filename => {
|
||||
const voiceName = filename.replace('.wav', '');
|
||||
return {
|
||||
name: voiceName,
|
||||
voice_id: voiceName,
|
||||
preview_url: null, // Preview URL will be dynamically generated
|
||||
lang: 'en' // Default language
|
||||
};
|
||||
});
|
||||
this.voices = voices; // Assign to the class property
|
||||
return voices; // Also return this list
|
||||
}
|
||||
|
||||
//##########################################//
|
||||
// Get Current AT Server Config & Update ST //
|
||||
//##########################################//
|
||||
|
||||
async updateSettingsFromServer() {
|
||||
try {
|
||||
// Fetch current settings
|
||||
const response = await fetch(`${this.settings.provider_endpoint}/api/currentsettings`);
|
||||
if (!response.ok) {
|
||||
throw new Error(`Failed to fetch current settings: ${response.statusText}`);
|
||||
}
|
||||
const currentSettings = await response.json();
|
||||
// Update internal settings
|
||||
this.settings.modelsAvailable = currentSettings.models_available;
|
||||
this.settings.currentModel = currentSettings.current_model_loaded;
|
||||
this.settings.deepspeed_available = currentSettings.deepspeed_available;
|
||||
this.settings.deepSpeedEnabled = currentSettings.deepspeed_status;
|
||||
this.settings.lowVramEnabled = currentSettings.low_vram_status;
|
||||
this.settings.finetuned_model = currentSettings.finetuned_model
|
||||
// Update HTML elements
|
||||
this.updateModelDropdown();
|
||||
this.updateCheckboxes();
|
||||
} catch (error) {
|
||||
console.error(`Error updating settings from server: ${error}`);
|
||||
}
|
||||
}
|
||||
|
||||
//###################################################//
|
||||
// Get Current AT Server Config & Update ST (Models) //
|
||||
//###################################################//
|
||||
|
||||
updateModelDropdown() {
|
||||
const modelSelect = document.getElementById('switch_model');
|
||||
if (modelSelect) {
|
||||
modelSelect.innerHTML = ''; // Clear existing options
|
||||
this.settings.modelsAvailable.forEach(model => {
|
||||
const option = document.createElement('option');
|
||||
option.value = model.model_name;
|
||||
option.textContent = model.model_name; // Use model_name instead of name
|
||||
option.selected = model.model_name === this.settings.currentModel;
|
||||
modelSelect.appendChild(option);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
//#######################################################//
|
||||
// Get Current AT Server Config & Update ST (DS and LVR) //
|
||||
//#######################################################//
|
||||
|
||||
updateCheckboxes() {
|
||||
const deepspeedCheckbox = document.getElementById('deepspeed');
|
||||
const lowVramCheckbox = document.getElementById('low_vram');
|
||||
if (lowVramCheckbox) lowVramCheckbox.checked = this.settings.lowVramEnabled;
|
||||
if (deepspeedCheckbox) {
|
||||
deepspeedCheckbox.checked = this.settings.deepSpeedEnabled;
|
||||
deepspeedCheckbox.disabled = !this.settings.deepspeed_available; // Disable checkbox if deepspeed is not available
|
||||
}
|
||||
}
|
||||
|
||||
//###############################################################//
|
||||
// Get Current AT Server Config & Update ST (AT Narrator Voices) //
|
||||
//###############################################################//
|
||||
|
||||
updateNarratorVoicesDropdown() {
|
||||
const narratorVoiceSelect = document.getElementById('narrator_voice');
|
||||
if (narratorVoiceSelect && this.voices) {
|
||||
// Clear existing options
|
||||
narratorVoiceSelect.innerHTML = '';
|
||||
// Add new options
|
||||
for (let voice of this.voices) {
|
||||
const option = document.createElement('option');
|
||||
option.value = voice.voice_id;
|
||||
option.textContent = voice.name;
|
||||
narratorVoiceSelect.appendChild(option);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//######################################################//
|
||||
// Get Current AT Server Config & Update ST (Languages) //
|
||||
//######################################################//
|
||||
|
||||
updateLanguageDropdown() {
|
||||
const languageSelect = document.getElementById('language_options');
|
||||
if (languageSelect) {
|
||||
// Ensure default language is set
|
||||
this.settings.language = this.settings.language;
|
||||
|
||||
languageSelect.innerHTML = '';
|
||||
for (let language in this.languageLabels) {
|
||||
const option = document.createElement('option');
|
||||
option.value = this.languageLabels[language];
|
||||
option.textContent = language;
|
||||
if (this.languageLabels[language] === this.settings.language) {
|
||||
option.selected = true;
|
||||
}
|
||||
languageSelect.appendChild(option);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//########################################//
|
||||
// Start AT TTS extenstion page listeners //
|
||||
//########################################//
|
||||
|
||||
setupEventListeners() {
|
||||
|
||||
let debounceTimeout;
|
||||
const debounceDelay = 500; // Milliseconds
|
||||
|
||||
// Define the event handler function
|
||||
const onModelSelectChange = async (event) => {
|
||||
console.log("Model select change event triggered"); // Debugging statement
|
||||
const selectedModel = event.target.value;
|
||||
console.log(`Selected model: ${selectedModel}`); // Debugging statement
|
||||
// Set status to Processing
|
||||
updateStatus('Processing');
|
||||
try {
|
||||
const response = await fetch(`${this.settings.provider_endpoint}/api/reload?tts_method=${encodeURIComponent(selectedModel)}`, {
|
||||
method: 'POST'
|
||||
});
|
||||
if (!response.ok) {
|
||||
throw new Error(`HTTP Error: ${response.status}`);
|
||||
}
|
||||
const data = await response.json();
|
||||
console.log("POST response data:", data); // Debugging statement
|
||||
// Set status to Ready if successful
|
||||
updateStatus('Ready');
|
||||
} catch (error) {
|
||||
console.error("POST request error:", error); // Debugging statement
|
||||
// Set status to Error in case of failure
|
||||
updateStatus('Error');
|
||||
}
|
||||
|
||||
// Handle response or error
|
||||
};
|
||||
|
||||
const debouncedModelSelectChange = (event) => {
|
||||
clearTimeout(debounceTimeout);
|
||||
debounceTimeout = setTimeout(() => {
|
||||
onModelSelectChange(event);
|
||||
}, debounceDelay);
|
||||
};
|
||||
|
||||
// Switch Model Listener
|
||||
const modelSelect = document.getElementById('switch_model');
|
||||
if (modelSelect) {
|
||||
// Remove the event listener if it was previously added
|
||||
modelSelect.removeEventListener('change', debouncedModelSelectChange);
|
||||
// Add the debounced event listener
|
||||
modelSelect.addEventListener('change', debouncedModelSelectChange);
|
||||
}
|
||||
|
||||
// DeepSpeed Listener
|
||||
const deepspeedCheckbox = document.getElementById('deepspeed');
|
||||
if (deepspeedCheckbox) {
|
||||
deepspeedCheckbox.addEventListener('change', async (event) => {
|
||||
const deepSpeedValue = event.target.checked ? 'True' : 'False';
|
||||
// Set status to Processing
|
||||
updateStatus('Processing');
|
||||
try {
|
||||
const response = await fetch(`${this.settings.provider_endpoint}/api/deepspeed?new_deepspeed_value=${deepSpeedValue}`, {
|
||||
method: 'POST'
|
||||
});
|
||||
if (!response.ok) {
|
||||
throw new Error(`HTTP Error: ${response.status}`);
|
||||
}
|
||||
const data = await response.json();
|
||||
console.log("POST response data:", data); // Debugging statement
|
||||
// Set status to Ready if successful
|
||||
updateStatus('Ready');
|
||||
} catch (error) {
|
||||
console.error("POST request error:", error); // Debugging statement
|
||||
// Set status to Error in case of failure
|
||||
updateStatus('Error');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Low VRAM Listener
|
||||
const lowVramCheckbox = document.getElementById('low_vram');
|
||||
if (lowVramCheckbox) {
|
||||
lowVramCheckbox.addEventListener('change', async (event) => {
|
||||
const lowVramValue = event.target.checked ? 'True' : 'False';
|
||||
// Set status to Processing
|
||||
updateStatus('Processing');
|
||||
try {
|
||||
const response = await fetch(`${this.settings.provider_endpoint}/api/lowvramsetting?new_low_vram_value=${lowVramValue}`, {
|
||||
method: 'POST'
|
||||
});
|
||||
if (!response.ok) {
|
||||
throw new Error(`HTTP Error: ${response.status}`);
|
||||
}
|
||||
const data = await response.json();
|
||||
console.log("POST response data:", data); // Debugging statement
|
||||
// Set status to Ready if successful
|
||||
updateStatus('Ready');
|
||||
} catch (error) {
|
||||
console.error("POST request error:", error); // Debugging statement
|
||||
// Set status to Error in case of failure
|
||||
updateStatus('Error');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Narrator Voice Dropdown Listener
|
||||
const narratorVoiceSelect = document.getElementById('narrator_voice');
|
||||
if (narratorVoiceSelect) {
|
||||
narratorVoiceSelect.addEventListener('change', (event) => {
|
||||
this.settings.narrator_voice_gen = `${event.target.value}.wav`;
|
||||
this.onSettingsChange(); // Save the settings after change
|
||||
});
|
||||
}
|
||||
|
||||
const textNotInsideSelect = document.getElementById('at_narrator_text_not_inside');
|
||||
if (textNotInsideSelect) {
|
||||
textNotInsideSelect.addEventListener('change', (event) => {
|
||||
this.settings.text_not_inside = event.target.value;
|
||||
this.onSettingsChange(); // Save the settings after change
|
||||
});
|
||||
}
|
||||
|
||||
// AT Narrator Dropdown Listener
|
||||
const atNarratorSelect = document.getElementById('at_narrator_enabled');
|
||||
const ttsPassAsterisksCheckbox = document.getElementById('tts_pass_asterisks'); // Access the checkbox from index.js
|
||||
const ttsNarrateQuotedCheckbox = document.getElementById('tts_narrate_quoted'); // Access the checkbox from index.js
|
||||
const ttsNarrateDialoguesCheckbox = document.getElementById('tts_narrate_dialogues'); // Access the checkbox from index.js
|
||||
|
||||
if (atNarratorSelect && textNotInsideSelect && narratorVoiceSelect) {
|
||||
atNarratorSelect.addEventListener('change', (event) => {
|
||||
const isNarratorEnabled = event.target.value === 'true';
|
||||
this.settings.narrator_enabled = isNarratorEnabled; // Update the setting here
|
||||
textNotInsideSelect.disabled = !isNarratorEnabled;
|
||||
narratorVoiceSelect.disabled = !isNarratorEnabled;
|
||||
|
||||
// Sync the state with the checkbox in index.js
|
||||
if (isNarratorEnabled) {
|
||||
ttsPassAsterisksCheckbox.checked = false;
|
||||
$('#tts_pass_asterisks').click(); // Simulate a click event
|
||||
$('#tts_pass_asterisks').trigger('change');
|
||||
}
|
||||
if (!isNarratorEnabled) {
|
||||
ttsPassAsterisksCheckbox.checked = true;
|
||||
$('#tts_pass_asterisks').click(); // Simulate a click event
|
||||
$('#tts_pass_asterisks').trigger('change');
|
||||
}
|
||||
// Uncheck and set tts_narrate_quoted to false if narrator is enabled
|
||||
if (isNarratorEnabled) {
|
||||
ttsNarrateQuotedCheckbox.checked = true;
|
||||
ttsNarrateDialoguesCheckbox.checked = true;
|
||||
// Trigger click events instead of change events
|
||||
$('#tts_narrate_quoted').click();
|
||||
$('#tts_narrate_quoted').trigger('change');
|
||||
$('#tts_narrate_dialogues').click();
|
||||
$('#tts_narrate_dialogues').trigger('change');
|
||||
}
|
||||
this.onSettingsChange(); // Save the settings after change
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
// Event Listener for AT Generation Method Dropdown
|
||||
const atGenerationMethodSelect = document.getElementById('at_generation_method');
|
||||
const atNarratorEnabledSelect = document.getElementById('at_narrator_enabled');
|
||||
if (atGenerationMethodSelect) {
|
||||
atGenerationMethodSelect.addEventListener('change', (event) => {
|
||||
const selectedMethod = event.target.value;
|
||||
|
||||
if (selectedMethod === 'streaming_enabled') {
|
||||
// Disable and unselect AT Narrator
|
||||
atNarratorEnabledSelect.disabled = true;
|
||||
atNarratorEnabledSelect.value = 'false';
|
||||
textNotInsideSelect.disabled = true;
|
||||
narratorVoiceSelect.disabled = true;
|
||||
} else if (selectedMethod === 'standard_generation') {
|
||||
// Enable AT Narrator
|
||||
atNarratorEnabledSelect.disabled = false;
|
||||
}
|
||||
this.settings.at_generation_method = selectedMethod; // Update the setting here
|
||||
this.onSettingsChange(); // Save the settings after change
|
||||
});
|
||||
}
|
||||
|
||||
// Listener for Language Dropdown
|
||||
const languageSelect = document.getElementById('language_options');
|
||||
if (languageSelect) {
|
||||
languageSelect.addEventListener('change', (event) => {
|
||||
this.settings.language = event.target.value;
|
||||
this.onSettingsChange(); // Save the settings after change
|
||||
});
|
||||
}
|
||||
|
||||
// Listener for AllTalk Endpoint Input
|
||||
const atServerInput = document.getElementById('at_server');
|
||||
if (atServerInput) {
|
||||
atServerInput.addEventListener('input', (event) => {
|
||||
this.settings.provider_endpoint = event.target.value;
|
||||
this.onSettingsChange(); // Save the settings after change
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//#############################//
|
||||
// Store ST interface settings //
|
||||
//#############################//
|
||||
|
||||
onSettingsChange() {
|
||||
// Update settings based on the UI elements
|
||||
//this.settings.provider_endpoint = $('#at_server').val();
|
||||
this.settings.language = $('#language_options').val();
|
||||
//this.settings.voiceMap = $('#voicemap').val();
|
||||
this.settings.at_generation_method = $('#at_generation_method').val();
|
||||
this.settings.narrator_enabled = $('#at_narrator_enabled').val();
|
||||
this.settings.at_narrator_text_not_inside = $('#at_narrator_text_not_inside').val();
|
||||
this.settings.narrator_voice_gen = $('#narrator_voice').val();
|
||||
// Save the updated settings
|
||||
saveTtsProviderSettings();
|
||||
}
|
||||
|
||||
//#########################//
|
||||
// ST Handle Reload button //
|
||||
//#########################//
|
||||
|
||||
async onRefreshClick() {
|
||||
await this.checkReady(); // Check if the TTS provider is ready
|
||||
await this.loadSettings(this.settings); // Reload the settings
|
||||
// Additional actions as needed
|
||||
}
|
||||
|
||||
//##################//
|
||||
// Preview AT Voice //
|
||||
//##################//
|
||||
|
||||
async previewTtsVoice(voiceName) {
|
||||
try {
|
||||
// Prepare data for POST request
|
||||
const postData = new URLSearchParams();
|
||||
postData.append("voice", `${voiceName}.wav`);
|
||||
// Making the POST request
|
||||
const response = await fetch(`${this.settings.provider_endpoint}/api/previewvoice/`, {
|
||||
method: "POST",
|
||||
headers: {
|
||||
'Content-Type': 'application/x-www-form-urlencoded'
|
||||
},
|
||||
body: postData,
|
||||
});
|
||||
if (!response.ok) {
|
||||
const errorText = await response.text();
|
||||
console.error(`[previewTtsVoice] Error Response Text:`, errorText);
|
||||
throw new Error(`HTTP ${response.status}: ${errorText}`);
|
||||
}
|
||||
// Assuming the server returns a URL to the .wav file
|
||||
const data = await response.json();
|
||||
if (data.output_file_url) {
|
||||
// Use an audio element to play the .wav file
|
||||
const audioElement = new Audio(data.output_file_url);
|
||||
audioElement.play().catch(e => console.error("Error playing audio:", e));
|
||||
} else {
|
||||
console.warn("[previewTtsVoice] No output file URL received in the response");
|
||||
throw new Error("No output file URL received in the response");
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
console.error("[previewTtsVoice] Exception caught during preview generation:", error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
//#####################//
|
||||
// Populate ST voices //
|
||||
//#####################//
|
||||
|
||||
async getVoice(voiceName, generatePreview = false) {
|
||||
// Ensure this.voices is populated
|
||||
if (this.voices.length === 0) {
|
||||
// Fetch voice objects logic
|
||||
}
|
||||
// Find the object where the name matches voiceName
|
||||
const match = this.voices.find(voice => voice.name === voiceName);
|
||||
if (!match) {
|
||||
// Error handling
|
||||
}
|
||||
// Generate preview URL only if requested
|
||||
if (!match.preview_url && generatePreview) {
|
||||
// Generate preview logic
|
||||
}
|
||||
return match; // Return the found voice object
|
||||
}
|
||||
|
||||
//##########################################//
|
||||
// Generate TTS Streaming or call Standard //
|
||||
//##########################################//
|
||||
|
||||
async generateTts(inputText, voiceId) {
|
||||
try {
|
||||
if (this.settings.at_generation_method === 'streaming_enabled') {
|
||||
// Construct the streaming URL
|
||||
const streamingUrl = `${this.settings.provider_endpoint}/api/tts-generate-streaming?text=${encodeURIComponent(inputText)}&voice=${encodeURIComponent(voiceId)}.wav&language=${encodeURIComponent(this.settings.language)}&output_file=stream_output.wav`;
|
||||
console.log("Streaming URL:", streamingUrl);
|
||||
|
||||
// Return the streaming URL directly
|
||||
return streamingUrl;
|
||||
} else {
|
||||
// For standard method
|
||||
const outputUrl = await this.fetchTtsGeneration(inputText, voiceId);
|
||||
const audioResponse = await fetch(outputUrl);
|
||||
if (!audioResponse.ok) {
|
||||
throw new Error(`HTTP ${audioResponse.status}: Failed to fetch audio data`);
|
||||
}
|
||||
return audioResponse; // Return the fetch response directly
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error in generateTts:", error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//####################//
|
||||
// Generate Standard //
|
||||
//####################//
|
||||
|
||||
async fetchTtsGeneration(inputText, voiceId) {
|
||||
// Prepare the request payload
|
||||
const requestBody = new URLSearchParams({
|
||||
'text_input': inputText,
|
||||
'text_filtering': "standard",
|
||||
'character_voice_gen': voiceId + ".wav",
|
||||
'narrator_enabled': this.settings.narrator_enabled,
|
||||
'narrator_voice_gen': this.settings.narrator_voice_gen + ".wav",
|
||||
'text_not_inside': this.settings.at_narrator_text_not_inside,
|
||||
'language': this.settings.language,
|
||||
'output_file_name': "st_output",
|
||||
'output_file_timestamp': "true",
|
||||
'autoplay': "false",
|
||||
'autoplay_volume': "0.8"
|
||||
}).toString();
|
||||
|
||||
try {
|
||||
const response = await doExtrasFetch(
|
||||
`${this.settings.provider_endpoint}/api/tts-generate`,
|
||||
{
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/x-www-form-urlencoded',
|
||||
'Cache-Control': 'no-cache',
|
||||
},
|
||||
body: requestBody
|
||||
}
|
||||
);
|
||||
|
||||
if (!response.ok) {
|
||||
const errorText = await response.text();
|
||||
console.error(`[fetchTtsGeneration] Error Response Text:`, errorText);
|
||||
// toastr.error(response.statusText, 'TTS Generation Failed');
|
||||
throw new Error(`HTTP ${response.status}: ${errorText}`);
|
||||
}
|
||||
const data = await response.json();
|
||||
const outputUrl = data.output_file_url;
|
||||
return outputUrl; // Return only the output_file_url
|
||||
} catch (error) {
|
||||
console.error("[fetchTtsGeneration] Exception caught:", error);
|
||||
throw error; // Rethrow the error for further handling
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//#########################//
|
||||
// Update Status Messages //
|
||||
//#########################//
|
||||
|
||||
function updateStatus(message) {
|
||||
const statusElement = document.getElementById('status_info');
|
||||
if (statusElement) {
|
||||
statusElement.textContent = message;
|
||||
switch (message) {
|
||||
case 'Offline':
|
||||
statusElement.style.color = 'red';
|
||||
break;
|
||||
case 'Ready':
|
||||
statusElement.style.color = 'lightgreen';
|
||||
break;
|
||||
case 'Processing':
|
||||
statusElement.style.color = 'blue';
|
||||
break;
|
||||
case 'Error':
|
||||
statusElement.style.color = 'red';
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,159 @@
|
||||
#tts_media_control {
|
||||
/* order: 100; */
|
||||
/* width: 40px;
|
||||
height: 40px;
|
||||
margin: 0;
|
||||
padding: 1px; */
|
||||
outline: none;
|
||||
border: none;
|
||||
cursor: pointer;
|
||||
/* transition: 0.3s;
|
||||
opacity: 0.7; */
|
||||
display: flex;
|
||||
align-items: center;
|
||||
/* justify-content: center; */
|
||||
|
||||
}
|
||||
|
||||
#ttsExtensionMenuItem {
|
||||
transition: 0.3s;
|
||||
opacity: 0.7;
|
||||
}
|
||||
|
||||
#ttsExtensionMenuItem:hover {
|
||||
opacity: 1;
|
||||
filter: brightness(1.2);
|
||||
}
|
||||
|
||||
#tts_media_control:hover {
|
||||
opacity: 1;
|
||||
filter: brightness(1.2);
|
||||
}
|
||||
|
||||
.voice_preview {
|
||||
margin: 0.25rem 0.5rem;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
|
||||
.voice_preview .voice_name {
|
||||
text-align: left;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.voice_preview .voice_lang {
|
||||
width: 4rem;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.voice_preview .fa-play {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.tts-button {
|
||||
margin: 0;
|
||||
outline: none;
|
||||
border: none;
|
||||
cursor: pointer;
|
||||
transition: 0.3s;
|
||||
opacity: 0.7;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
|
||||
}
|
||||
|
||||
.tts-button:hover {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.tts_block {
|
||||
display: flex;
|
||||
align-items: baseline;
|
||||
column-gap: 5px;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.tts_custom_voices {
|
||||
display: flex;
|
||||
align-items: baseline;
|
||||
gap: 5px;
|
||||
}
|
||||
|
||||
.novel_tts_hints {
|
||||
font-size: calc(0.9 * var(--mainFontSize));
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 5px;
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
.at-settings-row {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.at-settings-option {
|
||||
flex: 1;
|
||||
margin: 0 10px;
|
||||
}
|
||||
|
||||
.at-endpoint-option {
|
||||
flex: 1;
|
||||
margin: 0 10px;
|
||||
margin-right: 25px;
|
||||
width: 38%;
|
||||
}
|
||||
|
||||
.at-website-row {
|
||||
display: flex;
|
||||
justify-content: start;
|
||||
align-items: center;
|
||||
margin-top: 10px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.at-website-option {
|
||||
flex: 1;
|
||||
margin-right: 10px;
|
||||
margin-left: 10px;
|
||||
}
|
||||
|
||||
.at-settings-separator {
|
||||
margin-top: 10px;
|
||||
margin-bottom: 10px;
|
||||
padding: 18x;
|
||||
font-weight: bold;
|
||||
border-top: 1px solid #e1e1e1; /* Grey line */
|
||||
border-bottom: 1px solid #e1e1e1; /* Grey line */
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.at-status-message {
|
||||
flex: 1;
|
||||
margin: 0 10px;
|
||||
}
|
||||
|
||||
.at-model-endpoint-row {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.at-model-option, .endpoint-option {
|
||||
flex: 1;
|
||||
margin: 0 10px;
|
||||
margin-left: 10px;
|
||||
}
|
||||
|
||||
.at-endpoint-option {
|
||||
width: 38%;
|
||||
}
|
||||
|
||||
#at-status_info {
|
||||
color: lightgreen;
|
||||
}
|
||||
@@ -0,0 +1,269 @@
|
||||
import json
|
||||
import argparse
|
||||
from pathlib import Path
|
||||
import os
|
||||
import glob
|
||||
import platform
|
||||
import site
|
||||
import subprocess
|
||||
import sys
|
||||
# version 0.8
|
||||
|
||||
def find_files_in_path_with_wildcard(pattern):
|
||||
# Get the site-packages directory of the current Python environment
|
||||
site_packages_path = site.getsitepackages()
|
||||
found_paths = []
|
||||
# Adjust the sub-directory based on the operating system
|
||||
sub_directory = "nvidia/cublas"
|
||||
if platform.system() == "Linux":
|
||||
sub_directory = os.path.join(sub_directory, "lib")
|
||||
else:
|
||||
sub_directory = os.path.join(sub_directory, "bin")
|
||||
|
||||
# Iterate over each site-packages directory (there can be more than one)
|
||||
for directory in site_packages_path:
|
||||
# Construct the search directory path
|
||||
search_directory = os.path.join(directory, sub_directory)
|
||||
# Use glob to find all files matching the pattern in this directory
|
||||
for file_path in glob.glob(os.path.join(search_directory, pattern)):
|
||||
if os.path.isfile(file_path): # Ensure it's a file
|
||||
found_paths.append(file_path)
|
||||
return found_paths
|
||||
|
||||
def detect_cublas():
|
||||
# Use different file patterns based on the operating system
|
||||
file_name_pattern = 'cublas64_11.*' if platform.system() == "Windows" else 'libcublas.so.11*'
|
||||
found_paths = find_files_in_path_with_wildcard(file_name_pattern)
|
||||
if found_paths:
|
||||
print("[AllTalk TTSDiff] \033[94mCublas:\033[0m \033[92mDetected\033[0m", found_paths)
|
||||
else:
|
||||
print("[AllTalk TTSDiff] \033[94mCublas:\033[0m \033[91mERROR Not Detected\033[0m")
|
||||
print("[AllTalk TTSDiff] \033[94mPlease install Cublas from the Nvidia CUDA Toolkit \033[0mhttps://developer.nvidia.com/cuda-downloads")
|
||||
exit(1)
|
||||
|
||||
|
||||
parser = argparse.ArgumentParser(description="Compare TTS output with the original text using detailed comparison.")
|
||||
parser.add_argument("--threshold", type=int, default=98, help="Similarity threshold for considering a match (default: 98)")
|
||||
parser.add_argument("--ttslistpath", help="Path to the ttsList.json file")
|
||||
parser.add_argument("--wavfilespath", help="Path to the wav outputs folder")
|
||||
args = parser.parse_args()
|
||||
|
||||
if not args.ttslistpath:
|
||||
parser.error("[AllTalk TTSDiff] Please specify the path to the ttslist.json file using the --ttslistpath argument.")
|
||||
if not args.wavfilespath:
|
||||
parser.error("[AllTalk TTSDiff] Please specify the path to the wavs output folder file using the --wavfilespath argument.")
|
||||
|
||||
json_file_path = Path(args.ttslistpath).resolve()
|
||||
wav_file_path = Path(args.wavfilespath).resolve()
|
||||
accuracy_threshold = args.threshold
|
||||
|
||||
def disclaimer_text():
|
||||
print(f"\nDESCRIPTION: ")
|
||||
print(f" This script is designed to assist in identifying bad TTS generation ID's from the TTS Generator.\n")
|
||||
print(f" This will download the Whisper Large-v2 model (if not already downloaded), and use it to compare your text to")
|
||||
print(f" to the generated TTS. If it finds an issue, it will flag the ID number that has the issue. This does NOT 100%")
|
||||
print(f" ensure that all TTS generated is perfect, sounds good etc, but it should flag up glaring issues and help with")
|
||||
print(f" the process of identifying bad TTS generations.")
|
||||
print(f"\nNOTES ON USE: ")
|
||||
print(f" - Ensure AllTalk is running in its own command prompt/terminal.")
|
||||
print(f" - This script should be running in its own command prompt/terminal window in the same Python Environment that")
|
||||
print(f" AllTalk is running in.")
|
||||
print(f" - After generating your text in the TTS Generator, you need to `Export List to JSON` and save the file in the")
|
||||
print(f" same folder/directory this script is running from.")
|
||||
print(f" - You can use `--threshold` with a numerical value from 1 to 100 to set accuracy detection. For example it may")
|
||||
print(f" detect/transcribe `their` as `there`, which of course, both words sound the same. If you want 100% accuracy")
|
||||
print(f" you will set 100 as the threshold. You will more than likely want a number in the high 90's. Default is set")
|
||||
print(f" at 98, which gives a little flexibility.")
|
||||
print(f" - The JSON file must be named \033[93mttsList.json\033[0m")
|
||||
print(f" - When you have your ID list, go back into the TTS Generator, correct any lines and regenerate them. If you")
|
||||
print(f" want to re-test everthing again after re-generating, you will need to export your list again and re-run this")
|
||||
print(f" script again, against your newly exported JSON list.")
|
||||
print(f" - This requires access to \033[93mcublas64_11\033[0m, the same as Finetuning.")
|
||||
print(f" https://github.com/erew123/alltalk_tts/tree/main?#-important-requirements-cuda-118\n")
|
||||
return
|
||||
|
||||
try:
|
||||
from faster_whisper import WhisperModel
|
||||
import re
|
||||
import string
|
||||
import spacy
|
||||
import torch
|
||||
from fuzzywuzzy import fuzz
|
||||
except ImportError as e:
|
||||
print(f"[AllTalk TTSDiff] ERROR STARTING SCRIPT:")
|
||||
print("[AllTalk TTSDiff] An error occurred importing one or more required libraries.")
|
||||
print("[AllTalk TTSDiff] Please ensure you have \033[94mactivated your Python environment \033[0mand \033[94minstalled all requirements.\033[0m")
|
||||
print("[AllTalk TTSDiff] \033[94mMissing library:\033[93m", str(e), "\033[0m")
|
||||
exit(1)
|
||||
|
||||
# Attempt to load the spaCy model
|
||||
try:
|
||||
# Load a medium-sized spaCy model, adjust to "en_core_web_md" or "en_core_web_lg" as needed
|
||||
nlp = spacy.load("en_core_web_md")
|
||||
except Exception as e:
|
||||
print(f"[AllTalk TTSDiff] ERROR STARTING SCRIPT:")
|
||||
print("[AllTalk TTSDiff] Failed to load the spaCy language model.")
|
||||
|
||||
# Attempt to download the spaCy model automatically
|
||||
try:
|
||||
print("[AllTalk TTSDiff] Attempting to automatically download the spaCy language model...")
|
||||
subprocess.run([sys.executable, "-m", "spacy", "download", "en_core_web_md"], check=True)
|
||||
|
||||
# Try to load the model again after downloading
|
||||
nlp = spacy.load("en_core_web_md")
|
||||
print("[AllTalk TTSDiff] Model downloaded and loaded successfully.")
|
||||
except Exception as download_exception:
|
||||
print("[AllTalk TTSDiff] Automatic download failed.")
|
||||
print("[AllTalk TTSDiff] Please manually install the spaCy language model by running:")
|
||||
print("[AllTalk TTSDiff] \033[93mpython -m spacy download en_core_web_md\033[0m")
|
||||
exit(1)
|
||||
|
||||
def texts_are_similar(text1, text2, threshold=0.8):
|
||||
# Preliminary check for short texts
|
||||
if len(text1) < 10 or len(text2) < 10:
|
||||
ratio = fuzz.ratio(text1, text2) / 100.0 # Use FuzzyWuzzy for short texts
|
||||
return ratio > threshold
|
||||
|
||||
# Process the texts through the NLP model for longer texts
|
||||
doc1 = nlp(text1)
|
||||
doc2 = nlp(text2)
|
||||
|
||||
# Compute semantic similarity
|
||||
similarity = doc1.similarity(doc2)
|
||||
|
||||
return similarity >= threshold
|
||||
|
||||
def normalize_text(text):
|
||||
# Normalize or remove CRLF and other non-standard whitespaces
|
||||
text = text.replace("\r\n", " ").replace("\n", " ").replace("\r", " ")
|
||||
|
||||
# Convert to lowercase
|
||||
text = text.lower()
|
||||
|
||||
# Standardize and then remove quotation marks
|
||||
text = text.replace("“", '"').replace("”", '"').replace("‘", "'").replace("’", "'")
|
||||
text = text.translate(str.maketrans('', '', '"\''))
|
||||
|
||||
# Remove all other punctuation except hyphens to preserve compound words
|
||||
text = text.translate(str.maketrans('', '', string.punctuation.replace("-", "")))
|
||||
|
||||
# Collapse any sequence of whitespace (including spaces) into a single space
|
||||
text = re.sub(r'\s+', ' ', text)
|
||||
|
||||
return text.strip()
|
||||
|
||||
spoken_punctuation_mapping = {
|
||||
"dot": ".",
|
||||
"comma": ",",
|
||||
# Add more mappings as needed
|
||||
}
|
||||
|
||||
def contains_spoken_punctuation(transcribed_text):
|
||||
for spoken, punctuation in spoken_punctuation_mapping.items():
|
||||
if spoken in transcribed_text:
|
||||
return True
|
||||
return False
|
||||
|
||||
def detailed_comparison(original_text, transcribed_text, threshold=98):
|
||||
original_clean = normalize_text(original_text)
|
||||
transcribed_clean = normalize_text(transcribed_text)
|
||||
ratio = fuzz.ratio(original_clean, transcribed_clean)
|
||||
return ratio > threshold
|
||||
|
||||
def access_local_file(url):
|
||||
# Extract the file name from the URL
|
||||
file_name = url.split('/')[-1]
|
||||
# Construct the full path to the file on disk
|
||||
file_path = wav_file_path / file_name
|
||||
# Check if the file exists
|
||||
if not file_path.exists():
|
||||
print(f"[AllTalk TTSDiff] File does not exist: {file_path}")
|
||||
return None
|
||||
return file_path
|
||||
|
||||
def transcribe_and_compare(file_url, original_text, model, item_id, flagged_ids):
|
||||
audio_file_path = access_local_file(file_url)
|
||||
if audio_file_path is None:
|
||||
print(f"[AllTalk TTSDiff] Could not access local file for URL: {file_url}")
|
||||
return # Skip this file if it can't be accessed
|
||||
|
||||
try:
|
||||
segments, info = model.transcribe(str(audio_file_path), beam_size=5)
|
||||
transcribed_text = " ".join([segment.text for segment in segments])
|
||||
except Exception as e:
|
||||
print(f"[AllTalk TTSDiff] Error transcribing file {audio_file_path}: {e}")
|
||||
return # Skip this file if transcription fails
|
||||
|
||||
segments, info = model.transcribe(str(audio_file_path), beam_size=5)
|
||||
transcribed_text = " ".join([segment.text for segment in segments])
|
||||
|
||||
# Normalize texts for comparison
|
||||
original_text_normalized = normalize_text(original_text)
|
||||
transcribed_text_normalized = normalize_text(transcribed_text)
|
||||
|
||||
# Enhanced comparison using detailed fuzzy matching
|
||||
is_detailed_match = detailed_comparison(original_text_normalized, transcribed_text_normalized, args.threshold)
|
||||
has_spoken_punctuation = contains_spoken_punctuation(transcribed_text_normalized) and not contains_spoken_punctuation(original_text_normalized)
|
||||
|
||||
# Adjust is_match based on detailed comparison and spoken punctuation check
|
||||
is_match = is_detailed_match and not has_spoken_punctuation
|
||||
|
||||
# Only log and flag IDs for review if there's a mismatch or detected issues
|
||||
if not is_match:
|
||||
#print(f"\033[93mMismatch Detected - ID:\033[0m {item_id}")
|
||||
print(f"[AllTalk TTSDiff] \033[93mText:\033[0m {original_text}")
|
||||
print(f"[AllTalk TTSDiff] \033[91mTTS :\033[0m{transcribed_text}")
|
||||
if has_spoken_punctuation:
|
||||
print(f"[AllTalk TTSDiff] Note: Potential incorrect spoken punctuation detected.")
|
||||
flagged_ids.append(item_id) # Track the ID for review
|
||||
|
||||
def main():
|
||||
print(f"[AllTalk TTSDiff]")
|
||||
print(f"[AllTalk TTSDiff]")
|
||||
print("[AllTalk TTSDiff] \033[92mStarting Compare of Text vs TTS:\033[0m")
|
||||
print("[AllTalk TTSDiff]")
|
||||
print("[AllTalk TTSDiff] \033[94mJSON file :\033[92m", json_file_path, "\033[0m")
|
||||
print("[AllTalk TTSDiff] \033[94mWAV files :\033[92m", wav_file_path, "\033[0m")
|
||||
print("[AllTalk TTSDiff] \033[94mAccuracy :\033[92m", accuracy_threshold, "%", "\033[0m")
|
||||
detect_cublas()
|
||||
model_size = "large-v2"
|
||||
device = "cuda" if torch.cuda.is_available() else "cpu"
|
||||
model = WhisperModel(model_size, device=device, compute_type="float32")
|
||||
|
||||
flagged_ids = [] # Initialize the list to track IDs needing review
|
||||
|
||||
try:
|
||||
with open(json_file_path, "r", encoding="utf-8") as f:
|
||||
tts_list = json.load(f)
|
||||
except FileNotFoundError:
|
||||
print(f"[AllTalk TTSDiff] ERROR STARTING SCRIPT:")
|
||||
print("[AllTalk TTSDiff] Error: \033[93mttsList.json\033[0m file not found.")
|
||||
print("[AllTalk TTSDiff] Please ensure you follow these steps:")
|
||||
print("[AllTalk TTSDiff] After generating your text in the TTS Generator, you need to `Export List to JSON` and save the file in the")
|
||||
print("[AllTalk TTSDiff] same folder/directory this script is running from.")
|
||||
print("[AllTalk TTSDiff] The JSON file must be named \033[93mttsList.json\033[0m")
|
||||
exit(1)
|
||||
|
||||
print(f"[AllTalk TTSDiff]")
|
||||
|
||||
for item in tts_list:
|
||||
print(f"[AllTalk TTSDiff] Processing ID: {item['id']}")
|
||||
transcribe_and_compare(item['fileUrl'], item['text'], model, item['id'], flagged_ids)
|
||||
|
||||
print(f"[AllTalk TTSDiff]")
|
||||
# Print summary information at the end
|
||||
if flagged_ids:
|
||||
print(f"[AllTalk TTSDiff] \033[94mIDs needing review:\033[0m", ', '.join(map(str, flagged_ids)))
|
||||
print(f"[AllTalk TTSDiff]")
|
||||
print(f"[AllTalk TTSDiff] Review ID's and correct any lines by editing & regenerating as necessary.")
|
||||
else:
|
||||
print("[AllTalk TTSDiff] \033[94mIDs needing review:\033[0m No issues detected.")
|
||||
summary_data = {
|
||||
"flagged_ids": flagged_ids
|
||||
}
|
||||
with open(wav_file_path / "analysis_summary.json", "w") as summary_file:
|
||||
json.dump(summary_data, summary_file)
|
||||
print("[AllTalk TTSDiff]")
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -1,2 +1,2 @@
|
||||
var e={d:(t,n)=>{for(var r in n)e.o(n,r)&&!e.o(t,r)&&Object.defineProperty(t,r,{enumerable:!0,get:n[r]})},o:(e,t)=>Object.prototype.hasOwnProperty.call(e,t)},t={};function n(e){return n="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e},n(e)}function r(e){return function(e){if(Array.isArray(e))return a(e)}(e)||function(e){if("undefined"!=typeof Symbol&&null!=e[Symbol.iterator]||null!=e["@@iterator"])return Array.from(e)}(e)||function(e,t){if(!e)return;if("string"==typeof e)return a(e,t);var n=Object.prototype.toString.call(e).slice(8,-1);"Object"===n&&e.constructor&&(n=e.constructor.name);if("Map"===n||"Set"===n)return Array.from(e);if("Arguments"===n||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n))return a(e,t)}(e)||function(){throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}()}function a(e,t){(null==t||t>e.length)&&(t=e.length);for(var n=0,r=new Array(t);n<t;n++)r[n]=e[n];return r}function o(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}function i(e,t){for(var r=0;r<t.length;r++){var a=t[r];a.enumerable=a.enumerable||!1,a.configurable=!0,"value"in a&&(a.writable=!0),Object.defineProperty(e,(o=a.key,i=void 0,i=function(e,t){if("object"!==n(e)||null===e)return e;var r=e[Symbol.toPrimitive];if(void 0!==r){var a=r.call(e,t||"default");if("object"!==n(a))return a;throw new TypeError("@@toPrimitive must return a primitive value.")}return("string"===t?String:Number)(e)}(o,"string"),"symbol"===n(i)?i:String(i)),a)}var o,i}e.d(t,{Z:()=>u});var u=function(){function e(){var t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{},n=t.sampleRate;o(this,e),this._context=this._createContext(n),n||(n=this._context.sampleRate),this._sampleRate=n}var t,n,a;return t=e,n=[{key:"_createContext",value:function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:44100;return window.AudioContext=window.AudioContext||window.webkitAudioContext||window.mozAudioContext,new AudioContext({sampleRate:e})}},{key:"context",get:function(){return this._context}},{key:"fetchAudio",value:async function(){for(var e=this,t=arguments.length,n=new Array(t),r=0;r<t;r++)n[r]=arguments[r];return await Promise.all(n.map((async function(t){var n;return n=t instanceof File||t instanceof Blob?await t.arrayBuffer():await fetch(t).then((function(e){return e.headers.has("Content-Type")&&!e.headers.get("Content-Type").includes("audio/")&&console.warn("Crunker: Attempted to fetch an audio file, but its MIME type is `".concat(e.headers.get("Content-Type").split(";")[0],"`. We'll try and continue anyway. (file: \"").concat(t,'")')),e.arrayBuffer()})),await e._context.decodeAudioData(n)})))}},{key:"mergeAudio",value:function(e){var t=this._context.createBuffer(this._maxNumberOfChannels(e),this._sampleRate*this._maxDuration(e),this._sampleRate);return e.forEach((function(e){for(var n=0;n<e.numberOfChannels;n++){for(var r=t.getChannelData(n),a=e.getChannelData(n),o=e.getChannelData(n).length-1;o>=0;o--)r[o]+=a[o];t.getChannelData(n).set(r)}})),t}},{key:"concatAudio",value:function(e){var t=this._context.createBuffer(this._maxNumberOfChannels(e),this._totalLength(e),this._sampleRate),n=0;return e.forEach((function(e){for(var r=0;r<e.numberOfChannels;r++)t.getChannelData(r).set(e.getChannelData(r),n);n+=e.length})),t}},{key:"padAudio",value:function(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:0,n=arguments.length>2&&void 0!==arguments[2]?arguments[2]:0;if(0===n)return e;if(t<0)throw new Error('Crunker: Parameter "padStart" in padAudio must be positive');if(n<0)throw new Error('Crunker: Parameter "seconds" in padAudio must be positive');for(var r=this._context.createBuffer(e.numberOfChannels,Math.ceil(e.length+n*e.sampleRate),e.sampleRate),a=0;a<e.numberOfChannels;a++){var o=e.getChannelData(a);r.getChannelData(a).set(o.subarray(0,Math.ceil(t*e.sampleRate)+1),0),r.getChannelData(a).set(o.subarray(Math.ceil(t*e.sampleRate)+2,r.length+1),Math.ceil((t+n)*e.sampleRate))}return r}},{key:"sliceAudio",value:function(e,t,n){var r=arguments.length>3&&void 0!==arguments[3]?arguments[3]:0,a=arguments.length>4&&void 0!==arguments[4]?arguments[4]:0;if(t>=n)throw new Error('Crunker: "start" time should be less than "end" time in sliceAudio method');for(var o=Math.round((n-t)*this._sampleRate),i=Math.round(t*this._sampleRate),u=this._context.createBuffer(e.numberOfChannels,o,this._sampleRate),l=0;l<e.numberOfChannels;l++)for(var s=e.getChannelData(l),c=u.getChannelData(l),f=0;f<o;f++)c[f]=s[i+f],f<r*this._sampleRate&&(c[f]*=f/(r*this._sampleRate)),f>o-a*this._sampleRate&&(c[f]*=(o-f)/(a*this._sampleRate));return u}},{key:"play",value:function(e){var t=this._context.createBufferSource();return t.buffer=e,t.connect(this._context.destination),t.start(),Line truncated
|
||||
var e={d:(t,n)=>{for(var r in n)e.o(n,r)&&!e.o(t,r)&&Object.defineProperty(t,r,{enumerable:!0,get:n[r]})},o:(e,t)=>Object.prototype.hasOwnProperty.call(e,t)},t={};function n(e){return n="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e},n(e)}function r(e){return function(e){if(Array.isArray(e))return a(e)}(e)||function(e){if("undefined"!=typeof Symbol&&null!=e[Symbol.iterator]||null!=e["@@iterator"])return Array.from(e)}(e)||function(e,t){if(!e)return;if("string"==typeof e)return a(e,t);var n=Object.prototype.toString.call(e).slice(8,-1);"Object"===n&&e.constructor&&(n=e.constructor.name);if("Map"===n||"Set"===n)return Array.from(e);if("Arguments"===n||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n))return a(e,t)}(e)||function(){throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}()}function a(e,t){(null==t||t>e.length)&&(t=e.length);for(var n=0,r=new Array(t);n<t;n++)r[n]=e[n];return r}function o(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}function i(e,t){for(var r=0;r<t.length;r++){var a=t[r];a.enumerable=a.enumerable||!1,a.configurable=!0,"value"in a&&(a.writable=!0),Object.defineProperty(e,(o=a.key,i=void 0,i=function(e,t){if("object"!==n(e)||null===e)return e;var r=e[Symbol.toPrimitive];if(void 0!==r){var a=r.call(e,t||"default");if("object"!==n(a))return a;throw new TypeError("@@toPrimitive must return a primitive value.")}return("string"===t?String:Number)(e)}(o,"string"),"symbol"===n(i)?i:String(i)),a)}var o,i}e.d(t,{Z:()=>u});var u=function(){function e(){var t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{},n=t.sampleRate;o(this,e),this._context=this._createContext(n),n||(n=this._context.sampleRate),this._sampleRate=n}var t,n,a;return t=e,n=[{key:"_createContext",value:function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:44100;return window.AudioContext=window.AudioContext||window.webkitAudioContext||window.mozAudioContext,new AudioContext({sampleRate:e})}},{key:"context",get:function(){return this._context}},{key:"fetchAudio",value:async function(){for(var e=this,t=arguments.length,n=new Array(t),r=0;r<t;r++)n[r]=arguments[r];return await Promise.all(n.map((async function(t){var n;return n=t instanceof File||t instanceof Blob?await t.arrayBuffer():await fetch(t).then((function(e){return e.headers.has("Content-Type")&&!e.headers.get("Content-Type").includes("audio/")&&console.warn("Crunker: Attempted to fetch an audio file, but its MIME type is `".concat(e.headers.get("Content-Type").split(";")[0],"`. We'll try and continue anyway. (file: \"").concat(t,'")')),e.arrayBuffer()})),await e._context.decodeAudioData(n)})))}},{key:"mergeAudio",value:function(e){var t=this._context.createBuffer(this._maxNumberOfChannels(e),this._sampleRate*this._maxDuration(e),this._sampleRate);return e.forEach((function(e){for(var n=0;n<e.numberOfChannels;n++){for(var r=t.getChannelData(n),a=e.getChannelData(n),o=e.getChannelData(n).length-1;o>=0;o--)r[o]+=a[o];t.getChannelData(n).set(r)}})),t}},{key:"concatAudio",value:function(e){var t=this._context.createBuffer(this._maxNumberOfChannels(e),this._totalLength(e),this._sampleRate),n=0;return e.forEach((function(e){for(var r=0;r<e.numberOfChannels;r++)t.getChannelData(r).set(e.getChannelData(r),n);n+=e.length})),t}},{key:"padAudio",value:function(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:0,n=arguments.length>2&&void 0!==arguments[2]?arguments[2]:0;if(0===n)return e;if(t<0)throw new Error('Crunker: Parameter "padStart" in padAudio must be positive');if(n<0)throw new Error('Crunker: Parameter "seconds" in padAudio must be positive');for(var r=this._context.createBuffer(e.numberOfChannels,Math.ceil(e.length+n*e.sampleRate),e.sampleRate),a=0;a<e.numberOfChannels;a++){var o=e.getChannelData(a);r.getChannelData(a).set(o.subarray(0,Math.ceil(t*e.sampleRate)+1),0),r.getChannelData(a).set(o.subarray(Math.ceil(t*e.sampleRate)+2,r.length+1),Math.ceil((t+n)*e.sampleRate))}return r}},{key:"sliceAudio",value:function(e,t,n){var r=arguments.length>3&&void 0!==arguments[3]?arguments[3]:0,a=arguments.length>4&&void 0!==arguments[4]?arguments[4]:0;if(t>=n)throw new Error('Crunker: "start" time should be less than "end" time in sliceAudio method');for(var o=Math.round((n-t)*this._sampleRate),i=Math.round(t*this._sampleRate),u=this._context.createBuffer(e.numberOfChannels,o,this._sampleRate),l=0;l<e.numberOfChannels;l++)for(var s=e.getChannelData(l),c=u.getChannelData(l),f=0;f<o;f++)c[f]=s[i+f],f<r*this._sampleRate&&(c[f]*=f/(r*this._sampleRate)),f>o-a*this._sampleRate&&(c[f]*=(o-f)/(a*this._sampleRate));return u}},{key:"play",value:function(e){var t=this._context.createBufferSource();return t.buffer=e,t.connect(this._context.destination),t.start(),Line truncated
|
||||
//# sourceMappingURL=crunker.esm.js.map
|
||||
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"crunker.esm.js","mappings":"AACA,IAAIA,EAAsB,CCA1BA,EAAwB,CAACC,EAASC,KACjC,IAAI,IAAIC,KAAOD,EACXF,EAAoBI,EAAEF,EAAYC,KAASH,EAAoBI,EAAEH,EAASE,IAC5EE,OAAOC,eAAeL,EAASE,EAAK,CAAEI,YAAY,EAAMC,IAAKN,EAAWC,IAE1E,ECNDH,EAAwB,CAACS,EAAKC,IAAUL,OAAOM,UAAUC,eAAeC,KAAKJ,EAAKC,I,8jDCoBlF,IAGqBI,EAAO,WAU1B,aAAqE,6DAAJ,CAAC,EAApDC,EAAU,EAAVA,WAAU,UACtBC,KAAKC,SAAWD,KAAKE,eAAeH,GAEpCA,IAAAA,EAAeC,KAAKC,SAASF,YAE7BC,KAAKG,YAAcJ,CACrB,C,UAoZC,O,EAlZD,E,EAAA,6BAKA,WAAkE,IAA3CA,EAAqB,UAAH,6CAAG,MAE1C,OADAK,OAAOC,aAAeD,OAAOC,cAAiBD,OAAeE,oBAAuBF,OAAeG,gBAC5F,IAAIF,aAAa,CAAEN,WAAAA,GAC5B,GAEA,mBAIA,WACE,OAAOC,KAAKC,QACd,GAEA,wBAGAO,iBAA4E,kCAAxDC,EAAS,yBAATA,EAAS,gBAC3B,aAAaC,QAAQC,IACnBF,EAAUG,KAAIJ,eAAOK,GACnB,IAAIC,EAkBJ,OAfEA,EADED,aAAoBE,MAAQF,aAAoBG,WACnCH,EAASI,oBAETC,MAAML,GAAUM,MAAK,SAACC,GASnC,OARIA,EAASC,QAAQC,IAAI,kBAAoBF,EAASC,QAAQ7B,IAAI,gBAAiB+B,SAAS,WAC1FC,QAAQC,KAAK,oEAAD,OAERL,EAASC,QAAQ7B,IAAI,gBAAiBkC,MAAM,KAAK,GAAE,sDACPb,EAAQ,OAInDO,EAASH,aAClB,UAGW,EAAKhB,SAAS0B,gBAAgBb,EAC7C,IAEJ,GAEA,wBAOA,SAAWc,GACT,IAAMC,EAAS7B,KAAKC,SAAS6B,aAC3B9B,KAAK+B,qBAAqBH,GAC1B5B,KAAKG,YAAcH,KAAKgC,aAAaJ,GACrC5B,KAAKG,aAgBP,OAbAyB,EAAQK,SAAQ,SAACnB,GACf,IAAK,IAAIoB,EAAgB,EAAGA,EAAgBpB,EAAOqB,iBAAkBD,IAAiB,CAIpF,IAHA,IAAME,EAAaP,EAAOQ,eAAeH,GACnCI,EAAaxB,EAAOuB,eAAeH,GAEhCK,EAAIzB,EAAOuB,eAAeH,GAAeM,OAAS,EAAGD,GAAK,EAAGA,IACpEH,EAAWG,IAAMD,EAAWC,GAG9BV,EAAOQ,eAAeH,GAAeO,IAAIL,EAC3C,CACF,IAEOP,CACT,GAEA,yBAOA,SAAYD,GACV,IAAMC,EAAS7B,KAAKC,SAAS6B,aAC3B9B,KAAK+B,qBAAqBH,GAC1B5B,KAAK0C,aAAad,GAClB5B,KAAKG,aAEHwC,EAAS,EAUb,OARAf,EAAQK,SAAQ,SAACnB,GACf,IAAK,IAAIoB,EAAgB,EAAGA,EAAgBpB,EAAOqB,iBAAkBD,IACnEL,EAAOQ,eAAeH,GAAeO,IAAI3B,EAAOuB,eAAeH,GAAgBS,GAGjFA,GAAU7B,EAAO0B,MACnB,IAEOX,CACT,GAEA,sBAUA,SAASf,GAA6E,IAAxD8B,EAAmB,UAAH,6CAAG,EAAGC,EAAkB,UAAH,6CAAG,EACpE,GAAgB,IAAZA,EAAe,OAAO/B,EAE1B,GAAI8B,EAAW,EAAG,MAAM,IAAIE,MAAM,8DAClC,GAAID,EAAU,EAAG,MAAM,IAAIC,MAAM,6DAQjC,IANA,IAAMC,EAAgB/C,KAAKC,SAAS6B,aAClChB,EAAOqB,iBACPa,KAAKC,KAAKnC,EAAO0B,OAASK,EAAU/B,EAAOf,YAC3Ce,EAAOf,YAGAmC,EAAgB,EAAGA,EAAgBpB,EAAOqB,iBAAkBD,IAAiB,CACpF,IAAMgB,EAAcpC,EAAOuB,eAAeH,GAC1Ca,EACGV,eAAeH,GACfO,IAAIS,EAAYC,SAAS,EAAGH,KAAKC,KAAKL,EAAW9B,EAAOf,YAAc,GAAI,GAE7EgD,EACGV,eAAeH,GACfO,IACCS,EAAYC,SAASH,KAAKC,KAAKL,EAAW9B,EAAOf,YAAc,EAAGgD,EAAcP,OAAS,GACzFQ,KAAKC,MAAML,EAAWC,GAAW/B,EAAOf,YAE9C,CAEA,OAAOgD,CACT,GAEA,wBASA,SAAWjC,EAAqBsC,EAAeC,GAAmE,IAAtDC,EAAiB,UAAH,6CAAG,EAAGC,EAAkB,UAAH,6CAAG,EAChG,GAAIH,GAASC,EAAK,MAAM,IAAIP,MAAM,6EAMlC,IAJA,IAAMN,EAASQ,KAAKQ,OAAOH,EAAMD,GAASpD,KAAKG,aACzCwC,EAASK,KAAKQ,MAAMJ,EAAQpD,KAAKG,aACjCsD,EAAYzD,KAAKC,SAAS6B,aAAahB,EAAOqB,iBAAkBK,EAAQxC,KAAKG,aAE1EuD,EAAU,EAAGA,EAAU5C,EAAOqB,iBAAkBuB,IAIvD,IAHA,IAAMC,EAAY7C,EAAOuB,eAAeqB,GAClCtB,EAAaqB,EAAUpB,eAAeqB,GAEnCnB,EAAI,EAAGA,EAAIC,EAAQD,IAC1BH,EAAWG,GAAKoB,EAAUhB,EAASJ,GAG/BA,EAAIe,EAAStD,KAAKG,cACpBiC,EAAWG,IAAMA,GAAKe,EAAStD,KAAKG,cAIlCoC,EAAIC,EAASe,EAAUvD,KAAKG,cAC9BiC,EAAWG,KAAOC,EAASD,IAAMgB,EAAUvD,KAAKG,cAKtD,OAAOsD,CACT,GAEA,kBAGA,SAAK3C,GACH,IAAM8C,EAAS5D,KAAKC,SAAS4D,qBAM7B,OAJAD,EAAO9C,OAASA,EAChB8C,EAAOE,QAAQ9D,KAAKC,SAAS8D,aAC7BH,EAAOR,QAEAQ,CACT,GAEA,oBAUA,SAAO9C,GAAuE,IAAlDkD,EAAe,UAAH,6CAAG,YACnCC,EAAWjE,KAAKkE,YAAYpD,GAC5BqD,EAAWnE,KAAKoE,cAAcH,EAAUnD,EAAOqB,iBAAkBrB,EAAOf,YACxEsE,EAAY,IAAIrD,KAAK,CAACmD,GAAW,CAAEH,KAAAA,IAEzC,MAAO,CACLM,KAAMD,EACNE,IAAKvE,KAAKwE,WAAWH,GACrBI,QAASzE,KAAK0E,oBAAoBL,GAEtC,GAEA,sBAMA,SAASC,GAA6D,IAAjDK,EAAmB,UAAH,6CAAG,UAChCC,EAAIC,SAASC,cAAc,KAOjC,OALAF,EAAEG,MAAMC,QAAU,OAClBJ,EAAEK,KAAOjF,KAAKwE,WAAWF,GACzBM,EAAEM,SAAW,GAAH,OAAMP,EAAQ,YAAIL,EAAKN,KAAKtC,MAAM,KAAK,IACjDkD,EAAEO,QAEKP,CACT,GAEA,0BAOA,SAAgBQ,GACd,OAAOpF,KAAKqF,oBAAiBC,EAAYF,GAC3C,GAEA,mBAGA,WAEE,OADApF,KAAKC,SAASsF,QACPvF,IACT,GAEA,0BAKA,SAAqB4B,GACnB,OAAOoB,KAAKwC,IAAG,MAARxC,KAAI,EAAQpB,EAAQhB,KAAI,SAACE,GAAM,OAAKA,EAAO2E,QAAQ,KAC5D,GAEA,kCAKA,SAA6B7D,GAC3B,OAAOoB,KAAKwC,IAAG,MAARxC,KAAI,EAAQpB,EAAQhB,KAAI,SAACE,GAAM,OAAKA,EAAOqB,gBAAgB,KACpE,GAEA,0BAKA,SAAqBP,GACnB,OAAOA,EAAQhB,KAAI,SAACE,GAAM,OAAKA,EAAO0B,MAAM,IAAEkD,QAAO,SAACd,EAAGe,GAAC,OAAKf,EAAIe,CAAC,GAAE,EACxE,GAEA,0BAKA,WACE,MAAO,iBAAkBvF,QAAU,uBAAwBA,QAAU,oBAAqBA,MAC5F,GAEA,2BAOA,SAAsBU,EAAsB8E,EAAuB7F,GACjE,IAEM8F,EADiBC,EACJF,EAIbG,EALiBD,EAKDhF,EAAO0B,OACvBwD,EAFkB,GAEiBD,EAEnC9E,EAAc,IAAIgF,YALD,EAK8BD,GAC/CE,EAAO,IAAIC,SAASlF,GAgB1B,OAdAjB,KAAKoG,aAAaF,EAAM,EAAG,QAC3BA,EAAKG,UAAU,EAAGL,GAAgB,GAClChG,KAAKoG,aAAaF,EAAM,EAAG,QAC3BlG,KAAKoG,aAAaF,EAAM,GAAI,QAC5BA,EAAKG,UAAU,GAAI,IAAI,GACvBH,EAAKI,UAAU,GAAI,GAAG,GACtBJ,EAAKI,UAAU,GAAIV,GAAe,GAClCM,EAAKG,UAAU,GAAItG,GAAY,GAC/BmG,EAAKG,UAAU,GAAItG,EAAa8F,GAAY,GAC5CK,EAAKI,UAAU,GAAIT,GAAY,GAC/BK,EAAKI,UAAU,GAtBE,IAsBY,GAC7BtG,KAAKoG,aAAaF,EAAM,GAAI,QAC5BA,EAAKG,UAAU,GAAIN,GAAe,GAE3B/F,KAAKuG,iBAAiBL,EAAMpF,EAAQ0F,GAC7C,GAEA,8BAKA,SAAyBrC,EAAoBrD,EAAsB6B,GACjE,IAAK,IAAIJ,EAAI,EAAGA,EAAIzB,EAAO0B,OAAQD,IAAKI,GAAU,EAAG,CACnD,IAAM8D,EAAMzD,KAAKwC,KAAK,EAAGxC,KAAK0D,IAAI,EAAG5F,EAAOyB,KAC5C4B,EAASwC,SAAShE,EAAQ8D,EAAM,EAAU,MAANA,EAAqB,MAANA,GAAc,EACnE,CAEA,OAAOtC,CACT,GAEA,0BAKA,SAAqBA,EAAoBxB,EAAgBiE,GACvD,IAAK,IAAIrE,EAAI,EAAGA,EAALine truncated
|
||||
|
After Width: | Height: | Size: 1.1 KiB |
@@ -0,0 +1,77 @@
|
||||
import json
|
||||
import argparse
|
||||
import soundfile as sf
|
||||
from pathlib import Path
|
||||
|
||||
parser = argparse.ArgumentParser(description="Compare TTS output with the original text using detailed comparison.")
|
||||
parser.add_argument("--ttslistpath", help="Path to the ttsList.json file")
|
||||
parser.add_argument("--wavfilespath", help="Path to the wav outputs folder")
|
||||
args = parser.parse_args()
|
||||
|
||||
if not args.ttslistpath:
|
||||
parser.error("[AllTalk TTSSRT] Please specify the path to the ttslist.json file using the --ttslistpath argument.")
|
||||
if not args.wavfilespath:
|
||||
parser.error("[AllTalk TTSSRT] Please specify the path to the wavs output folder file using the --wavfilespath argument.")
|
||||
|
||||
json_file_path = Path(args.ttslistpath).resolve()
|
||||
audio_files_base_path = Path(args.wavfilespath).resolve()
|
||||
|
||||
print(f"[AllTalk TTSSRT]")
|
||||
print("[AllTalk TTSSRT] \033[92mStarting SRT Subtitle generation:\033[0m")
|
||||
print("[AllTalk TTSSRT]")
|
||||
print("[AllTalk TTSSRT] \033[94mJSON file :\033[92m", json_file_path, "\033[0m")
|
||||
print("[AllTalk TTSSRT] \033[94mWAV files :\033[92m", audio_files_base_path, "\033[0m")
|
||||
print("[AllTalk TTSSRT]")
|
||||
|
||||
# Function to convert time in seconds to SRT time format
|
||||
def format_srt_time(seconds):
|
||||
hours = int(seconds // 3600)
|
||||
minutes = int((seconds % 3600) // 60)
|
||||
seconds = seconds % 60
|
||||
milliseconds = int((seconds - int(seconds)) * 1000)
|
||||
return f"{hours:02}:{minutes:02}:{int(seconds):02},{milliseconds:03}"
|
||||
|
||||
# Load the JSON data
|
||||
with open(json_file_path, 'r') as json_file:
|
||||
data = json.load(json_file)
|
||||
|
||||
# Initialize start time and SRT lines
|
||||
start_time = 0.0
|
||||
srt_lines = []
|
||||
|
||||
# Loop through the JSON data to build SRT entries
|
||||
for entry in data:
|
||||
file_name = Path(entry['fileUrl']).name # Get the file name from the URL
|
||||
file_path = audio_files_base_path / file_name
|
||||
print(f"[AllTalk TTSSRT] Processing file: {file_name}")
|
||||
|
||||
if not file_path.exists():
|
||||
print(f"[AllTalk TTSSRT] Audio file does not exist: {file_path}")
|
||||
continue
|
||||
|
||||
try:
|
||||
# Use soundfile to read the duration of the WAV file
|
||||
with sf.SoundFile(str(file_path), 'r') as f:
|
||||
duration = len(f) / f.samplerate
|
||||
end_time = start_time + duration
|
||||
|
||||
# Format the SRT entry
|
||||
start_timestamp = format_srt_time(start_time)
|
||||
end_timestamp = format_srt_time(end_time)
|
||||
srt_lines.append(f"{len(srt_lines)+1}\n{start_timestamp} --> {end_timestamp}\n{entry['text']}\n")
|
||||
|
||||
# Update the start time for the next entry
|
||||
start_time = end_time
|
||||
except RuntimeError as e:
|
||||
print(f"[AllTalk TTSSRT] Error reading {file_path}: {e}")
|
||||
|
||||
# Join all SRT lines into the final SRT content
|
||||
srt_content = "\n".join(srt_lines)
|
||||
|
||||
# Save the SRT content to a file
|
||||
srt_file_path = audio_files_base_path / "subtitles.srt"
|
||||
with open(srt_file_path, "w") as srt_file:
|
||||
srt_file.write(srt_content)
|
||||
|
||||
print("[AllTalk TTSSRT]")
|
||||
print(f"[AllTalk TTSSRT] \033[94mSRT file created at \033[0m{srt_file_path}")
|
||||
|
Before Width: | Height: | Size: 30 KiB After Width: | Height: | Size: 30 KiB |
|
Before Width: | Height: | Size: 839 B After Width: | Height: | Size: 839 B |
|
Before Width: | Height: | Size: 2.6 KiB After Width: | Height: | Size: 2.6 KiB |
|
Before Width: | Height: | Size: 8.8 KiB After Width: | Height: | Size: 8.8 KiB |
|
Before Width: | Height: | Size: 13 KiB After Width: | Height: | Size: 13 KiB |
|
Before Width: | Height: | Size: 99 KiB After Width: | Height: | Size: 99 KiB |
|
Before Width: | Height: | Size: 476 KiB |
|
Before Width: | Height: | Size: 547 KiB |
|
Before Width: | Height: | Size: 156 KiB |
@@ -1 +0,0 @@
|
||||
{"version":3,"file":"crunker.esm.js","mappings":"AACA,IAAIA,EAAsB,CCA1BA,EAAwB,CAACC,EAASC,KACjC,IAAI,IAAIC,KAAOD,EACXF,EAAoBI,EAAEF,EAAYC,KAASH,EAAoBI,EAAEH,EAASE,IAC5EE,OAAOC,eAAeL,EAASE,EAAK,CAAEI,YAAY,EAAMC,IAAKN,EAAWC,IAE1E,ECNDH,EAAwB,CAACS,EAAKC,IAAUL,OAAOM,UAAUC,eAAeC,KAAKJ,EAAKC,I,8jDCoBlF,IAGqBI,EAAO,WAU1B,aAAqE,6DAAJ,CAAC,EAApDC,EAAU,EAAVA,WAAU,UACtBC,KAAKC,SAAWD,KAAKE,eAAeH,GAEpCA,IAAAA,EAAeC,KAAKC,SAASF,YAE7BC,KAAKG,YAAcJ,CACrB,C,UA6YC,O,EA3YD,E,EAAA,6BAKA,WAAkE,IAA3CA,EAAqB,UAAH,6CAAG,MAE1C,OADAK,OAAOC,aAAeD,OAAOC,cAAiBD,OAAeE,oBAAuBF,OAAeG,gBAC5F,IAAIF,aAAa,CAAEN,WAAAA,GAC5B,GAEA,mBAIA,WACE,OAAOC,KAAKC,QACd,GAEA,wBAGAO,iBAA4E,kCAAxDC,EAAS,yBAATA,EAAS,gBAC3B,aAAaC,QAAQC,IACnBF,EAAUG,KAAIJ,eAAOK,GACnB,IAAIC,EAkBJ,OAfEA,EADED,aAAoBE,MAAQF,aAAoBG,WACnCH,EAASI,oBAETC,MAAML,GAAUM,MAAK,SAACC,GASnC,OARIA,EAASC,QAAQC,IAAI,kBAAoBF,EAASC,QAAQ7B,IAAI,gBAAiB+B,SAAS,WAC1FC,QAAQC,KAAK,oEAAD,OAERL,EAASC,QAAQ7B,IAAI,gBAAiBkC,MAAM,KAAK,GAAE,sDACPb,EAAQ,OAInDO,EAASH,aAClB,UAGW,EAAKhB,SAAS0B,gBAAgBb,EAC7C,IAEJ,GAEA,wBAOA,SAAWc,GACT,IAAMC,EAAS7B,KAAKC,SAAS6B,aAC3B9B,KAAK+B,qBAAqBH,GAC1B5B,KAAKG,YAAcH,KAAKgC,aAAaJ,GACrC5B,KAAKG,aAgBP,OAbAyB,EAAQK,SAAQ,SAACnB,GACf,IAAK,IAAIoB,EAAgB,EAAGA,EAAgBpB,EAAOqB,iBAAkBD,IAAiB,CAIpF,IAHA,IAAME,EAAaP,EAAOQ,eAAeH,GACnCI,EAAaxB,EAAOuB,eAAeH,GAEhCK,EAAIzB,EAAOuB,eAAeH,GAAeM,OAAS,EAAGD,GAAK,EAAGA,IACpEH,EAAWG,IAAMD,EAAWC,GAG9BV,EAAOQ,eAAeH,GAAeO,IAAIL,EAC3C,CACF,IAEOP,CACT,GAEA,yBAOA,SAAYD,GACV,IAAMC,EAAS7B,KAAKC,SAAS6B,aAC3B9B,KAAK+B,qBAAqBH,GAC1B5B,KAAK0C,aAAad,GAClB5B,KAAKG,aAEHwC,EAAS,EAUb,OARAf,EAAQK,SAAQ,SAACnB,GACf,IAAK,IAAIoB,EAAgB,EAAGA,EAAgBpB,EAAOqB,iBAAkBD,IACnEL,EAAOQ,eAAeH,GAAeO,IAAI3B,EAAOuB,eAAeH,GAAgBS,GAGjFA,GAAU7B,EAAO0B,MACnB,IAEOX,CACT,GAEA,sBAUA,SAASf,GAA6E,IAAxD8B,EAAmB,UAAH,6CAAG,EAAGC,EAAkB,UAAH,6CAAG,EACpE,GAAgB,IAAZA,EAAe,OAAO/B,EAE1B,GAAI8B,EAAW,EAAG,MAAM,IAAIE,MAAM,8DAClC,GAAID,EAAU,EAAG,MAAM,IAAIC,MAAM,6DAQjC,IANA,IAAMC,EAAgB/C,KAAKC,SAAS6B,aAClChB,EAAOqB,iBACPa,KAAKC,KAAKnC,EAAO0B,OAASK,EAAU/B,EAAOf,YAC3Ce,EAAOf,YAGAmC,EAAgB,EAAGA,EAAgBpB,EAAOqB,iBAAkBD,IAAiB,CACpF,IAAMgB,EAAcpC,EAAOuB,eAAeH,GAC1Ca,EACGV,eAAeH,GACfO,IAAIS,EAAYC,SAAS,EAAGH,KAAKC,KAAKL,EAAW9B,EAAOf,YAAc,GAAI,GAE7EgD,EACGV,eAAeH,GACfO,IACCS,EAAYC,SAASH,KAAKC,KAAKL,EAAW9B,EAAOf,YAAc,EAAGgD,EAAcP,OAAS,GACzFQ,KAAKC,MAAML,EAAWC,GAAW/B,EAAOf,YAE9C,CAEA,OAAOgD,CACT,GAEA,wBASA,SAAWjC,EAAqBsC,EAAeC,GAAmE,IAAtDC,EAAiB,UAAH,6CAAG,EAAGC,EAAkB,UAAH,6CAAG,EAChG,GAAIH,GAASC,EAAK,MAAM,IAAIP,MAAM,6EAMlC,IAJA,IAAMN,EAASQ,KAAKQ,OAAOH,EAAMD,GAASpD,KAAKG,aACzCwC,EAASK,KAAKQ,MAAMJ,EAAQpD,KAAKG,aACjCsD,EAAYzD,KAAKC,SAAS6B,aAAahB,EAAOqB,iBAAkBK,EAAQxC,KAAKG,aAE1EuD,EAAU,EAAGA,EAAU5C,EAAOqB,iBAAkBuB,IAIvD,IAHA,IAAMC,EAAY7C,EAAOuB,eAAeqB,GAClCtB,EAAaqB,EAAUpB,eAAeqB,GAEnCnB,EAAI,EAAGA,EAAIC,EAAQD,IAC1BH,EAAWG,GAAKoB,EAAUhB,EAASJ,GAG/BA,EAAIe,EAAStD,KAAKG,cACpBiC,EAAWG,IAAMA,GAAKe,EAAStD,KAAKG,cAIlCoC,EAAIC,EAASe,EAAUvD,KAAKG,cAC9BiC,EAAWG,KAAOC,EAASD,IAAMgB,EAAUvD,KAAKG,cAKtD,OAAOsD,CACT,GAEA,kBAGA,SAAK3C,GACH,IAAM8C,EAAS5D,KAAKC,SAAS4D,qBAM7B,OAJAD,EAAO9C,OAASA,EAChB8C,EAAOE,QAAQ9D,KAAKC,SAAS8D,aAC7BH,EAAOR,QAEAQ,CACT,GAEA,oBAUA,SAAO9C,GAAuE,IAAlDkD,EAAe,UAAH,6CAAG,YACnCC,EAAWjE,KAAKkE,YAAYpD,GAC5BqD,EAAWnE,KAAKoE,cAAcH,EAAUnD,EAAOqB,iBAAkBrB,EAAOf,YACxEsE,EAAY,IAAIrD,KAAK,CAACmD,GAAW,CAAEH,KAAAA,IAEzC,MAAO,CACLM,KAAMD,EACNE,IAAKvE,KAAKwE,WAAWH,GACrBI,QAASzE,KAAK0E,oBAAoBL,GAEtC,GAEA,sBAMA,SAASC,GAA6D,IAAjDK,EAAmB,UAAH,6CAAG,UAChCC,EAAIC,SAASC,cAAc,KAOjC,OALAF,EAAEG,MAAMC,QAAU,OAClBJ,EAAEK,KAAOjF,KAAKwE,WAAWF,GACzBM,EAAEM,SAAW,GAAH,OAAMP,EAAQ,YAAIL,EAAKN,KAAKtC,MAAM,KAAK,IACjDkD,EAAEO,QAEKP,CACT,GAEA,0BAOA,SAAgBQ,GACd,OAAOpF,KAAKqF,oBAAiBC,EAAYF,GAC3C,GAEA,mBAGA,WAEE,OADApF,KAAKC,SAASsF,QACPvF,IACT,GAEA,0BAKA,SAAqB4B,GACnB,OAAOoB,KAAKwC,IAAG,MAARxC,KAAI,EAAQpB,EAAQhB,KAAI,SAACE,GAAM,OAAKA,EAAO2E,QAAQ,KAC5D,GAEA,kCAKA,SAA6B7D,GAC3B,OAAOoB,KAAKwC,IAAG,MAARxC,KAAI,EAAQpB,EAAQhB,KAAI,SAACE,GAAM,OAAKA,EAAOqB,gBAAgB,KACpE,GAEA,0BAKA,SAAqBP,GACnB,OAAOA,EAAQhB,KAAI,SAACE,GAAM,OAAKA,EAAO0B,MAAM,IAAEkD,QAAO,SAACd,EAAGe,GAAC,OAAKf,EAAIe,CAAC,GAAE,EACxE,GAEA,0BAKA,WACE,MAAO,iBAAkBvF,QAAU,uBAAwBA,QAAU,oBAAqBA,MAC5F,GAEA,2BAOA,SAAsBU,EAAsB8E,EAAuB7F,GACjE,IAEM8F,EADiBC,EACJF,EAIbG,EALiBD,EAKDhF,EAAO0B,OACvBwD,EAFkB,GAEiBD,EAEnC9E,EAAc,IAAIgF,YALD,EAK8BD,GAC/CE,EAAO,IAAIC,SAASlF,GAgB1B,OAdAjB,KAAKoG,aAAaF,EAAM,EAAG,QAC3BA,EAAKG,UAAU,EAAGL,GAAgB,GAClChG,KAAKoG,aAAaF,EAAM,EAAG,QAC3BlG,KAAKoG,aAAaF,EAAM,GAAI,QAC5BA,EAAKG,UAAU,GAAI,IAAI,GACvBH,EAAKI,UAAU,GAAI,GAAG,GACtBJ,EAAKI,UAAU,GAAIV,GAAe,GAClCM,EAAKG,UAAU,GAAItG,GAAY,GAC/BmG,EAAKG,UAAU,GAAItG,EAAa8F,GAAY,GAC5CK,EAAKI,UAAU,GAAIT,GAAY,GAC/BK,EAAKI,UAAU,GAtBE,IAsBY,GAC7BtG,KAAKoG,aAAaF,EAAM,GAAI,QAC5BA,EAAKG,UAAU,GAAIN,GAAe,GAE3B/F,KAAKuG,iBAAiBL,EAAMpF,EAAQ0F,GAC7C,GAEA,8BAKA,SAAyBrC,EAAoBrD,EAAsB6B,GACjE,IAAK,IAAIJ,EAAI,EAAGA,EAAIzB,EAAO0B,OAAQD,IAAKI,GAAU,EAAG,CACnD,IAAM8D,EAAMzD,KAAKwC,KAAK,EAAGxC,KAAK0D,IAAI,EAAG5F,EAAOyB,KAC5C4B,EAASwC,SAAShE,EAAQ8D,EAAM,EAAU,MAANA,EAAqB,MAANA,GAAc,EACnE,CAEA,OAAOtC,CACT,GAEA,0BAKA,SAAqBA,EAAoBxB,EAAgBiE,GACvD,IAAK,IAAIrE,EAAI,EAAGA,EAALine truncated
|
||||
@@ -8,7 +8,8 @@ from TTS.tts.configs.xtts_config import XttsConfig
|
||||
from TTS.tts.models.xtts import Xtts
|
||||
import io
|
||||
import wave
|
||||
|
||||
import logging
|
||||
logging.disable(logging.WARNING)
|
||||
##########################
|
||||
#### Webserver Imports####
|
||||
##########################
|
||||
@@ -19,6 +20,7 @@ from fastapi import (
|
||||
Response,
|
||||
Depends,
|
||||
HTTPException,
|
||||
Query,
|
||||
)
|
||||
from fastapi.responses import JSONResponse, HTMLResponse, RedirectResponse, FileResponse, StreamingResponse
|
||||
from fastapi.middleware.cors import CORSMiddleware
|
||||
@@ -34,7 +36,7 @@ this_dir = Path(__file__).parent.resolve()
|
||||
# STARTUP VARIABLE - Set "device" to cuda if exists, otherwise cpu
|
||||
device = "cuda" if torch.cuda.is_available() else "cpu"
|
||||
# STARTUP VARIABLE - Import languges file for Gradio to be able to display them in the interface
|
||||
with open(this_dir / "languages.json", encoding="utf8") as f:
|
||||
with open(this_dir / "system" / "config" / "languages.json", encoding="utf8") as f:
|
||||
languages = json.load(f)
|
||||
# Base setting for a possible FineTuned model existing and the loader being available
|
||||
tts_method_xtts_ft = False
|
||||
@@ -57,6 +59,10 @@ params = load_config(configfile_path)
|
||||
# check someone hasnt enabled lowvram on a system thats not cuda enabled
|
||||
params["low_vram"] = "false" if not torch.cuda.is_available() else params["low_vram"]
|
||||
|
||||
# Load values for temperature and repetition_penalty
|
||||
temperature = params["local_temperature"]
|
||||
repetition_penalty = params["local_repetition_penalty"]
|
||||
|
||||
# Define the path to the JSON file
|
||||
config_file_path = this_dir / "modeldownload.json"
|
||||
|
||||
@@ -81,6 +87,17 @@ else:
|
||||
f"[{params['branding']}Startup] \033[91mWarning\033[0m modeldownload.config is missing so please re-download it and save it in the alltalk_tts main folder."
|
||||
)
|
||||
|
||||
##################################################
|
||||
#### Check to see if a finetuned model exists ####
|
||||
##################################################
|
||||
# Set the path to the directory
|
||||
trained_model_directory = this_dir / "models" / "trainedmodel"
|
||||
# Check if the directory "trainedmodel" exists
|
||||
finetuned_model = trained_model_directory.exists()
|
||||
# If the directory exists, check for the existence of the required files
|
||||
if finetuned_model:
|
||||
required_files = ["model.pth", "config.json", "vocab.json"]
|
||||
finetuned_model = all((trained_model_directory / file).exists() for file in required_files)
|
||||
|
||||
########################
|
||||
#### STARTUP CHECKS ####
|
||||
@@ -98,20 +115,12 @@ except ModuleNotFoundError:
|
||||
raise
|
||||
|
||||
# DEEPSPEED Import - Check for DeepSpeed and import it if it exists
|
||||
deepspeed_available = False
|
||||
try:
|
||||
import deepspeed
|
||||
|
||||
deepspeed_installed = True
|
||||
print(f"[{params['branding']}Startup] DeepSpeed \033[93mDetected\033[0m")
|
||||
print(
|
||||
f"[{params['branding']}Startup] Activate DeepSpeed in {params['branding']} settings"
|
||||
)
|
||||
deepspeed_available = True
|
||||
except ImportError:
|
||||
deepspeed_installed = False
|
||||
print(
|
||||
f"[{params['branding']}Startup] DeepSpeed \033[93mNot Detected\033[0m. See https://github.com/microsoft/DeepSpeed"
|
||||
)
|
||||
|
||||
pass
|
||||
|
||||
@asynccontextmanager
|
||||
async def startup_shutdown(no_actual_value_it_demanded_something_be_here):
|
||||
@@ -173,9 +182,8 @@ async def setup():
|
||||
# Calculate start time minus end time
|
||||
generate_elapsed_time = generate_end_time - generate_start_time
|
||||
# Print out the result of the load time
|
||||
print(
|
||||
f"[{params['branding']}Model] \033[94mModel Loaded in \033[93m{generate_elapsed_time:.2f} seconds.\033[0m"
|
||||
)
|
||||
print(f"[{params['branding']}Model] \033[94mModel Loaded in \033[93m{generate_elapsed_time:.2f} seconds.\033[0m")
|
||||
print(f"[{params['branding']}Model] Ready")
|
||||
# Set "tts_model_loaded" to true
|
||||
params["tts_model_loaded"] = True
|
||||
# Set the output path for wav files
|
||||
@@ -209,6 +217,8 @@ async def api_manual_load_model():
|
||||
model_path=modeldownload_base_path / modeldownload_model_path,
|
||||
config_path=modeldownload_base_path / modeldownload_model_path / "config.json",
|
||||
).to(device)
|
||||
print(f"[{params['branding']}Model] \033[94mCoqui Public Model License\033[0m")
|
||||
print(f"[{params['branding']}Model] \033[94mhttps://coqui.ai/cpml.txt\033[0m")
|
||||
return model
|
||||
|
||||
|
||||
@@ -238,6 +248,8 @@ async def xtts_manual_load_model():
|
||||
use_deepspeed=params["deepspeed_activate"],
|
||||
)
|
||||
model.to(device)
|
||||
print(f"[{params['branding']}Model] \033[94mCoqui Public Model License\033[0m")
|
||||
print(f"[{params['branding']}Model] \033[94mhttps://coqui.ai/cpml.txt\033[0m")
|
||||
return model
|
||||
|
||||
# MODEL LOADER For "XTTSv2 FT"
|
||||
@@ -256,6 +268,8 @@ async def xtts_ft_manual_load_model():
|
||||
use_deepspeed=params["deepspeed_activate"],
|
||||
)
|
||||
model.to(device)
|
||||
print(f"[{params['branding']}Model] \033[94mCoqui Public Model License\033[0m")
|
||||
print(f"[{params['branding']}Model] \033[94mhttps://coqui.ai/cpml.txt\033[0m")
|
||||
return model
|
||||
|
||||
# MODEL UNLOADER
|
||||
@@ -462,19 +476,26 @@ async def deepspeed(request: Request, new_deepspeed_value: bool):
|
||||
########################
|
||||
#### TTS GENERATION ####
|
||||
########################
|
||||
debug_generate_audio = False
|
||||
tts_stop_generation = False # Called to stop generation of the current text at whatever stage its at. currently only set for streaming.
|
||||
tts_generation_lock = False # Tracks locking of the generaetion process.
|
||||
tts_narrator_generatingtts = False # Tracks if the current tts processes are narrator based, to avoid moving model on each chunk of text generated.
|
||||
# Not in use tts_queue_behaviour = False # True is to queue the current generation request. False is to cancel the current generation and start a new one.
|
||||
|
||||
# TTS VOICE GENERATION METHODS (called from voice_preview and output_modifer)
|
||||
async def generate_audio(text, voice, language, output_file, streaming=False):
|
||||
async def generate_audio(text, voice, language, temperature, repetition_penalty, output_file, streaming=False):
|
||||
# Get the async generator from the internal function
|
||||
response = generate_audio_internal(text, voice, language, output_file, streaming)
|
||||
response = generate_audio_internal(text, voice, language, temperature, repetition_penalty, output_file, streaming)
|
||||
# If streaming, then return the generator as-is, otherwise just exhaust it and return
|
||||
if streaming:
|
||||
return response
|
||||
async for _ in response:
|
||||
pass
|
||||
|
||||
async def generate_audio_internal(text, voice, language, output_file, streaming):
|
||||
global model
|
||||
|
||||
async def generate_audio_internal(text, voice, language, temperature, repetition_penalty, output_file, streaming):
|
||||
global model, tts_stop_generation, tts_generation_lock
|
||||
tts_generation_lock = True
|
||||
if params["low_vram"] and device == "cpu":
|
||||
await switch_device()
|
||||
generate_start_time = time.time() # Record the start time of generating TTS
|
||||
@@ -495,9 +516,9 @@ async def generate_audio_internal(text, voice, language, output_file, streaming)
|
||||
"language": language,
|
||||
"gpt_cond_latent": gpt_cond_latent,
|
||||
"speaker_embedding": speaker_embedding,
|
||||
"temperature": float(params["local_temperature"]),
|
||||
"temperature": float(temperature),
|
||||
"length_penalty": float(model.config.length_penalty),
|
||||
"repetition_penalty": float(params["local_repetition_penalty"]),
|
||||
"repetition_penalty": float(repetition_penalty),
|
||||
"top_k": int(model.config.top_k),
|
||||
"top_p": float(model.config.top_p),
|
||||
"enable_text_splitting": True
|
||||
@@ -525,6 +546,12 @@ async def generate_audio_internal(text, voice, language, output_file, streaming)
|
||||
yield wav_buf.read()
|
||||
|
||||
for i, chunk in enumerate(output):
|
||||
if tts_stop_generation:
|
||||
print(f"[{params['branding']}TTSGen] Stopping audio generation.")
|
||||
file_chunks.clear() # Clear the file_chunks list
|
||||
tts_stop_generation = False
|
||||
tts_generation_lock = False
|
||||
break
|
||||
file_chunks.append(chunk)
|
||||
if isinstance(chunk, list):
|
||||
chunk = torch.cat(chunk, dim=0)
|
||||
@@ -533,6 +560,7 @@ async def generate_audio_internal(text, voice, language, output_file, streaming)
|
||||
chunk = np.clip(chunk, -1, 1)
|
||||
chunk = (chunk * 32767).astype(np.int16)
|
||||
yield chunk.tobytes()
|
||||
print(f"[{params['branding']}Debug] Stream audio generation: Yielded audio chunk {i}.") if debug_generate_audio else None
|
||||
else:
|
||||
# Non-streaming-specific operation
|
||||
torchaudio.save(output_file, torch.tensor(output["wav"]).unsqueeze(0), 24000)
|
||||
@@ -551,9 +579,9 @@ async def generate_audio_internal(text, voice, language, output_file, streaming)
|
||||
file_path=output_file,
|
||||
speaker_wav=[f"{this_dir}/voices/{voice}"],
|
||||
language=language,
|
||||
temperature=float(params["local_temperature"]),
|
||||
length_penalty=model.config.length_penalty,
|
||||
repetition_penalty=float(params["local_repetition_penalty"]),
|
||||
temperature=float(temperature),
|
||||
length_penalty=float(model.config.length_penalty),
|
||||
repetition_penalty=float(repetition_penalty),
|
||||
top_k=model.config.top_k,
|
||||
top_p=model.config.top_p,
|
||||
)
|
||||
@@ -575,12 +603,11 @@ async def generate_audio_internal(text, voice, language, output_file, streaming)
|
||||
# Print Generation time and settings
|
||||
generate_end_time = time.time() # Record the end time to generate TTS
|
||||
generate_elapsed_time = generate_end_time - generate_start_time
|
||||
print(
|
||||
f"[{params['branding']}TTSGen] \033[93m{generate_elapsed_time:.2f} seconds. \033[94mLowVRAM: \033[33m{params['low_vram']} \033[94mDeepSpeed: \033[33m{params['deepspeed_activate']}\033[0m"
|
||||
)
|
||||
print(f"[{params['branding']}TTSGen] \033[93m{generate_elapsed_time:.2f} seconds. \033[94mLowVRAM: \033[33m{params['low_vram']} \033[94mDeepSpeed: \033[33m{params['deepspeed_activate']}\033[0m")
|
||||
# Move model back to cpu system ram if needed.
|
||||
if params["low_vram"] and device == "cuda":
|
||||
if params["low_vram"] and device == "cuda" and tts_narrator_generatingtts == False:
|
||||
await switch_device()
|
||||
tts_generation_lock = False
|
||||
return
|
||||
|
||||
|
||||
@@ -593,10 +620,12 @@ async def generate(request: Request):
|
||||
text = data["text"]
|
||||
voice = data["voice"]
|
||||
language = data["language"]
|
||||
temperature = data["temperature"]
|
||||
repetition_penalty = data["repetition_penalty"]
|
||||
output_file = data["output_file"]
|
||||
streaming = False
|
||||
# Generation logic
|
||||
response = await generate_audio(text, voice, language, output_file, streaming)
|
||||
response = await generate_audio(text, voice, language, temperature, repetition_penalty, output_file, streaming)
|
||||
if streaming:
|
||||
return StreamingResponse(response, media_type="audio/wav")
|
||||
return JSONResponse(
|
||||
@@ -623,7 +652,7 @@ def list_files(directory):
|
||||
#############################
|
||||
|
||||
# Create an instance of Jinja2Templates for rendering HTML templates
|
||||
templates = Jinja2Templates(directory=this_dir / "templates")
|
||||
templates = Jinja2Templates(directory=this_dir / "system")
|
||||
|
||||
# Create a dependency to get the current JSON data
|
||||
def get_json_data():
|
||||
@@ -638,7 +667,7 @@ async def get_settings(request: Request):
|
||||
wav_files = list_files(this_dir / "voices")
|
||||
# Render the template with the current JSON data and list of WAV files
|
||||
return templates.TemplateResponse(
|
||||
"generate_form.html",
|
||||
"/at_admin/at_settings.html",
|
||||
{
|
||||
"request": request,
|
||||
"data": get_json_data(),
|
||||
@@ -648,7 +677,7 @@ async def get_settings(request: Request):
|
||||
)
|
||||
|
||||
# Define an endpoint to serve static files
|
||||
app.mount("/static", StaticFiles(directory=str(this_dir / "templates")), name="static")
|
||||
app.mount("/static", StaticFiles(directory=str(this_dir / "system")), name="static")
|
||||
|
||||
@app.post("/update-settings")
|
||||
async def update_settings(
|
||||
@@ -713,7 +742,7 @@ async def update_settings(
|
||||
async def tts_demo_request_streaming(text: str, voice: str, language: str, output_file: str):
|
||||
try:
|
||||
output_file_path = this_dir / "outputs" / output_file
|
||||
stream = await generate_audio(text, voice, language, output_file_path, streaming=True)
|
||||
stream = await generate_audio(text, voice, language, temperature, repetition_penalty, output_file_path, streaming=True)
|
||||
return StreamingResponse(stream, media_type="audio/wav")
|
||||
except Exception as e:
|
||||
print(f"An error occurred: {e}")
|
||||
@@ -723,12 +752,17 @@ async def tts_demo_request_streaming(text: str, voice: str, language: str, outpu
|
||||
async def tts_demo_request(request: Request, text: str = Form(...), voice: str = Form(...), language: str = Form(...), output_file: str = Form(...)):
|
||||
try:
|
||||
output_file_path = this_dir / "outputs" / output_file
|
||||
await generate_audio(text, voice, language, output_file_path, streaming=False)
|
||||
await generate_audio(text, voice, language, temperature, repetition_penalty, output_file_path, streaming=False)
|
||||
return JSONResponse(content={"output_file_path": str(output_file)}, status_code=200)
|
||||
except Exception as e:
|
||||
print(f"An error occurred: {e}")
|
||||
return JSONResponse(content={"error": "An error occurred"}, status_code=500)
|
||||
|
||||
|
||||
#####################
|
||||
#### Audio feeds ####
|
||||
#####################
|
||||
|
||||
# Gives web access to the output files
|
||||
@app.get("/audio/{filename}")
|
||||
async def get_audio(filename: str):
|
||||
@@ -780,7 +814,7 @@ async def preview_voice(request: Request, voice: str = Form(...)):
|
||||
|
||||
# Generate the audio
|
||||
output_file_path = this_dir / "outputs" / f"{output_file_name}.wav"
|
||||
await generate_audio(text, voice, language, output_file_path, streaming=False)
|
||||
await generate_audio(text, voice, language, temperature, repetition_penalty, output_file_path, streaming=False)
|
||||
|
||||
# Generate the URL
|
||||
output_file_url = f'http://{params["ip_address"]}:{params["port_number"]}/audio/{output_file_name}.wav'
|
||||
@@ -807,6 +841,42 @@ import uuid
|
||||
import numpy as np
|
||||
import soundfile as sf
|
||||
import sys
|
||||
import hashlib
|
||||
|
||||
##############################
|
||||
#### Streaming Generation ####
|
||||
##############################
|
||||
|
||||
@app.get("/api/tts-generate-streaming", response_class=StreamingResponse)
|
||||
async def tts_generate_streaming(text: str, voice: str, language: str, output_file: str):
|
||||
try:
|
||||
output_file_path = this_dir / "outputs" / output_file
|
||||
stream = await generate_audio(text, voice, language, temperature, repetition_penalty, output_file_path, streaming=True)
|
||||
return StreamingResponse(stream, media_type="audio/wav")
|
||||
except Exception as e:
|
||||
print(f"An error occurred: {e}")
|
||||
return JSONResponse(content={"error": "An error occurred"}, status_code=500)
|
||||
|
||||
@app.post("/api/tts-generate-streaming", response_class=JSONResponse)
|
||||
async def tts_generate_streaming(request: Request, text: str = Form(...), voice: str = Form(...), language: str = Form(...), output_file: str = Form(...)):
|
||||
try:
|
||||
output_file_path = this_dir / "outputs" / output_file
|
||||
await generate_audio(text, voice, language, temperature, repetition_penalty, output_file_path, streaming=False)
|
||||
return JSONResponse(content={"output_file_path": str(output_file)}, status_code=200)
|
||||
except Exception as e:
|
||||
print(f"An error occurred: {e}")
|
||||
return JSONResponse(content={"error": "An error occurred"}, status_code=500)
|
||||
|
||||
@app.put("/api/stop-generation")
|
||||
async def stop_generation_endpoint():
|
||||
global tts_stop_generation, tts_generation_lock
|
||||
if tts_generation_lock and not tts_stop_generation:
|
||||
tts_stop_generation = True
|
||||
return {"message": "Generation stopped"}
|
||||
|
||||
##############################
|
||||
#### Standard Generation ####
|
||||
##############################
|
||||
|
||||
# Check for PortAudio library on Linux
|
||||
try:
|
||||
@@ -821,7 +891,7 @@ except OSError:
|
||||
print(f"[{params['branding']}Startup] \033[91mInfo\033[0m On Linux, you can use the following command to install PortAudio:")
|
||||
print(f"[{params['branding']}Startup] \033[91mInfo\033[0m sudo apt-get install portaudio19-dev")
|
||||
|
||||
from typing import Union, Dict
|
||||
from typing import Union, Dict, List
|
||||
from pydantic import BaseModel, ValidationError, Field
|
||||
|
||||
def play_audio(file_path, volume):
|
||||
@@ -834,13 +904,13 @@ class Request(BaseModel):
|
||||
pass
|
||||
|
||||
class JSONInput(BaseModel):
|
||||
text_input: str = Field(..., max_length=1000, description="text_input needs to be 1000 characters or less.")
|
||||
text_input: str = Field(..., max_length=2000, description="text_input needs to be 2000 characters or less.")
|
||||
text_filtering: str = Field(..., pattern="^(none|standard|html)$", description="text_filtering needs to be 'none', 'standard' or 'html'.")
|
||||
character_voice_gen: str = Field(..., pattern="^.*\.wav$", description="character_voice_gen needs to be the name of a wav file e.g. mysample.wav.")
|
||||
narrator_enabled: bool = Field(..., description="narrator_enabled needs to be true or false.")
|
||||
narrator_voice_gen: str = Field(..., pattern="^.*\.wav$", description="narrator_voice_gen needs to be the name of a wav file e.g. mysample.wav.")
|
||||
text_not_inside: str = Field(..., pattern="^(character|narrator)$", description="text_not_inside needs to be 'character' or 'narrator'.")
|
||||
language: str = Field(..., pattern="^(ar|zh-cn|cs|nl|en|fr|de|hu|it|ja|ko|pl|pt|ru|es|tr)$", description="language needs to be one of the following ar|zh-cn|cs|nl|en|fr|de|hu|it|ja|ko|pl|pt|ru|es|tr.")
|
||||
language: str = Field(..., pattern="^(ar|zh-cn|cs|nl|en|fr|de|hu|hi|it|ja|ko|pl|pt|ru|es|tr)$", description="language needs to be one of the following ar|zh-cn|cs|nl|en|fr|de|hu|hi|it|ja|ko|pl|pt|ru|es|tr.")
|
||||
output_file_name: str = Field(..., pattern="^[a-zA-Z0-9_]+$", description="output_file_name needs to be the name without any special characters or file extension e.g. 'filename'")
|
||||
output_file_timestamp: bool = Field(..., description="output_file_timestamp needs to be true or false.")
|
||||
autoplay: bool = Field(..., description="autoplay needs to be a true or false value.")
|
||||
@@ -936,8 +1006,8 @@ def combine(output_file_timestamp, output_file_name, audio_files):
|
||||
output_cache_url = f'http://{params["ip_address"]}:{params["port_number"]}/audiocache/{output_file_name}_{timestamp}_combined.wav'
|
||||
else:
|
||||
output_file_path = os.path.join(this_dir / "outputs" / f'{output_file_name}_combined.wav')
|
||||
output_file_url = f'http://{params["ip_address"]}:{params["port_number"]}/audio/{output_file_name}.wav'
|
||||
output_cache_url = f'http://{params["ip_address"]}:{params["port_number"]}/audiocache/{output_file_name}.wav'
|
||||
output_file_url = f'http://{params["ip_address"]}:{params["port_number"]}/audio/{output_file_name}_combined.wav'
|
||||
output_cache_url = f'http://{params["ip_address"]}:{params["port_number"]}/audiocache/{output_file_name}_combined.wav'
|
||||
try:
|
||||
sf.write(output_file_path, audio, samplerate=sample_rate)
|
||||
# Clean up unnecessary files
|
||||
@@ -979,12 +1049,17 @@ async def tts_generate(
|
||||
"autoplay_volume": autoplay_volume,
|
||||
"streaming": streaming,
|
||||
}
|
||||
global tts_narrator_generatingtts
|
||||
JSONresult = TTSGenerator.validate_json_input(json_input_data)
|
||||
if JSONresult is None:
|
||||
pass
|
||||
else:
|
||||
print(f"[{params['branding']}API] \033[91mError with API request:\033[0m", JSONresult)
|
||||
return JSONResponse(content={"error": JSONresult}, status_code=400)
|
||||
if narrator_enabled:
|
||||
if params["low_vram"] and device == "cpu":
|
||||
await switch_device()
|
||||
tts_narrator_generatingtts = True
|
||||
processed_parts = process_text(text_input)
|
||||
audio_files_all_paragraphs = []
|
||||
for part_type, part in processed_parts:
|
||||
@@ -1004,44 +1079,61 @@ async def tts_generate(
|
||||
voice_description = "\033[36mCharacter (Text-not-inside)\033[0m" if text_not_inside == "character" else "\033[92mNarrator (Text-not-inside)\033[0m"
|
||||
print(f"[{params['branding']}TTSGen] {voice_description}")
|
||||
# Replace multiple exclamation marks, question marks, or other punctuation with a single instance
|
||||
cleaned_part = re.sub(r'([!?.])\1+', r'\1', part)
|
||||
cleaned_part = re.sub(r'([!?.\u3002\uFF1F\uFF01\uFF0C])\1+', r'\1', part)
|
||||
# Replace "Chinese ellipsis" with a single dot
|
||||
cleaned_part = re.sub(r"\u2026{1,2}", ". ", cleaned_part)
|
||||
# Further clean to remove any other unwanted characters
|
||||
cleaned_part = re.sub(r'[^a-zA-Z0-9\s\.,;:!?\-\'"\u0400-\u04FFÀ-ÿ\u0150\u0151\u0170\u0171\$]', '', cleaned_part)
|
||||
cleaned_part = re.sub(r'[^a-zA-Z0-9\s.,;:!?\-\'"$\u0400-\u04FF\u00C0-\u00FF\u0150\u0151\u0170\u0171\u0900-\u097F\u2018\u2019\u201C\u201D\u3001\u3002\u3040-\u309F\u30A0-\u30FF\u4E00-\u9FFF\u3400-\u4DBF\uF900-\uFAFF\u0600-\u06FF\u0750-\u077F\uFB50-\uFDFF\uFE70-\uFEFF\uAC00-\uD7A3\u1100-\u11FF\u3130-\u318F\uFF01\uFF0c\uFF1A\uFF1B\uFF1F]', '', cleaned_part)
|
||||
# Remove all newline characters (single or multiple)
|
||||
cleaned_part = re.sub(r'\n+', ' ', cleaned_part)
|
||||
output_file = this_dir / "outputs" / f"{output_file_name}_{uuid.uuid4()}_{int(time.time())}.wav"
|
||||
output_file_str = output_file.as_posix()
|
||||
response = await generate_audio(cleaned_part, voice_to_use, language, output_file_str, streaming)
|
||||
response = await generate_audio(cleaned_part, voice_to_use, language,temperature, repetition_penalty, output_file_str, streaming)
|
||||
audio_path = output_file_str
|
||||
audio_files_all_paragraphs.append(audio_path)
|
||||
# Combine audio files across paragraphs
|
||||
output_file_path, output_file_url, output_cache_url = combine(output_file_timestamp, output_file_name, audio_files_all_paragraphs)
|
||||
tts_narrator_generatingtts = False
|
||||
# Move model back to cpu system ram if needed.
|
||||
if params["low_vram"] and device == "cuda":
|
||||
await switch_device()
|
||||
else:
|
||||
if output_file_timestamp:
|
||||
timestamp = int(time.time())
|
||||
output_file_path = this_dir / "outputs" / f"{output_file_name}_{timestamp}.wav"
|
||||
output_file_url = f'http://{params["ip_address"]}:{params["port_number"]}/audio/{output_file_name}_{timestamp}.wav'
|
||||
output_cache_url = f'http://{params["ip_address"]}:{params["port_number"]}/audiocache/{output_file_name}_{timestamp}.wav'
|
||||
# Generate a standard UUID
|
||||
original_uuid = uuid.uuid4()
|
||||
# Hash the UUID using SHA-256
|
||||
hash_object = hashlib.sha256(str(original_uuid).encode())
|
||||
hashed_uuid = hash_object.hexdigest()
|
||||
# Truncate to the desired length, for example, 16 characters
|
||||
short_uuid = hashed_uuid[:5]
|
||||
output_file_path = this_dir / "outputs" / f"{output_file_name}_{timestamp}{short_uuid}.wav"
|
||||
output_file_url = f'http://{params["ip_address"]}:{params["port_number"]}/audio/{output_file_name}_{timestamp}{short_uuid}.wav'
|
||||
output_cache_url = f'http://{params["ip_address"]}:{params["port_number"]}/audiocache/{output_file_name}_{timestamp}{short_uuid}.wav'
|
||||
else:
|
||||
output_file_path = this_dir / "outputs" / f"{output_file_name}.wav"
|
||||
output_file_url = f'http://{params["ip_address"]}:{params["port_number"]}/audio/{output_file_name}.wav'
|
||||
output_cache_url = f'http://{params["ip_address"]}:{params["port_number"]}/audiocache/{output_file_name}_{timestamp}.wav'
|
||||
output_cache_url = f'http://{params["ip_address"]}:{params["port_number"]}/audiocache/{output_file_name}.wav'
|
||||
if text_filtering == "html":
|
||||
cleaned_string = html.unescape(standard_filtering(text_input))
|
||||
cleaned_string = re.sub(r'([!?.])\1+', r'\1', text_input)
|
||||
cleaned_string = re.sub(r'([!?.\u3002\uFF1F\uFF01\uFF0C])\1+', r'\1', text_input)
|
||||
# Replace "Chinese ellipsis" with a single dot
|
||||
cleaned_string = re.sub(r"\u2026{1,2}", ". ", cleaned_string)
|
||||
# Further clean to remove any other unwanted characters
|
||||
cleaned_string = re.sub(r'[^a-zA-Z0-9\s\.,;:!?\-\'"\u0400-\u04FFÀ-ÿ\u0150\u0151\u0170\u0171\$]', '', cleaned_string)
|
||||
cleaned_string = re.sub(r'[^a-zA-Z0-9\s.,;:!?\-\'"$\u0400-\u04FF\u00C0-\u00FF\u0150\u0151\u0170\u0171\u0900-\u097F\u2018\u2019\u201C\u201D\u3001\u3002\u3040-\u309F\u30A0-\u30FF\u4E00-\u9FFF\u3400-\u4DBF\uF900-\uFAFF\u0600-\u06FF\u0750-\u077F\uFB50-\uFDFF\uFE70-\uFEFF\uAC00-\uD7A3\u1100-\u11FF\u3130-\u318F\uFF01\uFF0c\uFF1A\uFF1B\uFF1F]', '', cleaned_string)
|
||||
# Remove all newline characters (single or multiple)
|
||||
cleaned_string = re.sub(r'\n+', ' ', cleaned_string)
|
||||
elif text_filtering == "standard":
|
||||
cleaned_string = re.sub(r'([!?.])\1+', r'\1', text_input)
|
||||
cleaned_string = re.sub(r'([!?.\u3002\uFF1F\uFF01\uFF0C])\1+', r'\1', text_input)
|
||||
# Replace "Chinese ellipsis" with a single dot
|
||||
cleaned_string = re.sub(r"\u2026{1,2}", ". ", cleaned_string)
|
||||
# Further clean to remove any other unwanted characters
|
||||
cleaned_string = re.sub(r'[^a-zA-Z0-9\s\.,;:!?\-\'"\u0400-\u04FFÀ-ÿ\u0150\u0151\u0170\u0171\$]', '', cleaned_string)
|
||||
cleaned_string = re.sub(r'[^a-zA-Z0-9\s.,;:!?\-\'"$\u0400-\u04FF\u00C0-\u00FF\u0150\u0151\u0170\u0171\u0900-\u097F\u2018\u2019\u201C\u201D\u3001\u3002\u3040-\u309F\u30A0-\u30FF\u4E00-\u9FFF\u3400-\u4DBF\uF900-\uFAFF\u0600-\u06FF\u0750-\u077F\uFB50-\uFDFF\uFE70-\uFEFF\uAC00-\uD7A3\u1100-\u11FF\u3130-\u318F\uFF01\uFF0c\uFF1A\uFF1B\uFF1F]', '', cleaned_string)
|
||||
# Remove all newline characters (single or multiple)
|
||||
cleaned_string = re.sub(r'\n+', ' ', cleaned_string)
|
||||
else:
|
||||
cleaned_string = text_input
|
||||
response = await generate_audio(cleaned_string, character_voice_gen, language, output_file_path, streaming)
|
||||
response = await generate_audio(cleaned_string, character_voice_gen, language, temperature, repetition_penalty, output_file_path, streaming)
|
||||
if sounddevice_installed == False or streaming == True:
|
||||
autoplay = False
|
||||
if autoplay:
|
||||
@@ -1053,11 +1145,105 @@ async def tts_generate(
|
||||
return JSONResponse(content={"status": "generate-failure", "error": "An error occurred"}, status_code=500)
|
||||
|
||||
|
||||
##########################
|
||||
#### Current Settings ####
|
||||
##########################
|
||||
# Define the available models
|
||||
models_available = [
|
||||
{"name": "Coqui", "model_name": "API TTS"},
|
||||
{"name": "Coqui", "model_name": "API Local"},
|
||||
{"name": "Coqui", "model_name": "XTTSv2 Local"}
|
||||
]
|
||||
|
||||
@app.get('/api/currentsettings')
|
||||
def get_current_settings():
|
||||
# Determine the current model loaded
|
||||
if params["tts_method_api_tts"]:
|
||||
current_model_loaded = "API TTS"
|
||||
elif params["tts_method_api_local"]:
|
||||
current_model_loaded = "API Local"
|
||||
elif params["tts_method_xtts_local"]:
|
||||
current_model_loaded = "XTTSv2 Local"
|
||||
else:
|
||||
current_model_loaded = None # or a default value if no method is active
|
||||
|
||||
settings = {
|
||||
"models_available": models_available,
|
||||
"current_model_loaded": current_model_loaded,
|
||||
"deepspeed_available": deepspeed_available,
|
||||
"deepspeed_status": params["deepspeed_activate"],
|
||||
"low_vram_status": params["low_vram"],
|
||||
"finetuned_model": finetuned_model
|
||||
}
|
||||
return settings # Automatically converted to JSON by Fas
|
||||
|
||||
#############################
|
||||
#### Word Add-in Sharing ####
|
||||
#############################
|
||||
# Mount the static files from the 'word_addin' directory
|
||||
app.mount("/api/word_addin", StaticFiles(directory=os.path.join(this_dir / 'templates' / 'word_addin')), name="word_addin")
|
||||
app.mount("/api/word_addin", StaticFiles(directory=os.path.join(this_dir / 'system' / 'word_addin')), name="word_addin")
|
||||
|
||||
#############################################
|
||||
#### TTS Generator Comparision Endpoints ####
|
||||
#############################################
|
||||
import subprocess
|
||||
import aiofiles
|
||||
|
||||
class TTSItem(BaseModel):
|
||||
id: int
|
||||
fileUrl: str
|
||||
text: str
|
||||
characterVoice: str
|
||||
language: str
|
||||
|
||||
class TTSData(BaseModel):
|
||||
ttsList: List[TTSItem]
|
||||
|
||||
@app.post("/api/save-tts-data")
|
||||
async def save_tts_data(tts_data: List[TTSItem]):
|
||||
# Convert the list of Pydantic models to a list of dictionaries
|
||||
tts_data_list = [item.dict() for item in tts_data]
|
||||
# Serialize the list of dictionaries to a JSON string
|
||||
tts_data_json = json.dumps(tts_data_list, indent=4)
|
||||
async with aiofiles.open(this_dir / "outputs" / "ttsList.json", 'w') as f:
|
||||
await f.write(tts_data_json)
|
||||
return {"message": "Data saved successfully"}
|
||||
|
||||
import sys
|
||||
|
||||
@app.get("/api/trigger-analysis")
|
||||
async def trigger_analysis(threshold: int = Query(default=98)):
|
||||
venv_path = sys.prefix
|
||||
env = os.environ.copy()
|
||||
env["PATH"] = os.path.join(venv_path, "bin") + ":" + env["PATH"]
|
||||
ttslist_path = this_dir / "outputs" / "ttsList.json"
|
||||
wavfile_path = this_dir / "outputs"
|
||||
subprocess.run(["python", "tts_diff.py", f"--threshold={threshold}", f"--ttslistpath={ttslist_path}", f"--wavfilespath={wavfile_path}"], cwd=this_dir / "system" / "tts_diff", env=env)
|
||||
# Read the analysis summary
|
||||
try:
|
||||
with open(this_dir / "outputs" / "analysis_summary.json", "r") as summary_file:
|
||||
summary_data = json.load(summary_file)
|
||||
except FileNotFoundError:
|
||||
summary_data = {"error": "Analysis summary file not found."}
|
||||
return {"message": "Analysis Completed", "summary": summary_data}
|
||||
|
||||
#################################################
|
||||
#### TTS Generator SRT Subtitiles generation ####
|
||||
#################################################
|
||||
@app.get("/api/srt-generation")
|
||||
async def srt_generation():
|
||||
venv_path = sys.prefix
|
||||
env = os.environ.copy()
|
||||
env["PATH"] = os.path.join(venv_path, "bin") + ":" + env["PATH"]
|
||||
ttslist_path = this_dir / "outputs" / "ttsList.json"
|
||||
wavfile_path = this_dir / "outputs"
|
||||
subprocess.run(["python", "tts_srt.py", f"--ttslistpath={ttslist_path}", f"--wavfilespath={wavfile_path}"], cwd=this_dir / "system" / "tts_srt", env=env)
|
||||
|
||||
srt_file_path = this_dir / "outputs" / "subtitles.srt"
|
||||
if not srt_file_path.exists():
|
||||
raise HTTPException(status_code=404, detail="Subtitle file not found.")
|
||||
|
||||
return FileResponse(path=srt_file_path, filename="subtitles.srt", media_type='application/octet-stream')
|
||||
|
||||
###################################################
|
||||
#### Webserver Startup & Initial model Loading ####
|
||||
|
||||
@@ -9,4 +9,6 @@ Author BBC
|
||||
This file is licensed under the Creative Commons Attribution 3.0 Unported license.
|
||||
https://creativecommons.org/licenses/by/3.0/deed.en
|
||||
|
||||
Downsampled to 22050hz, mono and cut shorter
|
||||
Downsampled to 22050hz, mono and cut shorter.
|
||||
|
||||
This license applies to both the original file ( «Clint_Eastwood CC3.wav» ) and it's derivative, the enhanced file ( «Clint_Eastwood (enhanced)CC3.wav» ).
|
||||