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 """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