chore: import upstream snapshot with attribution
This commit is contained in:
@@ -0,0 +1,4 @@
|
||||
# Copyright (c) Facebook, Inc. and its affiliates.
|
||||
#
|
||||
# This source code is licensed under the MIT license found in the
|
||||
# LICENSE file in the root directory of this source tree.
|
||||
@@ -0,0 +1,70 @@
|
||||
# Copyright (c) Facebook, Inc. and its affiliates.
|
||||
#
|
||||
# This source code is licensed under the MIT license found in the
|
||||
# LICENSE file in the root directory of this source tree.
|
||||
|
||||
from pathlib import Path
|
||||
from typing import List, Optional
|
||||
|
||||
from examples.speech_to_text.data_utils import S2TDataConfigWriter
|
||||
|
||||
|
||||
def gen_config_yaml(
|
||||
manifest_root: Path,
|
||||
yaml_filename: str = "config.yaml",
|
||||
specaugment_policy: Optional[str] = "lb",
|
||||
feature_transform: Optional[List[str]] = None,
|
||||
input_channels: Optional[int] = 1,
|
||||
input_feat_per_channel: Optional[int] = 80,
|
||||
audio_root: str = "",
|
||||
vocoder_type: Optional[str] = None,
|
||||
vocoder_checkpoint: Optional[str] = None,
|
||||
vocoder_cfg: Optional[str] = None,
|
||||
extra=None,
|
||||
):
|
||||
manifest_root = manifest_root.absolute()
|
||||
writer = S2TDataConfigWriter(manifest_root / yaml_filename)
|
||||
|
||||
if input_channels is not None:
|
||||
writer.set_input_channels(input_channels)
|
||||
if input_feat_per_channel is not None:
|
||||
writer.set_input_feat_per_channel(input_feat_per_channel)
|
||||
specaugment_setters = {
|
||||
"lb": writer.set_specaugment_lb_policy,
|
||||
"ld": writer.set_specaugment_ld_policy,
|
||||
"sm": writer.set_specaugment_sm_policy,
|
||||
"ss": writer.set_specaugment_ss_policy,
|
||||
}
|
||||
specaugment_setter = specaugment_setters.get(specaugment_policy, None)
|
||||
if specaugment_setter is not None:
|
||||
specaugment_setter()
|
||||
|
||||
if feature_transform is None:
|
||||
feature_transform = []
|
||||
else:
|
||||
writer.set_feature_transforms("*", feature_transform)
|
||||
|
||||
if specaugment_policy is not None:
|
||||
writer.set_feature_transforms("_train", feature_transform + ["specaugment"])
|
||||
|
||||
if len(audio_root) > 0:
|
||||
writer.set_audio_root(audio_root)
|
||||
|
||||
if (
|
||||
vocoder_type is not None
|
||||
and vocoder_checkpoint is not None
|
||||
and vocoder_cfg is not None
|
||||
):
|
||||
writer.set_extra(
|
||||
{
|
||||
"vocoder": {
|
||||
"type": vocoder_type,
|
||||
"config": vocoder_cfg,
|
||||
"checkpoint": vocoder_checkpoint,
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
if extra is not None:
|
||||
writer.set_extra(extra)
|
||||
writer.flush()
|
||||
@@ -0,0 +1,169 @@
|
||||
#!/usr/bin/env python3
|
||||
# Copyright (c) Facebook, Inc. and its affiliates.
|
||||
#
|
||||
# This source code is licensed under the MIT license found in the
|
||||
# LICENSE file in the root directory of this source tree.
|
||||
|
||||
import argparse
|
||||
import logging
|
||||
import os
|
||||
from pathlib import Path
|
||||
import shutil
|
||||
import torchaudio
|
||||
|
||||
import soundfile as sf
|
||||
from tqdm import tqdm
|
||||
import pandas as pd
|
||||
|
||||
from examples.speech_synthesis.data_utils import extract_logmel_spectrogram
|
||||
from examples.speech_to_speech.preprocessing.data_utils import gen_config_yaml
|
||||
from examples.speech_to_text.data_utils import create_zip, get_zip_manifest, save_df_to_tsv
|
||||
from fairseq.data.audio.audio_utils import convert_waveform
|
||||
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
MANIFEST_COLUMNS = ["id", "src_audio", "src_n_frames", "tgt_audio", "tgt_n_frames"]
|
||||
|
||||
|
||||
def prepare_target_data(args, tgt_audios):
|
||||
feature_name = "logmelspec80"
|
||||
zip_path = args.output_root / f"{feature_name}.zip"
|
||||
if zip_path.exists():
|
||||
print(f"{zip_path} exists.")
|
||||
return zip_path
|
||||
|
||||
feature_root = args.output_root / feature_name
|
||||
feature_root.mkdir(exist_ok=True)
|
||||
|
||||
print("Extracting Mel spectrogram features...")
|
||||
for tgt_audio in tqdm(tgt_audios):
|
||||
sample_id = tgt_audio.stem
|
||||
waveform, sample_rate = torchaudio.load(tgt_audio.as_posix())
|
||||
waveform, sample_rate = convert_waveform(
|
||||
waveform, sample_rate, normalize_volume=args.normalize_volume,
|
||||
to_sample_rate=args.sample_rate
|
||||
)
|
||||
extract_logmel_spectrogram(
|
||||
waveform, sample_rate, feature_root / f"{sample_id}.npy",
|
||||
win_length=args.win_length, hop_length=args.hop_length,
|
||||
n_fft=args.n_fft, n_mels=args.n_mels, f_min=args.f_min,
|
||||
f_max=args.f_max
|
||||
)
|
||||
print("ZIPing features...")
|
||||
create_zip(feature_root, zip_path)
|
||||
shutil.rmtree(feature_root)
|
||||
|
||||
return zip_path
|
||||
|
||||
|
||||
def process(args):
|
||||
os.makedirs(args.output_root, exist_ok=True)
|
||||
|
||||
manifest = {}
|
||||
tgt_audios = []
|
||||
for split in args.data_split:
|
||||
print(f"Processing {split}...")
|
||||
|
||||
manifest[split] = {c: [] for c in MANIFEST_COLUMNS}
|
||||
missing_tgt_audios = []
|
||||
src_audios = list(args.source_dir.glob(f"{split}/*.wav"))
|
||||
for src_audio in tqdm(src_audios):
|
||||
sample_id = src_audio.stem
|
||||
|
||||
tgt_audio = args.target_dir / split / f"{sample_id}.wav"
|
||||
if not tgt_audio.is_file():
|
||||
missing_tgt_audios.append(sample_id)
|
||||
continue
|
||||
|
||||
tgt_audios.append(tgt_audio)
|
||||
|
||||
src_n_frames = sf.info(src_audio.as_posix()).frames
|
||||
manifest[split]["id"].append(sample_id)
|
||||
manifest[split]["src_audio"].append(src_audio.as_posix())
|
||||
manifest[split]["src_n_frames"].append(
|
||||
src_n_frames // 160
|
||||
) # estimation of 10-ms frame for 16kHz audio
|
||||
|
||||
print(f"Processed {len(manifest[split]['id'])} samples")
|
||||
if len(missing_tgt_audios) > 0:
|
||||
print(
|
||||
f"{len(missing_tgt_audios)} with missing target data (first 3 examples: {', '.join(missing_tgt_audios[:3])})"
|
||||
)
|
||||
|
||||
# Extract features and pack features into ZIP
|
||||
zip_path = prepare_target_data(args, tgt_audios)
|
||||
|
||||
print("Fetching ZIP manifest...")
|
||||
tgt_audio_paths, tgt_audio_lengths = get_zip_manifest(zip_path)
|
||||
|
||||
print("Generating manifest...")
|
||||
for split in args.data_split:
|
||||
print(f"Processing {split}...")
|
||||
|
||||
for sample_id in tqdm(manifest[split]["id"]):
|
||||
manifest[split]["tgt_audio"].append(tgt_audio_paths[sample_id])
|
||||
manifest[split]["tgt_n_frames"].append(tgt_audio_lengths[sample_id])
|
||||
|
||||
out_manifest = args.output_root / f"{split}.tsv"
|
||||
print(f"Writing manifest to {out_manifest}...")
|
||||
save_df_to_tsv(pd.DataFrame.from_dict(manifest[split]), out_manifest)
|
||||
|
||||
# Generate config YAML
|
||||
win_len_t = args.win_length / args.sample_rate
|
||||
hop_len_t = args.hop_length / args.sample_rate
|
||||
extra = {
|
||||
"features": {
|
||||
"type": "spectrogram+melscale+log",
|
||||
"sample_rate": args.sample_rate,
|
||||
"eps": 1e-5, "n_mels": args.n_mels, "n_fft": args.n_fft,
|
||||
"window_fn": "hann", "win_length": args.win_length,
|
||||
"hop_length": args.hop_length,
|
||||
"win_len_t": win_len_t, "hop_len_t": hop_len_t,
|
||||
"f_min": args.f_min, "f_max": args.f_max,
|
||||
"n_stft": args.n_fft // 2 + 1
|
||||
}
|
||||
}
|
||||
gen_config_yaml(
|
||||
args.output_root,
|
||||
audio_root=args.output_root.as_posix(),
|
||||
specaugment_policy="lb",
|
||||
feature_transform=["utterance_cmvn", "delta_deltas"],
|
||||
extra=extra,
|
||||
)
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument(
|
||||
"--source-dir", required=True, type=Path, help="source audio directory"
|
||||
)
|
||||
parser.add_argument(
|
||||
"--target-dir", required=True, type=Path, help="target audio directory"
|
||||
)
|
||||
parser.add_argument(
|
||||
"--data-split",
|
||||
default=["train", "valid", "test"],
|
||||
nargs="+",
|
||||
help="data split names",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--output-root", required=True, type=Path, help="output directory"
|
||||
)
|
||||
# target feature related
|
||||
parser.add_argument("--win-length", type=int, default=1024)
|
||||
parser.add_argument("--hop-length", type=int, default=256)
|
||||
parser.add_argument("--n-fft", type=int, default=1024)
|
||||
parser.add_argument("--n-mels", type=int, default=80)
|
||||
parser.add_argument("--f-min", type=int, default=20)
|
||||
parser.add_argument("--f-max", type=int, default=8000)
|
||||
parser.add_argument("--sample-rate", type=int, default=22050)
|
||||
parser.add_argument("--normalize-volume", "-n", action="store_true")
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
process(args)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,128 @@
|
||||
#!/usr/bin/env python3
|
||||
# Copyright (c) Facebook, Inc. and its affiliates.
|
||||
#
|
||||
# This source code is licensed under the MIT license found in the
|
||||
# LICENSE file in the root directory of this source tree.
|
||||
|
||||
import argparse
|
||||
import logging
|
||||
from pathlib import Path
|
||||
|
||||
import soundfile as sf
|
||||
from tqdm import tqdm
|
||||
import pandas as pd
|
||||
|
||||
from examples.speech_to_speech.preprocessing.data_utils import gen_config_yaml
|
||||
from examples.speech_to_text.data_utils import save_df_to_tsv
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
MANIFEST_COLUMNS = ["id", "src_audio", "src_n_frames", "tgt_audio", "tgt_n_frames"]
|
||||
|
||||
|
||||
def load_units(in_file):
|
||||
out = {}
|
||||
with open(in_file) as f:
|
||||
for line in f:
|
||||
sample_id, units = line.strip().split("|", 1)
|
||||
out[sample_id] = units.split()
|
||||
|
||||
return out
|
||||
|
||||
|
||||
def process_units(units, reduce=False):
|
||||
if not reduce:
|
||||
return units
|
||||
|
||||
out = [u for i, u in enumerate(units) if i == 0 or u != units[i - 1]]
|
||||
return out
|
||||
|
||||
|
||||
def process(args):
|
||||
args.output_root.mkdir(exist_ok=True)
|
||||
|
||||
print("Generating manifest...")
|
||||
for split in args.data_split:
|
||||
print(f"Processing {split}")
|
||||
|
||||
# load target units
|
||||
target_unit_data = load_units(args.target_dir / f"{split}.txt")
|
||||
|
||||
manifest = {c: [] for c in MANIFEST_COLUMNS}
|
||||
missing_tgt_audios = []
|
||||
src_audios = list(args.source_dir.glob(f"{split}/*.wav"))
|
||||
for src_audio in tqdm(src_audios):
|
||||
sample_id = src_audio.stem
|
||||
|
||||
if sample_id not in target_unit_data:
|
||||
missing_tgt_audios.append(sample_id)
|
||||
continue
|
||||
|
||||
src_n_frames = sf.info(src_audio.as_posix()).frames
|
||||
manifest["id"].append(sample_id)
|
||||
manifest["src_audio"].append(src_audio.as_posix())
|
||||
manifest["src_n_frames"].append(
|
||||
src_n_frames // 160
|
||||
) # estimation of 10-ms frame for 16kHz audio
|
||||
|
||||
target_units = process_units(target_unit_data[sample_id], args.reduce_unit)
|
||||
manifest["tgt_audio"].append(" ".join(target_units))
|
||||
manifest["tgt_n_frames"].append(len(target_units))
|
||||
|
||||
print(f"Processed {len(manifest['id'])} samples")
|
||||
if len(missing_tgt_audios) > 0:
|
||||
print(
|
||||
f"{len(missing_tgt_audios)} with missing target data (first 3 examples: {', '.join(missing_tgt_audios[:3])})"
|
||||
)
|
||||
|
||||
out_manifest = args.output_root / f"{split}.tsv"
|
||||
print(f"Writing manifest to {out_manifest}...")
|
||||
save_df_to_tsv(pd.DataFrame.from_dict(manifest), out_manifest)
|
||||
|
||||
# Generate config YAML
|
||||
gen_config_yaml(
|
||||
args.output_root,
|
||||
specaugment_policy="lb",
|
||||
feature_transform=["utterance_cmvn"],
|
||||
vocoder_type="code_hifigan",
|
||||
vocoder_checkpoint=args.vocoder_checkpoint,
|
||||
vocoder_cfg=args.vocoder_cfg,
|
||||
)
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument(
|
||||
"--source-dir", required=True, type=Path, help="source audio directory"
|
||||
)
|
||||
parser.add_argument(
|
||||
"--target-dir", required=True, type=Path, help="target audio directory"
|
||||
)
|
||||
parser.add_argument(
|
||||
"--data-split",
|
||||
default=["train", "valid", "test"],
|
||||
nargs="+",
|
||||
help="data split names",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--output-root", required=True, type=Path, help="output directory"
|
||||
)
|
||||
parser.add_argument(
|
||||
"--reduce-unit",
|
||||
action="store_true",
|
||||
help="reduce a target unit sequence to a unique unit sequence, i.e. '1 1 1 2 2' -> '1 2'",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--vocoder-checkpoint", default=None, type=str, help="vocoder checkpoint"
|
||||
)
|
||||
parser.add_argument(
|
||||
"--vocoder-cfg", default=None, type=str, help="vocoder config file"
|
||||
)
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
process(args)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user