Index: webrtc/modules/audio_processing/test/py_quality_assessment/quality_assessment/noise_generation.py |
diff --git a/webrtc/modules/audio_processing/test/py_quality_assessment/quality_assessment/noise_generation.py b/webrtc/modules/audio_processing/test/py_quality_assessment/quality_assessment/noise_generation.py |
deleted file mode 100644 |
index 090d3506ab8cb78bb319083a90d706fa9469e652..0000000000000000000000000000000000000000 |
--- a/webrtc/modules/audio_processing/test/py_quality_assessment/quality_assessment/noise_generation.py |
+++ /dev/null |
@@ -1,501 +0,0 @@ |
-# Copyright (c) 2017 The WebRTC project authors. All Rights Reserved. |
-# |
-# Use of this source code is governed by a BSD-style license |
-# that can be found in the LICENSE file in the root of the source |
-# tree. An additional intellectual property rights grant can be found |
-# in the file PATENTS. All contributing project authors may |
-# be found in the AUTHORS file in the root of the source tree. |
- |
-"""Noise generators producing pairs of signals intended to be used to test the |
- APM module. Each pair consists of a noisy and a reference signal. The former |
- is used as input for APM, and it is generated by adding noise to a signal. |
- The reference is the expected APM output when using the generated input. |
- |
- Throughout this file, the following naming convention is used: |
- - input signal: the clean signal (e.g., speech), |
- - noise signal: the noise to be summed up to the input signal (e.g., white |
- noise, Gaussian noise), |
- - noisy signal: input + noise. |
- The noise signal may or may not be a function of the clean signal. For |
- instance, white noise is independently generated, whereas reverberation is |
- obtained by convolving the input signal with an impulse response. |
-""" |
- |
-import logging |
-import os |
-import sys |
- |
-try: |
- import scipy.io |
-except ImportError: |
- logging.critical('Cannot import the third-party Python package scipy') |
- sys.exit(1) |
- |
-from . import data_access |
-from . import exceptions |
-from . import signal_processing |
- |
- |
-class NoiseGenerator(object): |
- """Abstract class responsible for the generation of noisy signals. |
- |
- Given a clean signal, it generates two streams named noisy signal and |
- reference. The former is the clean signal deteriorated by the noise source, |
- the latter goes through the same deterioration process, but more "gently". |
- Noisy signal and reference are produced so that the reference is the signal |
- expected at the output of the APM module when the latter is fed with the nosiy |
- signal. |
- |
- A noise generator generates one or more input-reference pairs. |
- |
- TODO(alessiob): Rename from NoiseGenerator to InputReferencePairGenerator. |
- """ |
- |
- NAME = None |
- REGISTERED_CLASSES = {} |
- |
- def __init__(self): |
- # Init dictionaries with one entry for each noise generator configuration |
- # (e.g., different SNRs). |
- # Noisy audio track files (stored separately in a cache folder). |
- self._noisy_signal_filepaths = None |
- # Path to be used for the APM simulation output files. |
- self._apm_output_paths = None |
- # Reference audio track files (stored separately in a cache folder). |
- self._reference_signal_filepaths = None |
- self.Clear() |
- |
- @classmethod |
- def RegisterClass(cls, class_to_register): |
- """Registers an NoiseGenerator implementation. |
- |
- Decorator to automatically register the classes that extend NoiseGenerator. |
- Example usage: |
- |
- @NoiseGenerator.RegisterClass |
- class IdentityGenerator(NoiseGenerator): |
- pass |
- """ |
- cls.REGISTERED_CLASSES[class_to_register.NAME] = class_to_register |
- return class_to_register |
- |
- @property |
- def config_names(self): |
- return self._noisy_signal_filepaths.keys() |
- |
- @property |
- def noisy_signal_filepaths(self): |
- return self._noisy_signal_filepaths |
- |
- @property |
- def apm_output_paths(self): |
- return self._apm_output_paths |
- |
- @property |
- def reference_signal_filepaths(self): |
- return self._reference_signal_filepaths |
- |
- def Generate( |
- self, input_signal_filepath, input_noise_cache_path, base_output_path): |
- """Generates a set of noisy input and reference audiotrack file pairs. |
- |
- This method initializes an empty set of pairs and calls the _Generate() |
- method implemented in a concrete class. |
- |
- Args: |
- input_signal_filepath: path to the clean input audio track file. |
- input_noise_cache_path: path to the cache of noisy audio track files. |
- base_output_path: base path where output is written. |
- """ |
- self.Clear() |
- self._Generate( |
- input_signal_filepath, input_noise_cache_path, base_output_path) |
- |
- def Clear(self): |
- """Clears the generated output path dictionaries. |
- """ |
- self._noisy_signal_filepaths = {} |
- self._apm_output_paths = {} |
- self._reference_signal_filepaths = {} |
- |
- def _Generate( |
- self, input_signal_filepath, input_noise_cache_path, base_output_path): |
- """Abstract method to be implemented in each concrete class. |
- """ |
- raise NotImplementedError() |
- |
- def _AddNoiseSnrPairs(self, base_output_path, noisy_mix_filepaths, |
- snr_value_pairs): |
- """Adds noisy-reference signal pairs. |
- |
- Args: |
- base_output_path: noisy tracks base output path. |
- noisy_mix_filepaths: nested dictionary of noisy signal paths organized |
- by noisy track name and SNR level. |
- snr_value_pairs: list of SNR pairs. |
- """ |
- for noise_track_name in noisy_mix_filepaths: |
- for snr_noisy, snr_refence in snr_value_pairs: |
- config_name = '{0}_{1:d}_{2:d}_SNR'.format( |
- noise_track_name, snr_noisy, snr_refence) |
- output_path = self._MakeDir(base_output_path, config_name) |
- self._AddNoiseReferenceFilesPair( |
- config_name=config_name, |
- noisy_signal_filepath=noisy_mix_filepaths[ |
- noise_track_name][snr_noisy], |
- reference_signal_filepath=noisy_mix_filepaths[ |
- noise_track_name][snr_refence], |
- output_path=output_path) |
- |
- def _AddNoiseReferenceFilesPair(self, config_name, noisy_signal_filepath, |
- reference_signal_filepath, output_path): |
- """Adds one noisy-reference signal pair. |
- |
- Args: |
- config_name: name of the APM configuration. |
- noisy_signal_filepath: path to noisy audio track file. |
- reference_signal_filepath: path to reference audio track file. |
- output_path: APM output path. |
- """ |
- assert config_name not in self._noisy_signal_filepaths |
- self._noisy_signal_filepaths[config_name] = os.path.abspath( |
- noisy_signal_filepath) |
- self._apm_output_paths[config_name] = os.path.abspath(output_path) |
- self._reference_signal_filepaths[config_name] = os.path.abspath( |
- reference_signal_filepath) |
- |
- # Save noisy and reference file paths. |
- data_access.Metadata.SaveAudioInRefPaths( |
- output_path=output_path, |
- audio_in_filepath=self._noisy_signal_filepaths[config_name], |
- audio_ref_filepath=self._reference_signal_filepaths[config_name]) |
- |
- @classmethod |
- def _MakeDir(cls, base_output_path, noise_generator_config_name): |
- output_path = os.path.join(base_output_path, noise_generator_config_name) |
- data_access.MakeDirectory(output_path) |
- return output_path |
- |
- |
-# Identity generator. |
-@NoiseGenerator.RegisterClass |
-class IdentityGenerator(NoiseGenerator): |
- """Generator that adds no noise. |
- |
- Both the noisy and the reference signals are the input signal. |
- """ |
- |
- NAME = 'identity' |
- |
- def __init__(self): |
- NoiseGenerator.__init__(self) |
- |
- def _Generate( |
- self, input_signal_filepath, input_noise_cache_path, base_output_path): |
- CONFIG_NAME = 'default' |
- output_path = self._MakeDir(base_output_path, CONFIG_NAME) |
- self._AddNoiseReferenceFilesPair( |
- config_name=CONFIG_NAME, |
- noisy_signal_filepath=input_signal_filepath, |
- reference_signal_filepath=input_signal_filepath, |
- output_path=output_path) |
- |
- |
-@NoiseGenerator.RegisterClass |
-class WhiteNoiseGenerator(NoiseGenerator): |
- """Additive white noise generator. |
- """ |
- |
- NAME = 'white' |
- |
- # Each pair indicates the clean vs. noisy and reference vs. noisy SNRs. |
- # The reference (second value of each pair) always has a lower amount of noise |
- # - i.e., the SNR is 10 dB higher. |
- _SNR_VALUE_PAIRS = [ |
- [20, 30], # Smallest noise. |
- [10, 20], |
- [5, 15], |
- [0, 10], # Largest noise. |
- ] |
- |
- _NOISY_SIGNAL_FILENAME_TEMPLATE = 'noise_{0:d}_SNR.wav' |
- |
- def __init__(self): |
- NoiseGenerator.__init__(self) |
- |
- def _Generate( |
- self, input_signal_filepath, input_noise_cache_path, base_output_path): |
- # Load the input signal. |
- input_signal = signal_processing.SignalProcessingUtils.LoadWav( |
- input_signal_filepath) |
- input_signal = signal_processing.SignalProcessingUtils.Normalize( |
- input_signal) |
- |
- # Create the noise track. |
- noise_signal = signal_processing.SignalProcessingUtils.GenerateWhiteNoise( |
- input_signal) |
- noise_signal = signal_processing.SignalProcessingUtils.Normalize( |
- noise_signal) |
- |
- # Create the noisy mixes (once for each unique SNR value). |
- noisy_mix_filepaths = {} |
- snr_values = set([snr for pair in self._SNR_VALUE_PAIRS for snr in pair]) |
- for snr in snr_values: |
- noisy_signal_filepath = os.path.join( |
- input_noise_cache_path, |
- self._NOISY_SIGNAL_FILENAME_TEMPLATE.format(snr)) |
- |
- # Create and save if not done. |
- if not os.path.exists(noisy_signal_filepath): |
- # Create noisy signal. |
- noisy_signal = signal_processing.SignalProcessingUtils.MixSignals( |
- input_signal, noise_signal, snr) |
- |
- # Save. |
- signal_processing.SignalProcessingUtils.SaveWav( |
- noisy_signal_filepath, noisy_signal) |
- |
- # Add file to the collection of mixes. |
- noisy_mix_filepaths[snr] = noisy_signal_filepath |
- |
- # Add all the noisy-reference signal pairs. |
- for snr_noisy, snr_refence in self._SNR_VALUE_PAIRS: |
- config_name = '{0:d}_{1:d}_SNR'.format(snr_noisy, snr_refence) |
- output_path = self._MakeDir(base_output_path, config_name) |
- self._AddNoiseReferenceFilesPair( |
- config_name=config_name, |
- noisy_signal_filepath=noisy_mix_filepaths[snr_noisy], |
- reference_signal_filepath=noisy_mix_filepaths[snr_refence], |
- output_path=output_path) |
- |
- |
-# TODO(alessiob): remove comment when class implemented. |
-# @NoiseGenerator.RegisterClass |
-class NarrowBandNoiseGenerator(NoiseGenerator): |
- """Additive narrow-band noise generator. |
- """ |
- |
- NAME = 'narrow_band' |
- |
- def __init__(self): |
- NoiseGenerator.__init__(self) |
- |
- def _Generate( |
- self, input_signal_filepath, input_noise_cache_path, base_output_path): |
- # TODO(alessiob): implement. |
- pass |
- |
- |
-@NoiseGenerator.RegisterClass |
-class EnvironmentalNoiseGenerator(NoiseGenerator): |
- """Additive environmental noise generator. |
- """ |
- |
- NAME = 'environmental' |
- _NOISY_SIGNAL_FILENAME_TEMPLATE = '{0}_{1:d}_SNR.wav' |
- |
- # TODO(alessiob): allow the user to store the noise tracks in a custom path. |
- _NOISE_TRACKS_PATH = os.path.join(os.getcwd(), 'noise_tracks') |
- |
- # TODO(alessiob): allow the user to have custom noise tracks. |
- # TODO(alessiob): exploit NoiseGeneratorFactory.GetInstance(). |
- _NOISE_TRACKS = [ |
- 'city.wav' |
- ] |
- |
- # Each pair indicates the clean vs. noisy and reference vs. noisy SNRs. |
- # The reference (second value of each pair) always has a lower amount of noise |
- # - i.e., the SNR is 10 dB higher. |
- _SNR_VALUE_PAIRS = [ |
- [20, 30], # Smallest noise. |
- [10, 20], |
- [5, 15], |
- [0, 10], # Largest noise. |
- ] |
- |
- def __init__(self): |
- NoiseGenerator.__init__(self) |
- |
- def _Generate( |
- self, input_signal_filepath, input_noise_cache_path, base_output_path): |
- """Generates environmental noise. |
- |
- For each noise track and pair of SNR values, the following two audio tracks |
- are created: the noisy signal and the reference signal. The former is |
- obtained by mixing the (clean) input signal to the corresponding noise |
- track enforcing the target SNR. |
- """ |
- # Init. |
- snr_values = set([snr for pair in self._SNR_VALUE_PAIRS for snr in pair]) |
- |
- # Load the input signal. |
- input_signal = signal_processing.SignalProcessingUtils.LoadWav( |
- input_signal_filepath) |
- input_signal = signal_processing.SignalProcessingUtils.Normalize( |
- input_signal) |
- |
- noisy_mix_filepaths = {} |
- for noise_track_filename in self._NOISE_TRACKS: |
- # Load the noise track. |
- noise_track_name, _ = os.path.splitext(noise_track_filename) |
- noise_track_filepath = os.path.join( |
- self._NOISE_TRACKS_PATH, noise_track_filename) |
- if not os.path.exists(noise_track_filepath): |
- logging.error('cannot find the <%s> noise track', noise_track_filename) |
- raise exceptions.FileNotFoundError() |
- |
- noise_signal = signal_processing.SignalProcessingUtils.LoadWav( |
- noise_track_filepath) |
- noise_signal = signal_processing.SignalProcessingUtils.Normalize( |
- noise_signal) |
- |
- # Create the noisy mixes (once for each unique SNR value). |
- noisy_mix_filepaths[noise_track_name] = {} |
- for snr in snr_values: |
- noisy_signal_filepath = os.path.join( |
- input_noise_cache_path, |
- self._NOISY_SIGNAL_FILENAME_TEMPLATE.format(noise_track_name, snr)) |
- |
- # Create and save if not done. |
- if not os.path.exists(noisy_signal_filepath): |
- # Create noisy signal. |
- noisy_signal = signal_processing.SignalProcessingUtils.MixSignals( |
- input_signal, noise_signal, snr) |
- |
- # Save. |
- signal_processing.SignalProcessingUtils.SaveWav( |
- noisy_signal_filepath, noisy_signal) |
- |
- # Add file to the collection of mixes. |
- noisy_mix_filepaths[noise_track_name][snr] = noisy_signal_filepath |
- |
- # Add all the noise-SNR pairs. |
- self._AddNoiseSnrPairs( |
- base_output_path, noisy_mix_filepaths, self._SNR_VALUE_PAIRS) |
- |
- |
-@NoiseGenerator.RegisterClass |
-class EchoNoiseGenerator(NoiseGenerator): |
- """Echo noise generator. |
- |
- TODO(alessiob): Rename from echo to reverberation. |
- """ |
- |
- NAME = 'echo' |
- |
- _IMPULSE_RESPONSES = { |
- 'lecture': 'air_binaural_lecture_0_0_1.mat', # Long echo. |
- 'booth': 'air_binaural_booth_0_0_1.mat', # Short echo. |
- } |
- _MAX_IMPULSE_RESPONSE_LENGTH = None |
- |
- # Each pair indicates the clean vs. noisy and reference vs. noisy SNRs. |
- # The reference (second value of each pair) always has a lower amount of noise |
- # - i.e., the SNR is 5 dB higher. |
- _SNR_VALUE_PAIRS = [ |
- [3, 8], # Smallest noise. |
- [-3, 2], # Largest noise. |
- ] |
- |
- _NOISE_TRACK_FILENAME_TEMPLATE = '{0}.wav' |
- _NOISY_SIGNAL_FILENAME_TEMPLATE = '{0}_{1:d}_SNR.wav' |
- |
- def __init__(self, aechen_ir_database_path): |
- NoiseGenerator.__init__(self) |
- self._aechen_ir_database_path = aechen_ir_database_path |
- |
- def _Generate( |
- self, input_signal_filepath, input_noise_cache_path, base_output_path): |
- """Generates echo noise. |
- |
- For each impulse response, one noise track is created. For each impulse |
- response and pair of SNR values, the following 2 audio tracks are |
- created: the noisy signal and the reference signal. The former is |
- obtained by mixing the (clean) input signal to the corresponding noise |
- track enforcing the target SNR. |
- """ |
- # Init. |
- snr_values = set([snr for pair in self._SNR_VALUE_PAIRS for snr in pair]) |
- |
- # Load the input signal. |
- input_signal = signal_processing.SignalProcessingUtils.LoadWav( |
- input_signal_filepath) |
- |
- noisy_mix_filepaths = {} |
- for impulse_response_name in self._IMPULSE_RESPONSES: |
- noise_track_filename = self._NOISE_TRACK_FILENAME_TEMPLATE.format( |
- impulse_response_name) |
- noise_track_filepath = os.path.join( |
- input_noise_cache_path, noise_track_filename) |
- noise_signal = None |
- try: |
- # Load noise track. |
- noise_signal = signal_processing.SignalProcessingUtils.LoadWav( |
- noise_track_filepath) |
- except IOError: # File not found. |
- # Generate noise track by applying the impulse response. |
- impulse_response_filepath = os.path.join( |
- self._aechen_ir_database_path, |
- self._IMPULSE_RESPONSES[impulse_response_name]) |
- noise_signal = self._GenerateNoiseTrack( |
- noise_track_filepath, input_signal, impulse_response_filepath) |
- assert noise_signal is not None |
- |
- # Create the noisy mixes (once for each unique SNR value). |
- noisy_mix_filepaths[impulse_response_name] = {} |
- for snr in snr_values: |
- noisy_signal_filepath = os.path.join( |
- input_noise_cache_path, |
- self._NOISY_SIGNAL_FILENAME_TEMPLATE.format( |
- impulse_response_name, snr)) |
- |
- # Create and save if not done. |
- if not os.path.exists(noisy_signal_filepath): |
- # Create noisy signal. |
- noisy_signal = signal_processing.SignalProcessingUtils.MixSignals( |
- input_signal, noise_signal, snr, bln_pad_shortest=True) |
- |
- # Save. |
- signal_processing.SignalProcessingUtils.SaveWav( |
- noisy_signal_filepath, noisy_signal) |
- |
- # Add file to the collection of mixes. |
- noisy_mix_filepaths[impulse_response_name][snr] = noisy_signal_filepath |
- |
- # Add all the noise-SNR pairs. |
- self._AddNoiseSnrPairs(base_output_path, noisy_mix_filepaths, |
- self._SNR_VALUE_PAIRS) |
- |
- def _GenerateNoiseTrack(self, noise_track_filepath, input_signal, |
- impulse_response_filepath): |
- """Generates noise track. |
- |
- Generate a signal by convolving input_signal with the impulse response in |
- impulse_response_filepath; then save to noise_track_filepath. |
- |
- Args: |
- noise_track_filepath: output file path for the noise track. |
- input_signal: (clean) input signal samples. |
- impulse_response_filepath: impulse response file path. |
- |
- Returns: |
- AudioSegment instance. |
- """ |
- # Load impulse response. |
- data = scipy.io.loadmat(impulse_response_filepath) |
- impulse_response = data['h_air'].flatten() |
- if self._MAX_IMPULSE_RESPONSE_LENGTH is not None: |
- logging.info('truncating impulse response from %d to %d samples', |
- len(impulse_response), self._MAX_IMPULSE_RESPONSE_LENGTH) |
- impulse_response = impulse_response[:self._MAX_IMPULSE_RESPONSE_LENGTH] |
- |
- # Apply impulse response. |
- processed_signal = ( |
- signal_processing.SignalProcessingUtils.ApplyImpulseResponse( |
- input_signal, impulse_response)) |
- |
- # Save. |
- signal_processing.SignalProcessingUtils.SaveWav( |
- noise_track_filepath, processed_signal) |
- |
- return processed_signal |