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

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

Issue 2813883002: APM QA refactoring: render stream support, echo path simulation, new export engine (Closed)
Patch Set: merge Created 3 years, 5 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/simulation.py
diff --git a/webrtc/modules/audio_processing/test/py_quality_assessment/quality_assessment/simulation.py b/webrtc/modules/audio_processing/test/py_quality_assessment/quality_assessment/simulation.py
index a3bf9cceee3da85520865c3da9625101e69dadb4..7023b6a8c54e811d47b57bffb8d9fa7d147c0761 100644
--- a/webrtc/modules/audio_processing/test/py_quality_assessment/quality_assessment/simulation.py
+++ b/webrtc/modules/audio_processing/test/py_quality_assessment/quality_assessment/simulation.py
@@ -13,20 +13,31 @@ import logging
import os
from . import data_access
+from . import echo_path_simulation
+from . import echo_path_simulation_factory
from . import eval_scores
from . import eval_scores_factory
+from . import input_mixer
from . import test_data_generation
from . import test_data_generation_factory
class ApmModuleSimulator(object):
- """APM module simulator class.
+ """Audio processing module (APM) simulator class.
"""
_TEST_DATA_GENERATOR_CLASSES = (
test_data_generation.TestDataGenerator.REGISTERED_CLASSES)
_EVAL_SCORE_WORKER_CLASSES = eval_scores.EvaluationScore.REGISTERED_CLASSES
+ _PREFIX_APM_CONFIG = 'apmcfg-'
+ _PREFIX_CAPTURE = 'capture-'
+ _PREFIX_RENDER = 'render-'
+ _PREFIX_ECHO_SIMULATOR = 'echosim-'
+ _PREFIX_TEST_DATA_GEN = 'datagen-'
+ _PREFIX_TEST_DATA_GEN_PARAMS = 'datagen_params-'
+ _PREFIX_SCORE = 'score-'
+
def __init__(self, aechen_ir_database_path, polqa_tool_bin_path,
ap_wrapper, evaluator):
# Init.
@@ -36,9 +47,11 @@ class ApmModuleSimulator(object):
# Instance factory objects.
self._test_data_generator_factory = (
test_data_generation_factory.TestDataGeneratorFactory(
+ output_directory_prefix=self._PREFIX_TEST_DATA_GEN_PARAMS,
aechen_ir_database_path=aechen_ir_database_path))
self._evaluation_score_factory = (
eval_scores_factory.EvaluationScoreWorkerFactory(
+ score_filename_prefix=self._PREFIX_SCORE,
polqa_tool_bin_path=polqa_tool_bin_path))
# Properties for each run.
@@ -46,21 +59,65 @@ class ApmModuleSimulator(object):
self._test_data_generators = None
self._evaluation_score_workers = None
self._config_filepaths = None
- self._input_filepaths = None
+ self._capture_input_filepaths = None
+ self._render_input_filepaths = None
+ self._echo_path_simulator_class = None
+
+ @classmethod
+ def GetPrefixApmConfig(cls):
+ return cls._PREFIX_APM_CONFIG
+
+ @classmethod
+ def GetPrefixCapture(cls):
+ return cls._PREFIX_CAPTURE
+
+ @classmethod
+ def GetPrefixRender(cls):
+ return cls._PREFIX_RENDER
+
+ @classmethod
+ def GetPrefixEchoSimulator(cls):
+ return cls._PREFIX_ECHO_SIMULATOR
- def Run(self, config_filepaths, input_filepaths, test_data_generator_names,
- eval_score_names, output_dir):
+ @classmethod
+ def GetPrefixTestDataGenerator(cls):
+ return cls._PREFIX_TEST_DATA_GEN
+
+ @classmethod
+ def GetPrefixTestDataGeneratorParameters(cls):
+ return cls._PREFIX_TEST_DATA_GEN_PARAMS
+
+ @classmethod
+ def GetPrefixScore(cls):
+ return cls._PREFIX_SCORE
+
+ def Run(self, config_filepaths, capture_input_filepaths,
+ test_data_generator_names, eval_score_names, output_dir,
+ render_input_filepaths=None, echo_path_simulator_name=(
+ echo_path_simulation.NoEchoPathSimulator.NAME)):
"""Runs the APM simulation.
Initializes paths and required instances, then runs all the simulations.
+ The render input can be optionally added. If added, the number of capture
+ input audio tracks and the number of render input audio tracks have to be
+ equal. The two lists are used to form pairs of capture and render input.
Args:
config_filepaths: set of APM configuration files to test.
- input_filepaths: set of input audio track files to test.
+ capture_input_filepaths: set of capture input audio track files to test.
test_data_generator_names: set of test data generator 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.
+ render_input_filepaths: set of render input audio track files to test.
+ echo_path_simulator_name: name of the echo path simulator to use when
+ render input is provided.
"""
+ assert render_input_filepaths is None or (
+ len(capture_input_filepaths) == len(render_input_filepaths)), (
+ 'render input set size not matching input set size')
+ assert render_input_filepaths is None or echo_path_simulator_name in (
+ echo_path_simulation.EchoPathSimulator.REGISTERED_CLASSES), (
+ 'invalid echo path simulator')
self._base_output_path = os.path.abspath(output_dir)
# Instance test data generators.
@@ -79,7 +136,20 @@ class ApmModuleSimulator(object):
self._config_filepaths = self._CreatePathsCollection(config_filepaths)
# Set probing signal file paths.
- self._input_filepaths = self._CreatePathsCollection(input_filepaths)
+ if render_input_filepaths is None:
+ # Capture input only.
+ self._capture_input_filepaths = self._CreatePathsCollection(
+ capture_input_filepaths)
+ self._render_input_filepaths = None
+ else:
+ # Set both capture and render input signals.
+ self._SetTestInputSignalFilePaths(
+ capture_input_filepaths, render_input_filepaths)
+
+ # Set the echo path simulator class.
+ self._echo_path_simulator_class = (
+ echo_path_simulation.EchoPathSimulator.REGISTERED_CLASSES[
+ echo_path_simulator_name])
self._SimulateAll()
@@ -87,44 +157,73 @@ class ApmModuleSimulator(object):
"""Runs all the simulations.
Iterates over the combinations of APM configurations, probing signals, and
- test data generators.
+ test data generators. This method is mainly responsible for the creation of
+ the cache and output directories required in order to call _Simulate().
"""
+ without_render_input = self._render_input_filepaths is None
+
# Try different APM config files.
for config_name in self._config_filepaths:
config_filepath = self._config_filepaths[config_name]
- # Try different probing signal files.
- for input_name in self._input_filepaths:
- input_filepath = self._input_filepaths[input_name]
+ # Try different capture-render pairs.
+ for capture_input_name in self._capture_input_filepaths:
+ capture_input_filepath = self._capture_input_filepaths[
+ capture_input_name]
+ render_input_filepath = None if without_render_input else (
+ self._render_input_filepaths[capture_input_name])
+ render_input_name = '(none)' if without_render_input else (
+ self._ExtractFileName(render_input_filepath))
+
+ # Instance echo path simulator (if needed).
+ echo_path_simulator = (
+ echo_path_simulation_factory.EchoPathSimulatorFactory.GetInstance(
+ self._echo_path_simulator_class, render_input_filepath))
# Try different test data generators.
for test_data_generators in self._test_data_generators:
- logging.info('config: <%s>, input: <%s>, noise: <%s>',
- config_name, input_name, test_data_generators.NAME)
-
- # Output path for the input-noise pairs. It is used to cache the noisy
- # copies of the probing signals (shared across some simulations).
- input_noise_cache_path = os.path.join(
- self._base_output_path,
- '_cache',
- 'input_{}-noise_{}'.format(input_name, test_data_generators.NAME))
- data_access.MakeDirectory(input_noise_cache_path)
- logging.debug('input-noise cache path: <%s>', input_noise_cache_path)
+ logging.info('APM config preset: <%s>, capture: <%s>, render: <%s>,'
+ 'test data generator: <%s>, echo simulator: <%s>',
+ config_name, capture_input_name, render_input_name,
+ test_data_generators.NAME, echo_path_simulator.NAME)
+
+ # Output path for the generated test data.
+ # The path is used to cache the signals shared across simulations.
+ test_data_cache_path = os.path.join(
+ self._base_output_path, '_cache',
+ self._PREFIX_CAPTURE + capture_input_name,
+ self._PREFIX_TEST_DATA_GEN + test_data_generators.NAME)
+ data_access.MakeDirectory(test_data_cache_path)
+ logging.debug('test data cache path: <%s>', test_data_cache_path)
+
+ # Output path for the echo simulator and APM input mixer output.
+ echo_test_data_cache_path = os.path.join(
+ test_data_cache_path, 'echosim-{}'.format(
+ echo_path_simulator.NAME))
+ data_access.MakeDirectory(echo_test_data_cache_path)
+ logging.debug('echo test data cache path: <%s>',
+ echo_test_data_cache_path)
# Full output path.
output_path = os.path.join(
self._base_output_path,
- 'cfg-{}'.format(config_name),
- 'input-{}'.format(input_name),
- 'gen-{}'.format(test_data_generators.NAME))
+ self._PREFIX_APM_CONFIG + config_name,
+ self._PREFIX_CAPTURE + capture_input_name,
+ self._PREFIX_RENDER + render_input_name,
+ self._PREFIX_ECHO_SIMULATOR + echo_path_simulator.NAME,
+ self._PREFIX_TEST_DATA_GEN + test_data_generators.NAME)
data_access.MakeDirectory(output_path)
logging.debug('output path: <%s>', output_path)
- self._Simulate(test_data_generators, input_filepath,
- input_noise_cache_path, output_path, config_filepath)
+ self._Simulate(test_data_generators, capture_input_filepath,
+ render_input_filepath, test_data_cache_path,
+ echo_test_data_cache_path, output_path,
+ config_filepath, echo_path_simulator)
- def _Simulate(self, test_data_generators, input_filepath,
- input_noise_cache_path, output_path, config_filepath):
+ def _Simulate(self, test_data_generators, clean_capture_input_filepath,
+ render_input_filepath, test_data_cache_path,
+ echo_test_data_cache_path, output_path, config_filepath,
+ echo_path_simulator):
"""Runs a single set of simulation.
Simulates a given combination of APM configuration, probing signal, and
@@ -133,38 +232,52 @@ class ApmModuleSimulator(object):
Args:
test_data_generators: TestDataGenerator instance.
- input_filepath: input audio track file to test.
- input_noise_cache_path: path for the noisy audio track files.
+ clean_capture_input_filepath: capture input audio track file to be
+ processed by a test data generator and
+ not affected by echo.
+ render_input_filepath: render input audio track file to test.
+ test_data_cache_path: path for the generated test audio track files.
+ echo_test_data_cache_path: path for the echo simulator.
output_path: base output path for the test data generator.
config_filepath: APM configuration file to test.
+ echo_path_simulator: EchoPathSimulator instance.
"""
# Generate pairs of noisy input and reference signal files.
test_data_generators.Generate(
- input_signal_filepath=input_filepath,
- input_noise_cache_path=input_noise_cache_path,
+ input_signal_filepath=clean_capture_input_filepath,
+ test_data_cache_path=test_data_cache_path,
base_output_path=output_path)
# For each test data pair, simulate a call and evaluate.
for config_name in test_data_generators.config_names:
logging.info(' - test data generator config: <%s>', config_name)
- # APM input and output signal paths.
- noisy_signal_filepath = test_data_generators.noisy_signal_filepaths[
- config_name]
+ # Paths to the test data generator output.
+ # Note that the reference signal does not depend on the render input
+ # which is optional.
+ noisy_capture_input_filepath = (
+ test_data_generators.noisy_signal_filepaths[config_name])
+ reference_signal_filepath = (
+ test_data_generators.reference_signal_filepaths[config_name])
+
+ # Output path for the evaluation (e.g., APM output file).
evaluation_output_path = test_data_generators.apm_output_paths[
config_name]
- # Simulate a call using the audio processing module.
+ # Paths to the APM input signals.
+ echo_path_filepath = echo_path_simulator.Simulate(
+ echo_test_data_cache_path)
+ apm_input_filepath = input_mixer.ApmInputMixer.Mix(
+ echo_test_data_cache_path, noisy_capture_input_filepath,
+ echo_path_filepath)
+
+ # Simulate a call using APM.
self._audioproc_wrapper.Run(
config_filepath=config_filepath,
- input_filepath=noisy_signal_filepath,
+ capture_input_filepath=apm_input_filepath,
+ render_input_filepath=render_input_filepath,
output_path=evaluation_output_path)
- # Reference signal path for the evaluation step.
- reference_signal_filepath = (
- test_data_generators.reference_signal_filepaths[
- config_name])
-
# Evaluate.
self._evaluator.Run(
evaluation_score_workers=self._evaluation_score_workers,
@@ -172,6 +285,39 @@ class ApmModuleSimulator(object):
reference_input_filepath=reference_signal_filepath,
output_path=evaluation_output_path)
+ # Save simulation metadata.
+ data_access.Metadata.SaveAudioTestDataPaths(
+ output_path=evaluation_output_path,
+ clean_capture_input_filepath=clean_capture_input_filepath,
+ echo_free_capture_filepath=noisy_capture_input_filepath,
+ echo_filepath=echo_path_filepath,
+ render_filepath=render_input_filepath,
+ capture_filepath=apm_input_filepath,
+ apm_output_filepath=self._audioproc_wrapper.output_filepath,
+ apm_reference_filepath=reference_signal_filepath)
+
+ def _SetTestInputSignalFilePaths(self, capture_input_filepaths,
+ render_input_filepaths):
+ """Sets input and render input file paths collections.
+
+ Pairs the input and render input files by storing the file paths into two
+ collections. The key is the file name of the input file.
+
+ Args:
+ capture_input_filepaths: list of file paths.
+ render_input_filepaths: list of file paths.
+ """
+ self._capture_input_filepaths = {}
+ self._render_input_filepaths = {}
+ assert len(capture_input_filepaths) == len(render_input_filepaths)
+ for capture_input_filepath, render_input_filepath in zip(
+ capture_input_filepaths, render_input_filepaths):
+ name = self._ExtractFileName(capture_input_filepath)
+ self._capture_input_filepaths[name] = os.path.abspath(
+ capture_input_filepath)
+ self._render_input_filepaths[name] = os.path.abspath(
+ render_input_filepath)
+
@classmethod
def _CreatePathsCollection(cls, filepaths):
"""Creates a collection of file paths.
@@ -188,6 +334,10 @@ class ApmModuleSimulator(object):
"""
filepaths_collection = {}
for filepath in filepaths:
- name = os.path.splitext(os.path.split(filepath)[1])[0]
+ name = cls._ExtractFileName(filepath)
filepaths_collection[name] = os.path.abspath(filepath)
return filepaths_collection
+
+ @classmethod
+ def _ExtractFileName(cls, filepath):
+ return os.path.splitext(os.path.split(filepath)[-1])[0]

Powered by Google App Engine
This is Rietveld 408576698