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 6d2f6e99e21e135210121a70bb0634451ca17ad0..fa6d6ada1cc048c99023f57e90f22dd2da20f550 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 |
| @@ -9,8 +9,10 @@ |
| import logging |
| import os |
| +import scipy.io |
|
kjellander_webrtc
2017/03/09 07:49:31
This is not part of the standard library. Is it po
AleBzk
2017/03/09 11:31:20
True. And in another file I also use another 3rd p
kjellander_webrtc
2017/03/09 14:05:42
This is fine. If this never is going to run on any
|
| + |
| from . import data_access |
| -from .signal_processing import SignalProcessingUtils |
| +from . import signal_processing |
| class NoiseGenerator(object): |
| """Abstract class responsible for the generation of noisy signals. |
| @@ -45,6 +47,7 @@ class NoiseGenerator(object): |
| NoiseGenerator. |
| """ |
| cls.REGISTERED_CLASSES[class_to_register.NAME] = class_to_register |
| + return class_to_register |
|
AleBzk
2017/03/06 11:29:53
Bug fix: the class decorator MUST return the class
|
| @property |
| def config_names(self): |
| @@ -175,12 +178,16 @@ class WhiteNoiseGenerator(NoiseGenerator): |
| def _generate( |
| self, input_signal_filepath, input_noise_cache_path, base_output_path): |
| # Load the input signal. |
| - input_signal = SignalProcessingUtils.load_wav(input_signal_filepath) |
| - input_signal = SignalProcessingUtils.normalize(input_signal) |
| + input_signal = signal_processing.SignalProcessingUtils.load_wav( |
| + input_signal_filepath) |
| + input_signal = signal_processing.SignalProcessingUtils.normalize( |
| + input_signal) |
| # Create the noise track. |
| - noise_signal = SignalProcessingUtils.generate_white_noise(input_signal) |
| - noise_signal = SignalProcessingUtils.normalize(noise_signal) |
| + noise_signal = signal_processing.SignalProcessingUtils.generate_white_noise( |
| + input_signal) |
| + noise_signal = signal_processing.SignalProcessingUtils.normalize( |
| + noise_signal) |
| # Create the noisy mixes (once for each unique SNR value). |
| noisy_mix_filepaths = {} |
| @@ -193,11 +200,12 @@ class WhiteNoiseGenerator(NoiseGenerator): |
| # Create and save if not done. |
| if not os.path.exists(noisy_signal_filepath): |
| # Create noisy signal. |
| - noisy_signal = SignalProcessingUtils.mix_signals( |
| + noisy_signal = signal_processing.SignalProcessingUtils.mix_signals( |
| noise_signal, input_signal, snr) |
| # Save. |
| - SignalProcessingUtils.save_wav(noisy_signal_filepath, noisy_signal) |
| + signal_processing.SignalProcessingUtils.save_wav( |
| + noisy_signal_filepath, noisy_signal) |
| # Add file to the collection of mixes. |
| noisy_mix_filepaths[snr] = noisy_signal_filepath |
| @@ -245,6 +253,7 @@ class EnvironmentalNoiseGenerator(NoiseGenerator): |
| os.path.dirname(os.path.realpath(__file__)), 'noise_tracks') |
| # TODO(alessiob): allow the user to have custom noise tracks. |
| + # TODO(alessiob): exploit NoiseGeneratorFactory.GetInstance(). |
| _NOISE_TRACKS = [ |
| 'city.wav' |
| ] |
| @@ -271,8 +280,10 @@ class EnvironmentalNoiseGenerator(NoiseGenerator): |
| 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) |
| + input_signal = signal_processing.SignalProcessingUtils.load_wav( |
| + input_signal_filepath) |
| + input_signal = signal_processing.SignalProcessingUtils.normalize( |
| + input_signal) |
| noisy_mix_filepaths = {} |
| for noise_track_filename in self._NOISE_TRACKS: |
| @@ -283,8 +294,10 @@ class EnvironmentalNoiseGenerator(NoiseGenerator): |
| if not os.path.exists(noise_track_filepath): |
| logging.error('cannot find the <%s> noise track', noise_track_filename) |
| - noise_signal = SignalProcessingUtils.load_wav(noise_track_filepath) |
| - noise_signal = SignalProcessingUtils.normalize(noise_signal) |
| + noise_signal = signal_processing.SignalProcessingUtils.load_wav( |
| + 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] = {} |
| @@ -296,11 +309,12 @@ class EnvironmentalNoiseGenerator(NoiseGenerator): |
| # Create and save if not done. |
| if not os.path.exists(noisy_signal_filepath): |
| # Create noisy signal. |
| - noisy_signal = SignalProcessingUtils.mix_signals( |
| + noisy_signal = signal_processing.SignalProcessingUtils.mix_signals( |
| noise_signal, input_signal, snr) |
| # Save. |
| - SignalProcessingUtils.save_wav(noisy_signal_filepath, noisy_signal) |
| + signal_processing.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 |
| @@ -310,8 +324,7 @@ class EnvironmentalNoiseGenerator(NoiseGenerator): |
| base_output_path, noisy_mix_filepaths, self._SNR_VALUE_PAIRS) |
| -# TODO(alessiob): remove comment when class implemented. |
| -# @NoiseGenerator.register_class |
| +@NoiseGenerator.register_class |
| class EchoNoiseGenerator(NoiseGenerator): |
| """ |
| Echo noise generator. |
| @@ -319,10 +332,105 @@ class EchoNoiseGenerator(NoiseGenerator): |
| NAME = 'echo' |
| - def __init__(self): |
| + _IMPULSE_RESPONSES = { |
|
kjellander_webrtc
2017/03/09 07:49:31
I know nothing about the signal processing involve
AleBzk
2017/03/09 11:31:20
Yup
|
| + '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. |
| + # 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 5 dB higher. |
| + _SNR_VALUE_PAIRS = [ |
| + [3, -2], # Largest noise. |
| + [-3, -8], |
| + ] |
| + |
| + _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 |
|
AleBzk
2017/03/06 11:29:53
@kjellander: the same comment I got from you about
|
| def _generate( |
|
kjellander_webrtc
2017/03/09 07:49:31
Add docstring
AleBzk
2017/03/09 11:31:19
Done.
|
| 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 = signal_processing.SignalProcessingUtils.load_wav( |
| + 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.load_wav( |
| + 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._generate_noise_track( |
| + 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.mix_signals( |
| + noise_signal, input_signal, snr, bln_pad_shortest=True) |
| + |
| + # Save. |
| + signal_processing.SignalProcessingUtils.save_wav( |
| + 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._add_noise_snr_pairs(base_output_path, noisy_mix_filepaths, |
| + self._SNR_VALUE_PAIRS) |
| + |
| + def _generate_noise_track(self, noise_track_filepath, input_signal, |
| + impulse_response_filepath): |
| + """ |
| + Generate a signal by convolving input_signal with the impulse response in |
| + impulse_response_filepath; then save to noise_track_filepath. |
|
kjellander_webrtc
2017/03/09 07:49:31
Add docstring for arguments
AleBzk
2017/03/09 11:31:20
Done.
|
| + """ |
| + # 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.apply_impulse_response( |
| + input_signal, impulse_response)) |
| + |
| + # Save. |
| + signal_processing.SignalProcessingUtils.save_wav( |
| + noise_track_filepath, processed_signal) |
| + |
| + return processed_signal |