-

+
}})
+
+ {%if show_details%}
+
+ {%endif%}
diff --git a/TTS/tts/layers/glow_tts/glow.py b/TTS/tts/layers/glow_tts/glow.py
index f6385747..c8ad410d 100644
--- a/TTS/tts/layers/glow_tts/glow.py
+++ b/TTS/tts/layers/glow_tts/glow.py
@@ -128,8 +128,9 @@ class InvConvNear(nn.Module):
return z, logdet
def store_inverse(self):
- self.weight_inv = torch.inverse(
+ weight_inv = torch.inverse(
self.weight.float()).to(dtype=self.weight.dtype)
+ self.weight_inv = nn.Parameter(weight_inv, requires_grad=False)
class CouplingBlock(nn.Module):
diff --git a/TTS/tts/layers/glow_tts/monotonic_align/__init__.py b/TTS/tts/layers/glow_tts/monotonic_align/__init__.py
index 267fb7f4..a2912a98 100644
--- a/TTS/tts/layers/glow_tts/monotonic_align/__init__.py
+++ b/TTS/tts/layers/glow_tts/monotonic_align/__init__.py
@@ -2,7 +2,13 @@ import numpy as np
import torch
from torch.nn import functional as F
from TTS.tts.utils.generic_utils import sequence_mask
-from TTS.tts.layers.glow_tts.monotonic_align.core import maximum_path_c
+
+try:
+ # TODO: fix pypi cython installation problem.
+ from TTS.tts.layers.glow_tts.monotonic_align.core import maximum_path_c
+ CYTHON = True
+except ModuleNotFoundError:
+ CYTHON = False
def convert_pad_shape(pad_shape):
@@ -32,6 +38,12 @@ def generate_path(duration, mask):
def maximum_path(value, mask):
+ if CYTHON:
+ return maximum_path_cython(value, mask)
+ return maximum_path_numpy(value, mask)
+
+
+def maximum_path_cython(value, mask):
""" Cython optimised version.
value: [b, t_x, t_y]
mask: [b, t_x, t_y]
@@ -47,3 +59,45 @@ def maximum_path(value, mask):
t_y_max = mask.sum(2)[:, 0].astype(np.int32)
maximum_path_c(path, value, t_x_max, t_y_max)
return torch.from_numpy(path).to(device=device, dtype=dtype)
+
+
+def maximum_path_numpy(value, mask, max_neg_val=None):
+ """
+ Monotonic alignment search algorithm
+ Numpy-friendly version. It's about 4 times faster than torch version.
+ value: [b, t_x, t_y]
+ mask: [b, t_x, t_y]
+ """
+ if max_neg_val is None:
+ max_neg_val = -np.inf # Patch for Sphinx complaint
+ value = value * mask
+
+ device = value.device
+ dtype = value.dtype
+ value = value.cpu().detach().numpy()
+ mask = mask.cpu().detach().numpy().astype(np.bool)
+
+ b, t_x, t_y = value.shape
+ direction = np.zeros(value.shape, dtype=np.int64)
+ v = np.zeros((b, t_x), dtype=np.float32)
+ x_range = np.arange(t_x, dtype=np.float32).reshape(1, -1)
+ for j in range(t_y):
+ v0 = np.pad(v, [[0, 0], [1, 0]], mode="constant", constant_values=max_neg_val)[:, :-1]
+ v1 = v
+ max_mask = v1 >= v0
+ v_max = np.where(max_mask, v1, v0)
+ direction[:, :, j] = max_mask
+
+ index_mask = x_range <= j
+ v = np.where(index_mask, v_max + value[:, :, j], max_neg_val)
+ direction = np.where(mask, direction, 1)
+
+ path = np.zeros(value.shape, dtype=np.float32)
+ index = mask[:, :, 0].sum(1).astype(np.int64) - 1
+ index_range = np.arange(b)
+ for j in reversed(range(t_y)):
+ path[index_range, index, j] = 1
+ index = index + direction[index_range, index, j] - 1
+ path = path * mask.astype(np.float32)
+ path = torch.from_numpy(path).to(device=device, dtype=dtype)
+ return path
diff --git a/TTS/tts/layers/glow_tts/monotonic_align/setup.py b/TTS/tts/layers/glow_tts/monotonic_align/setup.py
index 1d669ea0..f22bc6a3 100644
--- a/TTS/tts/layers/glow_tts/monotonic_align/setup.py
+++ b/TTS/tts/layers/glow_tts/monotonic_align/setup.py
@@ -1,7 +1,7 @@
-from distutils.core import setup
-from Cython.Build import cythonize
-import numpy
+# from distutils.core import setup
+# from Cython.Build import cythonize
+# import numpy
-setup(name='monotonic_align',
- ext_modules=cythonize("core.pyx"),
- include_dirs=[numpy.get_include()])
+# setup(name='monotonic_align',
+# ext_modules=cythonize("core.pyx"),
+# include_dirs=[numpy.get_include()])
diff --git a/TTS/tts/models/glow_tts.py b/TTS/tts/models/glow_tts.py
index b55ba1b1..2f9b6f9b 100644
--- a/TTS/tts/models/glow_tts.py
+++ b/TTS/tts/models/glow_tts.py
@@ -223,3 +223,11 @@ class GlowTts(nn.Module):
def store_inverse(self):
self.decoder.store_inverse()
+
+ def load_checkpoint(self, config, checkpoint_path, eval=False): # pylint: disable=unused-argument, redefined-builtin
+ state = torch.load(checkpoint_path, map_location=torch.device('cpu'))
+ self.load_state_dict(state['model'])
+ if eval:
+ self.eval()
+ self.store_inverse()
+ assert not self.training
diff --git a/TTS/tts/models/speedy_speech.py b/TTS/tts/models/speedy_speech.py
index 2e7d0a5f..93496d59 100644
--- a/TTS/tts/models/speedy_speech.py
+++ b/TTS/tts/models/speedy_speech.py
@@ -188,5 +188,12 @@ class SpeedySpeech(nn.Module):
o_dr_log = self.duration_predictor(o_en_dp.detach(), x_mask)
o_dr = self.format_durations(o_dr_log, x_mask).squeeze(1)
y_lengths = o_dr.sum(1)
- o_de, attn= self._forward_decoder(o_en, o_en_dp, o_dr, x_mask, y_lengths, g=g)
+ o_de, attn = self._forward_decoder(o_en, o_en_dp, o_dr, x_mask, y_lengths, g=g)
return o_de, attn
+
+ def load_checkpoint(self, config, checkpoint_path, eval=False): # pylint: disable=unused-argument, redefined-builtin
+ state = torch.load(checkpoint_path, map_location=torch.device('cpu'))
+ self.load_state_dict(state['model'])
+ if eval:
+ self.eval()
+ assert not self.training
diff --git a/TTS/tts/models/tacotron_abstract.py b/TTS/tts/models/tacotron_abstract.py
index 54c46be2..10953269 100644
--- a/TTS/tts/models/tacotron_abstract.py
+++ b/TTS/tts/models/tacotron_abstract.py
@@ -121,6 +121,14 @@ class TacotronAbstract(ABC, nn.Module):
def inference(self):
pass
+ def load_checkpoint(self, config, checkpoint_path, eval=False): # pylint: disable=unused-argument, redefined-builtin
+ state = torch.load(checkpoint_path, map_location=torch.device('cpu'))
+ self.load_state_dict(state['model'])
+ self.decoder.set_r(state['r'])
+ if eval:
+ self.eval()
+ assert not self.training
+
#############################
# COMMON COMPUTE FUNCTIONS
#############################
diff --git a/TTS/tts/utils/io.py b/TTS/tts/utils/io.py
index 830529a3..63e04283 100644
--- a/TTS/tts/utils/io.py
+++ b/TTS/tts/utils/io.py
@@ -7,7 +7,7 @@ from TTS.utils.io import RenamingUnpickler
-def load_checkpoint(model, checkpoint_path, amp=None, use_cuda=False):
+def load_checkpoint(model, checkpoint_path, amp=None, use_cuda=False, eval=False):
"""Load ```TTS.tts.models``` checkpoints.
Args:
@@ -33,6 +33,8 @@ def load_checkpoint(model, checkpoint_path, amp=None, use_cuda=False):
if hasattr(model.decoder, 'r'):
model.decoder.set_r(state['r'])
print(" > Model r: ", state['r'])
+ if eval:
+ model.eval()
return model, state
diff --git a/TTS/tts/utils/visual.py b/TTS/tts/utils/visual.py
index 17cba648..e5bb5891 100644
--- a/TTS/tts/utils/visual.py
+++ b/TTS/tts/utils/visual.py
@@ -50,7 +50,7 @@ def plot_spectrogram(spectrogram,
spectrogram_ = spectrogram_.astype(
np.float32) if spectrogram_.dtype == np.float16 else spectrogram_
if ap is not None:
- spectrogram_ = ap._denormalize(spectrogram_) # pylint: disable=protected-access
+ spectrogram_ = ap.denormalize(spectrogram_) # pylint: disable=protected-access
fig = plt.figure(figsize=fig_size)
plt.imshow(spectrogram_, aspect="auto", origin="lower")
plt.colorbar()
diff --git a/TTS/utils/audio.py b/TTS/utils/audio.py
index 9d25aeb7..93a5880f 100644
--- a/TTS/utils/audio.py
+++ b/TTS/utils/audio.py
@@ -35,9 +35,9 @@ class AudioProcessor(object):
trim_db=60,
do_sound_norm=False,
stats_path=None,
+ verbose=True,
**_):
- print(" > Setting up Audio Processor...")
# setup class attributed
self.sample_rate = sample_rate
self.resample = resample
@@ -73,8 +73,10 @@ class AudioProcessor(object):
assert min_level_db != 0.0, " [!] min_level_db is 0"
assert self.win_length <= self.fft_size, " [!] win_length cannot be larger than fft_size"
members = vars(self)
- for key, value in members.items():
- print(" | > {}:{}".format(key, value))
+ if verbose:
+ print(" > Setting up Audio Processor...")
+ for key, value in members.items():
+ print(" | > {}:{}".format(key, value))
# create spectrogram utils
self.mel_basis = self._build_mel_basis()
self.inv_mel_basis = np.linalg.pinv(self._build_mel_basis())
@@ -107,7 +109,7 @@ class AudioProcessor(object):
return hop_length, win_length
### normalization ###
- def _normalize(self, S):
+ def normalize(self, S):
"""Put values in [0, self.max_norm] or [-self.max_norm, self.max_norm]"""
#pylint: disable=no-else-return
S = S.copy()
@@ -136,7 +138,7 @@ class AudioProcessor(object):
else:
return S
- def _denormalize(self, S):
+ def denormalize(self, S):
"""denormalize values"""
#pylint: disable=no-else-return
S_denorm = S.copy()
@@ -221,7 +223,7 @@ class AudioProcessor(object):
else:
D = self._stft(y)
S = self._amp_to_db(np.abs(D))
- return self._normalize(S)
+ return self.normalize(S)
def melspectrogram(self, y):
if self.preemphasis != 0:
@@ -229,11 +231,11 @@ class AudioProcessor(object):
else:
D = self._stft(y)
S = self._amp_to_db(self._linear_to_mel(np.abs(D)))
- return self._normalize(S)
+ return self.normalize(S)
def inv_spectrogram(self, spectrogram):
"""Converts spectrogram to waveform using librosa"""
- S = self._denormalize(spectrogram)
+ S = self.denormalize(spectrogram)
S = self._db_to_amp(S)
# Reconstruct phase
if self.preemphasis != 0:
@@ -242,7 +244,7 @@ class AudioProcessor(object):
def inv_melspectrogram(self, mel_spectrogram):
'''Converts melspectrogram to waveform using librosa'''
- D = self._denormalize(mel_spectrogram)
+ D = self.denormalize(mel_spectrogram)
S = self._db_to_amp(D)
S = self._mel_to_linear(S) # Convert back to linear
if self.preemphasis != 0:
@@ -250,11 +252,11 @@ class AudioProcessor(object):
return self._griffin_lim(S**self.power)
def out_linear_to_mel(self, linear_spec):
- S = self._denormalize(linear_spec)
+ S = self.denormalize(linear_spec)
S = self._db_to_amp(S)
S = self._linear_to_mel(np.abs(S))
S = self._amp_to_db(S)
- mel = self._normalize(S)
+ mel = self.normalize(S)
return mel
### STFT and ISTFT ###
diff --git a/TTS/utils/generic_utils.py b/TTS/utils/generic_utils.py
index 7d7911b0..5890f04d 100644
--- a/TTS/utils/generic_utils.py
+++ b/TTS/utils/generic_utils.py
@@ -3,6 +3,8 @@ import glob
import os
import shutil
import subprocess
+import sys
+from pathlib import Path
import torch
@@ -67,6 +69,22 @@ def count_parameters(model):
return sum(p.numel() for p in model.parameters() if p.requires_grad)
+def get_user_data_dir(appname):
+ if sys.platform == "win32":
+ import winreg # pylint: disable=import-outside-toplevel
+ key = winreg.OpenKey(
+ winreg.HKEY_CURRENT_USER,
+ r"Software\Microsoft\Windows\CurrentVersion\Explorer\Shell Folders"
+ )
+ dir_, _ = winreg.QueryValueEx(key, "Local AppData")
+ ans = Path(dir_).resolve(strict=False)
+ elif sys.platform == 'darwin':
+ ans = Path('~/Library/Application Support/').expanduser()
+ else:
+ ans = Path.home().joinpath('.local/share')
+ return ans.joinpath(appname)
+
+
def set_init_dict(model_dict, checkpoint_state, c):
# Partial initialization: if there is a mismatch with new and old layer, it is skipped.
for k, v in checkpoint_state.items():
@@ -97,6 +115,7 @@ def set_init_dict(model_dict, checkpoint_state, c):
len(model_dict)))
return model_dict
+
class KeepAverage():
def __init__(self):
self.avg_values = {}
diff --git a/TTS/utils/io.py b/TTS/utils/io.py
index 2c5c8e49..46abf1c8 100644
--- a/TTS/utils/io.py
+++ b/TTS/utils/io.py
@@ -20,6 +20,16 @@ class AttrDict(dict):
self.__dict__ = self
+def read_json_with_comments(json_path):
+ # fallback to json
+ with open(json_path, "r") as f:
+ input_str = f.read()
+ # handle comments
+ input_str = re.sub(r'\\\n', '', input_str)
+ input_str = re.sub(r'//.*\n', '\n', input_str)
+ data = json.loads(input_str)
+ return data
+
def load_config(config_path: str) -> AttrDict:
"""Load config files and discard comments
@@ -33,14 +43,7 @@ def load_config(config_path: str) -> AttrDict:
with open(config_path, "r") as f:
data = yaml.safe_load(f)
else:
- # fallback to json
- with open(config_path, "r") as f:
- input_str = f.read()
- # handle comments
- input_str = re.sub(r'\\\n', '', input_str)
- input_str = re.sub(r'//.*\n', '\n', input_str)
- data = json.loads(input_str)
-
+ data = read_json_with_comments(config_path)
config.update(data)
return config
diff --git a/TTS/utils/manage.py b/TTS/utils/manage.py
new file mode 100644
index 00000000..25b3d797
--- /dev/null
+++ b/TTS/utils/manage.py
@@ -0,0 +1,103 @@
+import json
+import gdown
+from pathlib import Path
+import os
+
+from TTS.utils.io import load_config
+from TTS.utils.generic_utils import get_user_data_dir
+
+class ModelManager(object):
+ """Manage TTS models defined in .models.json.
+ It provides an interface to list and download
+ models defines in '.model.json'
+
+ Models are downloaded under '.TTS' folder in the user's
+ home path.
+
+ Args:
+ models_file (str): path to .model.json
+ """
+ def __init__(self, models_file):
+ super().__init__()
+ self.output_prefix = get_user_data_dir('tts')
+ self.url_prefix = "https://drive.google.com/uc?id="
+ self.models_dict = None
+ self.read_models_file(models_file)
+
+ def read_models_file(self, file_path):
+ """Read .models.json as a dict
+
+ Args:
+ file_path (str): path to .models.json.
+ """
+ with open(file_path) as json_file:
+ self.models_dict = json.load(json_file)
+
+ def list_langs(self):
+ print(" Name format: type/language")
+ for model_type in self.models_dict:
+ for lang in self.models_dict[model_type]:
+ print(f" >: {model_type}/{lang} ")
+
+ def list_datasets(self):
+ print(" Name format: type/language/dataset")
+ for model_type in self.models_dict:
+ for lang in self.models_dict[model_type]:
+ for dataset in self.models_dict[model_type][lang]:
+ print(f" >: {model_type}/{lang}/{dataset}")
+
+ def list_models(self):
+ print(" Name format: type/language/dataset/model")
+ for model_type in self.models_dict:
+ for lang in self.models_dict[model_type]:
+ for dataset in self.models_dict[model_type][lang]:
+ for model in self.models_dict[model_type][lang][dataset]:
+ print(f" >: {model_type}/{lang}/{dataset}/{model} ")
+
+ def download_model(self, model_name):
+ """Download model files given the full model name.
+ Model name is in the format
+ 'type/language/dataset/model'
+ e.g. 'tts_model/en/ljspeech/tacotron'
+
+ Args:
+ model_name (str): model name as explained above.
+
+ TODO: support multi-speaker models
+ """
+ # fetch model info from the dict
+ model_type, lang, dataset, model = model_name.split("/")
+ model_full_name = f"{model_type}--{lang}--{dataset}--{model}"
+ model_item = self.models_dict[model_type][lang][dataset][model]
+ # set the model specific output path
+ output_path = os.path.join(self.output_prefix, model_full_name)
+ output_model_path = os.path.join(output_path, "model_file.pth.tar")
+ output_config_path = os.path.join(output_path, "config.json")
+ if os.path.exists(output_path):
+ print(f" > {model_name} is already downloaded.")
+ else:
+ os.makedirs(output_path, exist_ok=True)
+ print(f" > Downloading model to {output_path}")
+ output_stats_path = None
+ # download files to the output path
+ self._download_file(model_item['model_file'], output_model_path)
+ self._download_file(model_item['config_file'], output_config_path)
+ if model_item['stats_file'] is not None and len(model_item['stats_file']) > 1:
+ output_stats_path = os.path.join(output_path, 'scale_stats.npy')
+ self._download_file(model_item['stats_file'], output_stats_path)
+ # set scale stats path in config.json
+ config_path = output_config_path
+ config = load_config(config_path)
+ config["audio"]['stats_path'] = output_stats_path
+ with open(config_path, "w") as jf:
+ json.dump(config, jf)
+ return output_model_path, output_config_path
+
+ def _download_file(self, idx, output):
+ gdown.download(f"{self.url_prefix}{idx}", output=output)
+
+
+
+
+
+
diff --git a/TTS/utils/synthesizer.py b/TTS/utils/synthesizer.py
new file mode 100644
index 00000000..615e0d1d
--- /dev/null
+++ b/TTS/utils/synthesizer.py
@@ -0,0 +1,169 @@
+import time
+
+import numpy as np
+import torch
+import pysbd
+
+from TTS.utils.audio import AudioProcessor
+from TTS.utils.io import load_config
+from TTS.tts.utils.generic_utils import setup_model
+from TTS.tts.utils.speakers import load_speaker_mapping
+from TTS.vocoder.utils.generic_utils import setup_generator, interpolate_vocoder_input
+# pylint: disable=unused-wildcard-import
+# pylint: disable=wildcard-import
+from TTS.tts.utils.synthesis import *
+
+from TTS.tts.utils.text import make_symbols, phonemes, symbols
+
+
+class Synthesizer(object):
+ def __init__(self, tts_checkpoint, tts_config, vocoder_checkpoint=None, vocoder_config=None, use_cuda=False):
+ """Encapsulation of tts and vocoder models for inference.
+
+ TODO: handle multi-speaker and GST inference.
+
+ Args:
+ tts_checkpoint (str): path to the tts model file.
+ tts_config (str): path to the tts config file.
+ vocoder_checkpoint (str, optional): path to the vocoder model file. Defaults to None.
+ vocoder_config (str, optional): path to the vocoder config file. Defaults to None.
+ use_cuda (bool, optional): enable/disable cuda. Defaults to False.
+ """
+ self.tts_checkpoint = tts_checkpoint
+ self.tts_config = tts_config
+ self.vocoder_checkpoint = vocoder_checkpoint
+ self.vocoder_config = vocoder_config
+ self.use_cuda = use_cuda
+ self.wavernn = None
+ self.vocoder_model = None
+ self.num_speakers = 0
+ self.tts_speakers = None
+ self.speaker_embedding_dim = None
+ self.seg = self.get_segmenter("en")
+ self.use_cuda = use_cuda
+ if self.use_cuda:
+ assert torch.cuda.is_available(), "CUDA is not availabe on this machine."
+ self.load_tts(tts_checkpoint, tts_config,
+ use_cuda)
+ if vocoder_checkpoint:
+ self.load_vocoder(vocoder_checkpoint, vocoder_config, use_cuda)
+
+ @staticmethod
+ def get_segmenter(lang):
+ return pysbd.Segmenter(language=lang, clean=True)
+
+ def load_speakers(self):
+ # load speakers
+ if self.model_config.use_speaker_embedding is not None:
+ self.tts_speakers = load_speaker_mapping(self.tts_config.tts_speakers_json)
+ self.num_speakers = len(self.tts_speakers)
+ else:
+ self.num_speakers = 0
+ # set external speaker embedding
+ if self.tts_config.use_external_speaker_embedding_file:
+ speaker_embedding = self.tts_speakers[list(self.tts_speakers.keys())[0]]['embedding']
+ self.speaker_embedding_dim = len(speaker_embedding)
+
+ def init_speaker(self, speaker_idx):
+ # load speakers
+ speaker_embedding = None
+ if hasattr(self, 'tts_speakers') and speaker_idx is not None:
+ assert speaker_idx < len(self.tts_speakers), f" [!] speaker_idx is out of the range. {speaker_idx} vs {len(self.tts_speakers)}"
+ if self.tts_config.use_external_speaker_embedding_file:
+ speaker_embedding = self.tts_speakers[speaker_idx]['embedding']
+ return speaker_embedding
+
+ def load_tts(self, tts_checkpoint, tts_config, use_cuda):
+ # pylint: disable=global-statement
+ global symbols, phonemes
+
+ self.tts_config = load_config(tts_config)
+ self.use_phonemes = self.tts_config.use_phonemes
+ self.ap = AudioProcessor(**self.tts_config.audio)
+
+ if 'characters' in self.tts_config.keys():
+ symbols, phonemes = make_symbols(**self.tts_config.characters)
+
+ if self.use_phonemes:
+ self.input_size = len(phonemes)
+ else:
+ self.input_size = len(symbols)
+
+ self.tts_model = setup_model(self.input_size, num_speakers=self.num_speakers, c=self.tts_config)
+ self.tts_model.load_checkpoint(tts_config, tts_checkpoint, eval=True)
+ if use_cuda:
+ self.tts_model.cuda()
+
+ def load_vocoder(self, model_file, model_config, use_cuda):
+ self.vocoder_config = load_config(model_config)
+ self.vocoder_ap = AudioProcessor(**self.vocoder_config['audio'])
+ self.vocoder_model = setup_generator(self.vocoder_config)
+ self.vocoder_model.load_checkpoint(self.vocoder_config, model_file, eval=True)
+ if use_cuda:
+ self.vocoder_model.cuda()
+
+ def save_wav(self, wav, path):
+ wav = np.array(wav)
+ self.ap.save_wav(wav, path)
+
+ def split_into_sentences(self, text):
+ return self.seg.segment(text)
+
+ def tts(self, text, speaker_idx=None):
+ start_time = time.time()
+ wavs = []
+ sens = self.split_into_sentences(text)
+ print(" > Text splitted to sentences.")
+ print(sens)
+
+ speaker_embedding = self.init_speaker(speaker_idx)
+ use_gl = self.vocoder_model is None
+
+ for sen in sens:
+ # synthesize voice
+ waveform, _, _, mel_postnet_spec, _, _ = synthesis(
+ self.tts_model,
+ sen,
+ self.tts_config,
+ self.use_cuda,
+ self.ap,
+ speaker_idx,
+ None,
+ False,
+ self.tts_config.enable_eos_bos_chars,
+ use_gl,
+ speaker_embedding=speaker_embedding)
+ if not use_gl:
+ # denormalize tts output based on tts audio config
+ mel_postnet_spec = self.ap.denormalize(mel_postnet_spec.T).T
+ device_type = "cuda" if self.use_cuda else "cpu"
+ # renormalize spectrogram based on vocoder config
+ vocoder_input = self.vocoder_ap.normalize(mel_postnet_spec.T)
+ # compute scale factor for possible sample rate mismatch
+ scale_factor = [1, self.vocoder_config['audio']['sample_rate'] / self.ap.sample_rate]
+ if scale_factor[1] != 1:
+ print(" > interpolating tts model output.")
+ vocoder_input = interpolate_vocoder_input(scale_factor, vocoder_input)
+ else:
+ vocoder_input = torch.tensor(vocoder_input).unsqueeze(0) # pylint: disable=not-callable
+ # run vocoder model
+ # [1, T, C]
+ waveform = self.vocoder_model.inference(vocoder_input.to(device_type))
+ if self.use_cuda and not use_gl:
+ waveform = waveform.cpu()
+ if not use_gl:
+ waveform = waveform.numpy()
+ waveform = waveform.squeeze()
+
+ # trim silence
+ waveform = trim_silence(waveform, self.ap)
+
+ wavs += list(waveform)
+ wavs += [0] * 10000
+
+ # compute stats
+ process_time = time.time() - start_time
+ audio_time = len(wavs) / self.tts_config.audio['sample_rate']
+ print(f" > Processing time: {process_time}")
+ print(f" > Real-time factor: {process_time / audio_time}")
+ return wavs
diff --git a/TTS/vocoder/layers/losses.py b/TTS/vocoder/layers/losses.py
index e705b1e0..1107b3c5 100644
--- a/TTS/vocoder/layers/losses.py
+++ b/TTS/vocoder/layers/losses.py
@@ -4,13 +4,15 @@ from torch import nn
from torch.nn import functional as F
-class TorchSTFT():
+class TorchSTFT(nn.Module):
def __init__(self, n_fft, hop_length, win_length, window='hann_window'):
""" Torch based STFT operation """
+ super(TorchSTFT, self).__init__()
self.n_fft = n_fft
self.hop_length = hop_length
self.win_length = win_length
- self.window = getattr(torch, window)(win_length)
+ self.window = nn.Parameter(getattr(torch, window)(win_length),
+ requires_grad=False)
def __call__(self, x):
# B x D x T x 2
@@ -22,7 +24,8 @@ class TorchSTFT():
center=True,
pad_mode="reflect", # compatible with audio.py
normalized=False,
- onesided=True)
+ onesided=True,
+ return_complex=False)
M = o[:, :, :, 0]
P = o[:, :, :, 1]
return torch.sqrt(torch.clamp(M ** 2 + P ** 2, min=1e-8))
diff --git a/TTS/vocoder/models/melgan_generator.py b/TTS/vocoder/models/melgan_generator.py
index 9ab98cef..3070eac7 100644
--- a/TTS/vocoder/models/melgan_generator.py
+++ b/TTS/vocoder/models/melgan_generator.py
@@ -95,3 +95,11 @@ class MelganGenerator(nn.Module):
nn.utils.remove_weight_norm(layer)
except ValueError:
layer.remove_weight_norm()
+
+ def load_checkpoint(self, config, checkpoint_path, eval=False): # pylint: disable=unused-argument, redefined-builtin
+ state = torch.load(checkpoint_path, map_location=torch.device('cpu'))
+ self.load_state_dict(state['model'])
+ if eval:
+ self.eval()
+ assert not self.training
+ self.remove_weight_norm()
diff --git a/TTS/vocoder/models/parallel_wavegan_generator.py b/TTS/vocoder/models/parallel_wavegan_generator.py
index d703f327..1d1bcdcb 100644
--- a/TTS/vocoder/models/parallel_wavegan_generator.py
+++ b/TTS/vocoder/models/parallel_wavegan_generator.py
@@ -39,6 +39,7 @@ class ParallelWaveganGenerator(torch.nn.Module):
self.upsample_factors = upsample_factors
self.upsample_scale = np.prod(upsample_factors)
self.inference_padding = inference_padding
+ self.use_weight_norm = use_weight_norm
# check the number of layers and stacks
assert num_res_blocks % stacks == 0
@@ -156,3 +157,12 @@ class ParallelWaveganGenerator(torch.nn.Module):
def receptive_field_size(self):
return self._get_receptive_field_size(self.layers, self.stacks,
self.kernel_size)
+
+ def load_checkpoint(self, config, checkpoint_path, eval=False): # pylint: disable=unused-argument, redefined-builtin
+ state = torch.load(checkpoint_path, map_location=torch.device('cpu'))
+ self.load_state_dict(state['model'])
+ if eval:
+ self.eval()
+ assert not self.training
+ if self.use_weight_norm:
+ self.remove_weight_norm()
diff --git a/TTS/vocoder/models/wavegrad.py b/TTS/vocoder/models/wavegrad.py
index da491771..f4a5faa3 100644
--- a/TTS/vocoder/models/wavegrad.py
+++ b/TTS/vocoder/models/wavegrad.py
@@ -175,3 +175,22 @@ class Wavegrad(nn.Module):
self.x_conv = weight_norm(self.x_conv)
self.out_conv = weight_norm(self.out_conv)
self.y_conv = weight_norm(self.y_conv)
+
+
+ def load_checkpoint(self, config, checkpoint_path, eval=False): # pylint: disable=unused-argument, redefined-builtin
+ state = torch.load(checkpoint_path, map_location=torch.device('cpu'))
+ self.load_state_dict(state['model'])
+ if eval:
+ self.eval()
+ assert not self.training
+ if self.use_weight_norm:
+ self.remove_weight_norm()
+ betas = np.linspace(config['test_noise_schedule']['min_val'],
+ config['test_noise_schedule']['max_val'],
+ config['test_noise_schedule']['num_steps'])
+ self.compute_noise_level(betas)
+ else:
+ betas = np.linspace(config['train_noise_schedule']['min_val'],
+ config['train_noise_schedule']['max_val'],
+ config['train_noise_schedule']['num_steps'])
+ self.compute_noise_level(betas)
diff --git a/TTS/vocoder/models/wavernn.py b/TTS/vocoder/models/wavernn.py
index 8aa84d34..cb03deb3 100644
--- a/TTS/vocoder/models/wavernn.py
+++ b/TTS/vocoder/models/wavernn.py
@@ -499,3 +499,10 @@ class WaveRNN(nn.Module):
unfolded[start:end] += y[i]
return unfolded
+
+ def load_checkpoint(self, config, checkpoint_path, eval=False): # pylint: disable=unused-argument, redefined-builtin
+ state = torch.load(checkpoint_path, map_location=torch.device('cpu'))
+ self.load_state_dict(state['model'])
+ if eval:
+ self.eval()
+ assert not self.training
diff --git a/TTS/vocoder/utils/generic_utils.py b/TTS/vocoder/utils/generic_utils.py
index d6e2e13b..fb943a37 100644
--- a/TTS/vocoder/utils/generic_utils.py
+++ b/TTS/vocoder/utils/generic_utils.py
@@ -1,4 +1,5 @@
import re
+import torch
import importlib
import numpy as np
from matplotlib import pyplot as plt
@@ -6,6 +7,29 @@ from matplotlib import pyplot as plt
from TTS.tts.utils.visual import plot_spectrogram
+def interpolate_vocoder_input(scale_factor, spec):
+ """Interpolate spectrogram by the scale factor.
+ It is mainly used to match the sampling rates of
+ the tts and vocoder models.
+
+ Args:
+ scale_factor (float): scale factor to interpolate the spectrogram
+ spec (np.array): spectrogram to be interpolated
+
+ Returns:
+ torch.tensor: interpolated spectrogram.
+ """
+ print(" > before interpolation :", spec.shape)
+ spec = torch.tensor(spec).unsqueeze(0).unsqueeze(0) # pylint: disable=not-callable
+ spec = torch.nn.functional.interpolate(spec,
+ scale_factor=scale_factor,
+ recompute_scale_factor=True,
+ mode='bilinear',
+ align_corners=False).squeeze(0)
+ print(" > after interpolation :", spec.shape)
+ return spec
+
+
def plot_results(y_hat, y, ap, global_step, name_prefix):
""" Plot vocoder model results """
diff --git a/TTS/vocoder/utils/io.py b/TTS/vocoder/utils/io.py
index c33d2cb9..5c42dfca 100644
--- a/TTS/vocoder/utils/io.py
+++ b/TTS/vocoder/utils/io.py
@@ -6,7 +6,7 @@ import pickle as pickle_tts
from TTS.utils.io import RenamingUnpickler
-def load_checkpoint(model, checkpoint_path, use_cuda=False):
+def load_checkpoint(model, checkpoint_path, use_cuda=False, eval=False):
try:
state = torch.load(checkpoint_path, map_location=torch.device('cpu'))
except ModuleNotFoundError:
@@ -15,6 +15,8 @@ def load_checkpoint(model, checkpoint_path, use_cuda=False):
model.load_state_dict(state['model'])
if use_cuda:
model.cuda()
+ if eval:
+ model.eval()
return model, state
diff --git a/notebooks/DDC_TTS_and_MultiBand_MelGAN_Example.ipynb b/notebooks/DDC_TTS_and_MultiBand_MelGAN_Example.ipynb
index dc582830..17403771 100644
--- a/notebooks/DDC_TTS_and_MultiBand_MelGAN_Example.ipynb
+++ b/notebooks/DDC_TTS_and_MultiBand_MelGAN_Example.ipynb
@@ -112,7 +112,7 @@
" t_1 = time.time()\n",
" waveform, alignment, mel_spec, mel_postnet_spec, stop_tokens, inputs = synthesis(model, text, CONFIG, use_cuda, ap, speaker_id, style_wav=None,\n",
" truncated=False, enable_eos_bos_chars=CONFIG.enable_eos_bos_chars)\n",
- " # mel_postnet_spec = ap._denormalize(mel_postnet_spec.T)\n",
+ " # mel_postnet_spec = ap.denormalize(mel_postnet_spec.T)\n",
" if not use_gl:\n",
" waveform = vocoder_model.inference(torch.FloatTensor(mel_postnet_spec.T).unsqueeze(0))\n",
" waveform = waveform.flatten()\n",
diff --git a/notebooks/DDC_TTS_and_ParallelWaveGAN_Example.ipynb b/notebooks/DDC_TTS_and_ParallelWaveGAN_Example.ipynb
index 00de8bbd..35a257e0 100644
--- a/notebooks/DDC_TTS_and_ParallelWaveGAN_Example.ipynb
+++ b/notebooks/DDC_TTS_and_ParallelWaveGAN_Example.ipynb
@@ -112,7 +112,7 @@
" t_1 = time.time()\n",
" waveform, alignment, mel_spec, mel_postnet_spec, stop_tokens, inputs = synthesis(model, text, CONFIG, use_cuda, ap, speaker_id, style_wav=None,\n",
" truncated=False, enable_eos_bos_chars=CONFIG.enable_eos_bos_chars)\n",
- " # mel_postnet_spec = ap._denormalize(mel_postnet_spec.T)\n",
+ " # mel_postnet_spec = ap.denormalize(mel_postnet_spec.T)\n",
" if not use_gl:\n",
" waveform = vocoder_model.inference(torch.FloatTensor(mel_postnet_spec.T).unsqueeze(0))\n",
" waveform = waveform.flatten()\n",
diff --git a/notebooks/dataset_analysis/CheckSpectrograms.ipynb b/notebooks/dataset_analysis/CheckSpectrograms.ipynb
index a1f2fab8..4d4ba57a 100644
--- a/notebooks/dataset_analysis/CheckSpectrograms.ipynb
+++ b/notebooks/dataset_analysis/CheckSpectrograms.ipynb
@@ -230,8 +230,8 @@
"\u001b[0;31m---------------------------------------------------------------------------\u001b[0m",
"\u001b[0;31mRuntimeError\u001b[0m Traceback (most recent call last)",
"\u001b[0;32m
\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mspec\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mAP\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mspectrogram\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mwav\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 2\u001b[0m \u001b[0mprint\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"Max:\"\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mspec\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mmax\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 3\u001b[0m \u001b[0mprint\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"Min:\"\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mspec\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mmin\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 4\u001b[0m \u001b[0mprint\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"Mean:\"\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mspec\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mmean\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 5\u001b[0m \u001b[0mplot_spectrogram\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mspec\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mT\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mAP\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m;\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n",
- "\u001b[0;32m~/Projects/TTS/tts/utils/audio.py\u001b[0m in \u001b[0;36mspectrogram\u001b[0;34m(self, y)\u001b[0m\n\u001b[1;32m 218\u001b[0m \u001b[0mD\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_stft\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0my\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 219\u001b[0m \u001b[0mS\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_amp_to_db\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mnp\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mabs\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mD\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 220\u001b[0;31m \u001b[0;32mreturn\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_normalize\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mS\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 221\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 222\u001b[0m \u001b[0;32mdef\u001b[0m \u001b[0mmelspectrogram\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0my\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n",
- "\u001b[0;32m~/Projects/TTS/tts/utils/audio.py\u001b[0m in \u001b[0;36m_normalize\u001b[0;34m(self, S)\u001b[0m\n\u001b[1;32m 117\u001b[0m \u001b[0;32mreturn\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mlinear_scaler\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mtransform\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mS\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mT\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mT\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 118\u001b[0m \u001b[0;32melse\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 119\u001b[0;31m \u001b[0;32mraise\u001b[0m \u001b[0mRuntimeError\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m' [!] Mean-Var stats does not match the given feature dimensions.'\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 120\u001b[0m \u001b[0;31m# range normalization\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 121\u001b[0m \u001b[0mS\u001b[0m \u001b[0;34m-=\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mref_level_db\u001b[0m \u001b[0;31m# discard certain range of DB assuming it is air noise\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n",
+ "\u001b[0;32m~/Projects/TTS/tts/utils/audio.py\u001b[0m in \u001b[0;36mspectrogram\u001b[0;34m(self, y)\u001b[0m\n\u001b[1;32m 218\u001b[0m \u001b[0mD\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_stft\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0my\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 219\u001b[0m \u001b[0mS\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_amp_to_db\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mnp\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mabs\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mD\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 220\u001b[0;31m \u001b[0;32mreturn\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mnormalize\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mS\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 221\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 222\u001b[0m \u001b[0;32mdef\u001b[0m \u001b[0mmelspectrogram\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0my\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n",
+ "\u001b[0;32m~/Projects/TTS/tts/utils/audio.py\u001b[0m in \u001b[0;36mnormalize\u001b[0;34m(self, S)\u001b[0m\n\u001b[1;32m 117\u001b[0m \u001b[0;32mreturn\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mlinear_scaler\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mtransform\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mS\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mT\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mT\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 118\u001b[0m \u001b[0;32melse\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 119\u001b[0;31m \u001b[0;32mraise\u001b[0m \u001b[0mRuntimeError\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m' [!] Mean-Var stats does not match the given feature dimensions.'\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 120\u001b[0m \u001b[0;31m# range normalization\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 121\u001b[0m \u001b[0mS\u001b[0m \u001b[0;34m-=\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mref_level_db\u001b[0m \u001b[0;31m# discard certain range of DB assuming it is air noise\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n",
"\u001b[0;31mRuntimeError\u001b[0m: [!] Mean-Var stats does not match the given feature dimensions."
]
}
@@ -314,7 +314,7 @@
" exec(set_val_cmd)\n",
" wav = AP.load_wav(file)\n",
" spec = AP.spectrogram(wav)\n",
- " spec_norm = AP._denormalize(spec.T)\n",
+ " spec_norm = AP.denormalize(spec.T)\n",
" plt.subplot(len(values), 2, 2*idx + 1)\n",
" plt.imshow(spec_norm.T, aspect=\"auto\", origin=\"lower\")\n",
" # plt.colorbar()\n",
diff --git a/pyproject.toml b/pyproject.toml
new file mode 100644
index 00000000..fc0aca47
--- /dev/null
+++ b/pyproject.toml
@@ -0,0 +1,2 @@
+[build-system]
+requires = ["setuptools", "wheel", "Cython", "numpy>=1.16.0"]
\ No newline at end of file
diff --git a/setup.py b/setup.py
index d55b2c12..6cc06f89 100644
--- a/setup.py
+++ b/setup.py
@@ -5,22 +5,16 @@ import os
import shutil
import subprocess
import sys
+
import numpy
-
-from setuptools import setup, find_packages, Extension
-import setuptools.command.develop
import setuptools.command.build_py
+import setuptools.command.develop
-# handle import if cython is not already installed.
-try:
- from Cython.Build import cythonize
-except ImportError:
- # create closure for deferred import
- def cythonize(*args, **kwargs): #pylint: disable=redefined-outer-name
- from Cython.Build import cythonize #pylint: disable=redefined-outer-name, import-outside-toplevel
- return cythonize(*args, **kwargs)
-
+from setuptools import find_packages, setup
+from distutils.extension import Extension
+from Cython.Build import cythonize
+# parameters for wheeling server.
parser = argparse.ArgumentParser(add_help=False, allow_abbrev=False)
parser.add_argument('--checkpoint',
type=str,
@@ -33,38 +27,25 @@ args, unknown_args = parser.parse_known_args()
# Remove our arguments from argv so that setuptools doesn't see them
sys.argv = [sys.argv[0]] + unknown_args
-version = '0.0.8'
-
-# Adapted from https://github.com/pytorch/pytorch
+version = '0.0.9'
cwd = os.path.dirname(os.path.abspath(__file__))
-if os.getenv('TTS_PYTORCH_BUILD_VERSION'):
- version = os.getenv('TTS_PYTORCH_BUILD_VERSION')
-else:
- try:
- sha = subprocess.check_output(['git', 'rev-parse', 'HEAD'],
- cwd=cwd).decode('ascii').strip()
- version += '+' + sha[:7]
- except subprocess.CalledProcessError:
- pass
- except IOError: # FileNotFoundError for python 3
- pass
-
# Handle Cython code
-def find_pyx(path='.'):
- pyx_files = []
- for root, _, filenames in os.walk(path):
- for fname in filenames:
- if fname.endswith('.pyx'):
- pyx_files.append(os.path.join(root, fname))
- return pyx_files
+# def find_pyx(path='.'):
+# pyx_files = []
+# for root, _, filenames in os.walk(path):
+# for fname in filenames:
+# if fname.endswith('.pyx'):
+# pyx_files.append(os.path.join(root, fname))
+# return pyx_files
-def find_cython_extensions(path="."):
- exts = cythonize(find_pyx(path), language_level=3)
- for ext in exts:
- ext.include_dirs = [numpy.get_include()]
- return exts
+# def find_cython_extensions(path="."):
+# exts = cythonize(find_pyx(path), language_level=3)
+# for ext in exts:
+# ext.include_dirs = [numpy.get_include()]
+
+# return exts
class build_py(setuptools.command.build_py.build_py): # pylint: disable=too-many-ancestors
@@ -105,12 +86,12 @@ def pip_install(package_name):
subprocess.call([sys.executable, '-m', 'pip', 'install', package_name])
-reqs_from_file = open('requirements.txt').readlines()
-reqs_without_tf = [r for r in reqs_from_file if not r.startswith('tensorflow')]
-tf_req = [r for r in reqs_from_file if r.startswith('tensorflow')]
-
-requirements = {'install_requires': reqs_without_tf, 'pip_install': tf_req}
+requirements = open(os.path.join(cwd, 'requirements.txt'), 'r').readlines()
+with open('README.md', "r", encoding="utf-8") as readme_file:
+ README = readme_file.read()
+exts = [Extension(name='TTS.tts.layers.glow_tts.monotonic_align.core',
+ sources=["TTS/tts/layers/glow_tts/monotonic_align/core.pyx"])]
setup(
name='TTS',
version=version,
@@ -118,9 +99,15 @@ setup(
author='Eren GΓΆlge',
author_email='egolge@mozilla.com',
description='Text to Speech with Deep Learning',
+ long_description=README,
+ long_description_content_type="text/markdown",
license='MPL-2.0',
- entry_points={'console_scripts': ['tts-server = TTS.server.server:main']},
- ext_modules=find_cython_extensions(),
+ # cython
+ include_dirs=numpy.get_include(),
+ ext_modules=cythonize(exts, language_level=3),
+ # ext_modules=find_cython_extensions(),
+ # package
+ include_package_data=True,
packages=find_packages(include=['TTS*']),
project_urls={
'Documentation': 'https://github.com/mozilla/TTS/wiki',
@@ -131,9 +118,16 @@ setup(
cmdclass={
'build_py': build_py,
'develop': develop,
+ # 'build_ext': build_ext
+ },
+ install_requires=requirements,
+ python_requires='>=3.6.0, <3.9',
+ entry_points={
+ 'console_scripts': [
+ 'tts=TTS.bin.synthesize:main',
+ 'tts-server = TTS.server.server:main'
+ ]
},
- install_requires=requirements['install_requires'],
- python_requires='>=3.6.0',
classifiers=[
"Programming Language :: Python",
"Programming Language :: Python :: 3",
@@ -141,14 +135,16 @@ setup(
"Programming Language :: Python :: 3.7",
"Programming Language :: Python :: 3.8",
'Development Status :: 3 - Alpha',
- "Intended Audience :: Science/Research :: Developers",
+ "Intended Audience :: Science/Research",
+ "Intended Audience :: Developers",
"Operating System :: POSIX :: Linux",
'License :: OSI Approved :: Mozilla Public License 2.0 (MPL 2.0)',
- "Topic :: Software Development :: Libraries :: Python Modules :: Speech :: Sound/Audio :: Multimedia :: Artificial Intelligence",
- ])
-
-# for some reason having tensorflow in 'install_requires'
-# breaks some of the dependencies.
-if 'bdist_wheel' not in unknown_args:
- for module in requirements['pip_install']:
- pip_install(module)
+ "Topic :: Software Development",
+ "Topic :: Software Development :: Libraries :: Python Modules",
+ "Topic :: Multimedia :: Sound/Audio :: Speech",
+ "Topic :: Multimedia :: Sound/Audio",
+ "Topic :: Multimedia",
+ "Topic :: Scientific/Engineering :: Artificial Intelligence"
+ ],
+ zip_safe=False
+)
diff --git a/tests/test_audio.py b/tests/test_audio.py
index dcc511e2..c00cd8f8 100644
--- a/tests/test_audio.py
+++ b/tests/test_audio.py
@@ -67,21 +67,21 @@ class TestAudio(unittest.TestCase):
self.ap.symmetric_norm = False
self.ap.clip_norm = False
self.ap.max_norm = 4.0
- x_norm = self.ap._normalize(x)
+ x_norm = self.ap.normalize(x)
print(f" > MaxNorm: {self.ap.max_norm}, ClipNorm:{self.ap.clip_norm}, SymmetricNorm:{self.ap.symmetric_norm}, SignalNorm:{self.ap.signal_norm} Range-> {x_norm.max()} -- {x_norm.min()}")
assert (x_old - x).sum() == 0
# check value range
assert x_norm.max() <= self.ap.max_norm + 1, x_norm.max()
assert x_norm.min() >= 0 - 1, x_norm.min()
# check denorm.
- x_ = self.ap._denormalize(x_norm)
+ x_ = self.ap.denormalize(x_norm)
assert (x - x_).sum() < 1e-3, (x - x_).mean()
self.ap.signal_norm = True
self.ap.symmetric_norm = False
self.ap.clip_norm = True
self.ap.max_norm = 4.0
- x_norm = self.ap._normalize(x)
+ x_norm = self.ap.normalize(x)
print(f" > MaxNorm: {self.ap.max_norm}, ClipNorm:{self.ap.clip_norm}, SymmetricNorm:{self.ap.symmetric_norm}, SignalNorm:{self.ap.signal_norm} Range-> {x_norm.max()} -- {x_norm.min()}")
@@ -90,14 +90,14 @@ class TestAudio(unittest.TestCase):
assert x_norm.max() <= self.ap.max_norm, x_norm.max()
assert x_norm.min() >= 0, x_norm.min()
# check denorm.
- x_ = self.ap._denormalize(x_norm)
+ x_ = self.ap.denormalize(x_norm)
assert (x - x_).sum() < 1e-3, (x - x_).mean()
self.ap.signal_norm = True
self.ap.symmetric_norm = True
self.ap.clip_norm = False
self.ap.max_norm = 4.0
- x_norm = self.ap._normalize(x)
+ x_norm = self.ap.normalize(x)
print(f" > MaxNorm: {self.ap.max_norm}, ClipNorm:{self.ap.clip_norm}, SymmetricNorm:{self.ap.symmetric_norm}, SignalNorm:{self.ap.signal_norm} Range-> {x_norm.max()} -- {x_norm.min()}")
@@ -107,14 +107,14 @@ class TestAudio(unittest.TestCase):
assert x_norm.min() >= -self.ap.max_norm - 2, x_norm.min() #pylint: disable=invalid-unary-operand-type
assert x_norm.min() <= 0, x_norm.min()
# check denorm.
- x_ = self.ap._denormalize(x_norm)
+ x_ = self.ap.denormalize(x_norm)
assert (x - x_).sum() < 1e-3, (x - x_).mean()
self.ap.signal_norm = True
self.ap.symmetric_norm = True
self.ap.clip_norm = True
self.ap.max_norm = 4.0
- x_norm = self.ap._normalize(x)
+ x_norm = self.ap.normalize(x)
print(f" > MaxNorm: {self.ap.max_norm}, ClipNorm:{self.ap.clip_norm}, SymmetricNorm:{self.ap.symmetric_norm}, SignalNorm:{self.ap.signal_norm} Range-> {x_norm.max()} -- {x_norm.min()}")
@@ -124,26 +124,26 @@ class TestAudio(unittest.TestCase):
assert x_norm.min() >= -self.ap.max_norm, x_norm.min() #pylint: disable=invalid-unary-operand-type
assert x_norm.min() <= 0, x_norm.min()
# check denorm.
- x_ = self.ap._denormalize(x_norm)
+ x_ = self.ap.denormalize(x_norm)
assert (x - x_).sum() < 1e-3, (x - x_).mean()
self.ap.signal_norm = True
self.ap.symmetric_norm = False
self.ap.max_norm = 1.0
- x_norm = self.ap._normalize(x)
+ x_norm = self.ap.normalize(x)
print(f" > MaxNorm: {self.ap.max_norm}, ClipNorm:{self.ap.clip_norm}, SymmetricNorm:{self.ap.symmetric_norm}, SignalNorm:{self.ap.signal_norm} Range-> {x_norm.max()} -- {x_norm.min()}")
assert (x_old - x).sum() == 0
assert x_norm.max() <= self.ap.max_norm, x_norm.max()
assert x_norm.min() >= 0, x_norm.min()
- x_ = self.ap._denormalize(x_norm)
+ x_ = self.ap.denormalize(x_norm)
assert (x - x_).sum() < 1e-3
self.ap.signal_norm = True
self.ap.symmetric_norm = True
self.ap.max_norm = 1.0
- x_norm = self.ap._normalize(x)
+ x_norm = self.ap.normalize(x)
print(f" > MaxNorm: {self.ap.max_norm}, ClipNorm:{self.ap.clip_norm}, SymmetricNorm:{self.ap.symmetric_norm}, SignalNorm:{self.ap.signal_norm} Range-> {x_norm.max()} -- {x_norm.min()}")
@@ -151,7 +151,7 @@ class TestAudio(unittest.TestCase):
assert x_norm.max() <= self.ap.max_norm, x_norm.max()
assert x_norm.min() >= -self.ap.max_norm, x_norm.min() #pylint: disable=invalid-unary-operand-type
assert x_norm.min() < 0, x_norm.min()
- x_ = self.ap._denormalize(x_norm)
+ x_ = self.ap.denormalize(x_norm)
assert (x - x_).sum() < 1e-3
def test_scaler(self):
@@ -172,5 +172,5 @@ class TestAudio(unittest.TestCase):
wav = self.ap.load_wav(WAV_FILE)
mel_reference = self.ap.melspectrogram(wav)
mel_norm = ap.melspectrogram(wav)
- mel_denorm = ap._denormalize(mel_norm)
+ mel_denorm = ap.denormalize(mel_norm)
assert abs(mel_reference - mel_denorm).max() < 1e-4
diff --git a/tests/test_demo_server.py b/tests/test_demo_server.py
index 0576430c..bccff55d 100644
--- a/tests/test_demo_server.py
+++ b/tests/test_demo_server.py
@@ -2,7 +2,7 @@ import os
import unittest
from tests import get_tests_input_path, get_tests_output_path
-from TTS.server.synthesizer import Synthesizer
+from TTS.utils.synthesizer import Synthesizer
from TTS.tts.utils.generic_utils import setup_model
from TTS.tts.utils.io import save_checkpoint
from TTS.tts.utils.text.symbols import make_symbols, phonemes, symbols
@@ -29,7 +29,7 @@ class DemoServerTest(unittest.TestCase):
tts_root_path = get_tests_output_path()
config['tts_checkpoint'] = os.path.join(tts_root_path, config['tts_checkpoint'])
config['tts_config'] = os.path.join(tts_root_path, config['tts_config'])
- synthesizer = Synthesizer(config)
+ synthesizer = Synthesizer(config['tts_checkpoint'], config['tts_config'], None, None)
synthesizer.tts("Better this test works!!")
def test_split_into_sentences(self):