def do_spectral_analysis(self, check_quality=False): """Gets the spectral_analysis result. @param check_quality: Check quality of each channel. """ self.has_data() for channel_idx in xrange(self._raw_data.channel): signal = self._raw_data.channel_data[channel_idx] max_abs = max(numpy.abs(signal)) logging.debug('Channel %d max abs signal: %f', channel_idx, max_abs) if max_abs == 0: logging.info('No data on channel %d, skip this channel', channel_idx) continue saturate_value = audio_data.get_maximum_value_from_sample_format( self._raw_data.sample_format) normalized_signal = audio_analysis.normalize_signal( signal, saturate_value) logging.debug('saturate_value: %f', saturate_value) logging.debug('max signal after normalized: %f', max(normalized_signal)) spectral = audio_analysis.spectral_analysis( normalized_signal, self._rate) logging.info('Channel %d spectral:\n%s', channel_idx, pprint.pformat(spectral)) if check_quality: quality = audio_quality_measurement.quality_measurement( signal=normalized_signal, rate=self._rate, dominant_frequency=spectral[0][0]) logging.info('Channel %d quality:\n%s', channel_idx, pprint.pformat(quality))
def testSpectralAnalysisRealData(self): """This unittest checks the spectral analysis works on real data.""" binary = open('client/cros/audio/test_data/1k_2k.raw', 'r').read() data = audio_data.AudioRawData(binary, 2, 'S32_LE') saturate_value = audio_data.get_maximum_value_from_sample_format( 'S32_LE') golden_frequency = [1000, 2000] for channel in [0, 1]: normalized_signal = audio_analysis.normalize_signal( data.channel_data[channel],saturate_value) spectral = audio_analysis.spectral_analysis( normalized_signal, 48000, 0.02) logging.debug('channel %s: %s', channel, spectral) self.assertTrue(abs(spectral[0][0] - golden_frequency[channel]) < 5, 'Dominant frequency is not correct')
def do_spectral_analysis(self, ignore_high_freq, check_quality, quality_params): """Gets the spectral_analysis result. @param ignore_high_freq: Ignore high frequencies above this threshold. @param check_quality: Check quality of each channel. @param quality_params: A QualityParams object for quality measurement. """ self.has_data() for channel_idx in xrange(self._raw_data.channel): signal = self._raw_data.channel_data[channel_idx] max_abs = max(numpy.abs(signal)) logging.debug('Channel %d max abs signal: %f', channel_idx, max_abs) if max_abs == 0: logging.info('No data on channel %d, skip this channel', channel_idx) continue saturate_value = audio_data.get_maximum_value_from_sample_format( self._raw_data.sample_format) normalized_signal = audio_analysis.normalize_signal( signal, saturate_value) logging.debug('saturate_value: %f', saturate_value) logging.debug('max signal after normalized: %f', max(normalized_signal)) spectral = audio_analysis.spectral_analysis( normalized_signal, self._rate) logging.debug('Channel %d spectral:\n%s', channel_idx, pprint.pformat(spectral)) # Ignore high frequencies above the threshold. spectral = [(f, c) for (f, c) in spectral if f < ignore_high_freq] logging.info( 'Channel %d spectral after ignoring high frequencies ' 'above %f:\n%s', channel_idx, ignore_high_freq, pprint.pformat(spectral)) if check_quality: quality = audio_quality_measurement.quality_measurement( signal=normalized_signal, rate=self._rate, dominant_frequency=spectral[0][0], block_size_secs=quality_params.block_size_secs, frequency_error_threshold=quality_params. frequency_error_threshold, delay_amplitude_threshold=quality_params. delay_amplitude_threshold, noise_amplitude_threshold=quality_params. noise_amplitude_threshold, burst_amplitude_threshold=quality_params. burst_amplitude_threshold) logging.debug('Channel %d quality:\n%s', channel_idx, pprint.pformat(quality)) self._quality_result.append(quality) self._spectrals.append(spectral)
def testNormalize(self): y = [1, 2, 3, 4, 5] normalized_y = audio_analysis.normalize_signal(y, 10) expected = numpy.array([0.1, 0.2, 0.3, 0.4, 0.5]) for i in xrange(len(y)): self.assertEqual(expected[i], normalized_y[i])
def check_recorded_frequency( golden_file, recorder, second_peak_ratio=_DEFAULT_SECOND_PEAK_RATIO, frequency_diff_threshold=DEFAULT_FREQUENCY_DIFF_THRESHOLD, ignore_frequencies=None, check_anomaly=False, check_artifacts=False, mute_durations=None, volume_changes=None, tolerant_noise_level=DEFAULT_TOLERANT_NOISE_LEVEL): """Checks if the recorded data contains sine tone of golden frequency. @param golden_file: An AudioTestData object that serves as golden data. @param recorder: An AudioWidget used in the test to record data. @param second_peak_ratio: The test fails when the second dominant frequency has coefficient larger than this ratio of the coefficient of first dominant frequency. @param frequency_diff_threshold: The maximum difference between estimated frequency of test signal and golden frequency. This value should be small for signal passed through line. @param ignore_frequencies: A list of frequencies to be ignored. The component in the spectral with frequency too close to the frequency in the list will be ignored. The comparison of frequencies uses frequency_diff_threshold as well. @param check_anomaly: True to check anomaly in the signal. @param check_artifacts: True to check artifacts in the signal. @param mute_durations: Each duration of mute in seconds in the signal. @param volume_changes: A list containing alternative -1 for decreasing volume and +1 for increasing volume. @param tolerant_noise_level: The maximum noise level can be tolerated @returns: A list containing tuples of (dominant_frequency, coefficient) for valid channels. Coefficient can be a measure of signal magnitude on that dominant frequency. Invalid channels where golden_channel is None are ignored. @raises error.TestFail if the recorded data does not contain sine tone of golden frequency. """ if not ignore_frequencies: ignore_frequencies = [] # Also ignore harmonics of ignore frequencies. ignore_frequencies_harmonics = [] for ignore_freq in ignore_frequencies: ignore_frequencies_harmonics += [ignore_freq * n for n in xrange(1, 4)] data_format = recorder.data_format recorded_data = audio_data.AudioRawData( binary=recorder.get_binary(), channel=data_format['channel'], sample_format=data_format['sample_format']) errors = [] dominant_spectrals = [] for test_channel, golden_channel in enumerate(recorder.channel_map): if golden_channel is None: logging.info('Skipped channel %d', test_channel) continue signal = recorded_data.channel_data[test_channel] saturate_value = audio_data.get_maximum_value_from_sample_format( data_format['sample_format']) logging.debug('Channel %d max signal: %f', test_channel, max(signal)) normalized_signal = audio_analysis.normalize_signal( signal, saturate_value) logging.debug('saturate_value: %f', saturate_value) logging.debug('max signal after normalized: %f', max(normalized_signal)) spectral = audio_analysis.spectral_analysis(normalized_signal, data_format['rate']) logging.debug('spectral: %s', spectral) if not spectral: errors.append('Channel %d: Can not find dominant frequency.' % test_channel) golden_frequency = golden_file.frequencies[golden_channel] logging.debug('Checking channel %s spectral %s against frequency %s', test_channel, spectral, golden_frequency) dominant_frequency = spectral[0][0] if (abs(dominant_frequency - golden_frequency) > frequency_diff_threshold): errors.append( 'Channel %d: Dominant frequency %s is away from golden %s' % (test_channel, dominant_frequency, golden_frequency)) if check_anomaly: detected_anomaly = audio_analysis.anomaly_detection( signal=normalized_signal, rate=data_format['rate'], freq=golden_frequency) if detected_anomaly: errors.append( 'Channel %d: Detect anomaly near these time: %s' % (test_channel, detected_anomaly)) else: logging.info( 'Channel %d: Quality is good as there is no anomaly', test_channel) if check_artifacts or mute_durations or volume_changes: result = audio_quality_measurement.quality_measurement( normalized_signal, data_format['rate'], dominant_frequency=dominant_frequency) logging.debug('Quality measurement result:\n%s', pprint.pformat(result)) if check_artifacts: if len(result['artifacts']['noise_before_playback']) > 0: errors.append( 'Channel %d: Detects artifacts before playing near' ' these time and duration: %s' % (test_channel, str(result['artifacts']['noise_before_playback']))) if len(result['artifacts']['noise_after_playback']) > 0: errors.append( 'Channel %d: Detects artifacts after playing near' ' these time and duration: %s' % (test_channel, str(result['artifacts']['noise_after_playback']))) if mute_durations: delays = result['artifacts']['delay_during_playback'] delay_durations = [] for x in delays: delay_durations.append(x[1]) mute_matched, delay_matched = longest_common_subsequence( mute_durations, delay_durations, DEFAULT_EQUIVALENT_THRESHOLD) # updated delay list new_delays = [ delays[i] for i in delay_matched if not delay_matched[i] ] result['artifacts']['delay_during_playback'] = new_delays unmatched_mutes = [ mute_durations[i] for i in mute_matched if not mute_matched[i] ] if len(unmatched_mutes) > 0: errors.append('Channel %d: Unmatched mute duration: %s' % (test_channel, unmatched_mutes)) if check_artifacts: if len(result['artifacts']['delay_during_playback']) > 0: errors.append( 'Channel %d: Detects delay during playing near' ' these time and duration: %s' % (test_channel, result['artifacts']['delay_during_playback'])) if len(result['artifacts']['burst_during_playback']) > 0: errors.append( 'Channel %d: Detects burst/pop near these time: %s' % (test_channel, result['artifacts']['burst_during_playback'])) if result['equivalent_noise_level'] > tolerant_noise_level: errors.append( 'Channel %d: noise level is higher than tolerant' ' noise level: %f > %f' % (test_channel, result['equivalent_noise_level'], tolerant_noise_level)) if volume_changes: matched = True volume_changing = result['volume_changes'] if len(volume_changing) != len(volume_changes): matched = False else: for i in xrange(len(volume_changing)): if volume_changing[i][1] != volume_changes[i]: matched = False break if not matched: errors.append( 'Channel %d: volume changing is not as expected, ' 'found changing time and events are: %s while ' 'expected changing events are %s' % (test_channel, volume_changing, volume_changes)) # Filter out the harmonics resulted from imperfect sin wave. # This list is different for different channels. harmonics = [dominant_frequency * n for n in xrange(2, 10)] def should_be_ignored(frequency): """Checks if frequency is close to any frequency in ignore list. The ignore list is harmonics of frequency to be ignored (like power noise), plus harmonics of dominant frequencies, plus DC. @param frequency: The frequency to be tested. @returns: True if the frequency should be ignored. False otherwise. """ for ignore_frequency in (ignore_frequencies_harmonics + harmonics + [0.0]): if (abs(frequency - ignore_frequency) < frequency_diff_threshold): logging.debug('Ignore frequency: %s', frequency) return True # Checks DC is small enough. for freq, coeff in spectral: if freq < _DC_FREQ_THRESHOLD and coeff > _DC_COEFF_THRESHOLD: errors.append('Channel %d: Found large DC coefficient: ' '(%f Hz, %f)' % (test_channel, freq, coeff)) # Filter out the frequencies to be ignored. spectral = [x for x in spectral if not should_be_ignored(x[0])] if len(spectral) > 1: first_coeff = spectral[0][1] second_coeff = spectral[1][1] if second_coeff > first_coeff * second_peak_ratio: errors.append( 'Channel %d: Found large second dominant frequencies: ' '%s' % (test_channel, spectral)) dominant_spectrals.append(spectral[0]) if errors: raise error.TestFail(', '.join(errors)) return dominant_spectrals
def check_recorded_frequency( golden_file, recorder, second_peak_ratio=DEFAULT_SECOND_PEAK_RATIO, frequency_diff_threshold=DEFAULT_FREQUENCY_DIFF_THRESHOLD, ignore_frequencies=None, check_anomaly=False): """Checks if the recorded data contains sine tone of golden frequency. @param golden_file: An AudioTestData object that serves as golden data. @param recorder: An AudioWidget used in the test to record data. @param second_peak_ratio: The test fails when the second dominant frequency has coefficient larger than this ratio of the coefficient of first dominant frequency. @param frequency_diff_threshold: The maximum difference between estimated frequency of test signal and golden frequency. This value should be small for signal passed through line. @param ignore_frequencies: A list of frequencies to be ignored. The component in the spectral with frequency too close to the frequency in the list will be ignored. The comparison of frequencies uses frequency_diff_threshold as well. @param check_anomaly: True to check anomaly in the signal. @raises error.TestFail if the recorded data does not contain sine tone of golden frequency. """ data_format = recorder.data_format recorded_data = audio_data.AudioRawData( binary=recorder.get_binary(), channel=data_format['channel'], sample_format=data_format['sample_format']) errors = [] for test_channel, golden_channel in enumerate(recorder.channel_map): if golden_channel is None: logging.info('Skipped channel %d', test_channel) continue signal = recorded_data.channel_data[test_channel] saturate_value = audio_data.get_maximum_value_from_sample_format( data_format['sample_format']) normalized_signal = audio_analysis.normalize_signal( signal, saturate_value) spectral = audio_analysis.spectral_analysis(normalized_signal, data_format['rate']) if not spectral: errors.append('Channel %d: Can not find dominant frequency.' % test_channel) golden_frequency = golden_file.frequencies[golden_channel] logging.debug('Checking channel %s spectral %s against frequency %s', test_channel, spectral, golden_frequency) dominant_frequency = spectral[0][0] if (abs(dominant_frequency - golden_frequency) > frequency_diff_threshold): errors.append( 'Channel %d: Dominant frequency %s is away from golden %s' % (test_channel, dominant_frequency, golden_frequency)) if check_anomaly: detected_anomaly = audio_analysis.anomaly_detection( signal=normalized_signal, rate=data_format['rate'], freq=golden_frequency) if detected_anomaly: errors.append( 'Channel %d: Detect anomaly near these time: %s' % (test_channel, detected_anomaly)) else: logging.info( 'Channel %d: Quality is good as there is no anomaly', test_channel) def should_be_ignored(frequency): """Checks if frequency is close to any frequency in ignore list. @param frequency: The frequency to be tested. @returns: True if the frequency should be ignored. False otherwise. """ for ignore_frequency in ignore_frequencies: if (abs(frequency - ignore_frequency) < frequency_diff_threshold): logging.debug('Ignore frequency: %s', frequency) return True # Filter out the frequencies to be ignored. if ignore_frequencies: spectral = [x for x in spectral if not should_be_ignored(x[0])] if len(spectral) > 1: first_coeff = spectral[0][1] second_coeff = spectral[1][1] if second_coeff > first_coeff * second_peak_ratio: errors.append( 'Channel %d: Found large second dominant frequencies: ' '%s' % (test_channel, spectral)) if errors: raise error.TestFail(', '.join(errors))