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 |