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