• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 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