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

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

Issue 2805653002: APM-QA tool, renaming noise generators into input-reference generators. (Closed)
Patch Set: final changes Created 3 years, 8 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
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

Powered by Google App Engine
This is Rietveld 408576698