18581bf1f70afe8655c60005bfb9f7d19042c83a4Gilad Arnold# Copyright 2016 The Chromium OS Authors. All rights reserved.
28581bf1f70afe8655c60005bfb9f7d19042c83a4Gilad Arnold# Use of this source code is governed by a BSD-style license that can be
38581bf1f70afe8655c60005bfb9f7d19042c83a4Gilad Arnold# found in the LICENSE file.
48581bf1f70afe8655c60005bfb9f7d19042c83a4Gilad Arnold
58581bf1f70afe8655c60005bfb9f7d19042c83a4Gilad Arnoldimport logging
671e94bd284ce7cb7c6feed21d85c120218aa1757Ralph Nathanimport os
771e94bd284ce7cb7c6feed21d85c120218aa1757Ralph Nathanimport subprocess
871e94bd284ce7cb7c6feed21d85c120218aa1757Ralph Nathanimport tempfile
98581bf1f70afe8655c60005bfb9f7d19042c83a4Gilad Arnoldimport time
108581bf1f70afe8655c60005bfb9f7d19042c83a4Gilad Arnold
118581bf1f70afe8655c60005bfb9f7d19042c83a4Gilad Arnoldimport common
121aaf403448c8d4e4a6aa0fbd9aa1950741a549d8Ralph Nathanfrom autotest_lib.client.common_lib import error
1371e94bd284ce7cb7c6feed21d85c120218aa1757Ralph Nathanfrom autotest_lib.client.common_lib.feedback import client
148581bf1f70afe8655c60005bfb9f7d19042c83a4Gilad Arnoldfrom autotest_lib.server import test
158581bf1f70afe8655c60005bfb9f7d19042c83a4Gilad Arnold
168581bf1f70afe8655c60005bfb9f7d19042c83a4Gilad Arnold
1771e94bd284ce7cb7c6feed21d85c120218aa1757Ralph Nathan_BITS_PER_BYTE = 8
188581bf1f70afe8655c60005bfb9f7d19042c83a4Gilad Arnold# The amount of time to wait when producing silence (i.e. no playback).
198581bf1f70afe8655c60005bfb9f7d19042c83a4Gilad Arnold_SILENCE_DURATION_SECS = 5
208581bf1f70afe8655c60005bfb9f7d19042c83a4Gilad Arnold
2171e94bd284ce7cb7c6feed21d85c120218aa1757Ralph Nathan# Number of channels to generate.
2271e94bd284ce7cb7c6feed21d85c120218aa1757Ralph Nathan_DEFAULT_NUM_CHANNELS = 1
2371e94bd284ce7cb7c6feed21d85c120218aa1757Ralph Nathan# Sine wave sample rate (48kHz).
2471e94bd284ce7cb7c6feed21d85c120218aa1757Ralph Nathan_DEFAULT_SAMPLE_RATE = 48000
2571e94bd284ce7cb7c6feed21d85c120218aa1757Ralph Nathan# Sine wave default sample format is signed 16-bit PCM (two bytes).
2671e94bd284ce7cb7c6feed21d85c120218aa1757Ralph Nathan_DEFAULT_SAMPLE_WIDTH = 2
2771e94bd284ce7cb7c6feed21d85c120218aa1757Ralph Nathan# Default sine wave frequency.
2871e94bd284ce7cb7c6feed21d85c120218aa1757Ralph Nathan_DEFAULT_SINE_FREQUENCY = 440
2971e94bd284ce7cb7c6feed21d85c120218aa1757Ralph Nathan# Default duration of the sine wave in seconds.
3071e94bd284ce7cb7c6feed21d85c120218aa1757Ralph Nathan_DEFAULT_DURATION_SECS = 10
318581bf1f70afe8655c60005bfb9f7d19042c83a4Gilad Arnold
328581bf1f70afe8655c60005bfb9f7d19042c83a4Gilad Arnoldclass brillo_PlaybackAudioTest(test.test):
338581bf1f70afe8655c60005bfb9f7d19042c83a4Gilad Arnold    """Verify that basic audio playback works."""
348581bf1f70afe8655c60005bfb9f7d19042c83a4Gilad Arnold    version = 1
358581bf1f70afe8655c60005bfb9f7d19042c83a4Gilad Arnold
368581bf1f70afe8655c60005bfb9f7d19042c83a4Gilad Arnold    def __init__(self, *args, **kwargs):
378581bf1f70afe8655c60005bfb9f7d19042c83a4Gilad Arnold        super(brillo_PlaybackAudioTest, self).__init__(*args, **kwargs)
388581bf1f70afe8655c60005bfb9f7d19042c83a4Gilad Arnold        self.host = None
398581bf1f70afe8655c60005bfb9f7d19042c83a4Gilad Arnold
408581bf1f70afe8655c60005bfb9f7d19042c83a4Gilad Arnold
4171e94bd284ce7cb7c6feed21d85c120218aa1757Ralph Nathan    def _get_playback_cmd(self, method, dut_play_file):
421aaf403448c8d4e4a6aa0fbd9aa1950741a549d8Ralph Nathan        """Get the playback command to execute based on the playback method.
431aaf403448c8d4e4a6aa0fbd9aa1950741a549d8Ralph Nathan
441aaf403448c8d4e4a6aa0fbd9aa1950741a549d8Ralph Nathan        @param method: A string specifiying which method to use.
4571e94bd284ce7cb7c6feed21d85c120218aa1757Ralph Nathan        @param dut_play_file: A string containing the path to the file to play
4671e94bd284ce7cb7c6feed21d85c120218aa1757Ralph Nathan                              on the DUT.
471aaf403448c8d4e4a6aa0fbd9aa1950741a549d8Ralph Nathan        @return: A string containing the command to play audio using the
481aaf403448c8d4e4a6aa0fbd9aa1950741a549d8Ralph Nathan                 specified method.
491aaf403448c8d4e4a6aa0fbd9aa1950741a549d8Ralph Nathan
501aaf403448c8d4e4a6aa0fbd9aa1950741a549d8Ralph Nathan        @raises TestError: Invalid playback method.
511aaf403448c8d4e4a6aa0fbd9aa1950741a549d8Ralph Nathan        """
5271e94bd284ce7cb7c6feed21d85c120218aa1757Ralph Nathan        if dut_play_file:
5371e94bd284ce7cb7c6feed21d85c120218aa1757Ralph Nathan            return 'su root slesTest_playFdPath %s 0' % dut_play_file
541aaf403448c8d4e4a6aa0fbd9aa1950741a549d8Ralph Nathan        if method == 'libmedia':
55e21ca34c5e9edbf7f3744adffc57cb104bc6b0a7Ralph Nathan            return 'brillo_audio_test --playback --libmedia --sine'
561aaf403448c8d4e4a6aa0fbd9aa1950741a549d8Ralph Nathan        elif method == 'stagefright':
57e21ca34c5e9edbf7f3744adffc57cb104bc6b0a7Ralph Nathan            return 'brillo_audio_test --playback --stagefright --sine'
581aaf403448c8d4e4a6aa0fbd9aa1950741a549d8Ralph Nathan        elif method == 'opensles':
591aaf403448c8d4e4a6aa0fbd9aa1950741a549d8Ralph Nathan            return 'slesTest_sawtoothBufferQueue'
601aaf403448c8d4e4a6aa0fbd9aa1950741a549d8Ralph Nathan        else:
611aaf403448c8d4e4a6aa0fbd9aa1950741a549d8Ralph Nathan            raise error.TestError('Test called with invalid playback method.')
621aaf403448c8d4e4a6aa0fbd9aa1950741a549d8Ralph Nathan
631aaf403448c8d4e4a6aa0fbd9aa1950741a549d8Ralph Nathan
6471e94bd284ce7cb7c6feed21d85c120218aa1757Ralph Nathan    def test_playback(self, fb_query, playback_cmd, sample_width, sample_rate,
6571e94bd284ce7cb7c6feed21d85c120218aa1757Ralph Nathan                      duration_secs, num_channels, play_file_path=None):
668581bf1f70afe8655c60005bfb9f7d19042c83a4Gilad Arnold        """Performs a playback test.
678581bf1f70afe8655c60005bfb9f7d19042c83a4Gilad Arnold
688581bf1f70afe8655c60005bfb9f7d19042c83a4Gilad Arnold        @param fb_query: A feedback query.
698581bf1f70afe8655c60005bfb9f7d19042c83a4Gilad Arnold        @param playback_cmd: The playback generating command, or None for no-op.
7071e94bd284ce7cb7c6feed21d85c120218aa1757Ralph Nathan        @param play_file_path: A string of the path to the file being played.
7171e94bd284ce7cb7c6feed21d85c120218aa1757Ralph Nathan        @param sample_width: Sample width to test playback at.
7271e94bd284ce7cb7c6feed21d85c120218aa1757Ralph Nathan        @param sample_rate: Sample rate to test playback at.
7371e94bd284ce7cb7c6feed21d85c120218aa1757Ralph Nathan        @param num_channels: Number of channels to test playback with.
748581bf1f70afe8655c60005bfb9f7d19042c83a4Gilad Arnold        """
7571e94bd284ce7cb7c6feed21d85c120218aa1757Ralph Nathan        fb_query.prepare(sample_width=sample_width,
7671e94bd284ce7cb7c6feed21d85c120218aa1757Ralph Nathan                         sample_rate=sample_rate,
7771e94bd284ce7cb7c6feed21d85c120218aa1757Ralph Nathan                         duration_secs=duration_secs,
7871e94bd284ce7cb7c6feed21d85c120218aa1757Ralph Nathan                         num_channels=num_channels)
798581bf1f70afe8655c60005bfb9f7d19042c83a4Gilad Arnold        if playback_cmd:
808581bf1f70afe8655c60005bfb9f7d19042c83a4Gilad Arnold            self.host.run(playback_cmd)
818581bf1f70afe8655c60005bfb9f7d19042c83a4Gilad Arnold        else:
828581bf1f70afe8655c60005bfb9f7d19042c83a4Gilad Arnold            time.sleep(_SILENCE_DURATION_SECS)
8371e94bd284ce7cb7c6feed21d85c120218aa1757Ralph Nathan        if play_file_path:
8471e94bd284ce7cb7c6feed21d85c120218aa1757Ralph Nathan            fb_query.validate(audio_file=play_file_path)
8571e94bd284ce7cb7c6feed21d85c120218aa1757Ralph Nathan        else:
8671e94bd284ce7cb7c6feed21d85c120218aa1757Ralph Nathan            fb_query.validate()
878581bf1f70afe8655c60005bfb9f7d19042c83a4Gilad Arnold
888581bf1f70afe8655c60005bfb9f7d19042c83a4Gilad Arnold
8971e94bd284ce7cb7c6feed21d85c120218aa1757Ralph Nathan    def run_once(self, host, fb_client, playback_method, use_file=False,
9071e94bd284ce7cb7c6feed21d85c120218aa1757Ralph Nathan                 sample_width=_DEFAULT_SAMPLE_WIDTH,
9171e94bd284ce7cb7c6feed21d85c120218aa1757Ralph Nathan                 sample_rate=_DEFAULT_SAMPLE_RATE,
9271e94bd284ce7cb7c6feed21d85c120218aa1757Ralph Nathan                 num_channels=_DEFAULT_NUM_CHANNELS,
9371e94bd284ce7cb7c6feed21d85c120218aa1757Ralph Nathan                 duration_secs=_DEFAULT_DURATION_SECS):
948581bf1f70afe8655c60005bfb9f7d19042c83a4Gilad Arnold        """Runs the test.
958581bf1f70afe8655c60005bfb9f7d19042c83a4Gilad Arnold
968581bf1f70afe8655c60005bfb9f7d19042c83a4Gilad Arnold        @param host: A host object representing the DUT.
978581bf1f70afe8655c60005bfb9f7d19042c83a4Gilad Arnold        @param fb_client: A feedback client implementation.
981aaf403448c8d4e4a6aa0fbd9aa1950741a549d8Ralph Nathan        @param playback_method: A string representing a playback method to use.
991aaf403448c8d4e4a6aa0fbd9aa1950741a549d8Ralph Nathan                                Either 'opensles', 'libmedia', or 'stagefright'.
10071e94bd284ce7cb7c6feed21d85c120218aa1757Ralph Nathan        @param use_file: Use a file to test audio. Must be used with
10171e94bd284ce7cb7c6feed21d85c120218aa1757Ralph Nathan                         playback_method 'opensles'.
10271e94bd284ce7cb7c6feed21d85c120218aa1757Ralph Nathan        @param sample_width: Sample width to test playback at.
10371e94bd284ce7cb7c6feed21d85c120218aa1757Ralph Nathan        @param sample_rate: Sample rate to test playback at.
10471e94bd284ce7cb7c6feed21d85c120218aa1757Ralph Nathan        @param num_channels: Number of channels to test playback with.
10571e94bd284ce7cb7c6feed21d85c120218aa1757Ralph Nathan        @param duration_secs: Duration to play file for.
1068581bf1f70afe8655c60005bfb9f7d19042c83a4Gilad Arnold        """
1078581bf1f70afe8655c60005bfb9f7d19042c83a4Gilad Arnold        self.host = host
1088581bf1f70afe8655c60005bfb9f7d19042c83a4Gilad Arnold        with fb_client.initialize(self, host):
1098581bf1f70afe8655c60005bfb9f7d19042c83a4Gilad Arnold            logging.info('Testing silent playback')
1108581bf1f70afe8655c60005bfb9f7d19042c83a4Gilad Arnold            fb_query = fb_client.new_query(client.QUERY_AUDIO_PLAYBACK_SILENT)
11171e94bd284ce7cb7c6feed21d85c120218aa1757Ralph Nathan            self.test_playback(fb_query=fb_query,
11271e94bd284ce7cb7c6feed21d85c120218aa1757Ralph Nathan                               playback_cmd=None,
11371e94bd284ce7cb7c6feed21d85c120218aa1757Ralph Nathan                               sample_rate=sample_rate,
11471e94bd284ce7cb7c6feed21d85c120218aa1757Ralph Nathan                               sample_width=sample_width,
11571e94bd284ce7cb7c6feed21d85c120218aa1757Ralph Nathan                               num_channels=num_channels,
11671e94bd284ce7cb7c6feed21d85c120218aa1757Ralph Nathan                               duration_secs=duration_secs)
11771e94bd284ce7cb7c6feed21d85c120218aa1757Ralph Nathan
11871e94bd284ce7cb7c6feed21d85c120218aa1757Ralph Nathan            dut_play_file = None
11971e94bd284ce7cb7c6feed21d85c120218aa1757Ralph Nathan            host_filename = None
12071e94bd284ce7cb7c6feed21d85c120218aa1757Ralph Nathan            if use_file:
12171e94bd284ce7cb7c6feed21d85c120218aa1757Ralph Nathan                _, host_filename = tempfile.mkstemp(
12271e94bd284ce7cb7c6feed21d85c120218aa1757Ralph Nathan                        prefix='sine-', suffix='.wav',
12371e94bd284ce7cb7c6feed21d85c120218aa1757Ralph Nathan                        dir=tempfile.mkdtemp(dir=fb_client.tmp_dir))
12471e94bd284ce7cb7c6feed21d85c120218aa1757Ralph Nathan                if sample_width == 1:
12571e94bd284ce7cb7c6feed21d85c120218aa1757Ralph Nathan                    sine_format = '-e unsigned'
12671e94bd284ce7cb7c6feed21d85c120218aa1757Ralph Nathan                else:
12771e94bd284ce7cb7c6feed21d85c120218aa1757Ralph Nathan                    sine_format = '-e signed'
12871e94bd284ce7cb7c6feed21d85c120218aa1757Ralph Nathan                gen_file_cmd = ('sox -n -t wav -c %d %s -b %d -r %d %s synth %d '
12971e94bd284ce7cb7c6feed21d85c120218aa1757Ralph Nathan                       'sine %d vol 0.9' % (num_channels, sine_format,
13071e94bd284ce7cb7c6feed21d85c120218aa1757Ralph Nathan                                            sample_width * _BITS_PER_BYTE,
13171e94bd284ce7cb7c6feed21d85c120218aa1757Ralph Nathan                                            sample_rate, host_filename,
13271e94bd284ce7cb7c6feed21d85c120218aa1757Ralph Nathan                                            duration_secs,
13371e94bd284ce7cb7c6feed21d85c120218aa1757Ralph Nathan                                            _DEFAULT_SINE_FREQUENCY))
13471e94bd284ce7cb7c6feed21d85c120218aa1757Ralph Nathan                logging.info('Command to generate sine wave: %s', gen_file_cmd)
13571e94bd284ce7cb7c6feed21d85c120218aa1757Ralph Nathan                subprocess.call(gen_file_cmd, shell=True)
13671e94bd284ce7cb7c6feed21d85c120218aa1757Ralph Nathan                logging.info('Send file to DUT.')
13771e94bd284ce7cb7c6feed21d85c120218aa1757Ralph Nathan                dut_tmp_dir = '/data'
13871e94bd284ce7cb7c6feed21d85c120218aa1757Ralph Nathan                dut_play_file = os.path.join(dut_tmp_dir, 'sine.wav')
13971e94bd284ce7cb7c6feed21d85c120218aa1757Ralph Nathan                logging.info('dut_play_file %s', dut_play_file)
14071e94bd284ce7cb7c6feed21d85c120218aa1757Ralph Nathan                host.send_file(host_filename, dut_play_file)
1418581bf1f70afe8655c60005bfb9f7d19042c83a4Gilad Arnold
1428581bf1f70afe8655c60005bfb9f7d19042c83a4Gilad Arnold            logging.info('Testing audible playback')
1438581bf1f70afe8655c60005bfb9f7d19042c83a4Gilad Arnold            fb_query = fb_client.new_query(client.QUERY_AUDIO_PLAYBACK_AUDIBLE)
14471e94bd284ce7cb7c6feed21d85c120218aa1757Ralph Nathan            playback_cmd = self._get_playback_cmd(playback_method, dut_play_file)
14571e94bd284ce7cb7c6feed21d85c120218aa1757Ralph Nathan
14671e94bd284ce7cb7c6feed21d85c120218aa1757Ralph Nathan            self.test_playback(fb_query=fb_query,
14771e94bd284ce7cb7c6feed21d85c120218aa1757Ralph Nathan                               playback_cmd=playback_cmd,
14871e94bd284ce7cb7c6feed21d85c120218aa1757Ralph Nathan                               sample_rate=sample_rate,
14971e94bd284ce7cb7c6feed21d85c120218aa1757Ralph Nathan                               sample_width=sample_width,
15071e94bd284ce7cb7c6feed21d85c120218aa1757Ralph Nathan                               num_channels=num_channels,
15171e94bd284ce7cb7c6feed21d85c120218aa1757Ralph Nathan                               duration_secs=duration_secs,
15271e94bd284ce7cb7c6feed21d85c120218aa1757Ralph Nathan                               play_file_path=host_filename)
153