xref: /aosp_15_r20/external/autotest/server/site_tests/audio_AudioArtifacts/audio_AudioArtifacts.py (revision 9c5db1993ded3edbeafc8092d69fe5de2ee02df7)
1# Lint as: python2, python3
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 audio volume test using the Chameleon board."""
7
8import logging
9import os
10import time
11
12from autotest_lib.client.cros.chameleon import audio_test_utils
13from autotest_lib.client.cros.chameleon import chameleon_audio_ids
14from autotest_lib.client.cros.chameleon import chameleon_audio_helper
15from autotest_lib.server.cros.audio import audio_test
16from autotest_lib.server.cros.multimedia import remote_facade_factory
17
18
19class audio_AudioArtifacts(audio_test.AudioTest):
20    """Server side audio artifacts test.
21
22    This test talks to a Chameleon board and a Cros device to detect
23    audio artifacts from the Cros device.
24
25    """
26    version = 1
27    RECORD_SECONDS = 20
28    DELAY_AFTER_BINDING = 0.5
29    KEEP_VOLUME_SECONDS = 0.5
30    START_PLAYBACK_SECONDS = 0.5
31
32    def run_once(self, host, source_id, sink_id, recorder_id,
33                 golden_file, switch_hsp=False,
34                 mute_duration_in_secs=None,
35                 volume_changes=None,
36                 record_secs=None):
37        """Running audio volume test.
38
39        mute_duration_in_secs and volume_changes couldn't be both not None.
40
41        @param host: device under test CrosHost
42        @param source_id: An ID defined in chameleon_audio_ids for source.
43        @param sink_id: An ID defined in chameleon_audio_ids for sink if needed.
44                        Currently this is only used on bluetooth.
45        @param recorder: An ID defined in chameleon_audio_ids for recording.
46        @param golden_file: A test file defined in audio_test_data.
47        @param switch_hsp: Run a recording process on Cros device. This is
48                           to trigger Cros switching from A2DP to HSP.
49        @param mute_duration_in_secs: List of each duration of mute. For example,
50                                      [0.4, 0.6] will have two delays, each
51                                      duration will be 0.4 secs and 0.6 secs.
52                                      And, between delays, there will be
53                                      KEEP_VOLUME_SECONDS secs sine wave.
54        @param volume_changes: List consisting of -1 and +1, where -1 denoting
55                           decreasing volume, +1 denoting increasing volume.
56                           Between each changes, the volume will be kept for
57                           KEEP_VOLUME_SECONDS secs.
58        @param record_secs: The duration of recording in seconds. If it is not
59                            set, duration of the test data will be used. If
60                            duration of test data is not set either, default
61                            RECORD_SECONDS will be used.
62
63        """
64        if (source_id == chameleon_audio_ids.CrosIds.SPEAKER and
65            not audio_test_utils.has_internal_speaker(host)):
66            return
67
68        chameleon_board = host.chameleon
69        factory = remote_facade_factory.RemoteFacadeFactory(
70                host, results_dir=self.resultsdir)
71
72        chameleon_board.setup_and_reset(self.outputdir)
73
74        widget_factory = chameleon_audio_helper.AudioWidgetFactory(
75                factory, host)
76
77        source = widget_factory.create_widget(source_id)
78        recorder = widget_factory.create_widget(recorder_id)
79
80        # Chameleon Mic does not need binding.
81        binding = (recorder_id != chameleon_audio_ids.ChameleonIds.MIC)
82
83        binder = None
84
85        if binding:
86            if sink_id:
87                sink = widget_factory.create_widget(sink_id)
88                binder = widget_factory.create_binder(source, sink, recorder)
89            else:
90                binder = widget_factory.create_binder(source, recorder)
91
92        start_volume, low_volume, high_volume = 75, 50, 100
93
94        if not record_secs:
95            if golden_file.duration_secs:
96                record_secs = golden_file.duration_secs
97            else:
98                record_secs = self.RECORD_SECONDS
99        logging.debug('Record duration: %f seconds', record_secs)
100
101        # Checks if the file is local or is served on web.
102        file_url = getattr(golden_file, 'url', None)
103
104        with chameleon_audio_helper.bind_widgets(binder):
105            # Checks the node selected by cras is correct.
106            time.sleep(self.DELAY_AFTER_BINDING)
107            audio_facade = factory.create_audio_facade()
108
109            audio_test_utils.dump_cros_audio_logs(
110                    host, audio_facade, self.resultsdir, 'after_binding')
111
112            audio_facade.set_chrome_active_volume(start_volume)
113
114            audio_test_utils.check_output_port(audio_facade, source.port_id)
115
116            if switch_hsp:
117                audio_test_utils.switch_to_hsp(audio_facade)
118
119            if not file_url:
120                logging.info('Setting playback data on Cros device')
121                source.set_playback_data(golden_file)
122
123            logging.info('Start recording from Chameleon.')
124            recorder.start_recording()
125
126            if not file_url:
127                logging.info('Start playing %s on Cros device',
128                             golden_file.path)
129                source.start_playback()
130            else:
131                logging.info('Start playing %s on Cros device using browser',
132                             file_url)
133                browser_facade = factory.create_browser_facade()
134                tab_descriptor = browser_facade.new_tab(file_url)
135
136            if volume_changes:
137                time.sleep(self.START_PLAYBACK_SECONDS)
138                for x in volume_changes:
139                    if x == -1:
140                        audio_facade.set_chrome_active_volume(low_volume)
141                    if x == +1:
142                        audio_facade.set_chrome_active_volume(high_volume)
143                    time.sleep(self.KEEP_VOLUME_SECONDS)
144                passed_time_secs = self.START_PLAYBACK_SECONDS
145                passed_time_secs += len(volume_changes) * self.KEEP_VOLUME_SECONDS
146                rest = max(0, record_secs - passed_time_secs)
147                time.sleep(rest)
148            elif mute_duration_in_secs:
149                time.sleep(self.START_PLAYBACK_SECONDS)
150                passed_time_seconds = self.START_PLAYBACK_SECONDS
151                for mute_secs in mute_duration_in_secs:
152                    audio_facade.set_chrome_active_volume(0)
153                    time.sleep(mute_secs)
154                    audio_facade.set_chrome_active_volume(start_volume)
155                    time.sleep(self.KEEP_VOLUME_SECONDS)
156                    passed_time_seconds += mute_secs + self.KEEP_VOLUME_SECONDS
157                rest = max(0, record_secs - passed_time_seconds)
158                time.sleep(rest)
159            else:
160                time.sleep(record_secs)
161
162            recorder.stop_recording()
163            logging.info('Stopped recording from Chameleon.')
164
165            audio_test_utils.dump_cros_audio_logs(
166                    host, audio_facade, self.resultsdir,
167                    'after_recording')
168
169            if file_url:
170                browser_facade.close_tab(tab_descriptor)
171            else:
172                source.stop_playback()
173
174            recorder.read_recorded_binary()
175            logging.info('Read recorded binary from Chameleon.')
176
177            recorded_file = os.path.join(
178                    self.resultsdir, "recorded.raw" )
179            logging.info('Saving recorded data to %s', recorded_file)
180            recorder.save_file(recorded_file)
181
182            audio_test_utils.check_recorded_frequency(
183                    golden_file, recorder,
184                    check_artifacts=True,
185                    mute_durations=mute_duration_in_secs,
186                    volume_changes=volume_changes)
187