# Copyright 2015 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. """This module provides the audio board interface.""" import logging from autotest_lib.client.cros.chameleon import chameleon_audio_ids as ids class AudioBoard(object): """AudioBoard is an abstraction of an audio board on a Chameleon board. It provides methods to control audio board. A ChameleonConnection object is passed to the construction. """ def __init__(self, chameleon_connection): """Constructs an AudioBoard. @param chameleon_connection: A ChameleonConnection object. @returns: An AudioBoard object. """ self._audio_buses = { 1: AudioBus(1, chameleon_connection), 2: AudioBus(2, chameleon_connection) } self._chameleon_connection = chameleon_connection self._jack_plugger = None self._bluetooth_controller = BluetoothController(chameleon_connection) def get_audio_bus(self, bus_index): """Gets an audio bus on this audio board. @param bus_index: The bus index 1 or 2. @returns: An AudioBus object. """ return self._audio_buses[bus_index] def get_jack_plugger(self): """Gets an AudioJackPlugger on this audio board. @returns: An AudioJackPlugger object if there is an audio jack plugger. None if there is no audio jack plugger. @raises: AudioJackPluggerException if there is no jack plugger on this audio board. """ if self._jack_plugger is None: try: self._jack_plugger = AudioJackPlugger( self._chameleon_connection) except AudioJackPluggerException as e: logging.error( 'There is no jack plugger on this audio board. Please ' 'check the jack plugger if all labels are correctly ' 'configured.') self._jack_plugger = None return self._jack_plugger def get_bluetooth_controller(self): """Gets an BluetoothController on this audio board. @returns: An BluetoothController object. """ return self._bluetooth_controller class AudioBus(object): """AudioBus is an abstraction of an audio bus on an audio board. It provides methods to control audio bus. A ChameleonConnection object is passed to the construction. @properties: bus_index: The bus index 1 or 2. """ # Maps port id defined in chameleon_audio_ids to endpoint name used in # chameleond audio bus API. _PORT_ID_AUDIO_BUS_ENDPOINT_MAP = { ids.ChameleonIds.LINEIN: 'Chameleon FPGA line-in', ids.ChameleonIds.LINEOUT: 'Chameleon FPGA line-out', ids.CrosIds.HEADPHONE: 'Cros device headphone', ids.CrosIds.EXTERNAL_MIC: 'Cros device external microphone', ids.PeripheralIds.SPEAKER: 'Peripheral speaker', ids.PeripheralIds.MIC: 'Peripheral microphone', ids.PeripheralIds.BLUETOOTH_DATA_RX: 'Bluetooth module output', ids.PeripheralIds.BLUETOOTH_DATA_TX: 'Bluetooth module input' } class AudioBusSnapshot(object): """Abstracts the snapshot of AudioBus for user to restore it later.""" def __init__(self, endpoints): """Initializes an AudioBusSnapshot. @param endpoints: A set of endpoints to keep a copy. """ self._endpoints = endpoints.copy() def __init__(self, bus_index, chameleon_connection): """Constructs an AudioBus. @param bus_index: The bus index 1 or 2. @param chameleon_connection: A ChameleonConnection object. """ self.bus_index = bus_index self._chameleond_proxy = chameleon_connection self._connected_endpoints = set() def _get_endpoint_name(self, port_id): """Gets the endpoint name used in audio bus API. @param port_id: A string, that is, id in ChameleonIds, CrosIds, or PeripheralIds defined in chameleon_audio_ids. @returns: The endpoint name for the port used in audio bus API. """ return self._PORT_ID_AUDIO_BUS_ENDPOINT_MAP[port_id] def _connect_endpoint(self, endpoint): """Connects an endpoint to audio bus. @param endpoint: An endpoint name in _PORT_ID_AUDIO_BUS_ENDPOINT_MAP. """ logging.debug('Audio bus %s is connecting endpoint %s', self.bus_index, endpoint) self._chameleond_proxy.AudioBoardConnect(self.bus_index, endpoint) self._connected_endpoints.add(endpoint) def _disconnect_endpoint(self, endpoint): """Disconnects an endpoint from audio bus. @param endpoint: An endpoint name in _PORT_ID_AUDIO_BUS_ENDPOINT_MAP. """ logging.debug('Audio bus %s is disconnecting endpoint %s', self.bus_index, endpoint) self._chameleond_proxy.AudioBoardDisconnect(self.bus_index, endpoint) self._connected_endpoints.remove(endpoint) def connect(self, port_id): """Connects an audio port to this audio bus. @param port_id: A string, that is, id in ChameleonIds, CrosIds, or PeripheralIds defined in chameleon_audio_ids. """ endpoint = self._get_endpoint_name(port_id) self._connect_endpoint(endpoint) def disconnect(self, port_id): """Disconnects an audio port from this audio bus. @param port_id: A string, that is, id in ChameleonIds, CrosIds, or PeripheralIds defined in chameleon_audio_ids. """ endpoint = self._get_endpoint_name(port_id) self._disconnect_endpoint(endpoint) def clear(self): """Disconnects all audio port from this audio bus.""" self._disconnect_all_endpoints() def _disconnect_all_endpoints(self): """Disconnects all endpoints from this audio bus.""" for endpoint in self._connected_endpoints.copy(): self._disconnect_endpoint(endpoint) def get_snapshot(self): """Gets the snapshot of AudioBus so user can restore it later. @returns: An AudioBus.AudioBusSnapshot object. """ return self.AudioBusSnapshot(self._connected_endpoints) def restore_snapshot(self, snapshot): """Restore the snapshot. @param: An AudioBus.AudioBusSnapshot object got from get_snapshot. """ self._disconnect_all_endpoints() logging.debug('Restoring snapshot with %s', snapshot._endpoints) for endpoint in snapshot._endpoints: self._connect_endpoint(endpoint) class AudioJackPluggerException(Exception): """Errors in AudioJackPlugger.""" pass class AudioJackPlugger(object): """AudioJackPlugger is an abstraction of plugger controlled by audio board. There is a motor in the audio box which can plug/unplug 3.5mm 4-ring audio cable to/from audio jack of Cros deivce. This motor is controlled by audio board. A ChameleonConnection object is passed to the construction. """ def __init__(self, chameleon_connection): """Constructs an AudioJackPlugger. @param chameleon_connection: A ChameleonConnection object. @raises: AudioJackPluggerException if there is no jack plugger on this audio board. """ self._chameleond_proxy = chameleon_connection if not self._chameleond_proxy.AudioBoardHasJackPlugger(): raise AudioJackPluggerException( 'There is no jack plugger on audio board. ' 'Perhaps the audio board is not connected to audio box.') def plug(self): """Plugs the audio cable into audio jack of Cros device.""" self._chameleond_proxy.AudioBoardAudioJackPlug() logging.info('Plugged 3.5mm audio cable to Cros device.') def unplug(self): """Unplugs the audio cable from audio jack of Cros device.""" self._chameleond_proxy.AudioBoardAudioJackUnplug() logging.info('Unplugged 3.5mm audio cable from Cros device.') class BluetoothController(object): """An abstraction of bluetooth module on audio board. There is a bluetooth module on the audio board. It can be controlled through API provided by chameleon proxy. """ def __init__(self, chameleon_connection): """Constructs an BluetoothController. @param chameleon_connection: A ChameleonConnection object. """ self._chameleond_proxy = chameleon_connection def reset(self): """Resets the bluetooth module.""" self._chameleond_proxy.AudioBoardResetBluetooth() logging.info('Resets bluetooth module on audio board.') def disable(self): """Disables the bluetooth module.""" self._chameleond_proxy.AudioBoardDisableBluetooth() logging.info('Disables bluetooth module on audio board.') def is_enabled(self): """Checks if the bluetooth module is enabled. @returns: True if bluetooth module is enabled. False otherwise. """ return self._chameleond_proxy.AudioBoardIsBluetoothEnabled()