• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# Copyright 2016 The Chromium OS Authors. All rights reserved.
2# Copyright 2016 The Chromium OS Authors. All rights reserved.
3# Use of this source code is governed by a BSD-style license that can be
4# found in the LICENSE file.
5
6"""This is a server side internal speaker test using the Chameleon board,
7audio board and the audio box enclosure for sound isolation."""
8
9import logging
10import os
11import time
12
13from autotest_lib.server.cros.audio import audio_test
14from autotest_lib.client.cros.audio import audio_test_data
15from autotest_lib.client.cros.chameleon import audio_test_utils
16from autotest_lib.client.cros.chameleon import chameleon_audio_helper
17from autotest_lib.client.cros.chameleon import chameleon_audio_ids
18from autotest_lib.client.common_lib import error
19from autotest_lib.server.cros.multimedia import remote_facade_factory
20
21
22class audio_LeftRightInternalSpeaker(audio_test.AudioTest):
23    """Server side left/right internal speaker audio test.
24
25    This test verifies:
26    1. When a file with audio on the left channel is played, that a sound is
27       emitted from at least one speaker.
28    2. When a file with audio on the right channel is played, that a sound
29       is emitted from at least one speaker.
30
31    This test cannot verify:
32    1. If the speaker making the sound is not the one corresponding to the
33       channel the audio was embedded in in the file.
34
35    """
36    version = 1
37    DELAY_BEFORE_RECORD_SECONDS = 0.5
38    DELAY_AFTER_BINDING = 0.5
39    RECORD_SECONDS = 8
40    RIGHT_WAV_FILE_URL = (
41        'http://commondatastorage.googleapis.com/chromiumos-test-assets-'
42        'public/audio_test/chameleon/Speaker/right_440_half.wav')
43    LEFT_WAV_FILE_URL = (
44        'http://commondatastorage.googleapis.com/chromiumos-test-assets-'
45        'public/audio_test/chameleon/Speaker/left_440_half.wav')
46
47    def run_once(self, host, player):
48        """
49
50        Entry point for test case.
51
52        @param host: A reference to the DUT.
53        @param player: A string representing what audio player to use. Could
54                       be 'native' or 'browser'.
55
56        """
57
58        if not audio_test_utils.has_internal_speaker(host):
59            return
60
61        host.chameleon.setup_and_reset(self.outputdir)
62
63        facade_factory = remote_facade_factory.RemoteFacadeFactory(
64            host,
65            results_dir=self.resultsdir)
66        self.audio_facade = facade_factory.create_audio_facade()
67        self.browser_facade = facade_factory.create_browser_facade()
68
69        widget_factory = chameleon_audio_helper.AudioWidgetFactory(
70            facade_factory,
71            host)
72        self.sound_source = widget_factory.create_widget(
73            chameleon_audio_ids.CrosIds.SPEAKER)
74        self.sound_recorder = widget_factory.create_widget(
75            chameleon_audio_ids.ChameleonIds.MIC)
76
77        self.play_and_record(
78            host,
79            player,
80            'left')
81        self.process_and_save_data(channel='left')
82        self.validate_recorded_data(channel='left')
83
84        self.play_and_record(
85            host,
86            player,
87            'right')
88        self.process_and_save_data(channel='right')
89        self.validate_recorded_data(channel='right')
90
91
92    def play_and_record(self, host, player, channel):
93        """Play file using given details and record playback.
94
95        The recording is accessible through the recorder object and doesn't
96        need to be returned explicitly.
97
98        @param host: The DUT.
99        @param player: String name of audio player we intend to use.
100        @param channel: Either 'left' or 'right'
101
102        """
103
104        #audio_facade = factory.create_audio_facade()
105        audio_test_utils.dump_cros_audio_logs(
106            host, self.audio_facade, self.resultsdir,
107            'before_recording_' + channel)
108
109        # Verify that output node is correct.
110        output_nodes, _ = self.audio_facade.get_selected_node_types()
111        if output_nodes != ['INTERNAL_SPEAKER']:
112            raise error.TestFail(
113                '%s rather than internal speaker is selected on Cros '
114                'device' % output_nodes)
115        self.audio_facade.set_selected_output_volume(80)
116
117        if player == 'native':
118            if channel == 'left':
119                sound_file = audio_test_data.LEFT_CHANNEL_TEST_FILE
120            else:
121                sound_file = audio_test_data.RIGHT_CHANNEL_TEST_FILE
122
123            logging.info('Going to use cras_test_client on CrOS')
124            logging.info('Playing the file %s', sound_file)
125            self.sound_source.set_playback_data(sound_file)
126            self.sound_source.start_playback()
127            time.sleep(self.DELAY_BEFORE_RECORD_SECONDS)
128            self.sound_recorder.start_recording()
129            time.sleep(self.RECORD_SECONDS)
130            self.sound_recorder.stop_recording()
131            logging.info('Recording finished. Was done in format %s',
132                         self.sound_recorder.data_format)
133
134        elif player == 'browser':
135            if channel == 'left':
136                sound_file = self.LEFT_WAV_FILE_URL
137            else:
138                sound_file = self.RIGHT_WAV_FILE_URL
139
140            tab_descriptor = self.browser_facade.new_tab(sound_file)
141
142            time.sleep(self.DELAY_BEFORE_RECORD_SECONDS)
143            logging.info('Start recording from Chameleon.')
144            self.sound_recorder.start_recording()
145
146            time.sleep(self.RECORD_SECONDS)
147
148            self.sound_recorder.stop_recording()
149            logging.info('Stopped recording from Chameleon.')
150            self.browser_facade.close_tab(tab_descriptor)
151
152        else:
153            raise error.TestFail(
154                '%s is not in list of accepted audio players',
155                player)
156
157        audio_test_utils.dump_cros_audio_logs(
158            host, self.audio_facade, self.resultsdir,
159            'after_recording_' + channel)
160
161
162    def process_and_save_data(self, channel):
163        """Save recorded data to files and process for analysis.
164
165        @param channel: 'left' or 'right'.
166
167        """
168
169        self.sound_recorder.read_recorded_binary()
170        file_name = 'recorded_' + channel + '.raw'
171        unprocessed_file = os.path.join(self.resultsdir, file_name)
172        logging.info('Saving raw unprocessed output to %s', unprocessed_file)
173        self.sound_recorder.save_file(unprocessed_file)
174
175        # Removes the beginning of recorded data. This is to avoid artifact
176        # caused by Chameleon codec initialization in the beginning of
177        # recording.
178        self.sound_recorder.remove_head(1.0)
179
180        # Reduce noise
181        self.sound_recorder.lowpass_filter(1000)
182        file_name = 'recorded_filtered_' + channel + '.raw'
183        processsed_file = os.path.join(self.resultsdir, file_name)
184        logging.info('Saving processed sound output to %s', processsed_file)
185        self.sound_recorder.save_file(processsed_file)
186
187
188    def validate_recorded_data(self, channel):
189        """Read processed data and validate by comparing to golden file.
190
191        @param channel: 'left' or 'right'.
192
193        """
194
195        # Compares data by frequency. Audio signal recorded by microphone has
196        # gone through analog processing and through the air.
197        # This suffers from codec artifacts and noise on the path.
198        # Comparing data by frequency is more robust than comparing by
199        # correlation, which is suitable for fully-digital audio path like USB
200        # and HDMI.
201        logging.info('Validating recorded output for channel %s', channel)
202        audio_test_utils.check_recorded_frequency(
203            audio_test_data.SIMPLE_FREQUENCY_SPEAKER_TEST_FILE,
204            self.sound_recorder,
205            second_peak_ratio=0.1,
206            ignore_frequencies=[50, 60])
207