382 lines
11 KiB
Python
382 lines
11 KiB
Python
import json
|
|
import os
|
|
import pathlib
|
|
import sys
|
|
from collections import OrderedDict
|
|
from pathlib import Path
|
|
|
|
import click
|
|
from typing import Tuple
|
|
|
|
root_dir = Path(__file__).resolve().parent.parent
|
|
os.environ['PYTHONPATH'] = str(root_dir)
|
|
sys.path.insert(0, str(root_dir))
|
|
|
|
|
|
def find_exp(exp):
|
|
if not (root_dir / 'checkpoints' / exp).exists():
|
|
for subdir in (root_dir / 'checkpoints').iterdir():
|
|
if not subdir.is_dir():
|
|
continue
|
|
if subdir.name.startswith(exp):
|
|
print(f'| match ckpt by prefix: {subdir.name}')
|
|
exp = subdir.name
|
|
break
|
|
else:
|
|
raise click.BadParameter(
|
|
f'There are no matching exp starting with \'{exp}\' in \'checkpoints\' folder. '
|
|
'Please specify \'--exp\' as the folder name or prefix.'
|
|
)
|
|
else:
|
|
print(f'| found ckpt by name: {exp}')
|
|
return exp
|
|
|
|
|
|
@click.group()
|
|
def main():
|
|
pass
|
|
|
|
|
|
@main.command(help='Run DiffSinger acoustic model inference')
|
|
@click.argument(
|
|
'proj', type=click.Path(
|
|
exists=True, file_okay=True, dir_okay=False, readable=True,
|
|
path_type=pathlib.Path, resolve_path=True
|
|
),
|
|
metavar='DS_FILE'
|
|
)
|
|
@click.option(
|
|
'--exp', type=str,
|
|
required=True, metavar='EXP',
|
|
callback=lambda ctx, param, value: find_exp(value),
|
|
help='Selection of model'
|
|
)
|
|
@click.option(
|
|
'--ckpt', type=click.IntRange(min=0),
|
|
required=False, metavar='STEPS',
|
|
help='Selection of checkpoint training steps'
|
|
)
|
|
@click.option(
|
|
'--spk', type=click.STRING,
|
|
required=False,
|
|
help='Speaker name or mixture of speakers'
|
|
)
|
|
@click.option(
|
|
'--lang', type=click.STRING,
|
|
required=False,
|
|
help='Default language name'
|
|
)
|
|
@click.option(
|
|
'--out', type=click.Path(
|
|
file_okay=False, dir_okay=True, path_type=pathlib.Path
|
|
),
|
|
required=False,
|
|
help='Path of the output folder'
|
|
)
|
|
@click.option(
|
|
'--title', type=click.STRING,
|
|
required=False,
|
|
help='Title of output file'
|
|
)
|
|
@click.option(
|
|
'--num', type=click.IntRange(min=1),
|
|
required=False, default=1,
|
|
help='Number of runs'
|
|
)
|
|
@click.option(
|
|
'--key', type=click.INT,
|
|
required=False, default=0,
|
|
help='Key transition of pitch'
|
|
)
|
|
@click.option(
|
|
'--gender', type=click.FloatRange(min=-1, max=1),
|
|
required=False,
|
|
help='Formant shifting (gender control)'
|
|
)
|
|
@click.option(
|
|
'--seed', type=click.INT,
|
|
required=False, default=-1,
|
|
help='Random seed of the inference'
|
|
)
|
|
@click.option(
|
|
'--depth', type=click.FloatRange(min=0, max=1),
|
|
required=False,
|
|
help='Shallow diffusion depth'
|
|
)
|
|
@click.option(
|
|
'--steps', type=click.IntRange(min=1),
|
|
required=False,
|
|
help='Diffusion sampling steps'
|
|
)
|
|
@click.option(
|
|
'--mel', is_flag=True,
|
|
help='Save intermediate mel format instead of waveform'
|
|
)
|
|
def acoustic(
|
|
proj: pathlib.Path,
|
|
exp: str,
|
|
ckpt: int,
|
|
spk: str,
|
|
lang: str,
|
|
out: pathlib.Path,
|
|
title: str,
|
|
num: int,
|
|
key: int,
|
|
gender: float,
|
|
seed: int,
|
|
depth: float,
|
|
steps: int,
|
|
mel: bool
|
|
):
|
|
name = proj.stem if not title else title
|
|
if out is None:
|
|
out = proj.parent
|
|
|
|
with open(proj, 'r', encoding='utf-8') as f:
|
|
params = json.load(f)
|
|
|
|
if not isinstance(params, list):
|
|
params = [params]
|
|
|
|
if len(params) == 0:
|
|
print('The input file is empty.')
|
|
exit()
|
|
|
|
from utils.infer_utils import trans_key, parse_commandline_spk_mix
|
|
|
|
if key != 0:
|
|
params = trans_key(params, key)
|
|
key_suffix = '%+dkey' % key
|
|
if not title:
|
|
name += key_suffix
|
|
print(f'| key transition: {key:+d}')
|
|
|
|
sys.argv = [
|
|
sys.argv[0],
|
|
'--exp_name',
|
|
exp,
|
|
'--infer'
|
|
]
|
|
from utils.hparams import set_hparams, hparams
|
|
set_hparams()
|
|
|
|
# Check for vocoder path
|
|
assert mel or (root_dir / hparams['vocoder_ckpt']).exists(), \
|
|
f'Vocoder ckpt \'{hparams["vocoder_ckpt"]}\' not found. ' \
|
|
f'Please put it to the checkpoints directory to run inference.'
|
|
|
|
# For compatibility:
|
|
# migrate timesteps, K_step, K_step_infer, diff_speedup to time_scale_factor, T_start, T_start_infer, sampling_steps
|
|
if 'diff_speedup' not in hparams and 'pndm_speedup' in hparams:
|
|
hparams['diff_speedup'] = hparams['pndm_speedup']
|
|
if 'T_start' not in hparams:
|
|
hparams['T_start'] = 1 - hparams['K_step'] / hparams['timesteps']
|
|
if 'T_start_infer' not in hparams:
|
|
hparams['T_start_infer'] = 1 - hparams['K_step_infer'] / hparams['timesteps']
|
|
if 'sampling_steps' not in hparams:
|
|
if hparams['use_shallow_diffusion']:
|
|
hparams['sampling_steps'] = hparams['K_step_infer'] // hparams['diff_speedup']
|
|
else:
|
|
hparams['sampling_steps'] = hparams['timesteps'] // hparams['diff_speedup']
|
|
if 'time_scale_factor' not in hparams:
|
|
hparams['time_scale_factor'] = hparams['timesteps']
|
|
|
|
if depth is not None:
|
|
assert depth <= 1 - hparams['T_start'], (
|
|
f"Depth should not be larger than 1 - T_start ({1 - hparams['T_start']})"
|
|
)
|
|
hparams['K_step_infer'] = round(hparams['timesteps'] * depth)
|
|
hparams['T_start_infer'] = 1 - depth
|
|
if steps is not None:
|
|
if hparams['use_shallow_diffusion']:
|
|
step_size = (1 - hparams['T_start_infer']) / steps
|
|
if 'K_step_infer' in hparams:
|
|
hparams['diff_speedup'] = round(step_size * hparams['K_step_infer'])
|
|
else:
|
|
if 'timesteps' in hparams:
|
|
hparams['diff_speedup'] = round(hparams['timesteps'] / steps)
|
|
hparams['sampling_steps'] = steps
|
|
|
|
spk_mix = parse_commandline_spk_mix(spk) if hparams['use_spk_id'] and spk is not None else None
|
|
for param in params:
|
|
if gender is not None and hparams['use_key_shift_embed']:
|
|
param['gender'] = gender
|
|
if spk_mix is not None:
|
|
param['spk_mix'] = spk_mix
|
|
if lang is not None:
|
|
param['lang'] = lang
|
|
|
|
from inference.ds_acoustic import DiffSingerAcousticInfer
|
|
infer_ins = DiffSingerAcousticInfer(load_vocoder=not mel, ckpt_steps=ckpt)
|
|
print(f'| Model: {type(infer_ins.model)}')
|
|
|
|
try:
|
|
infer_ins.run_inference(
|
|
params, out_dir=out, title=name, num_runs=num,
|
|
spk_mix=spk_mix, seed=seed, save_mel=mel
|
|
)
|
|
except KeyboardInterrupt:
|
|
exit(-1)
|
|
|
|
|
|
@main.command(help='Run DiffSinger variance model inference')
|
|
@click.argument(
|
|
'proj', type=click.Path(
|
|
exists=True, file_okay=True, dir_okay=False, readable=True,
|
|
path_type=pathlib.Path, resolve_path=True
|
|
),
|
|
metavar='DS_FILE'
|
|
)
|
|
@click.option(
|
|
'--exp', type=str,
|
|
required=True, metavar='EXP',
|
|
callback=lambda ctx, param, value: find_exp(value),
|
|
help='Selection of model'
|
|
)
|
|
@click.option(
|
|
'--ckpt', type=click.IntRange(min=0),
|
|
required=False, metavar='STEPS',
|
|
help='Selection of checkpoint training steps'
|
|
)
|
|
@click.option(
|
|
'--predict', type=click.STRING,
|
|
multiple=True, metavar='TAGS',
|
|
help='Parameters to predict'
|
|
)
|
|
@click.option(
|
|
'--spk', type=click.STRING,
|
|
required=False,
|
|
help='Speaker name or mixture of speakers'
|
|
)
|
|
@click.option(
|
|
'--lang', type=click.STRING,
|
|
required=False,
|
|
help='Default language name'
|
|
)
|
|
@click.option(
|
|
'--out', type=click.Path(
|
|
file_okay=False, dir_okay=True, path_type=pathlib.Path
|
|
),
|
|
required=False,
|
|
help='Path of the output folder'
|
|
)
|
|
@click.option(
|
|
'--title', type=click.STRING,
|
|
required=False,
|
|
help='Title of output file'
|
|
)
|
|
@click.option(
|
|
'--num', type=click.IntRange(min=1),
|
|
required=False, default=1,
|
|
help='Number of runs'
|
|
)
|
|
@click.option(
|
|
'--key', type=click.INT,
|
|
required=False, default=0,
|
|
help='Key transition of pitch'
|
|
)
|
|
@click.option(
|
|
'--expr', type=click.FloatRange(min=0, max=1),
|
|
required=False, help='Static expressiveness control'
|
|
)
|
|
@click.option(
|
|
'--seed', type=click.INT,
|
|
required=False, default=-1,
|
|
help='Random seed of the inference'
|
|
)
|
|
@click.option(
|
|
'--steps', type=click.IntRange(min=1),
|
|
required=False,
|
|
help='Diffusion sampling steps'
|
|
)
|
|
def variance(
|
|
proj: pathlib.Path,
|
|
exp: str,
|
|
ckpt: int,
|
|
spk: str,
|
|
lang: str,
|
|
predict: Tuple[str],
|
|
out: pathlib.Path,
|
|
title: str,
|
|
num: int,
|
|
key: int,
|
|
expr: float,
|
|
seed: int,
|
|
steps: int
|
|
):
|
|
name = proj.stem if not title else title
|
|
if out is None:
|
|
out = proj.parent
|
|
if (not out or out.resolve() == proj.parent.resolve()) and not title:
|
|
name += '_variance'
|
|
|
|
with open(proj, 'r', encoding='utf-8') as f:
|
|
params = json.load(f)
|
|
|
|
if not isinstance(params, list):
|
|
params = [params]
|
|
params = [OrderedDict(p) for p in params]
|
|
|
|
if len(params) == 0:
|
|
print('The input file is empty.')
|
|
exit()
|
|
|
|
from utils.infer_utils import trans_key, parse_commandline_spk_mix
|
|
|
|
if key != 0:
|
|
params = trans_key(params, key)
|
|
key_suffix = '%+dkey' % key
|
|
if not title:
|
|
name += key_suffix
|
|
print(f'| key transition: {key:+d}')
|
|
|
|
sys.argv = [
|
|
sys.argv[0],
|
|
'--exp_name',
|
|
exp,
|
|
'--infer'
|
|
]
|
|
from utils.hparams import set_hparams, hparams
|
|
set_hparams()
|
|
|
|
# For compatibility:
|
|
# migrate timesteps, K_step, K_step_infer, diff_speedup to time_scale_factor, T_start, T_start_infer, sampling_steps
|
|
if 'diff_speedup' not in hparams and 'pndm_speedup' in hparams:
|
|
hparams['diff_speedup'] = hparams['pndm_speedup']
|
|
if 'sampling_steps' not in hparams:
|
|
hparams['sampling_steps'] = hparams['timesteps'] // hparams['diff_speedup']
|
|
if 'time_scale_factor' not in hparams:
|
|
hparams['time_scale_factor'] = hparams['timesteps']
|
|
|
|
if steps is not None:
|
|
if 'timesteps' in hparams:
|
|
hparams['diff_speedup'] = round(hparams['timesteps'] / steps)
|
|
hparams['sampling_steps'] = steps
|
|
|
|
spk_mix = parse_commandline_spk_mix(spk) if hparams['use_spk_id'] and spk is not None else None
|
|
for param in params:
|
|
if expr is not None:
|
|
param['expr'] = expr
|
|
if spk_mix is not None:
|
|
param['ph_spk_mix_backup'] = param.get('ph_spk_mix')
|
|
param['spk_mix_backup'] = param.get('spk_mix')
|
|
param['ph_spk_mix'] = param['spk_mix'] = spk_mix
|
|
if lang is not None:
|
|
param['lang'] = lang
|
|
|
|
from inference.ds_variance import DiffSingerVarianceInfer
|
|
infer_ins = DiffSingerVarianceInfer(ckpt_steps=ckpt, predictions=set(predict))
|
|
print(f'| Model: {type(infer_ins.model)}')
|
|
|
|
try:
|
|
infer_ins.run_inference(
|
|
params, out_dir=out, title=name,
|
|
num_runs=num, seed=seed
|
|
)
|
|
except KeyboardInterrupt:
|
|
exit(-1)
|
|
|
|
|
|
if __name__ == '__main__':
|
|
main()
|