1# Copyright 2016 The Chromium OS Authors. All rights reserved. 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 internal speaker test using the Chameleon board, 7audio board and the audio box enclosure for sound isolation.""" 8 9import logging 10import os 11import time 12 13from autotest_lib.server.cros.audio import audio_test 14from autotest_lib.client.cros.audio import audio_test_data 15from autotest_lib.client.cros.chameleon import audio_test_utils 16from autotest_lib.client.cros.chameleon import chameleon_audio_helper 17from autotest_lib.client.cros.chameleon import chameleon_audio_ids 18from autotest_lib.client.common_lib import error 19from autotest_lib.server.cros.multimedia import remote_facade_factory 20 21 22class audio_LeftRightInternalSpeaker(audio_test.AudioTest): 23 """Server side left/right internal speaker audio test. 24 25 This test verifies: 26 1. When a file with audio on the left channel is played, that a sound is 27 emitted from at least one speaker. 28 2. When a file with audio on the right channel is played, that a sound 29 is emitted from at least one speaker. 30 31 This test cannot verify: 32 1. If the speaker making the sound is not the one corresponding to the 33 channel the audio was embedded in in the file. 34 35 """ 36 version = 1 37 DELAY_BEFORE_RECORD_SECONDS = 0.5 38 DELAY_AFTER_BINDING = 0.5 39 RECORD_SECONDS = 8 40 RIGHT_WAV_FILE_URL = ( 41 'http://commondatastorage.googleapis.com/chromiumos-test-assets-' 42 'public/audio_test/chameleon/Speaker/right_440_half.wav') 43 LEFT_WAV_FILE_URL = ( 44 'http://commondatastorage.googleapis.com/chromiumos-test-assets-' 45 'public/audio_test/chameleon/Speaker/left_440_half.wav') 46 47 def run_once(self, host, player): 48 """ 49 50 Entry point for test case. 51 52 @param host: A reference to the DUT. 53 @param player: A string representing what audio player to use. Could 54 be 'native' or 'browser'. 55 56 """ 57 58 if not audio_test_utils.has_internal_speaker(host): 59 return 60 61 host.chameleon.setup_and_reset(self.outputdir) 62 63 facade_factory = remote_facade_factory.RemoteFacadeFactory( 64 host, 65 results_dir=self.resultsdir) 66 self.audio_facade = facade_factory.create_audio_facade() 67 self.browser_facade = facade_factory.create_browser_facade() 68 69 widget_factory = chameleon_audio_helper.AudioWidgetFactory( 70 facade_factory, 71 host) 72 self.sound_source = widget_factory.create_widget( 73 chameleon_audio_ids.CrosIds.SPEAKER) 74 self.sound_recorder = widget_factory.create_widget( 75 chameleon_audio_ids.ChameleonIds.MIC) 76 77 self.play_and_record( 78 host, 79 player, 80 'left') 81 self.process_and_save_data(channel='left') 82 self.validate_recorded_data(channel='left') 83 84 self.play_and_record( 85 host, 86 player, 87 'right') 88 self.process_and_save_data(channel='right') 89 self.validate_recorded_data(channel='right') 90 91 92 def play_and_record(self, host, player, channel): 93 """Play file using given details and record playback. 94 95 The recording is accessible through the recorder object and doesn't 96 need to be returned explicitly. 97 98 @param host: The DUT. 99 @param player: String name of audio player we intend to use. 100 @param channel: Either 'left' or 'right' 101 102 """ 103 104 #audio_facade = factory.create_audio_facade() 105 audio_test_utils.dump_cros_audio_logs( 106 host, self.audio_facade, self.resultsdir, 107 'before_recording_' + channel) 108 109 # Verify that output node is correct. 110 output_nodes, _ = self.audio_facade.get_selected_node_types() 111 if output_nodes != ['INTERNAL_SPEAKER']: 112 raise error.TestFail( 113 '%s rather than internal speaker is selected on Cros ' 114 'device' % output_nodes) 115 self.audio_facade.set_selected_output_volume(80) 116 117 if player == 'native': 118 if channel == 'left': 119 sound_file = audio_test_data.LEFT_CHANNEL_TEST_FILE 120 else: 121 sound_file = audio_test_data.RIGHT_CHANNEL_TEST_FILE 122 123 logging.info('Going to use cras_test_client on CrOS') 124 logging.info('Playing the file %s', sound_file) 125 self.sound_source.set_playback_data(sound_file) 126 self.sound_source.start_playback() 127 time.sleep(self.DELAY_BEFORE_RECORD_SECONDS) 128 self.sound_recorder.start_recording() 129 time.sleep(self.RECORD_SECONDS) 130 self.sound_recorder.stop_recording() 131 logging.info('Recording finished. Was done in format %s', 132 self.sound_recorder.data_format) 133 134 elif player == 'browser': 135 if channel == 'left': 136 sound_file = self.LEFT_WAV_FILE_URL 137 else: 138 sound_file = self.RIGHT_WAV_FILE_URL 139 140 tab_descriptor = self.browser_facade.new_tab(sound_file) 141 142 time.sleep(self.DELAY_BEFORE_RECORD_SECONDS) 143 logging.info('Start recording from Chameleon.') 144 self.sound_recorder.start_recording() 145 146 time.sleep(self.RECORD_SECONDS) 147 148 self.sound_recorder.stop_recording() 149 logging.info('Stopped recording from Chameleon.') 150 self.browser_facade.close_tab(tab_descriptor) 151 152 else: 153 raise error.TestFail( 154 '%s is not in list of accepted audio players', 155 player) 156 157 audio_test_utils.dump_cros_audio_logs( 158 host, self.audio_facade, self.resultsdir, 159 'after_recording_' + channel) 160 161 162 def process_and_save_data(self, channel): 163 """Save recorded data to files and process for analysis. 164 165 @param channel: 'left' or 'right'. 166 167 """ 168 169 self.sound_recorder.read_recorded_binary() 170 file_name = 'recorded_' + channel + '.raw' 171 unprocessed_file = os.path.join(self.resultsdir, file_name) 172 logging.info('Saving raw unprocessed output to %s', unprocessed_file) 173 self.sound_recorder.save_file(unprocessed_file) 174 175 # Removes the beginning of recorded data. This is to avoid artifact 176 # caused by Chameleon codec initialization in the beginning of 177 # recording. 178 self.sound_recorder.remove_head(1.0) 179 180 # Reduce noise 181 self.sound_recorder.lowpass_filter(1000) 182 file_name = 'recorded_filtered_' + channel + '.raw' 183 processsed_file = os.path.join(self.resultsdir, file_name) 184 logging.info('Saving processed sound output to %s', processsed_file) 185 self.sound_recorder.save_file(processsed_file) 186 187 188 def validate_recorded_data(self, channel): 189 """Read processed data and validate by comparing to golden file. 190 191 @param channel: 'left' or 'right'. 192 193 """ 194 195 # Compares data by frequency. Audio signal recorded by microphone has 196 # gone through analog processing and through the air. 197 # This suffers from codec artifacts and noise on the path. 198 # Comparing data by frequency is more robust than comparing by 199 # correlation, which is suitable for fully-digital audio path like USB 200 # and HDMI. 201 logging.info('Validating recorded output for channel %s', channel) 202 audio_test_utils.check_recorded_frequency( 203 audio_test_data.SIMPLE_FREQUENCY_SPEAKER_TEST_FILE, 204 self.sound_recorder, 205 second_peak_ratio=0.1, 206 ignore_frequencies=[50, 60]) 207