1# Copyright 2015 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 module provides the test utilities for audio tests using chameleon.""" 6 7# TODO (cychiang) Move test utilities from chameleon_audio_helpers 8# to this module. 9 10import logging 11import multiprocessing 12import os 13import pprint 14import re 15import time 16from contextlib import contextmanager 17 18from autotest_lib.client.common_lib import error 19from autotest_lib.client.cros import constants 20from autotest_lib.client.cros.audio import audio_analysis 21from autotest_lib.client.cros.audio import audio_data 22from autotest_lib.client.cros.audio import audio_helper 23from autotest_lib.client.cros.audio import audio_quality_measurement 24from autotest_lib.client.cros.chameleon import chameleon_audio_ids 25 26CHAMELEON_AUDIO_IDS_TO_CRAS_NODE_TYPES = { 27 chameleon_audio_ids.CrosIds.HDMI: 'HDMI', 28 chameleon_audio_ids.CrosIds.HEADPHONE: 'HEADPHONE', 29 chameleon_audio_ids.CrosIds.EXTERNAL_MIC: 'MIC', 30 chameleon_audio_ids.CrosIds.SPEAKER: 'INTERNAL_SPEAKER', 31 chameleon_audio_ids.CrosIds.INTERNAL_MIC: 'INTERNAL_MIC', 32 chameleon_audio_ids.CrosIds.BLUETOOTH_HEADPHONE: 'BLUETOOTH', 33 chameleon_audio_ids.CrosIds.BLUETOOTH_MIC: 'BLUETOOTH', 34 chameleon_audio_ids.CrosIds.USBIN: 'USB', 35 chameleon_audio_ids.CrosIds.USBOUT: 'USB', 36} 37 38 39def cros_port_id_to_cras_node_type(port_id): 40 """Gets Cras node type from Cros port id. 41 42 @param port_id: A port id defined in chameleon_audio_ids.CrosIds. 43 44 @returns: A Cras node type defined in cras_utils.CRAS_NODE_TYPES. 45 46 """ 47 return CHAMELEON_AUDIO_IDS_TO_CRAS_NODE_TYPES[port_id] 48 49 50def check_output_port(audio_facade, port_id): 51 """Checks selected output node on Cros device is correct for a port. 52 53 @param port_id: A port id defined in chameleon_audio_ids.CrosIds. 54 55 """ 56 output_node_type = cros_port_id_to_cras_node_type(port_id) 57 check_audio_nodes(audio_facade, ([output_node_type], None)) 58 59 60def check_input_port(audio_facade, port_id): 61 """Checks selected input node on Cros device is correct for a port. 62 63 @param port_id: A port id defined in chameleon_audio_ids.CrosIds. 64 65 """ 66 input_node_type = cros_port_id_to_cras_node_type(port_id) 67 check_audio_nodes(audio_facade, (None, [input_node_type])) 68 69 70def check_audio_nodes(audio_facade, audio_nodes): 71 """Checks the node selected by Cros device is correct. 72 73 @param audio_facade: A RemoteAudioFacade to access audio functions on 74 Cros device. 75 76 @param audio_nodes: A tuple (out_audio_nodes, in_audio_nodes) containing 77 expected selected output and input nodes. 78 79 @raises: error.TestFail if the nodes selected by Cros device are not expected. 80 81 """ 82 curr_out_nodes, curr_in_nodes = audio_facade.get_selected_node_types() 83 out_audio_nodes, in_audio_nodes = audio_nodes 84 if (in_audio_nodes != None and 85 sorted(curr_in_nodes) != sorted(in_audio_nodes)): 86 raise error.TestFail('Wrong input node(s) selected %s ' 87 'instead %s!' % (str(curr_in_nodes), str(in_audio_nodes))) 88 if (out_audio_nodes != None and 89 sorted(curr_out_nodes) != sorted(out_audio_nodes)): 90 raise error.TestFail('Wrong output node(s) selected %s ' 91 'instead %s!' % (str(curr_out_nodes), str(out_audio_nodes))) 92 93 94def check_plugged_nodes(audio_facade, audio_nodes): 95 """Checks the nodes that are currently plugged on Cros device are correct. 96 97 @param audio_facade: A RemoteAudioFacade to access audio functions on 98 Cros device. 99 100 @param audio_nodes: A tuple (out_audio_nodes, in_audio_nodes) containing 101 expected plugged output and input nodes. 102 103 @raises: error.TestFail if the plugged nodes on Cros device are not expected. 104 105 """ 106 curr_out_nodes, curr_in_nodes = audio_facade.get_plugged_node_types() 107 out_audio_nodes, in_audio_nodes = audio_nodes 108 if (in_audio_nodes != None and 109 sorted(curr_in_nodes) != sorted(in_audio_nodes)): 110 raise error.TestFail('Wrong input node(s) plugged %s ' 111 'instead %s!' % (str(curr_in_nodes), str(in_audio_nodes))) 112 if (out_audio_nodes != None and 113 sorted(curr_out_nodes) != sorted(out_audio_nodes)): 114 raise error.TestFail('Wrong output node(s) plugged %s ' 115 'instead %s!' % (str(curr_out_nodes), str(out_audio_nodes))) 116 117 118def bluetooth_nodes_plugged(audio_facade): 119 """Checks bluetooth nodes are plugged. 120 121 @param audio_facade: A RemoteAudioFacade to access audio functions on 122 Cros device. 123 124 @raises: error.TestFail if either input or output bluetooth node is 125 not plugged. 126 127 """ 128 curr_out_nodes, curr_in_nodes = audio_facade.get_plugged_node_types() 129 return 'BLUETOOTH' in curr_out_nodes and 'BLUETOOTH' in curr_in_nodes 130 131 132def _get_board_name(host): 133 """Gets the board name. 134 135 @param host: The CrosHost object. 136 137 @returns: The board name. 138 139 """ 140 return host.get_board().split(':')[1] 141 142 143def has_internal_speaker(host): 144 """Checks if the Cros device has speaker. 145 146 @param host: The CrosHost object. 147 148 @returns: True if Cros device has internal speaker. False otherwise. 149 150 """ 151 board_name = _get_board_name(host) 152 if host.get_board_type() == 'CHROMEBOX' and board_name != 'stumpy': 153 logging.info('Board %s does not have speaker.', board_name) 154 return False 155 return True 156 157 158def has_internal_microphone(host): 159 """Checks if the Cros device has internal microphone. 160 161 @param host: The CrosHost object. 162 163 @returns: True if Cros device has internal microphone. False otherwise. 164 165 """ 166 board_name = _get_board_name(host) 167 if host.get_board_type() == 'CHROMEBOX': 168 logging.info('Board %s does not have internal microphone.', board_name) 169 return False 170 return True 171 172 173def has_headphone(host): 174 """Checks if the Cros device has headphone. 175 176 @param host: The CrosHost object. 177 178 @returns: True if Cros device has headphone. False otherwise. 179 180 """ 181 board_name = _get_board_name(host) 182 if host.get_board_type() == 'CHROMEBIT': 183 logging.info('Board %s does not have headphone.', board_name) 184 return False 185 return True 186 187 188def suspend_resume(host, suspend_time_secs, resume_network_timeout_secs=50): 189 """Performs the suspend/resume on Cros device. 190 191 @param suspend_time_secs: Time in seconds to let Cros device suspend. 192 @resume_network_timeout_secs: Time in seconds to let Cros device resume and 193 obtain network. 194 """ 195 def action_suspend(): 196 """Calls the host method suspend.""" 197 host.suspend(suspend_time=suspend_time_secs) 198 199 boot_id = host.get_boot_id() 200 proc = multiprocessing.Process(target=action_suspend) 201 logging.info("Suspending...") 202 proc.daemon = True 203 proc.start() 204 host.test_wait_for_sleep(suspend_time_secs / 3) 205 logging.info("DUT suspended! Waiting to resume...") 206 host.test_wait_for_resume( 207 boot_id, suspend_time_secs + resume_network_timeout_secs) 208 logging.info("DUT resumed!") 209 210 211def dump_cros_audio_logs(host, audio_facade, directory, suffix=''): 212 """Dumps logs for audio debugging from Cros device. 213 214 @param host: The CrosHost object. 215 @param audio_facade: A RemoteAudioFacade to access audio functions on 216 Cros device. 217 @directory: The directory to dump logs. 218 219 """ 220 def get_file_path(name): 221 """Gets file path to dump logs. 222 223 @param name: The file name. 224 225 @returns: The file path with an optional suffix. 226 227 """ 228 file_name = '%s.%s' % (name, suffix) if suffix else name 229 file_path = os.path.join(directory, file_name) 230 return file_path 231 232 audio_facade.dump_diagnostics(get_file_path('audio_diagnostics.txt')) 233 234 host.get_file('/var/log/messages', get_file_path('messages')) 235 236 host.get_file(constants.MULTIMEDIA_XMLRPC_SERVER_LOG_FILE, 237 get_file_path('multimedia_xmlrpc_server.log')) 238 239 240def examine_audio_diagnostics(path): 241 """Examines audio diagnostic content. 242 243 @param path: Path to audio diagnostic file. 244 245 @returns: Warning messages or ''. 246 247 """ 248 warning_msgs = [] 249 line_number = 1 250 251 underrun_pattern = re.compile('num_underruns: (\d*)') 252 253 with open(path) as f: 254 for line in f.readlines(): 255 256 # Check for number of underruns. 257 search_result = underrun_pattern.search(line) 258 if search_result: 259 num_underruns = int(search_result.group(1)) 260 if num_underruns != 0: 261 warning_msgs.append( 262 'Found %d underrun at line %d: %s' % ( 263 num_underruns, line_number, line)) 264 265 # TODO(cychiang) add other check like maximum client reply delay. 266 line_number = line_number + 1 267 268 if warning_msgs: 269 return ('Found issue in audio diganostics result : %s' % 270 '\n'.join(warning_msgs)) 271 272 logging.info('audio_diagnostic result looks fine') 273 return '' 274 275 276@contextmanager 277def monitor_no_nodes_changed(audio_facade, callback=None): 278 """Context manager to monitor nodes changed signal on Cros device. 279 280 Starts the counter in the beginning. Stops the counter in the end to make 281 sure there is no NodesChanged signal during the try block. 282 283 E.g. with monitor_no_nodes_changed(audio_facade): 284 do something on playback/recording 285 286 @param audio_facade: A RemoteAudioFacade to access audio functions on 287 Cros device. 288 @param fail_callback: The callback to call before raising TestFail 289 when there is unexpected NodesChanged signals. 290 291 @raises: error.TestFail if there is NodesChanged signal on 292 Cros device during the context. 293 294 """ 295 try: 296 audio_facade.start_counting_signal('NodesChanged') 297 yield 298 finally: 299 count = audio_facade.stop_counting_signal() 300 if count: 301 message = 'Got %d unexpected NodesChanged signal' % count 302 logging.error(message) 303 if callback: 304 callback() 305 raise error.TestFail(message) 306 307 308# The second dominant frequency should have energy less than -26dB of the 309# first dominant frequency in the spectrum. 310_DEFAULT_SECOND_PEAK_RATIO = 0.05 311 312# Tolerate more noise for bluetooth audio using HSP. 313_HSP_SECOND_PEAK_RATIO = 0.2 314 315# Tolerate more noise for speaker. 316_SPEAKER_SECOND_PEAK_RATIO = 0.1 317 318# Tolerate more noise for internal microphone. 319_INTERNAL_MIC_SECOND_PEAK_RATIO = 0.2 320 321# maximum tolerant noise level 322DEFAULT_TOLERANT_NOISE_LEVEL = 0.01 323 324# If relative error of two durations is less than 0.2, 325# they will be considered equivalent. 326DEFAULT_EQUIVALENT_THRESHOLD = 0.2 327 328# The frequency at lower than _DC_FREQ_THRESHOLD should have coefficient 329# smaller than _DC_COEFF_THRESHOLD. 330_DC_FREQ_THRESHOLD = 0.001 331_DC_COEFF_THRESHOLD = 0.01 332 333def get_second_peak_ratio(source_id, recorder_id, is_hsp=False): 334 """Gets the second peak ratio suitable for use case. 335 336 @param source_id: ID defined in chameleon_audio_ids for source widget. 337 @param recorder_id: ID defined in chameleon_audio_ids for recorder widget. 338 @param is_hsp: For bluetooth HSP use case. 339 340 @returns: A float for proper second peak ratio to be used in 341 check_recorded_frequency. 342 """ 343 if is_hsp: 344 return _HSP_SECOND_PEAK_RATIO 345 elif source_id == chameleon_audio_ids.CrosIds.SPEAKER: 346 return _SPEAKER_SECOND_PEAK_RATIO 347 elif recorder_id == chameleon_audio_ids.CrosIds.INTERNAL_MIC: 348 return _INTERNAL_MIC_SECOND_PEAK_RATIO 349 else: 350 return _DEFAULT_SECOND_PEAK_RATIO 351 352 353# The deviation of estimated dominant frequency from golden frequency. 354DEFAULT_FREQUENCY_DIFF_THRESHOLD = 5 355 356def check_recorded_frequency( 357 golden_file, recorder, 358 second_peak_ratio=_DEFAULT_SECOND_PEAK_RATIO, 359 frequency_diff_threshold=DEFAULT_FREQUENCY_DIFF_THRESHOLD, 360 ignore_frequencies=None, check_anomaly=False, check_artifacts=False, 361 mute_durations=None, volume_changes=None, 362 tolerant_noise_level=DEFAULT_TOLERANT_NOISE_LEVEL): 363 """Checks if the recorded data contains sine tone of golden frequency. 364 365 @param golden_file: An AudioTestData object that serves as golden data. 366 @param recorder: An AudioWidget used in the test to record data. 367 @param second_peak_ratio: The test fails when the second dominant 368 frequency has coefficient larger than this 369 ratio of the coefficient of first dominant 370 frequency. 371 @param frequency_diff_threshold: The maximum difference between estimated 372 frequency of test signal and golden 373 frequency. This value should be small for 374 signal passed through line. 375 @param ignore_frequencies: A list of frequencies to be ignored. The 376 component in the spectral with frequency too 377 close to the frequency in the list will be 378 ignored. The comparison of frequencies uses 379 frequency_diff_threshold as well. 380 @param check_anomaly: True to check anomaly in the signal. 381 @param check_artifacts: True to check artifacts in the signal. 382 @param mute_durations: Each duration of mute in seconds in the signal. 383 @param volume_changes: A list containing alternative -1 for decreasing 384 volume and +1 for increasing volume. 385 @param tolerant_noise_level: The maximum noise level can be tolerated 386 387 @returns: A list containing tuples of (dominant_frequency, coefficient) for 388 valid channels. Coefficient can be a measure of signal magnitude 389 on that dominant frequency. Invalid channels where golden_channel 390 is None are ignored. 391 392 @raises error.TestFail if the recorded data does not contain sine tone of 393 golden frequency. 394 395 """ 396 if not ignore_frequencies: 397 ignore_frequencies = [] 398 399 # Also ignore harmonics of ignore frequencies. 400 ignore_frequencies_harmonics = [] 401 for ignore_freq in ignore_frequencies: 402 ignore_frequencies_harmonics += [ignore_freq * n for n in xrange(1, 4)] 403 404 data_format = recorder.data_format 405 recorded_data = audio_data.AudioRawData( 406 binary=recorder.get_binary(), 407 channel=data_format['channel'], 408 sample_format=data_format['sample_format']) 409 410 errors = [] 411 dominant_spectrals = [] 412 413 for test_channel, golden_channel in enumerate(recorder.channel_map): 414 if golden_channel is None: 415 logging.info('Skipped channel %d', test_channel) 416 continue 417 418 signal = recorded_data.channel_data[test_channel] 419 saturate_value = audio_data.get_maximum_value_from_sample_format( 420 data_format['sample_format']) 421 logging.debug('Channel %d max signal: %f', test_channel, max(signal)) 422 normalized_signal = audio_analysis.normalize_signal( 423 signal, saturate_value) 424 logging.debug('saturate_value: %f', saturate_value) 425 logging.debug('max signal after normalized: %f', max(normalized_signal)) 426 spectral = audio_analysis.spectral_analysis( 427 normalized_signal, data_format['rate']) 428 logging.debug('spectral: %s', spectral) 429 430 if not spectral: 431 errors.append( 432 'Channel %d: Can not find dominant frequency.' % 433 test_channel) 434 435 golden_frequency = golden_file.frequencies[golden_channel] 436 logging.debug('Checking channel %s spectral %s against frequency %s', 437 test_channel, spectral, golden_frequency) 438 439 dominant_frequency = spectral[0][0] 440 441 if (abs(dominant_frequency - golden_frequency) > 442 frequency_diff_threshold): 443 errors.append( 444 'Channel %d: Dominant frequency %s is away from golden %s' % 445 (test_channel, dominant_frequency, golden_frequency)) 446 447 if check_anomaly: 448 detected_anomaly = audio_analysis.anomaly_detection( 449 signal=normalized_signal, 450 rate=data_format['rate'], 451 freq=golden_frequency) 452 if detected_anomaly: 453 errors.append( 454 'Channel %d: Detect anomaly near these time: %s' % 455 (test_channel, detected_anomaly)) 456 else: 457 logging.info( 458 'Channel %d: Quality is good as there is no anomaly', 459 test_channel) 460 461 if check_artifacts or mute_durations or volume_changes: 462 result = audio_quality_measurement.quality_measurement( 463 normalized_signal, 464 data_format['rate'], 465 dominant_frequency=dominant_frequency) 466 logging.debug('Quality measurement result:\n%s', pprint.pformat(result)) 467 if check_artifacts: 468 if len(result['artifacts']['noise_before_playback']) > 0: 469 errors.append( 470 'Channel %d: Detects artifacts before playing near' 471 ' these time and duration: %s' % 472 (test_channel, 473 str(result['artifacts']['noise_before_playback']))) 474 475 if len(result['artifacts']['noise_after_playback']) > 0: 476 errors.append( 477 'Channel %d: Detects artifacts after playing near' 478 ' these time and duration: %s' % 479 (test_channel, 480 str(result['artifacts']['noise_after_playback']))) 481 482 if mute_durations: 483 delays = result['artifacts']['delay_during_playback'] 484 delay_durations = [] 485 for x in delays: 486 delay_durations.append(x[1]) 487 mute_matched, delay_matched = longest_common_subsequence( 488 mute_durations, 489 delay_durations, 490 DEFAULT_EQUIVALENT_THRESHOLD) 491 492 # updated delay list 493 new_delays = [delays[i] 494 for i in delay_matched if not delay_matched[i]] 495 496 result['artifacts']['delay_during_playback'] = new_delays 497 498 unmatched_mutes = [mute_durations[i] 499 for i in mute_matched if not mute_matched[i]] 500 501 if len(unmatched_mutes) > 0: 502 errors.append( 503 'Channel %d: Unmatched mute duration: %s' % 504 (test_channel, unmatched_mutes)) 505 506 if check_artifacts: 507 if len(result['artifacts']['delay_during_playback']) > 0: 508 errors.append( 509 'Channel %d: Detects delay during playing near' 510 ' these time and duration: %s' % 511 (test_channel, 512 result['artifacts']['delay_during_playback'])) 513 514 if len(result['artifacts']['burst_during_playback']) > 0: 515 errors.append( 516 'Channel %d: Detects burst/pop near these time: %s' % 517 (test_channel, 518 result['artifacts']['burst_during_playback'])) 519 520 if result['equivalent_noise_level'] > tolerant_noise_level: 521 errors.append( 522 'Channel %d: noise level is higher than tolerant' 523 ' noise level: %f > %f' % 524 (test_channel, 525 result['equivalent_noise_level'], 526 tolerant_noise_level)) 527 528 if volume_changes: 529 matched = True 530 volume_changing = result['volume_changes'] 531 if len(volume_changing) != len(volume_changes): 532 matched = False 533 else: 534 for i in xrange(len(volume_changing)): 535 if volume_changing[i][1] != volume_changes[i]: 536 matched = False 537 break 538 if not matched: 539 errors.append( 540 'Channel %d: volume changing is not as expected, ' 541 'found changing time and events are: %s while ' 542 'expected changing events are %s'% 543 (test_channel, 544 volume_changing, 545 volume_changes)) 546 547 # Filter out the harmonics resulted from imperfect sin wave. 548 # This list is different for different channels. 549 harmonics = [dominant_frequency * n for n in xrange(2, 10)] 550 551 def should_be_ignored(frequency): 552 """Checks if frequency is close to any frequency in ignore list. 553 554 The ignore list is harmonics of frequency to be ignored 555 (like power noise), plus harmonics of dominant frequencies, 556 plus DC. 557 558 @param frequency: The frequency to be tested. 559 560 @returns: True if the frequency should be ignored. False otherwise. 561 562 """ 563 for ignore_frequency in (ignore_frequencies_harmonics + harmonics 564 + [0.0]): 565 if (abs(frequency - ignore_frequency) < 566 frequency_diff_threshold): 567 logging.debug('Ignore frequency: %s', frequency) 568 return True 569 570 # Checks DC is small enough. 571 for freq, coeff in spectral: 572 if freq < _DC_FREQ_THRESHOLD and coeff > _DC_COEFF_THRESHOLD: 573 errors.append( 574 'Channel %d: Found large DC coefficient: ' 575 '(%f Hz, %f)' % (test_channel, freq, coeff)) 576 577 # Filter out the frequencies to be ignored. 578 spectral = [x for x in spectral if not should_be_ignored(x[0])] 579 580 if len(spectral) > 1: 581 first_coeff = spectral[0][1] 582 second_coeff = spectral[1][1] 583 if second_coeff > first_coeff * second_peak_ratio: 584 errors.append( 585 'Channel %d: Found large second dominant frequencies: ' 586 '%s' % (test_channel, spectral)) 587 588 dominant_spectrals.append(spectral[0]) 589 590 if errors: 591 raise error.TestFail(', '.join(errors)) 592 593 return dominant_spectrals 594 595 596def longest_common_subsequence(list1, list2, equivalent_threshold): 597 """Finds longest common subsequence of list1 and list2 598 599 Such as list1: [0.3, 0.4], 600 list2: [0.001, 0.299, 0.002, 0.401, 0.001] 601 equivalent_threshold: 0.001 602 it will return matched1: [True, True], 603 matched2: [False, True, False, True, False] 604 605 @param list1: a list of integer or float value 606 @param list2: a list of integer or float value 607 @param equivalent_threshold: two values are considered equivalent if their 608 relative error is less than 609 equivalent_threshold. 610 611 @returns: a tuple of list (matched_1, matched_2) indicating each item 612 of list1 and list2 are matched or not. 613 614 """ 615 length1, length2 = len(list1), len(list2) 616 matching = [[0] * (length2 + 1)] * (length1 + 1) 617 # matching[i][j] is the maximum number of matched pairs for first i items 618 # in list1 and first j items in list2. 619 for i in xrange(length1): 620 for j in xrange(length2): 621 # Maximum matched pairs may be obtained without 622 # i-th item in list1 or without j-th item in list2 623 matching[i + 1][j + 1] = max(matching[i + 1][j], 624 matching[i][j + 1]) 625 diff = abs(list1[i] - list2[j]) 626 relative_error = diff / list1[i] 627 # If i-th item in list1 can be matched to j-th item in list2 628 if relative_error < equivalent_threshold: 629 matching[i + 1][j + 1] = matching[i][j] + 1 630 631 # Backtracking which item in list1 and list2 are matched 632 matched1 = [False] * length1 633 matched2 = [False] * length2 634 i, j = length1, length2 635 while i > 0 and j > 0: 636 # Maximum number is obtained by matching i-th item in list1 637 # and j-th one in list2. 638 if matching[i][j] == matching[i - 1][j - 1] + 1: 639 matched1[i - 1] = True 640 matched2[j - 1] = True 641 i, j = i - 1, j - 1 642 elif matching[i][j] == matching[i - 1][j]: 643 i -= 1 644 else: 645 j -= 1 646 return (matched1, matched2) 647 648 649def switch_to_hsp(audio_facade): 650 """Switches to HSP profile. 651 652 Selects bluetooth microphone and runs a recording process on Cros device. 653 This triggers bluetooth profile be switched from A2DP to HSP. 654 Note the user can call stop_recording on audio facade to stop the recording 655 process, or let multimedia_xmlrpc_server terminates it in its cleanup. 656 657 """ 658 audio_facade.set_chrome_active_node_type(None, 'BLUETOOTH') 659 check_audio_nodes(audio_facade, (None, ['BLUETOOTH'])) 660 audio_facade.start_recording( 661 dict(file_type='raw', sample_format='S16_LE', channel=2, 662 rate=48000)) 663 664 665def compare_recorded_correlation(golden_file, recorder, parameters=None): 666 """Checks recorded audio in an AudioInputWidget against a golden file. 667 668 Compares recorded data with golden data by cross correlation method. 669 Refer to audio_helper.compare_data for details of comparison. 670 671 @param golden_file: An AudioTestData object that serves as golden data. 672 @param recorder: An AudioInputWidget that has recorded some audio data. 673 @param parameters: A dict containing parameters for method. 674 675 """ 676 logging.info('Comparing recorded data with golden file %s ...', 677 golden_file.path) 678 audio_helper.compare_data_correlation( 679 golden_file.get_binary(), golden_file.data_format, 680 recorder.get_binary(), recorder.data_format, recorder.channel_map, 681 parameters) 682 683 684def check_and_set_chrome_active_node_types(audio_facade, output_type=None, 685 input_type=None): 686 """Check the target types are available, and set them to be active nodes. 687 688 @param audio_facade: An AudioFacadeNative or AudioFacadeAdapter object. 689 @output_type: An output node type defined in cras_utils.CRAS_NODE_TYPES. 690 None to skip. 691 @input_type: An input node type defined in cras_utils.CRAS_NODE_TYPES. 692 None to skip. 693 694 @raises: error.TestError if the expected node type is missing. We use 695 error.TestError here because usually this step is not the main 696 purpose of the test, but a setup step. 697 698 """ 699 output_types, input_types = audio_facade.get_plugged_node_types() 700 logging.debug('Plugged types: output: %r, input: %r', 701 output_types, input_types) 702 if output_type and output_type not in output_types: 703 raise error.TestError( 704 'Target output type %s not present' % output_type) 705 if input_type and input_type not in input_types: 706 raise error.TestError( 707 'Target input type %s not present' % input_type) 708 audio_facade.set_chrome_active_node_type(output_type, input_type) 709