1# Copyright 2015 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
6"""This module provides the level control for audio widgets."""
7
8
9from autotest_lib.client.cros.chameleon import chameleon_audio_ids as ids
10
11
12class _AudioLevel(object):
13    """Audio signal level on audio widgets."""
14    # Line level signal on consumer equipment is typically -10 dBV, or
15    # 0.316 Volt RMS.
16    LINE_LEVEL = 'Line level'
17    # Mic level signal on microphone is typically -60 dBV, or
18    # 1 mV RMS.
19    MIC_LEVEL = 'Mic level'
20    # Digital signal, e.g., USB, HDMI. is not subjected to bias level or
21    # full swing constraints. The signal is guranteed to be transmitted to the
22    # other end without noise introduced on the path.
23    # Signal level is relative to full swing of data width.
24    # E.g. 2^12 is 1/8 of maximum amplitude, that is, 2^15 - 1, of signed
25    # 16 bit data format.
26    # TODO(cychiang) Check if we need to do level scaling for digital signal.
27    DIGITAL = 'Digital'
28    # The signal level of input of bluetooth module on the audio board is
29    # slightly higher than mic level.
30    BLUETOOTH_SIGNAL_INPUT_LEVEL = 'Bluetooth signal input level'
31
32
33# The relative level of audio levels. This is used to compute scale between
34# two levels.
35_RELATIVE_LEVEL = {
36    _AudioLevel.LINE_LEVEL: 1.0,
37    _AudioLevel.MIC_LEVEL: 0.033,
38    _AudioLevel.BLUETOOTH_SIGNAL_INPUT_LEVEL: 0.05,
39}
40
41_SOURCE_LEVEL_TABLE = {
42        ids.ChameleonIds.LINEOUT: _AudioLevel.LINE_LEVEL,
43        ids.ChameleonIds.USBOUT: _AudioLevel.DIGITAL,
44        ids.CrosIds.HDMI: _AudioLevel.DIGITAL,
45        ids.CrosIds.HEADPHONE: _AudioLevel.LINE_LEVEL,
46        ids.CrosIds.SPEAKER: _AudioLevel.LINE_LEVEL,
47        ids.CrosIds.BLUETOOTH_HEADPHONE: _AudioLevel.DIGITAL,
48        ids.CrosIds.USBOUT: _AudioLevel.DIGITAL,
49        ids.PeripheralIds.MIC: _AudioLevel.MIC_LEVEL,
50        ids.PeripheralIds.BLUETOOTH_DATA_RX: _AudioLevel.LINE_LEVEL,
51        ids.PeripheralIds.BLUETOOTH_DATA_TX: _AudioLevel.DIGITAL,
52}
53
54_SINK_LEVEL_TABLE = {
55        ids.ChameleonIds.HDMI: _AudioLevel.DIGITAL,
56        ids.ChameleonIds.LINEIN: _AudioLevel.LINE_LEVEL,
57        ids.ChameleonIds.USBIN: _AudioLevel.DIGITAL,
58        ids.CrosIds.EXTERNAL_MIC: _AudioLevel.MIC_LEVEL,
59        ids.CrosIds.INTERNAL_MIC: _AudioLevel.MIC_LEVEL,
60        ids.CrosIds.BLUETOOTH_MIC: _AudioLevel.DIGITAL,
61        ids.CrosIds.USBIN: _AudioLevel.DIGITAL,
62        ids.PeripheralIds.SPEAKER: _AudioLevel.LINE_LEVEL,
63        ids.PeripheralIds.BLUETOOTH_DATA_RX: _AudioLevel.DIGITAL,
64        ids.PeripheralIds.BLUETOOTH_DATA_TX:
65                _AudioLevel.BLUETOOTH_SIGNAL_INPUT_LEVEL,
66}
67
68
69class LevelController(object):
70    """The controller which sets scale between widgets of different levels."""
71    def __init__(self, source, sink):
72        """Initializes a LevelController.
73
74        @param source: An AudioWidget for source.
75        @param sink: An AudioWidget for sink.
76
77        """
78        self._source = source
79        self._sink = sink
80
81
82    def _get_needed_scale(self):
83        """Gets the needed scale for _source and _sink to balance the level.
84
85        @returns: A number for scaling on source widget.
86
87        """
88        source_level = _SOURCE_LEVEL_TABLE[self._source.port_id]
89        sink_level = _SINK_LEVEL_TABLE[self._sink.port_id]
90        if source_level == sink_level:
91            return 1
92        else:
93            return _RELATIVE_LEVEL[sink_level] / _RELATIVE_LEVEL[source_level]
94
95
96    def reset(self):
97        """Resets scale of _source."""
98        self._source.handler.scale = 1
99
100
101    def set_scale(self):
102        """Sets scale of _source to balance the level."""
103        self._source.handler.scale = self._get_needed_scale()
104        self._sink.handler.scale = 1
105