Source code for analysis.ZeroXAnalysis
from __future__ import print_function, division
import numpy as np
import logging
from numpy.lib import stride_tricks
from Analysis import Analysis
import pdb
logger = logging.getLogger(__name__)
[docs]class ZeroXAnalysis(Analysis): 
    """
    Zero-corssing descriptor class for generation of zero-crossing rate
    analysis.
    This descriptor calculates the zero-crossing rate for overlapping grains of
    an AnalysedAudioFile object.  A full definition of zero-crossing analysis
    can be found in the documentation.
    Arguments:
    - analysis_group: the HDF5 file group to use for the storage of the
      analysis.
    - config: The configuration module used to configure the analysis
    """
    def __init__(self, AnalysedAudioFile, frames, analysis_group, config=None):
        super(ZeroXAnalysis, self).__init__(AnalysedAudioFile,frames, analysis_group, 'ZeroCrossing')
        self.logger = logging.getLogger(__name__+'.{0}Analysis'.format(self.name))
        self.analysis_group = analysis_group
        self.logger.info("Creating zero crossing analysis for {0}".format(self.AnalysedAudioFile.name))
        self.create_analysis(frames)
    @staticmethod
    def create_zerox_analysis(
        frames,
        window_size=512,
        overlapFac=0.5,
        *args,
        **kwargs
    ):
        """Generate zero crossing value for window of the signal"""
        if hasattr(frames, '__call__'):
            frames = frames()
        hopSize = int(window_size - np.floor(overlapFac * window_size))
        # zeros at beginning (thus center of 1st window should be for sample nr. 0)
        samples = np.append(np.zeros(np.floor(window_size/2.0)), frames)
        # cols for windowing
        cols = np.ceil((len(samples) - window_size) / float(hopSize)) + 1
        # zeros at end (thus samples can be fully covered by frames)
        samples = np.append(samples, np.zeros(window_size))
        # TODO: Better handeling of zeros based on previous sign would improve
        # accuracy.
        epsilon = np.finfo(float).eps
        samples[samples == 0.] += epsilon
        frames = stride_tricks.as_strided(
            samples,
            shape=(cols, window_size),
            strides=(samples.strides[0]*hopSize, samples.strides[0])
        ).copy()
        zero_crossing = np.sum(np.abs(np.diff(np.sign(frames))), axis=1)
        return zero_crossing
    @staticmethod
    def calc_zerox_frame_times(zerox_frames, sample_frames, samplerate):
        """Calculate times for frames using sample size and samplerate."""
        if hasattr(sample_frames, '__call__'):
            sample_frames = sample_frames()
        # Get number of frames for time and frequency
        timebins = zerox_frames.shape[0]
        # Create array ranging from 0 to number of time frames
        scale = np.arange(timebins+1)
        # divide the number of samples by the total number of frames, then
        # multiply by the frame numbers.
        zerox_times = (sample_frames.shape[0]/timebins) * scale[:-1]
        # Divide by the samplerate to give times in seconds
        zerox_times = zerox_times / samplerate
        return zerox_times
    def hdf5_dataset_formatter(self, *args, **kwargs):
        '''
        Formats the output from the analysis method to save to the HDF5 file.
        '''
        samples = self.AnalysedAudioFile.read_grain()
        samplerate = self.AnalysedAudioFile.samplerate
        output = self.create_zerox_analysis(*args, **kwargs)
        times = self.calc_zerox_frame_times(output, args[0], samplerate)
        return ({'frames': output, 'times': times}, {})