Home | History | Annotate | Download | only in multimedia
      1 # Copyright 2014 The Chromium OS Authors. All rights reserved.
      2 # Use of this source code is governed by a BSD-style license that can be
      3 # found in the LICENSE file.
      4 
      5 """An adapter to remotely access the audio facade on DUT."""
      6 
      7 import os
      8 import tempfile
      9 
     10 
     11 class AudioFacadeError(Exception):
     12     """Errors in audio facade."""
     13     pass
     14 
     15 
     16 class AudioFacadeRemoteAdapter(object):
     17     """AudioFacadeRemoteAdapter is an adapter to remotely control DUT audio.
     18 
     19     The Autotest host object representing the remote DUT, passed to this
     20     class on initialization, can be accessed from its _client property.
     21 
     22     """
     23     def __init__(self, host, remote_facade_proxy):
     24         """Construct an AudioFacadeRemoteAdapter.
     25 
     26         @param host: Host object representing a remote host.
     27         @param remote_facade_proxy: RemoteFacadeProxy object.
     28 
     29         """
     30         self._client = host
     31         self._proxy = remote_facade_proxy
     32 
     33 
     34     @property
     35     def _audio_proxy(self):
     36         """Gets the proxy to DUT audio facade.
     37 
     38         @return XML RPC proxy to DUT audio facade.
     39 
     40         """
     41         return self._proxy.audio
     42 
     43 
     44     def playback(self, client_path, data_format, blocking=False):
     45         """Playback an audio file on DUT.
     46 
     47         @param client_path: The path to the file on DUT.
     48         @param data_format: A dict containing data format including
     49                             file_type, sample_format, channel, and rate.
     50                             file_type: file type e.g. 'raw' or 'wav'.
     51                             sample_format: One of the keys in
     52                                            audio_data.SAMPLE_FORMAT.
     53                             channel: number of channels.
     54                             rate: sampling rate.
     55         @param blocking: Blocks this call until playback finishes.
     56 
     57         @returns: True
     58 
     59         """
     60         self._audio_proxy.playback(
     61                 client_path, data_format, blocking)
     62 
     63 
     64     def stop_playback(self):
     65         """Stops playback process."""
     66         self._audio_proxy.stop_playback()
     67 
     68 
     69     def set_playback_file(self, path):
     70         """Copies a file to client.
     71 
     72         @param path: A path to the file.
     73 
     74         @returns: A new path to the file on client.
     75 
     76         """
     77         _, ext = os.path.splitext(path)
     78         _, client_file_path = tempfile.mkstemp(
     79                 prefix='playback_', suffix=ext)
     80         self._client.send_file(path, client_file_path)
     81         return client_file_path
     82 
     83 
     84     def start_recording(self, data_format):
     85         """Starts recording an audio file on DUT.
     86 
     87         @param data_format: A dict containing:
     88                             file_type: 'raw'.
     89                             sample_format: 'S16_LE' for 16-bit signed integer in
     90                                            little-endian.
     91                             channel: channel number.
     92                             rate: sampling rate.
     93 
     94         @returns: True
     95 
     96         """
     97         self._audio_proxy.start_recording(data_format)
     98         return True
     99 
    100 
    101     def stop_recording(self):
    102         """Stops recording on DUT.
    103 
    104         @returns: the path to the recorded file on DUT.
    105 
    106         @raises: AudioFacadeError if recorded path is None
    107         """
    108         path = self._audio_proxy.stop_recording()
    109         if not path:
    110             raise AudioFacadeError(
    111                     'Recording does not work on DUT. '
    112                     'Suggest checking messages on DUT')
    113         return path
    114 
    115 
    116     def get_recorded_file(self, remote_path, local_path):
    117         """Gets a recorded file from DUT.
    118 
    119         @param remote_path: The path to the file on DUT.
    120         @param local_path: The local path for copy destination.
    121 
    122         """
    123         self._client.get_file(remote_path, local_path)
    124 
    125 
    126     def set_selected_output_volume(self, volume):
    127         """Sets the selected output volume on DUT.
    128 
    129         @param volume: the volume to be set(0-100).
    130 
    131         """
    132         self._audio_proxy.set_selected_output_volume(volume)
    133 
    134 
    135     def set_input_gain(self, gain):
    136         """Sets the system capture gain.
    137 
    138         @param gain: the capture gain in db*100 (100 = 1dB)
    139 
    140         """
    141         self._audio_proxy.set_input_gain(gain)
    142 
    143 
    144     def set_selected_node_types(self, output_node_types, input_node_types):
    145         """Set selected node types.
    146 
    147         The node types are defined in cras_utils.CRAS_NODE_TYPES.
    148 
    149         @param output_node_types: A list of output node types.
    150                                   None to skip setting.
    151         @param input_node_types: A list of input node types.
    152                                  None to skip setting.
    153 
    154         """
    155         self._audio_proxy.set_selected_node_types(
    156                 output_node_types, input_node_types)
    157 
    158 
    159     def get_selected_node_types(self):
    160         """Gets the selected output and input node types on DUT.
    161 
    162         @returns: A tuple (output_node_types, input_node_types) where each
    163                   field is a list of selected node types defined in
    164                   cras_utils.CRAS_NODE_TYPES.
    165 
    166         """
    167         return self._audio_proxy.get_selected_node_types()
    168 
    169 
    170     def get_plugged_node_types(self):
    171         """Gets the plugged output and input node types on DUT.
    172 
    173         @returns: A tuple (output_node_types, input_node_types) where each
    174                   field is a list of plugged node types defined in
    175                   cras_utils.CRAS_NODE_TYPES.
    176 
    177         """
    178         return self._audio_proxy.get_plugged_node_types()
    179 
    180 
    181     def dump_diagnostics(self, file_path):
    182         """Dumps audio diagnostics results to a file.
    183 
    184         @param file_path: The path to dump results.
    185 
    186         @returns: True
    187 
    188         """
    189         _, remote_path = tempfile.mkstemp(
    190                 prefix='audio_dump_', suffix='.txt')
    191         self._audio_proxy.dump_diagnostics(remote_path)
    192         self._client.get_file(remote_path, file_path)
    193         return True
    194 
    195 
    196     def start_counting_signal(self, signal_name):
    197         """Starts counting DBus signal from Cras.
    198 
    199         @param signal_name: Signal of interest.
    200 
    201         """
    202         self._audio_proxy.start_counting_signal(signal_name)
    203 
    204 
    205     def stop_counting_signal(self):
    206         """Stops counting DBus signal from Cras.
    207 
    208         @returns: Number of signals counted starting from last
    209                   start_counting_signal call.
    210 
    211         """
    212         return self._audio_proxy.stop_counting_signal()
    213 
    214 
    215     def wait_for_unexpected_nodes_changed(self, timeout_secs):
    216         """Waits for unexpected nodes changed signal.
    217 
    218         @param timeout_secs: Timeout in seconds for waiting.
    219 
    220         """
    221         self._audio_proxy.wait_for_unexpected_nodes_changed(timeout_secs)
    222 
    223 
    224     def set_chrome_active_volume(self, volume):
    225         """Sets the active audio output volume using chrome.audio API.
    226 
    227         @param volume: Volume to set (0~100).
    228 
    229         """
    230         self._audio_proxy.set_chrome_active_volume(volume)
    231 
    232 
    233     def set_chrome_mute(self, mute):
    234         """Mutes the active audio output using chrome.audio API.
    235 
    236         @param mute: True to mute. False otherwise.
    237 
    238         """
    239         self._audio_proxy.set_chrome_mute(mute)
    240 
    241 
    242     def get_chrome_active_volume_mute(self):
    243         """Gets the volume state of active audio output using chrome.audio API.
    244 
    245         @param returns: A tuple (volume, mute), where volume is 0~100, and mute
    246                         is True if node is muted, False otherwise.
    247 
    248         """
    249         return self._audio_proxy.get_chrome_active_volume_mute()
    250 
    251 
    252     def set_chrome_active_node_type(self, output_node_type, input_node_type):
    253         """Sets active node type through chrome.audio API.
    254 
    255         The node types are defined in cras_utils.CRAS_NODE_TYPES.
    256         The current active node will be disabled first if the new active node
    257         is different from the current one.
    258 
    259         @param output_node_type: A node type defined in
    260                                  cras_utils.CRAS_NODE_TYPES. None to skip.
    261         @param input_node_type: A node type defined in
    262                                  cras_utils.CRAS_NODE_TYPES. None to skip
    263 
    264         """
    265         self._audio_proxy.set_chrome_active_node_type(
    266                 output_node_type, input_node_type)
    267 
    268 
    269     def start_arc_recording(self):
    270         """Starts recording using microphone app in container."""
    271         self._audio_proxy.start_arc_recording()
    272 
    273 
    274     def stop_arc_recording(self):
    275         """Checks the recording is stopped and gets the recorded path.
    276 
    277         The recording duration of microphone app is fixed, so this method just
    278         asks Cros device to copy the recorded result from container to a path
    279         on Cros device.
    280 
    281         @returns: Path to the recorded file on DUT.
    282 
    283         """
    284         return self._audio_proxy.stop_arc_recording()
    285 
    286 
    287     def set_arc_playback_file(self, path):
    288         """Copies the file from server to Cros host and into container.
    289 
    290         @param path: Path to the file on server.
    291 
    292         @returns: Path to the file in container on Cros host.
    293 
    294         """
    295         client_file_path = self.set_playback_file(path)
    296         return self._audio_proxy.set_arc_playback_file(client_file_path)
    297 
    298 
    299     def start_arc_playback(self, path):
    300         """Starts playback through ARC on Cros host.
    301 
    302         @param path: Path to the file in container on Cros host.
    303 
    304         """
    305         self._audio_proxy.start_arc_playback(path)
    306 
    307 
    308     def stop_arc_playback(self):
    309         """Stops playback through ARC on Cros host."""
    310         self._audio_proxy.stop_arc_playback()
    311