Source code for analysis.F0HarmRatioAnalysis
from __future__ import print_function, division
import numpy as np
import logging
import pdb
import numpy as np
from numpy.lib import stride_tricks
from Analysis import Analysis
from scipy import signal
from numpy.fft import fft, ifft, fftshift
import warnings
from numpy import polyfit, arange
[docs]class F0HarmRatioAnalysis(Analysis):
"""
The F0 HarmRatio analysis object is a placeholder class to allow access to
the harmonic ratio generated in the f0 analysis. As a result it does not
have it's own "create analysis method as other analyses do. it is designed
to be used for the retreival of the f0 harmonic ratio analysis for
matching.
F0 analysis must be generated for the AnalysedAudioFile in order to use
this object.
"""
def __init__(self, AnalysedAudioFile, frames, analysis_group, config=None):
super(F0HarmRatioAnalysis, self).__init__(AnalysedAudioFile, frames, analysis_group, 'F0HarmRatio')
self.logger = logging.getLogger(__name__+'.{0}Analysis'.format(self.name))
# Store reference to the file to be analysed
self.AnalysedAudioFile = AnalysedAudioFile
if config:
self.threshold = config.f0["ratio_threshold"]
else:
self.threshold = 0.
self.analysis_group = analysis_group
self.logger.info("Initialising F0HarmRatio analysis for {0}".format(self.AnalysedAudioFile.name))
def get_analysis_grains(self, start, end):
"""
Retrieve analysis frames for period specified in start and end times.
arrays of start and end time pairs will produce an array of equivelant
size containing frames for these times.
"""
times = self.analysis_group["F0"]["times"][:]
hr = self.analysis_group["F0"]["harmonic_ratio"][:]
start = start / 1000
end = end / 1000
vtimes = times.reshape(-1, 1)
nan_inds = hr < self.threshold
hr[nan_inds] = np.nan
selection = np.transpose((vtimes >= start) & (vtimes <= end))
if not selection.any():
frame_center = start + (end-start)/2.
closest_frames = np.abs(vtimes-frame_center).argsort()[:2]
selection[closest_frames] = True
return ((hr, times), selection)
@staticmethod
def calc_F0HarmRatio_frame_times(F0HarmRatioframes, sample_frames, samplerate):
"""Calculate times for frames using sample size and samplerate."""
samplerate *= 1
if hasattr(sample_frames, '__call__'):
sample_frames = sample_frames()
# Get number of frames for time and frequency
timebins = F0HarmRatioframes.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.
F0HarmRatio_times = (sample_frames.shape[0]/timebins) * scale[:-1]
# Divide by the samplerate to give times in seconds
F0HarmRatio_times = F0HarmRatio_times / samplerate
return F0HarmRatio_times
def analysis_formatter(self, data, selection, format):
"""Calculate the average analysis value of the grain using the match format specified."""
harm_ratio, times = data
# Get indexes of all valid frames (that aren't nan)
valid_inds = np.isfinite(harm_ratio)
format_style_dict = {
'mean': np.mean,
'median': np.median,
'log2_mean': self.log2_mean,
'log2_median': self.log2_median,
}
# For debugging apply along axis:
#for ind, i in enumerate(selection):
# output[ind] = self.formatter_func(i, frames, valid_inds, harm_ratio, formatter=format_style_dict[format])
if not selection.size:
# TODO: Add warning here
return np.nan
output = np.apply_along_axis(
self.formatter_func,
1,
selection,
harm_ratio,
valid_inds,
formatter=format_style_dict[format]
)
return output