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 7import os 8import tempfile 9 10 11class AudioFacadeError(Exception): 12 """Errors in audio facade.""" 13 pass 14 15 16class 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 node_type=None, block_size=None): 46 """Playback an audio file on DUT. 47 48 @param client_path: The path to the file on DUT. 49 @param data_format: A dict containing data format including 50 file_type, sample_format, channel, and rate. 51 file_type: file type e.g. 'raw' or 'wav'. 52 sample_format: One of the keys in 53 audio_data.SAMPLE_FORMAT. 54 channel: number of channels. 55 rate: sampling rate. 56 @param blocking: Blocks this call until playback finishes. 57 @param node_type: A Cras node type defined in cras_utils.CRAS_NODE_TYPES 58 that we like to pin at. None to have the playback on 59 active selected device. 60 @param block_size: The number for frames per callback. 61 62 """ 63 self._audio_proxy.playback( 64 client_path, data_format, blocking, node_type, block_size) 65 66 67 def stop_playback(self): 68 """Stops playback process.""" 69 self._audio_proxy.stop_playback() 70 71 72 def set_playback_file(self, path): 73 """Copies a file to client. 74 75 @param path: A path to the file. 76 77 @returns: A new path to the file on client. 78 79 """ 80 _, ext = os.path.splitext(path) 81 _, client_file_path = tempfile.mkstemp( 82 prefix='playback_', suffix=ext) 83 self._client.send_file(path, client_file_path) 84 return client_file_path 85 86 87 def start_recording(self, data_format, node_type=None, block_size=None): 88 """Starts recording an audio file on DUT. 89 90 @param data_format: A dict containing: 91 file_type: 'raw'. 92 sample_format: 'S16_LE' for 16-bit signed integer in 93 little-endian. 94 channel: channel number. 95 rate: sampling rate. 96 @param node_type: A Cras node type defined in cras_utils.CRAS_NODE_TYPES 97 that we like to pin at. None to have the recording 98 from active selected device. 99 @param block_size: The number for frames per callback. 100 101 @returns: True 102 103 """ 104 self._audio_proxy.start_recording(data_format, node_type, block_size) 105 return True 106 107 108 def stop_recording(self, node_type=None): 109 """Stops recording on DUT. 110 111 @param node_type: A Cras node type defined in cras_utils.CRAS_NODE_TYPES 112 that we like to stop recording from. None to stop the 113 recording from active selected device. 114 115 @returns: the path to the recorded file on DUT. 116 117 @raises: AudioFacadeError if recorded path is None 118 """ 119 path = self._audio_proxy.stop_recording(node_type) 120 if not path: 121 raise AudioFacadeError( 122 'Recording does not work on DUT. ' 123 'Suggest checking messages on DUT') 124 return path 125 126 127 def start_listening(self, data_format): 128 """Starts listening horword on DUT. 129 130 @param data_format: A dict containing: 131 file_type: 'raw'. 132 sample_format: 'S16_LE' for 16-bit signed integer in 133 little-endian. 134 channel: channel number. 135 rate: sampling rate. 136 137 @returns: True 138 139 """ 140 self._audio_proxy.start_listening(data_format) 141 return True 142 143 144 def stop_listening(self): 145 """Stops listening on DUT. 146 147 @returns: the path to the recorded file on DUT. 148 149 @raises: AudioFacadeError if hotwording does not work on DUT. 150 """ 151 path = self._audio_proxy.stop_listening() 152 if not path: 153 raise AudioFacadeError('Listening does not work on DUT.') 154 return path 155 156 157 def get_recorded_file(self, remote_path, local_path): 158 """Gets a recorded file from DUT. 159 160 @param remote_path: The path to the file on DUT. 161 @param local_path: The local path for copy destination. 162 163 """ 164 self._client.get_file(remote_path, local_path) 165 166 167 def set_selected_output_volume(self, volume): 168 """Sets the selected output volume on DUT. 169 170 @param volume: the volume to be set(0-100). 171 172 """ 173 self._audio_proxy.set_selected_output_volume(volume) 174 175 176 def set_input_gain(self, gain): 177 """Sets the system capture gain. 178 179 @param gain: the capture gain in db*100 (100 = 1dB) 180 181 """ 182 self._audio_proxy.set_input_gain(gain) 183 184 185 def set_selected_node_types(self, output_node_types, input_node_types): 186 """Set selected node types. 187 188 The node types are defined in cras_utils.CRAS_NODE_TYPES. 189 190 @param output_node_types: A list of output node types. 191 None to skip setting. 192 @param input_node_types: A list of input node types. 193 None to skip setting. 194 195 """ 196 self._audio_proxy.set_selected_node_types( 197 output_node_types, input_node_types) 198 199 200 def get_selected_node_types(self): 201 """Gets the selected 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 selected node types defined in 205 cras_utils.CRAS_NODE_TYPES. 206 207 """ 208 return self._audio_proxy.get_selected_node_types() 209 210 211 def get_plugged_node_types(self): 212 """Gets the plugged output and input node types on DUT. 213 214 @returns: A tuple (output_node_types, input_node_types) where each 215 field is a list of plugged node types defined in 216 cras_utils.CRAS_NODE_TYPES. 217 218 """ 219 return self._audio_proxy.get_plugged_node_types() 220 221 222 def dump_diagnostics(self, file_path): 223 """Dumps audio diagnostics results to a file. 224 225 @param file_path: The path to dump results. 226 227 @returns: True 228 229 """ 230 _, remote_path = tempfile.mkstemp( 231 prefix='audio_dump_', suffix='.txt') 232 self._audio_proxy.dump_diagnostics(remote_path) 233 self._client.get_file(remote_path, file_path) 234 return True 235 236 237 def start_counting_signal(self, signal_name): 238 """Starts counting DBus signal from Cras. 239 240 @param signal_name: Signal of interest. 241 242 """ 243 self._audio_proxy.start_counting_signal(signal_name) 244 245 246 def stop_counting_signal(self): 247 """Stops counting DBus signal from Cras. 248 249 @returns: Number of signals counted starting from last 250 start_counting_signal call. 251 252 """ 253 return self._audio_proxy.stop_counting_signal() 254 255 256 def wait_for_unexpected_nodes_changed(self, timeout_secs): 257 """Waits for unexpected nodes changed signal. 258 259 @param timeout_secs: Timeout in seconds for waiting. 260 261 """ 262 self._audio_proxy.wait_for_unexpected_nodes_changed(timeout_secs) 263 264 265 def get_chrome_audio_availablity(self): 266 """Gets if the chrome.audio API is ready. 267 268 @returns: chrome.audio is ready or not. 269 """ 270 return self._audio_proxy.get_audio_availability() 271 272 273 def set_chrome_active_volume(self, volume): 274 """Sets the active audio output volume using chrome.audio API. 275 276 @param volume: Volume to set (0~100). 277 278 """ 279 self._audio_proxy.set_chrome_active_volume(volume) 280 281 282 def set_chrome_mute(self, mute): 283 """Mutes the active audio output using chrome.audio API. 284 285 @param mute: True to mute. False otherwise. 286 287 """ 288 self._audio_proxy.set_chrome_mute(mute) 289 290 def check_audio_stream_at_selected_device(self): 291 """Checks the audio output is at expected node""" 292 self._audio_proxy.check_audio_stream_at_selected_device() 293 294 295 def get_chrome_active_volume_mute(self): 296 """Gets the volume state of active audio output using chrome.audio API. 297 298 @param returns: A tuple (volume, mute), where volume is 0~100, and mute 299 is True if node is muted, False otherwise. 300 301 """ 302 return self._audio_proxy.get_chrome_active_volume_mute() 303 304 305 def set_chrome_active_node_type(self, output_node_type, input_node_type): 306 """Sets active node type through chrome.audio API. 307 308 The node types are defined in cras_utils.CRAS_NODE_TYPES. 309 The current active node will be disabled first if the new active node 310 is different from the current one. 311 312 @param output_node_type: A node type defined in 313 cras_utils.CRAS_NODE_TYPES. None to skip. 314 @param input_node_type: A node type defined in 315 cras_utils.CRAS_NODE_TYPES. None to skip 316 317 """ 318 self._audio_proxy.set_chrome_active_node_type( 319 output_node_type, input_node_type) 320 321 322 def start_arc_recording(self): 323 """Starts recording using microphone app in container.""" 324 self._audio_proxy.start_arc_recording() 325 326 327 def stop_arc_recording(self): 328 """Checks the recording is stopped and gets the recorded path. 329 330 The recording duration of microphone app is fixed, so this method just 331 asks Cros device to copy the recorded result from container to a path 332 on Cros device. 333 334 @returns: Path to the recorded file on DUT. 335 336 """ 337 return self._audio_proxy.stop_arc_recording() 338 339 340 def set_arc_playback_file(self, path): 341 """Copies the file from server to Cros host and into container. 342 343 @param path: Path to the file on server. 344 345 @returns: Path to the file in container on Cros host. 346 347 """ 348 client_file_path = self.set_playback_file(path) 349 return self._audio_proxy.set_arc_playback_file(client_file_path) 350 351 352 def start_arc_playback(self, path): 353 """Starts playback through ARC on Cros host. 354 355 @param path: Path to the file in container on Cros host. 356 357 """ 358 self._audio_proxy.start_arc_playback(path) 359 360 361 def stop_arc_playback(self): 362 """Stops playback through ARC on Cros host.""" 363 self._audio_proxy.stop_arc_playback() 364