[autotest] chameleon: Move comparison method to audio_test_utils

This patch does following refactoring:
1. Move compare_recorded_result from chameleon_audio_helper to
audio_test_utils, and rename it to compare_recorded_correlation.
2. Remove 'method' argument because correlation is the only method.
There is another frequency comparison function to call.
3. Cleanup legacy frequency comparison method because now we use
spectral analysis method instead.
4. Raise error.TestFail with error messages when comparison fails.

BUG=chromium:620930
TEST=run audio_AudioBasicHDMI, audio_AudioBasicUSBPlayback,
audio_AudioBasicUSBPlaybackRecord, audio_AudioBasicUSBRecord test on
samus.

Change-Id: I5e9a4cb323e21af013314385b10b1fabc2dfa50e
Reviewed-on: https://chromium-review.googlesource.com/354892
Commit-Ready: Cheng-Yi Chiang <cychiang@chromium.org>
Tested-by: Cheng-Yi Chiang <cychiang@chromium.org>
Reviewed-by: Kalin Stoyanov <kalin@chromium.org>
diff --git a/client/cros/audio/audio_helper.py b/client/cros/audio/audio_helper.py
index 0e87b4d..2b5372f 100644
--- a/client/cros/audio/audio_helper.py
+++ b/client/cros/audio/audio_helper.py
@@ -787,106 +787,10 @@
     return result_dict
 
 
-def get_one_channel_stat(data, data_format):
-    """Gets statistic information of data.
-
-    @param data: A list containing one channel data.
-    @param data_format: A dict containing data format of data.
-
-    @return: The sox stat parsed result. An object containing
-             sameple_count: An int. Samples read.
-             length: A float. Length in seconds.
-             rms: A float. RMS amplitude.
-             rough_frequency: A float. Rough frequency.
-    """
-    if not data:
-        raise ValueError('Data is empty. Can not get stat')
-    raw_data = audio_data.AudioRawData(
-            binary=None, channel=1,
-            sample_format=data_format['sample_format'])
-    raw_data.copy_channel_data([data])
-    with tempfile.NamedTemporaryFile() as raw_data_file:
-        raw_data_path = raw_data_file.name
-        raw_data.write_to_file(raw_data_path)
-
-        bits = 8 * (audio_data.SAMPLE_FORMATS[
-                    data_format['sample_format']]['size_bytes'])
-        stat = sox_utils.get_stat(raw_data_path, channels=1, bits=bits,
-                                  rate=data_format['rate'])
-        return stat
-
-
-def compare_one_channel_frequency(test_data, test_data_format,
-                                  golden_data, golden_data_format):
-    """Compares two one-channel data by frequency.
-
-    @param test_data: A list containing the data to compare against golden data.
-    @param test_data_format: A dict containing data format of test data.
-    @param golden_data: A list containing the golden data.
-    @param golden_data_format: A dict containing data format of golden data.
-
-    @returns: A dict containing:
-              test_data_frequency: test data frequency.
-              golden_data_frequency: golden data frequency.
-              equal: A bool containing comparing result.
-
-    @raises: ValueError if the test data RMS is too small to be meaningful.
-
-    """
-    result_dict = dict()
-    golden_data_stat = get_one_channel_stat(golden_data, golden_data_format)
-    logging.info('Get golden data one channel stat: %s', golden_data_stat)
-    test_data_stat = get_one_channel_stat(test_data, test_data_format)
-    logging.info('Get test data one channel stat: %s', test_data_stat)
-
-    result_dict['golden_data_frequency'] = golden_data_stat.rough_frequency
-    result_dict['test_data_frequency'] = test_data_stat.rough_frequency
-    result_dict['equal'] = True if (
-            abs(result_dict['test_data_frequency'] -
-                result_dict['golden_data_frequency']) < _FREQUENCY_DIFF_THRESHOLD
-            ) else False
-    logging.debug('result_dict: %r', result_dict)
-    if test_data_stat.rms < _MEANINGFUL_RMS_THRESHOLD:
-        raise ValueError('Recorded RMS %f is too small to be meaningful.',
-                         test_data_stat.rms)
-    return result_dict
-
-
-def compare_one_channel_data(test_data, test_data_format,
-                             golden_data, golden_data_format, method,
-                             parameters):
-    """Compares two one-channel data.
-
-    @param test_data: A list containing the data to compare against golden data.
-    @param test_data_format: The data format of test data.
-    @param golden_data: A list containing the golden data.
-    @param golden_data_format: The data format of golden data.
-    @param method: The comparing method. Currently only 'correlation' is
-                   supported.
-    @param parameters: A dict containing parameters for method.
-
-    @returns: A dict containing:
-              index: The index of similarity where 1 means they are different
-                  only by a positive scale.
-              best_delay: The best delay of test data in relative to golden
-                  data.
-              equal: A bool containing comparing result.
-
-    @raises: NotImplementedError if method is not supported.
-    """
-    if method == 'correlation':
-        return compare_one_channel_correlation(test_data, golden_data,
-                parameters)
-    if method == 'frequency':
-        return compare_one_channel_frequency(
-                test_data, test_data_format, golden_data, golden_data_format)
-    raise NotImplementedError('method %s is not implemented' % method)
-
-
-def compare_data(golden_data_binary, golden_data_format,
-                 test_data_binary, test_data_format,
-                 channel_map, method, parameters=None):
-    """Compares two raw data.
+def compare_data_correlation(golden_data_binary, golden_data_format,
+                             test_data_binary, test_data_format,
+                             channel_map, parameters=None):
+    """Compares two raw data using correlation.
 
     @param golden_data_binary: The binary containing golden data.
     @param golden_data_format: The data format of golden data.
@@ -898,16 +802,11 @@
                         golden data. Channel 1 of test data should map to
                         channel 0 of golden data. Channel 2 to 7 of test data
                         should be skipped.
-    @param method: The method to compare data. Use 'correlation' to compare
-                   general data. Use 'frequency' to compare data containing
-                   sine wave.
-
     @param parameters: A dict containing parameters for method, if needed.
 
-    @returns: A boolean for compare result.
-
     @raises: NotImplementedError if file type is not raw.
              NotImplementedError if sampling rates of two data are not the same.
+             error.TestFail if golden data and test data are not equal.
     """
     if parameters is None:
         parameters = dict()
@@ -936,26 +835,26 @@
         result_dict = dict(test_channel=test_channel,
                            golden_channel=golden_channel)
         result_dict.update(
-                compare_one_channel_data(
-                        test_data_one_channel, test_data_format,
-                        golden_data_one_channel, golden_data_format, method,
+                compare_one_channel_correlation(
+                        test_data_one_channel, golden_data_one_channel,
                         parameters))
         compare_results.append(result_dict)
     logging.info('compare_results: %r', compare_results)
-    return_value = False if not compare_results else True
     for result in compare_results:
         if not result['equal']:
-            logging.error(
-                    'Failed on test channel %d and golden channel %d',
-                    result['test_channel'], result['golden_channel'])
-            return_value = False
+            error_msg = ('Failed on test channel %d and golden channel %d with '
+                         'index %f') % (
+                                 result['test_channel'],
+                                 result['golden_channel'],
+                                 result['index'])
+            logging.error(error_msg)
+            raise error.TestFail(error_msg)
     # Also checks best delay are exactly the same.
-    if method == 'correlation':
-        best_delays = set([result['best_delay'] for result in compare_results])
-        if len(best_delays) > 1:
-            logging.error('There are more than one best delay.')
-            return_value = False
-    return return_value
+    best_delays = set([result['best_delay'] for result in compare_results])
+    if len(best_delays) > 1:
+        error_msg = 'There are more than one best delay: %s' % best_delays
+        logging.error(error_msg)
+        raise error.TestFail(error_msg)
 
 
 class _base_rms_test(test.test):
diff --git a/client/cros/chameleon/audio_test_utils.py b/client/cros/chameleon/audio_test_utils.py
index 45a39a3..695e31f 100644
--- a/client/cros/chameleon/audio_test_utils.py
+++ b/client/cros/chameleon/audio_test_utils.py
@@ -17,6 +17,7 @@
 from autotest_lib.client.cros import constants
 from autotest_lib.client.cros.audio import audio_analysis
 from autotest_lib.client.cros.audio import audio_data
+from autotest_lib.client.cros.audio import audio_helper
 from autotest_lib.client.cros.chameleon import chameleon_audio_ids
 
 CHAMELEON_AUDIO_IDS_TO_CRAS_NODE_TYPES = {
@@ -426,3 +427,22 @@
     audio_facade.start_recording(
             dict(file_type='raw', sample_format='S16_LE', channel=2,
                  rate=48000))
+
+
+def compare_recorded_correlation(golden_file, recorder, parameters=None):
+    """Checks recorded audio in an AudioInputWidget against a golden file.
+
+    Compares recorded data with golden data by cross correlation method.
+    Refer to audio_helper.compare_data for details of comparison.
+
+    @param golden_file: An AudioTestData object that serves as golden data.
+    @param recorder: An AudioInputWidget that has recorded some audio data.
+    @param parameters: A dict containing parameters for method.
+
+    """
+    logging.info('Comparing recorded data with golden file %s ...',
+                 golden_file.path)
+    audio_helper.compare_data_correlation(
+            golden_file.get_binary(), golden_file.data_format,
+            recorder.get_binary(), recorder.data_format, recorder.channel_map,
+            parameters)
diff --git a/client/cros/chameleon/chameleon_audio_helper.py b/client/cros/chameleon/chameleon_audio_helper.py
index 9ff6c21..084f2fd 100644
--- a/client/cros/chameleon/chameleon_audio_helper.py
+++ b/client/cros/chameleon/chameleon_audio_helper.py
@@ -388,29 +388,6 @@
         return audio_widget_link.WidgetBinderChain(binders)
 
 
-def compare_recorded_result(golden_file, recorder, method, parameters=None):
-    """Check recoded audio in a AudioInputWidget against a golden file.
-
-    Compares recorded data with golden data by cross correlation method.
-    Refer to audio_helper.compare_data for details of comparison.
-
-    @param golden_file: An AudioTestData object that serves as golden data.
-    @param recorder: An AudioInputWidget that has recorded some audio data.
-    @param method: The method to compare recorded result. Currently,
-                   'correlation' and 'frequency' are supported.
-    @param parameters: A dict containing parameters for method.
-
-    @returns: True if the recorded data and golden data are similar enough.
-
-    """
-    logging.info('Comparing recorded data with golden file %s ...',
-                 golden_file.path)
-    return audio_helper.compare_data(
-            golden_file.get_binary(), golden_file.data_format,
-            recorder.get_binary(), recorder.data_format, recorder.channel_map,
-            method, parameters)
-
-
 @contextmanager
 def bind_widgets(binder):
     """Context manager for widget binders.
diff --git a/server/site_tests/audio_AudioBasicHDMI/audio_AudioBasicHDMI.py b/server/site_tests/audio_AudioBasicHDMI/audio_AudioBasicHDMI.py
index 959b126..464cda0 100644
--- a/server/site_tests/audio_AudioBasicHDMI/audio_AudioBasicHDMI.py
+++ b/server/site_tests/audio_AudioBasicHDMI/audio_AudioBasicHDMI.py
@@ -111,7 +111,4 @@
             logging.info('Saving recorded data to %s', recorded_file)
             recorder.save_file(recorded_file)
 
-            if not chameleon_audio_helper.compare_recorded_result(
-                    golden_file, recorder, 'correlation'):
-                raise error.TestFail(
-                        'Recorded file does not match playback file')
+            audio_test_utils.compare_recorded_correlation(golden_file, recorder)
diff --git a/server/site_tests/audio_AudioBasicUSBPlayback/audio_AudioBasicUSBPlayback.py b/server/site_tests/audio_AudioBasicUSBPlayback/audio_AudioBasicUSBPlayback.py
index 490dbbd..96127ae 100644
--- a/server/site_tests/audio_AudioBasicUSBPlayback/audio_AudioBasicUSBPlayback.py
+++ b/server/site_tests/audio_AudioBasicUSBPlayback/audio_AudioBasicUSBPlayback.py
@@ -93,7 +93,4 @@
         logging.info('Saving recorded data to %s', recorded_file)
         recorder.save_file(recorded_file)
 
-        if not chameleon_audio_helper.compare_recorded_result(
-                golden_file, recorder, 'correlation'):
-            raise error.TestFail(
-                    'Recorded file does not match playback file')
+        audio_test_utils.compare_recorded_correlation(golden_file, recorder)
diff --git a/server/site_tests/audio_AudioBasicUSBPlaybackRecord/audio_AudioBasicUSBPlaybackRecord.py b/server/site_tests/audio_AudioBasicUSBPlaybackRecord/audio_AudioBasicUSBPlaybackRecord.py
index 8e75fff..2ed2682 100644
--- a/server/site_tests/audio_AudioBasicUSBPlaybackRecord/audio_AudioBasicUSBPlaybackRecord.py
+++ b/server/site_tests/audio_AudioBasicUSBPlaybackRecord/audio_AudioBasicUSBPlaybackRecord.py
@@ -122,14 +122,7 @@
                      record_recorded_file)
         record_recorder.save_file(record_recorded_file)
 
-        error_messages = ''
-        if not chameleon_audio_helper.compare_recorded_result(
-                golden_file, playback_recorder, 'correlation'):
-            error_messages += ('Record: Recorded file does not match'
-                               ' playback file.')
-        if not chameleon_audio_helper.compare_recorded_result(
-                golden_file, record_recorder, 'correlation'):
-            error_messages += ('Playback: Recorded file does not match'
-                               ' playback file.')
-        if error_messages:
-            raise error.TestFail(error_messages)
+        audio_test_utils.compare_recorded_correlation(
+                golden_file, playback_recorder)
+        audio_test_utils.compare_recorded_correlation(
+                golden_file, record_recorder)
diff --git a/server/site_tests/audio_AudioBasicUSBRecord/audio_AudioBasicUSBRecord.py b/server/site_tests/audio_AudioBasicUSBRecord/audio_AudioBasicUSBRecord.py
index 6261acf..e8ab0d5 100644
--- a/server/site_tests/audio_AudioBasicUSBRecord/audio_AudioBasicUSBRecord.py
+++ b/server/site_tests/audio_AudioBasicUSBRecord/audio_AudioBasicUSBRecord.py
@@ -93,7 +93,4 @@
         logging.info('Saving recorded data to %s', recorded_file)
         recorder.save_file(recorded_file)
 
-        if not chameleon_audio_helper.compare_recorded_result(
-                golden_file, recorder, 'correlation'):
-            raise error.TestFail(
-                    'Recorded file does not match playback file')
+        audio_test_utils.compare_recorded_correlation(golden_file, recorder)