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. |