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

Side by Side 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: major refactoring Created 3 years, 7 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 unified diff | Download patch
OLDNEW
1 # Copyright (c) 2017 The WebRTC project authors. All Rights Reserved. 1 # Copyright (c) 2017 The WebRTC project authors. All Rights Reserved.
2 # 2 #
3 # Use of this source code is governed by a BSD-style license 3 # Use of this source code is governed by a BSD-style license
4 # that can be found in the LICENSE file in the root of the source 4 # that can be found in the LICENSE file in the root of the source
5 # tree. An additional intellectual property rights grant can be found 5 # tree. An additional intellectual property rights grant can be found
6 # in the file PATENTS. All contributing project authors may 6 # in the file PATENTS. All contributing project authors may
7 # be found in the AUTHORS file in the root of the source tree. 7 # be found in the AUTHORS file in the root of the source tree.
8 8
9 """APM module simulator. 9 """APM module simulator.
10 """ 10 """
11 11
12 import logging 12 import logging
13 import os 13 import os
14 14
15 from . import data_access 15 from . import data_access
16 from . import echo_path_simulation
17 from . import echo_path_simulation_factory
16 from . import eval_scores 18 from . import eval_scores
17 from . import eval_scores_factory 19 from . import eval_scores_factory
20 from . import input_mixer
18 from . import test_data_generation 21 from . import test_data_generation
19 from . import test_data_generation_factory 22 from . import test_data_generation_factory
20 23
21 24
22 class ApmModuleSimulator(object): 25 class ApmModuleSimulator(object):
23 """APM module simulator class. 26 """Audio processing module (APM) simulator class.
24 """ 27 """
25 28
26 _TEST_DATA_GENERATOR_CLASSES = ( 29 _TEST_DATA_GENERATOR_CLASSES = (
27 test_data_generation.TestDataGenerator.REGISTERED_CLASSES) 30 test_data_generation.TestDataGenerator.REGISTERED_CLASSES)
28 _EVAL_SCORE_WORKER_CLASSES = eval_scores.EvaluationScore.REGISTERED_CLASSES 31 _EVAL_SCORE_WORKER_CLASSES = eval_scores.EvaluationScore.REGISTERED_CLASSES
29 32
33 _PREFIX_APM_CONFIG = 'apmcfg-'
34 _PREFIX_CAPTURE = 'capture-'
35 _PREFIX_RENDER = 'render-'
36 _PREFIX_ECHO_SIMULATOR = 'echosim-'
37 _PREFIX_TEST_DATA_GEN = 'datagen-'
38 _PREFIX_TEST_DATA_GEN_PARAMS = 'datagen_params-'
39 _PREFIX_SCORE = 'score-'
40
30 def __init__(self, aechen_ir_database_path, polqa_tool_bin_path, 41 def __init__(self, aechen_ir_database_path, polqa_tool_bin_path,
31 ap_wrapper, evaluator): 42 ap_wrapper, evaluator):
32 # Init. 43 # Init.
33 self._audioproc_wrapper = ap_wrapper 44 self._audioproc_wrapper = ap_wrapper
34 self._evaluator = evaluator 45 self._evaluator = evaluator
35 46
36 # Instance factory objects. 47 # Instance factory objects.
37 self._test_data_generator_factory = ( 48 self._test_data_generator_factory = (
38 test_data_generation_factory.TestDataGeneratorFactory( 49 test_data_generation_factory.TestDataGeneratorFactory(
50 output_directory_prefix=self._PREFIX_TEST_DATA_GEN_PARAMS,
39 aechen_ir_database_path=aechen_ir_database_path)) 51 aechen_ir_database_path=aechen_ir_database_path))
40 self._evaluation_score_factory = ( 52 self._evaluation_score_factory = (
41 eval_scores_factory.EvaluationScoreWorkerFactory( 53 eval_scores_factory.EvaluationScoreWorkerFactory(
54 score_filename_prefix=self._PREFIX_SCORE,
42 polqa_tool_bin_path=polqa_tool_bin_path)) 55 polqa_tool_bin_path=polqa_tool_bin_path))
43 56
44 # Properties for each run. 57 # Properties for each run.
45 self._base_output_path = None 58 self._base_output_path = None
46 self._test_data_generators = None 59 self._test_data_generators = None
47 self._evaluation_score_workers = None 60 self._evaluation_score_workers = None
48 self._config_filepaths = None 61 self._config_filepaths = None
49 self._input_filepaths = None 62 self._capture_input_filepaths = None
63 self._render_input_filepaths = None
64 self._echo_path_simulator_class = None
50 65
51 def Run(self, config_filepaths, input_filepaths, test_data_generator_names, 66 @classmethod
52 eval_score_names, output_dir): 67 def GetPrefixApmConfig(cls):
68 return cls._PREFIX_APM_CONFIG
ivoc 2017/05/15 14:56:55 How about making these constants public instead? (
AleBzk 2017/05/19 15:45:35 It'd be much less boilerplate, but nothing prevent
69
70 @classmethod
71 def GetPrefixCapture(cls):
72 return cls._PREFIX_CAPTURE
73
74 @classmethod
75 def GetPrefixRender(cls):
76 return cls._PREFIX_RENDER
77
78 @classmethod
79 def GetPrefixEchoSimulator(cls):
80 return cls._PREFIX_ECHO_SIMULATOR
81
82 @classmethod
83 def GetPrefixTestDataGenerator(cls):
84 return cls._PREFIX_TEST_DATA_GEN
85
86 @classmethod
87 def GetPrefixTestDataGeneratorParameters(cls):
88 return cls._PREFIX_TEST_DATA_GEN_PARAMS
89
90 @classmethod
91 def GetPrefixScore(cls):
92 return cls._PREFIX_SCORE
93
94 def Run(self, config_filepaths, capture_input_filepaths,
95 test_data_generator_names, eval_score_names, output_dir,
96 render_input_filepaths=None, echo_path_simulator_name=(
97 echo_path_simulation.NoEchoPathSimulator.NAME)):
53 """Runs the APM simulation. 98 """Runs the APM simulation.
54 99
55 Initializes paths and required instances, then runs all the simulations. 100 Initializes paths and required instances, then runs all the simulations.
101 The render input can be optionally added. If added, the number of capture
102 input audio tracks and the number of render input audio tracks have to
ivoc 2017/05/15 14:56:55 input audio tracks have to be equal ("be" is missi
AleBzk 2017/05/19 15:45:35 Done.
103 equal. The two lists are used to form pairs of capture and render input.
56 104
57 Args: 105 Args:
58 config_filepaths: set of APM configuration files to test. 106 config_filepaths: set of APM configuration files to test.
59 input_filepaths: set of input audio track files to test. 107 capture_input_filepaths: set of capture input audio track files to test.
60 test_data_generator_names: set of test data generator names to test. 108 test_data_generator_names: set of test data generator names to test.
61 eval_score_names: set of evaluation score names to test. 109 eval_score_names: set of evaluation score names to test.
62 output_dir: base path to the output directory for wav files and outcomes. 110 output_dir: base path to the output directory for wav files and outcomes.
111 render_input_filepaths: set of render input audio track files to test.
112 echo_path_simulator_name: name of the echo path simulator to use when
113 render input is provided.
63 """ 114 """
115 assert render_input_filepaths is None or (
116 len(capture_input_filepaths) == len(render_input_filepaths)), (
117 'render input set size not matching input set size')
118 assert render_input_filepaths is None or echo_path_simulator_name in (
119 echo_path_simulation.EchoPathSimulator.REGISTERED_CLASSES), (
120 'invalid echo path simulator')
64 self._base_output_path = os.path.abspath(output_dir) 121 self._base_output_path = os.path.abspath(output_dir)
65 122
66 # Instance test data generators. 123 # Instance test data generators.
67 self._test_data_generators = [self._test_data_generator_factory.GetInstance( 124 self._test_data_generators = [self._test_data_generator_factory.GetInstance(
68 test_data_generators_class=( 125 test_data_generators_class=(
69 self._TEST_DATA_GENERATOR_CLASSES[name])) for name in ( 126 self._TEST_DATA_GENERATOR_CLASSES[name])) for name in (
70 test_data_generator_names)] 127 test_data_generator_names)]
71 128
72 # Instance evaluation score workers. 129 # Instance evaluation score workers.
73 self._evaluation_score_workers = [ 130 self._evaluation_score_workers = [
74 self._evaluation_score_factory.GetInstance( 131 self._evaluation_score_factory.GetInstance(
75 evaluation_score_class=self._EVAL_SCORE_WORKER_CLASSES[name]) for ( 132 evaluation_score_class=self._EVAL_SCORE_WORKER_CLASSES[name]) for (
76 name) in eval_score_names] 133 name) in eval_score_names]
77 134
78 # Set APM configuration file paths. 135 # Set APM configuration file paths.
79 self._config_filepaths = self._CreatePathsCollection(config_filepaths) 136 self._config_filepaths = self._CreatePathsCollection(config_filepaths)
80 137
81 # Set probing signal file paths. 138 # Set probing signal file paths.
82 self._input_filepaths = self._CreatePathsCollection(input_filepaths) 139 if render_input_filepaths is None:
140 # Capture input only.
141 self._capture_input_filepaths = self._CreatePathsCollection(
142 capture_input_filepaths)
143 self._render_input_filepaths = None
144 else:
145 # Set both capture and render input signals.
146 self._SetTestInputSignalFilePaths(
147 capture_input_filepaths, render_input_filepaths)
148
149 # Set the echo path simulator class.
150 self._echo_path_simulator_class = (
151 echo_path_simulation.EchoPathSimulator.REGISTERED_CLASSES[
152 echo_path_simulator_name])
83 153
84 self._SimulateAll() 154 self._SimulateAll()
85 155
86 def _SimulateAll(self): 156 def _SimulateAll(self):
87 """Runs all the simulations. 157 """Runs all the simulations.
88 158
89 Iterates over the combinations of APM configurations, probing signals, and 159 Iterates over the combinations of APM configurations, probing signals, and
90 test data generators. 160 test data generators. This method is mainly responsible for the creation of
161 the cache and output directories required in order to call _Simulate().
91 """ 162 """
163 without_render_input = self._render_input_filepaths is None
164
92 # Try different APM config files. 165 # Try different APM config files.
93 for config_name in self._config_filepaths: 166 for config_name in self._config_filepaths:
94 config_filepath = self._config_filepaths[config_name] 167 config_filepath = self._config_filepaths[config_name]
95 168
96 # Try different probing signal files. 169 # Try different capture-render pairs.
97 for input_name in self._input_filepaths: 170 for capture_input_name in self._capture_input_filepaths:
98 input_filepath = self._input_filepaths[input_name] 171 capture_input_filepath = self._capture_input_filepaths[
172 capture_input_name]
173 render_input_filepath = None if without_render_input else (
174 self._render_input_filepaths[capture_input_name])
175 render_input_name = '(none)' if without_render_input else (
176 self._ExtractFileName(render_input_filepath))
177
178 # Instance echo path simulator (if needed).
179 echo_path_simulator = (
180 echo_path_simulation_factory.EchoPathSimulatorFactory.GetInstance(
181 self._echo_path_simulator_class, render_input_filepath))
99 182
100 # Try different test data generators. 183 # Try different test data generators.
101 for test_data_generators in self._test_data_generators: 184 for test_data_generators in self._test_data_generators:
102 logging.info('config: <%s>, input: <%s>, noise: <%s>', 185 logging.info('APM config preset: <%s>, capture: <%s>, render: <%s>,'
103 config_name, input_name, test_data_generators.NAME) 186 'test data generator: <%s>, echo simulator: <%s>',
187 config_name, capture_input_name, render_input_name,
188 test_data_generators.NAME, echo_path_simulator.NAME)
104 189
105 # Output path for the input-noise pairs. It is used to cache the noisy 190 # Output path for the generated test data.
106 # copies of the probing signals (shared across some simulations). 191 # The path is used to cache the signals shared across simulations.
107 input_noise_cache_path = os.path.join( 192 test_data_cache_path = os.path.join(
108 self._base_output_path, 193 self._base_output_path, '_cache',
109 '_cache', 194 self._PREFIX_CAPTURE + capture_input_name,
110 'input_{}-noise_{}'.format(input_name, test_data_generators.NAME)) 195 self._PREFIX_TEST_DATA_GEN + test_data_generators.NAME)
111 data_access.MakeDirectory(input_noise_cache_path) 196 data_access.MakeDirectory(test_data_cache_path)
112 logging.debug('input-noise cache path: <%s>', input_noise_cache_path) 197 logging.debug('test data cache path: <%s>', test_data_cache_path)
198
199 # Output path for the echo simulator and APM input mixer output.
200 echo_test_data_cache_path = os.path.join(
201 test_data_cache_path, 'echosim-{}'.format(
202 echo_path_simulator.NAME))
203 data_access.MakeDirectory(echo_test_data_cache_path)
204 logging.debug('echo test data cache path: <%s>',
205 echo_test_data_cache_path)
113 206
114 # Full output path. 207 # Full output path.
115 output_path = os.path.join( 208 output_path = os.path.join(
116 self._base_output_path, 209 self._base_output_path,
117 'cfg-{}'.format(config_name), 210 self._PREFIX_APM_CONFIG + config_name,
118 'input-{}'.format(input_name), 211 self._PREFIX_CAPTURE + capture_input_name,
119 'gen-{}'.format(test_data_generators.NAME)) 212 self._PREFIX_RENDER + render_input_name,
213 self._PREFIX_ECHO_SIMULATOR + echo_path_simulator.NAME,
214 self._PREFIX_TEST_DATA_GEN + test_data_generators.NAME)
120 data_access.MakeDirectory(output_path) 215 data_access.MakeDirectory(output_path)
121 logging.debug('output path: <%s>', output_path) 216 logging.debug('output path: <%s>', output_path)
122 217
123 self._Simulate(test_data_generators, input_filepath, 218 self._Simulate(test_data_generators, capture_input_filepath,
124 input_noise_cache_path, output_path, config_filepath) 219 render_input_filepath, test_data_cache_path,
220 echo_test_data_cache_path, output_path,
221 config_filepath, echo_path_simulator)
125 222
126 def _Simulate(self, test_data_generators, input_filepath, 223 def _Simulate(self, test_data_generators, clean_capture_input_filepath,
127 input_noise_cache_path, output_path, config_filepath): 224 render_input_filepath, test_data_cache_path,
225 echo_test_data_cache_path, output_path, config_filepath,
226 echo_path_simulator):
128 """Runs a single set of simulation. 227 """Runs a single set of simulation.
129 228
130 Simulates a given combination of APM configuration, probing signal, and 229 Simulates a given combination of APM configuration, probing signal, and
131 test data generator. It iterates over the test data generator 230 test data generator. It iterates over the test data generator
132 internal configurations. 231 internal configurations.
133 232
134 Args: 233 Args:
135 test_data_generators: TestDataGenerator instance. 234 test_data_generators: TestDataGenerator instance.
136 input_filepath: input audio track file to test. 235 clean_capture_input_filepath: clean capture input audio track file to
ivoc 2017/05/15 14:56:55 It would be good to explain briefly what "clean" m
AleBzk 2017/05/19 15:45:35 Done.
137 input_noise_cache_path: path for the noisy audio track files. 236 test.
237 render_input_filepath: render input audio track file to test.
238 test_data_cache_path: path for the generated test audio track files.
239 echo_test_data_cache_path: path for the echo simulator.
138 output_path: base output path for the test data generator. 240 output_path: base output path for the test data generator.
139 config_filepath: APM configuration file to test. 241 config_filepath: APM configuration file to test.
242 echo_path_simulator: EchoPathSimulator instance.
140 """ 243 """
141 # Generate pairs of noisy input and reference signal files. 244 # Generate pairs of noisy input and reference signal files.
142 test_data_generators.Generate( 245 test_data_generators.Generate(
143 input_signal_filepath=input_filepath, 246 input_signal_filepath=clean_capture_input_filepath,
144 input_noise_cache_path=input_noise_cache_path, 247 test_data_cache_path=test_data_cache_path,
145 base_output_path=output_path) 248 base_output_path=output_path)
146 249
147 # For each test data pair, simulate a call and evaluate. 250 # For each test data pair, simulate a call and evaluate.
148 for config_name in test_data_generators.config_names: 251 for config_name in test_data_generators.config_names:
149 logging.info(' - test data generator config: <%s>', config_name) 252 logging.info(' - test data generator config: <%s>', config_name)
150 253
151 # APM input and output signal paths. 254 # Paths to the test data generator output.
152 noisy_signal_filepath = test_data_generators.noisy_signal_filepaths[ 255 # Note that the reference signal does not depend on the render input
153 config_name] 256 # which is optional.
257 noisy_capture_input_filepath = (
258 test_data_generators.noisy_signal_filepaths[config_name])
259 reference_signal_filepath = (
260 test_data_generators.reference_signal_filepaths[config_name])
261
262 # Output path for the evaluation (e.g., APM output file).
154 evaluation_output_path = test_data_generators.apm_output_paths[ 263 evaluation_output_path = test_data_generators.apm_output_paths[
155 config_name] 264 config_name]
156 265
157 # Simulate a call using the audio processing module. 266 # Paths to the APM input signals.
267 echo_path_filpath = echo_path_simulator.Simulate(
ivoc 2017/05/15 14:56:55 Should be "echo_path_filepath" I presume?
AleBzk 2017/05/19 15:45:35 Done.
268 echo_test_data_cache_path)
269 apm_input_filepath = input_mixer.ApmInputMixer.Mix(
270 echo_test_data_cache_path, noisy_capture_input_filepath,
271 echo_path_filpath)
272
273 # Simulate a call using APM.
158 self._audioproc_wrapper.Run( 274 self._audioproc_wrapper.Run(
159 config_filepath=config_filepath, 275 config_filepath=config_filepath,
160 input_filepath=noisy_signal_filepath, 276 capture_input_filepath=apm_input_filepath,
277 render_input_filepath=render_input_filepath,
161 output_path=evaluation_output_path) 278 output_path=evaluation_output_path)
162 279
163 # Reference signal path for the evaluation step.
164 reference_signal_filepath = (
165 test_data_generators.reference_signal_filepaths[
166 config_name])
167
168 # Evaluate. 280 # Evaluate.
169 self._evaluator.Run( 281 self._evaluator.Run(
170 evaluation_score_workers=self._evaluation_score_workers, 282 evaluation_score_workers=self._evaluation_score_workers,
171 apm_output_filepath=self._audioproc_wrapper.output_filepath, 283 apm_output_filepath=self._audioproc_wrapper.output_filepath,
172 reference_input_filepath=reference_signal_filepath, 284 reference_input_filepath=reference_signal_filepath,
173 output_path=evaluation_output_path) 285 output_path=evaluation_output_path)
174 286
287 # Save simulation metadata.
288 data_access.Metadata.SaveAudioTestDataPaths(
289 output_path=evaluation_output_path,
290 clean_capture_input_filepath=clean_capture_input_filepath,
291 echo_free_capture_filepath=noisy_capture_input_filepath,
292 echo_filepath=echo_path_filpath,
293 render_filepath=render_input_filepath,
294 capture_filepath=apm_input_filepath,
295 apm_output_filepath=self._audioproc_wrapper.output_filepath,
296 apm_reference_filepath=reference_signal_filepath)
297
298 def _SetTestInputSignalFilePaths(self, capture_input_filepaths,
299 render_input_filepaths):
300 """Sets input and render input file paths collections.
301
302 Pairs the input and render input files by storing the file paths into two
303 collections. The key is the file name of the input file.
304
305 Args:
306 capture_input_filepaths: list of file paths.
307 render_input_filepaths: list of file paths.
308 """
309 self._capture_input_filepaths = {}
310 self._render_input_filepaths = {}
311 assert len(capture_input_filepaths) == len(render_input_filepaths)
312 for capture_input_filepath, render_input_filepath in zip(
313 capture_input_filepaths, render_input_filepaths):
314 name = self._ExtractFileName(capture_input_filepath)
315 self._capture_input_filepaths[name] = os.path.abspath(
316 capture_input_filepath)
317 self._render_input_filepaths[name] = os.path.abspath(
318 render_input_filepath)
319
175 @classmethod 320 @classmethod
176 def _CreatePathsCollection(cls, filepaths): 321 def _CreatePathsCollection(cls, filepaths):
177 """Creates a collection of file paths. 322 """Creates a collection of file paths.
178 323
179 Given a list of file paths, makes a collection with one item for each file 324 Given a list of file paths, makes a collection with one item for each file
180 path. The value is absolute path, the key is the file name without 325 path. The value is absolute path, the key is the file name without
181 extenstion. 326 extenstion.
182 327
183 Args: 328 Args:
184 filepaths: list of file paths. 329 filepaths: list of file paths.
185 330
186 Returns: 331 Returns:
187 A dict. 332 A dict.
188 """ 333 """
189 filepaths_collection = {} 334 filepaths_collection = {}
190 for filepath in filepaths: 335 for filepath in filepaths:
191 name = os.path.splitext(os.path.split(filepath)[1])[0] 336 name = cls._ExtractFileName(filepath)
192 filepaths_collection[name] = os.path.abspath(filepath) 337 filepaths_collection[name] = os.path.abspath(filepath)
193 return filepaths_collection 338 return filepaths_collection
339
340 @classmethod
341 def _ExtractFileName(cls, filepath):
342 return os.path.splitext(os.path.split(filepath)[1])[0]
ivoc 2017/05/15 14:56:55 Why does this take [1] after splitting? Is that th
AleBzk 2017/05/19 15:45:35 Done.
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698