| # Copyright 2014 The Chromium OS Authors. All rights reserved. |
| # Use of this source code is governed by a BSD-style license that can be |
| # found in the LICENSE file. |
| |
| """An adapter to remotely access the audio facade on DUT.""" |
| |
| import os |
| import tempfile |
| |
| |
| class AudioFacadeError(Exception): |
| """Errors in audio facade.""" |
| pass |
| |
| |
| class AudioFacadeRemoteAdapter(object): |
| """AudioFacadeRemoteAdapter is an adapter to remotely control DUT audio. |
| |
| The Autotest host object representing the remote DUT, passed to this |
| class on initialization, can be accessed from its _client property. |
| |
| """ |
| def __init__(self, host, remote_facade_proxy): |
| """Construct an AudioFacadeRemoteAdapter. |
| |
| @param host: Host object representing a remote host. |
| @param remote_facade_proxy: RemoteFacadeProxy object. |
| |
| """ |
| self._client = host |
| self._proxy = remote_facade_proxy |
| |
| |
| @property |
| def _audio_proxy(self): |
| """Gets the proxy to DUT audio facade. |
| |
| @return XML RPC proxy to DUT audio facade. |
| |
| """ |
| return self._proxy.audio |
| |
| |
| def playback(self, client_path, data_format, blocking=False): |
| """Playback an audio file on DUT. |
| |
| @param client_path: The path to the file on DUT. |
| @param data_format: A dict containing data format including |
| file_type, sample_format, channel, and rate. |
| file_type: file type e.g. 'raw' or 'wav'. |
| sample_format: One of the keys in |
| audio_data.SAMPLE_FORMAT. |
| channel: number of channels. |
| rate: sampling rate. |
| @param blocking: Blocks this call until playback finishes. |
| |
| @returns: True |
| |
| """ |
| self._audio_proxy.playback( |
| client_path, data_format, blocking) |
| |
| |
| def stop_playback(self): |
| """Stops playback process.""" |
| self._audio_proxy.stop_playback() |
| |
| |
| def set_playback_file(self, path): |
| """Copies a file to client. |
| |
| @param path: A path to the file. |
| |
| @returns: A new path to the file on client. |
| |
| """ |
| _, ext = os.path.splitext(path) |
| _, client_file_path = tempfile.mkstemp( |
| prefix='playback_', suffix=ext) |
| self._client.send_file(path, client_file_path) |
| return client_file_path |
| |
| |
| def start_recording(self, data_format): |
| """Starts recording an audio file on DUT. |
| |
| @param data_format: A dict containing: |
| file_type: 'raw'. |
| sample_format: 'S16_LE' for 16-bit signed integer in |
| little-endian. |
| channel: channel number. |
| rate: sampling rate. |
| |
| @returns: True |
| |
| """ |
| self._audio_proxy.start_recording(data_format) |
| return True |
| |
| |
| def stop_recording(self): |
| """Stops recording on DUT. |
| |
| @returns: the path to the recorded file on DUT. |
| |
| @raises: AudioFacadeError if recorded path is None |
| """ |
| path = self._audio_proxy.stop_recording() |
| if not path: |
| raise AudioFacadeError( |
| 'Recording does not work on DUT. ' |
| 'Suggest checking messages on DUT') |
| return path |
| |
| |
| def start_listening(self, data_format): |
| """Starts listening horword on DUT. |
| |
| @param data_format: A dict containing: |
| file_type: 'raw'. |
| sample_format: 'S16_LE' for 16-bit signed integer in |
| little-endian. |
| channel: channel number. |
| rate: sampling rate. |
| |
| @returns: True |
| |
| """ |
| self._audio_proxy.start_listening(data_format) |
| return True |
| |
| |
| def stop_listening(self): |
| """Stops listening on DUT. |
| |
| @returns: the path to the recorded file on DUT. |
| |
| @raises: AudioFacadeError if hotwording does not work on DUT. |
| """ |
| path = self._audio_proxy.stop_listening() |
| if not path: |
| raise AudioFacadeError('Listening does not work on DUT.') |
| return path |
| |
| |
| def get_recorded_file(self, remote_path, local_path): |
| """Gets a recorded file from DUT. |
| |
| @param remote_path: The path to the file on DUT. |
| @param local_path: The local path for copy destination. |
| |
| """ |
| self._client.get_file(remote_path, local_path) |
| |
| |
| def set_selected_output_volume(self, volume): |
| """Sets the selected output volume on DUT. |
| |
| @param volume: the volume to be set(0-100). |
| |
| """ |
| self._audio_proxy.set_selected_output_volume(volume) |
| |
| |
| def set_input_gain(self, gain): |
| """Sets the system capture gain. |
| |
| @param gain: the capture gain in db*100 (100 = 1dB) |
| |
| """ |
| self._audio_proxy.set_input_gain(gain) |
| |
| |
| def set_selected_node_types(self, output_node_types, input_node_types): |
| """Set selected node types. |
| |
| The node types are defined in cras_utils.CRAS_NODE_TYPES. |
| |
| @param output_node_types: A list of output node types. |
| None to skip setting. |
| @param input_node_types: A list of input node types. |
| None to skip setting. |
| |
| """ |
| self._audio_proxy.set_selected_node_types( |
| output_node_types, input_node_types) |
| |
| |
| def get_selected_node_types(self): |
| """Gets the selected output and input node types on DUT. |
| |
| @returns: A tuple (output_node_types, input_node_types) where each |
| field is a list of selected node types defined in |
| cras_utils.CRAS_NODE_TYPES. |
| |
| """ |
| return self._audio_proxy.get_selected_node_types() |
| |
| |
| def get_plugged_node_types(self): |
| """Gets the plugged output and input node types on DUT. |
| |
| @returns: A tuple (output_node_types, input_node_types) where each |
| field is a list of plugged node types defined in |
| cras_utils.CRAS_NODE_TYPES. |
| |
| """ |
| return self._audio_proxy.get_plugged_node_types() |
| |
| |
| def dump_diagnostics(self, file_path): |
| """Dumps audio diagnostics results to a file. |
| |
| @param file_path: The path to dump results. |
| |
| @returns: True |
| |
| """ |
| _, remote_path = tempfile.mkstemp( |
| prefix='audio_dump_', suffix='.txt') |
| self._audio_proxy.dump_diagnostics(remote_path) |
| self._client.get_file(remote_path, file_path) |
| return True |
| |
| |
| def start_counting_signal(self, signal_name): |
| """Starts counting DBus signal from Cras. |
| |
| @param signal_name: Signal of interest. |
| |
| """ |
| self._audio_proxy.start_counting_signal(signal_name) |
| |
| |
| def stop_counting_signal(self): |
| """Stops counting DBus signal from Cras. |
| |
| @returns: Number of signals counted starting from last |
| start_counting_signal call. |
| |
| """ |
| return self._audio_proxy.stop_counting_signal() |
| |
| |
| def wait_for_unexpected_nodes_changed(self, timeout_secs): |
| """Waits for unexpected nodes changed signal. |
| |
| @param timeout_secs: Timeout in seconds for waiting. |
| |
| """ |
| self._audio_proxy.wait_for_unexpected_nodes_changed(timeout_secs) |
| |
| |
| def set_chrome_active_volume(self, volume): |
| """Sets the active audio output volume using chrome.audio API. |
| |
| @param volume: Volume to set (0~100). |
| |
| """ |
| self._audio_proxy.set_chrome_active_volume(volume) |
| |
| |
| def set_chrome_mute(self, mute): |
| """Mutes the active audio output using chrome.audio API. |
| |
| @param mute: True to mute. False otherwise. |
| |
| """ |
| self._audio_proxy.set_chrome_mute(mute) |
| |
| def check_audio_stream_at_selected_device(self): |
| """Checks the audio output is at expected node""" |
| self._audio_proxy.check_audio_stream_at_selected_device() |
| |
| |
| def get_chrome_active_volume_mute(self): |
| """Gets the volume state of active audio output using chrome.audio API. |
| |
| @param returns: A tuple (volume, mute), where volume is 0~100, and mute |
| is True if node is muted, False otherwise. |
| |
| """ |
| return self._audio_proxy.get_chrome_active_volume_mute() |
| |
| |
| def set_chrome_active_node_type(self, output_node_type, input_node_type): |
| """Sets active node type through chrome.audio API. |
| |
| The node types are defined in cras_utils.CRAS_NODE_TYPES. |
| The current active node will be disabled first if the new active node |
| is different from the current one. |
| |
| @param output_node_type: A node type defined in |
| cras_utils.CRAS_NODE_TYPES. None to skip. |
| @param input_node_type: A node type defined in |
| cras_utils.CRAS_NODE_TYPES. None to skip |
| |
| """ |
| self._audio_proxy.set_chrome_active_node_type( |
| output_node_type, input_node_type) |
| |
| |
| def start_arc_recording(self): |
| """Starts recording using microphone app in container.""" |
| self._audio_proxy.start_arc_recording() |
| |
| |
| def stop_arc_recording(self): |
| """Checks the recording is stopped and gets the recorded path. |
| |
| The recording duration of microphone app is fixed, so this method just |
| asks Cros device to copy the recorded result from container to a path |
| on Cros device. |
| |
| @returns: Path to the recorded file on DUT. |
| |
| """ |
| return self._audio_proxy.stop_arc_recording() |
| |
| |
| def set_arc_playback_file(self, path): |
| """Copies the file from server to Cros host and into container. |
| |
| @param path: Path to the file on server. |
| |
| @returns: Path to the file in container on Cros host. |
| |
| """ |
| client_file_path = self.set_playback_file(path) |
| return self._audio_proxy.set_arc_playback_file(client_file_path) |
| |
| |
| def start_arc_playback(self, path): |
| """Starts playback through ARC on Cros host. |
| |
| @param path: Path to the file in container on Cros host. |
| |
| """ |
| self._audio_proxy.start_arc_playback(path) |
| |
| |
| def stop_arc_playback(self): |
| """Stops playback through ARC on Cros host.""" |
| self._audio_proxy.stop_arc_playback() |