Chromium Code Reviews| 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 |
| index 854b4065cc1f0f7c95b05cfd36ffe02841fad877..ef1eec99aa159a9ac1b65e8562d2a6fa8a90a535 100644 |
| --- 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 |
| @@ -6,6 +6,22 @@ |
| # 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 |
| from . import data_access |
| @@ -15,17 +31,13 @@ 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 trhough the same deterioration process, but more "gently". |
| + 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. |
| - This is useful since it is not realistic to expect that APM will remove all |
| - the background noise or all the echo. Hence, the process that generates the |
| - reference signal is responsible for setting realistic expectations. |
| - |
| - Finally, note that a noise source can generate multiple input-reference pairs. |
| + A noise generator generates one or more input-reference pairs. |
| """ |
| NAME = None |
| @@ -63,6 +75,10 @@ class NoiseGenerator(object): |
| def generate( |
| self, input_signal_filepath, input_noise_cache_path, base_output_path): |
| + """Generate 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. |
| + """ |
| self.clear() |
| return self._generate( |
| input_signal_filepath, input_noise_cache_path, base_output_path) |
| @@ -74,6 +90,8 @@ class NoiseGenerator(object): |
| def _generate( |
| self, input_signal_filepath, input_noise_cache_path, base_output_path): |
| + """This is an abstract method to be implemented in each concrete class. |
| + """ |
| raise NotImplementedError() |
| def _add_noise_snr_pairs(self, base_output_path, noisy_mix_filepaths, |
| @@ -154,16 +172,13 @@ class WhiteNoiseGenerator(NoiseGenerator): |
| NAME = 'white' |
| # Each pair indicates the clean vs. noisy and reference vs. noisy SNRs. |
| - # Since the implementation below only changes the gain of the noise, the |
| - # values indicate the noise-to-signal ratio. Therefore a higher value means |
| - # larger amount of noise. |
| # 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 = [ |
| - [0, -10], # Largest noise. |
| - [-5, -15], |
| - [-10, -20], |
| - [-20, -30], # Smallest noise. |
| + [20, 30], # Smallest noise. |
| + [10, 20], |
| + [5, 15], |
| + [0, 10], # Largest noise. |
| ] |
| _NOISY_SIGNAL_FILENAME_TEMPLATE = 'noise_{0:d}_SNR.wav' |
| @@ -193,7 +208,7 @@ class WhiteNoiseGenerator(NoiseGenerator): |
| if not os.path.exists(noisy_signal_filepath): |
| # Create noisy signal. |
| noisy_signal = SignalProcessingUtils.mix_signals( |
| - noise_signal, input_signal, snr) |
| + input_signal, noise_signal, snr) |
| # Save. |
| SignalProcessingUtils.save_wav(noisy_signal_filepath, noisy_signal) |
| @@ -230,22 +245,80 @@ class NarrowBandNoiseGenerator(NoiseGenerator): |
| pass |
| -# TODO(alessiob): remove comment when class implemented. |
| -# @NoiseGenerator.register_class |
| +@NoiseGenerator.register_class |
| 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. |
| + _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): |
| - # TODO(alessiob): implement. |
| - pass |
| + # Init. |
| + snr_values = set([snr for pair in self._SNR_VALUE_PAIRS for snr in pair]) |
| + |
| + # Load the input signal. |
| + input_signal = SignalProcessingUtils.load_wav(input_signal_filepath) |
| + input_signal = SignalProcessingUtils.normalize(input_signal) |
|
peah-webrtc
2017/04/03 12:15:55
It is fine for now, but I don't see why the normal
AleBzk
2017/04/03 13:41:25
Thanks for this comment. I took note of it for fur
|
| + |
| + 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) |
| + continue |
| + |
| + noise_signal = SignalProcessingUtils.load_wav(noise_track_filepath) |
| + noise_signal = 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 = SignalProcessingUtils.mix_signals( |
| + input_signal, noise_signal, snr) |
| + |
| + # Save. |
| + SignalProcessingUtils.save_wav(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._add_noise_snr_pairs( |
| + base_output_path, noisy_mix_filepaths, self._SNR_VALUE_PAIRS) |
| # TODO(alessiob): remove comment when class implemented. |