Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(334)

Unified Diff: webrtc/modules/audio_processing/test/py_quality_assessment/quality_assessment/noise_generation.py

Issue 2718133002: APM Quality Assessment tool, environmental noise generator (Closed)
Patch Set: comments from Per addressed Created 3 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
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.

Powered by Google App Engine
This is Rietveld 408576698