Spaces:
Running
Running
class Metric: | |
def __init__(self, name, window, hop=None, verbose=False): | |
# the metric operates on some fixed rate only or only on mono ? | |
self.fixed_rate = None | |
self.mono = False | |
# is the metric absolute or relative ? | |
self.absolute = False | |
# length and hop of windows | |
self.window = window | |
if hop is None: | |
hop = window | |
self.hop = hop | |
self.name = name | |
self.verbose = verbose | |
def test_window(self, audios, rate): | |
raise NotImplementedError | |
def test(self, *test_files, array_rate=None): | |
"""loading sound files and making sure they all have the same lengths | |
(zero-padding to the largest). Also works with numpy arrays. | |
Then, calling the `test_window` function that should be specialised | |
depending on the metric.""" | |
# imports | |
import soundfile as sf | |
import resampy | |
from museval.metrics import Framing | |
import numpy as np | |
audios = [] | |
maxlen = 0 | |
if isinstance(test_files, str): | |
test_files = [test_files] | |
if self.absolute and len(test_files) > 1: | |
if self.verbose: | |
print(' [%s] is absolute. Processing first file only' | |
% self.name) | |
test_files = [test_files[0],] | |
for file in test_files: | |
# Loading sound file | |
if isinstance(file, str): | |
audio, rate = sf.read(file, always_2d=True) | |
else: | |
rate = array_rate | |
if rate is None: | |
raise ValueError('Sampling rate needs to be specified ' | |
'when feeding numpy arrays.') | |
audio = file | |
# Standardize shapes | |
if len(audio.shape) == 1: | |
audio = audio[:, None] | |
if len(audio.shape) != 2: | |
raise ValueError('Please provide 1D or 2D array, received ' | |
'{}D array'.format(len(audio.shape))) | |
if self.fixed_rate is not None and rate != self.fixed_rate: | |
if self.verbose: | |
print(' [%s] preferred is %dkHz rate. resampling' | |
% (self.name, self.fixed_rate)) | |
audio = resampy.resample(audio, rate, self.fixed_rate, axis=0) | |
rate = self.fixed_rate | |
if self.mono and audio.shape[1] > 1: | |
if self.verbose: | |
print(' [%s] only supports mono. Will use first channel' | |
% self.name) | |
audio = audio[..., 0, None] | |
if self.mono: | |
audio = audio[..., 0] | |
maxlen = max(maxlen, audio.shape[0]) | |
audios += [audio] | |
for index, audio in enumerate(audios): | |
if audio.shape[0] != maxlen: | |
new = np.zeros((maxlen,) + audio.shape[1:]) | |
new[:audio.shape[0]] = audio | |
audios[index] = new | |
if self.window is not None: | |
framer = Framing(self.window * rate, | |
self.hop * rate, maxlen) | |
nwin = framer.nwin | |
result = {} | |
for (t, win) in enumerate(framer): | |
result_t = self.test_window([audio[win] for audio in audios], | |
rate) | |
for metric in result_t.keys(): | |
if metric not in result.keys(): | |
result[metric] = np.empty(nwin) | |
result[metric][t] = result_t[metric] | |
else: | |
result = self.test_window(audios, rate) | |
return result | |
import absolute | |
import relative | |
class MetricsList: | |
def __init__(self): | |
self.metrics = [] | |
def __add__(self, metric): | |
self.metrics += [metric] | |
return self | |
def __str__(self): | |
return 'Metrics: ' + ' '.join([x.name for x in self.metrics]) | |
def __call__(self, *files, rate=None): | |
result = {} | |
for metric in self.metrics: | |
result_metric = metric.test(*files, array_rate=rate) | |
for name in result_metric.keys(): | |
result[name] = result_metric[name] | |
return result | |
def load(metrics='', window=2, verbose=False): | |
""" Load the desired metrics inside a Metrics object that can then | |
be called to compute all the desired metrics. | |
Parameters: | |
---------- | |
metrics: str or list of str | |
the metrics matching any of these will be automatically loaded. this | |
match is relative to the structure of the speechmetrics package. | |
For instance: | |
* 'absolute' will match all absolute metrics | |
* 'absolute.srmr' or 'srmr' will only match SRMR | |
* '' will match all | |
window: float | |
the window length to use for testing the files. | |
verbose: boolean | |
will display information during computations | |
Returns: | |
-------- | |
A MetricsList object, that can be run to get the desired metrics | |
""" | |
import pkgutil | |
import importlib | |
result = MetricsList() | |
found_modules = [] | |
iterator = pkgutil.walk_packages(__path__, __name__ + '.') | |
if isinstance(metrics, str): | |
metrics = [metrics] | |
for module_info in iterator: | |
if any([metric in module_info.name for metric in metrics]): | |
module = importlib.import_module(module_info.name) | |
if module not in found_modules: | |
found_modules += [module], | |
if hasattr(module, 'load'): | |
load_function = getattr(module, 'load') | |
new_metric = load_function(window) | |
new_metric.verbose = verbose | |
result += new_metric | |
print('Loaded ', module_info.name) | |
return result | |