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 os 7import subprocess 8import tempfile 9import time 10 11import common 12from autotest_lib.client.common_lib import error 13from autotest_lib.client.common_lib.feedback import client 14from autotest_lib.server import test 15 16 17_BITS_PER_BYTE = 8 18# The amount of time to wait when producing silence (i.e. no playback). 19_SILENCE_DURATION_SECS = 5 20 21# Number of channels to generate. 22_DEFAULT_NUM_CHANNELS = 1 23# Sine wave sample rate (48kHz). 24_DEFAULT_SAMPLE_RATE = 48000 25# Sine wave default sample format is signed 16-bit PCM (two bytes). 26_DEFAULT_SAMPLE_WIDTH = 2 27# Default sine wave frequency. 28_DEFAULT_SINE_FREQUENCY = 440 29# Default duration of the sine wave in seconds. 30_DEFAULT_DURATION_SECS = 10 31 32class brillo_PlaybackAudioTest(test.test): 33 """Verify that basic audio playback works.""" 34 version = 1 35 36 def __init__(self, *args, **kwargs): 37 super(brillo_PlaybackAudioTest, self).__init__(*args, **kwargs) 38 self.host = None 39 40 41 def _get_playback_cmd(self, method, dut_play_file): 42 """Get the playback command to execute based on the playback method. 43 44 @param method: A string specifiying which method to use. 45 @param dut_play_file: A string containing the path to the file to play 46 on the DUT. 47 @return: A string containing the command to play audio using the 48 specified method. 49 50 @raises TestError: Invalid playback method. 51 """ 52 if dut_play_file: 53 return 'su root slesTest_playFdPath %s 0' % dut_play_file 54 if method == 'libmedia': 55 return 'brillo_audio_test --playback --libmedia --sine' 56 elif method == 'stagefright': 57 return 'brillo_audio_test --playback --stagefright --sine' 58 elif method == 'opensles': 59 return 'slesTest_sawtoothBufferQueue' 60 else: 61 raise error.TestError('Test called with invalid playback method.') 62 63 64 def test_playback(self, fb_query, playback_cmd, sample_width, sample_rate, 65 duration_secs, num_channels, play_file_path=None): 66 """Performs a playback test. 67 68 @param fb_query: A feedback query. 69 @param playback_cmd: The playback generating command, or None for no-op. 70 @param play_file_path: A string of the path to the file being played. 71 @param sample_width: Sample width to test playback at. 72 @param sample_rate: Sample rate to test playback at. 73 @param num_channels: Number of channels to test playback with. 74 """ 75 fb_query.prepare(sample_width=sample_width, 76 sample_rate=sample_rate, 77 duration_secs=duration_secs, 78 num_channels=num_channels) 79 if playback_cmd: 80 self.host.run(playback_cmd) 81 else: 82 time.sleep(_SILENCE_DURATION_SECS) 83 if play_file_path: 84 fb_query.validate(audio_file=play_file_path) 85 else: 86 fb_query.validate() 87 88 89 def run_once(self, host, fb_client, playback_method, use_file=False, 90 sample_width=_DEFAULT_SAMPLE_WIDTH, 91 sample_rate=_DEFAULT_SAMPLE_RATE, 92 num_channels=_DEFAULT_NUM_CHANNELS, 93 duration_secs=_DEFAULT_DURATION_SECS): 94 """Runs the test. 95 96 @param host: A host object representing the DUT. 97 @param fb_client: A feedback client implementation. 98 @param playback_method: A string representing a playback method to use. 99 Either 'opensles', 'libmedia', or 'stagefright'. 100 @param use_file: Use a file to test audio. Must be used with 101 playback_method 'opensles'. 102 @param sample_width: Sample width to test playback at. 103 @param sample_rate: Sample rate to test playback at. 104 @param num_channels: Number of channels to test playback with. 105 @param duration_secs: Duration to play file for. 106 """ 107 self.host = host 108 with fb_client.initialize(self, host): 109 logging.info('Testing silent playback') 110 fb_query = fb_client.new_query(client.QUERY_AUDIO_PLAYBACK_SILENT) 111 self.test_playback(fb_query=fb_query, 112 playback_cmd=None, 113 sample_rate=sample_rate, 114 sample_width=sample_width, 115 num_channels=num_channels, 116 duration_secs=duration_secs) 117 118 dut_play_file = None 119 host_filename = None 120 if use_file: 121 _, host_filename = tempfile.mkstemp( 122 prefix='sine-', suffix='.wav', 123 dir=tempfile.mkdtemp(dir=fb_client.tmp_dir)) 124 if sample_width == 1: 125 sine_format = '-e unsigned' 126 else: 127 sine_format = '-e signed' 128 gen_file_cmd = ('sox -n -t wav -c %d %s -b %d -r %d %s synth %d ' 129 'sine %d vol 0.9' % (num_channels, sine_format, 130 sample_width * _BITS_PER_BYTE, 131 sample_rate, host_filename, 132 duration_secs, 133 _DEFAULT_SINE_FREQUENCY)) 134 logging.info('Command to generate sine wave: %s', gen_file_cmd) 135 subprocess.call(gen_file_cmd, shell=True) 136 logging.info('Send file to DUT.') 137 dut_tmp_dir = '/data' 138 dut_play_file = os.path.join(dut_tmp_dir, 'sine.wav') 139 logging.info('dut_play_file %s', dut_play_file) 140 host.send_file(host_filename, dut_play_file) 141 142 logging.info('Testing audible playback') 143 fb_query = fb_client.new_query(client.QUERY_AUDIO_PLAYBACK_AUDIBLE) 144 playback_cmd = self._get_playback_cmd(playback_method, dut_play_file) 145 146 self.test_playback(fb_query=fb_query, 147 playback_cmd=playback_cmd, 148 sample_rate=sample_rate, 149 sample_width=sample_width, 150 num_channels=num_channels, 151 duration_secs=duration_secs, 152 play_file_path=host_filename) 153