普通文本  |  908行  |  39.7 KB

# Copyright 2016 The Chromium OS Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.

"""This module provides utilities to detect some artifacts and measure the
    quality of audio."""

import logging
import math
import numpy

from autotest_lib.client.cros.audio import audio_analysis

# The input signal should be one sine wave with fixed frequency which
# can have silence before and/or after sine wave.
# For example:
#   silence      sine wave      silence
#  -----------|VVVVVVVVVVVVV|-----------
#     (a)           (b)           (c)
# This module detects these artifacts:
#   1. Detect noise in (a) and (c).
#   2. Detect delay in (b).
#   3. Detect burst in (b).
# Assume the transitions between (a)(b) and (b)(c) are smooth and
# amplitude increases/decreases linearly.
# This module will detect artifacts in the sine wave.
# This module also estimates the equivalent noise level by teager operator.
# This module also detects volume changes in the sine wave. However, volume
# changes may be affected by delay or burst.
# Some artifacts may cause each other.

# In this module, amplitude and frequency are derived from Hilbert transform.
# Both amplitude and frequency are a function of time.

# To detect each artifact, each point will be compared with
# average amplitude of its block. The block size will be 1.5 ms.
# Using average amplitude can mitigate the error caused by
# Hilbert transform and noise.
# In some case, for more accuracy, the block size may be modified
# to other values.
DEFAULT_BLOCK_SIZE_SECS = 0.0015

# If the difference between average frequency of this block and
# dominant frequency of full signal is less than 0.5 times of
# dominant frequency, this block is considered to be within the
# sine wave. In most cases, if there is no sine wave(only noise),
# average frequency will be much greater than 5 times of
# dominant frequency.
# Also, for delay during playback, the frequency will be about 0
# in perfect situation or much greater than 5 times of dominant
# frequency if it's noised.
DEFAULT_FREQUENCY_ERROR = 0.5

# If the amplitude of some sample is less than 0.6 times of the
# average amplitude of its left/right block, it will be considered
# as a delay during playing.
DEFAULT_DELAY_AMPLITUDE_THRESHOLD = 0.6

# If the average amplitude of the block before or after playing
# is more than 0.5 times to the average amplitude of the wave,
# it will be considered as a noise artifact.
DEFAULT_NOISE_AMPLITUDE_THRESHOLD = 0.5

# In the sine wave, if the amplitude is more than 1.4 times of
# its left side and its right side, it will be considered as
# a burst.
DEFAULT_BURST_AMPLITUDE_THRESHOLD = 1.4

# When detecting burst, if the amplitude is lower than 0.5 times
# average amplitude, we ignore it.
DEFAULT_BURST_TOO_SMALL = 0.5

# For a signal which is the combination of sine wave with fixed frequency f and
# amplitude 1 and standard noise with amplitude k, the average teager value is
# nearly linear to the noise level k.
# Given frequency f, we simulate a sine wave with default noise level and
# calculate its average teager value. Then, we can estimate the equivalent
# noise level of input signal by the average teager value of input signal.
DEFAULT_STANDARD_NOISE = 0.005

# For delay, burst, volume increasing/decreasing, if two delay(
# burst, volume increasing/decreasing) happen within
# DEFAULT_SAME_EVENT_SECS seconds, we consider they are the
# same event.
DEFAULT_SAME_EVENT_SECS = 0.001

# When detecting increasing/decreasing volume of signal, if the amplitude
# is lower than 0.1 times average amplitude, we ignore it.
DEFAULT_VOLUME_CHANGE_TOO_SMALL = 0.1

# If average amplitude of right block is less/more than average
# amplitude of left block times DEFAULT_VOLUME_CHANGE_AMPLITUDE, it will be
# considered as decreasing/increasing on volume.
DEFAULT_VOLUME_CHANGE_AMPLITUDE = 0.1

# If the increasing/decreasing volume event is too close to the start or the end
# of sine wave, we consider its volume change as part of rising/falling phase in
# the start/end.
NEAR_START_OR_END_SECS = 0.01

# After applying Hilbert transform, the resulting amplitude and frequency may be
# extremely large in the start and/or the end part. Thus, we will append zeros
# before and after the whole wave for 0.1 secs.
APPEND_ZEROS_SECS = 0.1

# If the noise event is too close to the start or the end of the data, we
# consider its noise as part of artifacts caused by edge effect of Hilbert
# transform.
# For example, originally, the data duration is 10 seconds.
# We append 0.1 seconds of zeros in the beginning and the end of the data, so
# the data becomes 10.2 seocnds long.
# Then, we apply Hilbert transform to 10.2 seconds of data.
# Near 0.1 seconds and 10.1 seconds, there will be edge effect of Hilbert
# transform. We do not want these be treated as noise.
# If NEAR_DATA_START_OR_END_SECS is set to 0.01, then the noise happened
# at [0, 0.11] and [10.09, 10.1] will be ignored.
NEAR_DATA_START_OR_END_SECS = 0.01

# If the noise event is too close to the start or the end of the sine wave in
# the data, we consider its noise as part of artifacts caused by edge effect of
# Hilbert transform.
# A |-------------|vvvvvvvvvvvvvvvvvvvvvvv|-------------|
# B |ooooooooo| d |                       | d |ooooooooo|
#
# A is full signal. It contains a sine wave and silence before and after sine
# wave.
# In B, |oooo| shows the parts that we are going to check for noise before/after
# sine wave. | d | is determined by NEAR_SINE_START_OR_END_SECS.
NEAR_SINE_START_OR_END_SECS = 0.01


class SineWaveNotFound(Exception):
    """Error when there's no sine wave found in the signal"""
    pass


def hilbert(x):
    """Hilbert transform copied from scipy.

    More information can be found here:
    http://docs.scipy.org/doc/scipy/reference/generated/scipy.signal.hilbert.html

    @param x: Real signal data to transform.

    @returns: Analytic signal of x, we can further extract amplitude and
              frequency from it.

    """
    x = numpy.asarray(x)
    if numpy.iscomplexobj(x):
        raise ValueError("x must be real.")
    axis = -1
    N = x.shape[axis]
    if N <= 0:
        raise ValueError("N must be positive.")

    Xf = numpy.fft.fft(x, N, axis=axis)
    h = numpy.zeros(N)
    if N % 2 == 0:
        h[0] = h[N // 2] = 1
        h[1:N // 2] = 2
    else:
        h[0] = 1
        h[1:(N + 1) // 2] = 2

    if len(x.shape) > 1:
        ind = [newaxis] * x.ndim
        ind[axis] = slice(None)
        h = h[ind]
    x = numpy.fft.ifft(Xf * h, axis=axis)
    return x


def noised_sine_wave(frequency, rate, noise_level):
    """Generates a sine wave of 2 second with specified noise level.

    @param frequency: Frequency of sine wave.
    @param rate: Sampling rate.
    @param noise_level: Required noise level.

    @returns: A sine wave with specified noise level.

    """
    wave = []
    for index in xrange(0, rate * 2):
        sample = 2.0 * math.pi * frequency * float(index) / float(rate)
        sine_wave = math.sin(sample)
        noise = noise_level * numpy.random.standard_normal()
        wave.append(sine_wave + noise)
    return wave


def average_teager_value(wave, amplitude):
    """Computes the normalized average teager value.

    After averaging the teager value, we will normalize the value by
    dividing square of amplitude.

    @param wave: Wave to apply teager operator.
    @param amplitude: Average amplitude of given wave.

    @returns: Average teager value.

    """
    teager_value, length = 0, len(wave)
    for i in range(1, length - 1):
        ith_teager_value = abs(wave[i] * wave[i] - wave[i - 1] * wave[i + 1])
        ith_teager_value *= max(1, abs(wave[i]))
        teager_value += ith_teager_value
    teager_value = (float(teager_value) / length) / (amplitude ** 2)
    return teager_value


def noise_level(amplitude, frequency, rate, teager_value_of_input):
    """Computes the noise level compared with standard_noise.

    For a signal which is the combination of sine wave with fixed frequency f
    and amplitude 1 and standard noise with amplitude k, the average teager
    value is nearly linear to the noise level k.
    Thus, we can compute the average teager value of a sine wave with
    standard_noise. Then, we can estimate the noise level of given input.

    @param amplitude: Amplitude of input audio.
    @param frequency: Dominant frequency of input audio.
    @param rate: Sampling rate.
    @param teager_value_of_input: Average teager value of input audio.

    @returns: A float value denotes the audio is equivalent to have
              how many times of noise compared with its amplitude.
              For example, 0.02 denotes that the wave has a noise which
              has standard distribution with standard deviation being
              0.02 times the amplitude of the wave.

    """
    standard_noise = DEFAULT_STANDARD_NOISE

    # Generates the standard sine wave with stdandard_noise level of noise.
    standard_wave = noised_sine_wave(frequency, rate, standard_noise)

    # Calculates the average teager value.
    teager_value_of_std_wave = average_teager_value(standard_wave, amplitude)

    return (teager_value_of_input / teager_value_of_std_wave) * standard_noise


def error(f1, f2):
    """Calculates the relative error between f1 and f2.

    @param f1: Exact value.
    @param f2: Test value.

    @returns: Relative error between f1 and f2.

    """
    return abs(float(f1) - float(f2)) / float(f1)


def hilbert_analysis(signal, rate, block_size):
    """Finds amplitude and frequency of each time of signal by Hilbert transform.

    @param signal: The wave to analyze.
    @param rate: Sampling rate.
    @param block_size: The size of block to transform.

    @returns: A tuple of list: (amplitude, frequency) composed of
              amplitude and frequency of each time.

    """
    # To apply Hilbert transform, the wave will be transformed
    # segment by segment. For each segment, its size will be
    # block_size and we will only take middle part of it.
    # Thus, each segment looks like: |-----|=====|=====|-----|.
    # "=...=" part will be taken while "-...-" part will be ignored.
    #
    # The whole size of taken part will be half of block_size
    # which will be hilbert_block.
    # The size of each ignored part will be half of hilbert_block
    # which will be half_hilbert_block.
    hilbert_block = block_size // 2
    half_hilbert_block = hilbert_block // 2
    # As mentioned above, for each block, we will only take middle
    # part of it. Thus, the whole transformation will be completed as:
    # |=====|=====|-----|           |-----|=====|=====|-----|
    #       |-----|=====|=====|-----|           |-----|=====|=====|
    #                   |-----|=====|=====|-----|
    # Specially, beginning and ending part may not have ignored part.
    length = len(signal)
    result = []
    for left_border in xrange(0, length, hilbert_block):
        right_border = min(length, left_border + hilbert_block)
        temp_left_border = max(0, left_border - half_hilbert_block)
        temp_right_border = min(length, right_border + half_hilbert_block)
        temp = hilbert(signal[temp_left_border:temp_right_border])
        for index in xrange(left_border, right_border):
            result.append(temp[index - temp_left_border])
    result = numpy.asarray(result)
    amplitude = numpy.abs(result)
    phase = numpy.unwrap(numpy.angle(result))
    frequency = numpy.diff(phase) / (2.0 * numpy.pi) * rate
    #frequency.append(frequency[len(frequency)-1])
    frequecny = numpy.append(frequency, frequency[len(frequency) - 1])
    return (amplitude, frequency)


def find_block_average_value(arr, side_block_size, block_size):
    """For each index, finds average value of its block, left block, right block.

    It will find average value for each index in the range.

    For each index, the range of its block is
        [max(0, index - block_size / 2), min(length - 1, index + block_size / 2)]
    For each index, the range of its left block is
        [max(0, index - size_block_size), index]
    For each index, the range of its right block is
        [index, min(length - 1, index + side_block_size)]

    @param arr: The array to be computed.
    @param side_block_size: the size of the left_block and right_block.
    @param block_size: the size of the block.

    @returns: A tuple of lists: (left_block_average_array,
                                 right_block_average_array,
                                 block_average_array)
    """
    length = len(arr)
    left_border, right_border = 0, 1
    left_block_sum = arr[0]
    right_block_sum = arr[0]
    left_average_array = numpy.zeros(length)
    right_average_array = numpy.zeros(length)
    block_average_array = numpy.zeros(length)
    for index in xrange(0, length):
        while left_border < index - side_block_size:
            left_block_sum -= arr[left_border]
            left_border += 1
        while right_border < min(length, index + side_block_size):
            right_block_sum += arr[right_border]
            right_border += 1

        left_average_value = float(left_block_sum) / (index - left_border + 1)
        right_average_value = float(right_block_sum) / (right_border - index)
        left_average_array[index] = left_average_value
        right_average_array[index] = right_average_value

        if index + 1 < length:
            left_block_sum += arr[index + 1]
        right_block_sum -= arr[index]
    left_border, right_border = 0, 1
    block_sum = 0
    for index in xrange(0, length):
        while left_border < index - block_size / 2:
            block_sum -= arr[left_border]
            left_border += 1
        while right_border < min(length, index + block_size / 2):
            block_sum += arr[right_border]
            right_border += 1

        average_value = float(block_sum) / (right_border - left_border)
        block_average_array[index] = average_value
    return (left_average_array, right_average_array, block_average_array)


def find_start_end_index(dominant_frequency,
                         block_frequency_delta,
                         block_size,
                         frequency_error_threshold):
    """Finds start and end index of sine wave.

    For each block with size of block_size, we check that whether its frequency
    is close enough to the dominant_frequency. If yes, we will consider this
    block to be within the sine wave.
    Then, it will return the start and end index of sine wave indicating that
    sine wave is between [start_index, end_index)
    It's okay if the whole signal only contains sine wave.

    @param dominant_frequency: Dominant frequency of signal.
    @param block_frequency_delta: Average absolute difference between dominant
                                  frequency and frequency of each block. For
                                  each index, its block is
                                  [max(0, index - block_size / 2),
                                   min(length - 1, index + block_size / 2)]
    @param block_size: Block size in samples.

    @returns: A tuple composed of (start_index, end_index)

    """
    length = len(block_frequency_delta)

    # Finds the start/end time index of playing based on dominant frequency
    start_index, end_index = length - 1 , 0
    for index in xrange(0, length):
        left_border = max(0, index - block_size / 2)
        right_border = min(length - 1, index + block_size / 2)
        frequency_error = block_frequency_delta[index] / dominant_frequency
        if frequency_error < frequency_error_threshold:
            start_index = min(start_index, left_border)
            end_index = max(end_index, right_border + 1)
    return (start_index, end_index)


def noise_detection(start_index, end_index,
                    block_amplitude, average_amplitude,
                    rate, noise_amplitude_threshold):
    """Detects noise before/after sine wave.

    If average amplitude of some sample's block before start of wave or after
    end of wave is more than average_amplitude times noise_amplitude_threshold,
    it will be considered as a noise.

    @param start_index: Start index of sine wave.
    @param end_index: End index of sine wave.
    @param block_amplitude: An array for average amplitude of each block, where
                            amplitude is computed from Hilbert transform.
    @param average_amplitude: Average amplitude of sine wave.
    @param rate: Sampling rate
    @param 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.

    @returns: A tuple of lists indicating the time that noise happens:
            (noise_before_playing, noise_after_playing).

    """
    length = len(block_amplitude)
    amplitude_threshold = average_amplitude * noise_amplitude_threshold
    same_event_samples = rate * DEFAULT_SAME_EVENT_SECS

    # Detects noise before playing.
    noise_time_point = []
    last_noise_end_time_point = []
    previous_noise_index = None
    times = 0
    for index in xrange(0, length):
        # Ignore noise too close to the beginning or the end of sine wave.
        # Check the docstring of NEAR_SINE_START_OR_END_SECS.
        if ((start_index - rate * NEAR_SINE_START_OR_END_SECS) <= index and
            (index < end_index + rate * NEAR_SINE_START_OR_END_SECS)):
            continue

        # Ignore noise too close to the beginning or the end of original data.
        # Check the docstring of NEAR_DATA_START_OR_END_SECS.
        if (float(index) / rate <=
            NEAR_DATA_START_OR_END_SECS + APPEND_ZEROS_SECS):
            continue
        if (float(length - index) / rate <=
            NEAR_DATA_START_OR_END_SECS + APPEND_ZEROS_SECS):
            continue
        if block_amplitude[index] > amplitude_threshold:
            same_event = False
            if previous_noise_index:
                same_event = (index - previous_noise_index) < same_event_samples
            if not same_event:
                index_start_sec = float(index) / rate - APPEND_ZEROS_SECS
                index_end_sec = float(index + 1) / rate - APPEND_ZEROS_SECS
                noise_time_point.append(index_start_sec)
                last_noise_end_time_point.append(index_end_sec)
                times += 1
            index_end_sec = float(index + 1) / rate - APPEND_ZEROS_SECS
            last_noise_end_time_point[times - 1] = index_end_sec
            previous_noise_index = index

    noise_before_playing, noise_after_playing = [], []
    for i in xrange(times):
        duration = last_noise_end_time_point[i] - noise_time_point[i]
        if noise_time_point[i] < float(start_index) / rate - APPEND_ZEROS_SECS:
            noise_before_playing.append((noise_time_point[i], duration))
        else:
            noise_after_playing.append((noise_time_point[i], duration))

    return (noise_before_playing, noise_after_playing)


def 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):
    """Detects delay during playing.

    For each sample, we will check whether the average amplitude of its block
    is less than average amplitude of its left block and its right block times
    delay_amplitude_threshold. Also, we will check whether the frequency of
    its block is far from the dominant frequency.
    If at least one constraint fulfilled, it will be considered as a delay.

    @param start_index: Start index of sine wave.
    @param end_index: End index of sine wave.
    @param block_amplitude: An array for average amplitude of each block, where
                            amplitude is computed from Hilbert transform.
    @param average_amplitude: Average amplitude of sine wave.
    @param dominant_frequency: Dominant frequency of signal.
    @param rate: Sampling rate
    @param left_block_amplitude: Average amplitude of left block of each index.
                                Ref to find_block_average_value function.
    @param right_block_amplitude: Average amplitude of right block of each index.
                                Ref to find_block_average_value function.
    @param block_frequency_delta: Average absolute difference frequency to
                                dominant frequency of block of each index.
                                Ref to find_block_average_value function.
    @param 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.
    @param frequency_error_threshold: Ref to DEFAULT_FREQUENCY_ERROR

    @returns: List of delay occurrence:
                [(time_1, duration_1), (time_2, duration_2), ...],
              where time and duration are in seconds.

    """
    delay_time_points = []
    last_delay_end_time_points = []
    previous_delay_index = None
    times = 0
    same_event_samples = rate * DEFAULT_SAME_EVENT_SECS
    start_time = float(start_index) / rate - APPEND_ZEROS_SECS
    end_time = float(end_index) / rate - APPEND_ZEROS_SECS
    for index in xrange(start_index, end_index):
        if block_amplitude[index] > average_amplitude * delay_amplitude_threshold:
            continue
        now_time = float(index) / rate - APPEND_ZEROS_SECS
        if abs(now_time - start_time) < NEAR_START_OR_END_SECS:
            continue
        if abs(now_time - end_time) < NEAR_START_OR_END_SECS:
            continue
        # If amplitude less than its left/right side and small enough,
        # it will be considered as a delay.
        amp_threshold = average_amplitude * delay_amplitude_threshold
        left_threshold = delay_amplitude_threshold * left_block_amplitude[index]
        amp_threshold = min(amp_threshold, left_threshold)
        right_threshold = delay_amplitude_threshold * right_block_amplitude[index]
        amp_threshold = min(amp_threshold, right_threshold)

        frequency_error = block_frequency_delta[index] / dominant_frequency

        amplitude_too_small = block_amplitude[index] < amp_threshold
        frequency_not_match = frequency_error > frequency_error_threshold

        if amplitude_too_small or frequency_not_match:
            same_event = False
            if previous_delay_index:
                same_event = (index - previous_delay_index) < same_event_samples
            if not same_event:
                index_start_sec = float(index) / rate - APPEND_ZEROS_SECS
                index_end_sec = float(index + 1) / rate - APPEND_ZEROS_SECS
                delay_time_points.append(index_start_sec)
                last_delay_end_time_points.append(index_end_sec)
                times += 1
            previous_delay_index = index
            index_end_sec = float(index + 1) / rate - APPEND_ZEROS_SECS
            last_delay_end_time_points[times - 1] = index_end_sec

    delay_list = []
    for i in xrange(len(delay_time_points)):
        duration = last_delay_end_time_points[i] - delay_time_points[i]
        delay_list.append( (delay_time_points[i], duration) )
    return delay_list


def 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):
    """Detects burst during playing.

    For each sample, we will check whether the average amplitude of its block is
    more than average amplitude of its left block and its right block times
    burst_amplitude_threshold. Also, we will check whether the frequency of
    its block is not compatible to the dominant frequency.
    If at least one constraint fulfilled, it will be considered as a burst.

    @param start_index: Start index of sine wave.
    @param end_index: End index of sine wave.
    @param block_amplitude: An array for average amplitude of each block, where
                            amplitude is computed from Hilbert transform.
    @param average_amplitude: Average amplitude of sine wave.
    @param dominant_frequency: Dominant frequency of signal.
    @param rate: Sampling rate
    @param left_block_amplitude: Average amplitude of left block of each index.
                                Ref to find_block_average_value function.
    @param right_block_amplitude: Average amplitude of right block of each index.
                                Ref to find_block_average_value function.
    @param block_frequency_delta: Average absolute difference frequency to
                                dominant frequency of block of each index.
    @param burst_amplitude_threshold: If the amplitude is higher than average
                            amplitude of its left block and its right block
                            times burst_amplitude_threshold. It will be
                            considered as a burst.
    @param frequency_error_threshold: Ref to DEFAULT_FREQUENCY_ERROR

    @returns: List of burst occurence: [time_1, time_2, ...],
              where time is in seconds.

    """
    burst_time_points = []
    previous_burst_index = None
    same_event_samples = rate * DEFAULT_SAME_EVENT_SECS
    for index in xrange(start_index, end_index):
        # If amplitude higher than its left/right side and large enough,
        # it will be considered as a burst.
        if block_amplitude[index] <= average_amplitude * DEFAULT_BURST_TOO_SMALL:
            continue
        if abs(index - start_index) < rate * NEAR_START_OR_END_SECS:
            continue
        if abs(index - end_index) < rate * NEAR_START_OR_END_SECS:
            continue
        amp_threshold = average_amplitude * DEFAULT_BURST_TOO_SMALL
        left_threshold = burst_amplitude_threshold * left_block_amplitude[index]
        amp_threshold = max(amp_threshold, left_threshold)
        right_threshold = burst_amplitude_threshold * right_block_amplitude[index]
        amp_threshold = max(amp_threshold, right_threshold)

        frequency_error = block_frequency_delta[index] / dominant_frequency

        amplitude_too_large = block_amplitude[index] > amp_threshold
        frequency_not_match = frequency_error > frequency_error_threshold

        if amplitude_too_large or frequency_not_match:
            same_event = False
            if previous_burst_index:
                same_event = index - previous_burst_index < same_event_samples
            if not same_event:
                burst_time_points.append(float(index) / rate - APPEND_ZEROS_SECS)
            previous_burst_index = index

    return burst_time_points


def changing_volume_detection(start_index, end_index,
                              average_amplitude,
                              rate,
                              left_block_amplitude,
                              right_block_amplitude,
                              volume_changing_amplitude_threshold):
    """Finds volume changing during playback.

    For each index, we will compare average amplitude of its left block and its
    right block. If average amplitude of right block is more than average
    amplitude of left block times (1 + DEFAULT_VOLUME_CHANGE_AMPLITUDE), it will
    be considered as an increasing volume. If the one of right block is less
    than that of left block times (1 - DEFAULT_VOLUME_CHANGE_AMPLITUDE), it will
    be considered as a decreasing volume.

    @param start_index: Start index of sine wave.
    @param end_index: End index of sine wave.
    @param average_amplitude: Average amplitude of sine wave.
    @param rate: Sampling rate
    @param left_block_amplitude: Average amplitude of left block of each index.
                                Ref to find_block_average_value function.
    @param right_block_amplitude: Average amplitude of right block of each index.
                                Ref to find_block_average_value function.
    @param 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
                                                DEFAULT_VOLUME_CHANGE_AMPLITUDE

    @returns: List of volume changing composed of 1 for increasing and
              -1 for decreasing.

    """
    length = len(left_block_amplitude)

    # Detects rising and/or falling volume.
    previous_rising_index, previous_falling_index = None, None
    changing_time = []
    changing_events = []
    amplitude_threshold = average_amplitude * DEFAULT_VOLUME_CHANGE_TOO_SMALL
    same_event_samples = rate * DEFAULT_SAME_EVENT_SECS
    for index in xrange(start_index, end_index):
        # Skips if amplitude is too small.
        if left_block_amplitude[index] < amplitude_threshold:
            continue
        if right_block_amplitude[index] < amplitude_threshold:
            continue
        # Skips if changing is from start or end time
        if float(abs(start_index - index)) / rate < NEAR_START_OR_END_SECS:
            continue
        if float(abs(end_index   - index)) / rate < NEAR_START_OR_END_SECS:
            continue

        delta_margin = volume_changing_amplitude_threshold
        if left_block_amplitude[index] > 0:
            delta_margin *= left_block_amplitude[index]

        increasing_threshold = left_block_amplitude[index] + delta_margin
        decreasing_threshold = left_block_amplitude[index] - delta_margin

        if right_block_amplitude[index] > increasing_threshold:
            same_event = False
            if previous_rising_index:
                same_event = index - previous_rising_index < same_event_samples
            if not same_event:
                changing_time.append(float(index) / rate - APPEND_ZEROS_SECS)
                changing_events.append(+1)
            previous_rising_index = index
        if right_block_amplitude[index] < decreasing_threshold:
            same_event = False
            if previous_falling_index:
                same_event = index - previous_falling_index < same_event_samples
            if not same_event:
                changing_time.append(float(index) / rate - APPEND_ZEROS_SECS)
                changing_events.append(-1)
            previous_falling_index = index

    # Combines consecutive increasing/decreasing event.
    combined_changing_events, prev = [], 0
    for i in xrange(len(changing_events)):
        if changing_events[i] == prev:
            continue
        combined_changing_events.append((changing_time[i], changing_events[i]))
        prev = changing_events[i]
    return combined_changing_events


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.

    @param signal: A list of numbers for one-channel PCM data. The data should
                   be normalized to [-1,1].
    @param rate: Sampling rate
    @param dominant_frequency: Dominant frequency of signal. Set None to
                               recalculate the frequency in this function.
    @param 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.
    @param frequency_error_threshold: Ref to DEFAULT_FREQUENCY_ERROR.
    @param 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.
    @param 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.
    @param 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.
    @param 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[start_index: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[start_index: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
           }