Source code for muda.deformers.time

#!/usr/bin/env python
# -*- encoding: utf-8 -*-
# CREATED:2015-02-02 10:09:43 by Brian McFee <brian.mcfee@nyu.edu>
'''Time stretching deformations'''

import pyrubberband as pyrb
import numpy as np
import pandas as pd

from ..base import BaseTransformer

__all__ = ['TimeStretch',
           'RandomTimeStretch',
           'LogspaceTimeStretch']


class AbstractTimeStretch(BaseTransformer):
    '''Abstract base class for time stretching

    This contains the deformation functions and
    annotation query mapping, but does not manage
    state or parameters.
    '''

    def __init__(self):
        BaseTransformer.__init__(self)

        # Build the annotation mappers
        self._register('.*', self.deform_times)
        self._register('tempo', self.deform_tempo)

    @staticmethod
    def audio(mudabox, state):
        # Deform the audio and metadata
        mudabox._audio['y'] = pyrb.time_stretch(mudabox._audio['y'],
                                                mudabox._audio['sr'],
                                                state['rate'])

    @staticmethod
    def metadata(metadata, state):
        # Deform the metadata
        metadata.duration /= state['rate']

    @staticmethod
    def deform_tempo(annotation, state):
        # Deform a tempo annotation

        for obs in annotation.pop_data():
            annotation.append(time=obs.time, duration=obs.duration,
                              confidence=obs.confidence,
                              value=state['rate'] * obs.value)

    @staticmethod
    def deform_times(ann, state):
        # Deform time values for all annotations.

        ann.time /= state['rate']

        if ann.duration is not None:
            ann.duration /= state['rate']

        for obs in ann.pop_data():
            ann.append(time=obs.time / state['rate'],
                       duration=obs.duration / state['rate'],
                       value=obs.value, confidence=obs.confidence)


[docs]class TimeStretch(AbstractTimeStretch): '''Static time stretching by a fixed rate This transformation affects the following attributes: - Annotations - all: time, duration - tempo: values - metadata - duration - Audio Attributes ---------- rate : float or list of floats, strictly positive The rate at which to speedup the audio. - rate > 1 speeds up, - rate < 1 slows down. Examples -------- >>> D = muda.deformers.TimeStretch(rate=2.0) >>> out_jams = list(D.transform(jam_in)) See Also -------- LogspaceTimeStretch RandomTimeStretch ''' def __init__(self, rate=1.2): '''Time stretching''' AbstractTimeStretch.__init__(self) self.rate = np.atleast_1d(rate).flatten() if np.any(self.rate <= 0): raise ValueError('rate parameter must be strictly positive.') self.rate = self.rate.tolist() def states(self, jam): for rate in self.rate: yield dict(rate=rate)
[docs]class LogspaceTimeStretch(AbstractTimeStretch): '''Logarithmically spaced time stretching. `n_samples` are generated with stretching spaced logarithmically between `2.0**lower` and 2`.0**upper`. This transformation affects the following attributes: - Annotations - all: time, duration - tempo: values - metadata - duration - Audio Attributes ---------- n_samples : int > 0 Number of deformations to generate lower : float upper : float > lower Minimum and maximum bounds on the stretch parameters See Also -------- TimeStretch RandomTimeStretch ''' def __init__(self, n_samples=3, lower=-0.3, upper=0.3): AbstractTimeStretch.__init__(self) if upper <= lower: raise ValueError('upper must be strictly larger than lower') if n_samples <= 0: raise ValueError('n_samples must be strictly positive') self.n_samples = n_samples self.lower = float(lower) self.upper = float(upper) def states(self, jam): rates = 2.0**np.linspace(self.lower, self.upper, num=self.n_samples, endpoint=True) for rate in rates: yield dict(rate=rate)
[docs]class RandomTimeStretch(AbstractTimeStretch): '''Random time stretching For each deformation, the rate parameter is drawn from a log-normal distribution with parameters `(location, scale)` - Annotations - all: time, duration - tempo: values - metadata - duration - Audio Attributes ---------- n_samples : int > 0 The number of samples to generate location : float scale : float > 0 Parameters of a log-normal distribution from which rate parameters are sampled. See Also -------- TimeStretch LogspaceTimeStretch numpy.random.lognormal ''' def __init__(self, n_samples=3, location=0.0, scale=1.0e-1): AbstractTimeStretch.__init__(self) if scale <= 0: raise ValueError('scale parameter must be strictly positive.') if n_samples <= 0: raise ValueError('n_samples must be strictly positive') self.n_samples = n_samples self.location = location self.scale = scale def states(self, jam): rates = np.random.lognormal(mean=self.location, sigma=self.scale, size=self.n_samples) for rate in rates: yield dict(rate=rate)