Updated Parler to the v1 model (mini and large available)

This commit is contained in:
erew123 authored and GitHub committed 2024-08-09 10:32:47 +01:00
1 parent df764f71ef
commit c1c7b8cf65
5 files changed
+1188 -1000

No files matched your search

@@ -13,6 +13,21 @@
"https://huggingface.co/parler-tts/parler-tts-mini-v1/resolve/main/tokenizer_config.json?download=true", "https://huggingface.co/parler-tts/parler-tts-mini-v1/resolve/main/tokenizer_config.json?download=true",
"https://huggingface.co/parler-tts/parler-tts-mini-v1/resolve/main/model.safetensors?download=true" "https://huggingface.co/parler-tts/parler-tts-mini-v1/resolve/main/model.safetensors?download=true"
] ]
},
{
"model_name": "parler-tts-large-v1",
"files_to_download": [
"https://huggingface.co/parler-tts/parler-tts-large-v1/resolve/main/config.json?download=true",
"https://huggingface.co/parler-tts/parler-tts-large-v1/resolve/main/generation_config.json?download=true",
"https://huggingface.co/parler-tts/parler-tts-large-v1/resolve/main/preprocessor_config.json?download=true",
"https://huggingface.co/parler-tts/parler-tts-large-v1/resolve/main/special_tokens_map.json?download=true",
"https://huggingface.co/parler-tts/parler-tts-large-v1/resolve/main/spiece.model?download=true",
"https://huggingface.co/parler-tts/parler-tts-large-v1/resolve/main/tokenizer.json?download=true",
"https://huggingface.co/parler-tts/parler-tts-large-v1/resolve/main/tokenizer_config.json?download=true",
"https://huggingface.co/parler-tts/parler-tts-large-v1/resolve/main/model.safetensors.index.json?download=true",
"https://huggingface.co/parler-tts/parler-tts-large-v1/resolve/main/model-00001-of-00002.safetensors?download=true",
"https://huggingface.co/parler-tts/parler-tts-large-v1/resolve/main/model-00002-of-00002.safetensors?download=true"
]
} }
] ]
} }
+470 -463
View File
@@ -1,463 +1,470 @@
############################################### ###############################################
# DONT CHANGE # These are base imports needed # # DONT CHANGE # These are base imports needed #
############################################### ###############################################
import os import os
import sys import sys
import json import json
import time import time
import torch import torch
import logging import logging
from pathlib import Path from pathlib import Path
from fastapi import (HTTPException) from fastapi import (HTTPException)
logging.disable(logging.WARNING) logging.disable(logging.WARNING)
############################################### ###############################################
# DONT CHANGE # Get Pytorch & Python versions # # DONT CHANGE # Get Pytorch & Python versions #
############################################### ###############################################
pytorch_version = torch.__version__ pytorch_version = torch.__version__
cuda_version = torch.version.cuda cuda_version = torch.version.cuda
major, minor, micro = sys.version_info[:3] major, minor, micro = sys.version_info[:3]
python_version = f"{major}.{minor}.{micro}" python_version = f"{major}.{minor}.{micro}"
try: try:
import deepspeed import deepspeed
deepspeed_available = True deepspeed_available = True
except ImportError: except ImportError:
deepspeed_available = False deepspeed_available = False
pass pass
############################################################################################################# #############################################################################################################
############################################################################################################# #############################################################################################################
# CHANGE ME # Run any specifc imports, requirements or setup any global vaiables needed for this TTS Engine # # CHANGE ME # Run any specifc imports, requirements or setup any global vaiables needed for this TTS Engine #
############################################################################################################# #############################################################################################################
############################################################################################################# #############################################################################################################
# In this section you will import any imports that your specific TTS Engine will use. You will provide any # In this section you will import any imports that your specific TTS Engine will use. You will provide any
# start-up errors for those bits, as if you were starting up a normal Python script. Note the logging.disable # start-up errors for those bits, as if you were starting up a normal Python script. Note the logging.disable
# a few lines up from here, you may want to # that out while debugging! # a few lines up from here, you may want to # that out while debugging!
from parler_tts import ParlerTTSForConditionalGeneration from parler_tts import ParlerTTSForConditionalGeneration
from transformers import AutoTokenizer from transformers import AutoTokenizer
import soundfile as sf import soundfile as sf
################################################################################################################################# #################################################################################################################################
# DONT CHANGE # Do not change the Class name from tts_class as this is what will be imported into the main tts_server.py script # # DONT CHANGE # Do not change the Class name from tts_class as this is what will be imported into the main tts_server.py script #
################################################################################################################################# #################################################################################################################################
class tts_class: class tts_class:
def __init__(self): def __init__(self):
######################################################################## ########################################################################
# DONT CHANGE # Sets up the base variables required for any tts engine # # DONT CHANGE # Sets up the base variables required for any tts engine #
######################################################################## ########################################################################
self.branding = None self.branding = None
self.this_dir = Path(__file__).parent.resolve() # Sets up self.this_dir as a variable for the folder THIS script is running in. self.this_dir = Path(__file__).parent.resolve() # Sets up self.this_dir as a variable for the folder THIS script is running in.
self.main_dir = Path(__file__).parent.parent.parent.parent.resolve() # Sets up self.main_dir as a variable for the folder AllTalk is running in self.main_dir = Path(__file__).parent.parent.parent.parent.resolve() # Sets up self.main_dir as a variable for the folder AllTalk is running in
self.device = "cuda" if torch.cuda.is_available() else "cpu" # Sets up self.device to cuda if torch exists with Nvidia/CUDA, otherwise sets to cpu self.device = "cuda" if torch.cuda.is_available() else "cpu" # Sets up self.device to cuda if torch exists with Nvidia/CUDA, otherwise sets to cpu
self.cuda_is_available = torch.cuda.is_available() # Sets up cuda_is_available as a True/False to track if Nvidia/CUDA was found on the system self.cuda_is_available = torch.cuda.is_available() # Sets up cuda_is_available as a True/False to track if Nvidia/CUDA was found on the system
self.tts_generating_lock = False # Used to lock and unlock the tts generation process at the start/end of tts generation. self.tts_generating_lock = False # Used to lock and unlock the tts generation process at the start/end of tts generation.
self.tts_stop_generation = False # Used in conjunction with tts_generating_lock to call for a stop to the current generation. If called (set True) it needs to be set back to False when generation has been stopped. self.tts_stop_generation = False # Used in conjunction with tts_generating_lock to call for a stop to the current generation. If called (set True) it needs to be set back to False when generation has been stopped.
self.tts_narrator_generatingtts = False # Used to track if the current tts processes is narrator based. This can be used in conjunction with lowvram and device to avoid moving model between GPU(CUDA)<>RAM(CPU) each chunk of narrated text generated. self.tts_narrator_generatingtts = False # Used to track if the current tts processes is narrator based. This can be used in conjunction with lowvram and device to avoid moving model between GPU(CUDA)<>RAM(CPU) each chunk of narrated text generated.
self.model = None # If loading a model into CUDA/VRAM/RAM "model" is used as the variable name to load and interact with (see the XTTS model_engine script for examples.) self.model = None # If loading a model into CUDA/VRAM/RAM "model" is used as the variable name to load and interact with (see the XTTS model_engine script for examples.)
self.is_tts_model_loaded = False # Used to track if a model is actually loaded in and error/fail things like TTS generation if its False self.is_tts_model_loaded = False # Used to track if a model is actually loaded in and error/fail things like TTS generation if its False
self.current_model_loaded = None # Stores the name of the currenly loaded in model self.current_model_loaded = None # Stores the name of the currenly loaded in model
self.available_models = None # List of available models found by "def scan_models_folder" self.available_models = None # List of available models found by "def scan_models_folder"
self.setup_has_run = False # Tracks if async def setup(self) has run, by setting to True, so that the /api/ready endpoint can provide a "Ready" status self.setup_has_run = False # Tracks if async def setup(self) has run, by setting to True, so that the /api/ready endpoint can provide a "Ready" status
############################################################################################## ##############################################################################################
# DONT CHANGE # Load in a list of the available TTS engines and the currently set TTS engine # # DONT CHANGE # Load in a list of the available TTS engines and the currently set TTS engine #
############################################################################################## ##############################################################################################
tts_engines_file = os.path.join(self.main_dir, "system", "tts_engines", "tts_engines.json") tts_engines_file = os.path.join(self.main_dir, "system", "tts_engines", "tts_engines.json")
with open(tts_engines_file, "r") as f: with open(tts_engines_file, "r") as f:
tts_engines_data = json.load(f) tts_engines_data = json.load(f)
self.engines_available = [engine["name"] for engine in tts_engines_data["engines_available"]] # A list of ALL the TTS engines available to be loaded by AllTalk self.engines_available = [engine["name"] for engine in tts_engines_data["engines_available"]] # A list of ALL the TTS engines available to be loaded by AllTalk
self.engine_loaded = tts_engines_data["engine_loaded"] # In "tts_engines.json" what is the currently set TTS engine loading into AllTalk self.engine_loaded = tts_engines_data["engine_loaded"] # In "tts_engines.json" what is the currently set TTS engine loading into AllTalk
self.selected_model = tts_engines_data["selected_model"] # In "tts_engines.json" what is the currently set TTS model loading into AllTalk self.selected_model = tts_engines_data["selected_model"] # In "tts_engines.json" what is the currently set TTS model loading into AllTalk
############################################################################ ############################################################################
# DONT CHANGE # Pull out all the settings for the currently set TTS engine # # DONT CHANGE # Pull out all the settings for the currently set TTS engine #
############################################################################ ############################################################################
with open(os.path.join(self.this_dir, "model_settings.json"), "r") as f: with open(os.path.join(self.this_dir, "model_settings.json"), "r") as f:
tts_model_loaded = json.load(f) tts_model_loaded = json.load(f)
# Access the model details # Access the model details
self.manufacturer_name = tts_model_loaded["model_details"]["manufacturer_name"] # The company/person/body that generated the TTS engine/models etc self.manufacturer_name = tts_model_loaded["model_details"]["manufacturer_name"] # The company/person/body that generated the TTS engine/models etc
self.manufacturer_website = tts_model_loaded["model_details"]["manufacturer_website"] # The website of the company/person/body where people can find more information self.manufacturer_website = tts_model_loaded["model_details"]["manufacturer_website"] # The website of the company/person/body where people can find more information
# Access the features the model is capable of: # Access the features the model is capable of:
self.audio_format = tts_model_loaded["model_capabilties"]["audio_format"] # This details the audio format your TTS engine is set to generate TTS in e.g. wav, mp3, flac, opus, acc, pcm. Please use only 1x format. self.audio_format = tts_model_loaded["model_capabilties"]["audio_format"] # This details the audio format your TTS engine is set to generate TTS in e.g. wav, mp3, flac, opus, acc, pcm. Please use only 1x format.
self.deepspeed_capable = tts_model_loaded["model_capabilties"]["deepspeed_capable"] # Is your model capable of DeepSpeed self.deepspeed_capable = tts_model_loaded["model_capabilties"]["deepspeed_capable"] # Is your model capable of DeepSpeed
self.deepspeed_available = 'deepspeed' in globals() # When we did the import earlier, at the top of this script, was DeepSpeed available for use self.deepspeed_available = 'deepspeed' in globals() # When we did the import earlier, at the top of this script, was DeepSpeed available for use
self.generationspeed_capable = tts_model_loaded["model_capabilties"]["generationspeed_capable"] # Does this TTS engine support changing the speed of the generated TTS self.generationspeed_capable = tts_model_loaded["model_capabilties"]["generationspeed_capable"] # Does this TTS engine support changing the speed of the generated TTS
self.languages_capable = tts_model_loaded["model_capabilties"]["languages_capable"] # Are the actual models themselves capable of generating in multiple languages OR is each model language specific self.languages_capable = tts_model_loaded["model_capabilties"]["languages_capable"] # Are the actual models themselves capable of generating in multiple languages OR is each model language specific
self.lowvram_capable = tts_model_loaded["model_capabilties"]["lowvram_capable"] # Is this engine capable of using low VRAM (moving the model between CPU And GPU memory) self.lowvram_capable = tts_model_loaded["model_capabilties"]["lowvram_capable"] # Is this engine capable of using low VRAM (moving the model between CPU And GPU memory)
self.multimodel_capable = tts_model_loaded["model_capabilties"]["multimodel_capable"] # Is there just the one model or are there multiple models this engine supports. self.multimodel_capable = tts_model_loaded["model_capabilties"]["multimodel_capable"] # Is there just the one model or are there multiple models this engine supports.
self.repetitionpenalty_capable = tts_model_loaded["model_capabilties"]["repetitionpenalty_capable"] # Is this TTS engine capable of changing the repititon penalty self.repetitionpenalty_capable = tts_model_loaded["model_capabilties"]["repetitionpenalty_capable"] # Is this TTS engine capable of changing the repititon penalty
self.streaming_capable = tts_model_loaded["model_capabilties"]["streaming_capable"] # Is this TTS engine capabale of generating streaming audio self.streaming_capable = tts_model_loaded["model_capabilties"]["streaming_capable"] # Is this TTS engine capabale of generating streaming audio
self.temperature_capable = tts_model_loaded["model_capabilties"]["temperature_capable"] # Is this TTS engine capable of changing the temperature of the models self.temperature_capable = tts_model_loaded["model_capabilties"]["temperature_capable"] # Is this TTS engine capable of changing the temperature of the models
self.multivoice_capable = tts_model_loaded["model_capabilties"]["multivoice_capable"] # Are the models multi-voice or single vocice models self.multivoice_capable = tts_model_loaded["model_capabilties"]["multivoice_capable"] # Are the models multi-voice or single vocice models
self.pitch_capable = tts_model_loaded["model_capabilties"]["pitch_capable"] # Is this TTS engine capable of changing the pitch of the genrated TTS self.pitch_capable = tts_model_loaded["model_capabilties"]["pitch_capable"] # Is this TTS engine capable of changing the pitch of the genrated TTS
# Access the current enginesettings # Access the current enginesettings
self.def_character_voice = tts_model_loaded["settings"]["def_character_voice"] # What is the current default main/character voice that will be used if no voice specified. self.def_character_voice = tts_model_loaded["settings"]["def_character_voice"] # What is the current default main/character voice that will be used if no voice specified.
self.def_narrator_voice = tts_model_loaded["settings"]["def_narrator_voice"] # What is the current default narrator voice that will be used if no voice specified. self.def_narrator_voice = tts_model_loaded["settings"]["def_narrator_voice"] # What is the current default narrator voice that will be used if no voice specified.
self.deepspeed_enabled = tts_model_loaded["settings"]["deepspeed_enabled"] # If its available, is DeepSpeed enabled for the TTS engine self.deepspeed_enabled = tts_model_loaded["settings"]["deepspeed_enabled"] # If its available, is DeepSpeed enabled for the TTS engine
self.engine_installed = tts_model_loaded["settings"]["engine_installed"] # Has the TTS engine been setup/installed (not curently used) self.engine_installed = tts_model_loaded["settings"]["engine_installed"] # Has the TTS engine been setup/installed (not curently used)
self.generationspeed_set = tts_model_loaded["settings"]["generationspeed_set"] # What is the set/stored speed for generation. self.generationspeed_set = tts_model_loaded["settings"]["generationspeed_set"] # What is the set/stored speed for generation.
self.lowvram_enabled = tts_model_loaded["settings"]["lowvram_enabled"] # If its available, is LowVRAM enabled for the TTS engine self.lowvram_enabled = tts_model_loaded["settings"]["lowvram_enabled"] # If its available, is LowVRAM enabled for the TTS engine
# Check if someone has enabled lowvram on a system that's not CUDA enabled # Check if someone has enabled lowvram on a system that's not CUDA enabled
self.lowvram_enabled = False if not torch.cuda.is_available() else self.lowvram_enabled # If LowVRAM is mistakenly set and CUDA is not available, this will force it back off self.lowvram_enabled = False if not torch.cuda.is_available() else self.lowvram_enabled # If LowVRAM is mistakenly set and CUDA is not available, this will force it back off
self.repetitionpenalty_set = tts_model_loaded["settings"]["repetitionpenalty_set"] # What is the currenly set repitition policy of the model (If it support repetition) self.repetitionpenalty_set = tts_model_loaded["settings"]["repetitionpenalty_set"] # What is the currenly set repitition policy of the model (If it support repetition)
self.temperature_set = tts_model_loaded["settings"]["temperature_set"] # What is the currenly set temperature of the model (If it support temp) self.temperature_set = tts_model_loaded["settings"]["temperature_set"] # What is the currenly set temperature of the model (If it support temp)
self.pitch_set = tts_model_loaded["settings"]["pitch_set"] # What is the currenly set pitch of the model (If it support temp) self.pitch_set = tts_model_loaded["settings"]["pitch_set"] # What is the currenly set pitch of the model (If it support temp)
# Gather the OpenAI API Voice Mappings # Gather the OpenAI API Voice Mappings
self.openai_alloy = tts_model_loaded["openai_voices"]["alloy"] # The TTS engine voice that will be mapped to Open AI Alloy voice self.openai_alloy = tts_model_loaded["openai_voices"]["alloy"] # The TTS engine voice that will be mapped to Open AI Alloy voice
self.openai_echo = tts_model_loaded["openai_voices"]["echo"] # The TTS engine voice that will be mapped to Open AI Echo voice self.openai_echo = tts_model_loaded["openai_voices"]["echo"] # The TTS engine voice that will be mapped to Open AI Echo voice
self.openai_fable = tts_model_loaded["openai_voices"]["fable"] # The TTS engine voice that will be mapped to Open AI Fable voice self.openai_fable = tts_model_loaded["openai_voices"]["fable"] # The TTS engine voice that will be mapped to Open AI Fable voice
self.openai_nova = tts_model_loaded["openai_voices"]["nova"] # The TTS engine voice that will be mapped to Open AI Nova voice self.openai_nova = tts_model_loaded["openai_voices"]["nova"] # The TTS engine voice that will be mapped to Open AI Nova voice
self.openai_onyx = tts_model_loaded["openai_voices"]["onyx"] # The TTS engine voice that will be mapped to Open AI Onyx voice self.openai_onyx = tts_model_loaded["openai_voices"]["onyx"] # The TTS engine voice that will be mapped to Open AI Onyx voice
self.openai_shimmer = tts_model_loaded["openai_voices"]["shimmer"] # The TTS engine voice that will be mapped to Open AI Shimmer voice self.openai_shimmer = tts_model_loaded["openai_voices"]["shimmer"] # The TTS engine voice that will be mapped to Open AI Shimmer voice
################################################################### ###################################################################
# DONT CHANGE # Load params and api_defaults from confignew.json # # DONT CHANGE # Load params and api_defaults from confignew.json #
################################################################### ###################################################################
# Define the path to the confignew.json file # Define the path to the confignew.json file
configfile_path = self.main_dir / "confignew.json" configfile_path = self.main_dir / "confignew.json"
# Load config file and get settings # Load config file and get settings
with open(configfile_path, "r") as configfile: with open(configfile_path, "r") as configfile:
configfile_data = json.load(configfile) configfile_data = json.load(configfile)
self.branding = configfile_data.get("branding", "") # Sets up self.branding for outputting the name stored in the "confgnew.json" file, as used in print statements. self.branding = configfile_data.get("branding", "") # Sets up self.branding for outputting the name stored in the "confgnew.json" file, as used in print statements.
self.params = configfile_data # Loads in the curent "confgnew.json" file to self.params. self.params = configfile_data # Loads in the curent "confgnew.json" file to self.params.
self.debug_tts = configfile_data.get("debugging").get("debug_tts") # Can be used within this script as a True/False flag for generally debugging the TTS generation process. self.debug_tts = configfile_data.get("debugging").get("debug_tts") # Can be used within this script as a True/False flag for generally debugging the TTS generation process.
self.debug_tts_variables = configfile_data.get("debugging").get("debug_tts_variables") # Can be used within this script as a True/False flag for generally debugging variables (if you wish). self.debug_tts_variables = configfile_data.get("debugging").get("debug_tts_variables") # Can be used within this script as a True/False flag for generally debugging variables (if you wish).
################################################################ ################################################################
# DONT CHANGE # Print out Python, CUDA, DeepSpeed versions #### # DONT CHANGE # Print out Python, CUDA, DeepSpeed versions ####
################################################################ ################################################################
def printout_versions(self): def printout_versions(self):
if deepspeed_available: if deepspeed_available:
print(f"[{self.branding}ENG] \033[92mDeepSpeed version :\033[93m",deepspeed.__version__,"\033[0m") print(f"[{self.branding}ENG] \033[92mDeepSpeed version :\033[93m",deepspeed.__version__,"\033[0m")
else: else:
print(f"[{self.branding}ENG] \033[92mDeepSpeed version :\033[93m Not available\033[0m") print(f"[{self.branding}ENG] \033[92mDeepSpeed version :\033[93m Not available\033[0m")
print(f"[{self.branding}ENG] \033[92mPython Version :\033[93m {python_version}\033[0m") print(f"[{self.branding}ENG] \033[92mPython Version :\033[93m {python_version}\033[0m")
print(f"[{self.branding}ENG] \033[92mPyTorch Version :\033[93m {pytorch_version}\033[0m") print(f"[{self.branding}ENG] \033[92mPyTorch Version :\033[93m {pytorch_version}\033[0m")
if cuda_version is None: if cuda_version is None:
print(f"[{self.branding}ENG] \033[92mCUDA Version :\033[91m Not available\033[0m") print(f"[{self.branding}ENG] \033[92mCUDA Version :\033[91m Not available\033[0m")
else: else:
print(f"[{self.branding}ENG] \033[92mCUDA Version :\033[93m {cuda_version}\033[0m") print(f"[{self.branding}ENG] \033[92mCUDA Version :\033[93m {cuda_version}\033[0m")
print(f"[{self.branding}ENG]") print(f"[{self.branding}ENG]")
return return
################################################################################### ###################################################################################
################################################################################### ###################################################################################
# CHANGE ME # Inital setup of the model and engine. Called when the script starts # # CHANGE ME # Inital setup of the model and engine. Called when the script starts #
################################################################################### ###################################################################################
################################################################################### ###################################################################################
# In here you will add code to load in your model and do its inital setup. So you # In here you will add code to load in your model and do its inital setup. So you
# may be calling your model loader via handle_tts_method_change or if your TTS # may be calling your model loader via handle_tts_method_change or if your TTS
# engine doesnt actually load a model into CUDA or System RAM, you may be doing # engine doesnt actually load a model into CUDA or System RAM, you may be doing
# Something to fake its start-up. # Something to fake its start-up.
async def setup(self): async def setup(self):
self.printout_versions() self.printout_versions()
# ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑ # ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑
# ↑↑↑ Keep everything above this line ↑↑↑ # ↑↑↑ Keep everything above this line ↑↑↑
# ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑ # ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑
self.available_models = self.scan_models_folder() self.available_models = self.scan_models_folder()
if self.selected_model: if self.selected_model:
tts_model = f"{self.selected_model}" tts_model = f"{self.selected_model}"
if tts_model in self.available_models: if tts_model in self.available_models:
await self.handle_tts_method_change(tts_model) await self.handle_tts_method_change(tts_model)
self.current_model_loaded = tts_model self.current_model_loaded = tts_model
# ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓ # ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓
# ↓↓↓ Keep everything below this line ↓↓↓ # ↓↓↓ Keep everything below this line ↓↓↓
# ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓ # ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓
else: else:
self.current_model_loaded = "No Models Available" self.current_model_loaded = "No Models Available"
print(f"[{self.branding}ENG] \033[91mError\033[0m: Selected model '{self.selected_model}' not found in the models folder.") print(f"[{self.branding}ENG] \033[91mError\033[0m: Selected model '{self.selected_model}' not found in the models folder.")
self.setup_has_run = True # Set to True, so that the /api/ready endpoint can provide a "Ready" status self.setup_has_run = True # Set to True, so that the /api/ready endpoint can provide a "Ready" status
################################## ##################################
################################## ##################################
# CHANGE ME # Low VRAM Swapping # # CHANGE ME # Low VRAM Swapping #
################################## ##################################
################################## ##################################
# If your model does load into CUDA and you want to support LowVRAM, aka moving the model # If your model does load into CUDA and you want to support LowVRAM, aka moving the model
# on the fly between CUDA and System RAM on each generation request, you will be adding # on the fly between CUDA and System RAM on each generation request, you will be adding
# The code in here to do it. See the XTTS tts engine for an example. Piper however, doesnt # The code in here to do it. See the XTTS tts engine for an example.
# Load into VRAM or System RAM, also low vram is set globally disabled by the model_settings.JSON async def handle_lowvram_change(self):
# file, so this would never get called anywyay, however, we still need to keep an empty if torch.cuda.is_available():
# function in place. Piper TTS uses "pass" telling function to just exit out cleanly if called. if self.device == "cuda":
# However, its quite a simple check along the lines of "if CUDA is available and model is self.device = "cpu"
# in X place, then send it to Y place (or Y to X). self.model.to(self.device)
async def handle_lowvram_change(self): torch.cuda.empty_cache()
if torch.cuda.is_available(): else:
if self.device == "cuda": self.device == "cpu"
self.device = "cpu" self.device = "cuda"
self.model.to(self.device) self.model.to(self.device)
torch.cuda.empty_cache()
else: ########################################
self.device == "cpu" ########################################
self.device = "cuda" # CHANGE ME # DeepSpeed model loading #
self.model.to(self.device) ########################################
########################################
######################################## # If the model supports CUDA and DeepSpeed, this is where you can handle re-loading
######################################## # the model as/when DeepSpeed is enabled/selected in the user interface. If your
# CHANGE ME # DeepSpeed model loading # # TTS model doesnt support DeepSpeed, then it should be globally set in your
######################################## # model_settings.JSON and this function will never get called, however it still needs
######################################## # to exist as a function.
# If the model supports CUDA and DeepSpeed, this is where you can handle re-loading async def handle_deepspeed_change(self, value):
# the model as/when DeepSpeed is enabled/selected in the user interface. If your if value:
# TTS model doesnt support DeepSpeed, then it should be globally set in your # DeepSpeed enabled
# model_settings.JSON and this function will never get called, however it still needs print(f"[{self.branding}ENG] \033[93mDeepSpeed Activating\033[0m")
# to exist as a function. await self.unload_model()
async def handle_deepspeed_change(self, value): self.params["tts_method_api_local"] = False
if value: self.params["tts_method_xtts_local"] = True
# DeepSpeed enabled self.deepspeed_enabled = True
print(f"[{self.branding}ENG] \033[93mDeepSpeed Activating\033[0m") await self.setup()
await self.unload_model() else:
self.params["tts_method_api_local"] = False # DeepSpeed disabled
self.params["tts_method_xtts_local"] = True print(f"[{self.branding}ENG] \033[93mDeepSpeed De-Activating\033[0m")
self.deepspeed_enabled = True self.deepspeed_enabled = False
await self.setup() await self.unload_model()
else: await self.setup()
# DeepSpeed disabled return value # Return new checkbox value
print(f"[{self.branding}ENG] \033[93mDeepSpeed De-Activating\033[0m")
self.deepspeed_enabled = False ##############################################################################################################################################
await self.unload_model() ##############################################################################################################################################
await self.setup() # CHANGE ME # scan for available models/voices that are relevant to this TTS engine # XTTS is very unusal as it has 2x model loading methods #
return value # Return new checkbox value ##############################################################################################################################################
##############################################################################################################################################
############################################################################################################################################## # This function looks and reports back the list of possible models your TTS engine can
############################################################################################################################################## # load in. Some TTS engines have multiple models they can load and you will want to use
# CHANGE ME # scan for available models/voices that are relevant to this TTS engine # XTTS is very unusal as it has 2x model loading methods # # code for checking if the models are in the correct location/placement within the disk
############################################################################################################################################## # the correct files exst per model etc (check XTTS for an example of this). Some models
############################################################################################################################################## # like Piper, the actual models are the voices, so in the Piper scan_models_folder
# This function looks and reports back the list of possible models your TTS engine can # function, we fake the only model being available as {'piper': 'piper'} aka, model name
# load in. Some TTS engines have multiple models they can load and you will want to use # then engine name, then we use the voices_file_list to populate the models as available
# code for checking if the models are in the correct location/placement within the disk # voices that can be selected in the interface.
# the correct files exst per model etc (check XTTS for an example of this). Some models # If no models are found, we return "No Models Available" and continue on with the script.
# like Piper, the actual models are the voices, so in the Piper scan_models_folder def scan_models_folder(self):
# function, we fake the only model being available as {'piper': 'piper'} aka, model name models_folder = self.main_dir / "models" / "parler" # Edit to match the name of your folder where voices are stored
# then engine name, then we use the voices_file_list to populate the models as available print("models_folder is:", models_folder) if self.debug_tts else None
# voices that can be selected in the interface. self.available_models = {}
# If no models are found, we return "No Models Available" and continue on with the script. # ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑
def scan_models_folder(self): # ↑↑↑ Keep everything above this line ↑↑↑
models_folder = self.main_dir / "models" / "parler" # Edit to match the name of your folder where voices are stored # ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑
print("models_folder is:", models_folder) if self.debug_tts else None # Check if model_path exists
self.available_models = {} common_required_files = [
# ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑ "config.json", "generation_config.json", "preprocessor_config.json",
# ↑↑↑ Keep everything above this line ↑↑↑ "special_tokens_map.json", "spiece.model", "tokenizer.json", "tokenizer_config.json"
# ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑ ]
# Check if model_path exists
if not os.path.exists(models_folder): for subfolder in models_folder.iterdir():
print(f"[{self.branding}ENG] \033[91mError\033[0m: The specified model path '{models_folder}' does not exist.") if subfolder.is_dir():
self.available_models = {'No Models Available': 'parler'} # Change the name in here to your TTS engine name model_name = subfolder.name
return self.available_models print("model_name is:", model_name) if self.debug_tts else None
required_files = ["config.json", "generation_config.json", "model.safetensors", "preprocessor_config.json", "special_tokens_map.json", "spiece.model", "tokenizer.json", "tokenizer_config.json"]
for subfolder in models_folder.iterdir(): # Check for common required files
if subfolder.is_dir(): if all(subfolder.joinpath(file).exists() for file in common_required_files):
model_name = subfolder.name # Check for mini model (single safetensors file)
print("model_name is:", model_name) if self.debug_tts else None if subfolder.joinpath("model.safetensors").exists():
if all(subfolder.joinpath(file).exists() for file in required_files): self.available_models[f"parler - {model_name}"] = "parler"
self.available_models[f"parler - {model_name}"] = "parler" print("self.available_models is:", self.available_models) if self.debug_tts else None
print("self.available_models is:", self.available_models) if self.debug_tts else None # Check for large model (split safetensors files and index)
elif (subfolder.joinpath("model.safetensors.index.json").exists() and
# ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓ subfolder.joinpath("model-00001-of-00002.safetensors").exists() and
# ↓↓↓ Keep everything below this line ↓↓↓ subfolder.joinpath("model-00002-of-00002.safetensors").exists()):
# ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓ self.available_models[f"parler - {model_name}"] = "parler"
else: print("self.available_models is:", self.available_models) if self.debug_tts else None
self.available_models = {'No Models Available': 'parler'} # Change the name in here to your TTS engine name else:
print(f"[{self.branding}ENG] \033[91mWarning\033[0m: Model folder '{model_name}' is missing required") print(f"[{self.branding}ENG] \033[91mWarning\033[0m: Model folder '{model_name}' is missing required model files.")
print(f"[{self.branding}ENG] \033[91mWarning\033[0m: files or the folder does not exist.") else:
return self.available_models print(f"[{self.branding}ENG] \033[91mWarning\033[0m: Model folder '{model_name}' is missing common required files.")
############################################################# # ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓
############################################################# # ↓↓↓ Keep everything below this line ↓↓↓
# CHANGE ME # POPULATE FILES LIST FROM VOICES DIRECTORY #### # ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓
############################################################# else:
############################################################# self.available_models = {'No Models Available': 'parler'} # Change the name in here to your TTS engine name
# This function looks and reports back the list of possible voice your TTS engine can print(f"[{self.branding}ENG] \033[91mWarning\033[0m: Model folder '{model_name}' is missing required")
# load in. Some TTS engines the voices are wav file samples (XTTS), some are models print(f"[{self.branding}ENG] \033[91mWarning\033[0m: files or the folder does not exist.")
# (Piper) and some are text (Parler) thats stored in a JSON file. We just need to return self.available_models
# populate the "voices" variable somehow and if no voices are found, we return
# "No Voices Found" back to the interface/api. #############################################################
def voices_file_list(self): #############################################################
try: # CHANGE ME # POPULATE FILES LIST FROM VOICES DIRECTORY ####
voices = [] #############################################################
# ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑ #############################################################
# ↑↑↑ Keep everything above this line ↑↑↑ # This function looks and reports back the list of possible voice your TTS engine can
# ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑ # load in. Some TTS engines the voices are wav file samples (XTTS), some are models
# (Piper) and some are text (Parler) thats stored in a JSON file. We just need to
voices_file = os.path.join(self.main_dir, "system", "tts_engines", "parler", "parler_voices.json") # populate the "voices" variable somehow and if no voices are found, we return
if os.path.exists(voices_file): # "No Voices Found" back to the interface/api.
with open(voices_file, "r") as f: def voices_file_list(self):
voices = json.load(f) try:
return [voice["voice_name"] for voice in voices["voices"]] voices = []
# ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑
# ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓ # ↑↑↑ Keep everything above this line ↑↑↑
# ↓↓↓ Keep everything below this line ↓↓↓ # ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑
# ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓
if not voices: voices_file = os.path.join(self.main_dir, "system", "tts_engines", "parler", "parler_voices.json")
return ["No Voices Found"] # Return a list with {'No Voices Found'} if there are no voices/voice models. if os.path.exists(voices_file):
return voices # Return a list of models in the format {'engine name': 'model 1', 'engine name': 'model 2", etc} with open(voices_file, "r") as f:
except Exception as e: voices = json.load(f)
print(f"[{self.branding}ENG] \033[91mError\033[0m: Voices/Voice Models not found. Cannot load a list of voices.") return [voice["voice_name"] for voice in voices["voices"]]
print(f"[{self.branding}ENG]")
return ["No Voices Found"] # ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓
# ↓↓↓ Keep everything below this line ↓↓↓
############################# # ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓
############################# if not voices:
# CHANGE ME # Model loading # return ["No Voices Found"] # Return a list with {'No Voices Found'} if there are no voices/voice models.
############################# return voices # Return a list of models in the format {'engine name': 'model 1', 'engine name': 'model 2", etc}
############################# except Exception as e:
# This function will handle the loading of your model, into VRAM/CUDA, System RAM or whatever. print(f"[{self.branding}ENG] \033[91mError\033[0m: Voices/Voice Models not found. Cannot load a list of voices.")
# In XTTS, which has 2x model loader types, there are 2x loaders. They are called by "def handle_tts_method_change" print(f"[{self.branding}ENG]")
# In Piper we fake a model loader as Piper doesnt actually load a model into CUDA/System RAM as such. So, in that return ["No Voices Found"]
# situation, api_manual_load_model is kind of a blank function. Though we do set self.is_tts_model_loaded = True
# as this is used elsewhere in the scripts to confirm that a model is available to be used for TTS generation. #############################
# We always check for "No Models Available" being sent as that means we are trying to load in a model that #############################
# doesnt exist/wasnt found on script start-up e.g. someone deleted the model from the folder or something. # CHANGE ME # Model loading #
async def api_manual_load_model(self, model_name): #############################
if "No Models Available" in self.available_models: #############################
print(f"[{self.branding}ENG] \033[91mError\033[0m: No models for this TTS engine were found to load. Please download a model.") # This function will handle the loading of your model, into VRAM/CUDA, System RAM or whatever.
return # In XTTS, which has 2x model loader types, there are 2x loaders. They are called by "def handle_tts_method_change"
model_path = self.main_dir / "models" / "parler" / model_name # In Parler we fake a model loader as Parler doesnt actually load a model into CUDA/System RAM as such. So, in that
print("model_path is:", model_path) if self.debug_tts_variables else None # situation, api_manual_load_model is kind of a blank function. Though we do set self.is_tts_model_loaded = True
# ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑ # as this is used elsewhere in the scripts to confirm that a model is available to be used for TTS generation.
# ↑↑↑ Keep everything above this line ↑↑↑ # We always check for "No Models Available" being sent as that means we are trying to load in a model that
# ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑ # doesnt exist/wasnt found on script start-up e.g. someone deleted the model from the folder or something.
async def api_manual_load_model(self, model_name):
self.model = ParlerTTSForConditionalGeneration.from_pretrained(model_path).to(self.device, dtype=torch.float32) if "No Models Available" in self.available_models:
self.tokenizer = AutoTokenizer.from_pretrained(model_path) print(f"[{self.branding}ENG] \033[91mError\033[0m: No models for this TTS engine were found to load. Please download a model.")
return
# ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓ model_path = self.main_dir / "models" / "parler" / model_name
# ↓↓↓ Keep everything below this line ↓↓↓ print("model_path is:", model_path) if self.debug_tts_variables else None
# ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓ # ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑
self.is_tts_model_loaded = True # ↑↑↑ Keep everything above this line ↑↑↑
return self.model # ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑
############################### self.model = ParlerTTSForConditionalGeneration.from_pretrained(model_path).to(self.device, dtype=torch.float32)
############################### self.tokenizer = AutoTokenizer.from_pretrained(model_path)
# CHANGE ME # Model unloading #
############################### # ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓
############################### # ↓↓↓ Keep everything below this line ↓↓↓
# This function will handle the UN-loading of your model, from VRAM/CUDA, System RAM or whatever. # ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓
# In XTTS, that model loads into CUDA/System Ram, so when we swap models, we want to unload the current model self.is_tts_model_loaded = True
# free up the memory and then load in the new model to VRAM/CUDA. On the flip side of that, Piper doesnt return self.model
# doesnt load into memory, so we just need to put a fake function here that doesnt really do anything
# other than set "self.is_tts_model_loaded = False", which would be set back to true by the model loader. ###############################
# So look at the Piper model_engine.py if you DONT need to unload models. ###############################
async def unload_model(self): # CHANGE ME # Model unloading #
self.is_tts_model_loaded = False ###############################
if not self.current_model_loaded == None: ###############################
print(f"[{self.branding}ENG] \033[94mUnloading model \033[0m") if self.debug_tts else None # This function will handle the UN-loading of your model, from VRAM/CUDA, System RAM or whatever.
if hasattr(self, 'model'): # In XTTS, that model loads into CUDA/System Ram, so when we swap models, we want to unload the current model
del self.model # free up the memory and then load in the new model to VRAM/CUDA. On the flip side of that, Parler doesnt
if torch.cuda.is_available(): # doesnt load into memory, so we just need to put a fake function here that doesnt really do anything
torch.cuda.empty_cache() # other than set "self.is_tts_model_loaded = False", which would be set back to true by the model loader.
return None # So look at the Parler model_engine.py if you DONT need to unload models.
async def unload_model(self):
################################################################################################################################### self.is_tts_model_loaded = False
################################################################################################################################### if not self.current_model_loaded == None:
# CHANGE ME # Model changing. Unload out old model and load in a new one # XTTS is very unusal as it has 2x model loading methods # print(f"[{self.branding}ENG] \033[94mUnloading model \033[0m") if self.debug_tts else None
################################################################################################################################### if hasattr(self, 'model'):
################################################################################################################################### del self.model
# This function is your central model loading/unloading handler that deals with the above functions as necesary, to call loading, unloading, if torch.cuda.is_available():
# swappng DeepSpeed, Low vram etc. This function gets called with a "engine name - model name" type call. In XTTS, because there are 2x torch.cuda.empty_cache()
# model loader types, (XTTS and APILocal), we take tts_method and split the "engine name - model name" into a loader type and the model return None
# that it needs to load in and then we call the correct loader function. Whereas in Piper, which doesnt load models into memory at all,
# we just have a fake function that doesnt really do anything. We always check to see if the model name has "No Models Available" in the ###################################################################################################################################
# name thats sent over, just to catch any potential errors. We display the start load time and end load time. Thats about it. ###################################################################################################################################
async def handle_tts_method_change(self, tts_method): # CHANGE ME # Model changing. Unload out old model and load in a new one # XTTS is very unusal as it has 2x model loading methods #
generate_start_time = time.time() # Record the start time of loading the model ###################################################################################################################################
if "No Models Available" in self.available_models: ###################################################################################################################################
print(f"[{self.branding}ENG] \033[91mError\033[0m: No models for this TTS engine were found to load. Please download a model.") # This function is your central model loading/unloading handler that deals with the above functions as necesary, to call loading, unloading,
return False # swappng DeepSpeed, Low vram etc. This function gets called with a "engine name - model name" type call. In XTTS, because there are 2x
# ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑ # model loader types, (XTTS and APILocal), we take tts_method and split the "engine name - model name" into a loader type and the model
# ↑↑↑ Keep everything above this line ↑↑↑ # that it needs to load in and then we call the correct loader function. Whereas in Parler, which doesnt load models into memory at all,
# ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑ # we just have a fake function that doesnt really do anything. We always check to see if the model name has "No Models Available" in the
# name thats sent over, just to catch any potential errors. We display the start load time and end load time. Thats about it.
await self.unload_model() async def handle_tts_method_change(self, tts_method):
if tts_method.startswith("parler"): generate_start_time = time.time() # Record the start time of loading the model
model_name = tts_method.split(" - ")[1] if "No Models Available" in self.available_models:
print(f"[{self.branding}ENG]\033[94m Model/Engine :\033[93m {model_name}\033[94m loading into\033[93m", self.device,"\033[0m") print(f"[{self.branding}ENG] \033[91mError\033[0m: No models for this TTS engine were found to load. Please download a model.")
self.model = await self.api_manual_load_model(model_name) return False
self.current_model_loaded = f"parler - {model_name}" # ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑
else: # ↑↑↑ Keep everything above this line ↑↑↑
self.current_model_loaded = None # ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑
return False
await self.unload_model()
# ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓ if tts_method.startswith("parler"):
# ↓↓↓ Keep everything below this line ↓↓↓ model_name = tts_method.split(" - ")[1]
# ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓ print(f"[{self.branding}ENG]\033[94m Model/Engine :\033[93m {model_name}\033[94m loading into\033[93m", self.device,"\033[0m")
generate_end_time = time.time() # Create an end timer for calculating load times self.model = await self.api_manual_load_model(model_name)
generate_elapsed_time = generate_end_time - generate_start_time # Calculate start time minus end time self.current_model_loaded = f"parler - {model_name}"
print(f"[{self.branding}ENG] \033[94mLoad time :\033[93m {generate_elapsed_time:.2f} seconds.\033[0m") # Print out the result of the load time else:
return True self.current_model_loaded = None
return False
##########################################################################################################################################
########################################################################################################################################## # ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓
# CHANGE ME # Model changing. Unload out old model and load in a new one # XTTS is very unusal as it has 2x model TTS generation methods # # ↓↓↓ Keep everything below this line ↓↓↓
########################################################################################################################################## # ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓
########################################################################################################################################## generate_end_time = time.time() # Create an end timer for calculating load times
# In here all the possible options are sent over (text, voice to use, lanugage, speed etc etc) and its up to you how you use them, or not. generate_elapsed_time = generate_end_time - generate_start_time # Calculate start time minus end time
# obviously if your TTS engine doesnt support speed for example, generationspeed_capable should be set False in your model_settings.JSON file print(f"[{self.branding}ENG] \033[94mLoad time :\033[93m {generate_elapsed_time:.2f} seconds.\033[0m") # Print out the result of the load time
# and a fake "generationspeed_set" value should be set. This allows a fake value to be sent over from the main script, even though it return True
# wouldnt actually ever be used in the generation below. Nonethless all these values, real or just whats inside the configuration file
# will be sent over for use. ##########################################################################################################################################
# Setting the xxxxxxx_capabale in the model_settings.JSON file, will enable/disable them being selectable by the user. For example, if you ##########################################################################################################################################
# set "generationspeed_capable" as false in the model_settings.JSON file, a user will not be able to select OR set the setting for # CHANGE ME # Model changing. Unload out old model and load in a new one # XTTS is very unusal as it has 2x model TTS generation methods #
# generation speed. ##########################################################################################################################################
# One thing to note is that we HAVE to have something in this generation request that is synchronous from the way its called, which means ##########################################################################################################################################
# we have to have an option for Streaming, even if our TTS engine doesnt support streaming. So in that case, we would set streaming_capable # In here all the possible options are sent over (text, voice to use, lanugage, speed etc etc) and its up to you how you use them, or not.
# as false in our model_settings.JSON file, meaning streaming will never be called. However, we have to put a fake streaming routine in our # obviously if your TTS engine doesnt support speed for example, generationspeed_capable should be set False in your model_settings.JSON file
# function below (or a real function if it does support streaming of course). Parler has an example of a fake streaming function, which is # and a fake "generationspeed_set" value should be set. This allows a fake value to be sent over from the main script, even though it
# very clearly highlighted in its model_engine.py script. # wouldnt actually ever be used in the generation below. Nonethless all these values, real or just whats inside the configuration file
# Piper TTS, which uses command line based calls and therefore has different ones for Windows and Linux/Mac, has an example of doing this # will be sent over for use.
# within its model_engine.py file. # Setting the xxxxxxx_capabale in the model_settings.JSON file, will enable/disable them being selectable by the user. For example, if you
async def generate_tts(self, text, voice, language, temperature, repetition_penalty, speed, pitch, output_file, streaming): # set "generationspeed_capable" as false in the model_settings.JSON file, a user will not be able to select OR set the setting for
print(f"[{self.branding}Debug] Entered model_engine.py generate_tts function") if self.debug_tts else None # generation speed.
if not self.is_tts_model_loaded: # Check if a model is loaded and error out if not. # One thing to note is that we HAVE to have something in this generation request that is synchronous from the way its called, which means
error_message = f"[{self.branding}ENG] \033[91mError\033[0m: You currently have no TTS model loaded." # we have to have an option for Streaming, even if our TTS engine doesnt support streaming. So in that case, we would set streaming_capable
print(error_message) # as false in our model_settings.JSON file, meaning streaming will never be called. However, we have to put a fake streaming routine in our
raise HTTPException(status_code=400, detail="You currently have no TTS model loaded.") # Raise an exception with a meaningful HTTP status code # function below (or a real function if it does support streaming of course). Parler has an example of a fake streaming function, which is
self.tts_generating_lock = True # Set the tts_generating lock to True, which stops other generation requests being sent into the pipeline # very clearly highlighted in its model_engine.py script.
print(f"[{self.branding}Debug] Checking low VRAM") if self.debug_tts else None async def generate_tts(self, text, voice, language, temperature, repetition_penalty, speed, pitch, output_file, streaming):
if self.lowvram_enabled and self.device == "cpu": # If necessary, move the model out of System Ram to VRAM print(f"[{self.branding}Debug] Entered model_engine.py generate_tts function") if self.debug_tts else None
print(f"[{self.branding}Debug] Switching device") if self.debug_tts else None if not self.is_tts_model_loaded: # Check if a model is loaded and error out if not.
await self.handle_lowvram_change() error_message = f"[{self.branding}ENG] \033[91mError\033[0m: You currently have no TTS model loaded."
print(f"[{self.branding}Debug] Setting a generate time") if self.debug_tts else None print(error_message)
generate_start_time = time.time() # Record the start time of generating TTS raise HTTPException(status_code=400, detail="You currently have no TTS model loaded.") # Raise an exception with a meaningful HTTP status code
# ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑ self.tts_generating_lock = True # Set the tts_generating lock to True, which stops other generation requests being sent into the pipeline
# ↑↑↑ Keep everything above this line ↑↑↑ print(f"[{self.branding}Debug] Checking low VRAM") if self.debug_tts else None
# ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑ if self.lowvram_enabled and self.device == "cpu": # If necessary, move the model out of System Ram to VRAM
print(f"[{self.branding}Debug] Switching device") if self.debug_tts else None
voices_file = os.path.join(self.main_dir, "system", "tts_engines", "parler", "parler_voices.json") await self.handle_lowvram_change()
if os.path.exists(voices_file): print(f"[{self.branding}Debug] Setting a generate time") if self.debug_tts else None
with open(voices_file, "r") as f: generate_start_time = time.time() # Record the start time of generating TTS
voices_data = json.load(f) # ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑
for voice_data in voices_data["voices"]: # ↑↑↑ Keep everything above this line ↑↑↑
if voice_data["voice_name"] == voice: # ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑
description = voice_data["description"]
break voices_file = os.path.join(self.main_dir, "system", "tts_engines", "parler", "parler_voices.json")
else: if os.path.exists(voices_file):
description = f"A {voice} voice speaking in {language}." with open(voices_file, "r") as f:
else: voices_data = json.load(f)
description = f"A {voice} voice speaking in {language}." for voice_data in voices_data["voices"]:
input_ids = self.tokenizer(description, return_tensors="pt").input_ids.to(self.device) if voice_data["voice_name"] == voice:
prompt_input_ids = self.tokenizer(text, return_tensors="pt").input_ids.to(self.device) description = voice_data["description"]
generation = self.model.generate(input_ids=input_ids, prompt_input_ids=prompt_input_ids).to(torch.float32) break
audio_arr = generation.cpu().numpy().squeeze() else:
sf.write(output_file, audio_arr, self.model.config.sampling_rate) description = f"A {voice} voice speaking in {language}."
else:
# Fake Streaming function here description = f"A {voice} voice speaking in {language}."
wavs = None # This is a fake function as this model doesnt stream but Python demands it otherwise it complains about await functions in the main script input_ids = self.tokenizer(description, return_tensors="pt").input_ids.to(self.device)
if streaming and wavs is not None: # This is a fake function as this model doesnt stream but Python demands it otherwise it complains about await functions in the main script prompt_input_ids = self.tokenizer(text, return_tensors="pt").input_ids.to(self.device)
# Streaming-specific operations # This is a fake function as this model doesnt stream but Python demands it otherwise it complains about await functions in the main script generation = self.model.generate(input_ids=input_ids, prompt_input_ids=prompt_input_ids).to(torch.float32)
for wav_chunk in wavs: # This is a fake function as this model doesnt stream but Python demands it otherwise it complains about await functions in the main script audio_arr = generation.cpu().numpy().squeeze()
yield wav_chunk.numpy().tobytes() # This is a fake function as this model doesnt stream but Python demands it otherwise it complains about await functions in the main script sf.write(output_file, audio_arr, self.model.config.sampling_rate)
# ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓ # Fake Streaming function here
# ↓↓↓ Keep everything below this line ↓↓↓ wavs = None # This is a fake function as this model doesnt stream but Python demands it otherwise it complains about await functions in the main script
# ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓ if streaming and wavs is not None: # This is a fake function as this model doesnt stream but Python demands it otherwise it complains about await functions in the main script
generate_end_time = time.time() # Record the end time to generate TTS # Streaming-specific operations # This is a fake function as this model doesnt stream but Python demands it otherwise it complains about await functions in the main script
generate_elapsed_time = generate_end_time - generate_start_time for wav_chunk in wavs: # This is a fake function as this model doesnt stream but Python demands it otherwise it complains about await functions in the main script
print(f"[{self.branding}GEN] \033[94mTTS Generate: \033[93m{generate_elapsed_time:.2f} seconds. \033[94mLowVRAM: \033[33m{self.lowvram_enabled} \033[94mDeepSpeed: \033[33m{self.deepspeed_enabled}\033[0m") yield wav_chunk.numpy().tobytes() # This is a fake function as this model doesnt stream but Python demands it otherwise it complains about await functions in the main script
if self.lowvram_enabled and self.device == "cuda" and self.tts_narrator_generatingtts == False:
await self.handle_lowvram_change() # ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓
self.tts_generating_lock = False # Unlock the TTS generation queue to allow TTS generation requests to come in again. # ↓↓↓ Keep everything below this line ↓↓↓
print(f"[{self.branding}Debug] generate_tts function completed") if self.debug_tts else None # ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓
return generate_end_time = time.time() # Record the end time to generate TTS
generate_elapsed_time = generate_end_time - generate_start_time
print(f"[{self.branding}GEN] \033[94mTTS Generate: \033[93m{generate_elapsed_time:.2f} seconds. \033[94mLowVRAM: \033[33m{self.lowvram_enabled} \033[94mDeepSpeed: \033[33m{self.deepspeed_enabled}\033[0m")
if self.lowvram_enabled and self.device == "cuda" and self.tts_narrator_generatingtts == False:
await self.handle_lowvram_change()
self.tts_generating_lock = False # Unlock the TTS generation queue to allow TTS generation requests to come in again.
print(f"[{self.branding}Debug] generate_tts function completed") if self.debug_tts else None
return
+41 -41
View File
@@ -1,42 +1,42 @@
{ {
"model_details": { "model_details": {
"manufacturer_name": "Parler-TTS", "manufacturer_name": "Parler-TTS",
"manufacturer_website": "https://github.com/huggingface/parler-tts", "manufacturer_website": "https://github.com/huggingface/parler-tts",
"model_description": "Parler-TTS is a lightweight text-to-speech (TTS) model that can generate high-quality, natural sounding speech in the style of a given speaker (gender, pitch, speaking style, etc). It is a reproduction of work from the paper Natural language guidance of high-fidelity text-to-speech with synthetic annotations by Dan Lyth and Simon King, from Stability AI and Edinburgh University respectively. Contrarily to other TTS models, Parler-TTS is a fully open-source release. All of the datasets, pre-processing, training code and weights are released publicly under permissive license, enabling the community to build on our work and develop their own powerful TTS models. \n\nParler's unique party trick is that like Stable Diffusion, you can describe the voice you want in a text entry; however, this means you are unlikely to get the same voice reproduced each time. Please see the Voice Editor tab." "model_description": "Parler-TTS is a lightweight text-to-speech (TTS) model that can generate high-quality, natural sounding speech in the style of a given speaker (gender, pitch, speaking style, etc). It is a reproduction of work from the paper Natural language guidance of high-fidelity text-to-speech with synthetic annotations by Dan Lyth and Simon King, from Stability AI and Edinburgh University respectively. Contrarily to other TTS models, Parler-TTS is a fully open-source release. All of the datasets, pre-processing, training code and weights are released publicly under permissive license, enabling the community to build on our work and develop their own powerful TTS models. \n\nParler's unique party trick is that like Stable Diffusion, you can describe the voice you want in a text entry; however, this means you are unlikely to get the same voice reproduced each time. Please see the Voice Editor tab."
}, },
"model_capabilties": { "model_capabilties": {
"audio_format": "wav", "audio_format": "wav",
"deepspeed_capable": false, "deepspeed_capable": false,
"generationspeed_capable": true, "generationspeed_capable": false,
"languages_capable": false, "languages_capable": false,
"lowvram_capable": true, "lowvram_capable": true,
"multimodel_capable": true, "multimodel_capable": true,
"repetitionpenalty_capable": false, "repetitionpenalty_capable": false,
"streaming_capable": false, "streaming_capable": false,
"temperature_capable": false, "temperature_capable": false,
"multivoice_capable": true, "multivoice_capable": true,
"pitch_capable": false, "pitch_capable": false,
"windows_capable": true, "windows_capable": true,
"linux_capable": true, "linux_capable": true,
"mac_capable": true "mac_capable": true
}, },
"settings": { "settings": {
"def_character_voice": "enthusiastic_female", "def_character_voice": "enthusiastic_female",
"def_narrator_voice": "enthusiastic_female", "def_narrator_voice": "enthusiastic_female",
"deepspeed_enabled": false, "deepspeed_enabled": false,
"engine_installed": true, "engine_installed": true,
"generationspeed_set": 1, "generationspeed_set": 1,
"lowvram_enabled": true, "lowvram_enabled": false,
"pitch_set": 0, "pitch_set": 0,
"repetitionpenalty_set": 10, "repetitionpenalty_set": 10,
"temperature_set": 0.75 "temperature_set": 0.75
}, },
"openai_voices": { "openai_voices": {
"alloy": "enthusiastic_female", "alloy": "enthusiastic_female",
"echo": "enthusiastic_female", "echo": "enthusiastic_female",
"fable": "enthusiastic_female", "fable": "enthusiastic_female",
"nova": "enthusiastic_female", "nova": "enthusiastic_female",
"onyx": "enthusiastic_female", "onyx": "enthusiastic_female",
"shimmer": "enthusiastic_female" "shimmer": "enthusiastic_female"
} }
} }
+431 -401
View File
@@ -1,402 +1,432 @@
import os import os
import json import json
import requests import requests
import gradio as gr import gradio as gr
from tqdm import tqdm from tqdm import tqdm
from pathlib import Path from pathlib import Path
this_dir = Path(__file__).parent.resolve() # Sets up self.this_dir as a variable for the folder THIS script is running in. this_dir = Path(__file__).parent.resolve() # Sets up self.this_dir as a variable for the folder THIS script is running in.
main_dir = Path(__file__).parent.parent.parent.parent.resolve() # Sets up self.main_dir as a variable for the folder AllTalk is running in main_dir = Path(__file__).parent.parent.parent.parent.resolve() # Sets up self.main_dir as a variable for the folder AllTalk is running in
########################################################################## ##########################################################################
# REQUIRED CHANGE # # REQUIRED CHANGE #
# Populate the voices list, using the method specific to your TTS engine # # Populate the voices list, using the method specific to your TTS engine #
########################################################################## ##########################################################################
# This function is responsible for populating the list of available voices for your TTS engine. # This function is responsible for populating the list of available voices for your TTS engine.
# You need to modify this function to use the appropriate method for your engine to retrieve the voice list. # You need to modify this function to use the appropriate method for your engine to retrieve the voice list.
# #
# The current implementation lists all the WAV files in a "voices" directory, which may not be suitable for your engine. # The current implementation lists all the WAV files in a "voices" directory, which may not be suitable for your engine.
# You should replace the `xxxx_voices_file_list` function name to match your engine name. For example, if your engine # You should replace the `xxxx_voices_file_list` function name to match your engine name. For example, if your engine
# is named "mytts", the function should be named `mytts_voices_file_list`. # is named "mytts", the function should be named `mytts_voices_file_list`.
# #
# You will also neef to update the code with your own implementation that retrieves the voice list according to your # You will also neef to update the code with your own implementation that retrieves the voice list according to your
# engine's specific requirements. Typically this is the same code as will be in your model_engine.py file. # engine's specific requirements. Typically this is the same code as will be in your model_engine.py file.
# #
# For example, if your engine has a dedicated API or configuration file for managing voices, you should modify this # For example, if your engine has a dedicated API or configuration file for managing voices, you should modify this
# function to interact with that API or read from that configuration file. # function to interact with that API or read from that configuration file.
# #
# After making the necessary changes, this function should return a list of available voices that can be used # After making the necessary changes, this function should return a list of available voices that can be used
# in your TTS engine's settings page. # in your TTS engine's settings page.
def parler_voices_file_list(): def parler_voices_file_list():
voices_file = os.path.join(main_dir, "system", "tts_engines", "parler", "parler_voices.json") voices_file = os.path.join(main_dir, "system", "tts_engines", "parler", "parler_voices.json")
if os.path.exists(voices_file): if os.path.exists(voices_file):
with open(voices_file, "r") as f: with open(voices_file, "r") as f:
voices_data = json.load(f) voices_data = json.load(f)
voices_list = [voice["voice_name"] for voice in voices_data["voices"]] voices_list = [voice["voice_name"] for voice in voices_data["voices"]]
return sorted(voices_list) return sorted(voices_list)
else: else:
return [] return []
###################################################### ######################################################
# REQUIRED CHANGE # # REQUIRED CHANGE #
# Imports and saves the TTS engine-specific settings # # Imports and saves the TTS engine-specific settings #
###################################################### ######################################################
# This function is responsible for importing and saving the settings specific to your TTS engine. # This function is responsible for importing and saving the settings specific to your TTS engine.
# You need to make the following change: # You need to make the following change:
# #
# 1. Change the name of the function `xxxx_model_update_settings` to match your engine's name. # 1. Change the name of the function `xxxx_model_update_settings` to match your engine's name.
# For example, if your engine is named "mytts", the function should be named `mytts_model_update_settings`. # For example, if your engine is named "mytts", the function should be named `mytts_model_update_settings`.
# #
# After making this change, the function will load the model settings from a JSON file, update the settings and voice # After making this change, the function will load the model settings from a JSON file, update the settings and voice
# dictionaries with the values provided as arguments, and save the updated settings back to the JSON file. # dictionaries with the values provided as arguments, and save the updated settings back to the JSON file.
# #
# You do not need to modify the function's logic or any other part of the code. # You do not need to modify the function's logic or any other part of the code.
def parler_model_update_settings(def_character_voice_gr, def_narrator_voice_gr, lowvram_enabled_gr, deepspeed_enabled_gr, temperature_set_gr, repetitionpenalty_set_gr, pitch_set_gr, generationspeed_set_gr, alloy_gr, echo_gr, fable_gr, nova_gr, onyx_gr, shimmer_gr): def parler_model_update_settings(def_character_voice_gr, def_narrator_voice_gr, lowvram_enabled_gr, deepspeed_enabled_gr, temperature_set_gr, repetitionpenalty_set_gr, pitch_set_gr, generationspeed_set_gr, alloy_gr, echo_gr, fable_gr, nova_gr, onyx_gr, shimmer_gr):
# Load the model_config_data from the JSON file # Load the model_config_data from the JSON file
with open(os.path.join(this_dir, "model_settings.json"), "r") as f: with open(os.path.join(this_dir, "model_settings.json"), "r") as f:
model_config_data = json.load(f) model_config_data = json.load(f)
# Update the settings and openai_voices dictionaries with the new values # Update the settings and openai_voices dictionaries with the new values
model_config_data["settings"]["def_character_voice"] = def_character_voice_gr model_config_data["settings"]["def_character_voice"] = def_character_voice_gr
model_config_data["settings"]["def_narrator_voice"] = def_narrator_voice_gr model_config_data["settings"]["def_narrator_voice"] = def_narrator_voice_gr
model_config_data["openai_voices"]["alloy"] = alloy_gr model_config_data["openai_voices"]["alloy"] = alloy_gr
model_config_data["openai_voices"]["echo"] = echo_gr model_config_data["openai_voices"]["echo"] = echo_gr
model_config_data["openai_voices"]["fable"] = fable_gr model_config_data["openai_voices"]["fable"] = fable_gr
model_config_data["openai_voices"]["nova"] = nova_gr model_config_data["openai_voices"]["nova"] = nova_gr
model_config_data["openai_voices"]["onyx"] = onyx_gr model_config_data["openai_voices"]["onyx"] = onyx_gr
model_config_data["openai_voices"]["shimmer"] = shimmer_gr model_config_data["openai_voices"]["shimmer"] = shimmer_gr
model_config_data["settings"]["lowvram_enabled"] = lowvram_enabled_gr == "Enabled" model_config_data["settings"]["lowvram_enabled"] = lowvram_enabled_gr == "Enabled"
model_config_data["settings"]["deepspeed_enabled"] = deepspeed_enabled_gr == "Enabled" model_config_data["settings"]["deepspeed_enabled"] = deepspeed_enabled_gr == "Enabled"
model_config_data["settings"]["temperature_set"] = temperature_set_gr model_config_data["settings"]["temperature_set"] = temperature_set_gr
model_config_data["settings"]["repetitionpenalty_set"] = repetitionpenalty_set_gr model_config_data["settings"]["repetitionpenalty_set"] = repetitionpenalty_set_gr
model_config_data["settings"]["pitch_set"] = pitch_set_gr model_config_data["settings"]["pitch_set"] = pitch_set_gr
model_config_data["settings"]["generationspeed_set"] = generationspeed_set_gr model_config_data["settings"]["generationspeed_set"] = generationspeed_set_gr
# Save the updated model_config_data to the JSON file # Save the updated model_config_data to the JSON file
with open(os.path.join(this_dir, "model_settings.json"), "w") as f: with open(os.path.join(this_dir, "model_settings.json"), "w") as f:
json.dump(model_config_data, f, indent=4) json.dump(model_config_data, f, indent=4)
return "Settings updated successfully!" return "Settings updated successfully!"
####################################################### #######################################################
# REQUIRED CHANGE # # REQUIRED CHANGE #
# Sets up the engine-specific settings page in Gradio # # Sets up the engine-specific settings page in Gradio #
####################################################### #######################################################
# This function sets up the Gradio interface for the settings page specific to your TTS engine. # This function sets up the Gradio interface for the settings page specific to your TTS engine.
# You need to make the following changes: # You need to make the following changes:
# #
# 1. Change the name of the function `xxxx_model_alltalk_settings` to match your engine's name. # 1. Change the name of the function `xxxx_model_alltalk_settings` to match your engine's name.
# For example, if your engine is named "mytts", the function should be named `mytts_model_alltalk_settings`. # For example, if your engine is named "mytts", the function should be named `mytts_model_alltalk_settings`.
# #
# 2. Change the name of the `submit_button.click` function call to match the name you gave to the function # 2. Change the name of the `submit_button.click` function call to match the name you gave to the function
# that imports and saves your engine's settings (the function you modified above). # that imports and saves your engine's settings (the function you modified above).
# #
# 3. Change the name of the `voice_list` function call to match the name of the function that lists # 3. Change the name of the `voice_list` function call to match the name of the function that lists
# the available voices for your TTS engine. # the available voices for your TTS engine.
# #
# 4. Change the 'title' of the `gr.Blocks` to match your engine's name e.g. title="mytts TTS" # 4. Change the 'title' of the `gr.Blocks` to match your engine's name e.g. title="mytts TTS"
# #
# After making these changes, this function will create and return the Gradio interface for your TTS engine's # After making these changes, this function will create and return the Gradio interface for your TTS engine's
# settings page, allowing users to configure various options and voice selections. # settings page, allowing users to configure various options and voice selections.
def parler_model_alltalk_settings(model_config_data): def parler_model_alltalk_settings(model_config_data):
features_list = model_config_data['model_capabilties'] features_list = model_config_data['model_capabilties']
voice_list = parler_voices_file_list() voice_list = parler_voices_file_list()
with gr.Blocks(title="parler TTS") as app: with gr.Blocks(title="parler TTS") as app:
with gr.Tab("Default Settings"): with gr.Tab("Default Settings"):
with gr.Row(): with gr.Row():
lowvram_enabled_gr = gr.Radio(choices={"Enabled": "true", "Disabled": "false"}, label="Low VRAM" if model_config_data["model_capabilties"]["lowvram_capable"] else "Low VRAM N/A", value="Enabled" if model_config_data["settings"]["lowvram_enabled"] else "Disabled", interactive=model_config_data["model_capabilties"]["lowvram_capable"]) lowvram_enabled_gr = gr.Radio(choices={"Enabled": "true", "Disabled": "false"}, label="Low VRAM" if model_config_data["model_capabilties"]["lowvram_capable"] else "Low VRAM N/A", value="Enabled" if model_config_data["settings"]["lowvram_enabled"] else "Disabled", interactive=model_config_data["model_capabilties"]["lowvram_capable"])
deepspeed_enabled_gr = gr.Radio(choices={"Enabled": "true", "Disabled": "false"}, label="DeepSpeed Activate" if model_config_data["model_capabilties"]["deepspeed_capable"] else "DeepSpeed N/A", value="Enabled" if model_config_data["settings"]["deepspeed_enabled"] else "Disabled", interactive=model_config_data["model_capabilties"]["deepspeed_capable"]) deepspeed_enabled_gr = gr.Radio(choices={"Enabled": "true", "Disabled": "false"}, label="DeepSpeed Activate" if model_config_data["model_capabilties"]["deepspeed_capable"] else "DeepSpeed N/A", value="Enabled" if model_config_data["settings"]["deepspeed_enabled"] else "Disabled", interactive=model_config_data["model_capabilties"]["deepspeed_capable"])
temperature_set_gr = gr.Slider(value=float(model_config_data["settings"]["temperature_set"]), minimum=0, maximum=1, step=0.05, label="Temperature" if model_config_data["model_capabilties"]["temperature_capable"] else "Temperature N/A", interactive=model_config_data["model_capabilties"]["temperature_capable"]) temperature_set_gr = gr.Slider(value=float(model_config_data["settings"]["temperature_set"]), minimum=0, maximum=1, step=0.05, label="Temperature" if model_config_data["model_capabilties"]["temperature_capable"] else "Temperature N/A", interactive=model_config_data["model_capabilties"]["temperature_capable"])
repetitionpenalty_set_gr = gr.Slider(value=float(model_config_data["settings"]["repetitionpenalty_set"]), minimum=1, maximum=20, step=1, label="Repetition Penalty" if model_config_data["model_capabilties"]["repetitionpenalty_capable"] else "Repetition N/A", interactive=model_config_data["model_capabilties"]["repetitionpenalty_capable"]) repetitionpenalty_set_gr = gr.Slider(value=float(model_config_data["settings"]["repetitionpenalty_set"]), minimum=1, maximum=20, step=1, label="Repetition Penalty" if model_config_data["model_capabilties"]["repetitionpenalty_capable"] else "Repetition N/A", interactive=model_config_data["model_capabilties"]["repetitionpenalty_capable"])
pitch_set_gr = gr.Slider(value=float(model_config_data["settings"]["pitch_set"]), minimum=-10, maximum=10, step=1, label="Pitch" if model_config_data["model_capabilties"]["pitch_capable"] else "Pitch N/A", interactive=model_config_data["model_capabilties"]["pitch_capable"]) pitch_set_gr = gr.Slider(value=float(model_config_data["settings"]["pitch_set"]), minimum=-10, maximum=10, step=1, label="Pitch" if model_config_data["model_capabilties"]["pitch_capable"] else "Pitch N/A", interactive=model_config_data["model_capabilties"]["pitch_capable"])
generationspeed_set_gr = gr.Slider(value=float(model_config_data["settings"]["generationspeed_set"]), minimum=0.25, maximum=2.00, step=0.25, label="Speed" if model_config_data["model_capabilties"]["generationspeed_capable"] else "Speed N/A", interactive=model_config_data["model_capabilties"]["generationspeed_capable"]) generationspeed_set_gr = gr.Slider(value=float(model_config_data["settings"]["generationspeed_set"]), minimum=0.25, maximum=2.00, step=0.25, label="Speed" if model_config_data["model_capabilties"]["generationspeed_capable"] else "Speed N/A", interactive=model_config_data["model_capabilties"]["generationspeed_capable"])
with gr.Row(): with gr.Row():
with gr.Column(): with gr.Column():
gr.Markdown("### OpenAI Voice Mappings") gr.Markdown("### OpenAI Voice Mappings")
with gr.Group(): with gr.Group():
with gr.Row(): with gr.Row():
alloy_gr = gr.Dropdown(value=model_config_data["openai_voices"]["alloy"], label="Alloy", choices=voice_list, allow_custom_value=True) alloy_gr = gr.Dropdown(value=model_config_data["openai_voices"]["alloy"], label="Alloy", choices=voice_list, allow_custom_value=True)
echo_gr = gr.Dropdown(value=model_config_data["openai_voices"]["echo"], label="Echo", choices=voice_list, allow_custom_value=True) echo_gr = gr.Dropdown(value=model_config_data["openai_voices"]["echo"], label="Echo", choices=voice_list, allow_custom_value=True)
with gr.Row(): with gr.Row():
fable_gr = gr.Dropdown(value=model_config_data["openai_voices"]["fable"], label="Fable", choices=voice_list, allow_custom_value=True) fable_gr = gr.Dropdown(value=model_config_data["openai_voices"]["fable"], label="Fable", choices=voice_list, allow_custom_value=True)
nova_gr = gr.Dropdown(value=model_config_data["openai_voices"]["nova"], label="Nova", choices=voice_list, allow_custom_value=True) nova_gr = gr.Dropdown(value=model_config_data["openai_voices"]["nova"], label="Nova", choices=voice_list, allow_custom_value=True)
with gr.Row(): with gr.Row():
onyx_gr = gr.Dropdown(value=model_config_data["openai_voices"]["onyx"], label="Onyx", choices=voice_list, allow_custom_value=True) onyx_gr = gr.Dropdown(value=model_config_data["openai_voices"]["onyx"], label="Onyx", choices=voice_list, allow_custom_value=True)
shimmer_gr = gr.Dropdown(value=model_config_data["openai_voices"]["shimmer"], label="Shimmer", choices=voice_list, allow_custom_value=True) shimmer_gr = gr.Dropdown(value=model_config_data["openai_voices"]["shimmer"], label="Shimmer", choices=voice_list, allow_custom_value=True)
with gr.Column(): with gr.Column():
gr.Markdown("### Default Voices") gr.Markdown("### Default Voices")
with gr.Row(): with gr.Row():
def_character_voice_gr = gr.Dropdown(value=model_config_data["settings"]["def_character_voice"], label="Default/Character Voice", choices=voice_list, allow_custom_value=True) def_character_voice_gr = gr.Dropdown(value=model_config_data["settings"]["def_character_voice"], label="Default/Character Voice", choices=voice_list, allow_custom_value=True)
def_narrator_voice_gr = gr.Dropdown(value=model_config_data["settings"]["def_narrator_voice"], label="Narrator Voice", choices=voice_list, allow_custom_value=True) def_narrator_voice_gr = gr.Dropdown(value=model_config_data["settings"]["def_narrator_voice"], label="Narrator Voice", choices=voice_list, allow_custom_value=True)
with gr.Group(): with gr.Group():
with gr.Row(): with gr.Row():
details_text = gr.Textbox(label="Details", show_label=False, lines=5, interactive=False, value="In this section, you can set the default settings for this TTS engine. Settings that are not supported by the current engine will be greyed out and cannot be selected. Default voices specified here will be used when no specific voice is provided in the TTS generation request. If a voice is specified in the request, it will override these default settings. When using the OpenAI API compatable API with this TTS engine, the voice mappings will be applied. As the OpenAI API has a limited set of 6 voices, these mappings ensure compatibility by mapping the OpenAI voices to the available voices in this TTS engine.") details_text = gr.Textbox(label="Details", show_label=False, lines=5, interactive=False, value="In this section, you can set the default settings for this TTS engine. Settings that are not supported by the current engine will be greyed out and cannot be selected. Default voices specified here will be used when no specific voice is provided in the TTS generation request. If a voice is specified in the request, it will override these default settings. When using the OpenAI API compatable API with this TTS engine, the voice mappings will be applied. As the OpenAI API has a limited set of 6 voices, these mappings ensure compatibility by mapping the OpenAI voices to the available voices in this TTS engine.")
with gr.Row(): with gr.Row():
submit_button = gr.Button("Update Settings") submit_button = gr.Button("Update Settings")
output_message = gr.Textbox(label="Output Message", interactive=False, show_label=False) output_message = gr.Textbox(label="Output Message", interactive=False, show_label=False)
submit_button.click(parler_model_update_settings, inputs=[def_character_voice_gr, def_narrator_voice_gr, lowvram_enabled_gr, deepspeed_enabled_gr, temperature_set_gr, repetitionpenalty_set_gr, pitch_set_gr, generationspeed_set_gr, alloy_gr, echo_gr, fable_gr, nova_gr, onyx_gr, shimmer_gr], outputs=output_message) submit_button.click(parler_model_update_settings, inputs=[def_character_voice_gr, def_narrator_voice_gr, lowvram_enabled_gr, deepspeed_enabled_gr, temperature_set_gr, repetitionpenalty_set_gr, pitch_set_gr, generationspeed_set_gr, alloy_gr, echo_gr, fable_gr, nova_gr, onyx_gr, shimmer_gr], outputs=output_message)
########################################################################################### ###########################################################################################
# Do not change this section apart from "TTS Engine Name" value to match your engine name # # Do not change this section apart from "TTS Engine Name" value to match your engine name #
########################################################################################### ###########################################################################################
with gr.Tab("Engine Information"): with gr.Tab("Engine Information"):
with gr.Row(): with gr.Row():
with gr.Group(): with gr.Group():
gr.Textbox(label="Manufacturer Name", value=model_config_data['model_details']['manufacturer_name'], interactive=False) gr.Textbox(label="Manufacturer Name", value=model_config_data['model_details']['manufacturer_name'], interactive=False)
gr.Textbox(label="Manufacturer Website/TTS Engine Support", value=model_config_data['model_details']['manufacturer_website'], interactive=False) gr.Textbox(label="Manufacturer Website/TTS Engine Support", value=model_config_data['model_details']['manufacturer_website'], interactive=False)
gr.Textbox(label="Engine/Model Description", value=model_config_data['model_details']['model_description'], interactive=False, lines=13) gr.Textbox(label="Engine/Model Description", value=model_config_data['model_details']['model_description'], interactive=False, lines=13)
with gr.Column(): with gr.Column():
with gr.Row(): with gr.Row():
gr.Textbox(label="DeepSpeed Capable", value='Yes' if features_list['deepspeed_capable'] else 'No', interactive=False) gr.Textbox(label="DeepSpeed Capable", value='Yes' if features_list['deepspeed_capable'] else 'No', interactive=False)
gr.Textbox(label="Pitch Capable", value='Yes' if features_list['pitch_capable'] else 'No', interactive=False) gr.Textbox(label="Pitch Capable", value='Yes' if features_list['pitch_capable'] else 'No', interactive=False)
gr.Textbox(label="Generation Speed Capable", value='Yes' if features_list['generationspeed_capable'] else 'No', interactive=False) gr.Textbox(label="Generation Speed Capable", value='Yes' if features_list['generationspeed_capable'] else 'No', interactive=False)
with gr.Row(): with gr.Row():
gr.Textbox(label="Repetition Penalty Capable", value='Yes' if features_list['repetitionpenalty_capable'] else 'No', interactive=False) gr.Textbox(label="Repetition Penalty Capable", value='Yes' if features_list['repetitionpenalty_capable'] else 'No', interactive=False)
gr.Textbox(label="Multi Languages Capable", value='Yes' if features_list['languages_capable'] else 'No', interactive=False) gr.Textbox(label="Multi Languages Capable", value='Yes' if features_list['languages_capable'] else 'No', interactive=False)
gr.Textbox(label="Streaming Capable", value='Yes' if features_list['streaming_capable'] else 'No', interactive=False) gr.Textbox(label="Streaming Capable", value='Yes' if features_list['streaming_capable'] else 'No', interactive=False)
with gr.Row(): with gr.Row():
gr.Textbox(label="Low VRAM Capable", value='Yes' if features_list['lowvram_capable'] else 'No', interactive=False) gr.Textbox(label="Low VRAM Capable", value='Yes' if features_list['lowvram_capable'] else 'No', interactive=False)
gr.Textbox(label="Temperature Capable", value='Yes' if features_list['temperature_capable'] else 'No', interactive=False) gr.Textbox(label="Temperature Capable", value='Yes' if features_list['temperature_capable'] else 'No', interactive=False)
gr.Textbox(label="Multi Model Capable Engine", value='Yes' if features_list['multimodel_capable'] else 'No', interactive=False) gr.Textbox(label="Multi Model Capable Engine", value='Yes' if features_list['multimodel_capable'] else 'No', interactive=False)
with gr.Row(): with gr.Row():
gr.Textbox(label="Multi Voice Capable Models", value='Yes' if features_list['multivoice_capable'] else 'No', interactive=False) gr.Textbox(label="Multi Voice Capable Models", value='Yes' if features_list['multivoice_capable'] else 'No', interactive=False)
gr.Textbox(label="Default Audio output format", value=model_config_data['model_capabilties']['audio_format'], interactive=False) gr.Textbox(label="Default Audio output format", value=model_config_data['model_capabilties']['audio_format'], interactive=False)
gr.Textbox(label="TTS Engine Name", value="parler", interactive=False) gr.Textbox(label="TTS Engine Name", value="parler", interactive=False)
with gr.Row(): with gr.Row():
gr.Textbox(label="Windows Support", value='Yes' if features_list['windows_capable'] else 'No', interactive=False) gr.Textbox(label="Windows Support", value='Yes' if features_list['windows_capable'] else 'No', interactive=False)
gr.Textbox(label="Linux Support", value='Yes' if features_list['linux_capable'] else 'No', interactive=False) gr.Textbox(label="Linux Support", value='Yes' if features_list['linux_capable'] else 'No', interactive=False)
gr.Textbox(label="Mac Support", value='Yes' if features_list['mac_capable'] else 'No', interactive=False) gr.Textbox(label="Mac Support", value='Yes' if features_list['mac_capable'] else 'No', interactive=False)
with gr.Row(): with gr.Row():
gr.Markdown(""" gr.Markdown("""
#### 🟧 DeepSpeed Capable #### 🟧 DeepSpeed Capable
DeepSpeed is a deep learning optimization library that can significantly speed up model training and inference. If a model is DeepSpeed capable, it means it can utilize DeepSpeed to accelerate the generation of text-to-speech output. This requires the model to be loaded into CUDA/VRAM on an Nvidia GPU and the model's inference method to support DeepSpeed. DeepSpeed is a deep learning optimization library that can significantly speed up model training and inference. If a model is DeepSpeed capable, it means it can utilize DeepSpeed to accelerate the generation of text-to-speech output. This requires the model to be loaded into CUDA/VRAM on an Nvidia GPU and the model's inference method to support DeepSpeed.
#### 🟧 Pitch Capable #### 🟧 Pitch Capable
Pitch refers to the highness or lowness of a sound. If a model is pitch capable, it can adjust the pitch of the generated speech, allowing for more expressive and varied output. Pitch refers to the highness or lowness of a sound. If a model is pitch capable, it can adjust the pitch of the generated speech, allowing for more expressive and varied output.
#### 🟧 Generation Speed Capable #### 🟧 Generation Speed Capable
Generation speed refers to the rate at which the model can generate text-to-speech output. If a model is generation speed capable, it means the speed of the generated speech can be adjusted, making it faster or slower depending on the desired output. Generation speed refers to the rate at which the model can generate text-to-speech output. If a model is generation speed capable, it means the speed of the generated speech can be adjusted, making it faster or slower depending on the desired output.
#### 🟧 Repetition Penalty Capable #### 🟧 Repetition Penalty Capable
Repetition penalty is a technique used to discourage the model from repeating the same words or phrases multiple times in the same sounding way. If a model is repetition penalty capable, it can apply this penalty during generation to improve the diversity and naturalness of the output. Repetition penalty is a technique used to discourage the model from repeating the same words or phrases multiple times in the same sounding way. If a model is repetition penalty capable, it can apply this penalty during generation to improve the diversity and naturalness of the output.
#### 🟧 Multi-Languages Capable #### 🟧 Multi-Languages Capable
Multi-language capable models can generate speech in multiple languages. This means that the model has been trained on data from different languages and can switch between them during generation. Some models are language-specific. Multi-language capable models can generate speech in multiple languages. This means that the model has been trained on data from different languages and can switch between them during generation. Some models are language-specific.
#### 🟧 Multi-Voice Capable #### 🟧 Multi-Voice Capable
Multi-voice capable models generate speech in multiple voices or speaking styles. This means that the model has been trained on data from different speakers and can mimic their voices during generation, or is a voice cloning model that can generate speech based on the input sample. Multi-voice capable models generate speech in multiple voices or speaking styles. This means that the model has been trained on data from different speakers and can mimic their voices during generation, or is a voice cloning model that can generate speech based on the input sample.
""") """)
gr.Markdown(""" gr.Markdown("""
#### 🟧 Streaming Capable #### 🟧 Streaming Capable
Streaming refers to the ability to generate speech output in real-time, without the need to generate the entire output before playback. If a model is streaming capable, it can generate speech on-the-fly, allowing for faster response times and more interactive applications. Streaming refers to the ability to generate speech output in real-time, without the need to generate the entire output before playback. If a model is streaming capable, it can generate speech on-the-fly, allowing for faster response times and more interactive applications.
#### 🟧 Low VRAM Capable #### 🟧 Low VRAM Capable
VRAM (Video Random Access Memory) is a type of memory used by GPUs to store and process data. If a model is low VRAM capable, it can efficiently utilize the available VRAM by moving data between CPU and GPU memory as needed, allowing for generation even on systems with limited VRAM where it may be competing with an LLM model for VRAM. VRAM (Video Random Access Memory) is a type of memory used by GPUs to store and process data. If a model is low VRAM capable, it can efficiently utilize the available VRAM by moving data between CPU and GPU memory as needed, allowing for generation even on systems with limited VRAM where it may be competing with an LLM model for VRAM.
#### 🟧 Temperature Capable #### 🟧 Temperature Capable
Temperature is a hyperparameter that controls the randomness of the generated output. If a model is temperature capable, the temperature can be adjusted to make the output more or less random, affecting the creativity and variability of the generated speech. Temperature is a hyperparameter that controls the randomness of the generated output. If a model is temperature capable, the temperature can be adjusted to make the output more or less random, affecting the creativity and variability of the generated speech.
#### 🟧 Multi-Model Capable Engine #### 🟧 Multi-Model Capable Engine
If an engine is multi-model capable, it means that it can support and utilize multiple models for text-to-speech generation. This allows for greater flexibility and the ability to switch between different models depending on the desired output. Different models may be capable of different languages, specific languages, voices, etc. If an engine is multi-model capable, it means that it can support and utilize multiple models for text-to-speech generation. This allows for greater flexibility and the ability to switch between different models depending on the desired output. Different models may be capable of different languages, specific languages, voices, etc.
#### 🟧 Default Audio Output Format #### 🟧 Default Audio Output Format
Specifies the file format in which the generated speech will be saved. Common audio formats include WAV, MP3, FLAC, Opus, AAC, and PCM. If you want different outputs, you can set the transcode function to change the output audio, though transcoding will add a little time to the generation and is not available for streaming generation. Specifies the file format in which the generated speech will be saved. Common audio formats include WAV, MP3, FLAC, Opus, AAC, and PCM. If you want different outputs, you can set the transcode function to change the output audio, though transcoding will add a little time to the generation and is not available for streaming generation.
#### 🟧 Windows/Linux/Mac Support #### 🟧 Windows/Linux/Mac Support
These indicators show whether the model and engine are compatible with Windows, Linux, or macOS. However, additional setup or requirements may be necessary to ensure full functionality on your operating system. Please note that full platform support has not been extensively tested. These indicators show whether the model and engine are compatible with Windows, Linux, or macOS. However, additional setup or requirements may be necessary to ensure full functionality on your operating system. Please note that full platform support has not been extensively tested.
""") """)
####################################################################################################################################################################################################### #######################################################################################################################################################################################################
# REQUIRED CHANGE # # REQUIRED CHANGE #
# You will need to build a custom method to identify if your models are installed and download them. Store your models list in the available_models.json file, alongside your model_settings.json etc # # You will need to build a custom method to identify if your models are installed and download them. Store your models list in the available_models.json file, alongside your model_settings.json etc #
####################################################################################################################################################################################################### #######################################################################################################################################################################################################
with gr.Tab("Models/Voices Download"): with gr.Tab("Models/Voices Download"):
with gr.Row(): with gr.Row():
# Load the available models from the JSON file # Load the available models from the JSON file
with open(os.path.join(this_dir, "available_models.json"), "r") as f: with open(os.path.join(this_dir, "available_models.json"), "r") as f:
available_models = json.load(f) available_models = json.load(f)
# Extract the model names for the dropdown # Extract the model names for the dropdown
model_names = [model["model_name"] for model in available_models["models"]] model_names = [model["model_name"] for model in available_models["models"]]
# Create the dropdown # Create the dropdown
model_dropdown = gr.Dropdown(choices=sorted(model_names), label="Select Model", value=model_names[0]) model_dropdown = gr.Dropdown(choices=sorted(model_names), label="Select Model", value=model_names[0])
download_button = gr.Button("Download Model/Missing Files") download_button = gr.Button("Download Model/Missing Files")
def download_model(model_name, force_download=False): def download_model(model_name, force_download=False):
# Find the selected model in the available models # Find the selected model in the available models
selected_model = next(model for model in available_models["models"] if model["model_name"] == model_name) selected_model = next(model for model in available_models["models"] if model["model_name"] == model_name)
# Get the folder path and files to download # Get the folder path and files to download
folder_path = os.path.join(main_dir, "models", "parler", model_name) folder_path = os.path.join(main_dir, "models", "parler", model_name)
files_to_download = selected_model["files_to_download"] files_to_download = selected_model["files_to_download"]
# Check if all files are already downloaded # Check if all files are already downloaded
all_files_exists = all(os.path.exists(os.path.join(folder_path, os.path.basename(url.split('?')[0]))) for url in files_to_download) all_files_exists = all(os.path.exists(os.path.join(folder_path, os.path.basename(url.split('?')[0]))) for url in files_to_download)
if all_files_exists and not force_download: if all_files_exists and not force_download:
return "All files are already downloaded. No need to download again." return "All files are already downloaded. No need to download again."
else: else:
# Create the folder if it doesn't exist # Create the folder if it doesn't exist
os.makedirs(folder_path, exist_ok=True) os.makedirs(folder_path, exist_ok=True)
# Download the missing files # Download the missing files
for url in files_to_download: for url in files_to_download:
file_name = os.path.basename(url.split('?')[0]) file_name = os.path.basename(url.split('?')[0])
file_path = os.path.join(folder_path, file_name) file_path = os.path.join(folder_path, file_name)
if not os.path.exists(file_path) or force_download: if not os.path.exists(file_path) or force_download:
print(f"Downloading {file_name}...") print(f"Downloading {file_name}...")
response = requests.get(url, stream=True) response = requests.get(url, stream=True)
total_size_in_bytes = int(response.headers.get("content-length", 0)) total_size_in_bytes = int(response.headers.get("content-length", 0))
block_size = 1024 # 1 Kibibyte block_size = 1024 # 1 Kibibyte
progress_bar = tqdm(total=total_size_in_bytes, unit="iB", unit_scale=True) progress_bar = tqdm(total=total_size_in_bytes, unit="iB", unit_scale=True)
with open(file_path, "wb") as file: with open(file_path, "wb") as file:
for data in response.iter_content(block_size): for data in response.iter_content(block_size):
progress_bar.update(len(data)) progress_bar.update(len(data))
file.write(data) file.write(data)
progress_bar.close() progress_bar.close()
return "Model downloaded successfully!" return "Model downloaded successfully!"
with gr.Row(): with gr.Row():
download_status = gr.Textbox(label="Download Status") download_status = gr.Textbox(label="Download Status")
download_button.click(download_model, inputs=model_dropdown, outputs=download_status) download_button.click(download_model, inputs=model_dropdown, outputs=download_status)
def show_confirm_cancel(model_name): def show_confirm_cancel(model_name):
all_files_exists = all(os.path.exists(os.path.join(main_dir, "models", "parler", os.path.basename(file.split('?')[0]))) for model in available_models["models"] if model["model_name"] == model_name for file in model["files_to_download"]) all_files_exists = all(os.path.exists(os.path.join(main_dir, "models", "parler", os.path.basename(file.split('?')[0]))) for model in available_models["models"] if model["model_name"] == model_name for file in model["files_to_download"])
if all_files_exists: if all_files_exists:
return [gr.update(visible=True), gr.update(visible=True), gr.update(visible=True)] return [gr.update(visible=True), gr.update(visible=True), gr.update(visible=True)]
else: else:
return [gr.update(visible=False), gr.update(visible=True), gr.update(visible=False)] return [gr.update(visible=False), gr.update(visible=True), gr.update(visible=False)]
def confirm_download(model_name): def confirm_download(model_name):
download_status_text = download_model(model_name, force_download=True) download_status_text = download_model(model_name, force_download=True)
return [gr.update(visible=False), gr.update(visible=True), gr.update(visible=False), download_status_text] return [gr.update(visible=False), gr.update(visible=True), gr.update(visible=False), download_status_text]
def cancel_download(): def cancel_download():
return [gr.update(visible=False), gr.update(visible=True), gr.update(visible=False), ""] return [gr.update(visible=False), gr.update(visible=True), gr.update(visible=False), ""]
with gr.Row(): with gr.Row():
confirm_button = gr.Button("Download Anyway", visible=False) confirm_button = gr.Button("Download Anyway", visible=False)
cancel_button = gr.Button("Cancel", visible=False) cancel_button = gr.Button("Cancel", visible=False)
download_button.click(show_confirm_cancel, inputs=model_dropdown, outputs=[confirm_button, download_button, cancel_button]) download_button.click(show_confirm_cancel, inputs=model_dropdown, outputs=[confirm_button, download_button, cancel_button])
confirm_button.click(confirm_download, inputs=model_dropdown, outputs=[confirm_button, download_button, cancel_button, download_status]) confirm_button.click(confirm_download, inputs=model_dropdown, outputs=[confirm_button, download_button, cancel_button, download_status])
cancel_button.click(cancel_download, inputs=None, outputs=[confirm_button, download_button, cancel_button, download_status]) cancel_button.click(cancel_download, inputs=None, outputs=[confirm_button, download_button, cancel_button, download_status])
##################################### #####################################
# Voices Editor - Unique to Parler # # Voices Editor - Unique to Parler #
##################################### #####################################
# Load voices from the JSON file # Load voices from the JSON file
def load_voices(): def load_voices():
voices_file = os.path.join(main_dir, "system", "tts_engines", "parler", "parler_voices.json") voices_file = os.path.join(main_dir, "system", "tts_engines", "parler", "parler_voices.json")
if os.path.exists(voices_file): if os.path.exists(voices_file):
with open(voices_file, "r") as f: with open(voices_file, "r") as f:
return json.load(f) return json.load(f)
else: else:
return {"voices": []} return {"voices": []}
# Save voices to the JSON file # Save voices to the JSON file
def save_voices(voices): def save_voices(voices):
voices_file = os.path.join(main_dir, "system", "tts_engines", "parler", "parler_voices.json") voices_file = os.path.join(main_dir, "system", "tts_engines", "parler", "parler_voices.json")
with open(voices_file, "w") as f: with open(voices_file, "w") as f:
json.dump(voices, f, indent=2) json.dump(voices, f, indent=2)
# Convert voices to a list for display # Convert voices to a list for display
def voices_to_list(voices): def voices_to_list(voices):
return [f"{i+1}. {voice['voice_name']} - {voice['description']}" for i, voice in enumerate(voices["voices"])] return [f"{i+1}. {voice['voice_name']} - {voice['description']}" for i, voice in enumerate(voices["voices"])]
# Populate text boxes with selected voice data # Populate text boxes with selected voice data
def populate_voice_data(selected): def populate_voice_data(selected):
if selected: if selected:
index = int(selected.split(".")[0]) - 1 index = int(selected.split(".")[0]) - 1
voices = load_voices() voices = load_voices()
selected_voice = voices["voices"][index] selected_voice = voices["voices"][index]
return selected_voice["voice_name"], selected_voice["description"], index, gr.update(interactive=False), "Save Voice" return selected_voice["voice_name"], selected_voice["description"], index, gr.update(interactive=False), "Save Voice"
return "", "", -1, gr.update(interactive=True), "Add/Save Voice" return "", "", -1, gr.update(interactive=True), "Add/Save Voice"
# Add or update voice # Add or update voice
def add_update_voice(voice_name, description, selected_index, button_label): def add_update_voice(voice_name, description, selected_index, button_label):
voices = load_voices() voices = load_voices()
if selected_index >= 0: if selected_index >= 0:
voices["voices"][selected_index]["description"] = description voices["voices"][selected_index]["description"] = description
else: else:
voices["voices"].append({"voice_name": voice_name, "description": description}) voices["voices"].append({"voice_name": voice_name, "description": description})
save_voices(voices) save_voices(voices)
return (gr.Dropdown(choices=voices_to_list(voices), value=voices_to_list(voices)[0] if voices["voices"] else None), return (gr.Dropdown(choices=voices_to_list(voices), value=voices_to_list(voices)[0] if voices["voices"] else None),
"", "", -1, gr.update(interactive=True), "Add/Save Voice") "", "", -1, gr.update(interactive=True), "Add/Save Voice")
# Delete selected voice # Delete selected voice
def delete_selected_voice(selected_index): def delete_selected_voice(selected_index):
voices = load_voices() voices = load_voices()
if selected_index >= 0: if selected_index >= 0:
del voices["voices"][selected_index] del voices["voices"][selected_index]
save_voices(voices) save_voices(voices)
return (gr.Dropdown(choices=voices_to_list(voices), value=voices_to_list(voices)[0] if voices["voices"] else None), return (gr.Dropdown(choices=voices_to_list(voices), value=voices_to_list(voices)[0] if voices["voices"] else None),
"", "", -1, gr.update(interactive=True), "Add/Save Voice") "", "", -1, gr.update(interactive=True), "Add/Save Voice")
# Clear text boxes # Clear text boxes
def clear_text_boxes(): def clear_text_boxes():
return "", "", -1, gr.update(interactive=True), "Add/Save Voice" return "", "", -1, gr.update(interactive=True), "Add/Save Voice"
# Load initial voices # Load initial voices
voices = load_voices() voices = load_voices()
with gr.Tab("Voice Editor"): with gr.Tab("Voice Editor"):
with gr.Row(): with gr.Row():
voice_name = gr.Textbox(label="Voice Name", scale=1) voice_name = gr.Textbox(label="Voice Name", scale=1)
description = gr.Textbox(label="Voice Description", lines=4, scale=3) description = gr.Textbox(label="Voice Description", lines=4, scale=3)
with gr.Row(): with gr.Row():
add_update_button = gr.Button("Add/Save Voice") add_update_button = gr.Button("Add/Save Voice")
delete_button = gr.Button("Delete Selected Voice") delete_button = gr.Button("Delete Selected Voice")
clear_button = gr.Button("Clear Text Boxes") clear_button = gr.Button("Clear Text Boxes")
with gr.Row(): with gr.Row():
voice_list = gr.Dropdown( voice_list = gr.Dropdown(
choices=voices_to_list(voices), choices=voices_to_list(voices),
label="Voices", label="Voices",
interactive=True, interactive=True,
value=voices_to_list(voices)[0] if voices["voices"] else None value=voices_to_list(voices)[0] if voices["voices"] else None
) )
# Hidden state for selected index and button label # Hidden state for selected index and button label
selected_index = gr.Number(value=-1, visible=False) selected_index = gr.Number(value=-1, visible=False)
button_label = gr.State(value="Add/Save Voice") button_label = gr.State(value="Add/Save Voice")
# Button click events # Button click events
voice_list.change(populate_voice_data, inputs=voice_list, outputs=[voice_name, description, selected_index, voice_name, button_label]) voice_list.change(populate_voice_data, inputs=voice_list, outputs=[voice_name, description, selected_index, voice_name, button_label])
add_update_button.click(add_update_voice, inputs=[voice_name, description, selected_index, button_label], outputs=[voice_list, voice_name, description, selected_index, voice_name, button_label]) add_update_button.click(add_update_voice, inputs=[voice_name, description, selected_index, button_label], outputs=[voice_list, voice_name, description, selected_index, voice_name, button_label])
delete_button.click(delete_selected_voice, inputs=selected_index, outputs=[voice_list, voice_name, description, selected_index, voice_name, button_label]) delete_button.click(delete_selected_voice, inputs=selected_index, outputs=[voice_list, voice_name, description, selected_index, voice_name, button_label])
clear_button.click(clear_text_boxes, outputs=[voice_name, description, selected_index, voice_name, button_label]) clear_button.click(clear_text_boxes, outputs=[voice_name, description, selected_index, voice_name, button_label])
################################################################################################### ###################################################################################################
# REQUIRED CHANGE # # REQUIRED CHANGE #
# Add any engine specific help, bugs, issues, operating system specifc requirements/setup in here # # Add any engine specific help, bugs, issues, operating system specifc requirements/setup in here #
# Please use Markdown format, so gr.Markdown() with your markdown inside it. # # Please use Markdown format, so gr.Markdown() with your markdown inside it. #
################################################################################################### ###################################################################################################
with gr.Tab("Engine Help"): with gr.Tab("Engine Help"):
with gr.Row(): with gr.Row():
gr.Markdown(""" gr.Markdown("""
### 🟧 Where are the Parler models stored? ### 🟧 Where are the Parler models stored?
This extension will download the models to `/alltalk_tts/models/parler/` folder.<br> This extension will download the models to `/alltalk_tts/models/parler/` folder.<br>
### 🟧 Where are the voices stored for parler models? ### 🟧 Where are the voices stored for parler models?
Parler is unlike any other TTS engine. It is actually mode like Stable Diffusion, where you write a text based description of what you want your image to look like, but in this case, you describe what you want the voice to sound like. This means you can have pretty much any voice you want, however, the downside to this is, there is no consistency of the voices, meaning that no 2x TTS generations will sound exactly like the same voice.<br> Parler is unlike any other TTS engine. It is actually mode like Stable Diffusion, where you write a text based description of what you want your image to look like, but in this case, you describe what you want the voice to sound like. For example `A female speaker with an enthusiastic and lively voice. Her tone is bright and energetic, with a fast pace and a lot of inflection. The audio is clear and vibrant.`
### 🟧 So how do I create my voices for Parler? This means you can have pretty much any voice you want, however, the downside to this is, there is little consistency of the voices, meaning that no 2x TTS generations will sound exactly like the same voice. You can however use one of the `Native` built in voices to give a level of consistency. Please refer to the Parler website for more infomation https://github.com/huggingface/parler-tts.<br>
On the settings page for Parler, there is a Voice Editor tab. In there you can add/remove/ammend voices as you wish, or edit the `parler_voices.json` file stored in `system/tts_engines/parler/`.
### 🟧 So how do I create my voices for Parler?
### 🟧 Where are the outputs stored & Automatic output wav file deletion On the settings page for Parler, there is a Voice Editor tab. In there you can add/remove/ammend voices as you wish, or edit the `parler_voices.json` file stored in `system/tts_engines/parler/`.
Voice outputs are stored in `/alltalk_tts/outputs/`. You can configure automatic maintenance deletion of old wav files by setting `Del WAV's older than` in the global settings.<br>
### 🟧 Where are the outputs stored & Automatic output wav file deletion
> When `Disabled`, your output wav files will be left untouched.<br> Voice outputs are stored in `/alltalk_tts/outputs/`. You can configure automatic maintenance deletion of old wav files by setting `Del WAV's older than` in the global settings.<br>
> When set to a setting `1 Day` or greater, your output wav files older than that time period will be automatically deleted on start-up of AllTalk.<br>
""") > When `Disabled`, your output wav files will be left untouched.<br>
gr.Markdown(""" > When set to a setting `1 Day` or greater, your output wav files older than that time period will be automatically deleted on start-up of AllTalk.<br>
""")
### 🟧 Skipped text/speech
return app At time of writing, this model is 1x day old and I could see reports of issues with some TTS not being generated, some longer text missing the middle portion of the text etc. You can confirm what AllTalk sent to the Parler TTS engine by looking at the command prompt/terminal window. If your text shows up there, it was sent to the Parler TTS engine. Please refer to the Parler website for more details https://github.com/huggingface/parler-tts.
""")
################################ gr.Markdown("""
# REQUIRED CHANGE # ### 🟧 How do I use Parler's voice generation system?
# Sets up the Gradio interface # To create a voice with Parler:
################################
# This function sets up the Gradio interface for your TTS engine's settings page. Describe the voice characteristics in your prompt. For example: `Jon's voice is monotone yet slightly fast in delivery, with a very close recording that almost has no background noise.`
# You need to change the name of the function calls to match the names you set in the functions above.
# Use one of the 34 inbuilt `Native` speaker names for a level of consistency: `Aaron, Alisa, Anna, Barbara, Bill, Brenda, Bruce, Carol, David, Eileen, Emily, Eric, Gary, James, Jason, Jenna, Jerry, Jon, Jordan, Joy, Karen, Laura, Lauren, Lea, Mike, Naomie, Patrick, Rebecca, Rick, Rose, Tina, Tom, Will, Yann`
# Specifically, you need to update the following:
# Control audio quality in the voice characteristics prompt:
# 1. The name of the function `xxxx_at_gradio_settings_page` to match your engine's name.
# For example, if your engine is named "mytts", the function should be named `mytts_at_gradio_settings_page`. > Include `very clear audio` for highest quality<br>
# > Use `very noisy audio` for high levels of background noise
# 2. The name of the function call `xxxx_model_alltalk_settings(model_config_data)`.
# This should match the name you gave to the function that sets up the engine-specific settings page in Gradio. Adjust other features in your voice characteristics prompt:
# If you named that function `mytts_model_alltalk_settings`, then the call should be:
# `mytts_model_alltalk_settings(model_config_data)` > Gender<br>
# > Speaking rate<br>
# After making these changes, this function will create and return the Gradio app for your TTS engine's settings page. > Pitch<br>
def parler_at_gradio_settings_page(model_config_data): > Reverberation
app = parler_model_alltalk_settings(model_config_data)
return app Remember, while you can create diverse voices, each generation may sound slightly different even with the same description.
def parler_at_gradio_settings_page(model_config_data):
app = parler_model_alltalk_settings(model_config_data) In the text sent for TTS generation, you can use punctuation to control speech rhythm (e.g. use `,` commas for small breaks)
### 🟧 UserWarning: 1Torch was not compiled with flash attention
This is a limitation of Windows Pytorch not yet having full flash attention support on later builds of Pytorch (is my understanding at this time).
""")
return app
################################
# REQUIRED CHANGE #
# Sets up the Gradio interface #
################################
# This function sets up the Gradio interface for your TTS engine's settings page.
# You need to change the name of the function calls to match the names you set in the functions above.
#
# Specifically, you need to update the following:
#
# 1. The name of the function `xxxx_at_gradio_settings_page` to match your engine's name.
# For example, if your engine is named "mytts", the function should be named `mytts_at_gradio_settings_page`.
#
# 2. The name of the function call `xxxx_model_alltalk_settings(model_config_data)`.
# This should match the name you gave to the function that sets up the engine-specific settings page in Gradio.
# If you named that function `mytts_model_alltalk_settings`, then the call should be:
# `mytts_model_alltalk_settings(model_config_data)`
#
# After making these changes, this function will create and return the Gradio app for your TTS engine's settings page.
def parler_at_gradio_settings_page(model_config_data):
app = parler_model_alltalk_settings(model_config_data)
return app
def parler_at_gradio_settings_page(model_config_data):
app = parler_model_alltalk_settings(model_config_data)
return app return app
+231 -95
View File
@@ -1,96 +1,232 @@
{ {
"voices": [ "voices": [
{ {
"voice_name": "soothing_female", "voice_name": "Native Aaron",
"description": "A female speaker with a soft, soothing voice. Her tone is gentle and calming, with a slight reverb as if she is in a small, cozy room. She speaks slowly and clearly." "description": "Aaron's voice is a deep male voice with a resonant quality. He speaks slowly and clearly, with a commanding presence."
}, },
{ {
"voice_name": "authoritative_male", "voice_name": "Native Alisa",
"description": "A male speaker with a strong, authoritative voice. His tone is firm and commanding, with a slight bass boost that adds to the depth. He speaks at a moderate pace." "description": "Alisa's voice is a crisp female voice with a hint of assertiveness. She speaks at a brisk pace with clear enunciation."
}, },
{ {
"voice_name": "friendly_young_male", "voice_name": "Native Anna",
"description": "A young male speaker with a friendly, approachable voice. He has a slightly high-pitched tone and speaks with enthusiasm. The audio is clear and bright." "description": "Anna's voice is a soft female voice with a slight Eastern European accent. She speaks at a careful pace with gentle tones."
}, },
{ {
"voice_name": "whispering_female", "voice_name": "Native Barbara",
"description": "A female speaker who whispers softly, creating an intimate and personal feel. The audio has a slight breathy quality, adding to the whispering effect." "description": "Barbara's voice is a sophisticated female voice with a slight huskiness. She speaks at a measured pace with refined intonation."
}, },
{ {
"voice_name": "robotic_male", "voice_name": "Native Bill",
"description": "A male speaker with a robotic, synthetic voice. The tone is monotone and slightly mechanical, with clear and precise pronunciation." "description": "Bill's voice is a mature male voice with a gravelly texture. He speaks slowly and deliberately, with a wise tone."
}, },
{ {
"voice_name": "dramatic_female", "voice_name": "Native Brenda",
"description": "A female speaker with a dramatic and expressive voice. She speaks with varying pitch and intensity, in a high-quality audio environment. Her pace varies to add emphasis." "description": "Brenda's voice is a soft-spoken female voice with a gentle, soothing quality. She speaks slowly and clearly."
}, },
{ {
"voice_name": "casual_male", "voice_name": "Native Bruce",
"description": "A male speaker with a casual, conversational tone. He speaks in a relaxed manner, with natural pauses and inflections. The audio is clear and slightly ambient." "description": "Bruce's voice is a strong male voice with a commanding presence. He speaks at a steady pace with authoritative tones."
}, },
{ {
"voice_name": "bright_young_female", "voice_name": "Native Carol",
"description": "A young female speaker with a bright, cheerful voice. She speaks with a lot of enthusiasm and a slightly fast pace, in a clear and vibrant audio environment." "description": "Carol's voice is a mature female voice with a warm, motherly quality. She speaks at a gentle pace with nurturing tones."
}, },
{ {
"voice_name": "calm_older_male", "voice_name": "Native David",
"description": "An older male speaker with a calm, soothing voice. His tone is deep and steady, with a slight reverb suggesting a large, quiet room. He speaks slowly and deliberately." "description": "David's voice is an authoritative male voice with a rich timbre. He speaks at a steady pace with precise diction."
}, },
{ {
"voice_name": "energetic_child", "voice_name": "Native Eileen",
"description": "A child speaker with a high-pitched, energetic voice. The tone is playful and lively, with clear and crisp audio quality. The speech pace is fast." "description": "Eileen's voice is a mature female voice with a slight breathiness. She speaks at a relaxed pace with a warm tone."
}, },
{ {
"voice_name": "mysterious_female", "voice_name": "Native Emily",
"description": "A female speaker with a low, mysterious voice. Her tone is hushed and slightly breathy, creating an air of secrecy. The audio has a slight echo effect." "description": "Emily's voice is a sweet female voice with a youthful quality. She speaks at a lively pace with cheerful inflections."
}, },
{ {
"voice_name": "confident_male", "voice_name": "Native Eric",
"description": "A male speaker with a confident and assertive voice. His tone is strong and clear, with a moderate pace and slight emphasis on certain words to convey assurance." "description": "Eric's voice is a casual male voice with a hint of gruffness. He speaks at a relaxed pace with natural pauses."
}, },
{ {
"voice_name": "gentle_young_female", "voice_name": "Native Gary",
"description": "A young female speaker with a gentle, kind voice. Her tone is soft and sweet, with clear audio quality. She speaks at a slow and soothing pace." "description": "Gary's voice is a deep, resonant male voice with a hint of gravel. He speaks slowly and deliberately, giving weight to each word."
}, },
{ {
"voice_name": "excited_male", "voice_name": "Native James",
"description": "A male speaker with an excited, enthusiastic voice. His tone is lively and energetic, with a fast pace and a lot of inflection. The audio is clear and vibrant." "description": "James's voice is a refined male voice with a slight British accent. He speaks at a moderate pace with crisp articulation."
}, },
{ {
"voice_name": "professional_female", "voice_name": "Native Jason",
"description": "A female speaker with a professional and articulate voice. Her tone is clear and measured, with precise pronunciation and a moderate pace. The audio is high-quality." "description": "Jason's voice is a calm male voice with a neutral accent. He speaks at a moderate pace with even tones."
}, },
{ {
"voice_name": "relaxed_male", "voice_name": "Native Jenna",
"description": "A male speaker with a relaxed and laid-back voice. His tone is warm and soothing, with a slight drawl. The audio has a comfortable and intimate feel." "description": "Jenna's voice is a young female voice with a slight vocal fry. She speaks at a moderate pace with modern inflections."
}, },
{ {
"voice_name": "melancholic_female", "voice_name": "Native Jerry",
"description": "A female speaker with a melancholic and introspective voice. Her tone is soft and slightly sad, with a slow and deliberate pace. The audio has a gentle reverb effect." "description": "Jerry's voice is a jovial male voice with a hint of a chuckle. He speaks at a relaxed pace with natural inflections."
}, },
{ {
"voice_name": "cheerful_child", "voice_name": "Native Jon",
"description": "A child speaker with a cheerful and happy voice. The tone is high-pitched and playful, with a fast pace and clear audio quality. The speech is lively and energetic." "description": "Jon's voice is monotone yet slightly fast in delivery, with a very close recording that almost has no background noise."
}, },
{ {
"voice_name": "serious_male", "voice_name": "Native Jordan",
"description": "A male speaker with a serious and formal voice. His tone is deep and authoritative, with a slow and deliberate pace. The audio is clear and slightly reverberant." "description": "Jordan's voice is a youthful, gender-neutral voice with a slight rasp. They speak at a variable pace with natural inflections."
}, },
{ {
"voice_name": "enthusiastic_female", "voice_name": "Native Joy",
"description": "A female speaker with an enthusiastic and lively voice. Her tone is bright and energetic, with a fast pace and a lot of inflection. The audio is clear and vibrant." "description": "Joy's voice is a cheerful female voice with a musical quality. She speaks at a lively pace with expressive intonation."
}, },
{ {
"voice_name": "soft_spoken_male", "voice_name": "Native Karen",
"description": "A male speaker with a soft-spoken and gentle voice. His tone is warm and soothing, with a slow and measured pace. The audio has a slight ambient quality." "description": "Karen's voice is a mature female voice with a slight huskiness. She speaks at a measured pace with a calming tone."
}, },
{ {
"voice_name": "deep_female", "voice_name": "Native Laura",
"description": "A female speaker with a deep and resonant voice. Her tone is strong and authoritative, with a slow and deliberate pace. The audio is clear and slightly reverberant." "description": "Laura's voice is a warm and friendly female tone with a slight lilt. She speaks at a moderate pace with clear enunciation."
}, },
{ {
"voice_name": "lighthearted_male", "voice_name": "Native Lauren",
"description": "A male speaker with a lighthearted and cheerful voice. His tone is warm and friendly, with a moderate pace and natural inflection. The audio is clear and bright." "description": "Lauren's voice is a professional female voice with a confident tone. She speaks at a steady pace with clear diction."
} },
] {
"voice_name": "Native Lea",
"description": "Lea's voice is a bright and energetic female voice with a slight upward inflection at the end of sentences. She speaks at a brisk pace."
},
{
"voice_name": "Native Mike",
"description": "Mike's voice is a friendly male voice with a hint of enthusiasm. He speaks at a brisk pace with clear enunciation."
},
{
"voice_name": "Native Naomie",
"description": "Naomie's voice is a vibrant female voice with a slight exotic accent. She speaks at a variable pace with expressive tones."
},
{
"voice_name": "Native Patrick",
"description": "Patrick's voice is a warm male voice with a slight Irish lilt. He speaks at a moderate pace with a friendly tone."
},
{
"voice_name": "Native Rebecca",
"description": "Rebecca's voice is a clear female voice with a hint of enthusiasm. She speaks at a moderate pace with engaging inflections."
},
{
"voice_name": "Native Rick",
"description": "Rick's voice is a confident male voice with a slight regional accent. He speaks at a moderate pace with clear articulation."
},
{
"voice_name": "Native Rose",
"description": "Rose's voice is a gentle female voice with a slight breathiness. She speaks slowly and softly, with a soothing quality."
},
{
"voice_name": "Native Tina",
"description": "Tina's voice is a bubbly female voice with an upbeat quality. She speaks at a lively pace with enthusiastic tones."
},
{
"voice_name": "Native Tom",
"description": "Tom's voice is a neutral male voice with a clear tone. He speaks at a steady pace with precise articulation."
},
{
"voice_name": "Native Will",
"description": "Will's voice is an energetic male voice with a youthful tone. He speaks at a fast pace with enthusiastic inflections."
},
{
"voice_name": "Native Yann",
"description": "Yann's voice is a sophisticated male voice with a slight foreign accent. He speaks at a measured pace with precise pronunciation."
},
{
"voice_name": "soothing_female",
"description": "A female speaker with a soft, soothing voice. Her tone is gentle and calming, with a slight reverb as if she is in a small, cozy room."
},
{
"voice_name": "authoritative_male",
"description": "Nicks voice is a male speaker with a strong, authoritative voice. His tone is firm and commanding, with a slight bass boost that adds to the depth. He speaks at a moderate pace."
},
{
"voice_name": "friendly_young_male",
"description": "A young male speaker with a friendly, approachable voice. He has a slightly high-pitched tone and speaks with enthusiasm. The audio is clear and bright."
},
{
"voice_name": "whispering_female",
"description": "A female speaker who whispers softly, creating an intimate and personal feel. The audio has a slight breathy quality, adding to the whispering effect."
},
{
"voice_name": "robotic_male",
"description": "A male speaker with a robotic, synthetic voice. The tone is monotone and slightly mechanical, with clear and precise pronunciation."
},
{
"voice_name": "dramatic_female",
"description": "A female speaker with a dramatic and expressive voice. She speaks with varying pitch and intensity, in a high-quality audio environment. Her pace varies to add emphasis."
},
{
"voice_name": "casual_male",
"description": "A male speaker with a casual, conversational tone. He speaks in a relaxed manner, with natural pauses and inflections. The audio is clear and slightly ambient."
},
{
"voice_name": "bright_young_female",
"description": "A young female speaker with a bright, cheerful voice. She speaks with a lot of enthusiasm and a slightly fast pace, in a clear and vibrant audio environment."
},
{
"voice_name": "calm_older_male",
"description": "An older male speaker with a calm, soothing voice. His tone is deep and steady, with a slight reverb suggesting a large, quiet room. He speaks slowly and deliberately."
},
{
"voice_name": "energetic_child",
"description": "A child speaker with a high-pitched, energetic voice. The tone is playful and lively, with clear and crisp audio quality. The speech pace is fast."
},
{
"voice_name": "mysterious_female",
"description": "A female speaker with a low, mysterious voice. Her tone is hushed and slightly breathy, creating an air of secrecy. The audio has a slight echo effect."
},
{
"voice_name": "confident_male",
"description": "A male speaker with a confident and assertive voice. His tone is strong and clear, with a moderate pace and slight emphasis on certain words to convey assurance."
},
{
"voice_name": "gentle_young_female",
"description": "A young female speaker with a gentle, kind voice. Her tone is soft and sweet, with clear audio quality. She speaks at a slow and soothing pace."
},
{
"voice_name": "excited_male",
"description": "A male speaker with an excited, enthusiastic voice. His tone is lively and energetic, with a fast pace and a lot of inflection. The audio is clear and vibrant."
},
{
"voice_name": "professional_female",
"description": "A female speaker with a professional and articulate voice. Her tone is clear and measured, with precise pronunciation and a moderate pace. The audio is high-quality."
},
{
"voice_name": "relaxed_male",
"description": "A male speaker with a relaxed and laid-back voice. His tone is warm and soothing, with a slight drawl. The audio has a comfortable and intimate feel."
},
{
"voice_name": "melancholic_female",
"description": "A female speaker with a melancholic and introspective voice. Her tone is soft and slightly sad, with a slow and deliberate pace. The audio has a gentle reverb effect."
},
{
"voice_name": "cheerful_child",
"description": "A child speaker with a cheerful and happy voice. The tone is high-pitched and playful, with a fast pace and clear audio quality. The speech is lively and energetic."
},
{
"voice_name": "serious_male",
"description": "A male speaker with a serious and formal voice. His tone is deep and authoritative, with a slow and deliberate pace. The audio is clear and slightly reverberant."
},
{
"voice_name": "enthusiastic_female",
"description": "A female speaker with an enthusiastic and lively voice. Her tone is bright and energetic, with a fast pace and a lot of inflection. The audio is clear and vibrant."
},
{
"voice_name": "soft_spoken_male",
"description": "A male speaker with a soft-spoken and gentle voice. His tone is warm and soothing, with a slow and measured pace. The audio has a slight ambient quality."
},
{
"voice_name": "deep_female",
"description": "A female speaker with a deep and resonant voice. Her tone is strong and authoritative, with a slow and deliberate pace. The audio is clear and slightly reverberant."
},
{
"voice_name": "lighthearted_male",
"description": "A male speaker with a lighthearted and cheerful voice. His tone is warm and friendly, with a moderate pace and natural inflection. The audio is clear and bright."
}
]
} }