Compare commits

..
72 Commits
Author SHA1 Message Date
erew123 6c20f45154 Add files via upload 2024-03-28 21:51:42 +00:00
erew123 97cda27c7e Delete finetune/put-voice-samples-in-here directory 2024-03-28 21:51:11 +00:00
erew123 a075495a0a Large Update
TTS Generator - Various interface bugs & filtering options cleaned up.
TTS Generator - TTSDiff now scans generated text and TTS for errors.
TTS Generator - TTSSRT now creates subtitle files for video production e.g. a Youtube video.
Finetune - Now uses a customised tokenizer to deal with Japanese.
Finetune - Pre flight check and warning messages.
Finetune - Extra documentation and warnings.
Entire file structure has been re-organised to simplify management and future changes.
Documentation (built in and Github) has been rewritten/tidied up.
Requirements files have been cleaned up and simplified.
ATsetup has been re-written as necessary with additional options.
DeepSpeed moved up to version 14.
Standalone Application moved to PyTorch 2.2.1.
Nvidia CUDA Toolkit installation is NO LONGER needed (other than to compile DeepSpeed on Linux)

Tested on Linux and Windows.
2024-03-28 21:49:33 +00:00
erew123 b205657be3 Delete finetune/compact.py 2024-03-28 21:38:03 +00:00
erew123 483d6c8e79 Delete harvard_sentences.txt 2024-03-28 21:37:52 +00:00
erew123 9bd4665e6b Delete languages.json 2024-03-28 21:37:45 +00:00
erew123 62e1ab7e89 Delete requirements_other.txt 2024-03-28 21:37:35 +00:00
erew123 740c54db1c Delete requirements_nvidia.txt 2024-03-28 21:37:27 +00:00
erew123 e188467730 Delete requirements_finetune.txt 2024-03-28 21:37:12 +00:00
erew123 743f23240e Delete templates directory 2024-03-28 21:36:51 +00:00
erew123 66dadbca25 Finetune - further tidy up
Amended instructions.
Tidied up Pre-Flight check.
Added  dropdowns refresh button for step 3.
2024-03-25 14:09:35 +00:00
erew123 8125b46dbe Update README.md 2024-03-24 14:38:08 +00:00
erew123 d4bd46f203 Update README.md 2024-03-24 14:00:14 +00:00
erew123 f13d6d336b Update README.md 2024-03-24 11:37:33 +00:00
erew123 1fa2f2e110 Update README.md 2024-03-24 11:16:45 +00:00
erew123 9ad4005e52 Update bug-report-support-request.md 2024-03-23 23:06:59 +00:00
erew123 6d6b92d4b2 Update README.md 2024-03-23 12:18:09 +00:00
erew123 bf5d913cae Delete Deepspeed-Windows directory 2024-03-23 12:03:21 +00:00
erew123 f52ccd3931 Update README.md 2024-03-23 11:57:13 +00:00
erew123 37e7c8002d Update README.md 2024-03-22 17:23:12 +00:00
erew123 ad7382ee68 Update README.md 2024-03-22 14:53:28 +00:00
erew123 2428bfe357 Update README.md 2024-03-22 13:59:00 +00:00
erew123 74f3acc51f Update README.md
Large documentation update
2024-03-22 13:53:28 +00:00
erew123 90a98f140b Update ttsgencomp.py 2024-03-20 16:56:59 +00:00
erew123 885311ecd8 ttsgencomp added --threshold
gives a little flexibility to deal with words that sound the same when transcribed.
2024-03-20 16:28:15 +00:00
erew123 c2952e9758 Add files via upload 2024-03-20 15:50:02 +00:00
erew123 7e6e18c6d9 TTSGEN Compare - Work in progress
A BETA test/Proof of concept script to compare Speech that was generated in TTS Generator to the original Text and flag issues.

Anyone reading this, you are welcome to try it, the readme instructions are in the folder, however, I am not providing support on this while it is BETA/Proof of concept.
2024-03-20 15:26:36 +00:00
erew123 47eafafeda Update README.md 2024-03-19 12:35:15 +00:00
erew123 5edaab1356 Update README.md 2024-03-19 12:20:44 +00:00
erew123 7bde34d2ce Update README.md 2024-03-19 12:03:32 +00:00
erew123 0aa4638f68 Update README.md 2024-03-19 11:54:50 +00:00
erew123 22dd9c741c Update README.md 2024-03-19 11:51:03 +00:00
erew123 e000b8bb87 Update README.md 2024-03-19 11:10:00 +00:00
erew123 2d26f309df Update README.md 2024-03-19 11:06:04 +00:00
erew123 22d3e53a28 Update bug-report-support-request.md 2024-03-19 10:42:56 +00:00
erew123 0ea646dbf3 Update bug-report-support-request.md 2024-03-19 10:41:42 +00:00
erew123 282e3ce84c Update bug-report-support-request.md 2024-03-19 10:41:04 +00:00
erew123 772afd47a1 Update README.md 2024-03-19 10:11:29 +00:00
erew123 1612835c5d Update README.md 2024-03-19 09:55:50 +00:00
erew123 ea551d3a84 ID Playing reset on stop - corrected
Corrected the ID playing from resetting when the Stop button is pressed.
2024-03-17 12:51:41 +00:00
erew123 47001203cd Added DeepSpeed v14 for CUDA 11.8 w/PT 2.2.1 2024-03-16 13:21:25 +00:00
erew123 27ff5a12b0 Delete templates/tokenizer/__pycache__ directory 2024-03-16 11:16:01 +00:00
erew123 0a89407e4d Custom tokenizer for Japanese
Replaces the standard TTS tokenizer within Finetuning.
2024-03-16 11:15:01 +00:00
erew123 a579c5e6d2 Additional checks, documentation & tokenizer
Added warnings if PFC is not passed.
Added a custom tokenizer to better handle Japanese.
2024-03-16 11:14:07 +00:00
erew123 5a21d14672 Update atsetup.bat
TTS version updated to 0.22.0
2024-03-16 02:35:58 +00:00
erew123 f4f5f82bb0 Update atsetup.sh
TTS version updated to 0.22.0
2024-03-16 02:35:39 +00:00
erew123 7b03804f9e Finetune updated
Now uses TTS 0.22.0
Pre-flight check system & help documentation.
Cleaned up the interface a little
More yet to do re documentation.
2024-03-16 02:34:17 +00:00
erew123 f46493e6d8 Updated diagnostic checks
Added a few additional checks
Simplified screen output
Cleaned up log file output
2024-03-15 02:14:58 +00:00
erew123 2a64f6ad2b Merge pull request #127 from arthurwolf/arthurwolf-patch-2-clint-cleaned
Added a cleaned up version of the Clint Eastwood file.
2024-03-14 19:21:04 +00:00
erew123 3368f8b016 Merge pull request #126 from arthurwolf/patch-1
Add note about the project's rate.
2024-03-14 19:19:57 +00:00
Arthur Wolf 121585ae84 Update Clint_Eastwood CC3.txt to mention the updated file. 2024-03-14 14:53:40 +01:00
Arthur Wolf 3e6c11740d Uploading new version of the enhanced clint file, with correct bitrate.
Previous version of the file had wrong bitrate.
2024-03-14 14:51:06 +01:00
Arthur Wolf 6b111494b3 Delete voices/Clint_Eastwood CC3 (enhanced).wav
It had the wrong bitrate, replacing.
2024-03-14 14:50:35 +01:00
Arthur Wolf b376a27da2 Added a cleaned up version of the Clint Eastwood file.
As it's a derivative of the original file, it's automatically under the same license.
2024-03-14 14:42:41 +01:00
Arthur Wolf eeec4de703 Add note about the project's rate.
If this is not set correctly, the file will be saved as the incorrect rate despite the resampling (had to search docs for 30 minutes before solving this one...)
2024-03-14 14:07:45 +01:00
erew123 49f98033b3 Added a few extra notes to instructions 2024-03-13 20:15:40 +00:00
erew123 c1470930f4 Update README.md 2024-03-13 09:26:00 +00:00
erew123 1dec4f027b Added Windows DeepSpeed update for PyTorch 2.2.1 2024-03-12 11:34:35 +00:00
erew123 a9ef8edcb4 Update README.md 2024-03-11 13:33:01 +00:00
erew123 299238be7d Update README.md 2024-03-11 13:13:26 +00:00
erew123 29c7ea1526 Update README.md 2024-03-11 13:00:33 +00:00
erew123 e974e14a10 Links update
Just a minor update to make the links to TTS generator dynamic
2024-03-11 12:51:56 +00:00
erew123 59c654bacd Update README.md 2024-03-11 12:21:58 +00:00
erew123 969b4e3f46 Text-gen-webui - SD Image filtering
AllTalk will now strip any image from TTS and re-add the image data after TTS. 

This is so that when used with Stable Diffusion Image generation within Text-generation-webui, AllTalk isnt trying to generate the Image data as TTS.
2024-03-11 11:22:50 +00:00
erew123 86c64fc9ba Documentation & Japanese support
Improved some areas of the documentation to clarify some issues.
Dropped worker threads down ONLY when Japanese is used, due to limitations of the translation.
2024-03-10 15:17:31 +00:00
erew123 b8c0db6f56 Merge pull request #114 from johnbenac/16_to_32_for_1070
Made compatible with older GPUs (1070)
2024-03-10 15:12:42 +00:00
erew123 57982cb810 Update README.md 2024-03-10 14:54:47 +00:00
erew123 6387007f56 Update README.md 2024-03-10 14:53:26 +00:00
erew123 cdf8d695e2 Update README.md 2024-03-10 14:45:20 +00:00
erew123 833bf3da4f Update README.md 2024-03-10 13:52:41 +00:00
erew123 f68086db0b Update requirements_finetune.txt 2024-03-10 11:58:27 +00:00
johnbenac 1f7842ef5a Made compatible with older GPUs (1070) 2024-03-09 20:42:01 -05:00
79 changed files with 6344 additions and 7676 deletions

No files matched your search

@@ -1,13 +1,14 @@
--- ---
name: Bug report/Support request name: Support request OR Bug report
about: Create a report to help us improve about: Create a report to help us improve
title: '' title: ''
labels: '' labels: ''
assignees: '' 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 https://github.com/erew123/alltalk_tts/tree/main?#-how-to-make-a-diagnostics-report-file
@@ -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);
}
File diff suppressed because it is too large. Load diff
@@ -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);
}
File diff suppressed because it is too large. Load diff
@@ -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()
-18
View File
@@ -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
+861 -479
View File
File diff suppressed because it is too large. Load diff
+260 -126
View File
@@ -61,28 +61,36 @@ echo environment. If you have NOT done this, please run %L_GREEN%cmd_windows.
echo in the %L_GREEN%text-generation-webui%RESET% folder and then re-run this script. echo in the %L_GREEN%text-generation-webui%RESET% folder and then re-run this script.
echo. echo.
echo BASE REQUIREMENTS echo BASE REQUIREMENTS
echo 1) Install the requirements for a %L_GREEN%Nvidia machine%RESET%. echo 1) Apply/Re-Apply the requirements for %L_GREEN%Text-generation-webui%RESET%.
echo 2) Install the requirements for a %L_GREEN%AMD or MAC machine%RESET%.
echo. echo.
echo OPTIONAL echo OPTIONAL
echo 3) Install the Finetuning requirements. echo 2) Git Pull the latest AllTalk updates from Github
echo 4) Install DeepSpeed v11.2 for CUDA %L_GREEN%11.8%RESET% and Python-3.11.x. echo.
echo 5) Install DeepSpeed v11.2 for CUDA %L_GREEN%12.1%RESET% and Python-3.11.x. echo DEEPSPEED FOR %L_YELLOW=%PyTorch 2.1.x%RESET%
echo 6) Uninstall DeepSpeed. 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.
echo. OTHER echo. OTHER
echo 7) Generate a diagnostics file. echo 8) Generate a diagnostics file.
echo. echo.
echo 9) %L_RED%Exit/Quit%RESET% echo 9) %L_RED%Exit/Quit%RESET%
echo. echo.
set /p WebUIOption="Enter your choice: " set /p WebUIOption="Enter your choice: "
if "%WebUIOption%"=="1" goto InstallNvidiaTextGen if "%WebUIOption%"=="1" goto InstallNvidiaTextGen
if "%WebUIOption%"=="2" goto InstallOtherTextGen if "%WebUIOption%"=="2" goto TGGitpull
if "%WebUIOption%"=="3" goto InstallFinetuneTextGen
if "%WebUIOption%"=="4" goto InstallDeepSpeed118TextGen if "%WebUIOption%"=="4" goto InstallDeepSpeed118TextGen
if "%WebUIOption%"=="5" goto InstallDeepSpeed121TextGen if "%WebUIOption%"=="5" goto InstallDeepSpeed121TextGen
if "%WebUIOption%"=="6" goto UnInstallDeepSpeed if "%WebUIOption%"=="6" goto InstallDeepSpeed121TextGenPytorch221
if "%WebUIOption%"=="7" goto GenerateDiagsTextGen 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 if "%WebUIOption%"=="9" goto End
goto WebUIMenu goto WebUIMenu
@@ -95,27 +103,31 @@ echo BASE REQUIREMENTS
echo 1) Install AllTalk as a Standalone Application echo 1) Install AllTalk as a Standalone Application
echo. echo.
echo OPTIONAL echo OPTIONAL
echo 2) Delete AllTalk's custom Python environment echo 2) Git Pull the latest AllTalk updates from Github
echo 3) Install the Finetuning requirements echo 3) Re-Apply/Update the requirements file
echo 4) Delete AllTalk's custom Python environment
echo 5) Purge the PIP cache
echo. echo.
echo. OTHER echo. OTHER
echo 4) Generate a diagnostics file echo 8) Generate a diagnostics file
echo. echo.
echo 9) %L_RED%Exit/Quit%RESET% echo 9) %L_RED%Exit/Quit%RESET%
echo. echo.
set /p StandaloneOption="Enter your choice: " set /p StandaloneOption="Enter your choice: "
if "%StandaloneOption%"=="1" goto InstallCustomStandalone if "%StandaloneOption%"=="1" goto InstallCustomStandalone
if "%StandaloneOption%"=="2" goto DeleteCustomStandalone if "%StandaloneOption%"=="2" goto STGitpull
if "%StandaloneOption%"=="3" goto InstallFinetuneStandalone if "%StandaloneOption%"=="3" goto STReapplyrequirements
if "%StandaloneOption%"=="4" goto GenerateDiagsStandalone if "%StandaloneOption%"=="4" goto STDeleteCustomStandalone
if "%StandaloneOption%"=="5" goto STPurgepipcache
if "%StandaloneOption%"=="8" goto GenerateDiagsStandalone
if "%StandaloneOption%"=="9" goto End if "%StandaloneOption%"=="9" goto End
goto StandaloneMenu goto StandaloneMenu
:InstallNvidiaTextGen :InstallNvidiaTextGen
pip install -r requirements_nvidia.txt pip install -r system\requirements\requirements_textgen.txt
if %ERRORLEVEL% neq 0 ( if %ERRORLEVEL% neq 0 (
echo. echo.
echo There was an error installing the Nvidia requirements. echo There was an error installing the requirements.
echo Press any key to return to the menu. echo Press any key to return to the menu.
echo. echo.
pause pause
@@ -123,42 +135,24 @@ if %ERRORLEVEL% neq 0 (
) )
Echo. Echo.
echo. echo.
Echo Nvidia machine requirements installed successfully. Echo Requirements installed successfully.
Echo. Echo.
pause pause
goto WebUIMenu goto WebUIMenu
:InstallOtherTextGen :TGGitpull
pip install -r requirements_other.txt git pull
if %ERRORLEVEL% neq 0 ( if %ERRORLEVEL% neq 0 (
echo. echo.
echo There was an error installing the Other requirements. echo There was an error pulling from Github.
echo Press any key to return to the menu. echo Press any key to return to the menu.
echo. echo.
pause pause
goto WebUIMenu goto WebUIMenu
) )
Echo. Echo.
echo. echo AllTalk Updated from Github. Please re-apply
Echo Other machine requirements installed successfully. echo the latest requirements file. Option 1
Echo.
pause
goto WebUIMenu
:InstallFinetuneTextGen
pip install -r requirements_finetune.txt
if %ERRORLEVEL% neq 0 (
echo.
echo There was an error installing the Finetune requirements.
echo Press any key to return to the menu.
echo.
pause
goto WebUIMenu
)
Echo.
Echo Finetune requirements installed successfully. Please ensure you have installed CUDA 11.8.
Echo Finetune needs access to %L_GREEN%cublas64_11.dll%RESET% and will error if this is not setup correctly.
Echo Please see here for details %L_GREEN%https://github.com/erew123/alltalk_tts#-finetuning-a-model%RESET%
Echo. Echo.
pause pause
goto WebUIMenu goto WebUIMenu
@@ -227,6 +221,70 @@ del deepspeed-0.11.2+cuda121-cp311-cp311-win_amd64.whl
pause pause
goto WebUIMenu 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 :UnInstallDeepSpeed
pip uninstall deepspeed pip uninstall deepspeed
if %ERRORLEVEL% neq 0 ( if %ERRORLEVEL% neq 0 (
@@ -320,49 +378,20 @@ if not exist "%INSTALL_ENV_DIR%\python.exe" ( echo. && echo Conda environment is
@rem activate installer env @rem activate installer env
call "%CONDA_ROOT_PREFIX%\condabin\conda.bat" activate "%INSTALL_ENV_DIR%" || ( echo. && echo Miniconda hook not found. && goto end ) call "%CONDA_ROOT_PREFIX%\condabin\conda.bat" activate "%INSTALL_ENV_DIR%" || ( echo. && echo Miniconda hook not found. && goto end )
@rem Ask user for the type of requirements to install
echo. echo.
echo %L_BLUE%Choose the type of requirements to install%RESET% 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. echo.
echo 1. Nvidia graphics card machines pip install torch>=2.2.1+cu121 torchaudio>=2.2.1+cu121 --extra-index-url https://download.pytorch.org/whl/cu121
echo 2. Other machines (mac, amd, etc) echo Installing other requirements.
echo. echo.
set /p UserChoice="Enter your choice (1 or 2): " pip install -r system\requirements\requirements_standalone.txt
curl -LO https://github.com/erew123/alltalk_tts/releases/download/DeepSpeed-12.7/deepspeed-0.12.7+d058d4b-cp311-cp311-win_amd64.whl
echo Installing DeepSpeed...
pip install deepspeed-0.12.7+d058d4b-cp311-cp311-win_amd64.whl
del deepspeed-0.12.7+d058d4b-cp311-cp311-win_amd64.whl
@rem Install requirements based on user choice
if "%UserChoice%" == "1" (
echo.
echo Installing Nvidia requirements...
echo Downloading Pytorch with CUDA..
pip install torch==2.1.0 torchvision==0.16.0 torchaudio==2.1.0 --upgrade --force-reinstall --index-url https://download.pytorch.org/whl/cu121
pip install numpy==1.24.4
pip install typing-extensions>=4.8.0
pip install networkx<3.0.0,>=2.5.0
pip install fsspec>=2023.6.0
pip install soundfile==0.12.1
pip install uvicorn==0.24.0.post1
pip install transformers>=4.37.1
pip install TTS==0.21.3
pip install fastapi==0.104.1
pip install Jinja2==3.1.2
pip install requests==2.31.0
pip install tqdm==4.66.1
pip install importlib-metadata==4.8.1
pip install packaging==23.2
pip install pydantic==1.10.13
pip install sounddevice==0.4.6
pip install python-multipart==0.0.6
pip install cutlet>=0.3.0
pip install unidic-lite>=1.0.8
echo Downloading DeepSpeed...
curl -LO https://github.com/erew123/alltalk_tts/releases/download/DeepSpeed-12.7/deepspeed-0.12.7+d058d4b-cp311-cp311-win_amd64.whl
echo Installing DeepSpeed...
pip install deepspeed-0.12.7+d058d4b-cp311-cp311-win_amd64.whl
del deepspeed-0.12.7+d058d4b-cp311-cp311-win_amd64.whl
) else (
echo Installing requirements for other machines...
pip install -r requirements_other.txt
)
@rem Create start_environment.bat to run AllTalk environment @rem Create start_environment.bat to run AllTalk environment
echo @echo off > start_environment.bat echo @echo off > start_environment.bat
@@ -377,14 +406,22 @@ echo set CONDA_ROOT_PREFIX=%cd%\alltalk_environment\conda >> start_alltalk.bat
echo set INSTALL_ENV_DIR=%cd%\alltalk_environment\env >> start_alltalk.bat echo set INSTALL_ENV_DIR=%cd%\alltalk_environment\env >> start_alltalk.bat
echo call "%CONDA_ROOT_PREFIX%\condabin\conda.bat" activate "%INSTALL_ENV_DIR%" >> start_alltalk.bat echo call "%CONDA_ROOT_PREFIX%\condabin\conda.bat" activate "%INSTALL_ENV_DIR%" >> start_alltalk.bat
echo call python script.py >> start_alltalk.bat echo call python script.py >> start_alltalk.bat
@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.
Echo start_alltalk.bat has been created. Echo Run %L_YELLOW%start_alltalk.bat%RESET% to start AllTalk.
Echo You can now start AllTalk with %L_YELLOW%start_alltalk.bat%RESET% 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. Echo.
pause pause
goto StandaloneMenu goto StandaloneMenu
:DeleteCustomStandalone :STDeleteCustomStandalone
@rem Check if the alltalk_environment directory exists @rem Check if the alltalk_environment directory exists
if not exist "%cd%\alltalk_environment\" ( if not exist "%cd%\alltalk_environment\" (
echo. echo.
@@ -418,47 +455,6 @@ Echo.
pause pause
goto StandaloneMenu goto StandaloneMenu
:InstallFinetuneStandalone
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 Proceed with installing requirements
pip install -r requirements_finetune.txt
if %ERRORLEVEL% neq 0 (
echo.
echo There was an error installing the Finetune requirements.
echo Press any key to return to the menu.
echo.
pause
goto StandaloneMenu
)
Echo.
Echo Finetune requirements installed successfully. Please ensure you have installed CUDA 11.8.
Echo Finetune needs access to %L_GREEN%cublas64_11.dll%RESET% and will error if this is not setup correctly.
Echo Please see here for details %L_GREEN%https://github.com/erew123/alltalk_tts#-finetuning-a-model%RESET%
Echo.
pause
goto StandaloneMenu
:GenerateDiagsStandalone :GenerateDiagsStandalone
cd /D "%~dp0" cd /D "%~dp0"
set CONDA_ROOT_PREFIX=%cd%\alltalk_environment\conda set CONDA_ROOT_PREFIX=%cd%\alltalk_environment\conda
@@ -499,6 +495,144 @@ Echo.
pause pause
goto StandaloneMenu 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.1+cu121 torchaudio>=2.2.1+cu121 --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-12.7/deepspeed-0.12.7+d058d4b-cp311-cp311-win_amd64.whl
echo Installing DeepSpeed...
pip install deepspeed-0.12.7+d058d4b-cp311-cp311-win_amd64.whl
del deepspeed-0.12.7+d058d4b-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
:End :End
echo Exiting AllTalk Setup Utility... echo Exiting AllTalk Setup Utility...
exit /b exit /b
+154 -111
View File
@@ -64,11 +64,12 @@ webui_menu() {
echo -e " in the ${L_GREEN}text-generation-webui${NC} folder and then re-run this script." echo -e " in the ${L_GREEN}text-generation-webui${NC} folder and then re-run this script."
echo echo
echo " BASE REQUIREMENTS" echo " BASE REQUIREMENTS"
echo -e " 1) Install the AllTalk requirements for an ${L_GREEN}Nvidia GPU machine.${NC}" echo -e " 1) Apply/Re-Apply the requirements for an ${L_GREEN}Text-generation-webui${NC}"
echo -e " 2) Install the AllTalk requirements for an ${L_GREEN}AMD or MAC machine.${NC}"
echo echo
echo " OPTIONAL" echo " OPTIONAL"
echo " 3) Install the Finetuning requirements." echo " 2) Git Pull the latest AllTalk updates from Github"
echo
echo " DEEPSPEED"
echo " 4) Install DeepSpeed." echo " 4) Install DeepSpeed."
echo " 5) Uninstall DeepSpeed." echo " 5) Uninstall DeepSpeed."
echo echo
@@ -81,8 +82,7 @@ webui_menu() {
case $webui_option in case $webui_option in
1) install_nvidia_textgen ;; 1) install_nvidia_textgen ;;
2) install_other_textgen ;; 2) tg_gitpull ;;
3) install_finetune_textgen ;;
4) install_deepspeed ;; 4) install_deepspeed ;;
5) uninstall_deepspeed ;; 5) uninstall_deepspeed ;;
6) generate_diagnostics_textgen ;; 6) generate_diagnostics_textgen ;;
@@ -93,52 +93,40 @@ webui_menu() {
} }
install_nvidia_textgen() { install_nvidia_textgen() {
local requirements_file="requirements_nvidia.txt" local requirements_file="system/requirements/requirements_textgen.txt"
echo " Installing Finetune requirements from $requirements_file..." echo " Installing Requirements from $requirements_file..."
if ! pip install -r "$requirements_file"; then if ! pip install -r "$requirements_file"; then
echo " There was an error installing the Nvidia requirements." echo
echo " There was an error pulling from Github."
echo " Please check the output for details." echo " Please check the output for details."
read -p " Press any key to return to the menu. " -n 1 read -p " Press any key to return to the menu. " -n 1
echo echo
return return
fi fi
echo " Nvidia requirements installed successfully." 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 read -p " Press any key to continue. " -n 1
echo echo
} }
install_other_textgen() { tg_gitpull() {
local requirements_file="requirements_other.txt"
echo " Installing Other machine requirements from $requirements_file..."
if ! pip install -r "$requirements_file"; then
echo " There was an error installing the Other requirements."
echo " Please check the output for details."
read -p " Press any key to return to the menu. " -n 1
echo
return
fi
echo " Other machine requirements installed successfully."
read -p " Press any key to continue. " -n 1
echo echo
} if ! git pull; then
echo
install_finetune_textgen() { echo " There was an error installing the requirements."
local requirements_file="requirements_finetune.txt"
echo " Installing Finetune requirements from $requirements_file..."
if ! pip install -r "$requirements_file"; then
echo " There was an error installing the Finetune requirements."
echo " Please check the output for details." echo " Please check the output for details."
echo
read -p " Press any key to return to the menu. " -n 1 read -p " Press any key to return to the menu. " -n 1
echo echo
return return
fi fi
echo echo
echo " Finetune base requirements installed successfully." echo " AllTalk Updated from Github. Please re-apply"
echo echo " the latest requirements file. (Option 1)"
echo -e " Finetuning requires access to ${GREEN}Cublas${NC} from the ${GREEN}Nvidia CUDA Toolkit${NC}, so will need installing."
echo
echo -e " Instructions can be found here:"
echo -e " ${GREEN}https://github.com/erew123/alltalk_tts?#-finetuning-a-model${NC}"
echo echo
read -p " Press any key to continue. " -n 1 read -p " Press any key to continue. " -n 1
echo echo
@@ -185,12 +173,16 @@ uninstall_deepspeed() {
echo "Uninstalling DeepSpeed..." echo "Uninstalling DeepSpeed..."
pip uninstall -y deepspeed pip uninstall -y deepspeed
if [ $? -ne 0 ]; then if [ $? -ne 0 ]; then
echo
echo " There was an error uninstalling DeepSpeed." echo " There was an error uninstalling DeepSpeed."
echo
echo " Press any key to return to the menu." echo " Press any key to return to the menu."
read -n 1 read -n 1
return return
fi fi
echo
echo " DeepSpeed uninstalled successfully." echo " DeepSpeed uninstalled successfully."
echo
echo " Press any key to continue." echo " Press any key to continue."
read -n 1 read -n 1
} }
@@ -198,13 +190,17 @@ uninstall_deepspeed() {
generate_diagnostics_textgen() { generate_diagnostics_textgen() {
# Run diagnostics # Run diagnostics
if ! python diagnostics.py; then if ! python diagnostics.py; then
echo
echo " There was an error running diagnostics." echo " There was an error running diagnostics."
echo
read -p " Press any key to return to the menu. " -n 1 read -p " Press any key to return to the menu. " -n 1
echo echo
return return
fi fi
echo
echo " Diagnostics completed successfully." echo " Diagnostics log file generated successfully."
echo " Please see diagnostics.log"
echo
read -p " Press any key to continue. " -n 1 read -p " Press any key to continue. " -n 1
echo echo
} }
@@ -220,12 +216,16 @@ standalone_menu() {
echo " 1) Install AllTalk as a Standalone Application" echo " 1) Install AllTalk as a Standalone Application"
echo echo
echo " OPTIONAL" echo " OPTIONAL"
echo " 2) Delete AllTalk's custom Python environment" echo " 2) Git Pull the latest AllTalk updates from Github"
echo " 3) Install the Finetuning requirements" echo " 3) Re-Apply/Update the requirements file"
echo " 4) Install DeepSpeed." echo " 4) Delete AllTalk's custom Python environment"
echo " 5) Purge the PIP cache"
echo
echo " DEEPSPEED"
echo " 6) DeepSpeed Instructions/Install"
echo echo
echo " OTHER" echo " OTHER"
echo " 5) Generate a diagnostics file" echo " 8) Generate a diagnostics file"
echo echo
echo -e " 9)${L_RED} Exit/Quit${NC}" echo -e " 9)${L_RED} Exit/Quit${NC}"
echo echo
@@ -233,16 +233,19 @@ standalone_menu() {
case $standalone_option in case $standalone_option in
1) install_custom_standalone ;; 1) install_custom_standalone ;;
2) delete_custom_standalone ;; 2) gitpull_standalone ;;
3) install_finetune_standalone ;; 3) reapply_standalone ;;
4) install_deepspeed ;; 4) delete_custom_standalone ;;
5) generate_diagnostics_standalone ;; 5) pippurge_standalone ;;
6) install_deepspeed ;;
8) generate_diagnostics_standalone ;;
9) exit 0 ;; 9) exit 0 ;;
*) echo "Invalid option"; sleep 2 ;; *) echo "Invalid option"; sleep 2 ;;
esac esac
done done
} }
install_custom_standalone() { install_custom_standalone() {
cd "$(dirname "${BASH_SOURCE[0]}")" cd "$(dirname "${BASH_SOURCE[0]}")"
@@ -284,39 +287,15 @@ install_custom_standalone() {
# Activate the environment and install requirements # Activate the environment and install requirements
source "${CONDA_ROOT_PREFIX}/etc/profile.d/conda.sh" source "${CONDA_ROOT_PREFIX}/etc/profile.d/conda.sh"
conda activate "${INSTALL_ENV_DIR}" conda activate "${INSTALL_ENV_DIR}"
echo echo
echo -e " ${L_BLUE}Choose the type of requirements to install${NC}" 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 --extra-index-url https://download.pytorch.org/whl/cu121
echo echo
echo " 1. Nvidia graphics card machines" echo " Installing additional requirements."
echo " 2. Other machines (mac, amd, etc)"
echo echo
read -p " Enter your choice (1 or 2): " user_choice pip install -r system/requirements/requirements_standalone.txt
if [ "$user_choice" == "1" ]; then
pip install torch==2.1.0 torchvision==0.16.0 torchaudio==2.1.0 --upgrade --force-reinstall --index-url https://download.pytorch.org/whl/cu121
pip install numpy==1.24.4
pip install typing-extensions>=4.8.0
pip install networkx<3.0.0,>=2.5.0
pip install fsspec>=2023.6.0
pip install soundfile==0.12.1
pip install uvicorn==0.24.0.post1
pip install transformers>=4.37.1
pip install TTS==0.21.3
pip install fastapi==0.104.1
pip install Jinja2==3.1.2
pip install requests==2.31.0
pip install tqdm==4.66.1
pip install importlib-metadata==4.8.1
pip install packaging==23.2
pip install pydantic==1.10.13
pip install sounddevice==0.4.6
pip install python-multipart==0.0.6
pip install cutlet>=0.3.0
pip install unidic-lite>=1.0.8
else
pip install -r requirements_other.txt
fi
# Create start_environment.sh to run AllTalk # Create start_environment.sh to run AllTalk
cat << EOF > start_environment.sh cat << EOF > start_environment.sh
#!/bin/bash #!/bin/bash
@@ -333,27 +312,42 @@ unset PYTHONPATH
unset PYTHONHOME unset PYTHONHOME
export CUDA_PATH="$INSTALL_ENV_DIR" export CUDA_PATH="$INSTALL_ENV_DIR"
export CUDA_HOME="$CUDA_PATH" 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 # activate env
bash --init-file <(echo "source \"$CONDA_ROOT_PREFIX/etc/profile.d/conda.sh\" && conda activate \"$INSTALL_ENV_DIR\"") bash --init-file <(echo "source \"$CONDA_ROOT_PREFIX/etc/profile.d/conda.sh\" && conda activate \"$INSTALL_ENV_DIR\"")
EOF EOF
# Create start_alltalk.sh to run AllTalk # Create start_alltalk.sh to run AllTalk
cat << EOF > start_alltalk.sh cat << EOF > start_alltalk.sh
#!/bin/bash #!/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" source "${CONDA_ROOT_PREFIX}/etc/profile.d/conda.sh"
conda activate "${INSTALL_ENV_DIR}" conda activate "${INSTALL_ENV_DIR}"
python script.py 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 EOF
chmod +x start_alltalk.sh chmod +x start_alltalk.sh
chmod +x start_environment.sh chmod +x start_environment.sh
chmod +x start_finetune.sh
echo echo
echo echo
echo -e " start_alltalk.sh has been created." echo -e " Run ${L_YELLOW}./start_alltalk.sh${NC} to start AllTalk."
echo -e " You can now start AllTalk with ${L_YELLOW}./start_alltalk.sh${NC}" 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 echo
read -p " Press any key to continue. " -n 1 read -p " Press any key to continue. " -n 1
} }
delete_custom_standalone() { delete_custom_standalone() {
local env_dir="$PWD/alltalk_environment" local env_dir="$PWD/alltalk_environment"
# Check if the alltalk_environment directory exists # Check if the alltalk_environment directory exists
@@ -380,47 +374,15 @@ delete_custom_standalone() {
echo echo
} }
install_finetune_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 " The Conda environment at '${install_env_dir}' does not exist."
echo " Please install the environment before proceeding."
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}"
local requirements_file="requirements_finetune.txt"
echo "Installing Finetune requirements from $requirements_file..."
if ! pip install -r "$requirements_file"; then
echo " There was an error installing the Finetune requirements."
echo " Please check the output for details."
read -p " Press any key to return to the menu. " -n 1
echo
return
fi
echo
echo " Finetune base requirements installed successfully."
echo
echo -e " Finetuning requires access to ${GREEN}Cublas${NC} from the ${GREEN}Nvidia CUDA Toolkit${NC}, so will need installing."
echo
echo -e " Instructions can be found here:"
echo -e " ${GREEN}https://github.com/erew123/alltalk_tts?#-finetuning-a-model${NC}"
echo
read -p " Press any key to continue. " -n 1
echo
}
generate_diagnostics_standalone() { generate_diagnostics_standalone() {
local env_dir="$PWD/alltalk_environment" local env_dir="$PWD/alltalk_environment"
local conda_root_prefix="${env_dir}/conda" local conda_root_prefix="${env_dir}/conda"
local install_env_dir="${env_dir}/env" local install_env_dir="${env_dir}/env"
if [ ! -d "${install_env_dir}" ]; then if [ ! -d "${install_env_dir}" ]; then
echo
echo " The Conda environment at '${install_env_dir}' does not exist." echo " The Conda environment at '${install_env_dir}' does not exist."
echo " Please install the environment before proceeding." echo " Please install the environment before proceeding."
echo
read -p " Press any key to return to the menu. " -n 1 read -p " Press any key to return to the menu. " -n 1
echo echo
return return
@@ -428,16 +390,97 @@ generate_diagnostics_standalone() {
source "${conda_root_prefix}/etc/profile.d/conda.sh" source "${conda_root_prefix}/etc/profile.d/conda.sh"
conda activate "${install_env_dir}" conda activate "${install_env_dir}"
if ! python diagnostics.py; then if ! python diagnostics.py; then
echo
echo " There was an error running diagnostics." echo " There was an error running diagnostics."
echo
read -p " Press any key to return to the menu. " -n 1 read -p " Press any key to return to the menu. " -n 1
echo echo
return return
fi fi
echo
echo " Diagnostics completed successfully." echo " Diagnostics completed successfully."
read -p " Press any key to continue. " -n 1 read -p " Press any key to continue. " -n 1
echo 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 --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 # Start the main menu
main_menu main_menu
+121 -35
View File
@@ -8,12 +8,15 @@ try:
import re import re
import sys import sys
import glob import glob
import site
import textwrap import textwrap
import torch
import packaging.version import packaging.version
import packaging.specifiers import packaging.specifiers
from packaging.specifiers import SpecifierSet from packaging.specifiers import SpecifierSet
from packaging.specifiers import SpecifierSet from packaging.specifiers import SpecifierSet
from packaging.version import parse as parse_version from packaging.version import parse as parse_version
from pathlib import Path
except ImportError as e: except ImportError as e:
print(f"\033[91mError importing module: {e}\033[0m\n") 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") 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") print("\033[94mfrom the text-generation-webui directory, and then try running the diagnostics again.\033[0m")
exit(1) exit(1)
this_dir = Path(__file__).parent
try: try:
import psutil import psutil
except ImportError: except ImportError:
@@ -29,24 +34,30 @@ except ImportError:
import psutil import psutil
def get_requirements_file(): 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: if not requirements_files:
print("\033[91mNo requirements files found.\033[0m") print("\033[91mNo requirements files found.\033[0m")
return None return None
print("\033[94m\nSelect a requirements file to check against (or press Enter for default 'requirements.txt'):\033[0m\n") 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): for i, file_path in enumerate(requirements_files, start=1):
print(f"{i}. {file}") print(f" {i}. {file_path.name}") # Only print the filename
choice = input("\nEnter the number of your choice: ") choice = input("\nEnter the number of your choice: ")
try: try:
choice = int(choice) - 1 choice = int(choice) - 1
return requirements_files[choice] return str(requirements_files[choice]) # Return the full path as a string
except (ValueError, IndexError): 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' # 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 # Function to get GPU information using subprocess
def get_gpu_info(): def get_gpu_info():
@@ -56,6 +67,22 @@ def get_gpu_info():
except FileNotFoundError: except FileNotFoundError:
return "NVIDIA GPU information not available" 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 # Function to check if a port is in use
def is_port_in_use(port): def is_port_in_use(port):
for conn in psutil.net_connections(): for conn in psutil.net_connections():
@@ -73,6 +100,38 @@ def satisfies_wildcard(installed_version, required_version):
return True return True
return False 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 # Function to log and print system information
def log_system_info(): def log_system_info():
# System information # System information
@@ -88,6 +147,9 @@ def log_system_info():
# Torch version # Torch version
torch_version = 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 # System RAM using psutil
try: try:
@@ -123,6 +185,10 @@ def log_system_info():
search_path = sys.path search_path = sys.path
path_env = os.environ.get('PATH', 'N/A') 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 # Compare with requirements file
requirements_file = get_requirements_file() requirements_file = get_requirements_file()
if requirements_file: if requirements_file:
@@ -146,52 +212,72 @@ def log_system_info():
logging.info(f"NOTE {requirements_file} not found. Skipping version checks.") logging.info(f"NOTE {requirements_file} not found. Skipping version checks.")
# Log and print information # Log and print information
logging.info(f"OS Version: {os_version}") logging.info(f"OPERATING SYSTEM:")
logging.info(f"Note: Windows 11 will list as build is 10.x.22xxx") logging.info(f" OS Version: {os_version}")
logging.info(f"Torch Version: {torch_version}") logging.info(f" Note: Windows 11 will list as build is 10.x.22xxx")
logging.info(f"System RAM: {system_ram}") logging.info(f"\nHARDWARE ENVIRONMENT:")
logging.info(f"CUDA_HOME: {cuda_home}") 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"Port Status: {port_status}") logging.info(f" System RAM: {system_ram}")
logging.info(f"Python Version: {platform.python_version()}") logging.info(f"\nGPU INFORMATION:{gpu_info}")
logging.info(f"Python Version Info: {python_version_info}") logging.info("DISK INFORMATION:")
logging.info(f"Python Executable: {python_executable}") for disk in disk_info:
logging.info(f"Python Virtual Environment: {python_virtual_env} (Should be N/A when in Text-generation-webui Conda Python environment)") logging.info(disk)
logging.info(f"Conda Environment: {conda_env}") 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:") logging.info("\nPython Search Path:")
for path in search_path: for path in search_path:
logging.info(f" {path}") logging.info(f" {path}")
logging.info("\nOS PATH Environment Variable:") logging.info("\nOS SEARCHPATH ENVIRONMENT:")
for path in path_env.split(';'): for path in path_env.split(';'):
logging.info(f" {path}") logging.info(f" {path}")
if required_packages: # Check if the dictionary is not empty 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()) max_package_length = max(len(package) for package in required_packages.keys())
for package_name, (operator, required_version) in required_packages.items(): for package_name, (operator, required_version) in required_packages.items():
installed_version = installed_packages.get(package_name, 'Not installed') 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" {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("\nPYTHON PACKAGES:")
logging.info("Package Versions:")
for package, version in package_versions.items(): for package, version in package_versions.items():
logging.info(f"{package}>= {version}") logging.info(f" {package}>= {version}")
# Print to screen # Print to screen
print(f"\n\033[94mOS Version:\033[0m \033[92m{os_version}\033[0m") 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[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[94mSystem RAM:\033[0m \033[92m{system_ram}\033[0m")
print(f"\033[94mPort Status:\033[0m \033[92m{port_status}\033[0m") for disk in disk_info:
print(f"\033[94mTorch Version:\033[0m \033[92m{torch_version}\033[0m") 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:\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 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"\033[94mConda Environment:\033[0m \033[92m{conda_env}\033[0m")
print(f"\n\033[94mPython Search Path:\033[0m") print(f"\n\033[94mPython Search Path:\033[0m")
for path in search_path: for path in search_path:
print(f" {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 if required_packages: # Check if the dictionary is not empty
print("\033[94m\nRequirements file package comparison:\033[0m") print("\033[94m\nRequirements file package comparison:\033[0m")
max_package_length = max(len(package) for package in required_packages.keys()) max_package_length = max(len(package) for package in required_packages.keys())
@@ -214,7 +300,7 @@ def log_system_info():
color_installed = "\033[92m" if condition_met else "\033[91m" color_installed = "\033[92m" if condition_met else "\033[91m"
# Print colored output # 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("\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("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")
@@ -231,9 +317,9 @@ def log_system_info():
""") """)
print(explanation.strip()) print(explanation.strip())
print("") print("")
print(f"GPU Information:{gpu_info}") 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[94mDiagnostic log created:\033[0m \033[92mdiagnostics.log\033[0m") print(f"\033[94mscreen. Please see the log file for more detail.\033[0m")
print(f"\033[94mPlease upload the log file with any support ticket.\033[0m") print(f"\033[94m\nPlease upload the log file with any support ticket.\033[0m")
if __name__ == "__main__": if __name__ == "__main__":
log_system_info() log_system_info()
+523 -158
View File
@@ -1,6 +1,8 @@
import argparse import argparse
import os import os
import sys import sys
import platform
import site
import tempfile import tempfile
import signal import signal
import gradio as gr import gradio as gr
@@ -13,14 +15,18 @@ import random
import gc import gc
import time import time
import shutil import shutil
import psutil
import pandas import pandas
import glob import glob
import json import json
from pathlib import Path from pathlib import Path
from tqdm import tqdm from tqdm import tqdm
from faster_whisper import WhisperModel from faster_whisper import WhisperModel
from TTS.tts.layers.xtts.tokenizer import multilingual_cleaners # Use a local Tokenizer to resolve Japanese support
# from TTS.tts.layers.xtts.tokenizer import multilingual_cleaners
from system.ft_tokenizer.tokenizer import multilingual_cleaners
import importlib.metadata as metadata
from packaging import version
# STARTUP VARIABLES # STARTUP VARIABLES
this_dir = Path(__file__).parent.resolve() this_dir = Path(__file__).parent.resolve()
@@ -30,6 +36,7 @@ progress = 0
theme = gr.themes.Default() theme = gr.themes.Default()
refresh_symbol = '🔄' refresh_symbol = '🔄'
os.environ['TRAINER_TELEMETRY'] = '0' os.environ['TRAINER_TELEMETRY'] = '0'
pfc_status = "pass"
# Define the path to the modeldownload config file file # Define the path to the modeldownload config file file
modeldownload_config_file_path = this_dir / "modeldownload.json" modeldownload_config_file_path = this_dir / "modeldownload.json"
@@ -62,6 +69,215 @@ if finetuned_model:
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)
basemodel_or_finetunedmodel = True basemodel_or_finetunedmodel = True
#######################
#### DIAGS for PFC ####
#######################
def check_disk_space():
global pfc_status
# Get the current working directory
current_directory = os.getcwd()
# Get the disk usage statistics for the current directory's disk
disk_usage = shutil.disk_usage(current_directory)
# Convert the free space to GB (1GB = 1 << 30 bytes)
free_space_gb = disk_usage.free / (1 << 30)
# Check if the free space is more than 18GB
is_more_than_18gb = free_space_gb > 18
disk_space_icon = "✅"
if not is_more_than_18gb:
disk_space_icon ="❌"
pfc_status = "fail" # Update global status if disk space check fails
# Generating the markdown text for disk space check
disk_space_markdown = f"""
### 🟩 <u>Disk Space Check</u>
&nbsp;&nbsp;&nbsp;&nbsp; {disk_space_icon} **Disk Space (> 18 GB):** {'' if is_more_than_18gb else 'You have less than 18GB on this disk '} {free_space_gb:.2f} GB
"""
return disk_space_markdown
def test_cuda():
global pfc_status
cuda_home = os.environ.get('CUDA_HOME', 'N/A')
cuda_available = torch.cuda.is_available()
if cuda_available:
try:
# Attempt to create a tensor on GPU
torch.tensor([1.0, 2.0]).cuda()
cuda_status = "CUDA is available and working."
cuda_icon = "✅"
except Exception as e:
cuda_status = f"CUDA is available but not working. Error: {e}"
cuda_icon = "❌"
pfc_status = "fail" # Update global status
else:
cuda_status = "CUDA is not available."
pfc_status = "fail" # Update global status
return cuda_status, cuda_icon, cuda_home
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 generate_cuda_markdown():
global pfc_status
cuda_status, cuda_icon, cuda_home = test_cuda()
file_name = 'cublas64_11.*' if platform.system() == "Windows" else 'libcublas.so.11*'
found_paths = find_files_in_path_with_wildcard(file_name)
if found_paths:
found_paths_str = ' '.join(found_paths)
found_path_icon = '✅'
else:
found_paths_str = "cublas64_11 is not accessible."
found_path_icon = '❌'
pfc_status = "fail" # Update global status
# Check if 'cu118' or 'cu121' is in the PyTorch version string
pytorch_version = torch.__version__
if 'cu118' in pytorch_version or 'cu121' in pytorch_version:
pytorch_cuda_version_status = ''
pytorch_icon = '✅'
else:
pytorch_cuda_version_status = 'Pytorch CUDA version problem '
pytorch_icon = '❌'
pfc_status = "fail" # Update global status
cuda_markdown = f"""
### 🟨 <u>CUDA Information</u><br>
&nbsp;&nbsp;&nbsp;&nbsp; {found_path_icon} **Cublas64_11 found:** {found_paths_str}
&nbsp;&nbsp;&nbsp;&nbsp; {pytorch_icon} **CUDA_HOME path:** {cuda_home}
"""
pytorch_markdown = f"""
### 🟦 <u>Python & Pytorch Information</u>
&nbsp;&nbsp;&nbsp;&nbsp; {pytorch_icon} **PyTorch Version:** {pytorch_cuda_version_status} {torch.__version__}
&nbsp;&nbsp;&nbsp;&nbsp; {cuda_icon} **CUDA is working:** {cuda_status}
"""
return cuda_markdown, pytorch_markdown
def get_system_ram_markdown():
global pfc_status
virtual_memory = psutil.virtual_memory()
total_ram_gb = virtual_memory.total / (1024 ** 3)
available_ram_gb = virtual_memory.available / (1024 ** 3)
used_ram_percentage = virtual_memory.percent
# Check if the available RAM is less than 8GB
warning_if_low_ram = available_ram_gb < 8
# Decide the message based on the available RAM
ram_status_message = "Warning" if warning_if_low_ram else ""
ram_status_icon = "⚠️" if warning_if_low_ram else "✅"
if torch.cuda.is_available():
gpu_device_id = torch.cuda.current_device()
gpu_device_name = torch.cuda.get_device_name(gpu_device_id)
# Get the total and available memory in bytes, then convert to GB
gpu_total_mem_gb = torch.cuda.get_device_properties(gpu_device_id).total_memory / (1024 ** 3)
# gpu_available_mem_gb = (torch.cuda.get_device_properties(gpu_device_id).total_memory - torch.cuda.memory_allocated(gpu_device_id)) / (1024 ** 3)
# gpu_available_mem_gb = (torch.cuda.get_device_properties(gpu_device_id).total_memory - torch.cuda.memory_reserved(gpu_device_id)) / (1024 ** 3)
gpu_reserved_mem_gb = torch.cuda.memory_reserved(gpu_device_id) / (1024 ** 3)
gpu_available_mem_gb = gpu_total_mem_gb - gpu_reserved_mem_gb
# Check if total or available memory is less than 11 GB and set icons
gpu_total_status_icon = "⚠️" if gpu_total_mem_gb < 12 else "✅"
gpu_available_status_icon = "⚠️" if gpu_available_mem_gb < 12 else "✅"
gpu_status_icon = "✅"
else:
gpu_status_icon = "⚠️"
gpu_device_name = "Cannot detect a CUDA card"
gpu_total_mem_gb = "Cannot detect a CUDA card"
gpu_available_mem_gb = "Cannot detect a CUDA card"
gpu_total_status_icon = gpu_status_icon
gpu_available_status_icon = gpu_status_icon
system_ram_markdown = f"""
### 🟪 <u>System RAM and VRAM Information</u> <br>
&nbsp;&nbsp;&nbsp;&nbsp; {ram_status_icon} **Total RAM:** {total_ram_gb:.2f} GB<br>
&nbsp;&nbsp;&nbsp;&nbsp; {ram_status_icon} **Available RAM:** {ram_status_message + ' - Available RAM is less than 8 GB. You have ' if warning_if_low_ram else ''} {available_ram_gb:.2f} GB available ({used_ram_percentage:.2f}% used)<br><br>
&nbsp;&nbsp;&nbsp;&nbsp; {gpu_status_icon} **GPU Name:** {gpu_device_name}<br>
&nbsp;&nbsp;&nbsp;&nbsp; {gpu_total_status_icon} **GPU Total RAM:** {gpu_total_mem_gb:.2f} GB<br>
&nbsp;&nbsp;&nbsp;&nbsp; {gpu_available_status_icon} **GPU Available RAM:** {gpu_available_mem_gb:.2f} GB<br>
"""
return system_ram_markdown
def check_base_model(base_model_path, files_to_download):
global pfc_status
# Assuming files_to_download is a dict with keys as filenames
base_model_files = list(files_to_download.keys())
missing_files = []
# Check if all base model files exist
for file in base_model_files:
file_path = this_dir / base_path / model_path / file
if not file_path.exists():
missing_files.append(file)
pfc_status = "fail"
return len(missing_files) == 0
# Assuming base_model_path and files_to_download are set from the JSON config as shown above
base_model_detected = check_base_model(base_model_path, files_to_download)
def generate_base_model_markdown(base_model_detected):
global pfc_status
base_model_status = 'Base model detected' if base_model_detected else 'Base model not detected'
base_model_icon = '✅' if base_model_detected else '❌'
base_model_markdown = f"""
### ⬛ <u>XTTS Base Model Detection</u>
&nbsp;&nbsp;&nbsp;&nbsp; {base_model_icon} **Base XTTS Model Status:** {base_model_status}
"""
return base_model_markdown
def check_tts_version(required_version="0.22.0"):
global pfc_status
try:
# Get the installed version of TTS
installed_version = metadata.version("tts")
# Check if the installed version meets the required version
if version.parse(installed_version) >= version.parse(required_version):
tts_status = f"TTS version {installed_version} is installed and meets the requirement."
tts_status_icon = "✅"
else:
tts_status = f"❌ Fail - TTS version {installed_version} is installed but does not meet the required version {required_version}."
tts_status_icon = "❌"
pfc_status = "fail" # Update global status
except metadata.PackageNotFoundError:
# If TTS is not installed
tts_status = "TTS is not installed."
pfc_status = "fail" # Update global status
tts_markdown = f"""
### 🟥 <u>TTS Information</u><br>
&nbsp;&nbsp;&nbsp;&nbsp; {tts_status_icon} **TTS Version:** {tts_status}
"""
return tts_markdown
# Disk space check results to append to the Markdown
disk_space_results = check_disk_space()
cuda_results, pytorch_results = generate_cuda_markdown()
system_ram_results = get_system_ram_markdown()
base_model_results = generate_base_model_markdown(base_model_detected)
tts_version_status = check_tts_version()
def pfc_check_fail():
global pfc_status
if pfc_status == "fail":
print("[FINETUNE]")
print("[FINETUNE] \033[91m****** WARNING PRE-FLIGHT CHECKS FAILED ******* WARNING PRE-FLIGHT CHECKS FAILED *****\033[0m")
print("[FINETUNE] \033[91m* Please refer to the \033[93mPre-flight check tab \033[91mand resolve any issues before continuing. *\033[0m")
print("[FINETUNE] \033[91m*********** Expect errors and failures if you do not resolve these issues. ***********\033[0m")
print("[FINETUNE]")
return
##################### #####################
#### STEP 1 BITS #### #### STEP 1 BITS ####
##################### #####################
@@ -76,6 +292,7 @@ def create_temporary_file(folder, suffix=".wav"):
return os.path.join(folder, unique_filename) return os.path.join(folder, unique_filename)
def format_audio_list(target_language, whisper_model, out_path, eval_split_number, gradio_progress=progress): def format_audio_list(target_language, whisper_model, out_path, eval_split_number, gradio_progress=progress):
pfc_check_fail()
audio_files = [os.path.join(audio_folder, file) for file in os.listdir(audio_folder) if file.endswith(('.mp3', '.flac', '.wav'))] audio_files = [os.path.join(audio_folder, file) for file in os.listdir(audio_folder) if file.endswith(('.mp3', '.flac', '.wav'))]
buffer=0.2 buffer=0.2
eval_percentage = eval_split_number / 100.0 eval_percentage = eval_split_number / 100.0
@@ -110,7 +327,8 @@ def format_audio_list(target_language, whisper_model, out_path, eval_split_numbe
device = "cuda" if torch.cuda.is_available() else "cpu" device = "cuda" if torch.cuda.is_available() else "cpu"
print("[FINETUNE] Loading Whisper Model:", whisper_model) print("[FINETUNE] Loading Whisper Model:", whisper_model)
asr_model = WhisperModel(whisper_model, device=device, compute_type="float16") print("[FINETUNE] Model will be downloaded if its not available, which will take a few minutes.")
asr_model = WhisperModel(whisper_model, device=device, compute_type="float32")
metadata = {"audio_file": [], "text": [], "speaker_name": []} metadata = {"audio_file": [], "text": [], "speaker_name": []}
@@ -287,6 +505,8 @@ def format_audio_list(target_language, whisper_model, out_path, eval_split_numbe
# deallocate VRAM and RAM # deallocate VRAM and RAM
del asr_model, final_eval_set, final_training_set, new_data_df, existing_metadata del asr_model, final_eval_set, final_training_set, new_data_df, existing_metadata
gc.collect() gc.collect()
if torch.cuda.is_available():
torch.cuda.empty_cache()
existing_train_df = None existing_train_df = None
existing_eval_df = None existing_eval_df = None
print("[FINETUNE] Train CSV:", train_metadata_path) print("[FINETUNE] Train CSV:", train_metadata_path)
@@ -314,6 +534,7 @@ def basemodel_or_finetunedmodel_choice(value):
basemodel_or_finetunedmodel = False basemodel_or_finetunedmodel = False
def train_gpt(language, num_epochs, batch_size, grad_acumm, train_csv, eval_csv, output_path, max_audio_length=255995): def train_gpt(language, num_epochs, batch_size, grad_acumm, train_csv, eval_csv, output_path, max_audio_length=255995):
pfc_check_fail()
# Logging parameters # Logging parameters
RUN_NAME = "XTTS_FT" RUN_NAME = "XTTS_FT"
PROJECT_NAME = "XTTS_trainer" PROJECT_NAME = "XTTS_trainer"
@@ -323,6 +544,27 @@ def train_gpt(language, num_epochs, batch_size, grad_acumm, train_csv, eval_csv,
# Set here the path that the checkpoints will be saved. Default: ./training/ # Set here the path that the checkpoints will be saved. Default: ./training/
OUT_PATH = os.path.join(output_path, "training") OUT_PATH = os.path.join(output_path, "training")
print("[FINETUNE] \033[94mStarting Step 2\033[0m - Fine-tuning the XTTS Encoder") print("[FINETUNE] \033[94mStarting Step 2\033[0m - Fine-tuning the XTTS Encoder")
print(f"[FINETUNE] \033[94mLanguage: \033[92m{language} \033[94mEpochs: \033[92m{num_epochs} \033[94mBatch size: \033[92m{batch_size}\033[0m \033[94mGrad accumulation steps: \033[92m{grad_acumm}\033[0m")
print(f"[FINETUNE] \033[94mTraining : \033[92m{train_csv}\033[0m")
print(f"[FINETUNE] \033[94mEvaluation : \033[92m{eval_csv}\033[0m")
if torch.cuda.is_available():
torch.cuda.empty_cache()
# Get the current device ID
gpu_device_id = torch.cuda.current_device()
gpu_available_mem_gb = (torch.cuda.get_device_properties(gpu_device_id).total_memory - torch.cuda.memory_allocated(gpu_device_id)) / (1024 ** 3)
print(f"[FINETUNE] \033[94mAvailable VRAM: \033[92m{gpu_available_mem_gb:.2f} GB\033[0m")
if gpu_available_mem_gb < 12:
print(f"[FINETUNE]")
print(f"[FINETUNE] \033[91m****** WARNING PRE-FLIGHT CHECKS FAILED ******* WARNING PRE-FLIGHT CHECKS FAILED *****\033[0m")
print(f"[FINETUNE] \033[94mAvailable VRAM: \033[92m{gpu_available_mem_gb:.2f} GB\033[0m")
print(f"[FINETUNE] \033[94mIf you are running on a Linux system and you have 12GB's or less of VRAM, this step\033[0m")
print(f"[FINETUNE] \033[94mmay fail, due to not enough GPU VRAM. Windows systems will use system RAM as extended\033[0m")
print(f"[FINETUNE] \033[94mVRAM and so should work ok. However, Windows machines will need enough System RAM\033[0m")
print(f"[FINETUNE] \033[94mavailable. Please read the PFC help section available on the first tab of the web\033[0m")
print(f"[FINETUNE] \033[94minterface for more information.\033[0m")
print(f"[FINETUNE] \033[91m****** WARNING PRE-FLIGHT CHECKS FAILED ******* WARNING PRE-FLIGHT CHECKS FAILED *****\033[0m")
print(f"[FINETUNE]")
# Create the directory # Create the directory
os.makedirs(OUT_PATH, exist_ok=True) os.makedirs(OUT_PATH, exist_ok=True)
@@ -347,7 +589,7 @@ def train_gpt(language, num_epochs, batch_size, grad_acumm, train_csv, eval_csv,
if basemodel_or_finetunedmodel: if basemodel_or_finetunedmodel:
# BASE XTTS model checkpoints for fine-tuning. # BASE XTTS model checkpoints for fine-tuning.
print("[FINETUNE] Starting finetuning on Base Model") print("[FINETUNE] Starting finetuning on \033[92mBase Model\033[0m")
TOKENIZER_FILE = str(this_dir / base_path / model_path / "vocab.json") TOKENIZER_FILE = str(this_dir / base_path / model_path / "vocab.json")
XTTS_CHECKPOINT = str(this_dir / base_path / model_path / "model.pth") XTTS_CHECKPOINT = str(this_dir / base_path / model_path / "model.pth")
XTTS_CONFIG_FILE = str(this_dir / base_path / model_path / "config.json") XTTS_CONFIG_FILE = str(this_dir / base_path / model_path / "config.json")
@@ -355,7 +597,7 @@ def train_gpt(language, num_epochs, batch_size, grad_acumm, train_csv, eval_csv,
MEL_NORM_FILE = str(this_dir / base_path / model_path / "mel_stats.pth") MEL_NORM_FILE = str(this_dir / base_path / model_path / "mel_stats.pth")
else: else:
# FINETUNED XTTS model checkpoints for fine-tuning. # FINETUNED XTTS model checkpoints for fine-tuning.
print("[FINETUNE] Starting finetuning on Existing Finetuned Model") print("[FINETUNE] Starting finetuning on \033[92mExisting Finetuned Model\033[0m")
TOKENIZER_FILE = str(this_dir / base_path / "trainedmodel" / "vocab.json") TOKENIZER_FILE = str(this_dir / base_path / "trainedmodel" / "vocab.json")
XTTS_CHECKPOINT = str(this_dir / base_path / "trainedmodel" / "model.pth") XTTS_CHECKPOINT = str(this_dir / base_path / "trainedmodel" / "model.pth")
XTTS_CONFIG_FILE = str(this_dir / base_path / "trainedmodel" / "config.json") XTTS_CONFIG_FILE = str(this_dir / base_path / "trainedmodel" / "config.json")
@@ -382,6 +624,11 @@ def train_gpt(language, num_epochs, batch_size, grad_acumm, train_csv, eval_csv,
# define audio config # define audio config
audio_config = XttsAudioConfig(sample_rate=22050, dvae_sample_rate=22050, output_sample_rate=24000) audio_config = XttsAudioConfig(sample_rate=22050, dvae_sample_rate=22050, output_sample_rate=24000)
# Resolve Japanese threading issue
number_of_workers = 8
if language == "ja":
number_of_workers = 0
# training parameters config # training parameters config
config = GPTTrainerConfig( config = GPTTrainerConfig(
epochs=num_epochs, epochs=num_epochs,
@@ -396,7 +643,7 @@ def train_gpt(language, num_epochs, batch_size, grad_acumm, train_csv, eval_csv,
batch_size=BATCH_SIZE, batch_size=BATCH_SIZE,
batch_group_size=48, batch_group_size=48,
eval_batch_size=BATCH_SIZE, eval_batch_size=BATCH_SIZE,
num_loader_workers=8, num_loader_workers=number_of_workers,
eval_split_max_size=256, eval_split_max_size=256,
print_step=50, print_step=50,
plot_step=100, plot_step=100,
@@ -470,17 +717,25 @@ def clear_gpu_cache():
if torch.cuda.is_available(): if torch.cuda.is_available():
torch.cuda.empty_cache() torch.cuda.empty_cache()
def find_a_speaker_file(folder_path):
search_path = folder_path / "*" / "speakers_xtts.pth"
files = glob.glob(str(search_path), recursive=True)
latest_file = max(files, key=os.path.getctime, default=None)
return latest_file
XTTS_MODEL = None XTTS_MODEL = None
def load_model(xtts_checkpoint, xtts_config, xtts_vocab): def load_model(xtts_checkpoint, xtts_config, xtts_vocab):
pfc_check_fail()
global XTTS_MODEL global XTTS_MODEL
clear_gpu_cache() clear_gpu_cache()
if not xtts_checkpoint or not xtts_config or not xtts_vocab: if not xtts_checkpoint or not xtts_config or not xtts_vocab:
return "You need to run the previous steps or manually set the `XTTS checkpoint path`, `XTTS config path`, and `XTTS vocab path` fields !!" return "You need to run the previous steps or manually set the `XTTS checkpoint path`, `XTTS config path`, and `XTTS vocab path` fields !!"
xtts_speakers_pth = find_a_speaker_file(this_dir / "models")
config = XttsConfig() config = XttsConfig()
config.load_json(xtts_config) config.load_json(xtts_config)
XTTS_MODEL = Xtts.init_from_config(config) XTTS_MODEL = Xtts.init_from_config(config)
print("[FINETUNE] \033[94mStarting Step 3\033[0m Loading XTTS model!") print("[FINETUNE] \033[94mStarting Step 3\033[0m Loading XTTS model!")
XTTS_MODEL.load_checkpoint(config, checkpoint_path=xtts_checkpoint, vocab_path=xtts_vocab, use_deepspeed=False) XTTS_MODEL.load_checkpoint(config, checkpoint_path=xtts_checkpoint, vocab_path=xtts_vocab, use_deepspeed=False, speaker_file_path=xtts_speakers_pth)
if torch.cuda.is_available(): if torch.cuda.is_available():
XTTS_MODEL.cuda() XTTS_MODEL.cuda()
@@ -511,7 +766,6 @@ def run_tts(lang, tts_text, speaker_audio_file):
return "Speech generated !", out_path, speaker_audio_file return "Speech generated !", out_path, speaker_audio_file
def get_available_voices(minimum_size_kb=1200): def get_available_voices(minimum_size_kb=1200):
voice_files = [ voice_files = [
voice for voice in Path(f"{this_dir}/finetune/tmp-trn/wavs").glob("*.wav") voice for voice in Path(f"{this_dir}/finetune/tmp-trn/wavs").glob("*.wav")
@@ -582,7 +836,7 @@ def compact_model():
torch.save(checkpoint, target_dir / "model.pth") torch.save(checkpoint, target_dir / "model.pth")
# Specify the files you want to copy # Specify the files you want to copy
files_to_copy = ["vocab.json", "config.json", "dvae.pth", "mel_stats.pth"] files_to_copy = ["vocab.json", "config.json", "dvae.pth", "mel_stats.pth","speakers_xtts.pth", ]
for file_name in files_to_copy: for file_name in files_to_copy:
src_path = this_dir / base_path / base_model_path / file_name src_path = this_dir / base_path / base_model_path / file_name
@@ -640,7 +894,7 @@ def compact_lastfinetuned_model():
torch.save(checkpoint, target_dir / "model.pth") torch.save(checkpoint, target_dir / "model.pth")
# Specify the files you want to copy # Specify the files you want to copy
files_to_copy = ["vocab.json", "config.json", "dvae.pth", "mel_stats.pth"] files_to_copy = ["vocab.json", "config.json", "dvae.pth", "mel_stats.pth", "speakers_xtts.pth",]
for file_name in files_to_copy: for file_name in files_to_copy:
src_path = this_dir / base_path / base_model_path / file_name src_path = this_dir / base_path / base_model_path / file_name
@@ -700,7 +954,6 @@ def compact_legacy_model():
print("[FINETUNE] model.pth created in '/finetune/'") print("[FINETUNE] model.pth created in '/finetune/'")
return "model.pth created in '/finetune/'" return "model.pth created in '/finetune/'"
def delete_training_data(): def delete_training_data():
# Define the folder to be deleted # Define the folder to be deleted
folder_to_delete = Path(this_dir / "finetune" / "tmp-trn") folder_to_delete = Path(this_dir / "finetune" / "tmp-trn")
@@ -725,7 +978,6 @@ def delete_training_data():
print(f"[FINETUNE] Folder {folder_to_delete} does not exist.") print(f"[FINETUNE] Folder {folder_to_delete} does not exist.")
return "Folder '/finetune/tmp-trn/' does not exist." return "Folder '/finetune/tmp-trn/' does not exist."
def delete_voice_sample_contents(): def delete_voice_sample_contents():
# Define the folder to be cleared # Define the folder to be cleared
folder_to_clear = Path(this_dir / "finetune" / "put-voice-samples-in-here") folder_to_clear = Path(this_dir / "finetune" / "put-voice-samples-in-here")
@@ -748,7 +1000,6 @@ def delete_voice_sample_contents():
print(f"[FINETUNE] Folder {folder_to_clear} does not exist.") print(f"[FINETUNE] Folder {folder_to_clear} does not exist.")
return f"Folder 'put-voice-samples-in-here' does not exist." return f"Folder 'put-voice-samples-in-here' does not exist."
####################### #######################
#### OTHER Generic #### #### OTHER Generic ####
####################### #######################
@@ -774,7 +1025,6 @@ class Logger:
sys.stdout = Logger() sys.stdout = Logger()
sys.stderr = sys.stdout sys.stderr = sys.stdout
# logging.basicConfig(stream=sys.stdout, level=logging.INFO) # logging.basicConfig(stream=sys.stdout, level=logging.INFO)
import logging import logging
logging.basicConfig( logging.basicConfig(
@@ -785,47 +1035,67 @@ logging.basicConfig(
] ]
) )
def read_logs(): def read_logs():
sys.stdout.flush() sys.stdout.flush()
with open(sys.stdout.log_file, "r") as f: with open(sys.stdout.log_file, "r") as f:
return f.read() return f.read()
def cleanup_before_exit(signum, frame): def cleanup_before_exit(signum, frame):
print("[FINETUNE] Received interrupt signal. Cleaning up and exiting...") print("[FINETUNE] Received interrupt signal. Cleaning up and exiting...")
# Perform cleanup operations here if necessary # Perform cleanup operations here if necessary
sys.exit(0) sys.exit(0)
def create_refresh_button(refresh_components, refresh_methods, elem_class, interactive=True):
def create_refresh_button(refresh_component, refresh_method, refreshed_args, elem_class, interactive=True):
"""
Copied from https://github.com/AUTOMATIC1111/stable-diffusion-webui
"""
def refresh(): def refresh():
refresh_method() updates = {}
args = refreshed_args() if callable(refreshed_args) else refreshed_args for component, method in zip(refresh_components, refresh_methods):
args = method() if callable(method) else method
if args and 'choices' in args:
# Select the most recent file (last in the sorted list)
args['value'] = args['choices'][-1] if args['choices'] else ""
for k, v in args.items():
setattr(component, k, v)
updates[component] = gr.update(**(args or {}))
return updates
for k, v in args.items(): refresh_button = gr.Button("Refresh Dropdowns", elem_classes=elem_class, interactive=interactive)
setattr(refresh_component, k, v)
return gr.update(**(args or {}))
refresh_button = gr.Button(refresh_symbol, elem_classes=elem_class, interactive=interactive, scale=0)
refresh_button.click( refresh_button.click(
fn=refresh, fn=refresh,
inputs=[], inputs=[],
outputs=[refresh_component] outputs=refresh_components
) )
return refresh_button return refresh_button
pfc_markdown = f"""
### 🚀 <u>Pre-flight Checklist for Fine-tuning</u><br>
◽ <strong>Ensure</strong> each criterion is marked with a green check mark ✅ and a Pass status. <strong>Finetuning will fail otherwise.</strong><br>
◽ The help tabs along the top will assist in resolving issues and you can also find additional help guides on the AllTalk [GitHub repository](https://github.com/erew123/alltalk_tts#-finetuning-a-model).<br>
◽ For an overview of fine-tuning procedures, please refer to the "General Finetuning info" tab or visit the AllTalk [GitHub repository](https://github.com/erew123/alltalk_tts#-finetuning-a-model).
"""
custom_css = """
body {
font-size: 16px; /* Adjust the base font size as needed */
}
h1, h2, h3, h4, h5, h6 {
font-size: 1.25em; /* Adjust heading sizes relative to the base size */
}
p {
font-size: 1.1em; /* Paragraph font size, relative to the base size */
margin-bottom: 10px; /* Adjust paragraph spacing */
}
.gradio_container {
zoom: 1.1; /* Adjust the zoom to scale the entire container */
}
"""
if __name__ == "__main__": if __name__ == "__main__":
# Register the signal handler # Register the signal handler
signal.signal(signal.SIGINT, cleanup_before_exit) signal.signal(signal.SIGINT, cleanup_before_exit)
################ ################
#### GRADIO #### #### GRADIO ####
################ ################
@@ -869,63 +1139,150 @@ if __name__ == "__main__":
#### GRADIO INFO #### #### GRADIO INFO ####
##################### #####################
with gr.Blocks(theme=theme) as demo: with gr.Blocks(theme=theme, css=custom_css) as demo:
with gr.Tab("Information/Guide"): with gr.Tab("🚀 PFC"):
with gr.Tab("🚀 Pre-Flight Checklist"):
gr.Markdown( gr.Markdown(
f""" f"""
## <u>Finetuning Information</u><br> {pfc_markdown}
### 🟥 <u>Important Note</u> {disk_space_results}
#### &nbsp;&nbsp;&nbsp;&nbsp; - <span style="color: #3366ff;">finetune.py</span> needs to be run from the <span style="color: #3366ff;">/alltalk_tts/</span> folder. Don't move the location of this script. {system_ram_results}
#### &nbsp;&nbsp;&nbsp;&nbsp; - Have you run AllTalk at least once? It needs to have downloaded+updated the voice model, before we can finetune it. {cuda_results}
### 🟦 <u>What you need to run finetuning</u> {pytorch_results}
#### &nbsp;&nbsp;&nbsp;&nbsp; - An Nvidia GPU. Tested on Windows with extended shared VRAM and training used about 16GB's total (which worked on a 12GB card). {base_model_results}
#### &nbsp;&nbsp;&nbsp;&nbsp; - If you have multiple Nvidia GPU's in your system, please see this [important note](https://github.com/erew123/alltalk_tts#-i-have-multiple-gpus-and-i-have-problems-running-finetuning). {tts_version_status}
#### &nbsp;&nbsp;&nbsp;&nbsp; - I have not been able to test this on a GPU with less than 12GB of VRAM, so cannot say if it will work or how that would affect performance.
#### &nbsp;&nbsp;&nbsp;&nbsp; - <span style="color: red;">Version 11.8</span> of Nvidia cuBLAS and cuDNN (guide below). Only 11.8 of cuBLAS and cuDNN work for this process currently.
#### &nbsp;&nbsp;&nbsp;&nbsp; - Minimum <span style="color: red;">18GB</span> free disk space (most of it is used temporarily).
#### &nbsp;&nbsp;&nbsp;&nbsp; - Some decent quality audio, multiple files if you like. Minimum of 2 minutes and Ive tested up to 20 minutes of audio.
#### &nbsp;&nbsp;&nbsp;&nbsp; - There is no major need to chop down your audio files into small slices as Step 1 will do that for you automatically and prepare the training set. Ive been testing with 5 minute long clips.
#### &nbsp;&nbsp;&nbsp;&nbsp; - This process will need access to all your GPU and VRAM, so close any other software that's using your GPU currently.
### 🟨 <u>Setting up cuBLAS and cuDNN <span style="color: red;">Version 11.8</span></u>
#### &nbsp;&nbsp;&nbsp;&nbsp; - If you have the <span style="color: #3366ff;;">Nvidia CUDA Toolkit Version 11.8</span> installed and can type <span style="color: #3366ff;;">nvcc --version</span> at the command prompt/terminal and it reports <span style="color: #00a000;">Cuda compilation tools, release 11.8</span> you should be good to go.
#### &nbsp;&nbsp;&nbsp;&nbsp; - If you dont have the toolkit installed, the idea is just to install the smallest bit possible and this will not affect or impact other things on your system.
#### &nbsp;&nbsp;&nbsp;&nbsp; - You will need to download the Nvidia Cuda Toolkit 11.8<span style="color: #3366ff;"> network</span> install from [here](https://developer.nvidia.com/cuda-11-8-0-download-archive)
#### &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 1) Run the installer and select <span style="color: #3366ff;">Custom Advanced</span> Uncheck <span style="color: #3366ff;">everything</span> at the top then expand <span style="color: #3366ff;">CUDA</span>, <span style="color: #3366ff;">Development</span> > <span style="color: #3366ff;">Compiler</span> > and select <span style="color: #3366ff;;">nvcc</span> then expand <span style="color: #3366ff;;">Libraries</span> and select <span style="color: #3366ff;;">CUBLAS</span>.
#### &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 2) Back at the top of <span style="color: #3366ff;">CUDA</span>, expand <span style="color: #3366ff;">Runtime</span> > <span style="color: #3366ff;">Libraries</span> and select <span style="color: #3366ff;">CUBLAS</span>. Click <span style="color: #3366ff;;">Next</span>, accept the default path (taking a note of its location) and let the install run.
#### &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 3) You should be able to drop to your terminal or command prompt and type <span style="color: #3366ff;">nvcc --version</span> and have it report <span style="color: #00a000;">Cuda compilation tools, release 11.8</span>. If it does you are good to go. If it doesn't > Step 4.
#### &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 4) Linux users, you can temporarily add these paths on your current terminal window with (you may need to confirm these are correct for your flavour of Linux):
#### &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span style="color: #3366ff;">export LD_LIBRARY_PATH=/usr/local/cuda-11.8/lib64&colon;&dollar;&lbrace;LD_LIBRARY_PATH&colon;&plus;&colon;&dollar;&lbrace;LD_LIBRARY_PATH&rbrace;&rbrace;</span> (Add it to your ~/.bashrc if you want this to be permanent)
#### &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span style="color: #3366ff;">export LD_LIBRARY_PATH=/usr/local/cuda-11.8/bin</span>
#### &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Windows users need the add the following to the PATH environment variable. Start menu and search for "Environment Variables" or "Edit the system environment variables.".
#### &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Find and select the "Path" variable, then click on the "Edit...". Click on the "New" button and add:
#### &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span style="color: #3366ff;">C:&bsol;Program Files&bsol;NVIDIA GPU Computing Toolkit&bsol;CUDA&bsol;v11.8&bsol;bin.</span>
#### &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 5) Once you have these set correctly, you should be able to open a new command prompt/terminal and <span style="color: #3366ff;">nvcc --version</span> at the command prompt/terminal, resulting in <span style="color: #00a000;">Cuda compilation tools, release 11.8</span>.
#### &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 6) If the nvcc command doesn't work OR it reports a version different from 11.8, finetuning wont work, so you will to double check your environment variables and get them working correctly.
""" """
) )
with gr.Tab("🟩 Disks Help"):
gr.Markdown(
f"""
{disk_space_results}<br><br>
◽ During actual training (Step 2) Finetuning will require approximately 18GB's of free disk space while performing training and will fail or perform badly if there is any less disk space. The majority of this disk space is used temporarily and will be cleared when you reach Step 4 and move & compact the model then delete the training data.<br>
◽ Because lots of data is being copied around, <strong>mechanical hard disks</strong> will be slow.
"""
)
with gr.Tab("🟪 RAM & VRAM Help"):
gr.Markdown(
f"""
{system_ram_results}<br>
◽ During actual training (Step 2) Finetuning will use around **14GB's** of VRAM. If your GPU doesnt have 14GB's of VRAM:<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; - **Windows** systems will attempt to extend VRAM into your System RAM, and so should work.<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; - **Linux** systems can only use the available VRAM, so may fail on 12GB VRAM or smaller GPU's.<br>
◽ For **Windows** users with 12GB or less, if you also have very low or slow System RAM you can expect bad performance or the training to fail.<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; - 12GB cards may need 2GB System RAM.<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; - 8GB cards may need 6GB System RAM.<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; - 6GB cards may need 8GB System RAM.<br>
◽ Its hard to estimate what performance impact this could will have, due to different memory speeds, PCI speeds, GPU speeds etc.<br>
◽ If you have a low VRAM scenario and also are attempting to run on a mechanical hard drive, Finetuning could take ??? amount of time.<br>
◽ On Windows machines, please ensure you have **not** disabled System Memory Fallback for Stable Diffusion <a href="https://nvidia.custhelp.com/app/answers/detail/a_id/5490/~/system-memory-fallback-for-stable-diffusion" target="_blank">link here</a><br>
"""
)
with gr.Tab("🟨 CUDA & Cublas Help"):
gr.Markdown(
f"""
{cuda_results}<br><br>
◽ It DOESNT matter what version of CUDA you have installed within Python either, CUDA 11.8, CUDA 12.1 etc. The NVIDIA CUDA Development Toolkit is a completly different and seperate thing from Python/PyTorch.<br>
◽ Finetuning simply wants to access a tool within the CUDA Development Toolkit 11.8 called Cublas64_11.<br>
◽ If you dont have the toolkit installed, the idea is just to install the smallest bit possible and this will not affect or impact other things on your system.<br><br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;◽ You will need to download the Nvidia Cuda Toolkit 11.8<span style="color: #3366ff;"> network install</span> from <a href="https://developer.nvidia.com/cuda-11-8-0-download-archive" target="_blank">link here</a><br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;◽ 1) Run the installer and select <span style="color: #3366ff;">Custom Advanced</span> Uncheck <span style="color: #3366ff;">everything</span> at the top then expand <span style="color: #3366ff;">CUDA</span>, <span style="color: #3366ff;">Development</span> > <span style="color: #3366ff;">Compiler</span> > and select <span style="color: #3366ff;;">nvcc</span> then expand <span style="color: #3366ff;;">Libraries</span> and select <span style="color: #3366ff;;">CUBLAS</span>.<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;◽ 2) Back at the top of <span style="color: #3366ff;">CUDA</span>, expand <span style="color: #3366ff;">Runtime</span> > <span style="color: #3366ff;">Libraries</span> and select <span style="color: #3366ff;">CUBLAS</span>. Click <span style="color: #3366ff;;">Next</span>, accept the default path (taking a note of its location) and let the install run. <br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;◽ 3) You should be able to drop to your terminal or command prompt and type <span style="color: #3366ff;">nvcc --version</span> and have it report <span style="color: #00a000;">Cuda compilation tools, release 11.8</span>. If it does you are good to go. If it doesn't > Step 4.<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;◽ 4) <strong>Linux users</strong>, you can temporarily add these paths on your current terminal window with (you may need to confirm these are correct for your flavour of Linux):<br><br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span style="color: #3366ff;">export LD_LIBRARY_PATH=/usr/local/cuda-11.8/lib64&colon;&dollar;&lbrace;LD_LIBRARY_PATH&colon;&plus;&colon;&dollar;&lbrace;LD_LIBRARY_PATH&rbrace;&rbrace;</span> (Add it to your ~/.bashrc if you want this to be permanent)<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span style="color: #3366ff;">export LD_LIBRARY_PATH=/usr/local/cuda-11.8/bin</span><br><br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <strong>Windows users</strong> need the add the following to the PATH environment variable. Start menu and search for "Environment Variables" or "Edit the system environment variables.". <br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Find and select the "Path" variable, then click on the "Edit...". Click on the "New" button and add:<br><br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span style="color: #3366ff;">C:&bsol;Program Files&bsol;NVIDIA GPU Computing Toolkit&bsol;CUDA&bsol;v11.8&bsol;bin.</span><br><br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;◽ 5) Once you have these set correctly, you should be able to open a new command prompt/terminal and <span style="color: #3366ff;">nvcc --version</span> at the command prompt/terminal, resulting in <span style="color: #00a000;">Cuda compilation tools, release 11.8</span>.<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;◽ 6) If the nvcc command doesn't work OR it reports a version different from 11.8, finetuning wont work, so you will to double check your environment variables and get them working correctly.<br>
"""
)
with gr.Tab("🟦 Python & PyTorch Help"):
gr.Markdown(
f"""
{pytorch_results}<br><br>
◽ On the PyTorch version the:<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;- first few digits are the version of PyTorch e.g. 2.1.0 is PyTorch 2.1.0<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;- last few digits refer to the CUDA version e.g. cu118 is Cuda 11.8. cu121 is Cuda 12.1.<br>
◽ Ensure you have started your Python envuronment before running finetuning otherwise you will have failures on the above checks.<br>
◽ If PyTorch does not show a CUDA version, then PyTorch will need reinstalling with CUDA. I would suggest running <span style="color: #3366ff;">pip cache purge</span> before installing PyTorch again.<br>
◽ It DOESNT matter what version of PyTorch and CUDA you have installed within Python, CUDA 11.8, CUDA 12.1 etc. The NVIDIA CUDA Development Toolkit is a completly different and seperate thing.<br>
◽ Finetuning simply wants to access a tool within the CUDA Development Toolkit called Cublas64_11.<br>
◽ If you dont have the toolkit installed, the idea is just to install the smallest bit possible and this will not affect or impact other things on your system.<br>
"""
)
with gr.Tab("⬛ XTTS Base Model Help"):
gr.Markdown(
f"""
{base_model_results}<br><br>
◽ If your basemodel is not being detected, please ensure that <span style="color: #3366ff;">finetune.py</span> is being run from the AllTalk main folder.<br>
◽ Ensure you have started AllTalk normally at least once. You can start it again and it will download any missing files.<br>
◽ Check that there is an XTTS model within the models folder e.g. <span style="color: #3366ff;">/models/xttsv2_2.0.2/</span><br>
◽ The files required are "model.pth", "vocab.json", "config.json", "dvae.pth", "mel_stats.pth", "speakers_xtts.pth".
"""
)
with gr.Tab("🟥 TTS Version Help"):
gr.Markdown(
f"""
{tts_version_status}<br><br>
◽ If your TTS version is showing as the incorrect version, please reinstall the Finetuning requirements at the command prompt/terminal.<br>
◽ <span style="color: #3366ff;">pip install -r requirements_finetune.txt</span><br>
"""
)
with gr.Tab("ℹ️ General Finetuning info"):
gr.Markdown(
f"""
### 🟥 <u>Important Note</u>
◽ <span style="color: #3366ff;">finetune.py</span> needs to be run from the <span style="color: #3366ff;">/alltalk_tts/</span> folder. Don't move the location of this script.
### 🟦 <u>What you need to run finetuning</u>
◽ An Nvidia GPU.<br>
◽ If you have multiple Nvidia GPU's in your system, please see the Github Help section [Multiple GPU's](https://github.com/erew123/alltalk_tts#performance-and-compatibility-issues).<br>
◽ Some decent quality audio, multiple files if you like. Minimum of 2 minutes and Ive tested up to 20 minutes of audio.<br>
◽ There is no major need to chop down your audio files into small slices as Step 1 will do that for you automatically and prepare the training set. But this can be helpful in cases where:<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; - Whisper doesnt correctly detect and split down audio files.<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; - You do not get a lot of "Speaker Reference Audio" files at the end of training.<br>
◽ This process will need access to all your GPU and VRAM, so close any other software that's using your GPU currently.<br>
### 🟨 <u>What do I do from here?</u><br>
◽ Proceed through Step 1, 2, 3 and onto "What to do next".<br>
### 🟩 <u>Additional Information</u><br>
◽ Guidance is provided on each step of the process however, if you are after more detailed information please visit:<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ◽ [AllTalk Github Finetuning](https://github.com/erew123/alltalk_tts#-finetuning-a-model)<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ◽ [Coqui XTTS Documentation](https://docs.coqui.ai/en/latest/index.html)<br>
"""
)
####################### #######################
#### GRADIO STEP 1 #### #### GRADIO STEP 1 ####
####################### #######################
with gr.Tab("Step 1 - Preparing Audio/Generating the dataset"): with gr.Tab("📁 Step 1 - Generating the dataset"):
gr.Markdown( gr.Markdown(
f""" f"""
## <u>STEP 1 - Preparing Audio/Generating the dataset</u><br> ### 📁 <u>Generating the dataset</u><br>
### 🟥 <u>Important Note - Windows - "UserWarning: huggingface_hub cache-system uses symlinks."</u>
◽ This error is caused by Huggingfaces download software downloading the Whisper model. If you get this error, please restart your Windows command prompt with "Run as Administrator" and restart finetuning.<br>
◽ This should only occur the 1st time it downloads the Whisper model. [Huggingface Reference here](https://huggingface.co/docs/huggingface_hub/en/guides/manage-cache#limitations)
### 🟦 <u>What you need to do</u> ### 🟦 <u>What you need to do</u>
#### &nbsp;&nbsp;&nbsp;&nbsp; - Please read Coqui's guide on what makes a good dataset [here](https://docs.coqui.ai/en/latest/what_makes_a_good_dataset.html#what-makes-a-good-dataset) ◽ Please read Coqui's guide on what makes a good dataset [here](https://docs.coqui.ai/en/latest/what_makes_a_good_dataset.html#what-makes-a-good-dataset)<br>
#### &nbsp;&nbsp;&nbsp;&nbsp; - Place your audio files in <span style="color: #3366ff;">{str(audio_folder)}</span> ◽ Place your audio files in <span style="color: #3366ff;">{str(audio_folder)}</span>
#### &nbsp;&nbsp;&nbsp;&nbsp; - Your audio samples can be in the format <span style="color: #3366ff;">mp3, wav,</span> or <span style="color: #3366ff;">flac.</span> ◽ Your audio samples can be in the format <span style="color: #3366ff;">mp3, wav,</span> or <span style="color: #3366ff;">flac.</span><br>
#### &nbsp;&nbsp;&nbsp;&nbsp; - You will need a minimum of <span style="color: #3366ff;">2 minutes</span> of audio in either one or multiple audio files. Very small sample files cause errors, so I would suggest 30 seconds and longer samples. ◽ You will need a minimum of <span style="color: #3366ff;">2 minutes</span> of audio in either one or multiple audio files. 5 to 10 minutes of audio would probably be better, allowing for more varied sample data to be generated.<br>
#### &nbsp;&nbsp;&nbsp;&nbsp; - When you have completed Steps 1, 2, and 3, you are welcome to delete your samples from "put-voice-samples-in-here". ◽ Very small sample files cause errors, so I would recommend that the samples are at least 30 seconds and longer.<br>
#### &nbsp;&nbsp;&nbsp;&nbsp; - FYI Anecdotal evidence suggests that the Whisper 2 model may yield superior results in audio splitting and dataset creation. ◽ FYI Anecdotal evidence suggests that the Whisper 2 model may yield superior results in audio splitting and dataset creation.<br>
#### &nbsp;&nbsp;&nbsp;&nbsp; - If this step is failing, it will be worth running the diagnostics with atsetup and confirming you have cu118 or cu112 listed against your torch and torchaudio.<br> ### 🟨 <u>What this step is doing</u><br>
### 🟨 <u>What this step is doing</u> ◽ With step one, we are going to be stripping your audio file(s) into smaller files, using Whisper to find spoken words/sentences, compile that into excel sheets of training data, ready for Step 2.<br>
#### &nbsp;&nbsp;&nbsp;&nbsp; - With step one, we are going to be stripping your audio file(s) into smaller files, using Whisper to find spoken words/sentences, compile that into excel sheets of training data, ready for finetuning the model on Step 2. ◽ Whisper is making a best effort to find spoken audio and break it down into smaller audio files. The content of these audio files is then transcribed into CSV fles (which you can edit in Excel or similar).<br>
#### &nbsp;&nbsp;&nbsp;&nbsp; - Whilst you can choose multiple Whisper models, its best only to use the 1 model as each one is about 3GB in size and will download to your local huggingface cache on first-time use. If and when you have completed training, you wish to delete this 3GB model from your system, you are welcome to do so. ◽ These files (audio and CSV) are used at the next step to train the model "this is what the audio sounds like and these are the words being spoken".<br>
### 🟩 <u>How long will this take?</u> ◽ If you wish to manually look at the CSV files before running the next step, you are welcome to do so and edit them as necessary. The greater the accuracy of the text the better the training will be.<br>
#### &nbsp;&nbsp;&nbsp;&nbsp; - First time, it needs to download the Whisper model which is 3GB. After that a few minutes on an average 3-4 year old system. &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ◽ <span style="color: #3366ff;">/alltalk_tts/finetune/tmp-trn/metadata_train.csv</span> Contains the list of wavs to text that will be used. This is what the model is trained with.<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ◽ <span style="color: #3366ff;">/alltalk_tts/finetune/tmp-trn/metadata_eval.csv</span> Lists the evaluation wav to text, used to generate TTS while training and evaluate the quality of generated TTS to the original sample.<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ◽ <span style="color: #3366ff;">/alltalk_tts/finetune/tmp-trn/wavs/</span> These are the wavs split out from your original training samples and match the contents of the CSV files.<br>
◽ Whilst you can choose multiple Whisper models, its best only to use the 1 model as each one is about 3GB in size and will download to your local huggingface cache on first-time use. <br>
◽ If and when you have completed training, should you wish to delete this 3GB Whisper model from your system, you are welcome to do so.<br>
### 🟩 <u>How long will this take?</u><br>
◽ First time, it needs to download the Whisper model which is 3GB. After that a few minutes on an average 3-4 year old system.<br>
""" """
) )
@@ -937,7 +1294,7 @@ if __name__ == "__main__":
with gr.Row(): with gr.Row():
whisper_model = gr.Dropdown( whisper_model = gr.Dropdown(
label="Whisper Model", label="Whisper Model",
value="large-v3", value="large-v2",
choices=[ choices=[
"large-v3", "large-v3",
"large-v2", "large-v2",
@@ -1015,20 +1372,27 @@ if __name__ == "__main__":
####################### #######################
#### GRADIO STEP 2 #### #### GRADIO STEP 2 ####
####################### #######################
with gr.Tab("Step 2 - Fine-tuning the XTTS Encoder"): with gr.Tab("💻 Step 2 - Training"):
gr.Markdown( gr.Markdown(
f""" f"""
## <u>STEP 2 - Fine-tuning the XTTS Encoder</u><br> ### 💻 <u>Training</u><br>
### 🟦 <u>What you need to do</u> ### 🟦 <u>What you need to do</u>
#### &nbsp;&nbsp;&nbsp;&nbsp; - The <span style="color: #3366ff;">Train CSV</span> and <span style="color: #3366ff;">Eval CSV</span> should already be populated. If not, just go back to Step 1 and click the button again. ◽ The <span style="color: #3366ff;">Train CSV</span> and <span style="color: #3366ff;">Eval CSV</span> should already be populated. If not, just go back to Step 1 and click "Create Dataset" again.<br>
#### &nbsp;&nbsp;&nbsp;&nbsp; - If for any reason it crashed, close the app and re-start the finetuning and kick it off again, clicking through setp 1 (it doesnt need to regenerate the data) ◽ The default settings below are the suggested settings for most purposes, however you may choose to alter them depending on your specific use case.<br>
#### &nbsp;&nbsp;&nbsp;&nbsp; - The settings below are factory and should be ok.<br> ◽ There are no absolute correct settings for training. It will vary based on:<br>
### 🟨 <u>What this step is doing</u> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;- What you are training (A human voice, A cartoon voice, A new language entirely etc)<br>
#### &nbsp;&nbsp;&nbsp;&nbsp; - Very simply put, it's taking all our wav files generated in Step 1, along with our recorded speech in out excel documents and its training the model on that voice. &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;- How much audio you have (you may want less or more eval or epochs)<br>
### 🟩 <u>How long will this take?</u> ◽ The key indicator of sufficient model training is whether it sounds right to you. Coqui suggests beginning with the base settings for training. If the resulting audio doesn't meet your expectations, additional training sessions may be necessary.<br>
#### &nbsp;&nbsp;&nbsp;&nbsp; - On a RTX 4070 with factory settings (as below) and 20 minutes of audio, this took 20 minutes. Again, it will vary by system and how much audio you are throwing at it. You have time to go make a coffee. ◽ **Number of epochs:** Defines how many times the entire dataset is passed through the neural network; more epochs can improve learning but increase the risk of memorizing the training data.<br>
#### &nbsp;&nbsp;&nbsp;&nbsp; - Look at your command prompt/terminal window if you want to see what it is doing. ◽ **Batch size:** Sets how many training examples are used in one iteration. Larger sizes speed up training but require more computational memory.<br>
◽ **Grad(ient) accumulation steps:** Allows larger batches by spreading the computation across multiple steps, enabling training with limited hardware resources.<br>
◽ **Max permitted audio size in seconds:** Limits the length of audio that the model processes. Any audio exceeding this duration will be truncated to fit the model's input constraints.<br>
### 🟨 <u>What this step is doing</u><br>
◽ Very simply put, it's taking all our wav files generated in Step 1, along with our recorded speech in out excel documents and its training the model on that voice e.g. listen to this audio file and this is what is being said in it, so learn to reproduce it.<br>
### 🟩 <u>How long will this take?</u><br>
◽ On a RTX 4070 with factory settings (as below) and 20 minutes of audio, this took 20 minutes. Again, it will vary by system and how much audio you are throwing at it. You have time to go make a coffee.<br>
◽ Look at your command prompt/terminal window if you want to see what it is doing.<br>
""" """
) )
with gr.Row(): with gr.Row():
@@ -1078,7 +1442,7 @@ if __name__ == "__main__":
) )
gr.Markdown( gr.Markdown(
f""" f"""
##### If you have an existing finetuned model in <span style="color: #3366ff;">/models/trainedmodel/</span> you will have an option to select <span style="color: #3366ff;">Existing finetuned model</span>, allowing you to further train it. Otherwise, you will only have Base Model as an option. If you have an existing finetuned model in <span style="color: #3366ff;">/models/trainedmodel/</span> you will have an option to select <span style="color: #3366ff;">Existing finetuned model</span>, allowing you to further train it. Otherwise, you will only have Base Model as an option.
""" """
) )
@@ -1106,8 +1470,8 @@ if __name__ == "__main__":
return f"The training was interrupted due an error !! Please check the console to check the full error message! \n Error summary: {error}", "", "", "", "" return f"The training was interrupted due an error !! Please check the console to check the full error message! \n Error summary: {error}", "", "", "", ""
# copy original files to avoid parameters changes issues # copy original files to avoid parameters changes issues
os.system(f"cp {config_path} {exp_path}") shutil.copy(config_path, exp_path)
os.system(f"cp {vocab_file} {exp_path}") shutil.copy(vocab_file, exp_path)
ft_xtts_checkpoint = os.path.join(exp_path, "best_model.pth") ft_xtts_checkpoint = os.path.join(exp_path, "best_model.pth")
print("[FINETUNE] Model training done. Move to Step 3") print("[FINETUNE] Model training done. Move to Step 3")
@@ -1118,21 +1482,25 @@ if __name__ == "__main__":
#### GRADIO STEP 3 #### #### GRADIO STEP 3 ####
####################### #######################
with gr.Tab("Step 3 - Inference/Testing"): with gr.Tab("✅ Step 3 - Testing"):
gr.Markdown( gr.Markdown(
f""" f"""
## <u>STEP 3 - Inference/Testing</u><br> ### ✅ <u>Testing</u><br>
### 🟥 <u>Important Note</u> ### 🟥 <u>Important Note</u>
#### &nbsp;&nbsp;&nbsp;&nbsp; - This step will error if you are using TTS version 0.22.0. Please re-run <span style="color: #3366ff;">pip install -r requirements_finetune.txt</span> if it errors loading the model. ◽ If you dont believe the dropdown lists are populated correctly, you can use the "Refresh Dropdowns" button to search the finetuning folder path and update the lists. You can also manually edit the path to the listed files.<br>
#### &nbsp;&nbsp;&nbsp;&nbsp; - If you dont see multiple speaker reference files and you used more than 3 minutes of speech, try refreshing the page as it may not have loaded them correctly. ◽ You can use any config.json or vocab.json, as long as they match the model version you trained on. Only the XTTS checkpoint matters.<br>
◽ Upon successful processing of your speech data, expect to find multiple speaker reference files if your input consisted of over 3 minutes of speech.<br>
◽ Audio clips shorter than 7 seconds are automatically excluded from the reference list, as they generally do not provide sufficient length for effective TTS generation.<br>
◽ Should you find a lower number of reference files than anticipated, it may be due to the initial segmentation performed by Whisper (Step 1), which can occasionally result in clips that are too brief for our criteria or indeed too long. In such cases, consider manually segmenting your audio to give Whisper a better chance at generating training data and repeating the process.<br>
### 🟦 <u>What you need to do</u> ### 🟦 <u>What you need to do</u>
#### &nbsp;&nbsp;&nbsp;&nbsp; - The model is now trained and you are at the testing stage. Hopefully all the dropdowns should be pre-populated now. ◽ **Click** the **Refresh Dropdowns** button to correctly populate the **Speaker Reference Audio** with all the available WAV samples.<br>
#### &nbsp;&nbsp;&nbsp;&nbsp; - You need to <span style="color: #3366ff;">Load Fine-tuned XTTS model</span> and then select your <span style="color: #3366ff;">Speaker Reference Audio</span>. You can choose various <span style="color: #3366ff;">Speaker Reference Audios</span> to see which works best. ◽ The model is now trained and you are at the testing stage. Hopefully all the dropdowns should be pre-populated now.<br>
#### &nbsp;&nbsp;&nbsp;&nbsp; - All the <span style="color: #3366ff;">Speaker Reference Audios</span> in the dropdown are ones that are <span style="color: #3366ff;">8 seconds</span> long or more. You can use one of these later for your voice sample in All Talk, so remember the one you like. ◽ You need to <span style="color: #3366ff;">Load Fine-tuned XTTS model</span> and then select your <span style="color: #3366ff;">Speaker Reference Audio</span>. You can choose various <span style="color: #3366ff;">Speaker Reference Audios</span> to see which works best.<br>
#### &nbsp;&nbsp;&nbsp;&nbsp; - Some <span style="color: #3366ff;">Speaker Reference Audios</span> are a bit long and may cause issues generating the TTS, so try different ones. ◽ All the <span style="color: #3366ff;">Speaker Reference Audios</span> in the dropdown are ones that are <span style="color: #3366ff;">8 seconds</span> long or more. You can use one of these later for your voice sample in All Talk, so remember the one you like.<br>
#### &nbsp;&nbsp;&nbsp;&nbsp; - Once you are happy, move to the next tab for instruction on cleaning out the training data and how to copy your newly trained model over your current model. ◽ Some <span style="color: #3366ff;">Speaker Reference Audios</span> are a bit long and may cause issues generating the TTS, so try different ones.<br>
◽ Once you are happy, move to the next tab for instruction on cleaning out the training data and how to copy your newly trained model over your current model.<br>
### 🟨 <u>What this step is doing</u> ### 🟨 <u>What this step is doing</u>
#### &nbsp;&nbsp;&nbsp;&nbsp; - Its loading the finetuned model into memory, loading a voice sample and generating TTS, so that you can test out how well fine tuning worked. ◽ Its loading the finetuned model into memory, loading a voice sample and generating TTS, so that you can test out how well fine tuning worked.
""" """
) )
@@ -1140,21 +1508,21 @@ if __name__ == "__main__":
with gr.Column() as col1: with gr.Column() as col1:
xtts_checkpoint = gr.Dropdown( xtts_checkpoint = gr.Dropdown(
[str(file) for file in xtts_checkpoint_files], [str(file) for file in xtts_checkpoint_files],
label="XTTS checkpoint path:", label="XTTS checkpoint path (best_model.pth):",
value="", value="",
allow_custom_value=True, allow_custom_value=True,
) )
xtts_config = gr.Dropdown( xtts_config = gr.Dropdown(
[str(file) for file in xtts_config_files], [str(file) for file in xtts_config_files],
label="XTTS config path:", label="XTTS config path (config.json):",
value="", value="",
allow_custom_value=True, allow_custom_value=True,
) )
xtts_vocab = gr.Dropdown( xtts_vocab = gr.Dropdown(
[str(file) for file in xtts_vocab_files], [str(file) for file in xtts_vocab_files],
label="XTTS vocab path:", label="XTTS vocab path (vocab.json):",
value="", value="",
allow_custom_value=True, allow_custom_value=True,
) )
@@ -1176,36 +1544,41 @@ if __name__ == "__main__":
allow_custom_value=True, # Allow custom values allow_custom_value=True, # Allow custom values
scale=1, scale=1,
) )
with gr.Row():
tts_language = gr.Dropdown(
label="Language",
value="en",
choices=[
"en",
"es",
"fr",
"de",
"it",
"pt",
"pl",
"tr",
"ru",
"nl",
"cs",
"ar",
"zh",
"hu",
"ko",
"ja",
]
)
# Create refresh button # Create refresh button
refresh_button = create_refresh_button( refresh_button = create_refresh_button(
speaker_reference_audio, [xtts_checkpoint, xtts_config, xtts_vocab, speaker_reference_audio],
lambda: None, # Specify your refresh logic if needed [
lambda: {"choices": get_available_voices(), "value": ""}, # Update choices and value lambda: {"choices": find_models(main_directory, "pth"), "value": ""},
"refresh-button", lambda: {"choices": find_jsons(main_directory, "config.json"), "value": ""},
lambda: {"choices": find_jsons(main_directory, "vocab.json"), "value": ""},
lambda: {"choices": get_available_voices(), "value": ""},
],
elem_class="refresh-button-class"
) )
tts_language = gr.Dropdown(
label="Language",
value="en",
choices=[
"en",
"es",
"fr",
"de",
"it",
"pt",
"pl",
"tr",
"ru",
"nl",
"cs",
"ar",
"zh",
"hu",
"ko",
"ja",
]
)
tts_text = gr.Textbox( tts_text = gr.Textbox(
label="Input Text:", label="Input Text:",
value="I've just fine tuned a text to speech language model and this is how it sounds. If it doesn't sound right, I will try a different Speaker Reference Audio file.", value="I've just fine tuned a text to speech language model and this is how it sounds. If it doesn't sound right, I will try a different Speaker Reference Audio file.",
@@ -1220,22 +1593,25 @@ if __name__ == "__main__":
tts_output_audio = gr.Audio(label="TTS Generated Audio.") tts_output_audio = gr.Audio(label="TTS Generated Audio.")
reference_audio = gr.Audio(label="Speaker Reference Audio Used.") reference_audio = gr.Audio(label="Speaker Reference Audio Used.")
with gr.Tab("What to do next"): with gr.Tab("🔜 What to do next"):
gr.Markdown( gr.Markdown(
f""" f"""
## <u>What to do next</u><br> ### 🔜 <u>What to do next</u><br>
### 🟦 <u>What you need to do</u> ### 🟦 <u>What you need to do</u>
#### &nbsp;&nbsp;&nbsp;&nbsp; - You have a few options below: You have a few options below:<br>
#### &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 🔹 Compact &amp; move model. This will compress the raw finetuned model and move it, along with any large enough wav files created to one of two locations (please read text next to the button).<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; You can use the wav files in that location as sample voices with your model. Models moved to <span style="color: #3366ff;">/models/trainedmodel/</span> will be loadable within Text-gen-webui on the <span style="color: #3366ff;">XTTSv2 FT</span> loader. ◽ **Compact &amp; move model:** This will compress the raw finetuned model and move it, along with any large enough wav files created to one of two locations (please read text next to the button). You can use the wav files in that location as sample voices with your model. Models moved to <span style="color: #3366ff;">/models/trainedmodel/</span> will be loadable within Text-gen-webui on the <span style="color: #3366ff;">XTTSv2 FT</span> loader.<br>
#### &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 🔹 Delete Generated Training data. This will delete the finetuned model and any other training data generated during this process. You will more than likely want to do this after you have Compacted &amp; moved the model. ◽ **Delete Generated Training data:** This will delete the finetuned model and any other training data generated during this process. You will more than likely want to do this after you have Compacted &amp; moved the model.<br>
#### &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 🔹 Delete original voice samples. This will delete the voice samples you placed in put-voice-samples-in-here (if you no longer have a use for them). ◽ **Delete original voice samples:** This will delete the voice samples you placed in put-voice-samples-in-here (if you no longer have a use for them).<br>
#### &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 🔹 Compress a legacy finetuned model. For any models that were generated pre the current version of finetuning, you can still compact them down. ◽ **Note:** Sample wav files will be copied into the model folder. You will need to MANUALLY copy/move the WAV file(s) you want to use to the AllTalk voices folder, to make them available within the interface.<br>
#### &nbsp;&nbsp;&nbsp;&nbsp; - Instructions on using the finetuned model in Text-generation-webui and also further tuning this model you just created are on <span style="color: #3366ff;">OPTION A - Compact and move model to '/trainedmodel/'</span>. ◽ **Note:** Text-generation-webui users, <span style="color: #3366ff;">OPTION A - Compact and move model to '/trainedmodel/'</span>. will make the model available for use within Text-gen-webui.<br>
### 🟩 <u>Using your Finetuned model as you main model</u>
◽ If you wish to use the model you have just fintuned as your main model, you will need to MANUALLY copy it over the base model in <span style="color: #3366ff;">/models/trainedmodel/</span> after you have used Option A or B.<br>
◽ Should you ever wish to revert your model back to the base model, you can delete the files in <span style="color: #3366ff;">/models/xttsv2_2.0.2/</span> and new copies will be re-downloaded when AllTalk starts up.<br>
### 🟨 <u>Clearing up disk space</u> ### 🟨 <u>Clearing up disk space</u>
#### &nbsp;&nbsp;&nbsp;&nbsp; - If you are not going to train anything again, you can delete the whisper model from inside of your huggingface cache (3GB approx) ◽ If you are not going to train anything again, you can delete the whisper model from inside of your huggingface cache (3GB approx) <br>
#### &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Linux <span style="color: #3366ff;">~/.cache/huggingface/hub/(folder-here)</span> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ◽ **Linux:** <span style="color: #3366ff;">~/.cache/huggingface/hub/(folder-here)</span><br>
#### &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Windows <span style="color: #3366ff;">C:&bsol;users&lpar;your-username&rpar;&bsol;.cache&bsol;huggingface&bsol;hub&bsol;(folder-here)</span>. &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ◽ **Windows:** <span style="color: #3366ff;">C:&bsol;users&lpar;your-username&rpar;&bsol;.cache&bsol;huggingface&bsol;hub&bsol;(folder-here)</span>.<br>
#### &nbsp;&nbsp;&nbsp;&nbsp; - You can also uninstall the Nvidia CUDA 11.8 Toolkit if you wish and remove your environment variable entries. ◽ You can also uninstall the Nvidia CUDA 11.8 Toolkit if you wish and remove your environment variable entries.<br>
""" """
) )
final_progress_data = gr.Label( final_progress_data = gr.Label(
@@ -1245,35 +1621,28 @@ if __name__ == "__main__":
compact_btn = gr.Button(value="OPTION A - Compact and move model to '/trainedmodel/'") compact_btn = gr.Button(value="OPTION A - Compact and move model to '/trainedmodel/'")
gr.Markdown( gr.Markdown(
f""" f"""
##### This will compact the model and move it, along with the reference wav's to <span style="color: #3366ff;">/models/trainedmodel/</span> and will <span style="color: red;">OVERWRITE</span> any model in that location. This model will become available to load in Text-gen-webui with the <span style="color: #3366ff;">XTTSv2 FT</span> loader. It will also be detected by Finetune as a model that can be further trained (Reload finetune.py). This will compact the model and move it, along with the reference wav's to <span style="color: #3366ff;">/models/trainedmodel/</span> and will <span style="color: red;">OVERWRITE</span> any model in that location. This model will become available to load in Text-gen-webui with the <span style="color: #3366ff;">XTTSv2 FT</span> loader. It will also be detected by Finetune as a model that can be further trained (Reload finetune.py).
""" """
) )
with gr.Row(): with gr.Row():
compact_lastfinetuned_btn = gr.Button(value="OPTION B - Compact and move model to '/lastfinetuned/'") compact_lastfinetuned_btn = gr.Button(value="OPTION B - Compact and move model to '/lastfinetuned/'")
gr.Markdown( gr.Markdown(
f""" f"""
##### This will compact the model and move it, along with the reference wav's to <span style="color: #3366ff;">/models/lastfinetuned/</span> and will <span style="color: red;">OVERWRITE</span> any model in that location. This will just leave you a finetuned model that you can do with as you please. It will <span style="color: red;">NOT</span> be available in Text-gen-webui unless you copy it over one of the other model loader locations. This will compact the model and move it, along with the reference wav's to <span style="color: #3366ff;">/models/lastfinetuned/</span> and will <span style="color: red;">OVERWRITE</span> any model in that location. This will just leave you a finetuned model that you can do with as you please. It will <span style="color: red;">NOT</span> be available in Text-gen-webui unless you copy it over one of the other model loader locations.
""" """
) )
with gr.Row(): with gr.Row():
delete_training_btn = gr.Button(value="Delete generated training data") delete_training_btn = gr.Button(value="Delete generated training data")
gr.Markdown( gr.Markdown(
f""" f"""
##### This will <span style="color: red;">DELETE</span> your training data and the raw finetuned model from <span style="color: #3366ff;">/finetune/tmp-trn/</span>. This will <span style="color: red;">DELETE</span> your training data and the raw finetuned model from <span style="color: #3366ff;">/finetune/tmp-trn/</span>.
""" """
) )
with gr.Row(): with gr.Row():
delete_voicesamples_btn = gr.Button(value="Delete original voice samples") delete_voicesamples_btn = gr.Button(value="Delete original voice samples")
gr.Markdown( gr.Markdown(
f""" f"""
##### This will <span style="color: red;">DELETE</span> your original voice samples from <span style="color: #3366ff;">/finetune/put-voice-samples-in-here/</span>. This will <span style="color: red;">DELETE</span> your original voice samples from <span style="color: #3366ff;">/finetune/put-voice-samples-in-here/</span>.
"""
)
with gr.Row():
compact_legacy_btn = gr.Button(value="Compact a legacy finetuned model")
gr.Markdown(
f"""
##### This will compact your old finetuned models from 5GB to 1.9GB. Place your 5GB <span style="color: #3366ff;">base_model.pth</span> file in <span style="color: #3366ff;">/finetune/</span> (rename your file to <span style="color: #3366ff;">base_model.pth</span> if necessary). This will generate you a <span style="color: #3366ff;">model.pth</span> file.
""" """
) )
@@ -1344,10 +1713,6 @@ if __name__ == "__main__":
fn=delete_voice_sample_contents, fn=delete_voice_sample_contents,
outputs=[final_progress_data], outputs=[final_progress_data],
) )
compact_legacy_btn.click(
fn=compact_legacy_model,
outputs=[final_progress_data],
)
model_to_train.change(basemodel_or_finetunedmodel_choice, model_to_train, None) model_to_train.change(basemodel_or_finetunedmodel_choice, model_to_train, None)
demo.queue().launch( demo.queue().launch(
-26
View File
@@ -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.")
+4 -4
View File
@@ -45,18 +45,18 @@ else:
) )
# Read the version specifier from requirements_nvidia.txt # 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() requirements = req_file.readlines()
tts_version_required = None tts_version_required = None
for req in requirements: for req in requirements:
if req.startswith("TTS>="): if req.startswith("TTS=="):
tts_version_required = req.strip().split(">=")[1] tts_version_required = req.strip().split("==")[1]
break break
if tts_version_required is None: if tts_version_required is None:
raise ValueError( raise ValueError(
f"[{params['branding']}Startup] \033[91mWarning\033[0m Could not find TTS version specifier in requirements_nvidia.txt" f"[{params['branding']}Startup] \033[91mWarning\033[0m Could not find TTS version specifier in requirements file"
) )
-7
View File
@@ -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
-18
View File
@@ -1,18 +0,0 @@
numpy>=1.22.0
soundfile>=0.12.1
uvicorn>=0.24.0.post1
transformers>=4.37.1
TTS>=0.21.3
torch>=2.1.0
torchaudio>=2.1.0
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
cutlet>=0.3.0
unidic-lite>=1.0.8
+48 -30
View File
@@ -85,7 +85,7 @@ except ModuleNotFoundError:
#### STARTUP VARIABLES #### #### STARTUP VARIABLES ####
########################### ###########################
# STARTUP VARIABLE - Import languges file for Gradio to be able to display them in the interface # 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) languages = json.load(f)
# Create a global lock # Create a global lock
process_lock = threading.Lock() process_lock = threading.Lock()
@@ -347,26 +347,26 @@ def send_reload_request(tts_method):
params["tts_method_xtts_local"] = False params["tts_method_xtts_local"] = False
params["tts_method_api_tts"] = True params["tts_method_api_tts"] = True
params["deepspeed_activate"] = False 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 tts_method_xtts_ft = False
elif tts_method == "API Local": elif tts_method == "API Local":
params["tts_method_api_tts"] = False params["tts_method_api_tts"] = False
params["tts_method_xtts_local"] = False params["tts_method_xtts_local"] = False
params["tts_method_api_local"] = True params["tts_method_api_local"] = True
params["deepspeed_activate"] = False 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 tts_method_xtts_ft = False
elif tts_method == "XTTSv2 Local": elif tts_method == "XTTSv2 Local":
params["tts_method_api_tts"] = False params["tts_method_api_tts"] = False
params["tts_method_api_local"] = False params["tts_method_api_local"] = False
params["tts_method_xtts_local"] = True 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 tts_method_xtts_ft = False
elif tts_method == "XTTSv2 FT": elif tts_method == "XTTSv2 FT":
params["tts_method_api_tts"] = False params["tts_method_api_tts"] = False
params["tts_method_api_local"] = False params["tts_method_api_local"] = False
params["tts_method_xtts_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 tts_method_xtts_ft = True
return f'<audio src="file/{audio_path}" controls autoplay></audio>' return f'<audio src="file/{audio_path}" controls autoplay></audio>'
except requests.exceptions.RequestException as e: except requests.exceptions.RequestException as e:
@@ -385,9 +385,9 @@ def send_lowvram_request(low_vram):
try: try:
params["tts_model_loaded"] = False params["tts_model_loaded"] = False
if low_vram: if low_vram:
audio_path = this_dir / "templates" / "lowvramenabled.wav" audio_path = this_dir / "system" / "at_sounds" / "lowvramenabled.wav"
else: 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}" url = f"{base_url}/api/lowvramsetting?new_low_vram_value={low_vram}"
headers = {"Content-Type": "application/json"} headers = {"Content-Type": "application/json"}
response = requests.post(url, headers=headers) response = requests.post(url, headers=headers)
@@ -414,9 +414,9 @@ def send_deepspeed_request(deepspeed_param):
try: try:
params["tts_model_loaded"] = False params["tts_model_loaded"] = False
if deepspeed_param: if deepspeed_param:
audio_path = this_dir / "templates" / "deepspeedenabled.wav" audio_path = this_dir / "system" / "at_sounds" / "deepspeedenabled.wav"
else: 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}" url = f"{base_url}/api/deepspeed?new_deepspeed_value={deepspeed_param}"
headers = {"Content-Type": "application/json"} headers = {"Content-Type": "application/json"}
@@ -511,7 +511,7 @@ def combine(audio_files, output_folder, state):
################################ ################################
# PREVIEW VOICE - Generate Random Sentence if Voice Preview box is empty # PREVIEW VOICE - Generate Random Sentence if Voice Preview box is empty
def random_sentence(): 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)) return random.choice(list(f))
@@ -557,6 +557,10 @@ def voice_preview(string):
return f"[{params['branding']}Server] Audio generation failed. Status: {generate_response.get('status')}" return f"[{params['branding']}Server] Audio generation failed. Status: {generate_response.get('status')}"
#######################
#### TEXT CLEANING ####
#######################
def process_text(text): def process_text(text):
# Normalize HTML encoded quotes # Normalize HTML encoded quotes
text = html.unescape(text) text = html.unescape(text)
@@ -606,6 +610,30 @@ def process_text(text):
return ordered_parts 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 #### #### TTS STANDARD GENERATION ####
################################# #################################
@@ -613,12 +641,12 @@ def process_text(text):
def output_modifier(string, state): def output_modifier(string, state):
if not params["activate"]: if not params["activate"]:
return string return string
original_string = string img_info = ""
# print("ORIGINAL STRING IS:", original_string) cleaned_text, img_info = extract_and_remove_images(string)
cleaned_string = before_audio_generation(string, params) # print("Cleaned STRING IS:", cleaned_text)
cleaned_string = before_audio_generation(cleaned_text, params)
if cleaned_string is None: if cleaned_string is None:
return return
string = cleaned_string
language_code = languages.get(params["language"]) language_code = languages.get(params["language"])
temperature = params["local_temperature"] temperature = params["local_temperature"]
repetition_penalty = params["local_repetition_penalty"] repetition_penalty = params["local_repetition_penalty"]
@@ -627,7 +655,7 @@ def output_modifier(string, state):
if process_lock.acquire(blocking=False): if process_lock.acquire(blocking=False):
try: try:
if params["narrator_enabled"]: if params["narrator_enabled"]:
processed_parts = process_text(original_string) processed_parts = process_text(cleaned_string)
audio_files_all_paragraphs = [] audio_files_all_paragraphs = []
for part_type, part in processed_parts: for part_type, part in processed_parts:
@@ -686,7 +714,7 @@ def output_modifier(string, state):
) )
else: else:
# Decode HTML entities first # 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 # Replace multiple instances of certain punctuation marks with a single instance
cleaned_part = re.sub(r"([!?.])\1+", r"\1", cleaned_part) cleaned_part = re.sub(r"([!?.])\1+", r"\1", cleaned_part)
# Further clean to remove any other unwanted characters # Further clean to remove any other unwanted characters
@@ -729,25 +757,15 @@ def output_modifier(string, state):
if audio_path: if audio_path:
# Handle Gradio and playback # Handle Gradio and playback
autoplay = "autoplay" if params["autoplay"] else "" autoplay = "autoplay" if params["autoplay"] else ""
string = ( string = (f'<audio src="file/{final_output_file}" controls {autoplay}></audio>')
f'<audio src="file/{final_output_file}" controls {autoplay}></audio>'
)
if params["show_text"]: if params["show_text"]:
string += f"\n\n{original_string}" string += reinsert_images(cleaned_string, img_info)
shared.processing_message = "*Is typing...*" shared.processing_message = "*Is typing...*"
return string return string
else: else:
print( print(f"[{params['branding']}Server] \033[91mWarning\033[0m No audio path in the response.")
f"[{params['branding']}Server] \033[91mWarning\033[0m No audio path in the response."
)
else: else:
print( print(f"[{params['branding']}Server] \033[91mWarning\033[0m Audio generation failed. Status:", generate_response.get("message"),)
f"[{params['branding']}Server] \033[91mWarning\033[0m Audio generation failed. Status:",
generate_response.get("message"),
)
def get_output_filename(state): def get_output_filename(state):
if "character_menu" in state: if "character_menu" in state:
+2614
View File
File diff suppressed because it is too large. Load diff
Binary file not shown.

After

Width:  |  Height:  |  Size: 440 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

@@ -89,25 +89,25 @@
<h3>AllTalk Startup Settings</h3><br> <h3>AllTalk Startup Settings</h3><br>
<form action="/update-settings" method="post"> <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=""> <select id="activate" name="activate" required="">
<option {% if data.activate %}selected{% endif %} value="true">True</option> <option {% if data.activate %}selected{% endif %} value="true">True</option>
<option {% if not data.activate %}selected{% endif %} value="false">False</option> <option {% if not data.activate %}selected{% endif %} value="false">False</option>
</select> </select>
<label for="autoplay" style="color: #33cc33;">&nbsp; &nbsp; Autoplay TTS output:</label> <label for="autoplay" style="color: #b273fa;">&nbsp; &nbsp; TG-webui Autoplay TTS:</label>
<select id="autoplay" name="autoplay" required=""> <select id="autoplay" name="autoplay" required="">
<option {% if data.autoplay %}selected{% endif %} value="true">True</option> <option {% if data.autoplay %}selected{% endif %} value="true">True</option>
<option {% if not data.autoplay %}selected{% endif %} value="false">False</option> <option {% if not data.autoplay %}selected{% endif %} value="false">False</option>
</select> </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=""> <select id="show_text" name="show_text" required="">
<option {% if data.show_text %}selected{% endif %} value="true">True</option> <option {% if data.show_text %}selected{% endif %} value="true">True</option>
<option {% if not data.show_text %}selected{% endif %} value="false">False</option> <option {% if not data.show_text %}selected{% endif %} value="false">False</option>
</select> </select>
<label for="voice" style="color: #33cc33;">&nbsp; &nbsp; Default voice:</label> <label for="voice" style="color: #b273fa;">&nbsp; &nbsp; TG-webui Default voice:</label>
<select id="voice" name="voice" required=""> <select id="voice" name="voice" required="">
{% for file in wav_files %} {% for file in wav_files %}
<option value="{{ file }}" {% if data.voice==file %}selected{% endif %}>{{ file }}</option> <option value="{{ file }}" {% if data.voice==file %}selected{% endif %}>{{ file }}</option>
@@ -115,7 +115,7 @@
</select> </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=""> <select id="language" name="language" required="">
<option value="Arabic" {% if data.language=='Arabic' %}selected{% endif %}>Arabic</option> <option value="Arabic" {% if data.language=='Arabic' %}selected{% endif %}>Arabic</option>
<option value="Chinese" {% if data.language=='Chinese' %}selected{% endif %}>Chinese</option> <option value="Chinese" {% if data.language=='Chinese' %}selected{% endif %}>Chinese</option>
@@ -135,20 +135,20 @@
<option value="Turkish" {% if data.language=='Turkish' %}selected{% endif %}>Turkish</option> <option value="Turkish" {% if data.language=='Turkish' %}selected{% endif %}>Turkish</option>
</select> </select>
<label for="narrator_voice" style="color: #33cc33;">&nbsp; &nbsp; Narrator voice:</label> <label for="narrator_voice" style="color: #b273fa;">&nbsp; &nbsp; TG-webui Narrator voice:</label>
<select id="narrator_voice" name="narrator_voice" required=""> <select id="narrator_voice" name="narrator_voice" required="">
{% for file in wav_files %} {% for file in wav_files %}
<option value="{{ file }}" {% if data.narrator_voice==file %}selected{% endif %}>{{ file }}</option> <option value="{{ file }}" {% if data.narrator_voice==file %}selected{% endif %}>{{ file }}</option>
{% endfor %} {% endfor %}
</select> </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=""> <select id="remove_trailing_dots" name="remove_trailing_dots" required="">
<option {% if data.remove_trailing_dots %}selected{% endif %} value="true">True</option> <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> <option {% if not data.remove_trailing_dots %}selected{% endif %} value="false">False</option>
</select> </select>
<label for="narrator_enabled" style="color: #33cc33;">&nbsp; &nbsp; Narrator enabled:</label> <label for="narrator_enabled" style="color: #b273fa;">&nbsp; &nbsp; TG-webui Narrator enabled:</label>
<select id="narrator_enabled" name="narrator_enabled" required=""> <select id="narrator_enabled" name="narrator_enabled" required="">
<option {% if data.narrator_enabled %}selected{% endif %} value="true">True</option> <option {% if data.narrator_enabled %}selected{% endif %} value="true">True</option>
<option {% if not data.narrator_enabled %}selected{% endif %} value="false">False</option> <option {% if not data.narrator_enabled %}selected{% endif %} value="false">False</option>
Binary file not shown.

After

Width:  |  Height:  |  Size: 445 KiB

File renamed without changes.

Before

Width:  |  Height:  |  Size: 476 KiB

After

Width:  |  Height:  |  Size: 476 KiB

File renamed without changes.

Before

Width:  |  Height:  |  Size: 1.1 KiB

After

Width:  |  Height:  |  Size: 1.1 KiB

File renamed without changes.

Before

Width:  |  Height:  |  Size: 547 KiB

After

Width:  |  Height:  |  Size: 547 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 171 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 172 KiB

File renamed without changes.

Before

Width:  |  Height:  |  Size: 156 KiB

After

Width:  |  Height:  |  Size: 156 KiB

File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.

Before

Width:  |  Height:  |  Size: 1.1 KiB

After

Width:  |  Height:  |  Size: 1.1 KiB

+869
View File
@@ -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,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
@@ -2,7 +2,7 @@ numpy>=1.22.0
soundfile>=0.12.1 soundfile>=0.12.1
uvicorn>=0.24.0.post1 uvicorn>=0.24.0.post1
transformers>=4.37.1 transformers>=4.37.1
TTS>=0.21.3 TTS>=0.22.0
torch>=2.1.0+cu118 torch>=2.1.0+cu118
torchaudio>=2.1.0+cu118 torchaudio>=2.1.0+cu118
fastapi>=0.104.1 fastapi>=0.104.1
@@ -16,3 +16,9 @@ sounddevice>=0.4.6
python-multipart>=0.0.6 python-multipart>=0.0.6
cutlet>=0.3.0 cutlet>=0.3.0
unidic-lite>=1.0.8 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
File renamed without changes.
File renamed without changes.
File renamed without changes.
+269
View File
@@ -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()
File renamed without changes.
Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

File renamed without changes.
@@ -366,6 +366,11 @@
/* Other styles for highlighting */ /* Other styles for highlighting */
} }
.problematic {
background-color: rgb(179, 104, 104);
/* Or any light red color of your choice */
}
.highlight-count { .highlight-count {
color: #ff7300; color: #ff7300;
/* Change to the color you want */ /* Change to the color you want */
@@ -390,6 +395,13 @@
font-size: 0.8em; font-size: 0.8em;
} }
/*Disable and enable buttons*/
button:disabled {
background-color: #919191;
color: #666;
cursor: not-allowed;
}
.edit-input { .edit-input {
/* Styles for the edit input field */ /* Styles for the edit input field */
margin-top: 5px; margin-top: 5px;
@@ -562,7 +574,7 @@
<div class="setting slider-setting-wrapper"> <div class="setting slider-setting-wrapper">
<label for="volume">Volume:</label> <label for="volume">Volume:</label>
<div class="slider-container"> <div class="slider-container">
<input type="range" id="volume" name="volume" class="slider-setting" min="0.0" max="1.0" step="0.1" <input type="range" id="volume" name="volume" class="slider-setting" min="0.1" max="1.0" step="0.1"
value="0.8" oninput="updateVolumeValue(this.value)"> value="0.8" oninput="updateVolumeValue(this.value)">
<span id="volumeValue">0.8</span> <span id="volumeValue">0.8</span>
</div> </div>
@@ -595,14 +607,17 @@
<div id="ttsListSection" class="tts-list-section"> <div id="ttsListSection" class="tts-list-section">
<h2>Generated TTS List</h2> <h2>Generated TTS List</h2>
<input type="number" id="startFromId" value="1" min="1" autocomplete="off" style="width: 60px; margin-right: 10px;"> <input type="number" id="startFromId" value="1" min="1" autocomplete="off" style="width: 60px; margin-right: 10px;">
<button id="playPauseList" onclick="togglePlayPauseList()" style="margin-right: 10px;">Play List</button> <button class="conditional-btn" id="playPauseList" onclick="togglePlayPauseList()" style="margin-right: 10px;">Play
<button onclick="stopPlayback()" style="margin-right: 10px;">Stop Playback</button> List</button>
<button id="clearList" onclick="clearTTSList()" style="margin-right: 10px;">Clear List</button> <button class="conditional-btn" onclick="stopPlayback()" style="margin-right: 10px;">Stop Playback</button>
<button onclick="exportToJSON()" style="margin-right: 10px;">Export List as JSON</button> <button class="conditional-btn" id="clearList" onclick="clearTTSList()" style="margin-right: 40px;">Clear
<button onclick="importFromJSON()" style="margin-right: 10px;">Import JSON to list</button> List</button>
<button onclick="exportMP3()" style="margin-right: 10px;">Export to Wav</button> <button onclick="importFromJSON()" style="margin-right: 10px;">Import JSON</button>
<label for="splitEvery">Split every:</label> <button class="conditional-btn" onclick="exportToJSON()" style="margin-right: 10px;">Export JSON</button>
<select id="splitEvery" name="splitEvery"> <button class="conditional-btn" id="exportsrttitles" style="margin-right: 10px;">Export SRT</button>
<button class="conditional-btn" onclick="exportMP3()" style="margin-right: 10px;">Export to WAV</button>
<label for="splitEvery">Export Split:</label>
<select id="splitEvery" name="splitEvery" style="margin-right: 40px;">
<option value="100">100</option> <option value="100">100</option>
<option value="200">200</option> <option value="200">200</option>
<option value="300">300</option> <option value="300">300</option>
@@ -610,22 +625,38 @@
<option value="500" selected>500</option> <option value="500" selected>500</option>
<option value="600">600</option> <option value="600">600</option>
</select> </select>
<button class="conditional-btn" id="saveAndAnalyzeTTS" style="margin-right: 10px;">Analyze TTS</button>
<label for="AnalyzeAccuracy">% Accuracy:</label>
<select id="AnalyzeAccuracy" name="AnalyzeAccuracy" style="margin-right: 10px;">
<option value="99">99</option>
<option value="98" selected>98</option>
<option value="97">97</option>
<option value="96">96</option>
<option value="95">95</option>
<option value="94">94</option>
<option value="93">93</option>
<option value="92">92</option>
<option value="91">91</option>
<option value="90">90</option>
</select>
<div id="paginationControls"> <div id="paginationControls">
<button id="prevPage" class="pagination-btn">Previous</button> <button class="conditional-btn pagination-btn" id="prevPage">Previous</button>
<span id="pageIndicator">Page 1 of X</span> <span id="pageIndicator">Page 1 of X</span>
<button id="nextPage" class="pagination-btn">Next</button> <button class="conditional-btn pagination-btn" id="nextPage">Next</button>
<input type="number" id="jumpToPage" class="pagination-input" min="1" placeholder="Jump to Page"> <input type="number" id="jumpToPage" class="conditional-btn pagination-input" min="1"
<button id="jumpButton" class="pagination-btn">Go</button> placeholder="Jump to Page">
<button class="conditional-btn pagination-btn" id="jumpButton">Go</button>
</div> </div>
<div id="ttsListDisplay" class="tts-list-display"> <div id="ttsListDisplay" class="tts-list-display">
<!-- Dynamically populated TTS details will go here --> <!-- Dynamically populated TTS details will go here -->
</div> </div>
<div id="paginationControls1"> <div id="paginationControls1">
<button id="prevPage1" class="pagination-btn">Previous</button> <button class="conditional-btn pagination-btn" id="prevPage1">Previous</button>
<span id="pageIndicator1">Page 1 of X</span> <span id="pageIndicator1">Page 1 of X</span>
<button id="nextPage1" class="pagination-btn">Next</button> <button class="conditional-btn pagination-btn" id="nextPage1">Next</button>
<input type="number" id="jumpToPage1" class="pagination-input" min="1" placeholder="Jump to Page"> <input type="number" id="jumpToPage1" class="conditional-btn pagination-input" min="1"
<button id="jumpButton1" class="pagination-btn">Go</button> placeholder="Jump to Page">
<button class="conditional-btn pagination-btn" id="jumpButton1">Go</button>
</div> </div>
</div> </div>
</div> </div>
@@ -686,8 +717,6 @@
</script> </script>
<script> <script>
///////////////////////////////////////// /////////////////////////////////////////
@@ -704,6 +733,7 @@
let isStreamInitialized = false; let isStreamInitialized = false;
let characterVoice = document.getElementById("character_voice_gen").value; let characterVoice = document.getElementById("character_voice_gen").value;
let ttsList = []; // Global list to store TTS details let ttsList = []; // Global list to store TTS details
let problematicIds = []; // Global list to store ProblematicIDs
const howlInstances = {}; const howlInstances = {};
let listPlaying = false; let listPlaying = false;
let listPlayIndex = 0; let listPlayIndex = 0;
@@ -759,6 +789,123 @@
} }
//////////////////////////////////
//////// Compare TTS list ////////
//////////////////////////////////
document.getElementById('saveAndAnalyzeTTS').addEventListener('click', async function () {
showProgress(); // Call this function to show the progress indicator at the start of the process
try {
// Serialize the ttsList data structure to a JSON string
const ttsDataJSON = JSON.stringify(ttsList);
// Send the serialized ttsList to the '/save-tts-data' endpoint
const saveResponse = await fetch('/api/save-tts-data', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: ttsDataJSON // Use the serialized JSON string as the request body
});
const saveData = await saveResponse.json();
// Log the server's response
console.log(saveData.message);
// If the server confirms the data was saved successfully, trigger analysis
if (saveData.message === "Data saved successfully") {
const accuracy = parseInt(document.getElementById('AnalyzeAccuracy').value) || 98;
const analyzeResponse = await fetch(`/api/trigger-analysis?threshold=${accuracy}`);
const analyzeData = await analyzeResponse.json();
if (analyzeData.summary.flagged_ids && analyzeData.summary.flagged_ids.length > 0) {
// Update the global problematicIds array with the new flagged IDs
problematicIds = analyzeData.summary.flagged_ids;
console.log("Initial problematicIds from analysis:", problematicIds); // Debugging: Check the initial array after receiving analysis results
// Call highlightProblems with the array of flagged IDs
highlightProblems(problematicIds);
} else {
console.log("No problematic IDs or error retrieving data.");
// Clear the problematicIds array since there are no problematic IDs
problematicIds = [];
highlightProblems(problematicIds);
}
// Optionally, you might want to provide feedback in the UI about the analysis status
} else {
throw new Error('Data save was unsuccessful.');
}
} catch (error) {
// Handle any errors that occurred during the fetch process
console.error('Error:', error);
} finally {
hideProgress(); // Hide the progress indicator when the process is complete or if an error occurs
}
});
function highlightProblems(idsToHighlight) {
// First, clear all previous 'problematic' highlights
document.querySelectorAll('.problematic').forEach(item => {
item.classList.remove('problematic');
});
// Now, highlight all the IDs that should still be problematic
idsToHighlight.forEach(id => {
const itemElement = document.getElementById(`item-${id}`);
if (itemElement) {
itemElement.classList.add('problematic');
}
});
}
/////////////////////////////////
//////// Export SRT Subs ////////
/////////////////////////////////
document.getElementById('exportsrttitles').addEventListener('click', async function () {
showProgress(); // Call this function to show the progress indicator at the start of the process
try {
const ttsDataJSON = JSON.stringify(ttsList);
const saveResponse = await fetch('/api/save-tts-data', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: ttsDataJSON
});
const saveData = await saveResponse.json();
console.log(saveData.message);
if (saveData.message === "Data saved successfully") {
// Trigger the SRT generation and handle file download
const analyzeResponse = await fetch(`/api/srt-generation`);
if (analyzeResponse.ok) {
// Create a temporary anchor element
const a = document.createElement('a');
// Create a blob URL from the response
const blob = await analyzeResponse.blob();
const url = window.URL.createObjectURL(blob);
a.href = url;
// Set the download name for the file
a.download = "subtitles.srt";
// Append the anchor to the document
document.body.appendChild(a);
// Trigger the download
a.click();
// Clean up by removing the anchor element and revoking the blob URL
window.URL.revokeObjectURL(url);
a.remove();
} else {
console.log("Error running SRT generation.");
}
} else {
throw new Error('Data save was unsuccessful.');
}
} catch (error) {
console.error('Error:', error);
} finally {
hideProgress(); // Hide the progress indicator when the process is complete or if an error occurs
}
});
/////////////////////////////////////////// ///////////////////////////////////////////
//////// TTS Generation Activities //////// //////// TTS Generation Activities ////////
/////////////////////////////////////////// ///////////////////////////////////////////
@@ -777,7 +924,14 @@
let cleanedText = textInput.replace(/ \- | \– /g, ' '); let cleanedText = textInput.replace(/ \- | \– /g, ' ');
cleanedText = cleanedText.replace(/%/g, " percent"); cleanedText = cleanedText.replace(/%/g, " percent");
cleanedText = cleanedText.replace(/\.\s'/g, ".'"); cleanedText = cleanedText.replace(/\.\s'/g, ".'");
cleanedText = cleanedText.replace(/(?<!\w)[']+(?!\w)/g, '');
cleanedText = cleanedText.replace(/"/g, "'");
cleanedText = cleanedText.replace(/[^a-zA-Z0-9\s\.,;:!?\-\'"À-ÿ\u0400-\u04FF\u0150\u0151\u0170\u0171]\$/g, ''); cleanedText = cleanedText.replace(/[^a-zA-Z0-9\s\.,;:!?\-\'"À-ÿ\u0400-\u04FF\u0150\u0151\u0170\u0171]\$/g, '');
// Replace dash followed by space with comma and space
cleanedText = cleanedText.replace(/-\s/g, '; ');
// Replace double dash "--" with semicolon "; "
cleanedText = cleanedText.replace(/--/g, '; ');
let sentences = cleanedText.split(/(?<=[.!?:])\s+/); let sentences = cleanedText.split(/(?<=[.!?:])\s+/);
if (!sentences) { if (!sentences) {
@@ -886,6 +1040,7 @@
// Refresh the list display to reflect the new pagination state // Refresh the list display to reflect the new pagination state
updateUIList(); updateUIList();
updateButtonStyles()
// Update the audio queue and UI // Update the audio queue and UI
const sound = new Howl({ const sound = new Howl({
@@ -902,11 +1057,50 @@
playNextLocalChunk(); playNextLocalChunk();
} }
} }
return result
} catch (error) { } catch (error) {
console.error("Error in generateAndCache:", error); console.error("Error in generateAndCache:", error);
} }
} }
async function regenerateTTS(id) {
console.log("Before regeneration:", problematicIds); // Debugging: Check the array before regeneration
// Find the details of the TTS to regenerate
const ttsDetails = ttsList.find(item => item.id === id);
if (!ttsDetails) return;
// Retrieve the current volume setting from the HTML input
const currentVolume = parseFloat(document.getElementById("volume").value);
// Call the TTS generation with the same details and current volume
const result = await generateAndCache(ttsDetails.text, ttsDetails.language, ttsDetails.characterVoice, currentVolume, id);
if (result && result.status === "generate-success") {
const index = problematicIds.indexOf(id);
if (index !== -1) {
problematicIds.splice(index, 1); // Remove the id from problematicIds
console.log(`ID ${id} removed from problematicIds. Updated array:`, problematicIds); // Debugging: Check the array after removing an ID
}
// Find the element for this ID and remove the 'problematic' class
const problemItemElement = document.getElementById(`item-${id}`);
if (problemItemElement) {
problemItemElement.classList.remove('problematic');
}
} else {
console.log(`Regeneration failed for ID ${id}.`, result); // If regeneration fails, log the result
}
// Refresh the UI list to reflect the new data
updateUIList(); // Assume this function exists and it updates the display list
// Reapply highlighting to the items still in problematicIds
highlightProblems(problematicIds);
console.log("After regeneration and re-highlighting:", problematicIds); // Debugging: Check the array after re-highlighting
}
async function generateWithoutPlayback(chunk, language, characterVoice, id = null) { async function generateWithoutPlayback(chunk, language, characterVoice, id = null) {
// Perform the generation process as usual but do not play the audio // Perform the generation process as usual but do not play the audio
try { try {
@@ -972,19 +1166,6 @@
} }
} }
async function regenerateTTS(id) {
// Find the details of the TTS to regenerate
const ttsDetails = ttsList.find(item => item.id === id);
if (!ttsDetails) return;
// Retrieve the current volume setting from the HTML input
const currentVolume = parseFloat(document.getElementById("volume").value);
// Call the TTS generation with the same details and current volume
await generateAndCache(ttsDetails.text, ttsDetails.language, ttsDetails.characterVoice, currentVolume, id);
}
function updateUIList() { function updateUIList() {
const listDisplay = document.getElementById('ttsListDisplay'); const listDisplay = document.getElementById('ttsListDisplay');
if (!listDisplay) { if (!listDisplay) {
@@ -1029,7 +1210,9 @@
document.getElementById('pageIndicator').innerText = `Page ${currentPage} of ${lastPage}`; document.getElementById('pageIndicator').innerText = `Page ${currentPage} of ${lastPage}`;
document.getElementById('pageIndicator1').innerText = `Page ${currentPage} of ${lastPage}`; document.getElementById('pageIndicator1').innerText = `Page ${currentPage} of ${lastPage}`;
document.getElementById('prevPage').disabled = (currentPage === 1); document.getElementById('prevPage').disabled = (currentPage === 1);
document.getElementById('prevPage1').disabled = (currentPage === 1);
document.getElementById('nextPage').disabled = (currentPage === lastPage); document.getElementById('nextPage').disabled = (currentPage === lastPage);
document.getElementById('nextPage1').disabled = (currentPage === lastPage);
} }
@@ -1240,7 +1423,7 @@
} else { } else {
// In chunk mode, toggle the chunk play/pause // In chunk mode, toggle the chunk play/pause
if (isPlaying && currentlyPlayingIndex > 0) { if (isPlaying && currentlyPlayingIndex > 0) {
let currentSound = audioQueue[currentlyPlayingIndex - 1]; let currentSound = audioQueue[currentlyPlayingIndex - 3];
if (currentSound.playing()) { if (currentSound.playing()) {
currentSound.pause(); currentSound.pause();
} else { } else {
@@ -1267,16 +1450,6 @@
} }
} }
function stopPlayback() {
Howler.stop();
audioQueue = [];
// Unload each sound in the queue
audioQueue.forEach(sound => sound.unload());
currentlyPlayingIndex = 0;
isPlaying = false;
}
function updatePlaybackButton() { function updatePlaybackButton() {
const playbackButton = document.querySelector(".toggle-playback"); const playbackButton = document.querySelector(".toggle-playback");
@@ -1451,38 +1624,64 @@
if (pageIndicator1) { if (pageIndicator1) {
pageIndicator1.textContent = 'Page 1 of 1'; pageIndicator1.textContent = 'Page 1 of 1';
} }
updateButtonStyles()
}
function encodeHtmlEntities(text) {
return text.replace(/"/g, '&quot;').replace(/'/g, '&#39;');
}
function decodeHtmlEntities(text) {
var textArea = document.createElement('textarea');
textArea.innerHTML = text;
return textArea.value;
} }
function editText(id) { function editText(id) {
console.log(`Initiating edit for ID: ${id}`);
console.log("Before edit, ttsList:", JSON.parse(JSON.stringify(ttsList)));
// First, check if we're already editing and should save the current value // First, check if we're already editing and should save the current value
const existingInput = document.getElementById(`edit-input-${id}`); const existingInput = document.getElementById(`edit-input-${id}`);
if (existingInput) { if (existingInput) {
console.log(`Existing input found for ID: ${id}, triggering blur event to save.`);
existingInput.blur(); // This will trigger the blur event, saving the current value existingInput.blur(); // This will trigger the blur event, saving the current value
return; // Don't proceed further since we're just saving the existing input return; // Don't proceed further since we're just saving the existing input
} }
const entryDiv = document.getElementById(`item-${id}`); const entryDiv = document.getElementById(`item-${id}`);
if (!entryDiv) { if (!entryDiv) {
console.error(`Item with ID ${id} not found.`); console.error(`Item with ID ${id} not found.`);
return; return;
} }
const textPreviewSpan = entryDiv.querySelector('.text-preview'); const textPreviewSpan = entryDiv.querySelector('.text-preview');
const fullText = textPreviewSpan.getAttribute('data-fulltext'); // Retrieve the full text and immediately encode it
let fullText = textPreviewSpan.getAttribute('data-fulltext');
fullText = decodeHtmlEntities(fullText);
//fullText = encodeHtmlEntities(fullText);
console.log(`Current full text for ID: ${id} is: ${fullText}`);
// Create an input field for editing // Create an input field for editing
const inputField = document.createElement('input'); const inputField = document.createElement('input');
inputField.type = 'text'; inputField.type = 'text';
// The value is already encoded at this point
inputField.value = fullText; inputField.value = fullText;
inputField.classList.add('edit-input'); inputField.classList.add('edit-input');
inputField.id = `edit-input-${id}`; // Assign an ID to the input field for later access inputField.id = `edit-input-${id}`; // Assign an ID to the input field for later access
// Handle saving the edited text // Handle saving the edited text
inputField.addEventListener('blur', function () { inputField.addEventListener('blur', function () {
console.log(`Saving edited text for ID: ${id}: ${this.value}`);
// Update the text in the ttsList array and the data-fulltext attribute // Update the text in the ttsList array and the data-fulltext attribute
const itemIndex = ttsList.findIndex(item => item.id === id); const itemIndex = ttsList.findIndex(item => item.id === id);
if (itemIndex !== -1) { if (itemIndex !== -1) {
console.log(`Updating ttsList with new text for ID: ${id}`);
ttsList[itemIndex].text = this.value; ttsList[itemIndex].text = this.value;
// If this item is in the list of problematic IDs, un-highlight it
if (problematicIds.includes(id)) {
console.log(`ID: ${id} is problematic, removing highlight class`);
const problemItemElement = document.getElementById(`item-${id}`);
problemItemElement.classList.remove('problematic');
}
} }
// Create a new span element to replace the input field // Create a new span element to replace the input field
@@ -1494,15 +1693,18 @@
newTextPreviewSpan.textContent = displayText; newTextPreviewSpan.textContent = displayText;
// Replace the input field with the new span // Replace the input field with the new span
console.log(`Replacing input field with text span for ID: ${id}`);
inputField.replaceWith(newTextPreviewSpan); inputField.replaceWith(newTextPreviewSpan);
}); });
// Replace the span with the input field // Replace the span with the input field
console.log(`Replacing text span with input field for ID: ${id}`);
textPreviewSpan.replaceWith(inputField); textPreviewSpan.replaceWith(inputField);
// Focus the input field and select its content to facilitate editing // Focus the input field and select its content to facilitate editing
inputField.focus(); inputField.focus();
inputField.select(); inputField.select();
console.log("After edit, ttsList:", JSON.parse(JSON.stringify(ttsList)));
} }
//////////////////////////////////////// ////////////////////////////////////////
@@ -1550,6 +1752,7 @@
ttsList = jsonData; ttsList = jsonData;
currentPage = 1; // Reset to the first page currentPage = 1; // Reset to the first page
updateUIList(); updateUIList();
updateButtonStyles()
updatePaginationControls(); // Update pagination controls updatePaginationControls(); // Update pagination controls
} catch (error) { } catch (error) {
console.error('Error parsing JSON:', error); console.error('Error parsing JSON:', error);
@@ -1563,34 +1766,42 @@
function updatePaginationControls() { function updatePaginationControls() {
const lastPage = Math.ceil(ttsList.length / itemsPerPage); const lastPage = Math.ceil(ttsList.length / itemsPerPage);
const pageIndicator = document.getElementById('pageIndicator'); const prevPageButtons = document.querySelectorAll('.pagination-btn[id^="prevPage"]');
if (pageIndicator) { const nextPageButtons = document.querySelectorAll('.pagination-btn[id^="nextPage"]');
pageIndicator.textContent = `Page ${currentPage} of ${lastPage}`;
}
const prevPageButton = document.getElementById('prevPage'); // Update page indicators and buttons
if (prevPageButton) { prevPageButtons.forEach(btn => btn.disabled = (currentPage === 1));
prevPageButton.disabled = (currentPage === 1); nextPageButtons.forEach(btn => btn.disabled = (currentPage === lastPage));
}
const nextPageButton = document.getElementById('nextPage'); // Assuming the page indicators are consistent with button IDs
if (nextPageButton) { document.querySelectorAll('span[id^="pageIndicator"]').forEach(indicator => {
nextPageButton.disabled = (currentPage === lastPage); indicator.textContent = `Page ${currentPage} of ${lastPage}`;
} });
// Similar updates for any additional pagination controls if you have them // Update 'Jump to Page' inputs and 'Go' buttons
// ... const jumpToPageInputs = document.querySelectorAll('.pagination-input[id^="jumpToPage"]');
const jumpButtons = document.querySelectorAll('.pagination-btn[id^="jumpButton"]');
const moreThanOnePage = lastPage > 1;
jumpToPageInputs.forEach(input => input.disabled = !moreThanOnePage);
jumpButtons.forEach(button => button.disabled = !moreThanOnePage);
} }
////////////////////////////// //////////////////////////////
//////// Other Things //////// //////// Other Things ////////
////////////////////////////// //////////////////////////////
function updateButtonStyles() {
const ttsList = document.getElementById('ttsListDisplay');
const buttons = document.querySelectorAll('.conditional-btn');
if (!ttsList.hasChildNodes()) { // Check if the list is empty
buttons.forEach(button => button.disabled = true); // Disable buttons
} else {
buttons.forEach(button => button.disabled = false); // Enable buttons
}
}
function updateLanguage(language) { function updateLanguage(language) {
// Update the language in your application logic // Update the language in your application logic
console.log("Selected language:", language); console.log("Selected language:", language);
@@ -1663,10 +1874,10 @@
function stopPlayback() { function stopPlayback() {
Howler.stop(); Howler.stop();
audioQueue = []; audioQueue = [];
currentlyPlayingIndex = 0; //currentlyPlayingIndex = 0;
isPlaying = false; isPlaying = false;
highlightPlaying(null); //highlightPlaying(null);
document.getElementById('startFromId').value = '1'; // Reset the startFromId input to 1 //document.getElementById('startFromId').value = '1'; // Reset the startFromId input to 1
} }
function displayOutput(result) { function displayOutput(result) {
@@ -1836,6 +2047,7 @@
function setupPage() { function setupPage() {
// Fetch voices or other initial data // Fetch voices or other initial data
fetchVoices(); fetchVoices();
updateButtonStyles()
// Check for dark mode preference in localStorage, default to enabled if not set // Check for dark mode preference in localStorage, default to enabled if not set
let isDarkModeEnabled = localStorage.getItem('darkMode'); let isDarkModeEnabled = localStorage.getItem('darkMode');
@@ -1989,8 +2201,8 @@
setupPage(); setupPage();
hideProgress(); // Ensure the progress indicator is hidden on page load hideProgress(); // Ensure the progress indicator is hidden on page load
if (window.location.protocol !== 'http:' && window.location.protocol !== 'https:') { if (window.location.protocol !== 'http:' && window.location.protocol !== 'https:') {
alert('AllTalk TTS Generator is not designed to be run from your disk folder. Please run it from the correct URL e.g. http://127.0.0.1:7851/static/tts_chunk_player/tts_chunk_player.html'); alert('AllTalk TTS Generator is not designed to be run from your disk folder. Please run it from the correct URL e.g. http://127.0.0.1:7851/static/tts_chunk_player/tts_chunk_player.html');
} }
}; };
</script> </script>
+77
View File
@@ -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}")
File renamed without changes.

Before

Width:  |  Height:  |  Size: 30 KiB

After

Width:  |  Height:  |  Size: 30 KiB

File renamed without changes.

Before

Width:  |  Height:  |  Size: 839 B

After

Width:  |  Height:  |  Size: 839 B

File renamed without changes.

Before

Width:  |  Height:  |  Size: 2.6 KiB

After

Width:  |  Height:  |  Size: 2.6 KiB

File renamed without changes.

Before

Width:  |  Height:  |  Size: 8.8 KiB

After

Width:  |  Height:  |  Size: 8.8 KiB

File renamed without changes.

Before

Width:  |  Height:  |  Size: 13 KiB

After

Width:  |  Height:  |  Size: 13 KiB

File renamed without changes.

Before

Width:  |  Height:  |  Size: 99 KiB

After

Width:  |  Height:  |  Size: 99 KiB

File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
-1631
View File
File diff suppressed because it is too large. Load diff
+69 -6
View File
@@ -19,6 +19,7 @@ from fastapi import (
Response, Response,
Depends, Depends,
HTTPException, HTTPException,
Query,
) )
from fastapi.responses import JSONResponse, HTMLResponse, RedirectResponse, FileResponse, StreamingResponse from fastapi.responses import JSONResponse, HTMLResponse, RedirectResponse, FileResponse, StreamingResponse
from fastapi.middleware.cors import CORSMiddleware from fastapi.middleware.cors import CORSMiddleware
@@ -34,7 +35,7 @@ this_dir = Path(__file__).parent.resolve()
# STARTUP VARIABLE - Set "device" to cuda if exists, otherwise cpu # STARTUP VARIABLE - Set "device" to cuda if exists, otherwise cpu
device = "cuda" if torch.cuda.is_available() else "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 # 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) languages = json.load(f)
# Base setting for a possible FineTuned model existing and the loader being available # Base setting for a possible FineTuned model existing and the loader being available
tts_method_xtts_ft = False tts_method_xtts_ft = False
@@ -638,7 +639,7 @@ def list_files(directory):
############################# #############################
# Create an instance of Jinja2Templates for rendering HTML templates # 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 # Create a dependency to get the current JSON data
def get_json_data(): def get_json_data():
@@ -653,7 +654,7 @@ async def get_settings(request: Request):
wav_files = list_files(this_dir / "voices") wav_files = list_files(this_dir / "voices")
# Render the template with the current JSON data and list of WAV files # Render the template with the current JSON data and list of WAV files
return templates.TemplateResponse( return templates.TemplateResponse(
"generate_form.html", "/at_admin/at_settings.html",
{ {
"request": request, "request": request,
"data": get_json_data(), "data": get_json_data(),
@@ -663,7 +664,7 @@ async def get_settings(request: Request):
) )
# Define an endpoint to serve static files # 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") @app.post("/update-settings")
async def update_settings( async def update_settings(
@@ -870,7 +871,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 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") 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 from pydantic import BaseModel, ValidationError, Field
def play_audio(file_path, volume): def play_audio(file_path, volume):
@@ -1145,7 +1146,69 @@ def get_current_settings():
#### Word Add-in Sharing #### #### Word Add-in Sharing ####
############################# #############################
# Mount the static files from the 'word_addin' directory # 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 #### #### Webserver Startup & Initial model Loading ####
Binary file not shown.
+3 -1
View File
@@ -9,4 +9,6 @@ Author BBC
This file is licensed under the Creative Commons Attribution 3.0 Unported license. This file is licensed under the Creative Commons Attribution 3.0 Unported license.
https://creativecommons.org/licenses/by/3.0/deed.en 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» ).