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 | |
16 from . import eval_scores | 17 from . import eval_scores |
17 from . import eval_scores_factory | 18 from . import eval_scores_factory |
18 from . import test_data_generation | 19 from . import test_data_generation |
19 from . import test_data_generation_factory | 20 from . import test_data_generation_factory |
20 | 21 |
21 | 22 |
22 class ApmModuleSimulator(object): | 23 class ApmModuleSimulator(object): |
23 """APM module simulator class. | 24 """Audio processing module (APM) simulator class. |
24 """ | 25 """ |
25 | 26 |
26 _TEST_DATA_GENERATOR_CLASSES = ( | 27 _TEST_DATA_GENERATOR_CLASSES = ( |
27 test_data_generation.TestDataGenerator.REGISTERED_CLASSES) | 28 test_data_generation.TestDataGenerator.REGISTERED_CLASSES) |
28 _EVAL_SCORE_WORKER_CLASSES = eval_scores.EvaluationScore.REGISTERED_CLASSES | 29 _EVAL_SCORE_WORKER_CLASSES = eval_scores.EvaluationScore.REGISTERED_CLASSES |
29 | 30 |
30 def __init__(self, aechen_ir_database_path, polqa_tool_bin_path, | 31 def __init__(self, aechen_ir_database_path, polqa_tool_bin_path, |
31 ap_wrapper, evaluator): | 32 ap_wrapper, evaluator): |
32 # Init. | 33 # Init. |
33 self._audioproc_wrapper = ap_wrapper | 34 self._audioproc_wrapper = ap_wrapper |
34 self._evaluator = evaluator | 35 self._evaluator = evaluator |
35 | 36 |
36 # Instance factory objects. | 37 # Instance factory objects. |
37 self._test_data_generator_factory = ( | 38 self._test_data_generator_factory = ( |
38 test_data_generation_factory.TestDataGeneratorFactory( | 39 test_data_generation_factory.TestDataGeneratorFactory( |
39 aechen_ir_database_path=aechen_ir_database_path)) | 40 aechen_ir_database_path=aechen_ir_database_path)) |
40 self._evaluation_score_factory = ( | 41 self._evaluation_score_factory = ( |
41 eval_scores_factory.EvaluationScoreWorkerFactory( | 42 eval_scores_factory.EvaluationScoreWorkerFactory( |
42 polqa_tool_bin_path=polqa_tool_bin_path)) | 43 polqa_tool_bin_path=polqa_tool_bin_path)) |
43 | 44 |
44 # Properties for each run. | 45 # Properties for each run. |
45 self._base_output_path = None | 46 self._base_output_path = None |
46 self._test_data_generators = None | 47 self._test_data_generators = None |
47 self._evaluation_score_workers = None | 48 self._evaluation_score_workers = None |
48 self._config_filepaths = None | 49 self._config_filepaths = None |
49 self._input_filepaths = None | 50 self._capture_input_filepaths = None |
51 self._render_input_filepaths = None | |
50 | 52 |
51 def Run(self, config_filepaths, input_filepaths, test_data_generator_names, | 53 def Run(self, config_filepaths, capture_input_filepaths, |
52 eval_score_names, output_dir): | 54 test_data_generator_names, eval_score_names, output_dir, |
55 render_input_filepaths=None, echo_path_simulator_name=None): | |
53 """Runs the APM simulation. | 56 """Runs the APM simulation. |
54 | 57 |
55 Initializes paths and required instances, then runs all the simulations. | 58 Initializes paths and required instances, then runs all the simulations. |
59 The render input can be optionally added. If added, the number of capture | |
60 input audio tracks and the number of render input audio tracks have to | |
61 equal. The two lists are used to form pairs of capture and render input. | |
peah-webrtc
2017/05/03 08:10:08
input->inputs
peah-webrtc
2017/05/03 08:10:08
be equal
| |
56 | 62 |
57 Args: | 63 Args: |
58 config_filepaths: set of APM configuration files to test. | 64 config_filepaths: set of APM configuration files to test. |
59 input_filepaths: set of input audio track files to test. | 65 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. | 66 test_data_generator_names: set of test data generator names to test. |
61 eval_score_names: set of evaluation score names to test. | 67 eval_score_names: set of evaluation score names to test. |
62 output_dir: base path to the output directory for wav files and outcomes. | 68 output_dir: base path to the output directory for wav files and outcomes. |
69 render_input_filepaths: set of render input audio track files to test. | |
70 echo_path_simulator_name: name of the echo path simulator to use when | |
71 render input is provided. | |
63 """ | 72 """ |
73 assert render_input_filepaths is None or ( | |
74 len(capture_input_filepaths) == len(render_input_filepaths)), ( | |
75 'render input set size not matching input set size') | |
76 assert render_input_filepaths is None or echo_path_simulator_name in ( | |
77 echo_path_simulation.EchoPathSimulator.REGISTERED_CLASSES), ( | |
78 'invalid echo path simulator') | |
64 self._base_output_path = os.path.abspath(output_dir) | 79 self._base_output_path = os.path.abspath(output_dir) |
65 | 80 |
66 # Instance test data generators. | 81 # Instance test data generators. |
67 self._test_data_generators = [self._test_data_generator_factory.GetInstance( | 82 self._test_data_generators = [self._test_data_generator_factory.GetInstance( |
68 test_data_generators_class=( | 83 test_data_generators_class=( |
69 self._TEST_DATA_GENERATOR_CLASSES[name])) for name in ( | 84 self._TEST_DATA_GENERATOR_CLASSES[name])) for name in ( |
70 test_data_generator_names)] | 85 test_data_generator_names)] |
71 | 86 |
72 # Instance evaluation score workers. | 87 # Instance evaluation score workers. |
73 self._evaluation_score_workers = [ | 88 self._evaluation_score_workers = [ |
74 self._evaluation_score_factory.GetInstance( | 89 self._evaluation_score_factory.GetInstance( |
75 evaluation_score_class=self._EVAL_SCORE_WORKER_CLASSES[name]) for ( | 90 evaluation_score_class=self._EVAL_SCORE_WORKER_CLASSES[name]) for ( |
76 name) in eval_score_names] | 91 name) in eval_score_names] |
77 | 92 |
78 # Set APM configuration file paths. | 93 # Set APM configuration file paths. |
79 self._config_filepaths = self._CreatePathsCollection(config_filepaths) | 94 self._config_filepaths = self._CreatePathsCollection(config_filepaths) |
80 | 95 |
81 # Set probing signal file paths. | 96 # Set probing signal file paths. |
82 self._input_filepaths = self._CreatePathsCollection(input_filepaths) | 97 if render_input_filepaths is None: |
98 # Capture input only. | |
99 self._capture_input_filepaths = self._CreatePathsCollection( | |
100 capture_input_filepaths) | |
101 self._render_input_filepaths = None | |
102 else: | |
103 # Set both capture and render input signals. | |
104 self._SetTestInputSignalFilePaths( | |
105 capture_input_filepaths, render_input_filepaths) | |
106 # TODO(alessiob): Instanciate EchoPathSimulator through factory. | |
peah-webrtc
2017/05/03 08:10:08
Instanciate -> Instantiate
| |
83 | 107 |
84 self._SimulateAll() | 108 self._SimulateAll() |
85 | 109 |
86 def _SimulateAll(self): | 110 def _SimulateAll(self): |
87 """Runs all the simulations. | 111 """Runs all the simulations. |
88 | 112 |
89 Iterates over the combinations of APM configurations, probing signals, and | 113 Iterates over the combinations of APM configurations, probing signals, and |
90 test data generators. | 114 test data generators. |
91 """ | 115 """ |
116 without_render_input = self._render_input_filepaths is None | |
peah-webrtc
2017/05/03 08:10:08
suggestion: without_render_input -> no_render_inpu
| |
117 | |
92 # Try different APM config files. | 118 # Try different APM config files. |
93 for config_name in self._config_filepaths: | 119 for config_name in self._config_filepaths: |
94 config_filepath = self._config_filepaths[config_name] | 120 config_filepath = self._config_filepaths[config_name] |
95 | 121 |
96 # Try different probing signal files. | 122 # Try different probing signal files. |
97 for input_name in self._input_filepaths: | 123 for capture_input_name in self._capture_input_filepaths: |
98 input_filepath = self._input_filepaths[input_name] | 124 capture_input_filepath = self._capture_input_filepaths[ |
125 capture_input_name] | |
126 render_input_filepath = None if without_render_input else ( | |
127 self._render_input_filepaths[capture_input_name]) | |
99 | 128 |
100 # Try different test data generators. | 129 # Try different test data generators. |
101 for test_data_generators in self._test_data_generators: | 130 for test_data_generators in self._test_data_generators: |
102 logging.info('config: <%s>, input: <%s>, noise: <%s>', | 131 logging.info('config: <%s>, input: <%s>, noise: <%s>', |
103 config_name, input_name, test_data_generators.NAME) | 132 config_name, capture_input_name, |
133 test_data_generators.NAME) | |
104 | 134 |
105 # Output path for the input-noise pairs. It is used to cache the noisy | 135 # Output path for the input-noise pairs. It is used to cache the noisy |
106 # copies of the probing signals (shared across some simulations). | 136 # copies of the probing signals (shared across some simulations). |
107 input_noise_cache_path = os.path.join( | 137 input_noise_cache_path = os.path.join( |
108 self._base_output_path, | 138 self._base_output_path, |
109 '_cache', | 139 '_cache', |
110 'input_{}-noise_{}'.format(input_name, test_data_generators.NAME)) | 140 'input_{}-noise_{}'.format(capture_input_name, |
141 test_data_generators.NAME)) | |
111 data_access.MakeDirectory(input_noise_cache_path) | 142 data_access.MakeDirectory(input_noise_cache_path) |
112 logging.debug('input-noise cache path: <%s>', input_noise_cache_path) | 143 logging.debug('input-noise cache path: <%s>', input_noise_cache_path) |
113 | 144 |
114 # Full output path. | 145 # Full output path. |
146 input_dir_suffix = capture_input_name if without_render_input else ( | |
147 '{}-{}'.format(capture_input_name, self._ExtractFileName( | |
148 render_input_filepath))) | |
115 output_path = os.path.join( | 149 output_path = os.path.join( |
116 self._base_output_path, | 150 self._base_output_path, |
117 'cfg-{}'.format(config_name), | 151 'cfg-{}'.format(config_name), |
118 'input-{}'.format(input_name), | 152 'input-{}'.format(input_dir_suffix), |
119 'gen-{}'.format(test_data_generators.NAME)) | 153 'gen-{}'.format(test_data_generators.NAME)) |
120 data_access.MakeDirectory(output_path) | 154 data_access.MakeDirectory(output_path) |
121 logging.debug('output path: <%s>', output_path) | 155 logging.debug('output path: <%s>', output_path) |
122 | 156 |
123 self._Simulate(test_data_generators, input_filepath, | 157 self._Simulate(test_data_generators, capture_input_filepath, |
124 input_noise_cache_path, output_path, config_filepath) | 158 render_input_filepath, input_noise_cache_path, |
159 output_path, config_filepath,) | |
125 | 160 |
126 def _Simulate(self, test_data_generators, input_filepath, | 161 def _Simulate(self, test_data_generators, capture_input_filepath, |
127 input_noise_cache_path, output_path, config_filepath): | 162 render_input_filepath, input_noise_cache_path, output_path, |
163 config_filepath): | |
128 """Runs a single set of simulation. | 164 """Runs a single set of simulation. |
129 | 165 |
130 Simulates a given combination of APM configuration, probing signal, and | 166 Simulates a given combination of APM configuration, probing signal, and |
131 test data generator. It iterates over the test data generator | 167 test data generator. It iterates over the test data generator |
132 internal configurations. | 168 internal configurations. |
133 | 169 |
134 Args: | 170 Args: |
135 test_data_generators: TestDataGenerator instance. | 171 test_data_generators: TestDataGenerator instance. |
136 input_filepath: input audio track file to test. | 172 capture_input_filepath: clean capture input audio track file to test. |
173 render_input_filepath: render input audio track file to test. | |
137 input_noise_cache_path: path for the noisy audio track files. | 174 input_noise_cache_path: path for the noisy audio track files. |
138 output_path: base output path for the test data generator. | 175 output_path: base output path for the test data generator. |
139 config_filepath: APM configuration file to test. | 176 config_filepath: APM configuration file to test. |
140 """ | 177 """ |
141 # Generate pairs of noisy input and reference signal files. | 178 # Generate pairs of noisy input and reference signal files. |
142 test_data_generators.Generate( | 179 test_data_generators.Generate( |
143 input_signal_filepath=input_filepath, | 180 input_signal_filepath=capture_input_filepath, |
144 input_noise_cache_path=input_noise_cache_path, | 181 input_noise_cache_path=input_noise_cache_path, |
145 base_output_path=output_path) | 182 base_output_path=output_path) |
146 | 183 |
147 # For each test data pair, simulate a call and evaluate. | 184 # For each test data pair, simulate a call and evaluate. |
148 for config_name in test_data_generators.config_names: | 185 for config_name in test_data_generators.config_names: |
149 logging.info(' - test data generator config: <%s>', config_name) | 186 logging.info(' - test data generator config: <%s>', config_name) |
150 | 187 |
151 # APM input and output signal paths. | 188 # Paths to the test data generator output. |
152 noisy_signal_filepath = test_data_generators.noisy_signal_filepaths[ | 189 # Note that the reference signal does not depend on the render input |
153 config_name] | 190 # which is optional. |
191 noisy_capture_input_filepath = ( | |
192 test_data_generators.noisy_signal_filepaths[config_name]) | |
193 reference_signal_filepath = ( | |
194 test_data_generators.reference_signal_filepaths[config_name]) | |
195 | |
196 # Paths to the APM input signals. | |
197 if render_input_filepath is not None: | |
198 logging.info(' - render input filepath: <%s>', render_input_filepath) | |
199 # TODO(alessiob): Simulate echo path using the EchoPathSimulator | |
200 # instance with which |noisy_capture_input_filepath| is updated. | |
201 | |
202 # Path for the APM output file. | |
154 evaluation_output_path = test_data_generators.apm_output_paths[ | 203 evaluation_output_path = test_data_generators.apm_output_paths[ |
155 config_name] | 204 config_name] |
156 | 205 |
157 # Simulate a call using the audio processing module. | 206 # Simulate a call using APM. |
158 self._audioproc_wrapper.Run( | 207 self._audioproc_wrapper.Run( |
159 config_filepath=config_filepath, | 208 config_filepath=config_filepath, |
160 input_filepath=noisy_signal_filepath, | 209 capture_input_filepath=noisy_capture_input_filepath, |
210 render_input_filepath=render_input_filepath, | |
161 output_path=evaluation_output_path) | 211 output_path=evaluation_output_path) |
162 | 212 |
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. | 213 # Evaluate. |
169 self._evaluator.Run( | 214 self._evaluator.Run( |
170 evaluation_score_workers=self._evaluation_score_workers, | 215 evaluation_score_workers=self._evaluation_score_workers, |
171 apm_output_filepath=self._audioproc_wrapper.output_filepath, | 216 apm_output_filepath=self._audioproc_wrapper.output_filepath, |
172 reference_input_filepath=reference_signal_filepath, | 217 reference_input_filepath=reference_signal_filepath, |
173 output_path=evaluation_output_path) | 218 output_path=evaluation_output_path) |
174 | 219 |
220 def _SetTestInputSignalFilePaths(self, capture_input_filepaths, | |
221 render_input_filepaths): | |
222 """Sets input and render input file paths collections. | |
223 | |
224 Pairs the input and render input files by storing the file paths into two | |
225 collections. The key is the file name of the input file. | |
226 | |
227 Args: | |
228 capture_input_filepaths: list of file paths. | |
229 render_input_filepaths: list of file paths. | |
230 """ | |
231 self._capture_input_filepaths = {} | |
232 self._render_input_filepaths = {} | |
233 assert len(capture_input_filepaths) == len(render_input_filepaths) | |
234 for capture_input_filepath, render_input_filepath in zip( | |
235 capture_input_filepaths, render_input_filepaths): | |
236 name = self._ExtractFileName(capture_input_filepath) | |
237 self._capture_input_filepaths[name] = os.path.abspath( | |
238 capture_input_filepath) | |
239 self._render_input_filepaths[name] = os.path.abspath( | |
240 render_input_filepath) | |
241 | |
175 @classmethod | 242 @classmethod |
176 def _CreatePathsCollection(cls, filepaths): | 243 def _CreatePathsCollection(cls, filepaths): |
177 """Creates a collection of file paths. | 244 """Creates a collection of file paths. |
178 | 245 |
179 Given a list of file paths, makes a collection with one item for each file | 246 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 | 247 path. The value is absolute path, the key is the file name without |
181 extenstion. | 248 extenstion. |
182 | 249 |
183 Args: | 250 Args: |
184 filepaths: list of file paths. | 251 filepaths: list of file paths. |
185 | 252 |
186 Returns: | 253 Returns: |
187 A dict. | 254 A dict. |
188 """ | 255 """ |
189 filepaths_collection = {} | 256 filepaths_collection = {} |
190 for filepath in filepaths: | 257 for filepath in filepaths: |
191 name = os.path.splitext(os.path.split(filepath)[1])[0] | 258 name = cls._ExtractFileName(filepath) |
192 filepaths_collection[name] = os.path.abspath(filepath) | 259 filepaths_collection[name] = os.path.abspath(filepath) |
193 return filepaths_collection | 260 return filepaths_collection |
261 | |
262 @classmethod | |
263 def _ExtractFileName(cls, filepath): | |
264 return os.path.splitext(os.path.split(filepath)[1])[0] | |
OLD | NEW |