blob: 5034b38187bcb8b435d7be86c7ffb7e77aa20a3c [file] [log] [blame]
Owen Linca365f82013-11-08 16:52:28 +08001# Copyright (c) 2013 The Chromium 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
Cheng-Yi Chiangb0ec9042015-03-10 15:45:18 +08005"""This module provides cras audio utilities."""
6
Owen Linca365f82013-11-08 16:52:28 +08007import logging
8import re
9
Owen Lin9d19b272013-11-28 12:13:24 +080010from autotest_lib.client.cros.audio import cmd_utils
Owen Linca365f82013-11-08 16:52:28 +080011
12_CRAS_TEST_CLIENT = '/usr/bin/cras_test_client'
Owen Linca365f82013-11-08 16:52:28 +080013
14def playback(*args, **kargs):
Cheng-Yi Chiangaaa0a5e2015-05-27 12:09:17 +080015 """A helper function to execute the playback_cmd.
16
17 @param args: args passed to playback_cmd.
18 @param kargs: kargs passed to playback_cmd.
19
20 """
Owen Lin9d19b272013-11-28 12:13:24 +080021 cmd_utils.execute(playback_cmd(*args, **kargs))
Owen Linca365f82013-11-08 16:52:28 +080022
Cheng-Yi Chiangb0ec9042015-03-10 15:45:18 +080023
Owen Linca365f82013-11-08 16:52:28 +080024def capture(*args, **kargs):
Cheng-Yi Chiangaaa0a5e2015-05-27 12:09:17 +080025 """A helper function to execute the capture_cmd.
26
27 @param args: args passed to capture_cmd.
28 @param kargs: kargs passed to capture_cmd.
29
30 """
Owen Lin9d19b272013-11-28 12:13:24 +080031 cmd_utils.execute(capture_cmd(*args, **kargs))
Owen Linca365f82013-11-08 16:52:28 +080032
Cheng-Yi Chiangb0ec9042015-03-10 15:45:18 +080033
Owen Lin2f7e7f42013-12-25 17:25:43 +080034def playback_cmd(playback_file, block_size=None, duration=None,
Owen Lin2013e462013-12-05 17:54:42 +080035 channels=2, rate=48000):
Owen Linca365f82013-11-08 16:52:28 +080036 """Gets a command to playback a file with given settings.
37
38 @param playback_file: the name of the file to play. '-' indicates to
39 playback raw audio from the stdin.
Owen Lin2f7e7f42013-12-25 17:25:43 +080040 @param block_size: the number of frames per callback(dictates latency).
Owen Linca365f82013-11-08 16:52:28 +080041 @param duration: seconds to playback
Cheng-Yi Chiangaaa0a5e2015-05-27 12:09:17 +080042 @param channels: number of channels.
Owen Linca365f82013-11-08 16:52:28 +080043 @param rate: the sampling rate
Cheng-Yi Chiangb0ec9042015-03-10 15:45:18 +080044
Cheng-Yi Chiangaaa0a5e2015-05-27 12:09:17 +080045 @returns: The command args put in a list of strings.
46
Owen Linca365f82013-11-08 16:52:28 +080047 """
48 args = [_CRAS_TEST_CLIENT]
49 args += ['--playback_file', playback_file]
Owen Lin2f7e7f42013-12-25 17:25:43 +080050 if block_size is not None:
51 args += ['--block_size', str(block_size)]
Owen Linca365f82013-11-08 16:52:28 +080052 if duration is not None:
53 args += ['--duration', str(duration)]
Owen Lin2013e462013-12-05 17:54:42 +080054 args += ['--num_channels', str(channels)]
Owen Linca365f82013-11-08 16:52:28 +080055 args += ['--rate', str(rate)]
56 return args
57
Cheng-Yi Chiangb0ec9042015-03-10 15:45:18 +080058
Owen Lina8129c62013-11-26 16:51:46 +080059def capture_cmd(
Owen Lin2f7e7f42013-12-25 17:25:43 +080060 capture_file, block_size=None, duration=10, channels=1, rate=48000):
Owen Linca365f82013-11-08 16:52:28 +080061 """Gets a command to capture the audio into the file with given settings.
62
63 @param capture_file: the name of file the audio to be stored in.
Owen Lin2f7e7f42013-12-25 17:25:43 +080064 @param block_size: the number of frames per callback(dictates latency).
Cheng-Yi Chiangeaf53292015-01-12 20:53:56 +080065 @param duration: seconds to record. If it is None, duration is not set,
66 and command will keep capturing audio until it is
67 terminated.
Cheng-Yi Chiangaaa0a5e2015-05-27 12:09:17 +080068 @param channels: number of channels.
Owen Linca365f82013-11-08 16:52:28 +080069 @param rate: the sampling rate.
Cheng-Yi Chiangb0ec9042015-03-10 15:45:18 +080070
Cheng-Yi Chiangaaa0a5e2015-05-27 12:09:17 +080071 @returns: The command args put in a list of strings.
72
Owen Linca365f82013-11-08 16:52:28 +080073 """
74 args = [_CRAS_TEST_CLIENT]
75 args += ['--capture_file', capture_file]
Owen Lin2f7e7f42013-12-25 17:25:43 +080076 if block_size is not None:
77 args += ['--block_size', str(block_size)]
Cheng-Yi Chiangeaf53292015-01-12 20:53:56 +080078 if duration is not None:
79 args += ['--duration', str(duration)]
Owen Lina8129c62013-11-26 16:51:46 +080080 args += ['--num_channels', str(channels)]
Owen Linca365f82013-11-08 16:52:28 +080081 args += ['--rate', str(rate)]
82 return args
83
Owen Lindae7a0d2013-12-05 13:34:06 +080084
85def loopback(*args, **kargs):
Cheng-Yi Chiangaaa0a5e2015-05-27 12:09:17 +080086 """A helper function to execute loopback_cmd.
87
88 @param args: args passed to loopback_cmd.
89 @param kargs: kargs passed to loopback_cmd.
90
91 """
92
Owen Lindae7a0d2013-12-05 13:34:06 +080093 cmd_utils.execute(loopback_cmd(*args, **kargs))
94
95
Owen Lin2013e462013-12-05 17:54:42 +080096def loopback_cmd(output_file, duration=10, channels=2, rate=48000):
Owen Lindae7a0d2013-12-05 13:34:06 +080097 """Gets a command to record the loopback.
98
99 @param output_file: The name of the file the loopback to be stored in.
100 @param channels: The number of channels of the recorded audio.
101 @param duration: seconds to record.
102 @param rate: the sampling rate.
Cheng-Yi Chiangb0ec9042015-03-10 15:45:18 +0800103
Cheng-Yi Chiangaaa0a5e2015-05-27 12:09:17 +0800104 @returns: The command args put in a list of strings.
105
Owen Lindae7a0d2013-12-05 13:34:06 +0800106 """
107 args = [_CRAS_TEST_CLIENT]
108 args += ['--loopback_file', output_file]
109 args += ['--duration_seconds', str(duration)]
110 args += ['--num_channels', str(channels)]
111 args += ['--rate', str(rate)]
112 return args
113
114
Cheng-Yi Chiang8de78112015-05-27 14:47:08 +0800115def get_cras_nodes_cmd():
116 """Gets a command to query the nodes from Cras.
117
118 @returns: The command to query nodes information from Cras using dbus-send.
119
120 """
121 return ('dbus-send --system --type=method_call --print-reply '
122 '--dest=org.chromium.cras /org/chromium/cras '
123 'org.chromium.cras.Control.GetNodes')
124
125
Owen Linca365f82013-11-08 16:52:28 +0800126def set_system_volume(volume):
127 """Set the system volume.
128
129 @param volume: the system output vlume to be set(0 - 100).
Cheng-Yi Chiangb0ec9042015-03-10 15:45:18 +0800130
Owen Linca365f82013-11-08 16:52:28 +0800131 """
Cheng-Yi Chiang79b9ada2015-05-27 14:46:56 +0800132 get_cras_control_interface().SetOutputVolume(volume)
Owen Linca365f82013-11-08 16:52:28 +0800133
Cheng-Yi Chiangb0ec9042015-03-10 15:45:18 +0800134
Owen Linca365f82013-11-08 16:52:28 +0800135def set_node_volume(node_id, volume):
136 """Set the volume of the given output node.
137
138 @param node_id: the id of the output node to be set the volume.
139 @param volume: the volume to be set(0-100).
Cheng-Yi Chiangb0ec9042015-03-10 15:45:18 +0800140
Owen Linca365f82013-11-08 16:52:28 +0800141 """
Cheng-Yi Chiang79b9ada2015-05-27 14:46:56 +0800142 get_cras_control_interface().SetOutputNodeVolume(node_id, volume)
Owen Linca365f82013-11-08 16:52:28 +0800143
Cheng-Yi Chiangb0ec9042015-03-10 15:45:18 +0800144
Owen Linca365f82013-11-08 16:52:28 +0800145def set_capture_gain(gain):
146 """Set the system capture gain.
Cheng-Yi Chiangb0ec9042015-03-10 15:45:18 +0800147
Owen Linca365f82013-11-08 16:52:28 +0800148 @param gain the capture gain in db*100 (100 = 1dB)
Cheng-Yi Chiangb0ec9042015-03-10 15:45:18 +0800149
Owen Linca365f82013-11-08 16:52:28 +0800150 """
Cheng-Yi Chiang79b9ada2015-05-27 14:46:56 +0800151 get_cras_control_interface().SetInputGain(gain)
Owen Linca365f82013-11-08 16:52:28 +0800152
Cheng-Yi Chiangb0ec9042015-03-10 15:45:18 +0800153
Cheng-Yi Chiang79b9ada2015-05-27 14:46:56 +0800154def get_cras_control_interface():
155 """Gets Cras DBus control interface.
156
157 @returns: A dBus.Interface object with Cras Control interface.
158
159 @raises: ImportError if this is not called on Cros device.
160
161 """
162 try:
163 import dbus
164 except ImportError, e:
165 logging.exception(
166 'Can not import dbus: %s. This method should only be '
167 'called on Cros device.', e)
168 raise
169 bus = dbus.SystemBus()
170 cras_object = bus.get_object('org.chromium.cras', '/org/chromium/cras')
171 return dbus.Interface(cras_object, 'org.chromium.cras.Control')
172
173
174def get_cras_nodes():
175 """Gets nodes information from Cras.
176
177 @returns: A dict containing information of each node.
178
179 """
180 return get_cras_control_interface().GetNodes()
Owen Linca365f82013-11-08 16:52:28 +0800181
Cheng-Yi Chiangb0ec9042015-03-10 15:45:18 +0800182
Owen Linca365f82013-11-08 16:52:28 +0800183def get_selected_nodes():
Cheng-Yi Chiang79b9ada2015-05-27 14:46:56 +0800184 """Gets selected output nodes and input nodes.
Owen Linca365f82013-11-08 16:52:28 +0800185
Cheng-Yi Chiang79b9ada2015-05-27 14:46:56 +0800186 @returns: A tuple (output_nodes, input_nodes) where each
187 field is a list of selected node IDs returned from Cras DBus API.
188 Note that there may be multiple output/input nodes being selected
189 at the same time.
190
191 """
192 output_nodes = []
193 input_nodes = []
194 nodes = get_cras_nodes()
195 for node in nodes:
196 if node['Active']:
197 if node['IsInput']:
198 input_nodes.append(node['Id'])
199 else:
200 output_nodes.append(node['Id'])
201 return (output_nodes, input_nodes)
Owen Lin7ab45a22013-11-19 17:26:33 +0800202
Cheng-Yi Chiangc902f2e2015-03-09 10:45:44 +0800203
204def set_selected_output_node_volume(volume):
205 """Sets the selected output node volume.
206
207 @param volume: the volume to be set (0-100).
Cheng-Yi Chiangb0ec9042015-03-10 15:45:18 +0800208
Cheng-Yi Chiangc902f2e2015-03-09 10:45:44 +0800209 """
Cheng-Yi Chiang79b9ada2015-05-27 14:46:56 +0800210 selected_output_node_ids, _ = get_selected_nodes()
211 for node_id in selected_output_node_ids:
212 set_node_volume(node_id, volume)
Cheng-Yi Chiangc902f2e2015-03-09 10:45:44 +0800213
214
Owen Lin7ab45a22013-11-19 17:26:33 +0800215def get_active_stream_count():
Cheng-Yi Chiang79b9ada2015-05-27 14:46:56 +0800216 """Gets the number of active streams.
217
218 @returns: The number of active streams.
219
220 """
221 return int(get_cras_control_interface().GetNumberOfActiveStreams())
Owen Lin56050862013-12-09 11:42:51 +0800222
223
224def set_system_mute(is_mute):
225 """Sets the system mute switch.
226
227 @param is_mute: Set True to mute the system playback.
Cheng-Yi Chiangb0ec9042015-03-10 15:45:18 +0800228
Owen Lin56050862013-12-09 11:42:51 +0800229 """
Cheng-Yi Chiang79b9ada2015-05-27 14:46:56 +0800230 get_cras_control_interface().SetOutputMute(is_mute)
Owen Lin56050862013-12-09 11:42:51 +0800231
232
233def set_capture_mute(is_mute):
234 """Sets the capture mute switch.
235
236 @param is_mute: Set True to mute the capture.
Cheng-Yi Chiangb0ec9042015-03-10 15:45:18 +0800237
Owen Lin56050862013-12-09 11:42:51 +0800238 """
Cheng-Yi Chiang79b9ada2015-05-27 14:46:56 +0800239 get_cras_control_interface().SetInputMute(is_mute)
Cheng-Yi Chiangf4104ff2014-12-23 19:39:01 +0800240
241
Cheng-Yi Chiang8de78112015-05-27 14:47:08 +0800242def node_type_is_plugged(node_type, nodes_info):
Cheng-Yi Chiangf4104ff2014-12-23 19:39:01 +0800243 """Determine if there is any node of node_type plugged.
244
Cheng-Yi Chiang8de78112015-05-27 14:47:08 +0800245 This method is used in has_loopback_dongle in cros_host, where
246 the call is executed on autotest server. Use get_cras_nodes instead if
247 the call can be executed on Cros device.
Cheng-Yi Chiangf4104ff2014-12-23 19:39:01 +0800248
Cheng-Yi Chiang8de78112015-05-27 14:47:08 +0800249 Since Cras only reports the plugged node in GetNodes, we can
250 parse the return value to see if there is any node with the given type.
251 For example, if INTERNAL_MIC is of intereset, the pattern we are
252 looking for is:
Cheng-Yi Chiangf4104ff2014-12-23 19:39:01 +0800253
Cheng-Yi Chiang8de78112015-05-27 14:47:08 +0800254 dict entry(
255 string "Type"
256 variant string "INTERNAL_MIC"
257 )
Cheng-Yi Chiangf4104ff2014-12-23 19:39:01 +0800258
Cheng-Yi Chiang8de78112015-05-27 14:47:08 +0800259 @param node_type: A str representing node type defined in CRAS_NODE_TYPES.
260 @param nodes_info: A str containing output of command get_nodes_cmd.
Cheng-Yi Chiangf4104ff2014-12-23 19:39:01 +0800261
262 @returns: True if there is any node of node_type plugged. False otherwise.
263
Cheng-Yi Chiangf4104ff2014-12-23 19:39:01 +0800264 """
Cheng-Yi Chiang8de78112015-05-27 14:47:08 +0800265 match = re.search(r'string "Type"\s+variant\s+string "%s"' % node_type,
266 nodes_info)
267 return True if match else False
Cheng-Yi Chiangea5a71f2015-03-19 21:01:24 +0800268
269
Cheng-Yi Chiang8de78112015-05-27 14:47:08 +0800270# Cras node types reported from Cras DBus control API.
Cheng-Yi Chiangea5a71f2015-03-19 21:01:24 +0800271CRAS_OUTPUT_NODE_TYPES = ['HEADPHONE', 'INTERNAL_SPEAKER', 'HDMI', 'USB',
272 'BLUETOOTH']
273CRAS_INPUT_NODE_TYPES = ['MIC', 'INTERNAL_MIC', 'USB', 'BLUETOOTH']
274CRAS_NODE_TYPES = CRAS_OUTPUT_NODE_TYPES + CRAS_INPUT_NODE_TYPES
275
276
Cheng-Yi Chiang79b9ada2015-05-27 14:46:56 +0800277def get_selected_node_types():
278 """Returns the pair of active output node types and input node types.
Cheng-Yi Chiangea5a71f2015-03-19 21:01:24 +0800279
Cheng-Yi Chiang79b9ada2015-05-27 14:46:56 +0800280 @returns: A tuple (output_node_types, input_node_types) where each
281 field is a list of selected node types defined in CRAS_NODE_TYPES.
Cheng-Yi Chiangea5a71f2015-03-19 21:01:24 +0800282
283 """
Cheng-Yi Chiang79b9ada2015-05-27 14:46:56 +0800284 output_node_types = []
285 input_node_types = []
286 nodes = get_cras_nodes()
287 for node in nodes:
288 if node['Active']:
289 node_type = str(node['Type'])
290 if node_type not in CRAS_NODE_TYPES:
291 raise RuntimeError(
292 'node type %s is not valid' % node_type)
293 if node['IsInput']:
294 input_node_types.append(node_type)
295 else:
296 output_node_types.append(node_type)
297 return (output_node_types, input_node_types)