From f5aa45537a4c217dd6ee758fef751f80c2d2ccc9 Mon Sep 17 00:00:00 2001 From: Roger Cheng Date: Tue, 28 Jan 2020 11:17:37 -0800 Subject: [PATCH 1/6] Add noisyspeech_synthesizer_multiprocessing.py, set default configuration to generate 500 hours, set seed for random library --- noisyspeech_synthesizer.cfg | 6 +- noisyspeech_synthesizer_multiprocessing.py | 342 +++++++++++++++++++++ noisyspeech_synthesizer_singleprocess.py | 5 +- 3 files changed, 348 insertions(+), 5 deletions(-) create mode 100644 noisyspeech_synthesizer_multiprocessing.py diff --git a/noisyspeech_synthesizer.cfg b/noisyspeech_synthesizer.cfg index cc2dfecec58..5a746456c0e 100644 --- a/noisyspeech_synthesizer.cfg +++ b/noisyspeech_synthesizer.cfg @@ -35,7 +35,7 @@ sampling_rate: 16000 audioformat: *.wav audio_length: 30 silence_length: 0.2 -total_hours: 1 +total_hours: 500 snr_lower: 0 snr_upper: 40 randomize_snr: True @@ -44,8 +44,8 @@ target_level_upper: -15 total_snrlevels: 5 clean_activity_threshold: 0.6 noise_activity_threshold: 0.0 -fileindex_start: 0 -fileindex_end: 5000 +fileindex_start: None +fileindex_end: None is_test_set: False noise_dir: .\datasets\noise speech_dir: .\datasets\clean diff --git a/noisyspeech_synthesizer_multiprocessing.py b/noisyspeech_synthesizer_multiprocessing.py new file mode 100644 index 00000000000..c2ac21084b7 --- /dev/null +++ b/noisyspeech_synthesizer_multiprocessing.py @@ -0,0 +1,342 @@ +""" +@author: chkarada +""" + +# Note that this file picks the clean speech files randomly, so it does not guarantee that all +# source files will be used + + +import os +import glob +import argparse +import ast +import configparser as CP +from itertools import repeat +import multiprocessing +from multiprocessing import Pool +import random +from random import shuffle +import librosa +import numpy as np +from audiolib import is_clipped, audioread, audiowrite, snr_mixer, activitydetector +import utils + + +PROCESSES = multiprocessing.cpu_count() +MAXTRIES = 50 +MAXFILELEN = 100 + +np.random.seed(2) +random.seed(3) + +clean_counter = None +noise_counter = None + +def init(args1, args2): + ''' store the counter for later use ''' + global clean_counter, noise_counter + clean_counter = args1 + noise_counter = args2 + + +def build_audio(is_clean, params, filenum, audio_samples_length=-1): + '''Construct an audio signal from source files''' + + fs_output = params['fs'] + silence_length = params['silence_length'] + if audio_samples_length == -1: + audio_samples_length = int(params['audio_length']*params['fs']) + + output_audio = np.zeros(0) + remaining_length = audio_samples_length + files_used = [] + clipped_files = [] + + global clean_counter, noise_counter + if is_clean: + source_files = params['cleanfilenames'] + idx_counter = clean_counter + else: + source_files = params['noisefilenames'] + idx_counter = noise_counter + + # initialize silence + silence = np.zeros(int(fs_output*silence_length)) + + # iterate through multiple clips until we have a long enough signal + tries_left = MAXTRIES + while remaining_length > 0 and tries_left > 0: + + # read next audio file and resample if necessary + with idx_counter.get_lock(): + idx_counter.value += 1 + idx = idx_counter.value % np.size(source_files) + + input_audio, fs_input = audioread(source_files[idx]) + if fs_input != fs_output: + input_audio = librosa.resample(input_audio, fs_input, fs_output) + + # if current file is longer than remaining desired length, and this is + # noise generation or this is training set, subsample it randomly + if len(input_audio) > remaining_length and (not is_clean or not params['is_test_set']): + idx_seg = np.random.randint(0, len(input_audio)-remaining_length) + input_audio = input_audio[idx_seg:idx_seg+remaining_length] + + # check for clipping, and if found move onto next file + if is_clipped(input_audio): + clipped_files.append(source_files[idx]) + tries_left -= 1 + continue + + # concatenate current input audio to output audio stream + files_used.append(source_files[idx]) + output_audio = np.append(output_audio, input_audio) + remaining_length -= len(input_audio) + + # add some silence if we have not reached desired audio length + if remaining_length > 0: + silence_len = min(remaining_length, len(silence)) + output_audio = np.append(output_audio, silence[:silence_len]) + remaining_length -= silence_len + + if tries_left == 0: + print("Audio generation failed for filenum " + str(filenum)) + return [], [], clipped_files + + return output_audio, files_used, clipped_files + + +def gen_audio(is_clean, params, filenum, audio_samples_length=-1): + '''Calls build_audio() to get an audio signal, and verify that it meets the + activity threshold''' + + clipped_files = [] + low_activity_files = [] + if audio_samples_length == -1: + audio_samples_length = int(params['audio_length']*params['fs']) + if is_clean: + activity_threshold = params['clean_activity_threshold'] + else: + activity_threshold = params['noise_activity_threshold'] + + while True: + audio, source_files, new_clipped_files = \ + build_audio(is_clean, params, filenum, audio_samples_length) + + clipped_files += new_clipped_files + if len(audio) < audio_samples_length: + continue + + if activity_threshold == 0.0: + break + + percactive = activitydetector(audio=audio) + if percactive > activity_threshold: + break + else: + low_activity_files += source_files + + return audio, source_files, clipped_files, low_activity_files + + +def main_gen(params, filenum): + '''Calls gen_audio() to generate the audio signals, verifies that they meet + the requirements, and writes the files to storage''' + + print("Generating file #" + str(filenum)) + + clean_clipped_files = [] + clean_low_activity_files = [] + noise_clipped_files = [] + noise_low_activity_files = [] + + while True: + # generate clean speech + clean, clean_source_files, clean_cf, clean_laf = \ + gen_audio(True, params, filenum) + # generate noise + noise, noise_source_files, noise_cf, noise_laf = \ + gen_audio(False, params, filenum, len(clean)) + + clean_clipped_files += clean_cf + clean_low_activity_files += clean_laf + noise_clipped_files += noise_cf + noise_low_activity_files += noise_laf + + # mix clean speech and noise + # if specified, use specified SNR value + if not params['randomize_snr']: + snr = params['snr'] + # use a randomly sampled SNR value between the specified bounds + else: + snr = np.random.randint(params['snr_lower'], params['snr_upper']) + + clean_snr, noise_snr, noisy_snr, target_level = snr_mixer(params=params, + clean=clean, + noise=noise, + snr=snr) + # Uncomment the below lines if you need segmental SNR and comment the above lines using snr_mixer + #clean_snr, noise_snr, noisy_snr, target_level = segmental_snr_mixer(params=params, + # clean=clean, + # noise=noise, + # snr=snr) + # unexpected clipping + if is_clipped(clean_snr) or is_clipped(noise_snr) or is_clipped(noisy_snr): + continue + else: + break + + # write resultant audio streams to files + hyphen = '-' + clean_source_filenamesonly = [i[:-4].split(os.path.sep)[-1] for i in clean_source_files] + clean_files_joined = hyphen.join(clean_source_filenamesonly)[:MAXFILELEN] + noise_source_filenamesonly = [i[:-4].split(os.path.sep)[-1] for i in noise_source_files] + noise_files_joined = hyphen.join(noise_source_filenamesonly)[:MAXFILELEN] + + noisyfilename = clean_files_joined + '_' + noise_files_joined + '_snr' + \ + str(snr) + '_fileid_' + str(filenum) + '.wav' + cleanfilename = 'clean_fileid_'+str(filenum)+'.wav' + noisefilename = 'noise_fileid_'+str(filenum)+'.wav' + + noisypath = os.path.join(params['noisyspeech_dir'], noisyfilename) + cleanpath = os.path.join(params['clean_proc_dir'], cleanfilename) + noisepath = os.path.join(params['noise_proc_dir'], noisefilename) + + audio_signals = [noisy_snr, clean_snr, noise_snr] + file_paths = [noisypath, cleanpath, noisepath] + + for i in range(len(audio_signals)): + try: + audiowrite(file_paths[i], audio_signals[i], params['fs']) + except Exception as e: + print(str(e)) + pass + + return clean_source_files, clean_clipped_files, clean_low_activity_files, \ + noise_source_files, noise_clipped_files, noise_low_activity_files + + +def extract_list(input_list, index): + output_list = [i[index] for i in input_list] + flat_output_list = [item for sublist in output_list for item in sublist] + flat_output_list = sorted(set(flat_output_list)) + return flat_output_list + + +def main_body(): + '''Main body of this file''' + + parser = argparse.ArgumentParser() + + # Configurations: read noisyspeech_synthesizer.cfg and gather inputs + parser.add_argument('--cfg', default='noisyspeech_synthesizer.cfg', + help='Read noisyspeech_synthesizer.cfg for all the details') + parser.add_argument('--cfg_str', type=str, default='noisy_speech') + args = parser.parse_args() + + params = dict() + params['args'] = args + cfgpath = os.path.join(os.path.dirname(__file__), args.cfg) + assert os.path.exists(cfgpath), f'No configuration file as [{cfgpath}]' + + cfg = CP.ConfigParser() + cfg._interpolation = CP.ExtendedInterpolation() + cfg.read(cfgpath) + params['cfg'] = cfg._sections[args.cfg_str] + cfg = params['cfg'] + + clean_dir = os.path.join(os.path.dirname(__file__), 'CleanSpeech') + if cfg['speech_dir'] != 'None': + clean_dir = cfg['speech_dir'] + if not os.path.exists: + assert False, ('Clean speech data is required') + + noise_dir = os.path.join(os.path.dirname(__file__), 'Noise') + if cfg['noise_dir'] != 'None': + noise_dir = cfg['noise_dir'] + if not os.path.exists: + assert False, ('Noise data is required') + + params['fs'] = int(cfg['sampling_rate']) + params['audioformat'] = cfg['audioformat'] + params['audio_length'] = float(cfg['audio_length']) + params['silence_length'] = float(cfg['silence_length']) + params['total_hours'] = float(cfg['total_hours']) + + if cfg['fileindex_start'] != 'None' and cfg['fileindex_start'] != 'None': + params['fileindex_start'] = int(cfg['fileindex_start']) + params['fileindex_end'] = int(cfg['fileindex_end']) + params['num_files'] = int(params['fileindex_end'])-int(params['fileindex_start']) + else: + params['num_files'] = int((params['total_hours']*60*60)/params['audio_length']) + + print('Number of files to be synthesized:', params['num_files']) + params['is_test_set'] = utils.str2bool(cfg['is_test_set']) + params['clean_activity_threshold'] = float(cfg['clean_activity_threshold']) + params['noise_activity_threshold'] = float(cfg['noise_activity_threshold']) + params['snr_lower'] = int(cfg['snr_lower']) + params['snr_upper'] = int(cfg['snr_upper']) + params['randomize_snr'] = utils.str2bool(cfg['randomize_snr']) + params['target_level_lower'] = int(cfg['target_level_lower']) + params['target_level_upper'] = int(cfg['target_level_upper']) + + if 'snr' in cfg.keys(): + params['snr'] = int(cfg['snr']) + else: + params['snr'] = int((params['snr_lower'] + params['snr_upper'])/2) + + params['noisyspeech_dir'] = utils.get_dir(cfg, 'noisy_destination', 'noisy') + params['clean_proc_dir'] = utils.get_dir(cfg, 'clean_destination', 'clean') + params['noise_proc_dir'] = utils.get_dir(cfg, 'noise_destination', 'noise') + + if 'speech_csv' in cfg.keys() and cfg['speech_csv'] != 'None': + cleanfilenames = pd.read_csv(cfg['speech_csv']) + cleanfilenames = cleanfilenames['filename'] + else: + cleanfilenames = glob.glob(os.path.join(clean_dir, params['audioformat'])) + params['cleanfilenames'] = cleanfilenames + shuffle(params['cleanfilenames']) + params['num_cleanfiles'] = len(params['cleanfilenames']) + + params['noisefilenames'] = glob.glob(os.path.join(noise_dir, params['audioformat'])) + shuffle(params['noisefilenames']) + + # Invoke multiple processes and fan out calls to main_gen() to these processes + global clean_counter, noise_counter + clean_counter = multiprocessing.Value('i', 0) + noise_counter = multiprocessing.Value('i', 0) + + multi_pool = multiprocessing.Pool(processes=PROCESSES, initializer = init, initargs = (clean_counter, noise_counter, )) + fileindices = range(params['num_files']) + output_lists = multi_pool.starmap(main_gen, zip(repeat(params), fileindices)) + + flat_output_lists = [] + num_lists = 6 + for i in range(num_lists): + flat_output_lists.append(extract_list(output_lists, i)) + + # Create log directory if needed, and write log files of clipped and low activity files + log_dir = utils.get_dir(cfg, 'log_dir', 'Logs') + + utils.write_log_file(log_dir, 'source_files.csv', flat_output_lists[0] + flat_output_lists[3]) + utils.write_log_file(log_dir, 'clipped_files.csv', flat_output_lists[1] + flat_output_lists[4]) + utils.write_log_file(log_dir, 'low_activity_files.csv', flat_output_lists[2] + flat_output_lists[5]) + + # Compute and print stats about percentange of clipped and low activity files + total_clean = len(flat_output_lists[0]) + len(flat_output_lists[1]) + len(flat_output_lists[2]) + total_noise = len(flat_output_lists[3]) + len(flat_output_lists[4]) + len(flat_output_lists[5]) + pct_clean_clipped = round(len(flat_output_lists[1])/total_clean*100, 1) + pct_noise_clipped = round(len(flat_output_lists[4])/total_noise*100, 1) + pct_clean_low_activity = round(len(flat_output_lists[2])/total_clean*100, 1) + pct_noise_low_activity = round(len(flat_output_lists[5])/total_noise*100, 1) + + print("Of the " + str(total_clean) + " clean speech files analyzed, " + str(pct_clean_clipped) + \ + "% had clipping, and " + str(pct_clean_low_activity) + "% had low activity " + \ + "(below " + str(params['clean_activity_threshold']*100) + "% active percentage)") + print("Of the " + str(total_noise) + " noise files analyzed, " + str(pct_noise_clipped) + \ + "% had clipping, and " + str(pct_noise_low_activity) + "% had low activity " + \ + "(below " + str(params['noise_activity_threshold']*100) + "% active percentage)") + + +if __name__ == '__main__': + main_body() diff --git a/noisyspeech_synthesizer_singleprocess.py b/noisyspeech_synthesizer_singleprocess.py index d4fb05a65f5..f5719a54cf0 100644 --- a/noisyspeech_synthesizer_singleprocess.py +++ b/noisyspeech_synthesizer_singleprocess.py @@ -23,6 +23,7 @@ MAXTRIES = 50 MAXFILELEN = 100 np.random.seed(2) +random.seed(3) def build_audio(is_clean, params, index, audio_samples_length=-1): '''Construct an audio signal from source files''' @@ -268,8 +269,8 @@ def main_body(): params['noise_activity_threshold'] = float(cfg['noise_activity_threshold']) params['snr_lower'] = int(cfg['snr_lower']) params['snr_upper'] = int(cfg['snr_upper']) - params['fileindex_start'] = int(cfg['fileindex_start']) - params['fileindex_end'] = int(cfg['fileindex_end']) + #params['fileindex_start'] = int(cfg['fileindex_start']) + #params['fileindex_end'] = int(cfg['fileindex_end']) params['randomize_snr'] = utils.str2bool(cfg['randomize_snr']) params['target_level_lower'] = int(cfg['target_level_lower']) params['target_level_upper'] = int(cfg['target_level_upper']) From 991de3578f1ee1b2268a59f0c76ec501a5caa4ee Mon Sep 17 00:00:00 2001 From: chandanka90 <42795582+chandanka90@users.noreply.github.com> Date: Thu, 30 Jan 2020 09:34:15 -0800 Subject: [PATCH 2/6] Update README.md --- README.md | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/README.md b/README.md index 76b1ee61486..0f7115f054a 100644 --- a/README.md +++ b/README.md @@ -21,6 +21,7 @@ This repository contains the datasets and scripts required for the DNS challenge * Run python **noisyspeech_synthesizer_singleprocess.py** to synthesize the data. ## Citation: +For the datasets and the DNS challenge: @misc{ch2020interspeech, title={The INTERSPEECH 2020 Deep Noise Suppression Challenge: Datasets, Subjective Speech Quality and Testing Framework}, author={Chandan K. A. Reddy and Ebrahim Beyrami and Harishchandra Dubey and Vishak Gopal and Roger Cheng and Ross Cutler and Sergiy Matusevych and Robert Aichner and Ashkan Aazami and Sebastian Braun and Puneet Rana and Sriram Srinivasan and Johannes Gehrke}, @@ -30,6 +31,17 @@ This repository contains the datasets and scripts required for the DNS challenge primaryClass={cs.SD} } +The baseline NSNet noise suppression: +@misc{xia2020weighted, + title={Weighted Speech Distortion Losses for Neural-network-based Real-time Speech Enhancement}, + author={Yangyang Xia and Sebastian Braun and Chandan K. A. Reddy and Harishchandra Dubey and Ross Cutler and Ivan Tashev}, + year={2020}, + eprint={2001.10601}, + archivePrefix={arXiv}, + primaryClass={eess.AS} +} + + # Contributing This project welcomes contributions and suggestions. Most contributions require you to agree to a From b7e0d2d0724466c085cddc32a8e5dd81be5c7dde Mon Sep 17 00:00:00 2001 From: chandanka90 <42795582+chandanka90@users.noreply.github.com> Date: Thu, 30 Jan 2020 09:35:36 -0800 Subject: [PATCH 3/6] Update README.md --- README.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 0f7115f054a..7416a6ee1e7 100644 --- a/README.md +++ b/README.md @@ -21,7 +21,8 @@ This repository contains the datasets and scripts required for the DNS challenge * Run python **noisyspeech_synthesizer_singleprocess.py** to synthesize the data. ## Citation: -For the datasets and the DNS challenge: +For the datasets and the DNS challenge:
+ @misc{ch2020interspeech, title={The INTERSPEECH 2020 Deep Noise Suppression Challenge: Datasets, Subjective Speech Quality and Testing Framework}, author={Chandan K. A. Reddy and Ebrahim Beyrami and Harishchandra Dubey and Vishak Gopal and Roger Cheng and Ross Cutler and Sergiy Matusevych and Robert Aichner and Ashkan Aazami and Sebastian Braun and Puneet Rana and Sriram Srinivasan and Johannes Gehrke}, @@ -31,7 +32,8 @@ For the datasets and the DNS challenge: primaryClass={cs.SD} } -The baseline NSNet noise suppression: +The baseline NSNet noise suppression:
+ @misc{xia2020weighted, title={Weighted Speech Distortion Losses for Neural-network-based Real-time Speech Enhancement}, author={Yangyang Xia and Sebastian Braun and Chandan K. A. Reddy and Harishchandra Dubey and Ross Cutler and Ivan Tashev}, From 62286467a1e987d05f555b32ab638999cb4a6d4e Mon Sep 17 00:00:00 2001 From: chandanka90 <42795582+chandanka90@users.noreply.github.com> Date: Thu, 30 Jan 2020 09:42:43 -0800 Subject: [PATCH 4/6] Update Readme.md --- NSNet-baseline/Readme.md | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/NSNet-baseline/Readme.md b/NSNet-baseline/Readme.md index 42f656ae392..50d932a7809 100644 --- a/NSNet-baseline/Readme.md +++ b/NSNet-baseline/Readme.md @@ -1,6 +1,6 @@ # Noise Suppression Net (NSNet) baseline inference script -* As a baseline for Interspeech 2020 Deep Noise Suppression challenge, we will use the recently developed SE method based on Recurrent Neural Network (RNN). For ease of reference, we will call this method as Noise Suppression Net (NSNet). +* As a baseline for Interspeech 2020 Deep Noise Suppression challenge, we will use the recently developed SE method based on Recurrent Neural Network (RNN). For ease of reference, we will call this method as Noise Suppression Net (NSNet). The details about this method can be found in the [published paper](https://arxiv.org/pdf/2001.10601.pdf) * This method uses log power spectra as input to predict the enhancement gain per frame using a learning machine based on Gated Recurrent Units (GRU) and fully connected layers. Please refer to the paper for more details of the method. * NSNet is computationally efficient. It only takes 0.16ms to enhance a 20ms frame on an Intel quad core i5 machine using the ONNX run time v1.1 . @@ -22,3 +22,14 @@ From the NSNet-baseline directory, run nsnet_eval_local.py with the following re - --modelpath "Specify the path to the onnx model provided" Use default values for the rest. Run to enhance the clips. + +## Citation: +The baseline NSNet noise suppression:
+@misc{xia2020weighted,
+ title={Weighted Speech Distortion Losses for Neural-network-based Real-time Speech Enhancement},
+ author={Yangyang Xia and Sebastian Braun and Chandan K. A. Reddy and Harishchandra Dubey and Ross Cutler and Ivan Tashev},
+ year={2020},
+ eprint={2001.10601},
+ archivePrefix={arXiv},
+ primaryClass={eess.AS}
+} From 0d85129d05c472c2a8fd0a07b7ef8e649b8cbbed Mon Sep 17 00:00:00 2001 From: chandanka90 <42795582+chandanka90@users.noreply.github.com> Date: Thu, 30 Jan 2020 09:43:19 -0800 Subject: [PATCH 5/6] Update Readme.md --- NSNet-baseline/Readme.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/NSNet-baseline/Readme.md b/NSNet-baseline/Readme.md index 50d932a7809..02198bd18ff 100644 --- a/NSNet-baseline/Readme.md +++ b/NSNet-baseline/Readme.md @@ -25,7 +25,7 @@ Use default values for the rest. Run to enhance the clips. ## Citation: The baseline NSNet noise suppression:
-@misc{xia2020weighted,
+@misc{xia2020weighted,
title={Weighted Speech Distortion Losses for Neural-network-based Real-time Speech Enhancement},
author={Yangyang Xia and Sebastian Braun and Chandan K. A. Reddy and Harishchandra Dubey and Ross Cutler and Ivan Tashev},
year={2020},
From 5fd2a7f421f33b9ad67884edadfde88cbb08b7c2 Mon Sep 17 00:00:00 2001 From: chandanka90 <42795582+chandanka90@users.noreply.github.com> Date: Thu, 30 Jan 2020 09:44:23 -0800 Subject: [PATCH 6/6] Update README.md --- README.md | 29 ++++++++++++++--------------- 1 file changed, 14 insertions(+), 15 deletions(-) diff --git a/README.md b/README.md index 7416a6ee1e7..1d21ef8a06b 100644 --- a/README.md +++ b/README.md @@ -23,24 +23,23 @@ This repository contains the datasets and scripts required for the DNS challenge ## Citation: For the datasets and the DNS challenge:
-@misc{ch2020interspeech, - title={The INTERSPEECH 2020 Deep Noise Suppression Challenge: Datasets, Subjective Speech Quality and Testing Framework}, +@misc{ch2020interspeech,
+ title={The INTERSPEECH 2020 Deep Noise Suppression Challenge: Datasets, Subjective Speech Quality and Testing Framework},
author={Chandan K. A. Reddy and Ebrahim Beyrami and Harishchandra Dubey and Vishak Gopal and Roger Cheng and Ross Cutler and Sergiy Matusevych and Robert Aichner and Ashkan Aazami and Sebastian Braun and Puneet Rana and Sriram Srinivasan and Johannes Gehrke}, - year={2020}, - eprint={2001.08662}, - archivePrefix={arXiv}, - primaryClass={cs.SD} + year={2020},
+ eprint={2001.08662},
+ archivePrefix={arXiv},
+ primaryClass={cs.SD}
} -The baseline NSNet noise suppression:
- -@misc{xia2020weighted, - title={Weighted Speech Distortion Losses for Neural-network-based Real-time Speech Enhancement}, - author={Yangyang Xia and Sebastian Braun and Chandan K. A. Reddy and Harishchandra Dubey and Ross Cutler and Ivan Tashev}, - year={2020}, - eprint={2001.10601}, - archivePrefix={arXiv}, - primaryClass={eess.AS} +The baseline NSNet noise suppression:
+@misc{xia2020weighted,
+ title={Weighted Speech Distortion Losses for Neural-network-based Real-time Speech Enhancement},
+ author={Yangyang Xia and Sebastian Braun and Chandan K. A. Reddy and Harishchandra Dubey and Ross Cutler and Ivan Tashev},
+ year={2020},
+ eprint={2001.10601},
+ archivePrefix={arXiv},
+ primaryClass={eess.AS}
}