desktopui_AudioFeedback.py revision 2d64e1fa61c136f7d1fdcb0fdb24f9bc21acba3f
1# Copyright (c) 2012 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
5import logging, tempfile
6
7from autotest_lib.client.bin import test
8from autotest_lib.client.common_lib import error
9from autotest_lib.client.cros import cros_ui_test, httpd
10from autotest_lib.client.cros.audio import audio_helper
11
12# Names of mixer controls.
13_CONTROL_MASTER = "'Master Playback Volume'"
14_CONTROL_HEADPHONE = "'Headphone Playback Volume'"
15_CONTROL_SPEAKER = "'Speaker Playback Volume'"
16_CONTROL_MIC_BOOST = "'Mic Boost Volume'"
17_CONTROL_MIC_CAPTURE = "'Mic Capture Volume'"
18_CONTROL_CAPTURE = "'Capture Volume'"
19_CONTROL_PCM = "'PCM Playback Volume'"
20_CONTROL_DIGITAL = "'Digital Capture Volume'"
21_CONTROL_CAPTURE_SWITCH = "'Capture Switch'"
22
23# Default test configuration.
24_DEFAULT_CARD = '0'
25_DEFAULT_MIXER_SETTINGS = [{'name': _CONTROL_MASTER, 'value': "100%"},
26                           {'name': _CONTROL_HEADPHONE, 'value': "100%"},
27                           {'name': _CONTROL_SPEAKER, 'value': "0%"},
28                           {'name': _CONTROL_MIC_BOOST, 'value': "50%"},
29                           {'name': _CONTROL_MIC_CAPTURE, 'value': "50%"},
30                           {'name': _CONTROL_PCM, 'value': "100%"},
31                           {'name': _CONTROL_DIGITAL, 'value': "100%"},
32                           {'name': _CONTROL_CAPTURE, 'value': "100%"},
33                           {'name': _CONTROL_CAPTURE_SWITCH, 'value': "on"}]
34
35_DEFAULT_NUM_CHANNELS = 2
36_DEFAULT_RECORD_DURATION = 15
37# Minimum RMS value to consider a "pass".
38_DEFAULT_SOX_RMS_THRESHOLD = 0.30
39
40
41class desktopui_AudioFeedback(cros_ui_test.UITest):
42    version = 1
43
44    def initialize(self,
45                   card=_DEFAULT_CARD,
46                   mixer_settings=_DEFAULT_MIXER_SETTINGS,
47                   num_channels=_DEFAULT_NUM_CHANNELS,
48                   record_duration=_DEFAULT_RECORD_DURATION,
49                   sox_min_rms=_DEFAULT_SOX_RMS_THRESHOLD):
50        """Setup the deps for the test.
51
52        Args:
53            card: The index of the sound card to use.
54            mixer_settings: Alsa control settings to apply to the mixer before
55                starting the test.
56            num_channels: The number of channels on the device to test.
57            record_duration: How long of a sample to record.
58            sox_min_rms: The minimum RMS value to consider a pass.
59
60        Raises:
61            error.TestError if the deps can't be run.
62        """
63        self._card = card
64        self._mixer_settings = mixer_settings
65        self._sox_min_rms = sox_min_rms
66
67        self._ah = audio_helper.AudioHelper(self,
68                record_duration=record_duration,
69                num_channels=num_channels)
70        self._ah.setup_deps(['sox'])
71
72        super(desktopui_AudioFeedback, self).initialize()
73        self._test_url = 'http://localhost:8000/youtube.html'
74        self._testServer = httpd.HTTPListener(8000, docroot=self.bindir)
75        self._testServer.run()
76
77    def run_once(self):
78        self._ah.set_mixer_controls(self._mixer_settings, self._card)
79        # Record a sample of "silence" to use as a noise profile.
80        with tempfile.NamedTemporaryFile(mode='w+t') as noise_file:
81            logging.info('Noise file: %s' % noise_file.name)
82            self._ah.record_sample(noise_file.name)
83
84            # Play the same video to test all channels.
85            self._ah.loopback_test_channels(noise_file,
86                    lambda channel: self.play_video(),
87                    self.check_recorded_audio)
88
89    def play_video(self):
90        """Plays a Youtube video to record audio samples.
91
92           Skipping initial 60 seconds so we can ignore initial silence
93           in the video.
94        """
95        logging.info('Playing back youtube media file %s.' % self._test_url)
96        self.pyauto.NavigateToURL(self._test_url)
97        if not self.pyauto.WaitUntil(lambda: self.pyauto.ExecuteJavascript("""
98                    player_status = document.getElementById('player_status');
99                    window.domAutomationController.send(player_status.innerHTML);
100               """), expect_retval='player ready'):
101            raise error.TestError('Failed to load the Youtube player')
102        self.pyauto.ExecuteJavascript("""
103            ytplayer.pauseVideo();
104            ytplayer.seekTo(60, true);
105            ytplayer.playVideo();
106            window.domAutomationController.send('');
107        """)
108
109    def check_recorded_audio(self, sox_output):
110        """Checks if the calculated RMS value is expected.
111
112        Args:
113            sox_output: The output from sox stat command.
114
115        Raises:
116            error.TestFail if the RMS amplitude of the recording isn't above
117                the threshold.
118        """
119        rms_val = self._ah.get_audio_rms(sox_output)
120
121        # In case sox didn't return an RMS value.
122        if rms_val is None:
123            raise error.TestError(
124                'Failed to generate an audio RMS value from playback.')
125
126        logging.info('Got audio RMS value of %f. Minimum pass is %f.' %
127                     (rms_val, self._sox_min_rms))
128        if rms_val < self._sox_min_rms:
129                raise error.TestError(
130                    'Audio RMS value %f too low. Minimum pass is %f.' %
131                    (rms_val, self._sox_min_rms))
132