blob: bd1c3d5c97ce5e853ed6b9d91e313c6de0738d7c [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):
15 """A helper function to execute the playback_cmd."""
Owen Lin9d19b272013-11-28 12:13:24 +080016 cmd_utils.execute(playback_cmd(*args, **kargs))
Owen Linca365f82013-11-08 16:52:28 +080017
Cheng-Yi Chiangb0ec9042015-03-10 15:45:18 +080018
Owen Linca365f82013-11-08 16:52:28 +080019def capture(*args, **kargs):
20 """A helper function to execute the capture_cmd."""
Owen Lin9d19b272013-11-28 12:13:24 +080021 cmd_utils.execute(capture_cmd(*args, **kargs))
Owen Linca365f82013-11-08 16:52:28 +080022
Cheng-Yi Chiangb0ec9042015-03-10 15:45:18 +080023
Owen Lin2f7e7f42013-12-25 17:25:43 +080024def playback_cmd(playback_file, block_size=None, duration=None,
Owen Lin2013e462013-12-05 17:54:42 +080025 channels=2, rate=48000):
Owen Linca365f82013-11-08 16:52:28 +080026 """Gets a command to playback a file with given settings.
27
28 @param playback_file: the name of the file to play. '-' indicates to
29 playback raw audio from the stdin.
Owen Lin2f7e7f42013-12-25 17:25:43 +080030 @param block_size: the number of frames per callback(dictates latency).
Owen Linca365f82013-11-08 16:52:28 +080031 @param duration: seconds to playback
32 @param rate: the sampling rate
Cheng-Yi Chiangb0ec9042015-03-10 15:45:18 +080033
Owen Linca365f82013-11-08 16:52:28 +080034 """
35 args = [_CRAS_TEST_CLIENT]
36 args += ['--playback_file', playback_file]
Owen Lin2f7e7f42013-12-25 17:25:43 +080037 if block_size is not None:
38 args += ['--block_size', str(block_size)]
Owen Linca365f82013-11-08 16:52:28 +080039 if duration is not None:
40 args += ['--duration', str(duration)]
Owen Lin2013e462013-12-05 17:54:42 +080041 args += ['--num_channels', str(channels)]
Owen Linca365f82013-11-08 16:52:28 +080042 args += ['--rate', str(rate)]
43 return args
44
Cheng-Yi Chiangb0ec9042015-03-10 15:45:18 +080045
Owen Lina8129c62013-11-26 16:51:46 +080046def capture_cmd(
Owen Lin2f7e7f42013-12-25 17:25:43 +080047 capture_file, block_size=None, duration=10, channels=1, rate=48000):
Owen Linca365f82013-11-08 16:52:28 +080048 """Gets a command to capture the audio into the file with given settings.
49
50 @param capture_file: the name of file the audio to be stored in.
Owen Lin2f7e7f42013-12-25 17:25:43 +080051 @param block_size: the number of frames per callback(dictates latency).
Cheng-Yi Chiangeaf53292015-01-12 20:53:56 +080052 @param duration: seconds to record. If it is None, duration is not set,
53 and command will keep capturing audio until it is
54 terminated.
Owen Linca365f82013-11-08 16:52:28 +080055 @param rate: the sampling rate.
Cheng-Yi Chiangb0ec9042015-03-10 15:45:18 +080056
Owen Linca365f82013-11-08 16:52:28 +080057 """
58 args = [_CRAS_TEST_CLIENT]
59 args += ['--capture_file', capture_file]
Owen Lin2f7e7f42013-12-25 17:25:43 +080060 if block_size is not None:
61 args += ['--block_size', str(block_size)]
Cheng-Yi Chiangeaf53292015-01-12 20:53:56 +080062 if duration is not None:
63 args += ['--duration', str(duration)]
Owen Lina8129c62013-11-26 16:51:46 +080064 args += ['--num_channels', str(channels)]
Owen Linca365f82013-11-08 16:52:28 +080065 args += ['--rate', str(rate)]
66 return args
67
Owen Lindae7a0d2013-12-05 13:34:06 +080068
69def loopback(*args, **kargs):
70 """A helper function to execute loopback_cmd."""
71 cmd_utils.execute(loopback_cmd(*args, **kargs))
72
73
Owen Lin2013e462013-12-05 17:54:42 +080074def loopback_cmd(output_file, duration=10, channels=2, rate=48000):
Owen Lindae7a0d2013-12-05 13:34:06 +080075 """Gets a command to record the loopback.
76
77 @param output_file: The name of the file the loopback to be stored in.
78 @param channels: The number of channels of the recorded audio.
79 @param duration: seconds to record.
80 @param rate: the sampling rate.
Cheng-Yi Chiangb0ec9042015-03-10 15:45:18 +080081
Owen Lindae7a0d2013-12-05 13:34:06 +080082 """
83 args = [_CRAS_TEST_CLIENT]
84 args += ['--loopback_file', output_file]
85 args += ['--duration_seconds', str(duration)]
86 args += ['--num_channels', str(channels)]
87 args += ['--rate', str(rate)]
88 return args
89
90
Cheng-Yi Chiang8de78112015-05-27 14:47:08 +080091def get_cras_nodes_cmd():
92 """Gets a command to query the nodes from Cras.
93
94 @returns: The command to query nodes information from Cras using dbus-send.
95
96 """
97 return ('dbus-send --system --type=method_call --print-reply '
98 '--dest=org.chromium.cras /org/chromium/cras '
99 'org.chromium.cras.Control.GetNodes')
100
101
Owen Linca365f82013-11-08 16:52:28 +0800102def set_system_volume(volume):
103 """Set the system volume.
104
105 @param volume: the system output vlume to be set(0 - 100).
Cheng-Yi Chiangb0ec9042015-03-10 15:45:18 +0800106
Owen Linca365f82013-11-08 16:52:28 +0800107 """
Cheng-Yi Chiang79b9ada2015-05-27 14:46:56 +0800108 get_cras_control_interface().SetOutputVolume(volume)
Owen Linca365f82013-11-08 16:52:28 +0800109
Cheng-Yi Chiangb0ec9042015-03-10 15:45:18 +0800110
Owen Linca365f82013-11-08 16:52:28 +0800111def set_node_volume(node_id, volume):
112 """Set the volume of the given output node.
113
114 @param node_id: the id of the output node to be set the volume.
115 @param volume: the volume to be set(0-100).
Cheng-Yi Chiangb0ec9042015-03-10 15:45:18 +0800116
Owen Linca365f82013-11-08 16:52:28 +0800117 """
Cheng-Yi Chiang79b9ada2015-05-27 14:46:56 +0800118 get_cras_control_interface().SetOutputNodeVolume(node_id, volume)
Owen Linca365f82013-11-08 16:52:28 +0800119
Cheng-Yi Chiangb0ec9042015-03-10 15:45:18 +0800120
Owen Linca365f82013-11-08 16:52:28 +0800121def set_capture_gain(gain):
122 """Set the system capture gain.
Cheng-Yi Chiangb0ec9042015-03-10 15:45:18 +0800123
Owen Linca365f82013-11-08 16:52:28 +0800124 @param gain the capture gain in db*100 (100 = 1dB)
Cheng-Yi Chiangb0ec9042015-03-10 15:45:18 +0800125
Owen Linca365f82013-11-08 16:52:28 +0800126 """
Cheng-Yi Chiang79b9ada2015-05-27 14:46:56 +0800127 get_cras_control_interface().SetInputGain(gain)
Owen Linca365f82013-11-08 16:52:28 +0800128
Cheng-Yi Chiangb0ec9042015-03-10 15:45:18 +0800129
Cheng-Yi Chiang79b9ada2015-05-27 14:46:56 +0800130def get_cras_control_interface():
131 """Gets Cras DBus control interface.
132
133 @returns: A dBus.Interface object with Cras Control interface.
134
135 @raises: ImportError if this is not called on Cros device.
136
137 """
138 try:
139 import dbus
140 except ImportError, e:
141 logging.exception(
142 'Can not import dbus: %s. This method should only be '
143 'called on Cros device.', e)
144 raise
145 bus = dbus.SystemBus()
146 cras_object = bus.get_object('org.chromium.cras', '/org/chromium/cras')
147 return dbus.Interface(cras_object, 'org.chromium.cras.Control')
148
149
150def get_cras_nodes():
151 """Gets nodes information from Cras.
152
153 @returns: A dict containing information of each node.
154
155 """
156 return get_cras_control_interface().GetNodes()
Owen Linca365f82013-11-08 16:52:28 +0800157
Cheng-Yi Chiangb0ec9042015-03-10 15:45:18 +0800158
Owen Linca365f82013-11-08 16:52:28 +0800159def get_selected_nodes():
Cheng-Yi Chiang79b9ada2015-05-27 14:46:56 +0800160 """Gets selected output nodes and input nodes.
Owen Linca365f82013-11-08 16:52:28 +0800161
Cheng-Yi Chiang79b9ada2015-05-27 14:46:56 +0800162 @returns: A tuple (output_nodes, input_nodes) where each
163 field is a list of selected node IDs returned from Cras DBus API.
164 Note that there may be multiple output/input nodes being selected
165 at the same time.
166
167 """
168 output_nodes = []
169 input_nodes = []
170 nodes = get_cras_nodes()
171 for node in nodes:
172 if node['Active']:
173 if node['IsInput']:
174 input_nodes.append(node['Id'])
175 else:
176 output_nodes.append(node['Id'])
177 return (output_nodes, input_nodes)
Owen Lin7ab45a22013-11-19 17:26:33 +0800178
Cheng-Yi Chiangc902f2e2015-03-09 10:45:44 +0800179
180def set_selected_output_node_volume(volume):
181 """Sets the selected output node volume.
182
183 @param volume: the volume to be set (0-100).
Cheng-Yi Chiangb0ec9042015-03-10 15:45:18 +0800184
Cheng-Yi Chiangc902f2e2015-03-09 10:45:44 +0800185 """
Cheng-Yi Chiang79b9ada2015-05-27 14:46:56 +0800186 selected_output_node_ids, _ = get_selected_nodes()
187 for node_id in selected_output_node_ids:
188 set_node_volume(node_id, volume)
Cheng-Yi Chiangc902f2e2015-03-09 10:45:44 +0800189
190
Owen Lin7ab45a22013-11-19 17:26:33 +0800191def get_active_stream_count():
Cheng-Yi Chiang79b9ada2015-05-27 14:46:56 +0800192 """Gets the number of active streams.
193
194 @returns: The number of active streams.
195
196 """
197 return int(get_cras_control_interface().GetNumberOfActiveStreams())
Owen Lin56050862013-12-09 11:42:51 +0800198
199
200def set_system_mute(is_mute):
201 """Sets the system mute switch.
202
203 @param is_mute: Set True to mute the system playback.
Cheng-Yi Chiangb0ec9042015-03-10 15:45:18 +0800204
Owen Lin56050862013-12-09 11:42:51 +0800205 """
Cheng-Yi Chiang79b9ada2015-05-27 14:46:56 +0800206 get_cras_control_interface().SetOutputMute(is_mute)
Owen Lin56050862013-12-09 11:42:51 +0800207
208
209def set_capture_mute(is_mute):
210 """Sets the capture mute switch.
211
212 @param is_mute: Set True to mute the capture.
Cheng-Yi Chiangb0ec9042015-03-10 15:45:18 +0800213
Owen Lin56050862013-12-09 11:42:51 +0800214 """
Cheng-Yi Chiang79b9ada2015-05-27 14:46:56 +0800215 get_cras_control_interface().SetInputMute(is_mute)
Cheng-Yi Chiangf4104ff2014-12-23 19:39:01 +0800216
217
Cheng-Yi Chiang8de78112015-05-27 14:47:08 +0800218def node_type_is_plugged(node_type, nodes_info):
Cheng-Yi Chiangf4104ff2014-12-23 19:39:01 +0800219 """Determine if there is any node of node_type plugged.
220
Cheng-Yi Chiang8de78112015-05-27 14:47:08 +0800221 This method is used in has_loopback_dongle in cros_host, where
222 the call is executed on autotest server. Use get_cras_nodes instead if
223 the call can be executed on Cros device.
Cheng-Yi Chiangf4104ff2014-12-23 19:39:01 +0800224
Cheng-Yi Chiang8de78112015-05-27 14:47:08 +0800225 Since Cras only reports the plugged node in GetNodes, we can
226 parse the return value to see if there is any node with the given type.
227 For example, if INTERNAL_MIC is of intereset, the pattern we are
228 looking for is:
Cheng-Yi Chiangf4104ff2014-12-23 19:39:01 +0800229
Cheng-Yi Chiang8de78112015-05-27 14:47:08 +0800230 dict entry(
231 string "Type"
232 variant string "INTERNAL_MIC"
233 )
Cheng-Yi Chiangf4104ff2014-12-23 19:39:01 +0800234
Cheng-Yi Chiang8de78112015-05-27 14:47:08 +0800235 @param node_type: A str representing node type defined in CRAS_NODE_TYPES.
236 @param nodes_info: A str containing output of command get_nodes_cmd.
Cheng-Yi Chiangf4104ff2014-12-23 19:39:01 +0800237
238 @returns: True if there is any node of node_type plugged. False otherwise.
239
Cheng-Yi Chiangf4104ff2014-12-23 19:39:01 +0800240 """
Cheng-Yi Chiang8de78112015-05-27 14:47:08 +0800241 match = re.search(r'string "Type"\s+variant\s+string "%s"' % node_type,
242 nodes_info)
243 return True if match else False
Cheng-Yi Chiangea5a71f2015-03-19 21:01:24 +0800244
245
Cheng-Yi Chiang8de78112015-05-27 14:47:08 +0800246# Cras node types reported from Cras DBus control API.
Cheng-Yi Chiangea5a71f2015-03-19 21:01:24 +0800247CRAS_OUTPUT_NODE_TYPES = ['HEADPHONE', 'INTERNAL_SPEAKER', 'HDMI', 'USB',
248 'BLUETOOTH']
249CRAS_INPUT_NODE_TYPES = ['MIC', 'INTERNAL_MIC', 'USB', 'BLUETOOTH']
250CRAS_NODE_TYPES = CRAS_OUTPUT_NODE_TYPES + CRAS_INPUT_NODE_TYPES
251
252
Cheng-Yi Chiang79b9ada2015-05-27 14:46:56 +0800253def get_selected_node_types():
254 """Returns the pair of active output node types and input node types.
Cheng-Yi Chiangea5a71f2015-03-19 21:01:24 +0800255
Cheng-Yi Chiang79b9ada2015-05-27 14:46:56 +0800256 @returns: A tuple (output_node_types, input_node_types) where each
257 field is a list of selected node types defined in CRAS_NODE_TYPES.
Cheng-Yi Chiangea5a71f2015-03-19 21:01:24 +0800258
259 """
Cheng-Yi Chiang79b9ada2015-05-27 14:46:56 +0800260 output_node_types = []
261 input_node_types = []
262 nodes = get_cras_nodes()
263 for node in nodes:
264 if node['Active']:
265 node_type = str(node['Type'])
266 if node_type not in CRAS_NODE_TYPES:
267 raise RuntimeError(
268 'node type %s is not valid' % node_type)
269 if node['IsInput']:
270 input_node_types.append(node_type)
271 else:
272 output_node_types.append(node_type)
273 return (output_node_types, input_node_types)