1
1
forked from mrq/tortoise-tts

owari da...

This commit is contained in:
mrq 2023-02-09 01:53:25 +00:00
parent 494f3c84a1
commit b23d6b4b4c
6 changed files with 109 additions and 69 deletions

View File

@ -71,11 +71,17 @@ After installing Python, open the Start Menu and search for `Command Prompt`. Ty
Paste `git clone https://git.ecker.tech/mrq/tortoise-tts` to download TorToiSe and additional scripts, then hit Enter. Inexperienced users can just download the repo as a ZIP, and extract.
Afterwards, run the setup script, depending on your GPU, to automatically set things up.
* AMD: `setup-directml.bat` (**!**NOTE**!**: DirectML support is currently being worked on)
* ~~AMD: `setup-directml.bat`~~
* NVIDIA: `setup-cuda.bat`
If you've done everything right, you shouldn't have any errors.
##### Note on DirectML Support
At first, I thought it was just one simple problem that needed to be fixed, but as I picked at it and did a new install (having CUDA enabled too caused some things to silently "work" despite using DML instead), more problems cropped up, exposing that PyTorch-DirectML isn't quite ready yet.
I doubt even if I sucked off a wizard, there'd still be other problems cropping up.
#### Linux
First, make sure you have both `python3.x` and `git` installed, as well as the required compute platform according to your GPU (ROCm or CUDA)

2
app.py
View File

@ -33,7 +33,7 @@ def generate(text, delimiter, emotion, prompt, voice, mic_audio, seed, candidate
else:
progress(0, desc="Loading voice...")
voice_samples, conditioning_latents = load_voice(voice)
if voice_samples is not None:
sample_voice = voice_samples[0]
conditioning_latents = tts.get_conditioning_latents(voice_samples, return_mels=not args.latents_lean_and_mean, progress=progress, max_chunk_size=args.cond_latent_max_chunk_size)

View File

@ -30,6 +30,8 @@ from tortoise.utils.diffusion import SpacedDiffusion, space_timesteps, get_named
from tortoise.utils.tokenizer import VoiceBpeTokenizer
from tortoise.utils.wav2vec_alignment import Wav2VecAlignment
from tortoise.utils.device import get_device, get_device_name, get_device_batch_size
pbar = None
MODELS_DIR = os.environ.get('TORTOISE_MODELS_DIR')
@ -191,57 +193,6 @@ def classify_audio_clip(clip):
results = F.softmax(classifier(clip), dim=-1)
return results[0][0]
def pick_best_batch_size_for_gpu():
"""
Tries to pick a batch size that will fit in your GPU. These sizes aren't guaranteed to work, but they should give
you a good shot.
"""
if torch.cuda.is_available():
_, available = torch.cuda.mem_get_info()
availableGb = available / (1024 ** 3)
if availableGb > 14:
return 16
elif availableGb > 10:
return 8
elif availableGb > 7:
return 4
return 1
def has_dml():
return False
# currently getting an error thrown during the autoregressive pass
# File "X:\programs\tortoise-tts\tortoise-venv\lib\site-packages\transformers\generation_utils.py", line 1905, in sample
# unfinished_sequences = input_ids.new(input_ids.shape[0]).fill_(1)
# RuntimeError: new(): expected key in DispatchKeySet(CPU, CUDA, HIP, XLA, MPS, IPU, XPU, HPU, Lazy, Meta) but got: PrivateUse1
# so I'll need to look into it more
"""
import importlib
loader = importlib.find_loader('torch_directml')
return loader is not None
"""
def get_optimal_device():
name = 'cpu'
if has_dml():
name = 'dml'
elif torch.cuda.is_available():
name = 'cuda'
if name == 'cpu':
print("No hardware acceleration is available, falling back to CPU...")
else:
print(f"Hardware acceleration found: {name}")
if name == "dml":
import torch_directml
return torch_directml.device()
return torch.device(name)
class TextToSpeech:
"""
Main entry point into Tortoise.
@ -260,18 +211,18 @@ class TextToSpeech:
:param device: Device to use when running the model. If omitted, the device will be automatically chosen.
"""
if device is None:
device = get_optimal_device()
device = get_device(verbose=True)
self.input_sample_rate = input_sample_rate
self.output_sample_rate = output_sample_rate
self.minor_optimizations = minor_optimizations
self.models_dir = models_dir
self.autoregressive_batch_size = pick_best_batch_size_for_gpu() if autoregressive_batch_size is None or autoregressive_batch_size == 0 else autoregressive_batch_size
self.autoregressive_batch_size = get_device_batch_size() if autoregressive_batch_size is None or autoregressive_batch_size == 0 else autoregressive_batch_size
self.enable_redaction = enable_redaction
self.device = device
if self.enable_redaction:
self.aligner = Wav2VecAlignment(device=self.device)
self.aligner = Wav2VecAlignment(device=None)
self.tokenizer = VoiceBpeTokenizer()
@ -331,13 +282,15 @@ class TextToSpeech:
:param voice_samples: List of 2 or more ~10 second reference clips, which should be torch tensors containing 22.05kHz waveform data.
"""
with torch.no_grad():
voice_samples = [v.to(self.device) for v in voice_samples]
device = 'cpu' if get_device_name() == "dml" else self.device
voice_samples = [v.to(device) for v in voice_samples]
auto_conds = []
if not isinstance(voice_samples, list):
voice_samples = [voice_samples]
for vs in voice_samples:
auto_conds.append(format_conditioning(vs, device=self.device, sampling_rate=self.input_sample_rate))
auto_conds.append(format_conditioning(vs, device=device, sampling_rate=self.input_sample_rate))
auto_conds = torch.stack(auto_conds, dim=1)
@ -372,20 +325,30 @@ class TextToSpeech:
for chunk in tqdm_override(chunks, verbose=verbose, progress=progress, desc="Computing conditioning latents..."):
chunk = pad_or_truncate(chunk, chunk_size)
cond_mel = wav_to_univnet_mel(chunk.to(self.device), do_normalization=False, device=self.device)
cond_mel = wav_to_univnet_mel(chunk.to(device), do_normalization=False, device=device)
diffusion_conds.append(cond_mel)
diffusion_conds = torch.stack(diffusion_conds, dim=1)
# required since DML implementation screams about falling back to CPU, but crashes anyways
if self.minor_optimizations:
auto_latent = self.autoregressive.get_conditioning(auto_conds)
diffusion_latent = self.diffusion.get_conditioning(diffusion_conds)
if get_device_name() == "dml":
self.autoregressive = self.autoregressive.cpu()
auto_latent = self.autoregressive.get_conditioning(auto_conds)
self.autoregressive = self.autoregressive.to(self.device)
self.diffusion = self.diffusion.cpu()
diffusion_latent = self.diffusion.get_conditioning(diffusion_conds)
self.diffusion = self.diffusion.to(self.device)
else:
auto_latent = self.autoregressive.get_conditioning(auto_conds)
diffusion_latent = self.diffusion.get_conditioning(diffusion_conds)
else:
self.autoregressive = self.autoregressive.to(self.device)
self.autoregressive = self.autoregressive.to(device)
auto_latent = self.autoregressive.get_conditioning(auto_conds)
self.autoregressive = self.autoregressive.cpu()
self.diffusion = self.diffusion.to(self.device)
self.diffusion = self.diffusion.to(device)
diffusion_latent = self.diffusion.get_conditioning(diffusion_conds)
self.diffusion = self.diffusion.cpu()
@ -509,7 +472,7 @@ class TextToSpeech:
diffuser = load_discrete_vocoder_diffuser(desired_diffusion_steps=diffusion_iterations, cond_free=cond_free, cond_free_k=cond_free_k)
self.autoregressive_batch_size = pick_best_batch_size_for_gpu() if sample_batch_size is None or sample_batch_size == 0 else sample_batch_size
self.autoregressive_batch_size = get_device_batch_size() if sample_batch_size is None or sample_batch_size == 0 else sample_batch_size
with torch.no_grad():
samples = []

View File

@ -97,7 +97,7 @@ def get_voices(extra_voice_dirs=[]):
return voices
def load_voice(voice, extra_voice_dirs=[], load_latents=True, sample_rate=22050):
def load_voice(voice, extra_voice_dirs=[], load_latents=True, sample_rate=22050, device='cpu'):
if voice == 'random':
return None, None
@ -120,7 +120,7 @@ def load_voice(voice, extra_voice_dirs=[], load_latents=True, sample_rate=22050)
if load_latents and latent is not None:
if os.path.getmtime(latent) > mtime:
print(f"Reading from latent: {latent}")
return None, torch.load(latent)
return None, torch.load(latent, map_location=device)
print(f"Latent file out of date: {latent}")
conds = []
@ -197,7 +197,7 @@ class TacotronSTFT(torch.nn.Module):
return mel_output
def wav_to_univnet_mel(wav, do_normalization=False, device='cuda', sample_rate=24000):
def wav_to_univnet_mel(wav, do_normalization=False, device='cpu', sample_rate=24000):
stft = TacotronSTFT(1024, 256, 1024, 100, sample_rate, 0, 12000)
stft = stft.to(device)
mel = stft.mel_spectrogram(wav)

71
tortoise/utils/device.py Executable file
View File

@ -0,0 +1,71 @@
import torch
def has_dml():
"""
# huggingface's transformer/GPT2 model will just lead to a long track of problems
# I will suck off a wizard if he gets this remedied somehow
"""
"""
# Note 1:
# self.inference_model.generate will lead to this error in torch.LongTensor.new:
# RuntimeError: new(): expected key in DispatchKeySet(CPU, CUDA, HIP, XLA, MPS, IPU, XPU, HPU, Lazy, Meta) but got: PrivateUse1
# Patching "./venv/lib/site-packages/transformers/generation_utils.py:1906" with:
# unfinished_sequences = input_ids.new_tensor(input_ids.shape[0], device=input_ids.device).fill_(1)
# "fixes" it, but meets another error/crash about an unimplemented functions.........
"""
"""
# Note 2:
# torch.load() will gripe about something CUDA not existing
# remedy this with passing map_location="cpu"
"""
"""
# Note 3:
# stft requires device='cpu' or it'll crash about some error about an unimplemented function I do not remember
"""
"""
# Note 4:
# 'Tensor.multinominal' and 'Tensor.repeat_interleave' throws errors about being unimplemented and falls back to CPU and crashes
"""
return False
"""
import importlib
loader = importlib.find_loader('torch_directml')
return loader is not None
"""
def get_device_name():
name = 'cpu'
if has_dml():
name = 'dml'
elif torch.cuda.is_available():
name = 'cuda'
return name
def get_device(verbose=False):
name = get_device_name()
if verbose:
if name == 'cpu':
print("No hardware acceleration is available, falling back to CPU...")
else:
print(f"Hardware acceleration found: {name}")
if name == "dml":
import torch_directml
return torch_directml.device()
return torch.device(name)
def get_device_batch_size():
if torch.cuda.is_available():
_, available = torch.cuda.mem_get_info()
availableGb = available / (1024 ** 3)
if availableGb > 14:
return 16
elif availableGb > 10:
return 8
elif availableGb > 7:
return 4
return 1

4
tortoise/utils/wav2vec_alignment.py Normal file → Executable file
View File

@ -5,7 +5,7 @@ import torchaudio
from transformers import Wav2Vec2ForCTC, Wav2Vec2FeatureExtractor, Wav2Vec2CTCTokenizer, Wav2Vec2Processor
from tortoise.utils.audio import load_audio
from tortoise.utils.device import get_device
def max_alignment(s1, s2, skip_character='~', record=None):
"""
@ -51,7 +51,7 @@ class Wav2VecAlignment:
"""
def __init__(self, device=None):
if device is None:
device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
device = torch.device(get_device())
self.model = Wav2Vec2ForCTC.from_pretrained("jbetker/wav2vec2-large-robust-ft-libritts-voxpopuli").cpu()
self.feature_extractor = Wav2Vec2FeatureExtractor.from_pretrained(f"facebook/wav2vec2-large-960h")