APM-QA tool, renaming noise generators into input-reference generators.

This CL changes the name of classes, methods and variables making using "noise generator".
This naming is replaced with "input-reference generator" which is more descriptive of the actual role.
Comments, CSS class and HTML item names have also been changed.
Consistency for variable names has been verified and the style checked with pylint.

BUG=webrtc:7218

Review-Url: https://codereview.webrtc.org/2805653002
Cr-Commit-Position: refs/heads/master@{#17639}
This commit is contained in:
alessiob 2017-04-11 01:06:28 -07:00 committed by Commit bot
parent 9765370416
commit 93cda2ebde
15 changed files with 307 additions and 285 deletions

View File

@ -42,14 +42,14 @@ copy("lib") {
"quality_assessment/evaluation.py", "quality_assessment/evaluation.py",
"quality_assessment/exceptions.py", "quality_assessment/exceptions.py",
"quality_assessment/export.py", "quality_assessment/export.py",
"quality_assessment/noise_generation.py",
"quality_assessment/noise_generation_factory.py",
"quality_assessment/noise_generation_unittest.py",
"quality_assessment/results.css", "quality_assessment/results.css",
"quality_assessment/results.js", "quality_assessment/results.js",
"quality_assessment/signal_processing.py", "quality_assessment/signal_processing.py",
"quality_assessment/signal_processing_unittest.py", "quality_assessment/signal_processing_unittest.py",
"quality_assessment/simulation.py", "quality_assessment/simulation.py",
"quality_assessment/test_data_generation.py",
"quality_assessment/test_data_generation_factory.py",
"quality_assessment/test_data_generation_unittest.py",
] ]
visibility = [ ":*" ] # Only targets in this file can depend on this. visibility = [ ":*" ] # Only targets in this file can depend on this.
outputs = [ outputs = [

View File

@ -46,11 +46,11 @@ reference one used for evaluation.
- `out/Default/py_quality_assessment/noise_tracks` (*1, *2) - `out/Default/py_quality_assessment/noise_tracks` (*1, *2)
(*1) You can use custom files as long as they are mono tracks sampled at 48kHz (*1) You can use custom files as long as they are mono tracks sampled at 48kHz
encoded in the 16 bit signed format (it is recommended that the tracks are encoded in the 16 bit signed format (it is recommended that the tracks are
converted and exported with Audacity). converted and exported with Audacity).
(*2) Adapt `EnvironmentalNoiseGenerator._NOISE_TRACKS` accordingly in (*2) Adapt `EnvironmentalNoiseTestDataGenerator._NOISE_TRACKS` accordingly in
`out/Default/py_quality_assessment/quality_assessment/noise_generation.py`. `out/Default/py_quality_assessment/quality_assessment/test_data_generation.py`.
## Usage (scores computation) ## Usage (scores computation)
@ -72,7 +72,7 @@ export separate reports. In this case, you can use the
- Use regular expressions to select/filter out scores by - Use regular expressions to select/filter out scores by
- APM configurations: `--config_names, -c` - APM configurations: `--config_names, -c`
- probing signals: `--input_names, -i` - probing signals: `--input_names, -i`
- noise generators: `--noise_generators, -n` - test data generators: `--test_data_generators, -t`
- scores: `--eval_scores, -e` - scores: `--eval_scores, -e`
- Assign a suffix to the report name using `-f <suffix>` - Assign a suffix to the report name using `-f <suffix>`

View File

@ -8,7 +8,7 @@
# be found in the AUTHORS file in the root of the source tree. # be found in the AUTHORS file in the root of the source tree.
"""Perform APM module quality assessment on one or more input files using one or """Perform APM module quality assessment on one or more input files using one or
more audioproc_f configuration files and one or more noise generators. more audioproc_f configuration files and one or more test data generators.
Usage: apm_quality_assessment.py -i audio1.wav [audio2.wav ...] Usage: apm_quality_assessment.py -i audio1.wav [audio2.wav ...]
-c cfg1.json [cfg2.json ...] -c cfg1.json [cfg2.json ...]
@ -22,11 +22,12 @@ import logging
import sys import sys
import quality_assessment.eval_scores as eval_scores import quality_assessment.eval_scores as eval_scores
import quality_assessment.noise_generation as noise_generation import quality_assessment.test_data_generation as test_data_generation
import quality_assessment.simulation as simulation import quality_assessment.simulation as simulation
_NOISE_GENERATOR_CLASSES = noise_generation.NoiseGenerator.REGISTERED_CLASSES _TEST_DATA_GENERATOR_CLASSES = (
_NOISE_GENERATORS_NAMES = _NOISE_GENERATOR_CLASSES.keys() test_data_generation.TestDataGenerator.REGISTERED_CLASSES)
_TEST_DATA_GENERATORS_NAMES = _TEST_DATA_GENERATOR_CLASSES.keys()
_EVAL_SCORE_WORKER_CLASSES = eval_scores.EvaluationScore.REGISTERED_CLASSES _EVAL_SCORE_WORKER_CLASSES = eval_scores.EvaluationScore.REGISTERED_CLASSES
_EVAL_SCORE_WORKER_NAMES = _EVAL_SCORE_WORKER_CLASSES.keys() _EVAL_SCORE_WORKER_NAMES = _EVAL_SCORE_WORKER_CLASSES.keys()
@ -38,8 +39,8 @@ def _InstanceArgumentsParser():
""" """
parser = argparse.ArgumentParser(description=( parser = argparse.ArgumentParser(description=(
'Perform APM module quality assessment on one or more input files using ' 'Perform APM module quality assessment on one or more input files using '
'one or more audioproc_f configuration files and one or more noise ' 'one or more audioproc_f configuration files and one or more '
'generators.')) 'test data generators.'))
parser.add_argument('-c', '--config_files', nargs='+', required=False, parser.add_argument('-c', '--config_files', nargs='+', required=False,
help=('path to the configuration files defining the ' help=('path to the configuration files defining the '
@ -50,10 +51,10 @@ def _InstanceArgumentsParser():
parser.add_argument('-i', '--input_files', nargs='+', required=True, parser.add_argument('-i', '--input_files', nargs='+', required=True,
help='path to the input wav files (one or more)') help='path to the input wav files (one or more)')
parser.add_argument('-n', '--noise_generators', nargs='+', required=False, parser.add_argument('-t', '--test_data_generators', nargs='+', required=False,
help='custom list of noise generators to use', help='custom list of test data generators to use',
choices=_NOISE_GENERATORS_NAMES, choices=_TEST_DATA_GENERATORS_NAMES,
default=_NOISE_GENERATORS_NAMES) default=_TEST_DATA_GENERATORS_NAMES)
parser.add_argument('-e', '--eval_scores', nargs='+', required=False, parser.add_argument('-e', '--eval_scores', nargs='+', required=False,
help='custom list of evaluation scores to use', help='custom list of evaluation scores to use',
@ -88,7 +89,7 @@ def main():
simulator.Run( simulator.Run(
config_filepaths=args.config_files, config_filepaths=args.config_files,
input_filepaths=args.input_files, input_filepaths=args.input_files,
noise_generator_names=args.noise_generators, test_data_generator_names=args.test_data_generators,
eval_score_names=args.eval_scores, eval_score_names=args.eval_scores,
output_dir=args.output_dir) output_dir=args.output_dir)

View File

@ -31,13 +31,13 @@ else
exit 1 exit 1
fi fi
# Customize probing signals, noise sources and scores if needed. # Customize probing signals, test data generators and scores if needed.
PROBING_SIGNALS=(probing_signals/*.wav) PROBING_SIGNALS=(probing_signals/*.wav)
NOISE_SOURCES=( \ TEST_DATA_GENERATORS=( \
"identity" \ "identity" \
"white" \ "white_noise" \
"environmental" \ "environmental_noise" \
"echo" \ "reverberation" \
) )
SCORES=( \ SCORES=( \
"polqa" \ "polqa" \
@ -57,22 +57,22 @@ if [ ! -d ${OUTPUT_PATH} ]; then
mkdir ${OUTPUT_PATH} mkdir ${OUTPUT_PATH}
fi fi
# Start one process for each "probing signal"-"noise source" pair. # Start one process for each "probing signal"-"test data source" pair.
chmod +x apm_quality_assessment.py chmod +x apm_quality_assessment.py
for probing_signal_filepath in "${PROBING_SIGNALS[@]}" ; do for probing_signal_filepath in "${PROBING_SIGNALS[@]}" ; do
probing_signal_name="$(basename $probing_signal_filepath)" probing_signal_name="$(basename $probing_signal_filepath)"
probing_signal_name="${probing_signal_name%.*}" probing_signal_name="${probing_signal_name%.*}"
for noise_source_name in "${NOISE_SOURCES[@]}" ; do for test_data_gen_name in "${TEST_DATA_GENERATORS[@]}" ; do
LOG_FILE="${OUTPUT_PATH}/apm_qa-${probing_signal_name}-"` LOG_FILE="${OUTPUT_PATH}/apm_qa-${probing_signal_name}-"`
`"${noise_source_name}.log" `"${test_data_gen_name}.log"
echo "Starting ${probing_signal_name} ${noise_source_name} "` echo "Starting ${probing_signal_name} ${test_data_gen_name} "`
`"(see ${LOG_FILE})" `"(see ${LOG_FILE})"
./apm_quality_assessment.py \ ./apm_quality_assessment.py \
--polqa_path ${POLQA_PATH}\ --polqa_path ${POLQA_PATH}\
--air_db_path ${AECHEN_IR_DATABASE_PATH}\ --air_db_path ${AECHEN_IR_DATABASE_PATH}\
-i ${probing_signal_filepath} \ -i ${probing_signal_filepath} \
-o ${OUTPUT_PATH} \ -o ${OUTPUT_PATH} \
-n ${noise_source_name} \ -t ${test_data_gen_name} \
-c "${APM_CONFIGS[@]}" \ -c "${APM_CONFIGS[@]}" \
-e "${SCORES[@]}" > $LOG_FILE 2>&1 & -e "${SCORES[@]}" > $LOG_FILE 2>&1 &
done done

View File

@ -26,7 +26,7 @@ import quality_assessment.export as export
# Regular expressions used to derive score descriptors from file paths. # Regular expressions used to derive score descriptors from file paths.
RE_CONFIG_NAME = re.compile(r'cfg-(.+)') RE_CONFIG_NAME = re.compile(r'cfg-(.+)')
RE_INPUT_NAME = re.compile(r'input-(.+)') RE_INPUT_NAME = re.compile(r'input-(.+)')
RE_NOISE_NAME = re.compile(r'noise-(.+)') RE_TEST_DATA_GEN_NAME = re.compile(r'gen-(.+)')
RE_SCORE_NAME = re.compile(r'score-(.+)\.txt') RE_SCORE_NAME = re.compile(r'score-(.+)\.txt')
@ -52,9 +52,9 @@ def _InstanceArgumentsParser():
help=('regular expression to filter the probing signal ' help=('regular expression to filter the probing signal '
'names')) 'names'))
parser.add_argument('-n', '--noise_generators', type=re.compile, parser.add_argument('-t', '--test_data_generators', type=re.compile,
help=('regular expression to filter the noise generator ' help=('regular expression to filter the test data '
'names')) 'generator names'))
parser.add_argument('-e', '--eval_scores', type=re.compile, parser.add_argument('-e', '--eval_scores', type=re.compile,
help=('regular expression to filter the evaluation score ' help=('regular expression to filter the evaluation score '
@ -71,29 +71,32 @@ def _GetScoreDescriptors(score_filepath):
Returns: Returns:
A tuple of strings (APM configuration name, input audio track name, A tuple of strings (APM configuration name, input audio track name,
noise generator name, noise generator parameters name, evaluation score test data generator name, test data generator parameters name,
name). evaluation score name).
""" """
config_name, input_name, noise_name, noise_params, score_name = ( (config_name, input_name, test_data_gen_name, test_data_gen_params,
score_filepath.split(os.sep)[-5:]) score_name) = score_filepath.split(os.sep)[-5:]
config_name = RE_CONFIG_NAME.match(config_name).groups(0)[0] config_name = RE_CONFIG_NAME.match(config_name).groups(0)[0]
input_name = RE_INPUT_NAME.match(input_name).groups(0)[0] input_name = RE_INPUT_NAME.match(input_name).groups(0)[0]
noise_name = RE_NOISE_NAME.match(noise_name).groups(0)[0] test_data_gen_name = RE_TEST_DATA_GEN_NAME.match(
test_data_gen_name).groups(0)[0]
score_name = RE_SCORE_NAME.match(score_name).groups(0)[0] score_name = RE_SCORE_NAME.match(score_name).groups(0)[0]
return config_name, input_name, noise_name, noise_params, score_name return (config_name, input_name, test_data_gen_name, test_data_gen_params,
score_name)
def _ExcludeScore(config_name, input_name, noise_name, score_name, args): def _ExcludeScore(config_name, input_name, test_data_gen_name, score_name,
args):
"""Decides whether excluding a score. """Decides whether excluding a score.
Given a score descriptor, encoded in config_name, input_name, noise_name, and Given a score descriptor, encoded in config_name, input_name,
score_name, use the corresponding regular expressions to determine if the test_data_gen_name and score_name, use the corresponding regular expressions
score should be excluded. to determine if the score should be excluded.
Args: Args:
config_name: APM configuration name. config_name: APM configuration name.
input_name: input audio track name. input_name: input audio track name.
noise_name: noise generator name. test_data_gen_name: test data generator name.
score_name: evaluation score name. score_name: evaluation score name.
args: parsed arguments. args: parsed arguments.
@ -103,7 +106,7 @@ def _ExcludeScore(config_name, input_name, noise_name, score_name, args):
value_regexpr_pairs = [ value_regexpr_pairs = [
(config_name, args.config_names), (config_name, args.config_names),
(input_name, args.input_names), (input_name, args.input_names),
(noise_name, args.noise_generators), (test_data_gen_name, args.test_data_generators),
(score_name, args.eval_scores), (score_name, args.eval_scores),
] ]
@ -143,32 +146,34 @@ def main():
# Find score files in the output path. # Find score files in the output path.
src_path = os.path.join( src_path = os.path.join(
args.output_dir, 'cfg-*', 'input-*', 'noise-*', '*', 'score-*.txt') args.output_dir, 'cfg-*', 'input-*', 'gen-*', '*', 'score-*.txt')
logging.debug(src_path) logging.debug(src_path)
for score_filepath in glob.iglob(src_path): for score_filepath in glob.iglob(src_path):
# Extract score descriptors from the path. # Extract score descriptors from the path.
config_name, input_name, noise_name, noise_params, score_name = ( (config_name, input_name, test_data_gen_name, test_data_gen_params,
_GetScoreDescriptors(score_filepath)) score_name) = _GetScoreDescriptors(score_filepath)
# Ignore the score if required. # Ignore the score if required.
if _ExcludeScore(config_name, input_name, noise_name, score_name, args): if _ExcludeScore(
config_name, input_name, test_data_gen_name, score_name, args):
logging.info('ignored score: %s %s %s %s', logging.info('ignored score: %s %s %s %s',
config_name, input_name, noise_name, score_name) config_name, input_name, test_data_gen_name, score_name)
continue continue
# Get metadata. # Get metadata.
score_path, _ = os.path.split(score_filepath) score_path, _ = os.path.split(score_filepath)
audio_in_filepath, audio_ref_filepath = ( audio_in_filepath, audio_ref_filepath = (
data_access.Metadata.LoadAudioInRefPaths(score_path)) data_access.Metadata.LoadAudioTestDataPaths(score_path))
audio_out_filepath = os.path.abspath(os.path.join( audio_out_filepath = os.path.abspath(os.path.join(
score_path, audioproc_wrapper.AudioProcWrapper.OUTPUT_FILENAME)) score_path, audioproc_wrapper.AudioProcWrapper.OUTPUT_FILENAME))
# Add the score to the nested dictionary. # Add the score to the nested dictionary.
scores[score_name][config_name][input_name][noise_name][noise_params] = { scores[score_name][config_name][input_name][test_data_gen_name][
'score': data_access.ScoreFile.Load(score_filepath), test_data_gen_params] = {
'audio_in_filepath': audio_in_filepath, 'score': data_access.ScoreFile.Load(score_filepath),
'audio_out_filepath': audio_out_filepath, 'audio_in_filepath': audio_in_filepath,
'audio_ref_filepath': audio_ref_filepath, 'audio_out_filepath': audio_out_filepath,
'audio_ref_filepath': audio_ref_filepath,
} }
# Export. # Export.

View File

@ -31,26 +31,27 @@ class Metadata(object):
def __init__(self): def __init__(self):
pass pass
_AUDIO_IN_REF_FILENAME = 'audio_in_ref.txt' _AUDIO_TEST_DATA_FILENAME = 'audio_test_data.txt'
@classmethod @classmethod
def LoadAudioInRefPaths(cls, metadata_path): def LoadAudioTestDataPaths(cls, metadata_path):
"""Loads the input and the reference audio track paths. """Loads the input and the reference audio track paths.
Args: Args:
metadata_path: path to the directory containing the metadata file. metadata_path: path to the directory containing the metadata file.
Returns: Returns:
Pair of metadata file paths for the input and output audio tracks. Tuple with the paths to the input and output audio tracks.
""" """
metadata_filepath = os.path.join(metadata_path, cls._AUDIO_IN_REF_FILENAME) metadata_filepath = os.path.join(
metadata_path, cls._AUDIO_TEST_DATA_FILENAME)
with open(metadata_filepath) as f: with open(metadata_filepath) as f:
audio_in_filepath = f.readline().strip() audio_in_filepath = f.readline().strip()
audio_ref_filepath = f.readline().strip() audio_ref_filepath = f.readline().strip()
return audio_in_filepath, audio_ref_filepath return audio_in_filepath, audio_ref_filepath
@classmethod @classmethod
def SaveAudioInRefPaths(cls, output_path, audio_in_filepath, def SaveAudioTestDataPaths(cls, output_path, audio_in_filepath,
audio_ref_filepath): audio_ref_filepath):
"""Saves the input and the reference audio track paths. """Saves the input and the reference audio track paths.
@ -59,7 +60,7 @@ class Metadata(object):
audio_in_filepath: path to the input audio track file. audio_in_filepath: path to the input audio track file.
audio_ref_filepath: path to the reference audio track file. audio_ref_filepath: path to the reference audio track file.
""" """
output_filepath = os.path.join(output_path, cls._AUDIO_IN_REF_FILENAME) output_filepath = os.path.join(output_path, cls._AUDIO_TEST_DATA_FILENAME)
with open(output_filepath, 'w') as f: with open(output_filepath, 'w') as f:
f.write('{}\n{}\n'.format(audio_in_filepath, audio_ref_filepath)) f.write('{}\n{}\n'.format(audio_in_filepath, audio_ref_filepath))

View File

@ -71,7 +71,7 @@ class EvaluationScore(object):
self._tested_signal_filepath = filepath self._tested_signal_filepath = filepath
def Run(self, output_path): def Run(self, output_path):
"""Extracts the score for the set input-reference pair. """Extracts the score for the set test data pair.
Args: Args:
output_path: path to the directory where the output is written. output_path: path to the directory where the output is written.

View File

@ -29,8 +29,8 @@ class HtmlExport(object):
_NEW_LINE = '\n' _NEW_LINE = '\n'
def __init__(self, output_filepath): def __init__(self, output_filepath):
self._noise_names = None self._test_data_generator_names = None
self._noise_params = None self._test_data_generator_params = None
self._output_filepath = output_filepath self._output_filepath = output_filepath
def Export(self, scores): def Export(self, scores):
@ -182,18 +182,20 @@ class HtmlExport(object):
Returns: Returns:
A string with the HTML of a table body cell. A string with the HTML of a table body cell.
""" """
# Init noise generator names and noise parameters cache (if not done). # Init test data generator names and parameters cache (if not done).
if self._noise_names is None: if self._test_data_generator_names is None:
self._noise_names = sorted(scores.keys()) self._test_data_generator_names = sorted(scores.keys())
self._noise_params = {noise_name: sorted(scores[noise_name].keys()) for ( self._test_data_generator_params = {test_data_generator_name: sorted(
noise_name) in self._noise_names} scores[test_data_generator_name].keys()) for (
test_data_generator_name) in self._test_data_generator_names}
# For each noisy input (that is a pair of noise generator name and noise # For each noisy input (that is a pair of test data generator and
# generator parameters), add an item with the score and its metadata. # generator parameters), add an item with the score and its metadata.
items = [] items = []
for name_index, noise_name in enumerate(self._noise_names): for name_index, test_data_generator_name in enumerate(
for params_index, noise_params in enumerate( self._test_data_generator_names):
self._noise_params[noise_name]): for params_index, test_data_generator_params in enumerate(
self._test_data_generator_params[test_data_generator_name]):
# Init. # Init.
score_value = '?' score_value = '?'
@ -201,29 +203,29 @@ class HtmlExport(object):
# Extract score value and its metadata. # Extract score value and its metadata.
try: try:
data = scores[noise_name][noise_params] data = scores[test_data_generator_name][test_data_generator_params]
score_value = '{0:f}'.format(data['score']) score_value = '{0:f}'.format(data['score'])
metadata = ( metadata = (
'<input type="hidden" name="noise_name" value="{}"/>' '<input type="hidden" name="gen_name" value="{}"/>'
'<input type="hidden" name="noise_params" value="{}"/>' '<input type="hidden" name="gen_params" value="{}"/>'
'<input type="hidden" name="audio_in" value="file://{}"/>' '<input type="hidden" name="audio_in" value="file://{}"/>'
'<input type="hidden" name="audio_out" value="file://{}"/>' '<input type="hidden" name="audio_out" value="file://{}"/>'
'<input type="hidden" name="audio_ref" value="file://{}"/>' '<input type="hidden" name="audio_ref" value="file://{}"/>'
).format( ).format(
noise_name, test_data_generator_name,
noise_params, test_data_generator_params,
data['audio_in_filepath'], data['audio_in_filepath'],
data['audio_out_filepath'], data['audio_out_filepath'],
data['audio_ref_filepath']) data['audio_ref_filepath'])
except TypeError: except TypeError:
logging.warning( logging.warning(
'missing score found: <score:%s> <config:%s> <input:%s> ' 'missing score found: <score:%s> <config:%s> <input:%s> '
'<noise:%s> <params:%s>', score_name, config_name, input_name, '<generator:%s> <params:%s>', score_name, config_name, input_name,
noise_name, noise_params) test_data_generator_name, test_data_generator_params)
# Add the score. # Add the score.
items.append( items.append(
'<div class="noise-desc">[{0:d}, {1:d}]{2}</div>' '<div class="test-data-gen-desc">[{0:d}, {1:d}]{2}</div>'
'<div class="value">{3}</div>'.format( '<div class="value">{3}</div>'.format(
name_index, params_index, metadata, score_value)) name_index, params_index, metadata, score_value))
@ -237,18 +239,20 @@ class HtmlExport(object):
def _BuildLegend(self): def _BuildLegend(self):
"""Builds the legend. """Builds the legend.
The legend details noise generator name and parameter pairs. The legend details test data generator name and parameter pairs.
Returns: Returns:
A string with a <div class="legend">...</div> HTML element. A string with a <div class="legend">...</div> HTML element.
""" """
items = [] items = []
for name_index, noise_name in enumerate(self._noise_names): for name_index, test_data_generator_name in enumerate(
for params_index, noise_params in enumerate( self._test_data_generator_names):
self._noise_params[noise_name]): for params_index, test_data_generator_params in enumerate(
items.append('<div class="noise-desc">[{0:d}, {1:d}]</div>: {2} noise, ' self._test_data_generator_params[test_data_generator_name]):
'{3}'.format(name_index, params_index, noise_name, items.append(
noise_params)) '<div class="test-data-gen-desc">[{0:d}, {1:d}]</div>: {2}, '
'{3}'.format(name_index, params_index, test_data_generator_name,
test_data_generator_params))
html = ( html = (
'<div class="legend"><div>' + '<div class="legend"><div>' +
'</div><div>'.join(items) + '</div></div>') '</div><div>'.join(items) + '</div></div>')

View File

@ -1,40 +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.
"""NoiseGenerator factory class.
"""
import logging
from . import noise_generation
class NoiseGeneratorFactory(object):
"""Factory class used to instantiate noise generator workers.
It can be used by instanciating a factory, passing parameters to the
constructor. These parameters are used to instantiate noise generator
workers.
"""
def __init__(self, aechen_ir_database_path):
self._aechen_ir_database_path = aechen_ir_database_path
def GetInstance(self, noise_generator_class):
"""Creates an NoiseGenerator instance given a class object.
Args:
noise_generator_class: NoiseGenerator class object (not an instance).
"""
logging.debug(
'factory producing a %s noise generator', noise_generator_class)
if noise_generator_class == noise_generation.EchoNoiseGenerator:
return noise_generation.EchoNoiseGenerator(self._aechen_ir_database_path)
else:
# By default, no arguments in the constructor.
return noise_generator_class()

View File

@ -49,7 +49,7 @@ table tbody tr td .value{
display: inline-block; display: inline-block;
} }
.noise-desc{ .test-data-gen-desc{
display: inline-block; display: inline-block;
margin-right: 0.3em; margin-right: 0.3em;
border: 1px solid #555; border: 1px solid #555;

View File

@ -13,8 +13,8 @@
function Inspector() { function Inspector() {
this.audioPlayer_ = new Audio(); this.audioPlayer_ = new Audio();
this.inspectorNode_ = document.createElement('div'); this.inspectorNode_ = document.createElement('div');
this.divNoiseGenerator_ = document.createElement('div'); this.divTestDataGeneratorName_ = document.createElement('div');
this.divNoiseParameters_ = document.createElement('div'); this.divTestDataGenParameters_ = document.createElement('div');
this.buttonPlayAudioIn_ = document.createElement('button'); this.buttonPlayAudioIn_ = document.createElement('button');
this.buttonPlayAudioOut_ = document.createElement('button'); this.buttonPlayAudioOut_ = document.createElement('button');
this.buttonPlayAudioRef_ = document.createElement('button'); this.buttonPlayAudioRef_ = document.createElement('button');
@ -79,15 +79,15 @@ Inspector.prototype.openInspector = function(target) {
this.selectedItem_ = target; this.selectedItem_ = target;
this.selectedItem_.classList.add('selected'); this.selectedItem_.classList.add('selected');
var target = this.selectedItem_.querySelector('.noise-desc'); var target = this.selectedItem_.querySelector('.test-data-gen-desc');
var noiseName = target.querySelector('input[name=noise_name]').value; var testDataGenName = target.querySelector('input[name=gen_name]').value;
var noiseParams = target.querySelector('input[name=noise_params]').value; var testDataGenParams = target.querySelector('input[name=gen_params]').value;
var audioIn = target.querySelector('input[name=audio_in]').value; var audioIn = target.querySelector('input[name=audio_in]').value;
var audioOut = target.querySelector('input[name=audio_out]').value; var audioOut = target.querySelector('input[name=audio_out]').value;
var audioRef = target.querySelector('input[name=audio_ref]').value; var audioRef = target.querySelector('input[name=audio_ref]').value;
this.divNoiseGenerator_.innerHTML = noiseName; this.divTestDataGeneratorName_.innerHTML = testDataGenName;
this.divNoiseParameters_.innerHTML = noiseParams; this.divTestDataGenParameters_.innerHTML = testDataGenParams;
this.audioInUrl_ = audioIn; this.audioInUrl_ = audioIn;
this.audioOutUrl_ = audioOut; this.audioOutUrl_ = audioOut;
@ -143,13 +143,14 @@ Inspector.prototype.buildInspector_ = function() {
var self = this; var self = this;
this.inspectorNode_.setAttribute('class', 'inspector'); this.inspectorNode_.setAttribute('class', 'inspector');
this.inspectorNode_.innerHTML = '<div class="property noise-generator">' + this.inspectorNode_.innerHTML =
'<div class="name">noise generator</div>' + '<div class="property test-data-gen-name">' +
'</div>' + '<div class="name">test data generator</div>' +
'<div class="property noise-parmas">' + '</div>' +
'<div class="name">parameters</div>' + '<div class="property test-data-gen-parmas">' +
'</div>' + '<div class="name">parameters</div>' +
'<div class="buttons"></div>'; '</div>' +
'<div class="buttons"></div>';
// Add value nodes. // Add value nodes.
function addValueNode(node, parent_selector) { function addValueNode(node, parent_selector) {
@ -158,8 +159,8 @@ Inspector.prototype.buildInspector_ = function() {
var parentNode = self.inspectorNode_.querySelector(parent_selector); var parentNode = self.inspectorNode_.querySelector(parent_selector);
parentNode.appendChild(node); parentNode.appendChild(node);
} }
addValueNode(this.divNoiseGenerator_, 'div.noise-generator'); addValueNode(this.divTestDataGeneratorName_, 'div.test-data-gen-name');
addValueNode(this.divNoiseParameters_, 'div.noise-parmas'); addValueNode(this.divTestDataGenParameters_, 'div.test-data-gen-parmas');
// Add buttons. // Add buttons.
var buttonsNode = this.inspectorNode_.querySelector('div.buttons'); var buttonsNode = this.inspectorNode_.querySelector('div.buttons');

View File

@ -17,15 +17,16 @@ from . import data_access
from . import eval_scores from . import eval_scores
from . import eval_scores_factory from . import eval_scores_factory
from . import evaluation from . import evaluation
from . import noise_generation from . import test_data_generation
from . import noise_generation_factory from . import test_data_generation_factory
class ApmModuleSimulator(object): class ApmModuleSimulator(object):
"""APM module simulator class. """APM module simulator class.
""" """
_NOISE_GENERATOR_CLASSES = noise_generation.NoiseGenerator.REGISTERED_CLASSES _TEST_DATA_GENERATOR_CLASSES = (
test_data_generation.TestDataGenerator.REGISTERED_CLASSES)
_EVAL_SCORE_WORKER_CLASSES = eval_scores.EvaluationScore.REGISTERED_CLASSES _EVAL_SCORE_WORKER_CLASSES = eval_scores.EvaluationScore.REGISTERED_CLASSES
def __init__(self, aechen_ir_database_path, polqa_tool_path): def __init__(self, aechen_ir_database_path, polqa_tool_path):
@ -34,8 +35,8 @@ class ApmModuleSimulator(object):
self._evaluator = evaluation.ApmModuleEvaluator() self._evaluator = evaluation.ApmModuleEvaluator()
# Instance factory objects. # Instance factory objects.
self._noise_generator_factory = ( self._test_data_generator_factory = (
noise_generation_factory.NoiseGeneratorFactory( test_data_generation_factory.TestDataGeneratorFactory(
aechen_ir_database_path=aechen_ir_database_path)) aechen_ir_database_path=aechen_ir_database_path))
self._evaluation_score_factory = ( self._evaluation_score_factory = (
eval_scores_factory.EvaluationScoreWorkerFactory( eval_scores_factory.EvaluationScoreWorkerFactory(
@ -43,12 +44,12 @@ class ApmModuleSimulator(object):
# Properties for each run. # Properties for each run.
self._base_output_path = None self._base_output_path = None
self._noise_generators = None self._test_data_generators = None
self._evaluation_score_workers = None self._evaluation_score_workers = None
self._config_filepaths = None self._config_filepaths = None
self._input_filepaths = None self._input_filepaths = None
def Run(self, config_filepaths, input_filepaths, noise_generator_names, def Run(self, config_filepaths, input_filepaths, test_data_generator_names,
eval_score_names, output_dir): eval_score_names, output_dir):
"""Runs the APM simulation. """Runs the APM simulation.
@ -57,16 +58,17 @@ class ApmModuleSimulator(object):
Args: Args:
config_filepaths: set of APM configuration files to test. config_filepaths: set of APM configuration files to test.
input_filepaths: set of input audio track files to test. input_filepaths: set of input audio track files to test.
noise_generator_names: set of noise generator names to test. test_data_generator_names: set of test data generator names to test.
eval_score_names: set of evaluation score names to test. eval_score_names: set of evaluation score names to test.
output_dir: base path to the output directory for wav files and outcomes. output_dir: base path to the output directory for wav files and outcomes.
""" """
self._base_output_path = os.path.abspath(output_dir) self._base_output_path = os.path.abspath(output_dir)
# Instance noise generators. # Instance test data generators.
self._noise_generators = [self._noise_generator_factory.GetInstance( self._test_data_generators = [self._test_data_generator_factory.GetInstance(
noise_generator_class=self._NOISE_GENERATOR_CLASSES[name]) for name in ( test_data_generators_class=(
noise_generator_names)] self._TEST_DATA_GENERATOR_CLASSES[name])) for name in (
test_data_generator_names)]
# Instance evaluation score workers. # Instance evaluation score workers.
self._evaluation_score_workers = [ self._evaluation_score_workers = [
@ -86,7 +88,7 @@ class ApmModuleSimulator(object):
"""Runs all the simulations. """Runs all the simulations.
Iterates over the combinations of APM configurations, probing signals, and Iterates over the combinations of APM configurations, probing signals, and
noise generators. test data generators.
""" """
# Try different APM config files. # Try different APM config files.
for config_name in self._config_filepaths: for config_name in self._config_filepaths:
@ -96,17 +98,17 @@ class ApmModuleSimulator(object):
for input_name in self._input_filepaths: for input_name in self._input_filepaths:
input_filepath = self._input_filepaths[input_name] input_filepath = self._input_filepaths[input_name]
# Try different noise generators. # Try different test data generators.
for noise_generator in self._noise_generators: for test_data_generators in self._test_data_generators:
logging.info('config: <%s>, input: <%s>, noise: <%s>', logging.info('config: <%s>, input: <%s>, noise: <%s>',
config_name, input_name, noise_generator.NAME) config_name, input_name, test_data_generators.NAME)
# Output path for the input-noise pairs. It is used to cache the noisy # Output path for the input-noise pairs. It is used to cache the noisy
# copies of the probing signals (shared across some simulations). # copies of the probing signals (shared across some simulations).
input_noise_cache_path = os.path.join( input_noise_cache_path = os.path.join(
self._base_output_path, self._base_output_path,
'_cache', '_cache',
'input_{}-noise_{}'.format(input_name, noise_generator.NAME)) 'input_{}-noise_{}'.format(input_name, test_data_generators.NAME))
data_access.MakeDirectory(input_noise_cache_path) data_access.MakeDirectory(input_noise_cache_path)
logging.debug('input-noise cache path: <%s>', input_noise_cache_path) logging.debug('input-noise cache path: <%s>', input_noise_cache_path)
@ -115,43 +117,44 @@ class ApmModuleSimulator(object):
self._base_output_path, self._base_output_path,
'cfg-{}'.format(config_name), 'cfg-{}'.format(config_name),
'input-{}'.format(input_name), 'input-{}'.format(input_name),
'noise-{}'.format(noise_generator.NAME)) 'gen-{}'.format(test_data_generators.NAME))
data_access.MakeDirectory(output_path) data_access.MakeDirectory(output_path)
logging.debug('output path: <%s>', output_path) logging.debug('output path: <%s>', output_path)
self._Simulate(noise_generator, input_filepath, self._Simulate(test_data_generators, input_filepath,
input_noise_cache_path, output_path, config_filepath) input_noise_cache_path, output_path, config_filepath)
def _Simulate(self, noise_generator, input_filepath, input_noise_cache_path, def _Simulate(self, test_data_generators, input_filepath,
output_path, config_filepath): input_noise_cache_path, output_path, config_filepath):
"""Runs a single set of simulation. """Runs a single set of simulation.
Simulates a given combination of APM configuration, probing signal, and Simulates a given combination of APM configuration, probing signal, and
noise generator. It iterates over the noise generator internal test data generator. It iterates over the test data generator
configurations. internal configurations.
Args: Args:
noise_generator: NoiseGenerator instance. test_data_generators: TestDataGenerator instance.
input_filepath: input audio track file to test. input_filepath: input audio track file to test.
input_noise_cache_path: path for the noisy audio track files. input_noise_cache_path: path for the noisy audio track files.
output_path: base output path for the noise generator. output_path: base output path for the test data generator.
config_filepath: APM configuration file to test. config_filepath: APM configuration file to test.
""" """
# Generate pairs of noisy input and reference signal files. # Generate pairs of noisy input and reference signal files.
noise_generator.Generate( test_data_generators.Generate(
input_signal_filepath=input_filepath, input_signal_filepath=input_filepath,
input_noise_cache_path=input_noise_cache_path, input_noise_cache_path=input_noise_cache_path,
base_output_path=output_path) base_output_path=output_path)
# For each input-reference pair, simulate a call and evaluate. # For each test data pair, simulate a call and evaluate.
for noise_generator_config_name in noise_generator.config_names: for test_data_generators_config_name in test_data_generators.config_names:
logging.info(' - noise config: <%s>', noise_generator_config_name) logging.info(' - test data generator config: <%s>',
test_data_generators_config_name)
# APM input and output signal paths. # APM input and output signal paths.
noisy_signal_filepath = noise_generator.noisy_signal_filepaths[ noisy_signal_filepath = test_data_generators.noisy_signal_filepaths[
noise_generator_config_name] test_data_generators_config_name]
evaluation_output_path = noise_generator.apm_output_paths[ evaluation_output_path = test_data_generators.apm_output_paths[
noise_generator_config_name] test_data_generators_config_name]
# Simulate a call using the audio processing module. # Simulate a call using the audio processing module.
self._audioproc_wrapper.Run( self._audioproc_wrapper.Run(
@ -160,8 +163,9 @@ class ApmModuleSimulator(object):
output_path=evaluation_output_path) output_path=evaluation_output_path)
# Reference signal path for the evaluation step. # Reference signal path for the evaluation step.
reference_signal_filepath = noise_generator.reference_signal_filepaths[ reference_signal_filepath = (
noise_generator_config_name] test_data_generators.reference_signal_filepaths[
test_data_generators_config_name])
# Evaluate. # Evaluate.
self._evaluator.Run( self._evaluator.Run(

View File

@ -6,19 +6,19 @@
# in the file PATENTS. All contributing project authors may # in the file PATENTS. All contributing project authors may
# be found in the AUTHORS file in the root of the source tree. # 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 """Test data generators producing signals pairs intended to be used to
APM module. Each pair consists of a noisy and a reference signal. The former test the APM module. Each pair consists of a noisy input and a reference signal.
is used as input for APM, and it is generated by adding noise to a signal. The former is used as APM input and it is generated by adding noise to a
The reference is the expected APM output when using the generated input. clean audio track. The reference is the expected APM output.
Throughout this file, the following naming convention is used: Throughout this file, the following naming convention is used:
- input signal: the clean signal (e.g., speech), - input signal: the clean signal (e.g., speech),
- noise signal: the noise to be summed up to the input signal (e.g., white - noise signal: the noise to be summed up to the input signal (e.g., white
noise, Gaussian noise), noise, Gaussian noise),
- noisy signal: input + noise. - noisy signal: input + noise.
The noise signal may or may not be a function of the clean signal. For The noise signal may or may not be a function of the clean signal. For
instance, white noise is independently generated, whereas reverberation is instance, white noise is independently generated, whereas reverberation is
obtained by convolving the input signal with an impulse response. obtained by convolving the input signal with an impulse response.
""" """
import logging import logging
@ -36,7 +36,7 @@ from . import exceptions
from . import signal_processing from . import signal_processing
class NoiseGenerator(object): class TestDataGenerator(object):
"""Abstract class responsible for the generation of noisy signals. """Abstract class responsible for the generation of noisy signals.
Given a clean signal, it generates two streams named noisy signal and Given a clean signal, it generates two streams named noisy signal and
@ -46,17 +46,15 @@ class NoiseGenerator(object):
expected at the output of the APM module when the latter is fed with the nosiy expected at the output of the APM module when the latter is fed with the nosiy
signal. signal.
A noise generator generates one or more input-reference pairs. An test data generator generates one or more pairs.
TODO(alessiob): Rename from NoiseGenerator to InputReferencePairGenerator.
""" """
NAME = None NAME = None
REGISTERED_CLASSES = {} REGISTERED_CLASSES = {}
def __init__(self): def __init__(self):
# Init dictionaries with one entry for each noise generator configuration # Init dictionaries with one entry for each test data generator
# (e.g., different SNRs). # configuration (e.g., different SNRs).
# Noisy audio track files (stored separately in a cache folder). # Noisy audio track files (stored separately in a cache folder).
self._noisy_signal_filepaths = None self._noisy_signal_filepaths = None
# Path to be used for the APM simulation output files. # Path to be used for the APM simulation output files.
@ -67,13 +65,14 @@ class NoiseGenerator(object):
@classmethod @classmethod
def RegisterClass(cls, class_to_register): def RegisterClass(cls, class_to_register):
"""Registers an NoiseGenerator implementation. """Registers an TestDataGenerator implementation.
Decorator to automatically register the classes that extend NoiseGenerator. Decorator to automatically register the classes that extend
TestDataGenerator.
Example usage: Example usage:
@NoiseGenerator.RegisterClass @TestDataGenerator.RegisterClass
class IdentityGenerator(NoiseGenerator): class IdentityGenerator(TestDataGenerator):
pass pass
""" """
cls.REGISTERED_CLASSES[class_to_register.NAME] = class_to_register cls.REGISTERED_CLASSES[class_to_register.NAME] = class_to_register
@ -165,21 +164,21 @@ class NoiseGenerator(object):
reference_signal_filepath) reference_signal_filepath)
# Save noisy and reference file paths. # Save noisy and reference file paths.
data_access.Metadata.SaveAudioInRefPaths( data_access.Metadata.SaveAudioTestDataPaths(
output_path=output_path, output_path=output_path,
audio_in_filepath=self._noisy_signal_filepaths[config_name], audio_in_filepath=self._noisy_signal_filepaths[config_name],
audio_ref_filepath=self._reference_signal_filepaths[config_name]) audio_ref_filepath=self._reference_signal_filepaths[config_name])
@classmethod @classmethod
def _MakeDir(cls, base_output_path, noise_generator_config_name): def _MakeDir(cls, base_output_path, test_data_generator_config_name):
output_path = os.path.join(base_output_path, noise_generator_config_name) output_path = os.path.join(
base_output_path, test_data_generator_config_name)
data_access.MakeDirectory(output_path) data_access.MakeDirectory(output_path)
return output_path return output_path
# Identity generator. @TestDataGenerator.RegisterClass
@NoiseGenerator.RegisterClass class IdentityTestDataGenerator(TestDataGenerator):
class IdentityGenerator(NoiseGenerator):
"""Generator that adds no noise. """Generator that adds no noise.
Both the noisy and the reference signals are the input signal. Both the noisy and the reference signals are the input signal.
@ -188,7 +187,7 @@ class IdentityGenerator(NoiseGenerator):
NAME = 'identity' NAME = 'identity'
def __init__(self): def __init__(self):
NoiseGenerator.__init__(self) TestDataGenerator.__init__(self)
def _Generate( def _Generate(
self, input_signal_filepath, input_noise_cache_path, base_output_path): self, input_signal_filepath, input_noise_cache_path, base_output_path):
@ -201,12 +200,12 @@ class IdentityGenerator(NoiseGenerator):
output_path=output_path) output_path=output_path)
@NoiseGenerator.RegisterClass @TestDataGenerator.RegisterClass
class WhiteNoiseGenerator(NoiseGenerator): class WhiteNoiseTestDataGenerator(TestDataGenerator):
"""Additive white noise generator. """Generator that adds white noise.
""" """
NAME = 'white' NAME = 'white_noise'
# Each pair indicates the clean vs. noisy and reference vs. noisy SNRs. # 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 # The reference (second value of each pair) always has a lower amount of noise
@ -221,7 +220,7 @@ class WhiteNoiseGenerator(NoiseGenerator):
_NOISY_SIGNAL_FILENAME_TEMPLATE = 'noise_{0:d}_SNR.wav' _NOISY_SIGNAL_FILENAME_TEMPLATE = 'noise_{0:d}_SNR.wav'
def __init__(self): def __init__(self):
NoiseGenerator.__init__(self) TestDataGenerator.__init__(self)
def _Generate( def _Generate(
self, input_signal_filepath, input_noise_cache_path, base_output_path): self, input_signal_filepath, input_noise_cache_path, base_output_path):
@ -270,15 +269,15 @@ class WhiteNoiseGenerator(NoiseGenerator):
# TODO(alessiob): remove comment when class implemented. # TODO(alessiob): remove comment when class implemented.
# @NoiseGenerator.RegisterClass # @TestDataGenerator.RegisterClass
class NarrowBandNoiseGenerator(NoiseGenerator): class NarrowBandNoiseTestDataGenerator(TestDataGenerator):
"""Additive narrow-band noise generator. """Generator that adds narrow-band noise.
""" """
NAME = 'narrow_band' NAME = 'narrow_band_noise'
def __init__(self): def __init__(self):
NoiseGenerator.__init__(self) TestDataGenerator.__init__(self)
def _Generate( def _Generate(
self, input_signal_filepath, input_noise_cache_path, base_output_path): self, input_signal_filepath, input_noise_cache_path, base_output_path):
@ -286,19 +285,22 @@ class NarrowBandNoiseGenerator(NoiseGenerator):
pass pass
@NoiseGenerator.RegisterClass @TestDataGenerator.RegisterClass
class EnvironmentalNoiseGenerator(NoiseGenerator): class EnvironmentalNoiseTestDataGenerator(TestDataGenerator):
"""Additive environmental noise generator. """Generator that adds environmental noise.
TODO(alessiob): Make the class more generic e.g.,
MixNoiseTrackTestDataGenerator.
""" """
NAME = 'environmental' NAME = 'environmental_noise'
_NOISY_SIGNAL_FILENAME_TEMPLATE = '{0}_{1:d}_SNR.wav' _NOISY_SIGNAL_FILENAME_TEMPLATE = '{0}_{1:d}_SNR.wav'
# TODO(alessiob): allow the user to store the noise tracks in a custom path. # TODO(alessiob): allow the user to store the noise tracks in a custom path.
_NOISE_TRACKS_PATH = os.path.join(os.getcwd(), 'noise_tracks') _NOISE_TRACKS_PATH = os.path.join(os.getcwd(), 'noise_tracks')
# TODO(alessiob): allow the user to have custom noise tracks. # TODO(alessiob): allow the user to have custom noise tracks.
# TODO(alessiob): exploit NoiseGeneratorFactory.GetInstance(). # TODO(alessiob): exploit TestDataGeneratorFactory.GetInstance().
_NOISE_TRACKS = [ _NOISE_TRACKS = [
'city.wav' 'city.wav'
] ]
@ -314,11 +316,11 @@ class EnvironmentalNoiseGenerator(NoiseGenerator):
] ]
def __init__(self): def __init__(self):
NoiseGenerator.__init__(self) TestDataGenerator.__init__(self)
def _Generate( def _Generate(
self, input_signal_filepath, input_noise_cache_path, base_output_path): self, input_signal_filepath, input_noise_cache_path, base_output_path):
"""Generates environmental noise. """Generates test data pairs using environmental noise.
For each noise track and pair of SNR values, the following two audio tracks 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 are created: the noisy signal and the reference signal. The former is
@ -374,14 +376,16 @@ class EnvironmentalNoiseGenerator(NoiseGenerator):
base_output_path, noisy_mix_filepaths, self._SNR_VALUE_PAIRS) base_output_path, noisy_mix_filepaths, self._SNR_VALUE_PAIRS)
@NoiseGenerator.RegisterClass @TestDataGenerator.RegisterClass
class EchoNoiseGenerator(NoiseGenerator): class ReverberationTestDataGenerator(TestDataGenerator):
"""Echo noise generator. """Generator that adds reverberation noise.
TODO(alessiob): Rename from echo to reverberation. TODO(alessiob): Make this class more generic since the impulse response can be
anything (not just reverberation); call it e.g.,
ConvolutionalNoiseTestDataGenerator.
""" """
NAME = 'echo' NAME = 'reverberation'
_IMPULSE_RESPONSES = { _IMPULSE_RESPONSES = {
'lecture': 'air_binaural_lecture_0_0_1.mat', # Long echo. 'lecture': 'air_binaural_lecture_0_0_1.mat', # Long echo.
@ -401,12 +405,12 @@ class EchoNoiseGenerator(NoiseGenerator):
_NOISY_SIGNAL_FILENAME_TEMPLATE = '{0}_{1:d}_SNR.wav' _NOISY_SIGNAL_FILENAME_TEMPLATE = '{0}_{1:d}_SNR.wav'
def __init__(self, aechen_ir_database_path): def __init__(self, aechen_ir_database_path):
NoiseGenerator.__init__(self) TestDataGenerator.__init__(self)
self._aechen_ir_database_path = aechen_ir_database_path self._aechen_ir_database_path = aechen_ir_database_path
def _Generate( def _Generate(
self, input_signal_filepath, input_noise_cache_path, base_output_path): self, input_signal_filepath, input_noise_cache_path, base_output_path):
"""Generates echo noise. """Generates test data pairs using reverberation noise.
For each impulse response, one noise track is created. For each impulse For each impulse response, one noise track is created. For each impulse
response and pair of SNR values, the following 2 audio tracks are response and pair of SNR values, the following 2 audio tracks are

View File

@ -0,0 +1,41 @@
# 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.
"""TestDataGenerator factory class.
"""
import logging
from . import test_data_generation
class TestDataGeneratorFactory(object):
"""Factory class used to create test data generators.
Usage: Create a factory passing parameters to the ctor with which the
generators will be produced.
"""
def __init__(self, aechen_ir_database_path):
self._aechen_ir_database_path = aechen_ir_database_path
def GetInstance(self, test_data_generators_class):
"""Creates an TestDataGenerator instance given a class object.
Args:
test_data_generators_class: TestDataGenerator class object (not an
instance).
"""
logging.debug('factory producing %s', test_data_generators_class)
if test_data_generators_class == (
test_data_generation.ReverberationTestDataGenerator):
return test_data_generation.ReverberationTestDataGenerator(
self._aechen_ir_database_path)
else:
# By default, no arguments in the constructor.
return test_data_generators_class()

View File

@ -6,7 +6,7 @@
# in the file PATENTS. All contributing project authors may # in the file PATENTS. All contributing project authors may
# be found in the AUTHORS file in the root of the source tree. # be found in the AUTHORS file in the root of the source tree.
"""Unit tests for the noise_generation module. """Unit tests for the test_data_generation module.
""" """
import os import os
@ -14,13 +14,13 @@ import shutil
import tempfile import tempfile
import unittest import unittest
from . import noise_generation from . import test_data_generation
from . import noise_generation_factory from . import test_data_generation_factory
from . import signal_processing from . import signal_processing
class TestNoiseGen(unittest.TestCase): class TestTestDataGenerators(unittest.TestCase):
"""Unit tests for the noise_generation module. """Unit tests for the test_data_generation module.
""" """
def setUp(self): def setUp(self):
@ -33,22 +33,25 @@ class TestNoiseGen(unittest.TestCase):
shutil.rmtree(self._base_output_path) shutil.rmtree(self._base_output_path)
shutil.rmtree(self._input_noise_cache_path) shutil.rmtree(self._input_noise_cache_path)
def testNoiseGenerators(self): def testTestDataGenerators(self):
# Preliminary check. # Preliminary check.
self.assertTrue(os.path.exists(self._base_output_path)) self.assertTrue(os.path.exists(self._base_output_path))
self.assertTrue(os.path.exists(self._input_noise_cache_path)) self.assertTrue(os.path.exists(self._input_noise_cache_path))
# Check that there is at least one registered noise generator. # Check that there is at least one registered test data generator.
registered_classes = noise_generation.NoiseGenerator.REGISTERED_CLASSES registered_classes = (
test_data_generation.TestDataGenerator.REGISTERED_CLASSES)
self.assertIsInstance(registered_classes, dict) self.assertIsInstance(registered_classes, dict)
self.assertGreater(len(registered_classes), 0) self.assertGreater(len(registered_classes), 0)
# Instance noise generator factory. # Instance generators factory.
noise_generator_factory = noise_generation_factory.NoiseGeneratorFactory( generators_factory = (
aechen_ir_database_path='') test_data_generation_factory.TestDataGeneratorFactory(
# TODO(alessiob): Replace with a mock of NoiseGeneratorFactory that takes aechen_ir_database_path=''))
# no arguments in the ctor. For those generators that need parameters, it # TODO(alessiob): Replace with a mock of TestDataGeneratorFactory that
# will return a mock generator (see the first comment in the next for loop). # takes no arguments in the ctor. For those generators that need parameters,
# it will return a mock generator (see the first comment in the next for
# loop).
# Use a sample input file as clean input signal. # Use a sample input file as clean input signal.
input_signal_filepath = os.path.join( input_signal_filepath = os.path.join(
@ -59,64 +62,62 @@ class TestNoiseGen(unittest.TestCase):
input_signal = signal_processing.SignalProcessingUtils.LoadWav( input_signal = signal_processing.SignalProcessingUtils.LoadWav(
input_signal_filepath) input_signal_filepath)
# Try each registered noise generator. # Try each registered test data generator.
for noise_generator_name in registered_classes: for generator_name in registered_classes:
# Exclude EchoNoiseGenerator. # Exclude ReverberationTestDataGenerator.
# TODO(alessiob): Mock EchoNoiseGenerator, the mock should rely on # TODO(alessiob): Mock ReverberationTestDataGenerator, the mock
# hard-coded impulse responses. This requires a mock for # should rely on hard-coded impulse responses. This requires a mock for
# NoiseGeneratorFactory. The latter knows whether returning the actual # TestDataGeneratorFactory. The latter knows whether returning the
# generator or a mock object (as in the case of EchoNoiseGenerator). # actual generator or a mock object (as in the case of
if noise_generator_name == 'echo': # ReverberationTestDataGenerator).
if generator_name == (
test_data_generation.ReverberationTestDataGenerator.NAME):
continue continue
# Instance noise generator. # Instance test data generator.
noise_generator = noise_generator_factory.GetInstance( generator = generators_factory.GetInstance(
registered_classes[noise_generator_name]) registered_classes[generator_name])
# Generate the noisy input - reference pairs. # Generate the noisy input - reference pairs.
noise_generator.Generate( generator.Generate(
input_signal_filepath=input_signal_filepath, input_signal_filepath=input_signal_filepath,
input_noise_cache_path=self._input_noise_cache_path, input_noise_cache_path=self._input_noise_cache_path,
base_output_path=self._base_output_path) base_output_path=self._base_output_path)
# Perform checks. # Perform checks.
self._CheckNoiseGeneratorPairsListSizes(noise_generator) self._CheckGeneratedPairsListSizes(generator)
self._CheckNoiseGeneratorPairsSignalDurations( self._CheckGeneratedPairsSignalDurations(generator, input_signal)
noise_generator, input_signal) self._CheckGeneratedPairsOutputPaths(generator)
self._CheckNoiseGeneratorPairsOutputPaths(noise_generator)
def _CheckNoiseGeneratorPairsListSizes(self, noise_generator): def _CheckGeneratedPairsListSizes(self, generator):
# Noise configuration names. config_names = generator.config_names
noise_config_names = noise_generator.config_names number_of_pairs = len(config_names)
number_of_pairs = len(noise_config_names) self.assertEqual(number_of_pairs,
len(generator.noisy_signal_filepaths))
self.assertEqual(number_of_pairs,
len(generator.apm_output_paths))
self.assertEqual(number_of_pairs,
len(generator.reference_signal_filepaths))
# Check. def _CheckGeneratedPairsSignalDurations(
self.assertEqual(number_of_pairs, self, generator, input_signal):
len(noise_generator.noisy_signal_filepaths)) """Checks duration of the generated signals.
self.assertEqual(number_of_pairs,
len(noise_generator.apm_output_paths))
self.assertEqual(number_of_pairs,
len(noise_generator.reference_signal_filepaths))
def _CheckNoiseGeneratorPairsSignalDurations(
self, noise_generator, input_signal):
"""Check duration of the signals generated by a noise generator.
Checks that the noisy input and the reference tracks are audio files Checks that the noisy input and the reference tracks are audio files
with duration equal to or greater than that of the input signal. with duration equal to or greater than that of the input signal.
Args: Args:
noise_generator: NoiseGenerator instance. generator: TestDataGenerator instance.
input_signal: AudioSegment instance. input_signal: AudioSegment instance.
""" """
input_signal_length = ( input_signal_length = (
signal_processing.SignalProcessingUtils.CountSamples(input_signal)) signal_processing.SignalProcessingUtils.CountSamples(input_signal))
# Iterate over the noisy signal - reference pairs. # Iterate over the noisy signal - reference pairs.
for noise_config_name in noise_generator.config_names: for config_name in generator.config_names:
# Load the noisy input file. # Load the noisy input file.
noisy_signal_filepath = noise_generator.noisy_signal_filepaths[ noisy_signal_filepath = generator.noisy_signal_filepaths[
noise_config_name] config_name]
noisy_signal = signal_processing.SignalProcessingUtils.LoadWav( noisy_signal = signal_processing.SignalProcessingUtils.LoadWav(
noisy_signal_filepath) noisy_signal_filepath)
@ -126,8 +127,8 @@ class TestNoiseGen(unittest.TestCase):
self.assertGreaterEqual(noisy_signal_length, input_signal_length) self.assertGreaterEqual(noisy_signal_length, input_signal_length)
# Load the reference file. # Load the reference file.
reference_signal_filepath = ( reference_signal_filepath = generator.reference_signal_filepaths[
noise_generator.reference_signal_filepaths[noise_config_name]) config_name]
reference_signal = signal_processing.SignalProcessingUtils.LoadWav( reference_signal = signal_processing.SignalProcessingUtils.LoadWav(
reference_signal_filepath) reference_signal_filepath)
@ -137,13 +138,13 @@ class TestNoiseGen(unittest.TestCase):
reference_signal)) reference_signal))
self.assertGreaterEqual(reference_signal_length, input_signal_length) self.assertGreaterEqual(reference_signal_length, input_signal_length)
def _CheckNoiseGeneratorPairsOutputPaths(self, noise_generator): def _CheckGeneratedPairsOutputPaths(self, generator):
"""Checks that the output path created by the generator exists. """Checks that the output path created by the generator exists.
Args: Args:
noise_generator: NoiseGenerator instance. generator: TestDataGenerator instance.
""" """
# Iterate over the noisy signal - reference pairs. # Iterate over the noisy signal - reference pairs.
for noise_config_name in noise_generator.config_names: for config_name in generator.config_names:
output_path = noise_generator.apm_output_paths[noise_config_name] output_path = generator.apm_output_paths[config_name]
self.assertTrue(os.path.exists(output_path)) self.assertTrue(os.path.exists(output_path))