Home | History | Annotate | Download | only in audio_AudioVolume
      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 
      7 import logging
      8 import os
      9 import time
     10 
     11 from autotest_lib.client.common_lib import error
     12 from autotest_lib.client.cros.chameleon import audio_test_utils
     13 from autotest_lib.client.cros.chameleon import chameleon_audio_ids
     14 from autotest_lib.client.cros.chameleon import chameleon_audio_helper
     15 from autotest_lib.server.cros.audio import audio_test
     16 from autotest_lib.server.cros.multimedia import remote_facade_factory
     17 
     18 
     19 class 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, cfm_speaker=False, 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         @param cfm_speaker: whether cfm_speaker's audio is tested which is an
     51             external USB speaker on CFM (ChromeBox For Meetings) devices.
     52 
     53         """
     54         if (source_id == chameleon_audio_ids.CrosIds.SPEAKER and
     55             (not cfm_speaker and
     56             not audio_test_utils.has_internal_speaker(host))):
     57             return
     58 
     59         chameleon_board = host.chameleon
     60         factory = remote_facade_factory.RemoteFacadeFactory(
     61                 host, results_dir=self.resultsdir)
     62 
     63         chameleon_board.setup_and_reset(self.outputdir)
     64 
     65         widget_factory = chameleon_audio_helper.AudioWidgetFactory(
     66                 factory, host)
     67 
     68         source = widget_factory.create_widget(source_id)
     69         recorder = widget_factory.create_widget(recorder_id)
     70 
     71         # Chameleon Mic does not need binding.
     72         binding = (recorder_id != chameleon_audio_ids.ChameleonIds.MIC)
     73 
     74         binder = None
     75 
     76         if binding:
     77             if sink_id:
     78                 sink = widget_factory.create_widget(sink_id)
     79                 binder = widget_factory.create_binder(source, sink, recorder)
     80             else:
     81                 binder = widget_factory.create_binder(source, recorder)
     82 
     83         low_volume, high_volume, highest_ratio = volume_spec
     84         ignore_frequencies = [50, 60]
     85 
     86         second_peak_ratio = audio_test_utils.get_second_peak_ratio(
     87                 source_id=source_id,
     88                 recorder_id=recorder_id,
     89                 is_hsp=switch_hsp)
     90 
     91         with chameleon_audio_helper.bind_widgets(binder):
     92             # Checks the node selected by cras is correct.
     93             time.sleep(self.DELAY_AFTER_BINDING)
     94             audio_facade = factory.create_audio_facade()
     95 
     96             audio_test_utils.dump_cros_audio_logs(
     97                     host, audio_facade, self.resultsdir, 'after_binding')
     98 
     99             if not cfm_speaker:
    100                 audio_test_utils.check_output_port(audio_facade, source.port_id)
    101             else:
    102                 audio_test_utils.check_audio_nodes(audio_facade,
    103                                                    (['USB'], None))
    104 
    105             if switch_hsp:
    106                 audio_test_utils.switch_to_hsp(audio_facade)
    107 
    108             logging.info('Setting playback data on Cros device')
    109             source.set_playback_data(golden_file)
    110 
    111             def playback_record(tag):
    112                 """Playback and record.
    113 
    114                 @param tag: file name tag.
    115 
    116                 """
    117                 logging.info('Start recording from Chameleon.')
    118                 recorder.start_recording()
    119 
    120                 logging.info('Start playing %s on Cros device',
    121                              golden_file.path)
    122                 source.start_playback()
    123 
    124                 time.sleep(self.RECORD_SECONDS)
    125 
    126                 recorder.stop_recording()
    127                 logging.info('Stopped recording from Chameleon.')
    128 
    129                 audio_test_utils.dump_cros_audio_logs(
    130                         host, audio_facade, self.resultsdir,
    131                         'after_recording_' + 'tag')
    132 
    133                 source.stop_playback()
    134 
    135                 recorder.read_recorded_binary()
    136                 logging.info('Read recorded binary from Chameleon.')
    137 
    138                 recorded_file = os.path.join(
    139                         self.resultsdir, "recorded_%s.raw" % tag)
    140                 logging.info('Saving recorded data to %s', recorded_file)
    141                 recorder.save_file(recorded_file)
    142                 recorder.remove_head(0.5)
    143 
    144             audio_facade.set_chrome_active_volume(low_volume)
    145             playback_record('low')
    146             low_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             audio_facade.set_chrome_active_volume(high_volume)
    152             playback_record('high')
    153             high_dominant_spectrals = audio_test_utils.check_recorded_frequency(
    154                     golden_file, recorder,
    155                     second_peak_ratio=second_peak_ratio,
    156                     ignore_frequencies=ignore_frequencies)
    157 
    158             error_messages = []
    159             logging.info('low_dominant_spectrals: %s', low_dominant_spectrals)
    160             logging.info('high_dominant_spectrals: %s', high_dominant_spectrals)
    161 
    162             for channel in xrange(len(low_dominant_spectrals)):
    163                 _, low_coeff  = low_dominant_spectrals[channel]
    164                 _, high_coeff  = high_dominant_spectrals[channel]
    165                 ratio = low_coeff / high_coeff
    166                 logging.info('Channel %d volume(at %f) / volume(at %f) = %f',
    167                              channel, low_volume, high_volume, ratio)
    168                 if ratio > highest_ratio:
    169                     error_messages.append(
    170                             'Channel %d volume ratio: %f is incorrect.' % (
    171                                     channel, ratio))
    172             if error_messages:
    173                 raise error.TestFail(
    174                         'Failed volume check: %s' % ' '.join(error_messages))
    175