ba4be087d5
CICD NeMo / cicd-main-unit-tests (push) Blocked by required conditions
CICD NeMo / cicd-main-speech (push) Blocked by required conditions
CICD NeMo / cicd-test-container-build (push) Blocked by required conditions
CICD NeMo / cicd-import-tests (push) Blocked by required conditions
CICD NeMo / L0_Setup_Test_Data_And_Models (push) Blocked by required conditions
CICD NeMo / Nemo_CICD_Test (push) Blocked by required conditions
CICD NeMo / Coverage (e2e) (push) Blocked by required conditions
CICD NeMo / Coverage (unit-test) (push) Blocked by required conditions
CodeQL / Analyze (python) (push) Waiting to run
Create PR to main with cherry-pick from release / cherry-pick (push) Failing after 0s
CICD NeMo / pre-flight (push) Failing after 0s
CICD NeMo / configure (push) Has been skipped
Build, validate, and release Neural Modules / pre-flight (push) Failing after 1s
CICD NeMo / code-linting (push) Has been skipped
CICD NeMo / cicd-wait-in-queue (push) Waiting to run
Build, validate, and release Neural Modules / release (push) Has been skipped
Build, validate, and release Neural Modules / release-summary (push) Has been cancelled
172 lines
6.2 KiB
Python
172 lines
6.2 KiB
Python
# Copyright (c) 2021, NVIDIA CORPORATION. All rights reserved.
|
|
#
|
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
# you may not use this file except in compliance with the License.
|
|
# You may obtain a copy of the License at
|
|
#
|
|
# http://www.apache.org/licenses/LICENSE-2.0
|
|
#
|
|
# Unless required by applicable law or agreed to in writing, software
|
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
# See the License for the specific language governing permissions and
|
|
# limitations under the License.
|
|
|
|
# USAGE: python add_noise.py --input_manifest=<manifest file of original "clean" dataset>
|
|
# --noise_manifest=<manifest file poinitng to noise data>
|
|
# --out_dir=<destination directory for noisy audio and manifests>
|
|
# --snrs=<list of snrs at which noise should be added to the audio>
|
|
# --seed=<seed for random number generator>
|
|
# --num_workers=<number of parallel workers>
|
|
# To be able to reproduce the same noisy dataset, use a fixed seed and num_workers=1
|
|
|
|
import argparse
|
|
import copy
|
|
import json
|
|
import multiprocessing
|
|
import os
|
|
import random
|
|
|
|
import numpy as np
|
|
import soundfile as sf
|
|
|
|
from nemo.collections.asr.parts.preprocessing.perturb import NoisePerturbation
|
|
from nemo.collections.asr.parts.preprocessing.segment import AudioSegment
|
|
|
|
rng = None
|
|
att_factor = 0.8
|
|
save_noise = False
|
|
sample_rate = 16000
|
|
|
|
|
|
def get_out_dir_name(out_dir, input_name, noise_name, snr):
|
|
return os.path.join(out_dir, input_name, noise_name + "_" + str(snr) + "db")
|
|
|
|
|
|
def create_manifest(input_manifest, noise_manifest, snrs, out_path, save_noise):
|
|
os.makedirs(os.path.join(out_path, "manifests"), exist_ok=True)
|
|
for snr in snrs:
|
|
out_dir = get_out_dir_name(
|
|
out_path,
|
|
os.path.splitext(os.path.basename(input_manifest))[0],
|
|
os.path.splitext(os.path.basename(noise_manifest))[0],
|
|
snr,
|
|
)
|
|
out_mfst = os.path.join(
|
|
os.path.join(out_path, "manifests"),
|
|
os.path.splitext(os.path.basename(input_manifest))[0]
|
|
+ "_"
|
|
+ os.path.splitext(os.path.basename(noise_manifest))[0]
|
|
+ "_"
|
|
+ str(snr)
|
|
+ "db"
|
|
+ ".json",
|
|
)
|
|
with open(input_manifest, "r") as inf, open(out_mfst, "w") as outf:
|
|
for line in inf:
|
|
row = json.loads(line.strip())
|
|
row['audio_filepath'] = os.path.join(out_dir, os.path.basename(row['audio_filepath']))
|
|
if save_noise:
|
|
file_ext = os.path.splitext(row['audio_filepath'])[1]
|
|
noise_filename = os.path.basename(row['audio_filepath']).replace(file_ext, "_noise" + file_ext)
|
|
row['noise_filepath'] = os.path.join(out_dir, noise_filename)
|
|
outf.write(json.dumps(row) + "\n")
|
|
|
|
|
|
def process_row(row):
|
|
audio_file = row['audio_filepath']
|
|
global sample_rate
|
|
data_orig = AudioSegment.from_file(audio_file, target_sr=sample_rate, offset=0)
|
|
for snr in row['snrs']:
|
|
min_snr_db = snr
|
|
max_snr_db = snr
|
|
global att_factor
|
|
perturber = NoisePerturbation(
|
|
manifest_path=row['noise_manifest'], min_snr_db=min_snr_db, max_snr_db=max_snr_db, rng=rng
|
|
)
|
|
out_dir = get_out_dir_name(
|
|
row['out_dir'],
|
|
os.path.splitext(os.path.basename(row['input_manifest']))[0],
|
|
os.path.splitext(os.path.basename(row['noise_manifest']))[0],
|
|
snr,
|
|
)
|
|
os.makedirs(out_dir, exist_ok=True)
|
|
out_f = os.path.join(out_dir, os.path.basename(audio_file))
|
|
if os.path.exists(out_f):
|
|
continue
|
|
data = copy.deepcopy(data_orig)
|
|
perturber.perturb(data)
|
|
|
|
max_level = np.max(np.abs(data.samples))
|
|
|
|
norm_factor = att_factor / max_level
|
|
new_samples = norm_factor * data.samples
|
|
sf.write(out_f, new_samples.transpose(), sample_rate)
|
|
|
|
global save_noise
|
|
if save_noise:
|
|
noise_samples = new_samples - norm_factor * data_orig.samples
|
|
out_f_ext = os.path.splitext(out_f)[1]
|
|
out_f_noise = out_f.replace(out_f_ext, "_noise" + out_f_ext)
|
|
sf.write(out_f_noise, noise_samples.transpose(), sample_rate)
|
|
|
|
|
|
def add_noise(infile, snrs, noise_manifest, out_dir, num_workers=1):
|
|
allrows = []
|
|
|
|
with open(infile, "r") as inf:
|
|
for line in inf:
|
|
row = json.loads(line.strip())
|
|
row['snrs'] = snrs
|
|
row['out_dir'] = out_dir
|
|
row['noise_manifest'] = noise_manifest
|
|
row['input_manifest'] = infile
|
|
allrows.append(row)
|
|
pool = multiprocessing.Pool(num_workers)
|
|
pool.map(process_row, allrows)
|
|
pool.close()
|
|
print('Done!')
|
|
|
|
|
|
def main():
|
|
parser = argparse.ArgumentParser()
|
|
parser.add_argument(
|
|
"--input_manifest",
|
|
type=str,
|
|
required=True,
|
|
help="clean test set",
|
|
)
|
|
parser.add_argument("--noise_manifest", type=str, required=True, help="path to noise manifest file")
|
|
parser.add_argument("--out_dir", type=str, required=True, help="destination directory for audio and manifests")
|
|
parser.add_argument("--snrs", type=int, nargs="+", default=[0, 10, 20, 30])
|
|
parser.add_argument("--seed", type=int, default=None)
|
|
parser.add_argument("--num_workers", default=1, type=int)
|
|
parser.add_argument("--sample_rate", default=16000, type=int)
|
|
parser.add_argument(
|
|
"--attenuation_factor",
|
|
default=0.8,
|
|
type=float,
|
|
help="Attenuation factor applied on the normalized noise-added samples before writing to wave",
|
|
)
|
|
parser.add_argument(
|
|
"--save_noise", default=False, action="store_true", help="save the noise added to the input signal"
|
|
)
|
|
|
|
args = parser.parse_args()
|
|
global sample_rate
|
|
sample_rate = args.sample_rate
|
|
global att_factor
|
|
att_factor = args.attenuation_factor
|
|
global save_noise
|
|
save_noise = args.save_noise
|
|
global rng
|
|
rng = args.seed
|
|
num_workers = args.num_workers
|
|
|
|
add_noise(args.input_manifest, args.snrs, args.noise_manifest, args.out_dir, num_workers=num_workers)
|
|
create_manifest(args.input_manifest, args.noise_manifest, args.snrs, args.out_dir, args.save_noise)
|
|
|
|
|
|
if __name__ == '__main__':
|
|
main()
|