#!/usr/bin/env python
# -*- encoding: utf-8 -*-
'''
Core functionality
==================
.. autosummary::
:toctree: generated/
Pump
'''
import librosa
import jams
from .exceptions import ParameterError
from .task import BaseTaskTransformer
from .feature import FeatureExtractor
from .sampler import Sampler
[docs]class Pump(object):
'''Top-level pump object.
This class is used to collect feature and task transformers
Attributes
----------
ops : list of (BaseTaskTransformer, FeatureExtractor)
The operations to apply
Examples
--------
Create a CQT and chord transformer
>>> p_cqt = pumpp.feature.CQT('cqt', sr=44100, hop_length=1024)
>>> p_chord = pumpp.task.ChordTagTransformer(sr=44100, hop_length=1024)
>>> pump = pumpp.Pump(p_cqt, p_chord)
>>> data = pump.transform(audio_f='/my/audio/file.mp3',
... jam='/my/jams/annotation.jams')
Or use the call interface:
>>> data = pump(audio_f='/my/audio/file.mp3',
... jam='/my/jams/annotation.jams')
Or apply to audio in memory, and without existing annotations:
>>> y, sr = librosa.load('/my/audio/file.mp3')
>>> data = pump(y=y, sr=sr)
Access all the fields produced by this pump:
>>> pump.fields
{'chord/chord': Tensor(shape=(None, 170), dtype=<class 'bool'>),
'cqt/mag': Tensor(shape=(None, 288), dtype=<class 'numpy.float32'>),
'cqt/phase': Tensor(shape=(None, 288), dtype=<class 'numpy.float32'>)}
Access a constituent operator by name:
>>> pump['chord'].fields
{'chord/chord': Tensor(shape=(None, 170), dtype=<class 'bool'>)}
'''
[docs] def __init__(self, *ops):
self.ops = []
self.opmap = dict()
for op in ops:
self.add(op)
[docs] def add(self, op):
'''Add an operation to this pump.
Parameters
----------
op : BaseTaskTransformer, FeatureExtractor
The operation to add
Raises
------
ParameterError
if `op` is not of a correct type
'''
if not isinstance(op, (BaseTaskTransformer, FeatureExtractor)):
raise ParameterError('op={} must be one of '
'(BaseTaskTransformer, FeatureExtractor)'
.format(op))
if op.name in self.opmap:
raise ParameterError('Duplicate operator name detected: '
'{}'.format(op))
self.opmap[op.name] = op
self.ops.append(op)
[docs] def sampler(self, n_samples, duration, random_state=None):
'''Construct a sampler object for this pump's operators.
Parameters
----------
n_samples : None or int > 0
The number of samples to generate
duration : int > 0
The duration (in frames) of each sample patch
random_state : None, int, or np.random.RandomState
If int, random_state is the seed used by the random number
generator;
If RandomState instance, random_state is the random number
generator;
If None, the random number generator is the RandomState instance
used by np.random.
Returns
-------
sampler : pumpp.Sampler
The sampler object
See Also
--------
pumpp.sampler.Sampler
'''
return Sampler(n_samples, duration,
random_state=random_state,
*self.ops)
@property
def fields(self):
out = dict()
for op in self.ops:
out.update(**op.fields)
return out
[docs] def layers(self):
'''Construct Keras input layers for all feature transformers
in the pump.
Returns
-------
layers : {field: keras.layers.Input}
A dictionary of keras input layers, keyed by the corresponding
fields.
'''
L = dict()
for op in self.ops:
if hasattr(op, 'layers'):
L.update(op.layers())
return L
def __getitem__(self, key):
return self.opmap.get(key)
def __call__(self, *args, **kwargs):
return self.transform(*args, **kwargs)