Ejemplo n.º 1
0
 def test_not_meaningful_data(self):
     """Checks that sepectral analysis handles un-meaningful data."""
     rate = 48000
     length_in_secs = 0.5
     samples = length_in_secs * rate
     noise_amplitude = audio_analysis.MEANINGFUL_RMS_THRESHOLD * 0.5
     noise = numpy.random.standard_normal(int(samples)) * noise_amplitude
     results = audio_analysis.spectral_analysis(noise, rate)
     self.assertEqual([(0, 0)], results)
Ejemplo n.º 2
0
 def test_spectral_snalysis_real_data(self):
     """This unittest checks the spectral analysis works on real data."""
     file_path = os.path.join(
         os.path.dirname(__file__), 'test_data', '1k_2k.raw')
     binary = open(file_path, 'rb').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')
Ejemplo n.º 3
0
 def test_spectral_analysis(self):
     rate = 48000
     length_in_secs = 0.5
     freq_1 = 490.0
     freq_2 = 60.0
     coeff_1 = 1
     coeff_2 = 0.3
     samples = length_in_secs * rate
     noise = numpy.random.standard_normal(int(samples)) * 0.005
     x = numpy.linspace(0.0, (samples - 1) * 1.0 / rate, samples)
     y = (coeff_1 * numpy.sin(freq_1 * 2.0 * numpy.pi * x) + coeff_2 *
          numpy.sin(freq_2 * 2.0 * numpy.pi * x)) + noise
     results = audio_analysis.spectral_analysis(y, rate)
     # Results should contains
     # [(490, 1*k), (60, 0.3*k), (0, 0.1*k)] where 490Hz is the dominant
     # frequency with coefficient 1, 60Hz is the second dominant frequency
     # with coefficient 0.3, 0Hz is from Gaussian noise with coefficient
     # around 0.1. The k constant is resulted from window function.
     logging.debug('Results: %s', results)
     self.assertTrue(abs(results[0][0] - freq_1) < 1)
     self.assertTrue(abs(results[1][0] - freq_2) < 1)
     self.assertTrue(
         abs(results[0][1] / results[1][1] - coeff_1 / coeff_2) < 0.01)
Ejemplo n.º 4
0
    def do_spectral_analysis(self, ignore_high_freq, check_quality,
                             quality_params):
        """Gets the spectral_analysis result.

        Args:
            ignore_high_freq: Ignore high frequencies above this threshold.
            check_quality: Check quality of each channel.
            quality_params: A QualityParams object for quality measurement.

        """
        self.has_data()
        for channel_idx in range(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))

            try:
                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)
            except Exception as error:
                logging.warning(
                    "Failed to analyze channel {} with error: {}".format(
                        channel_idx, error))
def quality_measurement(
        signal,
        rate,
        dominant_frequency=None,
        block_size_secs=DEFAULT_BLOCK_SIZE_SECS,
        frequency_error_threshold=DEFAULT_FREQUENCY_ERROR,
        delay_amplitude_threshold=DEFAULT_DELAY_AMPLITUDE_THRESHOLD,
        noise_amplitude_threshold=DEFAULT_NOISE_AMPLITUDE_THRESHOLD,
        burst_amplitude_threshold=DEFAULT_BURST_AMPLITUDE_THRESHOLD,
        volume_changing_amplitude_threshold=DEFAULT_VOLUME_CHANGE_AMPLITUDE):
    """Detects several artifacts and estimates the noise level.

    This method detects artifact before playing, after playing, and delay
    during playing. Also, it estimates the noise level of the signal.
    To avoid the influence of noise, it calculates amplitude and frequency
    block by block.

    Args:
        signal: A list of numbers for one-channel PCM data. The data should
                   be normalized to [-1,1].
        rate: Sampling rate in samples per second. Example inputs: 44100,
        48000
        dominant_frequency: Dominant frequency of signal. Set None to
                               recalculate the frequency in this function.
        block_size_secs: Block size in seconds. The measurement will be done
                            block-by-block using average amplitude and frequency
                            in each block to avoid noise.
        frequency_error_threshold: Ref to DEFAULT_FREQUENCY_ERROR.
        delay_amplitude_threshold: If the average amplitude of a block is
                                      lower than average amplitude of the wave
                                      times delay_amplitude_threshold, it will
                                      be considered as delay.
                                      Also refer to delay_detection and
                                      DEFAULT_DELAY_AMPLITUDE_THRESHOLD.
        noise_amplitude_threshold: If the average amplitude of a block is
                                      higher than average amplitude of the wave
                                      times noise_amplitude_threshold, it will
                                      be considered as noise before/after
                                      playback.
                                      Also refer to noise_detection and
                                      DEFAULT_NOISE_AMPLITUDE_THRESHOLD.
        burst_amplitude_threshold: If the average amplitude of a block is
                                      higher than average amplitude of its left
                                      block and its right block times
                                      burst_amplitude_threshold. It will be
                                      considered as a burst.
                                      Also refer to burst_detection and
                                      DEFAULT_BURST_AMPLITUDE_THRESHOLD.
        volume_changing_amplitude_threshold: If the average amplitude of right
                                                block is higher or lower than
                                                that of left one times this
                                                value, it will be considered as
                                                a volume change.
                                                Also refer to
                                                changing_volume_detection and
                                                DEFAULT_VOLUME_CHANGE_AMPLITUDE

    Returns:
        A dictoinary of detection/estimation:
              {'artifacts':
                {'noise_before_playback':
                    [(time_1, duration_1), (time_2, duration_2), ...],
                 'noise_after_playback':
                    [(time_1, duration_1), (time_2, duration_2), ...],
                 'delay_during_playback':
                    [(time_1, duration_1), (time_2, duration_2), ...],
                 'burst_during_playback':
                    [time_1, time_2, ...]
                },
               'volume_changes':
                 [(time_1, flag_1), (time_2, flag_2), ...],
               'equivalent_noise_level': level
              }
              where durations and time points are in seconds. And,
              equivalence_noise_level is the quotient of noise and wave which
              refers to DEFAULT_STANDARD_NOISE. volume_changes is a list of
              tuples containing time stamps and decreasing/increasing flags for
              volume change events.

    """
    # Calculates the block size, from seconds to samples.
    block_size = int(block_size_secs * rate)

    signal = numpy.concatenate(
        (numpy.zeros(int(rate * APPEND_ZEROS_SECS)), signal,
         numpy.zeros(int(rate * APPEND_ZEROS_SECS))))
    signal = numpy.array(signal, dtype=float)
    length = len(signal)

    # Calculates the amplitude and frequency.
    amplitude, frequency = hilbert_analysis(signal, rate, block_size)

    # Finds the dominant frequency.
    if not dominant_frequency:
        dominant_frequency = audio_analysis.spectral_analysis(signal,
                                                              rate)[0][0]

    # Finds the array which contains absolute difference between dominant
    # frequency and frequency at each time point.
    frequency_delta = abs(frequency - dominant_frequency)

    # Computes average amplitude of each type of block
    res = find_block_average_value(amplitude, block_size * 2, block_size)
    left_block_amplitude, right_block_amplitude, block_amplitude = res

    # Computes average absolute difference of frequency and dominant frequency
    # of the block of each index
    _, _, block_frequency_delta = find_block_average_value(
        frequency_delta, block_size * 2, block_size)

    # Finds start and end index of sine wave.
    start_index, end_index = find_start_end_index(dominant_frequency,
                                                  block_frequency_delta,
                                                  block_size,
                                                  frequency_error_threshold)

    if start_index > end_index:
        raise SineWaveNotFound('No sine wave found in signal')

    logging.debug('Found sine wave: start: %s, end: %s',
                  float(start_index) / rate - APPEND_ZEROS_SECS,
                  float(end_index) / rate - APPEND_ZEROS_SECS)

    sum_of_amplitude = float(sum(amplitude[int(start_index):int(end_index)]))
    # Finds average amplitude of sine wave.
    average_amplitude = sum_of_amplitude / (end_index - start_index)

    # Finds noise before and/or after playback.
    noise_before_playing, noise_after_playing = noise_detection(
        start_index, end_index, block_amplitude, average_amplitude, rate,
        noise_amplitude_threshold)

    # Finds delay during playback.
    delays = delay_detection(start_index, end_index, block_amplitude,
                             average_amplitude, dominant_frequency, rate,
                             left_block_amplitude, right_block_amplitude,
                             block_frequency_delta, delay_amplitude_threshold,
                             frequency_error_threshold)

    # Finds burst during playback.
    burst_time_points = burst_detection(
        start_index, end_index, block_amplitude, average_amplitude,
        dominant_frequency, rate, left_block_amplitude, right_block_amplitude,
        block_frequency_delta, burst_amplitude_threshold,
        frequency_error_threshold)

    # Finds volume changing during playback.
    volume_changes = changing_volume_detection(
        start_index, end_index, average_amplitude, rate, left_block_amplitude,
        right_block_amplitude, volume_changing_amplitude_threshold)

    # Calculates the average teager value.
    teager_value = average_teager_value(
        signal[int(start_index):int(end_index)], average_amplitude)

    # Finds out the noise level.
    noise = noise_level(average_amplitude, dominant_frequency, rate,
                        teager_value)

    return {
        'artifacts': {
            'noise_before_playback': noise_before_playing,
            'noise_after_playback': noise_after_playing,
            'delay_during_playback': delays,
            'burst_during_playback': burst_time_points
        },
        'volume_changes': volume_changes,
        'equivalent_noise_level': noise
    }
Ejemplo n.º 6
0
 def testEmptyData(self):
     """Checks that sepectral analysis rejects empty data."""
     with self.assertRaises(audio_analysis.EmptyDataError):
         results = audio_analysis.spectral_analysis([], 100)