chore: import upstream snapshot with attribution

This commit is contained in:
wehub-resource-sync
2026-07-13 12:35:17 +08:00
commit 344816a5d8
136 changed files with 25044 additions and 0 deletions
+25
View File
@@ -0,0 +1,25 @@
import importlib
import os
import sys
from pathlib import Path
root_dir = Path(__file__).parent.parent.resolve()
os.environ['PYTHONPATH'] = str(root_dir)
sys.path.insert(0, str(root_dir))
from utils.hparams import set_hparams, hparams
set_hparams()
def binarize():
binarizer_cls = hparams["binarizer_cls"]
pkg = ".".join(binarizer_cls.split(".")[:-1])
cls_name = binarizer_cls.split(".")[-1]
binarizer_cls = getattr(importlib.import_module(pkg), cls_name)
print("| Binarizer: ", binarizer_cls)
binarizer_cls().process()
if __name__ == '__main__':
binarize()
+72
View File
@@ -0,0 +1,72 @@
import torch
import argparse
import pathlib
import re
def modify_spk_embed(spk_embed):
num_spk, hidden_size = spk_embed.shape
all_ids = set(range(num_spk))
if args.drop is not None:
drop_ids = set([int(i) for i in args.drop.split(',') if i != '']).intersection(all_ids)
else:
drop_ids = all_ids - set([int(i) for i in args.retain.split(',') if i != ''])
fill_list = None
if args.fill == 'zeros':
fill_list = [0. for _ in drop_ids]
elif args.fill == 'random':
fill_list = [torch.randn(1, hidden_size, dtype=torch.float32, device='cpu') for _ in drop_ids]
elif args.fill == 'mean':
mean = torch.mean(spk_embed, dim=0, keepdim=True)
fill_list = [mean for _ in drop_ids]
elif args.fill == 'cyclic':
retain_ids = sorted(all_ids - drop_ids)
num_retain = len(retain_ids)
fill_list = [spk_embed[retain_ids[i % num_retain], :] for i, _ in enumerate(drop_ids)]
for spk_id, fill in zip(sorted(drop_ids), fill_list):
spk_embed[spk_id, :] = fill
parser = argparse.ArgumentParser(description='Drop or edit spk_embed in a checkpoint.')
parser.add_argument('input', type=str, help='Path to the input file')
parser.add_argument('output', type=str, help='Path to the output file')
drop_retain_group = parser.add_mutually_exclusive_group()
drop_retain_group.add_argument('--drop', type=str, required=False, metavar='ID,ID,...',
help='Drop specific speaker IDs.')
drop_retain_group.add_argument('--retain', type=str, required=False, metavar='ID,ID,...',
help='Retain specific speaker IDs and drop all the others.')
parser.add_argument('--fill', type=str, required=False, default='zeros', metavar='METHOD',
choices=['zeros', 'random', 'mean', 'cyclic'],
help='Specify a filling method for the dropped embedding. '
'Available methods: zeros, random, mean, cyclic')
parser.add_argument('--overwrite', required=False, default=False,
action='store_true', help='Overwrite if the output file exists.')
args = parser.parse_args()
assert args.drop is not None or args.retain is not None, 'Either --drop or --retain should be specified.'
if args.drop and not re.fullmatch(r'(\d+)?(,\d+)*,?', args.drop):
print(f'Invalid format for --drop: \'{args.drop}\'')
exit(-1)
if args.retain and not re.fullmatch(r'(\d+)?(,\d+)*,?', args.retain):
print(f'Invalid format for --retain: \'{args.retain}\'')
exit(-1)
import torch
input_ckpt = pathlib.Path(args.input).resolve()
output_ckpt = pathlib.Path(args.output).resolve()
assert input_ckpt.exists(), 'The input file does not exist.'
assert args.overwrite or not output_ckpt.exists(), \
'The output file already exists or is the same as the input file.\n' \
'This is not recommended because spk_embed dropping scripts may not be stable, ' \
'and you may be at risk of losing your model.\n' \
'If you are sure to OVERWRITE the existing file, please re-run this script with the \'--overwrite\' argument.'
ckpt_loaded = torch.load(input_ckpt, map_location='cpu')
state_dict = ckpt_loaded['state_dict']
if 'model.fs2.spk_embed.weight' in state_dict:
modify_spk_embed(state_dict['model.fs2.spk_embed.weight'])
if 'model.spk_embed.weight' in state_dict:
modify_spk_embed(state_dict['model.spk_embed.weight'])
torch.save(ckpt_loaded, output_ckpt)
+294
View File
@@ -0,0 +1,294 @@
import os
import pathlib
import re
import sys
from typing import List
import click
import torch
root_dir = pathlib.Path(__file__).resolve().parent.parent
os.environ['PYTHONPATH'] = str(root_dir)
sys.path.insert(0, str(root_dir))
from utils.hparams import set_hparams, hparams
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
def parse_spk_settings(export_spk, freeze_spk):
if export_spk is None:
export_spk = []
else:
export_spk = list(export_spk)
from utils.infer_utils import parse_commandline_spk_mix
spk_name_pattern = r'[0-9A-Za-z_-]+'
export_spk_mix = []
for spk in export_spk:
assert '=' in spk or '|' not in spk, \
'You must specify an alias with \'NAME=\' for each speaker mix.'
if '=' in spk:
alias, mix = spk.split('=', maxsplit=1)
assert re.fullmatch(spk_name_pattern, alias) is not None, f'Invalid alias \'{alias}\' for speaker mix.'
export_spk_mix.append((alias, parse_commandline_spk_mix(mix)))
else:
export_spk_mix.append((spk, {spk: 1.0}))
freeze_spk_mix = None
if freeze_spk is not None:
assert '=' in freeze_spk or '|' not in freeze_spk, \
'You must specify an alias with \'NAME=\' for each speaker mix.'
if '=' in freeze_spk:
alias, mix = freeze_spk.split('=', maxsplit=1)
assert re.fullmatch(spk_name_pattern, alias) is not None, f'Invalid alias \'{alias}\' for speaker mix.'
freeze_spk_mix = (alias, parse_commandline_spk_mix(mix))
else:
freeze_spk_mix = (freeze_spk, {freeze_spk: 1.0})
return export_spk_mix, freeze_spk_mix
@click.group()
def main():
pass
@main.command(help='Export DiffSinger acoustic model to ONNX format.')
@click.option(
'--exp', type=click.STRING,
required=True, metavar='EXP', callback=lambda ctx, param, value: find_exp(value),
help='Choose an experiment to export.'
)
@click.option(
'--ckpt', type=click.IntRange(min=0),
required=False, metavar='STEPS',
help='Checkpoint training steps.'
)
@click.option(
'--out', type=click.Path(
dir_okay=True, file_okay=False,
path_type=pathlib.Path, resolve_path=True
),
required=False,
help='Output directory for the artifacts.'
)
@click.option(
'--freeze_gender', type=click.FloatRange(min=-1, max=1),
help='(for random pitch shifting) Freeze gender value into the model.'
)
@click.option(
'--freeze_velocity', is_flag=True,
help='(for random time stretching) Freeze default velocity value into the model.'
)
@click.option(
'--export_spk', type=click.STRING,
required=False, multiple=True,
help='(for multi-speaker models) Export one or more speaker or speaker mixture keys.'
)
@click.option(
'--freeze_spk', type=click.STRING,
required=False,
help='(for multi-speaker models) Freeze one speaker or speaker mixture into the model.'
)
def acoustic(
exp: str,
ckpt: int = None,
out: pathlib.Path = None,
freeze_gender: float = 0.,
freeze_velocity: bool = False,
export_spk: List[str] = None,
freeze_spk: str = None
):
# Validate arguments
if export_spk and freeze_spk:
print('--export_spk is exclusive to --freeze_spk.')
exit(-1)
if out is None:
out = root_dir / 'artifacts' / exp
export_spk_mix, freeze_spk_mix = parse_spk_settings(export_spk, freeze_spk)
# Load configurations
sys.argv = [
sys.argv[0],
'--exp_name',
exp,
'--infer'
]
set_hparams()
# Export artifacts
from deployment.exporters import DiffSingerAcousticExporter
print(f'| Exporter: {DiffSingerAcousticExporter}')
exporter = DiffSingerAcousticExporter(
device=torch.device('cuda' if torch.cuda.is_available() else 'cpu'),
cache_dir=root_dir / 'deployment' / 'cache',
ckpt_steps=ckpt,
freeze_gender=freeze_gender,
freeze_velocity=freeze_velocity,
export_spk=export_spk_mix,
freeze_spk=freeze_spk_mix
)
try:
exporter.export(out)
except KeyboardInterrupt:
exit(-1)
@main.command(help='Export DiffSinger variance model to ONNX format.')
@click.option(
'--exp', type=click.STRING,
required=True, metavar='EXP', callback=lambda ctx, param, value: find_exp(value),
help='Choose an experiment to export.'
)
@click.option(
'--ckpt', type=click.IntRange(min=0),
required=False, metavar='STEPS',
help='Checkpoint training steps.'
)
@click.option(
'--out', type=click.Path(
dir_okay=True, file_okay=False,
path_type=pathlib.Path, resolve_path=True
),
required=False,
help='Output directory for the artifacts.'
)
@click.option(
'--freeze_glide', is_flag=True,
help='Freeze default glide embedding into the model.'
)
@click.option(
'--freeze_expr', is_flag=True,
help='Freeze default pitch expressiveness factor into the model.'
)
@click.option(
'--export_spk', type=click.STRING,
required=False, multiple=True,
help='(for multi-speaker models) Export one or more speaker or speaker mixture keys.'
)
@click.option(
'--freeze_spk', type=click.STRING,
required=False,
help='(for multi-speaker models) Freeze one speaker or speaker mixture into the model.'
)
def variance(
exp: str,
ckpt: int = None,
out: str = None,
freeze_glide: bool = False,
freeze_expr: bool = False,
export_spk: List[str] = None,
freeze_spk: str = None
):
# Validate arguments
if export_spk and freeze_spk:
print('--export_spk is exclusive to --freeze_spk.')
exit(-1)
if out is None:
out = root_dir / 'artifacts' / exp
export_spk_mix, freeze_spk_mix = parse_spk_settings(export_spk, freeze_spk)
# Load configurations
sys.argv = [
sys.argv[0],
'--exp_name',
exp,
'--infer'
]
set_hparams()
from deployment.exporters import DiffSingerVarianceExporter
print(f'| Exporter: {DiffSingerVarianceExporter}')
exporter = DiffSingerVarianceExporter(
device=torch.device('cuda' if torch.cuda.is_available() else 'cpu'),
cache_dir=root_dir / 'deployment' / 'cache',
ckpt_steps=ckpt,
freeze_glide=freeze_glide,
freeze_expr=freeze_expr,
export_spk=export_spk_mix,
freeze_spk=freeze_spk_mix
)
try:
exporter.export(out)
except KeyboardInterrupt:
exit(-1)
@main.command(help='Export NSF-HiFiGAN vocoder model to ONNX format.')
@click.option(
'--config', type=click.Path(
exists=True, file_okay=True, dir_okay=False, readable=True,
path_type=pathlib.Path, resolve_path=True
),
required=True,
help='Specify a configuration file for the vocoder.'
)
@click.option(
'--ckpt', type=click.Path(
exists=True, file_okay=True, dir_okay=False, readable=True,
path_type=pathlib.Path, resolve_path=True
),
required=False,
help='Specify a model path of the vocoder checkpoint.'
)
@click.option(
'--out', type=click.Path(
dir_okay=True, file_okay=False,
path_type=pathlib.Path, resolve_path=True
),
required=False,
help='Output directory for the artifacts.'
)
@click.option(
'--name', type=click.STRING,
required=False, default='nsf_hifigan', show_default=False,
help='Specify filename (without suffix) of the target model file.'
)
def nsf_hifigan(
config: pathlib.Path,
ckpt: pathlib.Path = None,
out: pathlib.Path = None,
name: str = None
):
# Check arguments
if out is None:
out = root_dir / 'artifacts' / 'nsf_hifigan'
# Load configurations
set_hparams(config.as_posix())
if ckpt is None:
model_path = pathlib.Path(hparams['vocoder_ckpt']).resolve()
else:
model_path = ckpt
# Export artifacts
from deployment.exporters import NSFHiFiGANExporter
print(f'| Exporter: {NSFHiFiGANExporter}')
exporter = NSFHiFiGANExporter(
device=torch.device('cuda' if torch.cuda.is_available() else 'cpu'),
cache_dir=root_dir / 'deployment' / 'cache',
model_path=model_path,
model_name=name
)
try:
exporter.export(out)
except KeyboardInterrupt:
exit(-1)
if __name__ == '__main__':
main()
+381
View File
@@ -0,0 +1,381 @@
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()
+31
View File
@@ -0,0 +1,31 @@
import importlib
import os
import sys
from pathlib import Path
root_dir = Path(__file__).parent.parent.resolve()
os.environ['PYTHONPATH'] = str(root_dir)
sys.path.insert(0, str(root_dir))
os.environ['TORCH_CUDNN_V8_API_ENABLED'] = '1' # Prevent unacceptable slowdowns when using 16 precision
from utils.hparams import set_hparams, hparams
set_hparams()
if not hparams['nccl_p2p']:
print("Disabling NCCL P2P")
os.environ['NCCL_P2P_DISABLE'] = '1'
def run_task():
assert hparams['task_cls'] != ''
pkg = ".".join(hparams["task_cls"].split(".")[:-1])
cls_name = hparams["task_cls"].split(".")[-1]
task_cls = getattr(importlib.import_module(pkg), cls_name)
task_cls.start()
if __name__ == '__main__':
run_task()
+90
View File
@@ -0,0 +1,90 @@
# coding=utf8
import argparse
import os
import pathlib
import sys
root_dir = pathlib.Path(__file__).parent.parent.resolve()
os.environ['PYTHONPATH'] = str(root_dir)
sys.path.insert(0, str(root_dir))
import numpy as np
import torch
import tqdm
from inference.ds_acoustic import DiffSingerAcousticInfer
from utils.infer_utils import cross_fade, save_wav
from utils.hparams import set_hparams, hparams
parser = argparse.ArgumentParser(description='Run DiffSinger vocoder')
parser.add_argument('mel', type=str, help='Path to the input file')
parser.add_argument('--exp', type=str, required=False, help='Read vocoder class and path from chosen experiment')
parser.add_argument('--config', type=str, required=False, help='Read vocoder class and path from config file')
parser.add_argument('--class', type=str, required=False, help='Specify vocoder class')
parser.add_argument('--ckpt', type=str, required=False, help='Specify vocoder checkpoint path')
parser.add_argument('--out', type=str, required=False, help='Path of the output folder')
parser.add_argument('--title', type=str, required=False, help='Title of output file')
args = parser.parse_args()
mel = pathlib.Path(args.mel)
name = mel.stem if not args.title else args.title
config = None
if args.exp:
config = root_dir / 'checkpoints' / args.exp / 'config.yaml'
elif args.config:
config = pathlib.Path(args.config)
else:
assert False, 'Either argument \'--exp\' or \'--config\' should be specified.'
sys.argv = [
sys.argv[0],
'--config',
str(config)
]
set_hparams(print_hparams=False)
cls = getattr(args, 'class')
if cls:
hparams['vocoder'] = cls
if args.ckpt:
hparams['vocoder_ckpt'] = args.ckpt
out = args.out
if args.out:
out = pathlib.Path(args.out)
else:
out = mel.parent
mel_seq = torch.load(mel)
assert isinstance(mel_seq, list), 'Not a valid mel sequence.'
assert len(mel_seq) > 0, 'Mel sequence is empty.'
sample_rate = hparams['audio_sample_rate']
infer_ins = DiffSingerAcousticInfer(load_model=False)
def run_vocoder(path: pathlib.Path):
result = np.zeros(0)
current_length = 0
for seg_mel in tqdm.tqdm(mel_seq, desc='mel segment', total=len(mel_seq)):
seg_audio = infer_ins.run_vocoder(seg_mel['mel'].to(infer_ins.device), f0=seg_mel['f0'].to(infer_ins.device))
seg_audio = seg_audio.squeeze(0).cpu().numpy()
silent_length = round(seg_mel['offset'] * sample_rate) - current_length
if silent_length >= 0:
result = np.append(result, np.zeros(silent_length))
result = np.append(result, seg_audio)
else:
result = cross_fade(result, seg_audio, current_length + silent_length)
current_length = current_length + silent_length + seg_audio.shape[0]
print(f'| save audio: {path}')
save_wav(result, path, sample_rate)
os.makedirs(out, exist_ok=True)
try:
run_vocoder(out / (name + '.wav'))
except KeyboardInterrupt:
exit(-1)