1# Copyright (c) 2017 The WebRTC project authors. All Rights Reserved.
2#
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
5# tree. An additional intellectual property rights grant can be found
6# in the file PATENTS.  All contributing project authors may
7# be found in the AUTHORS file in the root of the source tree.
8"""Class implementing a wrapper for APM simulators.
9"""
10
11import cProfile
12import logging
13import os
14import subprocess
15
16from . import data_access
17from . import exceptions
18
19
20class AudioProcWrapper(object):
21    """Wrapper for APM simulators.
22  """
23
24    DEFAULT_APM_SIMULATOR_BIN_PATH = os.path.abspath(
25        os.path.join(os.pardir, 'audioproc_f'))
26    OUTPUT_FILENAME = 'output.wav'
27
28    def __init__(self, simulator_bin_path):
29        """Ctor.
30
31    Args:
32      simulator_bin_path: path to the APM simulator binary.
33    """
34        self._simulator_bin_path = simulator_bin_path
35        self._config = None
36        self._output_signal_filepath = None
37
38        # Profiler instance to measure running time.
39        self._profiler = cProfile.Profile()
40
41    @property
42    def output_filepath(self):
43        return self._output_signal_filepath
44
45    def Run(self,
46            config_filepath,
47            capture_input_filepath,
48            output_path,
49            render_input_filepath=None):
50        """Runs APM simulator.
51
52    Args:
53      config_filepath: path to the configuration file specifying the arguments
54                       for the APM simulator.
55      capture_input_filepath: path to the capture audio track input file (aka
56                              forward or near-end).
57      output_path: path of the audio track output file.
58      render_input_filepath: path to the render audio track input file (aka
59                             reverse or far-end).
60    """
61        # Init.
62        self._output_signal_filepath = os.path.join(output_path,
63                                                    self.OUTPUT_FILENAME)
64        profiling_stats_filepath = os.path.join(output_path, 'profiling.stats')
65
66        # Skip if the output has already been generated.
67        if os.path.exists(self._output_signal_filepath) and os.path.exists(
68                profiling_stats_filepath):
69            return
70
71        # Load configuration.
72        self._config = data_access.AudioProcConfigFile.Load(config_filepath)
73
74        # Set remaining parameters.
75        if not os.path.exists(capture_input_filepath):
76            raise exceptions.FileNotFoundError(
77                'cannot find capture input file')
78        self._config['-i'] = capture_input_filepath
79        self._config['-o'] = self._output_signal_filepath
80        if render_input_filepath is not None:
81            if not os.path.exists(render_input_filepath):
82                raise exceptions.FileNotFoundError(
83                    'cannot find render input file')
84            self._config['-ri'] = render_input_filepath
85
86        # Build arguments list.
87        args = [self._simulator_bin_path]
88        for param_name in self._config:
89            args.append(param_name)
90            if self._config[param_name] is not None:
91                args.append(str(self._config[param_name]))
92        logging.debug(' '.join(args))
93
94        # Run.
95        self._profiler.enable()
96        subprocess.call(args)
97        self._profiler.disable()
98
99        # Save profiling stats.
100        self._profiler.dump_stats(profiling_stats_filepath)
101