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