• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# Copyright 2016 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
5import logging
6import tempfile
7import time
8
9import common
10from autotest_lib.client.common_lib import error
11from autotest_lib.client.common_lib import site_utils
12from autotest_lib.client.common_lib.feedback import client
13from autotest_lib.server import test
14from autotest_lib.server.brillo import audio_utils
15
16
17# The amount of time to wait when producing silence (i.e. no playback).
18_SILENCE_DURATION_SECS = 5
19
20# Number of channels to generate.
21_DEFAULT_NUM_CHANNELS = 1
22# Sine wave sample rate (48kHz).
23_DEFAULT_SAMPLE_RATE = 48000
24# Sine wave default sample format is signed 16-bit PCM (two bytes).
25_DEFAULT_SAMPLE_WIDTH = 2
26# Default sine wave frequency.
27_DEFAULT_SINE_FREQUENCY = 440
28# Default duration of the sine wave in seconds.
29_DEFAULT_DURATION_SECS = 10
30
31class brillo_PlaybackAudioTest(test.test):
32    """Verify that basic audio playback works."""
33    version = 1
34
35    def __init__(self, *args, **kwargs):
36        super(brillo_PlaybackAudioTest, self).__init__(*args, **kwargs)
37        self.host = None
38
39
40    def _get_playback_cmd(self, method, dut_play_file):
41        """Get the playback command to execute based on the playback method.
42
43        @param method: A string specifiying which method to use.
44        @param dut_play_file: A string containing the path to the file to play
45                              on the DUT.
46        @return: A string containing the command to play audio using the
47                 specified method.
48
49        @raises TestError: Invalid playback method.
50        """
51        if dut_play_file:
52            if method == 'libmedia':
53                return ('brillo_audio_test --playback --libmedia '
54                        '--filename=%s' % dut_play_file)
55            elif method == 'stagefright':
56                return ('brillo_audio_test --playback --stagefright '
57                        '--filename=%s' % dut_play_file)
58            elif method == 'opensles':
59                return 'slesTest_playFdPath %s 0' % dut_play_file
60        else:
61            if method == 'libmedia':
62                return 'brillo_audio_test --playback --libmedia --sine'
63            elif method == 'stagefright':
64                return 'brillo_audio_test --playback --stagefright --sine'
65            elif method == 'opensles':
66                return 'slesTest_sawtoothBufferQueue'
67        raise error.TestError('Test called with invalid playback method.')
68
69
70    def test_playback(self, fb_query, playback_cmd, sample_width, sample_rate,
71                      num_channels, play_file_path=None):
72        """Performs a playback test.
73
74        @param fb_query: A feedback query.
75        @param playback_cmd: The playback generating command, or None for no-op.
76        @param play_file_path: A string of the path to the file being played.
77        @param sample_width: Sample width to test playback at.
78        @param sample_rate: Sample rate to test playback at.
79        @param num_channels: Number of channels to test playback with.
80        """
81        fb_query.prepare(sample_width=sample_width,
82                         sample_rate=sample_rate,
83                         duration_secs=self.duration_secs,
84                         num_channels=num_channels)
85        if playback_cmd:
86            self.host.run(playback_cmd)
87        else:
88            time.sleep(_SILENCE_DURATION_SECS)
89        if play_file_path:
90            fb_query.validate(audio_file=play_file_path)
91        else:
92            fb_query.validate()
93
94
95    def test_audio(self, fb_client, playback_method, sample_rate, sample_width,
96                   num_channels):
97        """Test audio playback with the given parameters.
98
99        @param fb_client: A feedback client implementation.
100        @param playback_method: A string representing a playback method to use.
101                                Either 'opensles', 'libmedia', or 'stagefright'.
102        @param sample_rate: Sample rate to test playback at.
103        @param sample_width: Sample width to test playback at.
104        @param num_channels: Number of channels to test playback with.
105        """
106        logging.info('Testing silent playback')
107        fb_query = fb_client.new_query(client.QUERY_AUDIO_PLAYBACK_SILENT)
108        self.test_playback(fb_query=fb_query,
109                           playback_cmd=None,
110                           sample_rate=sample_rate,
111                           sample_width=sample_width,
112                           num_channels=num_channels)
113
114        dut_play_file = None
115        host_filename = None
116        if self.use_file:
117           host_filename, dut_play_file = audio_utils.generate_sine_file(
118                   self.host, num_channels, sample_rate, sample_width,
119                   self.duration_secs, _DEFAULT_SINE_FREQUENCY, self.temp_dir)
120
121        logging.info('Testing audible playback')
122        fb_query = fb_client.new_query(client.QUERY_AUDIO_PLAYBACK_AUDIBLE)
123        playback_cmd = self._get_playback_cmd(playback_method, dut_play_file)
124
125        self.test_playback(fb_query=fb_query,
126                           playback_cmd=playback_cmd,
127                           sample_rate=sample_rate,
128                           sample_width=sample_width,
129                           num_channels=num_channels,
130                           play_file_path=host_filename)
131
132
133    def run_once(self, host, fb_client, playback_method, use_file=False,
134                 sample_widths_arr=[_DEFAULT_SAMPLE_WIDTH],
135                 sample_rates_arr=[_DEFAULT_SAMPLE_RATE],
136                 num_channels_arr=[_DEFAULT_NUM_CHANNELS],
137                 duration_secs=_DEFAULT_DURATION_SECS):
138        """Runs the test.
139
140        @param host: A host object representing the DUT.
141        @param fb_client: A feedback client implementation.
142        @param playback_method: A string representing a playback method to use.
143                                Either 'opensles', 'libmedia', or 'stagefright'.
144        @param use_file: Use a file to test audio. Must be used with
145                         playback_method 'opensles'.
146        @param sample_widths_arr: Array of sample widths to test playback at.
147        @param sample_rates_arr: Array of sample rates to test playback at.
148        @param num_channels_arr: Array of number of channels to test playback
149                                 with.
150        @param duration_secs: Duration to play file for.
151        """
152        self.host = host
153        self.duration_secs = duration_secs
154        self.use_file = use_file
155        self.temp_dir = tempfile.mkdtemp(dir=fb_client.tmp_dir)
156        failed_params = []
157        with fb_client.initialize(self, host):
158            for sample_rate in sample_rates_arr:
159                for sample_width in sample_widths_arr:
160                    for num_channels in num_channels_arr:
161                        logging.info('Running test with the following params:')
162                        logging.info('Sample rate: %d', sample_rate)
163                        logging.info('Sample width: %d', sample_width)
164                        logging.info('Number of channels: %d', num_channels)
165
166                        try:
167                            self.test_audio(fb_client=fb_client,
168                                            playback_method=playback_method,
169                                            sample_rate=sample_rate,
170                                            sample_width=sample_width,
171                                            num_channels=num_channels)
172                        except error.TestFail:
173                            logging.info('Test failed.')
174                            failed_params.append((sample_rate, sample_width,
175                                                  num_channels))
176                        finally:
177                            # Sleep to avoid conflict between different tests.
178                            time.sleep(duration_secs)
179
180        if failed_params == []:
181            logging.info('All tests successfully passed.')
182        else:
183            logging.error('The following combinations failed:')
184            for param in failed_params:
185                logging.error('Sample rate: %i, Sample width: %i, Num Channels '
186                              '%i', param[0], param[1], param[2])
187            raise error.TestFail('Some of the tests failed to pass.')
188