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 start_listening(self, data_format):
    117         """Starts listening horword on DUT.
    118 
    119         @param data_format: A dict containing:
    120                             file_type: 'raw'.
    121                             sample_format: 'S16_LE' for 16-bit signed integer in
    122                                            little-endian.
    123                             channel: channel number.
    124                             rate: sampling rate.
    125 
    126         @returns: True
    127 
    128         """
    129         self._audio_proxy.start_listening(data_format)
    130         return True
    131 
    132 
    133     def stop_listening(self):
    134         """Stops listening on DUT.
    135 
    136         @returns: the path to the recorded file on DUT.
    137 
    138         @raises: AudioFacadeError if hotwording does not work on DUT.
    139         """
    140         path = self._audio_proxy.stop_listening()
    141         if not path:
    142             raise AudioFacadeError('Listening does not work on DUT.')
    143         return path
    144 
    145 
    146     def get_recorded_file(self, remote_path, local_path):
    147         """Gets a recorded file from DUT.
    148 
    149         @param remote_path: The path to the file on DUT.
    150         @param local_path: The local path for copy destination.
    151 
    152         """
    153         self._client.get_file(remote_path, local_path)
    154 
    155 
    156     def set_selected_output_volume(self, volume):
    157         """Sets the selected output volume on DUT.
    158 
    159         @param volume: the volume to be set(0-100).
    160 
    161         """
    162         self._audio_proxy.set_selected_output_volume(volume)
    163 
    164 
    165     def set_input_gain(self, gain):
    166         """Sets the system capture gain.
    167 
    168         @param gain: the capture gain in db*100 (100 = 1dB)
    169 
    170         """
    171         self._audio_proxy.set_input_gain(gain)
    172 
    173 
    174     def set_selected_node_types(self, output_node_types, input_node_types):
    175         """Set selected node types.
    176 
    177         The node types are defined in cras_utils.CRAS_NODE_TYPES.
    178 
    179         @param output_node_types: A list of output node types.
    180                                   None to skip setting.
    181         @param input_node_types: A list of input node types.
    182                                  None to skip setting.
    183 
    184         """
    185         self._audio_proxy.set_selected_node_types(
    186                 output_node_types, input_node_types)
    187 
    188 
    189     def get_selected_node_types(self):
    190         """Gets the selected output and input node types on DUT.
    191 
    192         @returns: A tuple (output_node_types, input_node_types) where each
    193                   field is a list of selected node types defined in
    194                   cras_utils.CRAS_NODE_TYPES.
    195 
    196         """
    197         return self._audio_proxy.get_selected_node_types()
    198 
    199 
    200     def get_plugged_node_types(self):
    201         """Gets the plugged output and input node types on DUT.
    202 
    203         @returns: A tuple (output_node_types, input_node_types) where each
    204                   field is a list of plugged node types defined in
    205                   cras_utils.CRAS_NODE_TYPES.
    206 
    207         """
    208         return self._audio_proxy.get_plugged_node_types()
    209 
    210 
    211     def dump_diagnostics(self, file_path):
    212         """Dumps audio diagnostics results to a file.
    213 
    214         @param file_path: The path to dump results.
    215 
    216         @returns: True
    217 
    218         """
    219         _, remote_path = tempfile.mkstemp(
    220                 prefix='audio_dump_', suffix='.txt')
    221         self._audio_proxy.dump_diagnostics(remote_path)
    222         self._client.get_file(remote_path, file_path)
    223         return True
    224 
    225 
    226     def start_counting_signal(self, signal_name):
    227         """Starts counting DBus signal from Cras.
    228 
    229         @param signal_name: Signal of interest.
    230 
    231         """
    232         self._audio_proxy.start_counting_signal(signal_name)
    233 
    234 
    235     def stop_counting_signal(self):
    236         """Stops counting DBus signal from Cras.
    237 
    238         @returns: Number of signals counted starting from last
    239                   start_counting_signal call.
    240 
    241         """
    242         return self._audio_proxy.stop_counting_signal()
    243 
    244 
    245     def wait_for_unexpected_nodes_changed(self, timeout_secs):
    246         """Waits for unexpected nodes changed signal.
    247 
    248         @param timeout_secs: Timeout in seconds for waiting.
    249 
    250         """
    251         self._audio_proxy.wait_for_unexpected_nodes_changed(timeout_secs)
    252 
    253 
    254     def set_chrome_active_volume(self, volume):
    255         """Sets the active audio output volume using chrome.audio API.
    256 
    257         @param volume: Volume to set (0~100).
    258 
    259         """
    260         self._audio_proxy.set_chrome_active_volume(volume)
    261 
    262 
    263     def set_chrome_mute(self, mute):
    264         """Mutes the active audio output using chrome.audio API.
    265 
    266         @param mute: True to mute. False otherwise.
    267 
    268         """
    269         self._audio_proxy.set_chrome_mute(mute)
    270 
    271     def check_audio_stream_at_selected_device(self):
    272         """Checks the audio output is at expected node"""
    273         self._audio_proxy.check_audio_stream_at_selected_device()
    274 
    275 
    276     def get_chrome_active_volume_mute(self):
    277         """Gets the volume state of active audio output using chrome.audio API.
    278 
    279         @param returns: A tuple (volume, mute), where volume is 0~100, and mute
    280                         is True if node is muted, False otherwise.
    281 
    282         """
    283         return self._audio_proxy.get_chrome_active_volume_mute()
    284 
    285 
    286     def set_chrome_active_node_type(self, output_node_type, input_node_type):
    287         """Sets active node type through chrome.audio API.
    288 
    289         The node types are defined in cras_utils.CRAS_NODE_TYPES.
    290         The current active node will be disabled first if the new active node
    291         is different from the current one.
    292 
    293         @param output_node_type: A node type defined in
    294                                  cras_utils.CRAS_NODE_TYPES. None to skip.
    295         @param input_node_type: A node type defined in
    296                                  cras_utils.CRAS_NODE_TYPES. None to skip
    297 
    298         """
    299         self._audio_proxy.set_chrome_active_node_type(
    300                 output_node_type, input_node_type)
    301 
    302 
    303     def start_arc_recording(self):
    304         """Starts recording using microphone app in container."""
    305         self._audio_proxy.start_arc_recording()
    306 
    307 
    308     def stop_arc_recording(self):
    309         """Checks the recording is stopped and gets the recorded path.
    310 
    311         The recording duration of microphone app is fixed, so this method just
    312         asks Cros device to copy the recorded result from container to a path
    313         on Cros device.
    314 
    315         @returns: Path to the recorded file on DUT.
    316 
    317         """
    318         return self._audio_proxy.stop_arc_recording()
    319 
    320 
    321     def set_arc_playback_file(self, path):
    322         """Copies the file from server to Cros host and into container.
    323 
    324         @param path: Path to the file on server.
    325 
    326         @returns: Path to the file in container on Cros host.
    327 
    328         """
    329         client_file_path = self.set_playback_file(path)
    330         return self._audio_proxy.set_arc_playback_file(client_file_path)
    331 
    332 
    333     def start_arc_playback(self, path):
    334         """Starts playback through ARC on Cros host.
    335 
    336         @param path: Path to the file in container on Cros host.
    337 
    338         """
    339         self._audio_proxy.start_arc_playback(path)
    340 
    341 
    342     def stop_arc_playback(self):
    343         """Stops playback through ARC on Cros host."""
    344         self._audio_proxy.stop_arc_playback()
    345