diff --git a/TTS/bin/compute_embeddings.py b/TTS/bin/compute_embeddings.py index 003da1e5..9ed459a2 100644 --- a/TTS/bin/compute_embeddings.py +++ b/TTS/bin/compute_embeddings.py @@ -3,71 +3,44 @@ import glob import os import torch +import numpy as np from tqdm import tqdm from TTS.speaker_encoder.utils.generic_utils import setup_model from TTS.tts.datasets.preprocess import load_meta_data from TTS.tts.utils.speakers import SpeakerManager from TTS.utils.audio import AudioProcessor -from TTS.config import load_config, BaseDatasetConfig +from TTS.config import load_config parser = argparse.ArgumentParser( - description='Compute embedding vectors for each wav file in a dataset. If "target_dataset" is defined, it generates "speakers.json" necessary for training a multi-speaker model.' + description='Compute embedding vectors for each wav file in a dataset.' ) -parser.add_argument("model_path", type=str, help="Path to model outputs (checkpoint, tensorboard etc.).") +parser.add_argument("model_path", type=str, help="Path to model checkpoint file.") parser.add_argument( "config_path", type=str, - help="Path to config file for training.", + help="Path to model config file.", ) -parser.add_argument("data_path", type=str, help="Data path for wav files - directory or CSV file") -parser.add_argument("output_path", type=str, help="path for output speakers.json.") + parser.add_argument( - "--target_dataset", + "config_dataset_path", type=str, - default="", - help="Target dataset to pick a processor from TTS.tts.dataset.preprocess. Necessary to create a speakers.json file.", + help="Path to dataset config file.", ) +parser.add_argument("output_path", type=str, help="path for output speakers.json and/or speakers.npy.") parser.add_argument("--use_cuda", type=bool, help="flag to set cuda.", default=True) -parser.add_argument("--separator", type=str, help="Separator used in file if CSV is passed for data_path", default="|") +parser.add_argument("--save_npy", type=bool, help="flag to set cuda.", default=False) args = parser.parse_args() c = load_config(args.config_path) +c_dataset = load_config(args.config_dataset_path) + ap = AudioProcessor(**c["audio"]) -data_path = args.data_path -split_ext = os.path.splitext(data_path) -sep = args.separator +train_files, dev_files = load_meta_data(c_dataset.datasets, eval_split=True, ignore_generated_eval=True) -if args.target_dataset != "": - # if target dataset is defined - dataset_config = [ - BaseDatasetConfig(name=args.target_dataset, path=args.data_path, meta_file_train=None, meta_file_val=None), - ] - wav_files, _ = load_meta_data(dataset_config, eval_split=False) -else: - # if target dataset is not defined - if len(split_ext) > 0 and split_ext[1].lower() == ".csv": - # Parse CSV - print(f"CSV file: {data_path}") - with open(data_path) as f: - wav_path = os.path.join(os.path.dirname(data_path), "wavs") - wav_files = [] - print(f"Separator is: {sep}") - for line in f: - components = line.split(sep) - if len(components) != 2: - print("Invalid line") - continue - wav_file = os.path.join(wav_path, components[0] + ".wav") - # print(f'wav_file: {wav_file}') - if os.path.exists(wav_file): - wav_files.append(wav_file) - print(f"Count of wavs imported: {len(wav_files)}") - else: - # Parse all wav files in data_path - wav_files = glob.glob(data_path + "/**/*.wav", recursive=True) +wav_files = train_files + dev_files # define Encoder model model = setup_model(c) @@ -100,11 +73,19 @@ for idx, wav_file in enumerate(tqdm(wav_files)): if speaker_mapping: # save speaker_mapping if target dataset is defined - if '.json' not in args.output_path: + if '.json' not in args.output_path and '.npy' not in args.output_path: mapping_file_path = os.path.join(args.output_path, "speakers.json") + mapping_npy_file_path = os.path.join(args.output_path, "speakers.npy") else: - mapping_file_path = args.output_path + mapping_file_path = args.output_path.replace(".npy", ".json") + mapping_npy_file_path = mapping_file_path.replace(".json", ".npy") + os.makedirs(os.path.dirname(mapping_file_path), exist_ok=True) + + if args.save_npy: + np.save(mapping_npy_file_path, speaker_mapping) + print("Speaker embeddings saved at:", mapping_npy_file_path) + speaker_manager = SpeakerManager() # pylint: disable=W0212 speaker_manager._save_json(mapping_file_path, speaker_mapping) diff --git a/TTS/bin/find_unique_chars.py b/TTS/bin/find_unique_chars.py index 7891d65a..8fbc8f8e 100644 --- a/TTS/bin/find_unique_chars.py +++ b/TTS/bin/find_unique_chars.py @@ -2,40 +2,41 @@ import argparse import os from argparse import RawTextHelpFormatter - -from TTS.tts.datasets.preprocess import get_preprocessor_by_name +from TTS.tts.datasets.preprocess import load_meta_data +from TTS.config import load_config def main(): # pylint: disable=bad-option-value parser = argparse.ArgumentParser( description="""Find all the unique characters or phonemes in a dataset.\n\n""" - """Target dataset must be defined in TTS.tts.datasets.preprocess\n\n""" + """\n\n""" """ Example runs: - python TTS/bin/find_unique_chars.py --dataset ljspeech --meta_file /path/to/LJSpeech/metadata.csv + python TTS/bin/find_unique_chars.py --config_path config.json """, formatter_class=RawTextHelpFormatter, ) - parser.add_argument( - "--dataset", type=str, default="", help="One of the target dataset names in TTS.tts.datasets.preprocess." + "--config_path", type=str, help="Path to dataset config file.", required=True ) - - parser.add_argument("--meta_file", type=str, default=None, help="Path to the transcriptions file of the dataset.") - args = parser.parse_args() - preprocessor = get_preprocessor_by_name(args.dataset) - items = preprocessor(os.path.dirname(args.meta_file), os.path.basename(args.meta_file)) + c = load_config(args.config_path) + # load all datasets + train_items, dev_items = load_meta_data(c.datasets, eval_split=True, ignore_generated_eval=True) + items = train_items + dev_items + texts = "".join(item[0] for item in items) chars = set(texts) lower_chars = filter(lambda c: c.islower(), chars) + chars_force_lower = set([c.lower() for c in chars]) + print(f" > Number of unique characters: {len(chars)}") print(f" > Unique characters: {''.join(sorted(chars))}") print(f" > Unique lower characters: {''.join(sorted(lower_chars))}") - + print(f" > Unique all forced to lower characters: {''.join(sorted(chars_force_lower))}") if __name__ == "__main__": main() diff --git a/TTS/tts/datasets/preprocess.py b/TTS/tts/datasets/preprocess.py index 72ab160e..7fbc01b8 100644 --- a/TTS/tts/datasets/preprocess.py +++ b/TTS/tts/datasets/preprocess.py @@ -37,7 +37,7 @@ def split_dataset(items): return items[:eval_split_size], items[eval_split_size:] -def load_meta_data(datasets, eval_split=True): +def load_meta_data(datasets, eval_split=True, ignore_generated_eval=False): meta_data_train_all = [] meta_data_eval_all = [] if eval_split else None for dataset in datasets: @@ -54,9 +54,11 @@ def load_meta_data(datasets, eval_split=True): if eval_split: if meta_file_val: meta_data_eval = preprocessor(root_path, meta_file_val) - else: + meta_data_eval_all += meta_data_eval + elif not ignore_generated_eval: meta_data_eval, meta_data_train = split_dataset(meta_data_train) - meta_data_eval_all += meta_data_eval + meta_data_eval_all += meta_data_eval + meta_data_train_all += meta_data_train # load attention masks for duration predictor training if dataset.meta_file_attn_mask: @@ -270,16 +272,20 @@ def libri_tts(root_path, meta_files=None): items = [] if meta_files is None: meta_files = glob(f"{root_path}/**/*trans.tsv", recursive=True) + else: + if isinstance(meta_files, str): + meta_files = [os.path.join(root_path, meta_files)] + for meta_file in meta_files: _meta_file = os.path.basename(meta_file).split(".")[0] - speaker_name = _meta_file.split("_")[0] - chapter_id = _meta_file.split("_")[1] - _root_path = os.path.join(root_path, f"{speaker_name}/{chapter_id}") with open(meta_file, "r") as ttf: for line in ttf: cols = line.split("\t") - wav_file = os.path.join(_root_path, cols[0] + ".wav") - text = cols[1] + file_name = cols[0] + speaker_name, chapter_id, *_ = cols[0].split("_") + _root_path = os.path.join(root_path, f"{speaker_name}/{chapter_id}") + wav_file = os.path.join(_root_path, file_name + ".wav") + text = cols[2] items.append([text, wav_file, "LTTS_" + speaker_name]) for item in items: assert os.path.exists(item[1]), f" [!] wav files don't exist - {item[1]}" @@ -355,6 +361,18 @@ def vctk_slim(root_path, meta_files=None, wavs_path="wav48"): return items +def mls(root_path, meta_files=None): + """http://www.openslr.org/94/""" + items = [] + with open(os.path.join(root_path, meta_files), "r") as meta: + isTrain = "train" in meta_files + for line in meta: + file, text = line.split('\t') + text = text[:-1] + speaker, book, no = file.split('_') + wav_file = os.path.join(root_path, "train" if isTrain else "dev", 'audio', speaker, book, file + ".wav") + items.append([text, wav_file, "MLS_" + speaker]) + return items # ======================================== VOX CELEB =========================================== def voxceleb2(root_path, meta_file=None):