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 5"""This is a server side audio volume test using the Chameleon board.""" 6 7import logging 8import os 9import time 10 11from autotest_lib.client.common_lib import error 12from autotest_lib.client.cros.chameleon import audio_test_utils 13from autotest_lib.client.cros.chameleon import chameleon_audio_ids 14from autotest_lib.client.cros.chameleon import chameleon_audio_helper 15from autotest_lib.server.cros.audio import audio_test 16from autotest_lib.server.cros.multimedia import remote_facade_factory 17 18 19class audio_AudioVolume(audio_test.AudioTest): 20 """Server side audio volume test. 21 22 This test talks to a Chameleon board and a Cros device to verify 23 audio volume function of the Cros device. 24 25 """ 26 version = 1 27 RECORD_SECONDS = 5 28 DELAY_AFTER_BINDING = 0.5 29 30 def run_once(self, host, source_id, sink_id, recorder_id, volume_spec, 31 golden_file, switch_hsp=False): 32 """Running audio volume test. 33 34 @param host: device under test CrosHost 35 @param source_id: An ID defined in chameleon_audio_ids for source. 36 @param sink_id: An ID defined in chameleon_audio_ids for sink if needed. 37 Currently this is only used on bluetooth. 38 @param recorder: An ID defined in chameleon_audio_ids for recording. 39 @param volume_spec: A tuple (low_volume, high_volume, highest_ratio). 40 Low volume and high volume specifies the two volumes 41 used in the test, and highest_ratio speficies the 42 highest acceptable value for 43 recorded_volume_low / recorded_volume_high. 44 For example, (50, 100, 0.2) asserts that 45 (recorded magnitude at volume 50) should be lower 46 than (recorded magnitude at volume 100) * 0.2. 47 @param golden_file: A test file defined in audio_test_data. 48 @param switch_hsp: Run a recording process on Cros device. This is 49 to trigger Cros switching from A2DP to HSP. 50 51 """ 52 if (source_id == chameleon_audio_ids.CrosIds.SPEAKER and 53 not audio_test_utils.has_internal_speaker(host)): 54 return 55 56 chameleon_board = host.chameleon 57 factory = remote_facade_factory.RemoteFacadeFactory( 58 host, results_dir=self.resultsdir) 59 60 chameleon_board.setup_and_reset(self.outputdir) 61 62 widget_factory = chameleon_audio_helper.AudioWidgetFactory( 63 factory, host) 64 65 source = widget_factory.create_widget(source_id) 66 recorder = widget_factory.create_widget(recorder_id) 67 68 # Chameleon Mic does not need binding. 69 binding = (recorder_id != chameleon_audio_ids.ChameleonIds.MIC) 70 71 binder = None 72 73 if binding: 74 if sink_id: 75 sink = widget_factory.create_widget(sink_id) 76 binder = widget_factory.create_binder(source, sink, recorder) 77 else: 78 binder = widget_factory.create_binder(source, recorder) 79 80 low_volume, high_volume, highest_ratio = volume_spec 81 ignore_frequencies = [50, 60] 82 83 second_peak_ratio = audio_test_utils.get_second_peak_ratio( 84 source_id=source_id, 85 recorder_id=recorder_id, 86 is_hsp=switch_hsp) 87 88 with chameleon_audio_helper.bind_widgets(binder): 89 # Checks the node selected by cras is correct. 90 time.sleep(self.DELAY_AFTER_BINDING) 91 audio_facade = factory.create_audio_facade() 92 93 audio_test_utils.dump_cros_audio_logs( 94 host, audio_facade, self.resultsdir, 'after_binding') 95 96 audio_test_utils.check_output_port(audio_facade, source.port_id) 97 98 if switch_hsp: 99 audio_test_utils.switch_to_hsp(audio_facade) 100 101 logging.info('Setting playback data on Cros device') 102 source.set_playback_data(golden_file) 103 104 def playback_record(tag): 105 """Playback and record. 106 107 @param tag: file name tag. 108 109 """ 110 logging.info('Start recording from Chameleon.') 111 recorder.start_recording() 112 113 logging.info('Start playing %s on Cros device', 114 golden_file.path) 115 source.start_playback() 116 117 time.sleep(self.RECORD_SECONDS) 118 119 recorder.stop_recording() 120 logging.info('Stopped recording from Chameleon.') 121 122 audio_test_utils.dump_cros_audio_logs( 123 host, audio_facade, self.resultsdir, 124 'after_recording_' + 'tag') 125 126 source.stop_playback() 127 128 recorder.read_recorded_binary() 129 logging.info('Read recorded binary from Chameleon.') 130 131 recorded_file = os.path.join( 132 self.resultsdir, "recorded_%s.raw" % tag) 133 logging.info('Saving recorded data to %s', recorded_file) 134 recorder.save_file(recorded_file) 135 recorder.remove_head(0.5) 136 137 audio_facade.set_chrome_active_volume(low_volume) 138 playback_record('low') 139 low_dominant_spectrals = audio_test_utils.check_recorded_frequency( 140 golden_file, recorder, 141 second_peak_ratio=second_peak_ratio, 142 ignore_frequencies=ignore_frequencies) 143 144 audio_facade.set_chrome_active_volume(high_volume) 145 playback_record('high') 146 high_dominant_spectrals = audio_test_utils.check_recorded_frequency( 147 golden_file, recorder, 148 second_peak_ratio=second_peak_ratio, 149 ignore_frequencies=ignore_frequencies) 150 151 error_messages = [] 152 logging.info('low_dominant_spectrals: %s', low_dominant_spectrals) 153 logging.info('high_dominant_spectrals: %s', high_dominant_spectrals) 154 155 for channel in xrange(len(low_dominant_spectrals)): 156 _, low_coeff = low_dominant_spectrals[channel] 157 _, high_coeff = high_dominant_spectrals[channel] 158 ratio = low_coeff / high_coeff 159 logging.info('Channel %d volume(at %f) / volume(at %f) = %f', 160 channel, low_volume, high_volume, ratio) 161 if ratio > highest_ratio: 162 error_messages.append( 163 'Channel %d volume ratio: %f is incorrect.' % ( 164 channel, ratio)) 165 if error_messages: 166 raise error.TestFail( 167 'Failed volume check: %s' % ' '.join(error_messages)) 168