OLD | NEW |
---|---|
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.
| |
OLD | NEW |