def _viterbi(log_prob, log_trans, log_p_init, state, value, ptr):  # pragma: no cover
    '''Core Viterbi algorithm.

    This is intended for internal use only.

    Parameters
    ----------
    log_prob : np.ndarray [shape=(T, m)]
        `log_prob[t, s]` is the conditional log-likelihood
        log P[X = X(t) | State(t) = s]

    log_trans : np.ndarray [shape=(m, m)]
        The log transition matrix
        `log_trans[i, j]` = log P[State(t+1) = j | State(t) = i]

    log_p_init : np.ndarray [shape=(m,)]
        log of the initial state distribution

    state : np.ndarray [shape=(T,), dtype=int]
        Pre-allocated state index array

    value : np.ndarray [shape=(T, m)] float
        Pre-allocated value array

    ptr : np.ndarray [shape=(T, m), dtype=int]
        Pre-allocated pointer array

    Returns
    -------
    None
        All computations are performed in-place on `state, value, ptr`.
    '''
    n_steps, n_states = log_prob.shape

    # factor in initial state distribution
    value[0] = log_prob[0] + log_p_init

    for t in range(1, n_steps):
        # Want V[t, j] <- p[t, j] * max_k V[t-1, k] * A[k, j]
        #    assume at time t-1 we were in state k
        #    transition k -> j

        # Broadcast over rows:
        #    Tout[k, j] = V[t-1, k] * A[k, j]
        #    then take the max over columns
        # We'll do this in log-space for stability

        trans_out = value[t - 1] + log_trans.T

        # Unroll the max/argmax loop to enable numba support
        for j in range(n_states):
            ptr[t, j] = np.argmax(trans_out[j])
            # value[t, j] = log_prob[t, j] + np.max(trans_out[j])
            value[t, j] = log_prob[t, j] + trans_out[j, ptr[t][j]]

    # Now roll backward

    # Get the last state
    state[-1] = np.argmax(value[-1])

    for t in range(n_steps - 2, -1, -1):
        state[t] = ptr[t+1, state[t+1]]

def _viterbi ( log_prob , log_trans , log_p_init , state , value , ptr ) : n_steps , n_states = log_prob . shape value [ 0 ] = log_prob [ 0 ] + log_p_init for t in range ( 1 , n_steps ) : trans_out = value [ t - 1 ] + log_trans . T for j in range ( n_states ) : ptr [ t , j ] = np . argmax ( trans_out [ j ] ) value [ t , j ] = log_prob [ t , j ] + trans_out [ j , ptr [ t ] [ j ] ] state [ - 1 ] = np . argmax ( value [ - 1 ] ) for t in range ( n_steps - 2 , - 1 , - 1 ) : state [ t ] = ptr [ t + 1 , state [ t + 1 ] ]


def viterbi_discriminative(prob, transition, p_state=None, p_init=None, return_logp=False):
    '''Viterbi decoding from discriminative state predictions.

    Given a sequence of conditional state predictions `prob[s, t]`,
    indicating the conditional likelihood of state `s` given the
    observation at time `t`, and a transition matrix `transition[i, j]`
    which encodes the conditional probability of moving from state `i`
    to state `j`, the Viterbi algorithm computes the most likely sequence
    of states from the observations.

    This implementation uses the standard Viterbi decoding algorithm
    for observation likelihood sequences, under the assumption that
    `P[Obs(t) | State(t) = s]` is proportional to
    `P[State(t) = s | Obs(t)] / P[State(t) = s]`, where the denominator
    is the marginal probability of state `s` occurring as given by `p_state`.

    Parameters
    ----------
    prob : np.ndarray [shape=(n_states, n_steps), non-negative]
        `prob[s, t]` is the probability of state `s` conditional on
        the observation at time `t`.
        Must be non-negative and sum to 1 along each column.

    transition : np.ndarray [shape=(n_states, n_states), non-negative]
        `transition[i, j]` is the probability of a transition from i->j.
        Each row must sum to 1.

    p_state : np.ndarray [shape=(n_states,)]
        Optional: marginal probability distribution over states,
        must be non-negative and sum to 1.
        If not provided, a uniform distribution is assumed.

    p_init : np.ndarray [shape=(n_states,)]
        Optional: initial state distribution.
        If not provided, it is assumed to be uniform.

    return_logp : bool
        If `True`, return the log-likelihood of the state sequence.

    Returns
    -------
    Either `states` or `(states, logp)`:

    states : np.ndarray [shape=(n_steps,)]
        The most likely state sequence.

    logp : scalar [float]
        If `return_logp=True`, the log probability of `states` given
        the observations.

    See Also
    --------
    viterbi : Viterbi decoding from observation likelihoods
    viterbi_binary: Viterbi decoding for multi-label, conditional state likelihoods

    Examples
    --------
    This example constructs a simple, template-based discriminative chord estimator,
    using CENS chroma as input features.

    .. note:: this chord model is not accurate enough to use in practice. It is only
            intended to demonstrate how to use discriminative Viterbi decoding.

    >>> # Create templates for major, minor, and no-chord qualities
    >>> maj_template = np.array([1,0,0, 0,1,0, 0,1,0, 0,0,0])
    >>> min_template = np.array([1,0,0, 1,0,0, 0,1,0, 0,0,0])
    >>> N_template   = np.array([1,1,1, 1,1,1, 1,1,1, 1,1,1.]) / 4.
    >>> # Generate the weighting matrix that maps chroma to labels
    >>> weights = np.zeros((25, 12), dtype=float)
    >>> labels = ['C:maj', 'C#:maj', 'D:maj', 'D#:maj', 'E:maj', 'F:maj',
    ...           'F#:maj', 'G:maj', 'G#:maj', 'A:maj', 'A#:maj', 'B:maj',
    ...           'C:min', 'C#:min', 'D:min', 'D#:min', 'E:min', 'F:min',
    ...           'F#:min', 'G:min', 'G#:min', 'A:min', 'A#:min', 'B:min',
    ...           'N']
    >>> for c in range(12):
    ...     weights[c, :] = np.roll(maj_template, c) # c:maj
    ...     weights[c + 12, :] = np.roll(min_template, c)  # c:min
    >>> weights[-1] = N_template  # the last row is the no-chord class
    >>> # Make a self-loop transition matrix over 25 states
    >>> trans = librosa.sequence.transition_loop(25, 0.9)

    >>> # Load in audio and make features
    >>> y, sr = librosa.load(librosa.util.example_audio_file())
    >>> chroma = librosa.feature.chroma_cens(y=y, sr=sr, bins_per_octave=36)
    >>> # Map chroma (observations) to class (state) likelihoods
    >>> probs = np.exp(weights.dot(chroma))  # P[class | chroma] proportional to exp(template' chroma)
    >>> probs /= probs.sum(axis=0, keepdims=True)  # probabilities must sum to 1 in each column
    >>> # Compute independent frame-wise estimates
    >>> chords_ind = np.argmax(probs, axis=0)
    >>> # And viterbi estimates
    >>> chords_vit = librosa.sequence.viterbi_discriminative(probs, trans)

    >>> # Plot the features and prediction map
    >>> import matplotlib.pyplot as plt
    >>> plt.figure(figsize=(10, 6))
    >>> plt.subplot(2,1,1)
    >>> librosa.display.specshow(chroma, x_axis='time', y_axis='chroma')
    >>> plt.colorbar()
    >>> plt.subplot(2,1,2)
    >>> librosa.display.specshow(weights, x_axis='chroma')
    >>> plt.yticks(np.arange(25) + 0.5, labels)
    >>> plt.ylabel('Chord')
    >>> plt.colorbar()
    >>> plt.tight_layout()

    >>> # And plot the results
    >>> plt.figure(figsize=(10, 4))
    >>> librosa.display.specshow(probs, x_axis='time', cmap='gray')
    >>> plt.colorbar()
    >>> times = librosa.frames_to_time(np.arange(len(chords_vit)))
    >>> plt.scatter(times, chords_ind + 0.75, color='lime', alpha=0.5, marker='+', s=15, label='Independent')
    >>> plt.scatter(times, chords_vit + 0.25, color='deeppink', alpha=0.5, marker='o', s=15, label='Viterbi')
    >>> plt.yticks(0.5 + np.unique(chords_vit), [labels[i] for i in np.unique(chords_vit)], va='center')
    >>> plt.legend(loc='best')
    >>> plt.tight_layout()

    '''

    n_states, n_steps = prob.shape

    if transition.shape != (n_states, n_states):
        raise ParameterError('transition.shape={}, must be '
                             '(n_states, n_states)={}'.format(transition.shape,
                                                              (n_states, n_states)))

    if np.any(transition < 0) or not np.allclose(transition.sum(axis=1), 1):
        raise ParameterError('Invalid transition matrix: must be non-negative '
                             'and sum to 1 on each row.')

    if np.any(prob < 0) or not np.allclose(prob.sum(axis=0), 1):
        raise ParameterError('Invalid probability values: each column must '
                             'sum to 1 and be non-negative')

    states = np.zeros(n_steps, dtype=int)
    values = np.zeros((n_steps, n_states), dtype=float)
    ptr = np.zeros((n_steps, n_states), dtype=int)

    # Compute log-likelihoods while avoiding log-underflow
    epsilon = np.finfo(prob.dtype).tiny

    # Compute marginal log probabilities while avoiding underflow
    if p_state is None:
        p_state = np.empty(n_states)
        p_state.fill(1./n_states)
    elif p_state.shape != (n_states,):
        raise ParameterError('Marginal distribution p_state must have shape (n_states,). '
                             'Got p_state.shape={}'.format(p_state.shape))
    elif np.any(p_state < 0) or not np.allclose(p_state.sum(axis=-1), 1):
        raise ParameterError('Invalid marginal state distribution: '
                             'p_state={}'.format(p_state))

    log_trans = np.log(transition + epsilon)
    log_marginal = np.log(p_state + epsilon)

    # By Bayes' rule, P[X | Y] * P[Y] = P[Y | X] * P[X]
    # P[X] is constant for the sake of maximum likelihood inference
    # and P[Y] is given by the marginal distribution p_state.
    #
    # So we have P[X | y] \propto P[Y | x] / P[Y]
    # if X = observation and Y = states, this can be done in log space as
    # log P[X | y] \propto \log P[Y | x] - \log P[Y]
    log_prob = np.log(prob.T + epsilon) - log_marginal

    if p_init is None:
        p_init = np.empty(n_states)
        p_init.fill(1./n_states)
    elif np.any(p_init < 0) or not np.allclose(p_init.sum(), 1):
        raise ParameterError('Invalid initial state distribution: '
                             'p_init={}'.format(p_init))

    log_p_init = np.log(p_init + epsilon)

    _viterbi(log_prob, log_trans, log_p_init, states, values, ptr)

    if return_logp:
        return states, values[-1, states[-1]]

    return states

Viterbi decoding from discriminative state predictions .


def transition_uniform(n_states):
    '''Construct a uniform transition matrix over `n_states`.

    Parameters
    ----------
    n_states : int > 0
        The number of states

    Returns
    -------
    transition : np.ndarray [shape=(n_states, n_states)]
        `transition[i, j] = 1./n_states`

    Examples
    --------

    >>> librosa.sequence.transition_uniform(3)
    array([[0.333, 0.333, 0.333],
           [0.333, 0.333, 0.333],
           [0.333, 0.333, 0.333]])
    '''

    if not isinstance(n_states, int) or n_states <= 0:
        raise ParameterError('n_states={} must be a positive integer')

    transition = np.empty((n_states, n_states), dtype=np.float)
    transition.fill(1./n_states)
    return transition

Construct a uniform transition matrix over n_states .


def transition_loop(n_states, prob):
    '''Construct a self-loop transition matrix over `n_states`.

    The transition matrix will have the following properties:

        - `transition[i, i] = p` for all i
        - `transition[i, j] = (1 - p) / (n_states - 1)` for all `j != i`

    This type of transition matrix is appropriate when states tend to be
    locally stable, and there is no additional structure between different
    states.  This is primarily useful for de-noising frame-wise predictions.

    Parameters
    ----------
    n_states : int > 1
        The number of states

    prob : float in [0, 1] or iterable, length=n_states
        If a scalar, this is the probability of a self-transition.

        If a vector of length `n_states`, `p[i]` is the probability of state `i`'s self-transition.

    Returns
    -------
    transition : np.ndarray [shape=(n_states, n_states)]
        The transition matrix

    Examples
    --------
    >>> librosa.sequence.transition_loop(3, 0.5)
    array([[0.5 , 0.25, 0.25],
           [0.25, 0.5 , 0.25],
           [0.25, 0.25, 0.5 ]])

    >>> librosa.sequence.transition_loop(3, [0.8, 0.5, 0.25])
    array([[0.8  , 0.1  , 0.1  ],
           [0.25 , 0.5  , 0.25 ],
           [0.375, 0.375, 0.25 ]])
    '''

    if not isinstance(n_states, int) or n_states <= 1:
        raise ParameterError('n_states={} must be a positive integer > 1')

    transition = np.empty((n_states, n_states), dtype=np.float)

    # if it's a float, make it a vector
    prob = np.asarray(prob, dtype=np.float)

    if prob.ndim == 0:
        prob = np.tile(prob, n_states)

    if prob.shape != (n_states,):
        raise ParameterError('prob={} must have length equal to n_states={}'.format(prob, n_states))

    if np.any(prob < 0) or np.any(prob > 1):
        raise ParameterError('prob={} must have values in the range [0, 1]'.format(prob))

    for i, prob_i in enumerate(prob):
        transition[i] = (1. - prob_i) / (n_states - 1)
        transition[i, i] = prob_i

    return transition

Construct a self - loop transition matrix over n_states .


def transition_cycle(n_states, prob):
    '''Construct a cyclic transition matrix over `n_states`.

    The transition matrix will have the following properties:

        - `transition[i, i] = p`
        - `transition[i, i + 1] = (1 - p)`

    This type of transition matrix is appropriate for state spaces
    with cyclical structure, such as metrical position within a bar.
    For example, a song in 4/4 time has state transitions of the form

        1->{1, 2}, 2->{2, 3}, 3->{3, 4}, 4->{4, 1}.

    Parameters
    ----------
    n_states : int > 1
        The number of states

    prob : float in [0, 1] or iterable, length=n_states
        If a scalar, this is the probability of a self-transition.

        If a vector of length `n_states`, `p[i]` is the probability of state
        `i`'s self-transition.

    Returns
    -------
    transition : np.ndarray [shape=(n_states, n_states)]
        The transition matrix

    Examples
    --------
    >>> librosa.sequence.transition_cycle(4, 0.9)
    array([[0.9, 0.1, 0. , 0. ],
           [0. , 0.9, 0.1, 0. ],
           [0. , 0. , 0.9, 0.1],
           [0.1, 0. , 0. , 0.9]])
    '''

    if not isinstance(n_states, int) or n_states <= 1:
        raise ParameterError('n_states={} must be a positive integer > 1')

    transition = np.zeros((n_states, n_states), dtype=np.float)

    # if it's a float, make it a vector
    prob = np.asarray(prob, dtype=np.float)

    if prob.ndim == 0:
        prob = np.tile(prob, n_states)

    if prob.shape != (n_states,):
        raise ParameterError('prob={} must have length equal to n_states={}'.format(prob, n_states))

    if np.any(prob < 0) or np.any(prob > 1):
        raise ParameterError('prob={} must have values in the range [0, 1]'.format(prob))

    for i, prob_i in enumerate(prob):
        transition[i, np.mod(i + 1, n_states)] = 1. - prob_i
        transition[i, i] = prob_i

    return transition

Construct a cyclic transition matrix over n_states .


def transition_local(n_states, width, window='triangle', wrap=False):
    '''Construct a localized transition matrix.

    The transition matrix will have the following properties:

        - `transition[i, j] = 0` if `|i - j| > width`
        - `transition[i, i]` is maximal
        - `transition[i, i - width//2 : i + width//2]` has shape `window`

    This type of transition matrix is appropriate for state spaces
    that discretely approximate continuous variables, such as in fundamental
    frequency estimation.

    Parameters
    ----------
    n_states : int > 1
        The number of states

    width : int >= 1 or iterable
        The maximum number of states to treat as "local".
        If iterable, it should have length equal to `n_states`,
        and specify the width independently for each state.

    window : str, callable, or window specification
        The window function to determine the shape of the "local" distribution.

        Any window specification supported by `filters.get_window` will work here.

        .. note:: Certain windows (e.g., 'hann') are identically 0 at the boundaries,
            so and effectively have `width-2` non-zero values.  You may have to expand
            `width` to get the desired behavior.

    wrap : bool
        If `True`, then state locality `|i - j|` is computed modulo `n_states`.
        If `False` (default), then locality is absolute.

    See Also
    --------
    filters.get_window

    Returns
    -------
    transition : np.ndarray [shape=(n_states, n_states)]
        The transition matrix

    Examples
    --------

    Triangular distributions with and without wrapping

    >>> librosa.sequence.transition_local(5, 3, window='triangle', wrap=False)
    array([[0.667, 0.333, 0.   , 0.   , 0.   ],
           [0.25 , 0.5  , 0.25 , 0.   , 0.   ],
           [0.   , 0.25 , 0.5  , 0.25 , 0.   ],
           [0.   , 0.   , 0.25 , 0.5  , 0.25 ],
           [0.   , 0.   , 0.   , 0.333, 0.667]])

    >>> librosa.sequence.transition_local(5, 3, window='triangle', wrap=True)
    array([[0.5 , 0.25, 0.  , 0.  , 0.25],
           [0.25, 0.5 , 0.25, 0.  , 0.  ],
           [0.  , 0.25, 0.5 , 0.25, 0.  ],
           [0.  , 0.  , 0.25, 0.5 , 0.25],
           [0.25, 0.  , 0.  , 0.25, 0.5 ]])

    Uniform local distributions with variable widths and no wrapping

    >>> librosa.sequence.transition_local(5, [1, 2, 3, 3, 1], window='ones', wrap=False)
    array([[1.   , 0.   , 0.   , 0.   , 0.   ],
           [0.5  , 0.5  , 0.   , 0.   , 0.   ],
           [0.   , 0.333, 0.333, 0.333, 0.   ],
           [0.   , 0.   , 0.333, 0.333, 0.333],
           [0.   , 0.   , 0.   , 0.   , 1.   ]])
    '''

    if not isinstance(n_states, int) or n_states <= 1:
        raise ParameterError('n_states={} must be a positive integer > 1')

    width = np.asarray(width, dtype=int)
    if width.ndim == 0:
        width = np.tile(width, n_states)

    if width.shape != (n_states,):
        raise ParameterError('width={} must have length equal to n_states={}'.format(width, n_states))

    if np.any(width < 1):
        raise ParameterError('width={} must be at least 1')

    transition = np.zeros((n_states, n_states), dtype=np.float)

    # Fill in the widths.  This is inefficient, but simple
    for i, width_i in enumerate(width):
        trans_row = pad_center(get_window(window, width_i, fftbins=False), n_states)
        trans_row = np.roll(trans_row, n_states//2 + i + 1)

        if not wrap:
            # Knock out the off-diagonal-band elements
            trans_row[min(n_states, i + width_i//2 + 1):] = 0
            trans_row[:max(0, i - width_i//2)] = 0

        transition[i] = trans_row

    # Row-normalize
    transition /= transition.sum(axis=1, keepdims=True)

    return transition

Construct a localized transition matrix .


def onset_detect(y=None, sr=22050, onset_envelope=None, hop_length=512,
                 backtrack=False, energy=None,
                 units='frames', **kwargs):
    """Basic onset detector.  Locate note onset events by picking peaks in an
    onset strength envelope.

    The `peak_pick` parameters were chosen by large-scale hyper-parameter
    optimization over the dataset provided by [1]_.

    .. [1] https://github.com/CPJKU/onset_db

    Parameters
    ----------
    y          : np.ndarray [shape=(n,)]
        audio time series

    sr         : number > 0 [scalar]
        sampling rate of `y`

    onset_envelope     : np.ndarray [shape=(m,)]
        (optional) pre-computed onset strength envelope

    hop_length : int > 0 [scalar]
        hop length (in samples)

    units : {'frames', 'samples', 'time'}
        The units to encode detected onset events in.
        By default, 'frames' are used.

    backtrack : bool
        If `True`, detected onset events are backtracked to the nearest
        preceding minimum of `energy`.

        This is primarily useful when using onsets as slice points for segmentation.

    energy : np.ndarray [shape=(m,)] (optional)
        An energy function to use for backtracking detected onset events.
        If none is provided, then `onset_envelope` is used.

    kwargs : additional keyword arguments
        Additional parameters for peak picking.

        See `librosa.util.peak_pick` for details.

    Returns
    -------

    onsets : np.ndarray [shape=(n_onsets,)]
        estimated positions of detected onsets, in whichever units
        are specified.  By default, frame indices.

        .. note::
            If no onset strength could be detected, onset_detect returns
            an empty list.

    Raises
    ------
    ParameterError
        if neither `y` nor `onsets` are provided

        or if `units` is not one of 'frames', 'samples', or 'time'

    See Also
    --------
    onset_strength : compute onset strength per-frame
    onset_backtrack : backtracking onset events
    librosa.util.peak_pick : pick peaks from a time series

    Examples
    --------
    Get onset times from a signal

    >>> y, sr = librosa.load(librosa.util.example_audio_file(),
    ...                      offset=30, duration=2.0)
    >>> onset_frames = librosa.onset.onset_detect(y=y, sr=sr)
    >>> librosa.frames_to_time(onset_frames, sr=sr)
    array([ 0.07 ,  0.395,  0.511,  0.627,  0.766,  0.975,
            1.207,  1.324,  1.44 ,  1.788,  1.881])

    Or use a pre-computed onset envelope

    >>> o_env = librosa.onset.onset_strength(y, sr=sr)
    >>> times = librosa.frames_to_time(np.arange(len(o_env)), sr=sr)
    >>> onset_frames = librosa.onset.onset_detect(onset_envelope=o_env, sr=sr)

    >>> import matplotlib.pyplot as plt
    >>> D = np.abs(librosa.stft(y))
    >>> plt.figure()
    >>> ax1 = plt.subplot(2, 1, 1)
    >>> librosa.display.specshow(librosa.amplitude_to_db(D, ref=np.max),
    ...                          x_axis='time', y_axis='log')
    >>> plt.title('Power spectrogram')
    >>> plt.subplot(2, 1, 2, sharex=ax1)
    >>> plt.plot(times, o_env, label='Onset strength')
    >>> plt.vlines(times[onset_frames], 0, o_env.max(), color='r', alpha=0.9,
    ...            linestyle='--', label='Onsets')
    >>> plt.axis('tight')
    >>> plt.legend(frameon=True, framealpha=0.75)

    """

    # First, get the frame->beat strength profile if we don't already have one
    if onset_envelope is None:
        if y is None:
            raise ParameterError('y or onset_envelope must be provided')

        onset_envelope = onset_strength(y=y, sr=sr, hop_length=hop_length)

    # Shift onset envelope up to be non-negative
    # (a common normalization step to make the threshold more consistent)
    onset_envelope -= onset_envelope.min()

    # Do we have any onsets to grab?
    if not onset_envelope.any():
        return np.array([], dtype=np.int)

    # Normalize onset strength function to [0, 1] range
    onset_envelope /= onset_envelope.max()

    # These parameter settings found by large-scale search
    kwargs.setdefault('pre_max', 0.03*sr//hop_length)       # 30ms
    kwargs.setdefault('post_max', 0.00*sr//hop_length + 1)  # 0ms
    kwargs.setdefault('pre_avg', 0.10*sr//hop_length)       # 100ms
    kwargs.setdefault('post_avg', 0.10*sr//hop_length + 1)  # 100ms
    kwargs.setdefault('wait', 0.03*sr//hop_length)          # 30ms
    kwargs.setdefault('delta', 0.07)

    # Peak pick the onset envelope
    onsets = util.peak_pick(onset_envelope, **kwargs)

    # Optionally backtrack the events
    if backtrack:
        if energy is None:
            energy = onset_envelope

        onsets = onset_backtrack(onsets, energy)

    if units == 'frames':
        pass
    elif units == 'samples':
        onsets = core.frames_to_samples(onsets, hop_length=hop_length)
    elif units == 'time':
        onsets = core.frames_to_time(onsets, hop_length=hop_length, sr=sr)
    else:
        raise ParameterError('Invalid unit type: {}'.format(units))

    return onsets

def
onset_detect
(
y
=
,
sr
=
22050
,
onset_envelope
=
,
hop_length
=
512
,
backtrack
=
False
,
energy
=
,
units
=
'frames'
,
**
kwargs
)
:
if
onset_envelope
is
:
if
y
is
:
raise
ParameterError
(
'y or onset_envelope must be provided'
)
onset_envelope
=
onset_strength
(
y
=
y
,
sr
=
sr
,
hop_length
=
hop_length
)
onset_envelope
-=
onset_envelope
.
min
(
)
if
not
onset_envelope
.
any
(
)
:
return
np
.
array
(
[
]
,
dtype
=
np
.
int
)
onset_envelope
/=
onset_envelope
.
max
(
)
kwargs
.
setdefault
(
'pre_max'
,
0.03
*
sr
//
hop_length
)
kwargs
.
setdefault
(
'post_max'
,
0.00
*
sr
//
hop_length
+
1
)
kwargs
.
setdefault
(
'pre_avg'
,
0.10
*
sr
//
hop_length
)
kwargs
.
setdefault
(
'post_avg'
,
0.10
*
sr
//
hop_length
+
1
)
kwargs
.
setdefault
(
'wait'
,
0.03
*
sr
//
hop_length
)
kwargs
.
setdefault
(
'delta'
,
0.07
)
onsets
=
util
.
peak_pick
(
onset_envelope
,
**
kwargs
)
if
backtrack
:
if
energy
is
:
energy
=
onset_envelope
onsets
=
onset_backtrack
(
onsets
,
energy
)
if
units
==
'frames'
:
pass
elif
units
==
'samples'
:
onsets
=
core
.
frames_to_samples
(
onsets
,
hop_length
=
hop_length
)
elif
units
==
'time'
:
onsets
=
core
.
frames_to_time
(
onsets
,
hop_length
=
hop_length
,
sr
=
sr
)
else
:
raise
ParameterError
(
'Invalid unit type: {}'
.
format
(
units
)
)
return
onsets

Basic onset detector . Locate note onset events by picking peaks in an onset strength envelope .


def onset_strength(y=None, sr=22050, S=None, lag=1, max_size=1,
                   ref=None,
                   detrend=False, center=True,
                   feature=None, aggregate=None,
                   centering=None,
                   **kwargs):
    """Compute a spectral flux onset strength envelope.

    Onset strength at time `t` is determined by:

    `mean_f max(0, S[f, t] - ref[f, t - lag])`

    where `ref` is `S` after local max filtering along the frequency
    axis [1]_.

    By default, if a time series `y` is provided, S will be the
    log-power Mel spectrogram.

    .. [1] Böck, Sebastian, and Gerhard Widmer.
           "Maximum filter vibrato suppression for onset detection."
           16th International Conference on Digital Audio Effects,
           Maynooth, Ireland. 2013.

    Parameters
    ----------
    y        : np.ndarray [shape=(n,)]
        audio time-series

    sr       : number > 0 [scalar]
        sampling rate of `y`

    S        : np.ndarray [shape=(d, m)]
        pre-computed (log-power) spectrogram

    lag      : int > 0
        time lag for computing differences

    max_size : int > 0
        size (in frequency bins) of the local max filter.
        set to `1` to disable filtering.

    ref : None or np.ndarray [shape=(d, m)]
        An optional pre-computed reference spectrum, of the same shape as `S`.
        If not provided, it will be computed from `S`.
        If provided, it will override any local max filtering governed by `max_size`.

    detrend : bool [scalar]
        Filter the onset strength to remove the DC component

    center : bool [scalar]
        Shift the onset function by `n_fft / (2 * hop_length)` frames

    feature : function
        Function for computing time-series features, eg, scaled spectrograms.
        By default, uses `librosa.feature.melspectrogram` with `fmax=11025.0`

    aggregate : function
        Aggregation function to use when combining onsets
        at different frequency bins.

        Default: `np.mean`

    kwargs : additional keyword arguments
        Additional parameters to `feature()`, if `S` is not provided.

    Returns
    -------
    onset_envelope   : np.ndarray [shape=(m,)]
        vector containing the onset strength envelope

    Raises
    ------
    ParameterError
        if neither `(y, sr)` nor `S` are provided

        or if `lag` or `max_size` are not positive integers

    See Also
    --------
    onset_detect
    onset_strength_multi

    Examples
    --------
    First, load some audio and plot the spectrogram

    >>> import matplotlib.pyplot as plt
    >>> y, sr = librosa.load(librosa.util.example_audio_file(),
    ...                      duration=10.0)
    >>> D = np.abs(librosa.stft(y))
    >>> times = librosa.frames_to_time(np.arange(D.shape[1]))
    >>> plt.figure()
    >>> ax1 = plt.subplot(2, 1, 1)
    >>> librosa.display.specshow(librosa.amplitude_to_db(D, ref=np.max),
    ...                          y_axis='log', x_axis='time')
    >>> plt.title('Power spectrogram')

    Construct a standard onset function

    >>> onset_env = librosa.onset.onset_strength(y=y, sr=sr)
    >>> plt.subplot(2, 1, 2, sharex=ax1)
    >>> plt.plot(times, 2 + onset_env / onset_env.max(), alpha=0.8,
    ...          label='Mean (mel)')

    Median aggregation, and custom mel options

    >>> onset_env = librosa.onset.onset_strength(y=y, sr=sr,
    ...                                          aggregate=np.median,
    ...                                          fmax=8000, n_mels=256)
    >>> plt.plot(times, 1 + onset_env / onset_env.max(), alpha=0.8,
    ...          label='Median (custom mel)')

    Constant-Q spectrogram instead of Mel

    >>> onset_env = librosa.onset.onset_strength(y=y, sr=sr,
    ...                                          feature=librosa.cqt)
    >>> plt.plot(times, onset_env / onset_env.max(), alpha=0.8,
    ...          label='Mean (CQT)')
    >>> plt.legend(frameon=True, framealpha=0.75)
    >>> plt.ylabel('Normalized strength')
    >>> plt.yticks([])
    >>> plt.axis('tight')
    >>> plt.tight_layout()

    """

    if aggregate is False:
        raise ParameterError('aggregate={} cannot be False when computing full-spectrum onset strength.')

    odf_all = onset_strength_multi(y=y,
                                   sr=sr,
                                   S=S,
                                   lag=lag,
                                   max_size=max_size,
                                   ref=ref,
                                   detrend=detrend,
                                   center=center,
                                   feature=feature,
                                   aggregate=aggregate,
                                   channels=None,
                                   **kwargs)

    return odf_all[0]

Compute a spectral flux onset strength envelope .


def onset_backtrack(events, energy):
    '''Backtrack detected onset events to the nearest preceding local
    minimum of an energy function.

    This function can be used to roll back the timing of detected onsets
    from a detected peak amplitude to the preceding minimum.

    This is most useful when using onsets to determine slice points for
    segmentation, as described by [1]_.

    .. [1] Jehan, Tristan.
           "Creating music by listening"
           Doctoral dissertation
           Massachusetts Institute of Technology, 2005.

    Parameters
    ----------
    events : np.ndarray, dtype=int
        List of onset event frame indices, as computed by `onset_detect`

    energy : np.ndarray, shape=(m,)
        An energy function

    Returns
    -------
    events_backtracked : np.ndarray, shape=events.shape
        The input events matched to nearest preceding minima of `energy`.

    Examples
    --------
    >>> y, sr = librosa.load(librosa.util.example_audio_file(),
    ...                      offset=30, duration=2.0)
    >>> oenv = librosa.onset.onset_strength(y=y, sr=sr)
    >>> # Detect events without backtracking
    >>> onset_raw = librosa.onset.onset_detect(onset_envelope=oenv,
    ...                                        backtrack=False)
    >>> # Backtrack the events using the onset envelope
    >>> onset_bt = librosa.onset.onset_backtrack(onset_raw, oenv)
    >>> # Backtrack the events using the RMS values
    >>> rms = librosa.feature.rms(S=np.abs(librosa.stft(y=y)))
    >>> onset_bt_rms = librosa.onset.onset_backtrack(onset_raw, rms[0])

    >>> # Plot the results
    >>> import matplotlib.pyplot as plt
    >>> plt.figure()
    >>> plt.subplot(2,1,1)
    >>> plt.plot(oenv, label='Onset strength')
    >>> plt.vlines(onset_raw, 0, oenv.max(), label='Raw onsets')
    >>> plt.vlines(onset_bt, 0, oenv.max(), label='Backtracked', color='r')
    >>> plt.legend(frameon=True, framealpha=0.75)
    >>> plt.subplot(2,1,2)
    >>> plt.plot(rms[0], label='RMS')
    >>> plt.vlines(onset_bt_rms, 0, rms.max(), label='Backtracked (RMS)', color='r')
    >>> plt.legend(frameon=True, framealpha=0.75)
    '''

    # Find points where energy is non-increasing
    # all points:  energy[i] <= energy[i-1]
    # tail points: energy[i] < energy[i+1]
    minima = np.flatnonzero((energy[1:-1] <= energy[:-2]) &
                            (energy[1:-1] < energy[2:]))

    # Pad on a 0, just in case we have onsets with no preceding minimum
    # Shift by one to account for slicing in minima detection
    minima = util.fix_frames(1 + minima, x_min=0)

    # Only match going left from the detected events
    return minima[util.match_events(events, minima, right=False)]

def onset_backtrack ( events , energy ) : minima = np . flatnonzero ( ( energy [ 1 : - 1 ] <= energy [ : - 2 ] ) & ( energy [ 1 : - 1 ] < energy [ 2 : ] ) ) minima = util . fix_frames ( 1 + minima , x_min = 0 ) return minima [ util . match_events ( events , minima , right = False ) ]

Backtrack detected onset events to the nearest preceding local minimum of an energy function .


def onset_strength_multi(y=None, sr=22050, S=None, lag=1, max_size=1,
                         ref=None, detrend=False, center=True, feature=None,
                         aggregate=None, channels=None, **kwargs):
    """Compute a spectral flux onset strength envelope across multiple channels.

    Onset strength for channel `i` at time `t` is determined by:

    `mean_{f in channels[i]} max(0, S[f, t+1] - S[f, t])`

    Parameters
    ----------
    y        : np.ndarray [shape=(n,)]
        audio time-series

    sr       : number > 0 [scalar]
        sampling rate of `y`

    S        : np.ndarray [shape=(d, m)]
        pre-computed (log-power) spectrogram

    lag      : int > 0
        time lag for computing differences

    max_size : int > 0
        size (in frequency bins) of the local max filter.
        set to `1` to disable filtering.

    ref : None or np.ndarray [shape=(d, m)]
        An optional pre-computed reference spectrum, of the same shape as `S`.
        If not provided, it will be computed from `S`.
        If provided, it will override any local max filtering governed by `max_size`.

    detrend : bool [scalar]
        Filter the onset strength to remove the DC component

    center : bool [scalar]
        Shift the onset function by `n_fft / (2 * hop_length)` frames

    feature : function
        Function for computing time-series features, eg, scaled spectrograms.
        By default, uses `librosa.feature.melspectrogram` with `fmax=11025.0`

    aggregate : function or False
        Aggregation function to use when combining onsets
        at different frequency bins.

        If `False`, then no aggregation is performed.

        Default: `np.mean`

    channels : list or None
        Array of channel boundaries or slice objects.
        If `None`, then a single channel is generated to span all bands.

    kwargs : additional keyword arguments
        Additional parameters to `feature()`, if `S` is not provided.

    Returns
    -------
    onset_envelope   : np.ndarray [shape=(n_channels, m)]
        array containing the onset strength envelope for each specified channel

    Raises
    ------
    ParameterError
        if neither `(y, sr)` nor `S` are provided

    See Also
    --------
    onset_strength

    Notes
    -----
    This function caches at level 30.

    Examples
    --------
    First, load some audio and plot the spectrogram

    >>> import matplotlib.pyplot as plt
    >>> y, sr = librosa.load(librosa.util.example_audio_file(),
    ...                      duration=10.0)
    >>> D = np.abs(librosa.stft(y))
    >>> plt.figure()
    >>> plt.subplot(2, 1, 1)
    >>> librosa.display.specshow(librosa.amplitude_to_db(D, ref=np.max),
    ...                          y_axis='log')
    >>> plt.title('Power spectrogram')

    Construct a standard onset function over four sub-bands

    >>> onset_subbands = librosa.onset.onset_strength_multi(y=y, sr=sr,
    ...                                                     channels=[0, 32, 64, 96, 128])
    >>> plt.subplot(2, 1, 2)
    >>> librosa.display.specshow(onset_subbands, x_axis='time')
    >>> plt.ylabel('Sub-bands')
    >>> plt.title('Sub-band onset strength')

    """

    if feature is None:
        feature = melspectrogram
        kwargs.setdefault('fmax', 11025.0)

    if aggregate is None:
        aggregate = np.mean

    if lag < 1 or not isinstance(lag, int):
        raise ParameterError('lag must be a positive integer')

    if max_size < 1 or not isinstance(max_size, int):
        raise ParameterError('max_size must be a positive integer')

    # First, compute mel spectrogram
    if S is None:
        S = np.abs(feature(y=y, sr=sr, **kwargs))

        # Convert to dBs
        S = core.power_to_db(S)

    # Retrieve the n_fft and hop_length,
    # or default values for onsets if not provided
    n_fft = kwargs.get('n_fft', 2048)
    hop_length = kwargs.get('hop_length', 512)

    # Ensure that S is at least 2-d
    S = np.atleast_2d(S)

    # Compute the reference spectrogram.
    # Efficiency hack: skip filtering step and pass by reference
    # if max_size will produce a no-op.
    if ref is None:
        if max_size == 1:
            ref = S
        else:
            ref = scipy.ndimage.maximum_filter1d(S, max_size, axis=0)
    elif ref.shape != S.shape:
        raise ParameterError('Reference spectrum shape {} must match input spectrum {}'.format(ref.shape, S.shape))

    # Compute difference to the reference, spaced by lag
    onset_env = S[:, lag:] - ref[:, :-lag]

    # Discard negatives (decreasing amplitude)
    onset_env = np.maximum(0.0, onset_env)

    # Aggregate within channels
    pad = True
    if channels is None:
        channels = [slice(None)]
    else:
        pad = False

    if aggregate:
        onset_env = util.sync(onset_env, channels,
                              aggregate=aggregate,
                              pad=pad, axis=0)

    # compensate for lag
    pad_width = lag
    if center:
        # Counter-act framing effects. Shift the onsets by n_fft / hop_length
        pad_width += n_fft // (2 * hop_length)

    onset_env = np.pad(onset_env, ([0, 0], [int(pad_width), 0]),
                       mode='constant')

    # remove the DC component
    if detrend:
        onset_env = scipy.signal.lfilter([1.0, -1.0], [1.0, -0.99],
                                         onset_env, axis=-1)

    # Trim to match the input duration
    if center:
        onset_env = onset_env[:, :S.shape[1]]

    return onset_env

Compute a spectral flux onset strength envelope across multiple channels .


def times_csv(path, times, annotations=None, delimiter=',', fmt='%0.3f'):
    r"""Save time steps as in CSV format.  This can be used to store the output
    of a beat-tracker or segmentation algorithm.

    If only `times` are provided, the file will contain each value
    of `times` on a row::

        times[0]\n
        times[1]\n
        times[2]\n
        ...

    If `annotations` are also provided, the file will contain
    delimiter-separated values::

        times[0],annotations[0]\n
        times[1],annotations[1]\n
        times[2],annotations[2]\n
        ...

    Parameters
    ----------
    path : string
        path to save the output CSV file

    times : list-like of floats
        list of frame numbers for beat events

    annotations : None or list-like
        optional annotations for each time step

    delimiter : str
        character to separate fields

    fmt : str
        format-string for rendering time

    Raises
    ------
    ParameterError
        if `annotations` is not `None` and length does not
        match `times`

    Examples
    --------
    Write beat-tracker time to CSV

    >>> y, sr = librosa.load(librosa.util.example_audio_file())
    >>> tempo, beats = librosa.beat.beat_track(y, sr=sr, units='time')
    >>> librosa.output.times_csv('beat_times.csv', beats)
    """

    if annotations is not None and len(annotations) != len(times):
        raise ParameterError('len(annotations) != len(times)')

    with open(path, 'w') as output_file:
        writer = csv.writer(output_file, delimiter=delimiter)

        if annotations is None:
            for t in times:
                writer.writerow([fmt % t])
        else:
            for t, lab in zip(times, annotations):
                writer.writerow([(fmt % t), lab])

def
times_csv
(
path
,
times
,
annotations
=
,
delimiter
=
','
,
fmt
=
'%0.3f'
)
:
r
if
annotations
is
not
and
len
(
annotations
)
!=
len
(
times
)
:
raise
ParameterError
(
'len(annotations) != len(times)'
)
with
open
(
path
,
'w'
)
as
output_file
:
writer
=
csv
.
writer
(
output_file
,
delimiter
=
delimiter
)
if
annotations
is
:
for
t
in
times
:
writer
.
writerow
(
[
fmt
%
t
]
)
else
:
for
t
,
lab
in
zip
(
times
,
annotations
)
:
writer
.
writerow
(
[
(
fmt
%
t
)
,
lab
]
)

r Save time steps as in CSV format . This can be used to store the output of a beat - tracker or segmentation algorithm .


def write_wav(path, y, sr, norm=False):
    """Output a time series as a .wav file

    Note: only mono or stereo, floating-point data is supported.
        For more advanced and flexible output options, refer to
        `soundfile`.

    Parameters
    ----------
    path : str
        path to save the output wav file

    y : np.ndarray [shape=(n,) or (2,n), dtype=np.float]
        audio time series (mono or stereo).

        Note that only floating-point values are supported.

    sr : int > 0 [scalar]
        sampling rate of `y`

    norm : boolean [scalar]
        enable amplitude normalization.
        For floating point `y`, scale the data to the range [-1, +1].

    Examples
    --------
    Trim a signal to 5 seconds and save it back

    >>> y, sr = librosa.load(librosa.util.example_audio_file(),
    ...                      duration=5.0)
    >>> librosa.output.write_wav('file_trim_5s.wav', y, sr)

    See Also
    --------
    soundfile.write
    """

    # Validate the buffer.  Stereo is okay here.
    util.valid_audio(y, mono=False)

    # normalize
    if norm and np.issubdtype(y.dtype, np.floating):
        wav = util.normalize(y, norm=np.inf, axis=None)
    else:
        wav = y

    # Check for stereo
    if wav.ndim > 1 and wav.shape[0] == 2:
        wav = wav.T

    # Save
    scipy.io.wavfile.write(path, sr, wav)

def write_wav ( path , y , sr , norm = False ) : util . valid_audio ( y , mono = False ) if norm and np . issubdtype ( y . dtype , np . floating ) : wav = util . normalize ( y , norm = np . inf , axis = None ) else : wav = y if wav . ndim > 1 and wav . shape [ 0 ] == 2 : wav = wav . T scipy . io . wavfile . write ( path , sr , wav )


def cmap(data, robust=True, cmap_seq='magma', cmap_bool='gray_r', cmap_div='coolwarm'):
    '''Get a default colormap from the given data.

    If the data is boolean, use a black and white colormap.

    If the data has both positive and negative values,
    use a diverging colormap.

    Otherwise, use a sequential colormap.

    Parameters
    ----------
    data : np.ndarray
        Input data

    robust : bool
        If True, discard the top and bottom 2% of data when calculating
        range.

    cmap_seq : str
        The sequential colormap name

    cmap_bool : str
        The boolean colormap name

    cmap_div : str
        The diverging colormap name

    Returns
    -------
    cmap : matplotlib.colors.Colormap
        The colormap to use for `data`

    See Also
    --------
    matplotlib.pyplot.colormaps
    '''

    data = np.atleast_1d(data)

    if data.dtype == 'bool':
        return get_cmap(cmap_bool)

    data = data[np.isfinite(data)]

    if robust:
        min_p, max_p = 2, 98
    else:
        min_p, max_p = 0, 100

    max_val = np.percentile(data, max_p)
    min_val = np.percentile(data, min_p)

    if min_val >= 0 or max_val <= 0:
        return get_cmap(cmap_seq)

    return get_cmap(cmap_div)

def cmap ( data , robust = True , cmap_seq = 'magma' , cmap_bool = 'gray_r' , cmap_div = 'coolwarm' ) : data = np . atleast_1d ( data ) if data . dtype == 'bool' : return get_cmap ( cmap_bool ) data = data [ np . isfinite ( data ) ] if robust : min_p , max_p = 2 , 98 else : min_p , max_p = 0 , 100 max_val = np . percentile ( data , max_p ) min_val = np . percentile ( data , min_p ) if min_val >= 0 or max_val <= 0 : return get_cmap ( cmap_seq ) return get_cmap ( cmap_div )

Get a default colormap from the given data .


def waveplot(y, sr=22050, max_points=5e4, x_axis='time', offset=0.0,
             max_sr=1000, ax=None, **kwargs):
    '''Plot the amplitude envelope of a waveform.

    If `y` is monophonic, a filled curve is drawn between `[-abs(y), abs(y)]`.

    If `y` is stereo, the curve is drawn between `[-abs(y[1]), abs(y[0])]`,
    so that the left and right channels are drawn above and below the axis,
    respectively.

    Long signals (`duration >= max_points`) are down-sampled to at
    most `max_sr` before plotting.

    Parameters
    ----------
    y : np.ndarray [shape=(n,) or (2,n)]
        audio time series (mono or stereo)

    sr : number > 0 [scalar]
        sampling rate of `y`

    max_points : postive number or None
        Maximum number of time-points to plot: if `max_points` exceeds
        the duration of `y`, then `y` is downsampled.

        If `None`, no downsampling is performed.

    x_axis : str {'time', 'off', 'none'} or None
        If 'time', the x-axis is given time tick-marks.

    ax : matplotlib.axes.Axes or None
        Axes to plot on instead of the default `plt.gca()`.

    offset : float
        Horizontal offset (in seconds) to start the waveform plot

    max_sr : number > 0 [scalar]
        Maximum sampling rate for the visualization

    kwargs
        Additional keyword arguments to `matplotlib.pyplot.fill_between`

    Returns
    -------
    pc : matplotlib.collections.PolyCollection
        The PolyCollection created by `fill_between`.

    See also
    --------
    librosa.core.resample
    matplotlib.pyplot.fill_between

    Examples
    --------
    Plot a monophonic waveform

    >>> import matplotlib.pyplot as plt
    >>> y, sr = librosa.load(librosa.util.example_audio_file(), duration=10)
    >>> plt.figure()
    >>> plt.subplot(3, 1, 1)
    >>> librosa.display.waveplot(y, sr=sr)
    >>> plt.title('Monophonic')

    Or a stereo waveform

    >>> y, sr = librosa.load(librosa.util.example_audio_file(),
    ...                      mono=False, duration=10)
    >>> plt.subplot(3, 1, 2)
    >>> librosa.display.waveplot(y, sr=sr)
    >>> plt.title('Stereo')

    Or harmonic and percussive components with transparency

    >>> y, sr = librosa.load(librosa.util.example_audio_file(), duration=10)
    >>> y_harm, y_perc = librosa.effects.hpss(y)
    >>> plt.subplot(3, 1, 3)
    >>> librosa.display.waveplot(y_harm, sr=sr, alpha=0.25)
    >>> librosa.display.waveplot(y_perc, sr=sr, color='r', alpha=0.5)
    >>> plt.title('Harmonic + Percussive')
    >>> plt.tight_layout()
    '''

    util.valid_audio(y, mono=False)

    if not (isinstance(max_sr, int) and max_sr > 0):
        raise ParameterError('max_sr must be a non-negative integer')

    target_sr = sr
    hop_length = 1

    if max_points is not None:
        if max_points <= 0:
            raise ParameterError('max_points must be strictly positive')

        if max_points < y.shape[-1]:
            target_sr = min(max_sr, (sr * y.shape[-1]) // max_points)

        hop_length = sr // target_sr

        if y.ndim == 1:
            y = __envelope(y, hop_length)
        else:
            y = np.vstack([__envelope(_, hop_length) for _ in y])

    if y.ndim > 1:
        y_top = y[0]
        y_bottom = -y[1]
    else:
        y_top = y
        y_bottom = -y

    axes = __check_axes(ax)

    kwargs.setdefault('color', next(axes._get_lines.prop_cycler)['color'])

    locs = offset + core.frames_to_time(np.arange(len(y_top)),
                                        sr=sr,
                                        hop_length=hop_length)

    out = axes.fill_between(locs, y_bottom, y_top, **kwargs)

    axes.set_xlim([locs.min(), locs.max()])
    if x_axis == 'time':
        axes.xaxis.set_major_formatter(TimeFormatter(lag=False))
        axes.xaxis.set_label_text('Time')
    elif x_axis is None or x_axis in ['off', 'none']:
        axes.set_xticks([])
    else:
        raise ParameterError('Unknown x_axis value: {}'.format(x_axis))

    return out

def
waveplot
(
y
,
sr
=
22050
,
max_points
=
5e4
,
x_axis
=
'time'
,
offset
=
0.0
,
max_sr
=
1000
,
ax
=
,
**
kwargs
)
:
util
.
valid_audio
(
y
,
mono
=
False
)
if
not
(
isinstance
(
max_sr
,
int
)
and
max_sr
>
0
)
:
raise
ParameterError
(
'max_sr must be a non-negative integer'
)
target_sr
=
sr
hop_length
=
1
if
max_points
is
not
:
if
max_points
<=
0
:
raise
ParameterError
(
'max_points must be strictly positive'
)
if
max_points
<
y
.
shape
[
1
]
:
target_sr
=
min
(
max_sr
,
(
sr
*
y
.
shape
[
1
]
)
//
max_points
)
hop_length
=
sr
//
target_sr
if
y
.
ndim
==
1
:
y
=
__envelope
(
y
,
hop_length
)
else
:
y
=
np
.
vstack
(
[
__envelope
(
_
,
hop_length
)
for
_
in
y
]
)
if
y
.
ndim
>
1
:
y_top
=
y
[
0
]
y_bottom
=
y
[
1
]
else
:
y_top
=
y
y_bottom
=
y
axes
=
__check_axes
(
ax
)
kwargs
.
setdefault
(
'color'
,
next
(
axes
.
_get_lines
.
prop_cycler
)
[
'color'
]
)
locs
=
offset
+
core
.
frames_to_time
(
np
.
arange
(
len
(
y_top
)
)
,
sr
=
sr
,
hop_length
=
hop_length
)
out
=
axes
.
fill_between
(
locs
,
y_bottom
,
y_top
,
**
kwargs
)
axes
.
set_xlim
(
[
locs
.
min
(
)
,
locs
.
max
(
)
]
)
if
x_axis
==
'time'
:
axes
.
xaxis
.
set_major_formatter
(
TimeFormatter
(
lag
=
False
)
)
axes
.
xaxis
.
set_label_text
(
'Time'
)
elif
x_axis
is
or
x_axis
in
[
'off'
,
'none'
]
:
axes
.
set_xticks
(
[
]
)
else
:
raise
ParameterError
(
'Unknown x_axis value: {}'
.
format
(
x_axis
)
)
return
out

Plot the amplitude envelope of a waveform .


def __set_current_image(ax, img):
    '''Helper to set the current image in pyplot mode.

    If the provided `ax` is not `None`, then we assume that the user is using the object API.
    In this case, the pyplot current image is not set.
    '''

    if ax is None:
        import matplotlib.pyplot as plt
        plt.sci(img)

def __set_current_image ( ax , img ) : if ax is None : import matplotlib . pyplot as plt plt . sci ( img )

Helper to set the current image in pyplot mode .


def __mesh_coords(ax_type, coords, n, **kwargs):
    '''Compute axis coordinates'''

    if coords is not None:
        if len(coords) < n:
            raise ParameterError('Coordinate shape mismatch: '
                                 '{}<{}'.format(len(coords), n))
        return coords

    coord_map = {'linear': __coord_fft_hz,
                 'hz': __coord_fft_hz,
                 'log': __coord_fft_hz,
                 'mel': __coord_mel_hz,
                 'cqt': __coord_cqt_hz,
                 'cqt_hz': __coord_cqt_hz,
                 'cqt_note': __coord_cqt_hz,
                 'chroma': __coord_chroma,
                 'time': __coord_time,
                 's': __coord_time,
                 'ms': __coord_time,
                 'lag': __coord_time,
                 'lag_s': __coord_time,
                 'lag_ms': __coord_time,
                 'tonnetz': __coord_n,
                 'off': __coord_n,
                 'tempo': __coord_tempo,
                 'frames': __coord_n,
                 None: __coord_n}

    if ax_type not in coord_map:
        raise ParameterError('Unknown axis type: {}'.format(ax_type))
    return coord_map[ax_type](n, **kwargs)

def
__mesh_coords
(
ax_type
,
coords
,
n
,
**
kwargs
)
:
if
coords
is
not
:
if
len
(
coords
)
<
n
:
raise
ParameterError
(
'Coordinate shape mismatch: '
'{}<{}'
.
format
(
len
(
coords
)
,
n
)
)
return
coords
coord_map
=
{
'linear'
:
__coord_fft_hz
,
'hz'
:
__coord_fft_hz
,
'log'
:
__coord_fft_hz
,
'mel'
:
__coord_mel_hz
,
'cqt'
:
__coord_cqt_hz
,
'cqt_hz'
:
__coord_cqt_hz
,
'cqt_note'
:
__coord_cqt_hz
,
'chroma'
:
__coord_chroma
,
'time'
:
__coord_time
,
's'
:
__coord_time
,
'ms'
:
__coord_time
,
'lag'
:
__coord_time
,
'lag_s'
:
__coord_time
,
'lag_ms'
:
__coord_time
,
'tonnetz'
:
__coord_n
,
'off'
:
__coord_n
,
'tempo'
:
__coord_tempo
,
'frames'
:
__coord_n
,
:
__coord_n
}
if
ax_type
not
in
coord_map
:
raise
ParameterError
(
'Unknown axis type: {}'
.
format
(
ax_type
)
)
return
coord_map
[
ax_type
]
(
n
,
**
kwargs
)


def __check_axes(axes):
    '''Check if "axes" is an instance of an axis object. If not, use `gca`.'''
    if axes is None:
        import matplotlib.pyplot as plt
        axes = plt.gca()
    elif not isinstance(axes, Axes):
        raise ValueError("`axes` must be an instance of matplotlib.axes.Axes. "
                         "Found type(axes)={}".format(type(axes)))
    return axes

Check if axes is an instance of an axis object . If not use gca .


def __scale_axes(axes, ax_type, which):
    '''Set the axis scaling'''

    kwargs = dict()
    if which == 'x':
        thresh = 'linthreshx'
        base = 'basex'
        scale = 'linscalex'
        scaler = axes.set_xscale
        limit = axes.set_xlim
    else:
        thresh = 'linthreshy'
        base = 'basey'
        scale = 'linscaley'
        scaler = axes.set_yscale
        limit = axes.set_ylim

    # Map ticker scales
    if ax_type == 'mel':
        mode = 'symlog'
        kwargs[thresh] = 1000.0
        kwargs[base] = 2

    elif ax_type == 'log':
        mode = 'symlog'
        kwargs[base] = 2
        kwargs[thresh] = core.note_to_hz('C2')
        kwargs[scale] = 0.5

    elif ax_type in ['cqt', 'cqt_hz', 'cqt_note']:
        mode = 'log'
        kwargs[base] = 2

    elif ax_type == 'tempo':
        mode = 'log'
        kwargs[base] = 2
        limit(16, 480)
    else:
        return

    scaler(mode, **kwargs)

def __scale_axes ( axes , ax_type , which ) : kwargs = dict ( ) if which == 'x' : thresh = 'linthreshx' base = 'basex' scale = 'linscalex' scaler = axes . set_xscale limit = axes . set_xlim else : thresh = 'linthreshy' base = 'basey' scale = 'linscaley' scaler = axes . set_yscale limit = axes . set_ylim if ax_type == 'mel' : mode = 'symlog' kwargs [ thresh ] = 1000.0 kwargs [ base ] = 2 elif ax_type == 'log' : mode = 'symlog' kwargs [ base ] = 2 kwargs [ thresh ] = core . note_to_hz ( 'C2' ) kwargs [ scale ] = 0.5 elif ax_type in [ 'cqt' , 'cqt_hz' , 'cqt_note' ] : mode = 'log' kwargs [ base ] = 2 elif ax_type == 'tempo' : mode = 'log' kwargs [ base ] = 2 limit ( 16 , 480 ) else : return scaler ( mode , ** kwargs )


def __coord_fft_hz(n, sr=22050, **_kwargs):
    '''Get the frequencies for FFT bins'''
    n_fft = 2 * (n - 1)
    # The following code centers the FFT bins at their frequencies
    # and clips to the non-negative frequency range [0, nyquist]
    basis = core.fft_frequencies(sr=sr, n_fft=n_fft)
    fmax = basis[-1]
    basis -= 0.5 * (basis[1] - basis[0])
    basis = np.append(np.maximum(0, basis), [fmax])
    return basis

def __coord_fft_hz ( n , sr = 22050 , ** _kwargs ) : n_fft = 2 * ( n - 1 ) basis = core . fft_frequencies ( sr = sr , n_fft = n_fft ) fmax = basis [ - 1 ] basis -= 0.5 * ( basis [ 1 ] - basis [ 0 ] ) basis = np . append ( np . maximum ( 0 , basis ) , [ fmax ] ) return basis


def __coord_mel_hz(n, fmin=0, fmax=11025.0, **_kwargs):
    '''Get the frequencies for Mel bins'''

    if fmin is None:
        fmin = 0
    if fmax is None:
        fmax = 11025.0

    basis = core.mel_frequencies(n, fmin=fmin, fmax=fmax)
    basis[1:] -= 0.5 * np.diff(basis)
    basis = np.append(np.maximum(0, basis), [fmax])
    return basis

def __coord_mel_hz ( n , fmin = 0 , fmax = 11025.0 , ** _kwargs ) : if fmin is None : fmin = 0 if fmax is None : fmax = 11025.0 basis = core . mel_frequencies ( n , fmin = fmin , fmax = fmax ) basis [ 1 : ] -= 0.5 * np . diff ( basis ) basis = np . append ( np . maximum ( 0 , basis ) , [ fmax ] ) return basis


def __coord_cqt_hz(n, fmin=None, bins_per_octave=12, **_kwargs):
    '''Get CQT bin frequencies'''
    if fmin is None:
        fmin = core.note_to_hz('C1')

    # we drop by half a bin so that CQT bins are centered vertically
    return core.cqt_frequencies(n+1,
                                fmin=fmin / 2.0**(0.5/bins_per_octave),
                                bins_per_octave=bins_per_octave)

def __coord_cqt_hz ( n , fmin = None , bins_per_octave = 12 , ** _kwargs ) : if fmin is None : fmin = core . note_to_hz ( 'C1' ) return core . cqt_frequencies ( n + 1 , fmin = fmin / 2.0 ** ( 0.5 / bins_per_octave ) , bins_per_octave = bins_per_octave )


def __coord_chroma(n, bins_per_octave=12, **_kwargs):
    '''Get chroma bin numbers'''
    return np.linspace(0, (12.0 * n) / bins_per_octave, num=n+1, endpoint=True)

def __coord_chroma ( n , bins_per_octave = 12 , ** _kwargs ) : return np . linspace ( 0 , ( 12.0 * n ) / bins_per_octave , num = n + 1 , endpoint = True )


def __coord_time(n, sr=22050, hop_length=512, **_kwargs):
    '''Get time coordinates from frames'''
    return core.frames_to_time(np.arange(n+1), sr=sr, hop_length=hop_length)

def __coord_time ( n , sr = 22050 , hop_length = 512 , ** _kwargs ) : return core . frames_to_time ( np . arange ( n + 1 ) , sr = sr , hop_length = hop_length )


def estimate_tuning(y=None, sr=22050, S=None, n_fft=2048,
                    resolution=0.01, bins_per_octave=12, **kwargs):
    '''Estimate the tuning of an audio time series or spectrogram input.

    Parameters
    ----------
    y: np.ndarray [shape=(n,)] or None
        audio signal

    sr : number > 0 [scalar]
        audio sampling rate of `y`

    S: np.ndarray [shape=(d, t)] or None
        magnitude or power spectrogram

    n_fft : int > 0 [scalar] or None
        number of FFT bins to use, if `y` is provided.

    resolution : float in `(0, 1)`
        Resolution of the tuning as a fraction of a bin.
        0.01 corresponds to measurements in cents.

    bins_per_octave : int > 0 [scalar]
        How many frequency bins per octave

    kwargs : additional keyword arguments
        Additional arguments passed to `piptrack`

    Returns
    -------
    tuning: float in `[-0.5, 0.5)`
        estimated tuning deviation (fractions of a bin)

    See Also
    --------
    piptrack
        Pitch tracking by parabolic interpolation

    Examples
    --------
    >>> # With time-series input
    >>> y, sr = librosa.load(librosa.util.example_audio_file())
    >>> librosa.estimate_tuning(y=y, sr=sr)
    0.089999999999999969

    >>> # In tenths of a cent
    >>> y, sr = librosa.load(librosa.util.example_audio_file())
    >>> librosa.estimate_tuning(y=y, sr=sr, resolution=1e-3)
    0.093999999999999972

    >>> # Using spectrogram input
    >>> y, sr = librosa.load(librosa.util.example_audio_file())
    >>> S = np.abs(librosa.stft(y))
    >>> librosa.estimate_tuning(S=S, sr=sr)
    0.089999999999999969

    >>> # Using pass-through arguments to `librosa.piptrack`
    >>> y, sr = librosa.load(librosa.util.example_audio_file())
    >>> librosa.estimate_tuning(y=y, sr=sr, n_fft=8192,
    ...                         fmax=librosa.note_to_hz('G#9'))
    0.070000000000000062

    '''

    pitch, mag = piptrack(y=y, sr=sr, S=S, n_fft=n_fft, **kwargs)

    # Only count magnitude where frequency is > 0
    pitch_mask = pitch > 0

    if pitch_mask.any():
        threshold = np.median(mag[pitch_mask])
    else:
        threshold = 0.0

    return pitch_tuning(pitch[(mag >= threshold) & pitch_mask],
                        resolution=resolution,
                        bins_per_octave=bins_per_octave)

def estimate_tuning ( y = None , sr = 22050 , S = None , n_fft = 2048 , resolution = 0.01 , bins_per_octave = 12 , ** kwargs ) : pitch , mag = piptrack ( y = y , sr = sr , S = S , n_fft = n_fft , ** kwargs ) pitch_mask = pitch > 0 if pitch_mask . any ( ) : threshold = np . median ( mag [ pitch_mask ] ) else : threshold = 0.0 return pitch_tuning ( pitch [ ( mag >= threshold ) & pitch_mask ] , resolution = resolution , bins_per_octave = bins_per_octave )

Estimate the tuning of an audio time series or spectrogram input .


def piptrack(y=None, sr=22050, S=None, n_fft=2048, hop_length=None,
             fmin=150.0, fmax=4000.0, threshold=0.1,
             win_length=None, window='hann', center=True, pad_mode='reflect',
             ref=None):
    '''Pitch tracking on thresholded parabolically-interpolated STFT.

    This implementation uses the parabolic interpolation method described by [1]_.

    .. [1] https://ccrma.stanford.edu/~jos/sasp/Sinusoidal_Peak_Interpolation.html

    Parameters
    ----------
    y: np.ndarray [shape=(n,)] or None
        audio signal

    sr : number > 0 [scalar]
        audio sampling rate of `y`

    S: np.ndarray [shape=(d, t)] or None
        magnitude or power spectrogram

    n_fft : int > 0 [scalar] or None
        number of FFT bins to use, if `y` is provided.

    hop_length : int > 0 [scalar] or None
        number of samples to hop

    threshold : float in `(0, 1)`
        A bin in spectrum `S` is considered a pitch when it is greater than
        `threshold*ref(S)`.

        By default, `ref(S)` is taken to be `max(S, axis=0)` (the maximum value in
        each column).

    fmin : float > 0 [scalar]
        lower frequency cutoff.

    fmax : float > 0 [scalar]
        upper frequency cutoff.

    win_length : int <= n_fft [scalar]
        Each frame of audio is windowed by `window()`.
        The window will be of length `win_length` and then padded
        with zeros to match `n_fft`.

        If unspecified, defaults to ``win_length = n_fft``.

    window : string, tuple, number, function, or np.ndarray [shape=(n_fft,)]
        - a window specification (string, tuple, or number);
          see `scipy.signal.get_window`
        - a window function, such as `scipy.signal.hanning`
        - a vector or array of length `n_fft`

        .. see also:: `filters.get_window`

    center : boolean
        - If `True`, the signal `y` is padded so that frame
          `t` is centered at `y[t * hop_length]`.
        - If `False`, then frame `t` begins at `y[t * hop_length]`

    pad_mode : string
        If `center=True`, the padding mode to use at the edges of the signal.
        By default, STFT uses reflection padding.

    ref : scalar or callable [default=np.max]
        If scalar, the reference value against which `S` is compared for determining
        pitches.

        If callable, the reference value is computed as `ref(S, axis=0)`.

    .. note::
        One of `S` or `y` must be provided.

        If `S` is not given, it is computed from `y` using
        the default parameters of `librosa.core.stft`.

    Returns
    -------
    pitches : np.ndarray [shape=(d, t)]
    magnitudes : np.ndarray [shape=(d,t)]
        Where `d` is the subset of FFT bins within `fmin` and `fmax`.

        `pitches[f, t]` contains instantaneous frequency at bin
        `f`, time `t`

        `magnitudes[f, t]` contains the corresponding magnitudes.

        Both `pitches` and `magnitudes` take value 0 at bins
        of non-maximal magnitude.

    Notes
    -----
    This function caches at level 30.

    Examples
    --------
    Computing pitches from a waveform input

    >>> y, sr = librosa.load(librosa.util.example_audio_file())
    >>> pitches, magnitudes = librosa.piptrack(y=y, sr=sr)

    Or from a spectrogram input

    >>> S = np.abs(librosa.stft(y))
    >>> pitches, magnitudes = librosa.piptrack(S=S, sr=sr)

    Or with an alternate reference value for pitch detection, where
    values above the mean spectral energy in each frame are counted as pitches

    >>> pitches, magnitudes = librosa.piptrack(S=S, sr=sr, threshold=1,
    ...                                        ref=np.mean)

    '''

    # Check that we received an audio time series or STFT
    S, n_fft = _spectrogram(y=y, S=S, n_fft=n_fft, hop_length=hop_length,
                            win_length=win_length, window=window,
                            center=center, pad_mode=pad_mode)

    # Make sure we're dealing with magnitudes
    S = np.abs(S)

    # Truncate to feasible region
    fmin = np.maximum(fmin, 0)
    fmax = np.minimum(fmax, float(sr) / 2)

    fft_freqs = time_frequency.fft_frequencies(sr=sr, n_fft=n_fft)

    # Do the parabolic interpolation everywhere,
    # then figure out where the peaks are
    # then restrict to the feasible range (fmin:fmax)
    avg = 0.5 * (S[2:] - S[:-2])

    shift = 2 * S[1:-1] - S[2:] - S[:-2]

    # Suppress divide-by-zeros.
    # Points where shift == 0 will never be selected by localmax anyway
    shift = avg / (shift + (np.abs(shift) < util.tiny(shift)))

    # Pad back up to the same shape as S
    avg = np.pad(avg, ([1, 1], [0, 0]), mode='constant')
    shift = np.pad(shift, ([1, 1], [0, 0]), mode='constant')

    dskew = 0.5 * avg * shift

    # Pre-allocate output
    pitches = np.zeros_like(S)
    mags = np.zeros_like(S)

    # Clip to the viable frequency range
    freq_mask = ((fmin <= fft_freqs) & (fft_freqs < fmax)).reshape((-1, 1))

    # Compute the column-wise local max of S after thresholding
    # Find the argmax coordinates
    if ref is None:
        ref = np.max

    if six.callable(ref):
        ref_value = threshold * ref(S, axis=0)
    else:
        ref_value = np.abs(ref)

    idx = np.argwhere(freq_mask & util.localmax(S * (S > ref_value)))

    # Store pitch and magnitude
    pitches[idx[:, 0], idx[:, 1]] = ((idx[:, 0] + shift[idx[:, 0], idx[:, 1]])
                                     * float(sr) / n_fft)

    mags[idx[:, 0], idx[:, 1]] = (S[idx[:, 0], idx[:, 1]]
                                  + dskew[idx[:, 0], idx[:, 1]])

    return pitches, mags

def piptrack ( y = None , sr = 22050 , S = None , n_fft = 2048 , hop_length = None , fmin = 150.0 , fmax = 4000.0 , threshold = 0.1 , win_length = None , window = 'hann' , center = True , pad_mode = 'reflect' , ref = None ) : S , n_fft = _spectrogram ( y = y , S = S , n_fft = n_fft , hop_length = hop_length , win_length = win_length , window = window , center = center , pad_mode = pad_mode ) S = np . abs ( S ) fmin = np . maximum ( fmin , 0 ) fmax = np . minimum ( fmax , float ( sr ) / 2 ) fft_freqs = time_frequency . fft_frequencies ( sr = sr , n_fft = n_fft ) avg = 0.5 * ( S [ 2 : ] - S [ : - 2 ] ) shift = 2 * S [ 1 : - 1 ] - S [ 2 : ] - S [ : - 2 ] shift = avg / ( shift + ( np . abs ( shift ) < util . tiny ( shift ) ) ) avg = np . pad ( avg , ( [ 1 , 1 ] , [ 0 , 0 ] ) , mode = 'constant' ) shift = np . pad ( shift , ( [ 1 , 1 ] , [ 0 , 0 ] ) , mode = 'constant' ) dskew = 0.5 * avg * shift pitches = np . zeros_like ( S ) mags = np . zeros_like ( S ) freq_mask = ( ( fmin <= fft_freqs ) & ( fft_freqs < fmax ) ) . reshape ( ( - 1 , 1 ) ) if ref is None : ref = np . max if six . callable ( ref ) : ref_value = threshold * ref ( S , axis = 0 ) else : ref_value = np . abs ( ref ) idx = np . argwhere ( freq_mask & util . localmax ( S * ( S > ref_value ) ) ) pitches [ idx [ : , 0 ] , idx [ : , 1 ] ] = ( ( idx [ : , 0 ] + shift [ idx [ : , 0 ] , idx [ : , 1 ] ] ) * float ( sr ) / n_fft ) mags [ idx [ : , 0 ] , idx [ : , 1 ] ] = ( S [ idx [ : , 0 ] , idx [ : , 1 ] ] + dskew [ idx [ : , 0 ] , idx [ : , 1 ] ] ) return pitches , mags

Pitch tracking on thresholded parabolically - interpolated STFT .


def hpss(y, **kwargs):
    '''Decompose an audio time series into harmonic and percussive components.

    This function automates the STFT->HPSS->ISTFT pipeline, and ensures that
    the output waveforms have equal length to the input waveform `y`.

    Parameters
    ----------
    y : np.ndarray [shape=(n,)]
        audio time series
    kwargs : additional keyword arguments.
        See `librosa.decompose.hpss` for details.

    Returns
    -------
    y_harmonic : np.ndarray [shape=(n,)]
        audio time series of the harmonic elements

    y_percussive : np.ndarray [shape=(n,)]
        audio time series of the percussive elements

    See Also
    --------
    harmonic : Extract only the harmonic component
    percussive : Extract only the percussive component
    librosa.decompose.hpss : HPSS on spectrograms

    Examples
    --------
    >>> # Extract harmonic and percussive components
    >>> y, sr = librosa.load(librosa.util.example_audio_file())
    >>> y_harmonic, y_percussive = librosa.effects.hpss(y)

    >>> # Get a more isolated percussive component by widening its margin
    >>> y_harmonic, y_percussive = librosa.effects.hpss(y, margin=(1.0,5.0))

    '''

    # Compute the STFT matrix
    stft = core.stft(y)

    # Decompose into harmonic and percussives
    stft_harm, stft_perc = decompose.hpss(stft, **kwargs)

    # Invert the STFTs.  Adjust length to match the input.
    y_harm = util.fix_length(core.istft(stft_harm, dtype=y.dtype), len(y))
    y_perc = util.fix_length(core.istft(stft_perc, dtype=y.dtype), len(y))

    return y_harm, y_perc

def hpss ( y , ** kwargs ) : stft = core . stft ( y ) stft_harm , stft_perc = decompose . hpss ( stft , ** kwargs ) y_harm = util . fix_length ( core . istft ( stft_harm , dtype = y . dtype ) , len ( y ) ) y_perc = util . fix_length ( core . istft ( stft_perc , dtype = y . dtype ) , len ( y ) ) return y_harm , y_perc

Decompose an audio time series into harmonic and percussive components .


def harmonic(y, **kwargs):
    '''Extract harmonic elements from an audio time-series.

    Parameters
    ----------
    y : np.ndarray [shape=(n,)]
        audio time series
    kwargs : additional keyword arguments.
        See `librosa.decompose.hpss` for details.

    Returns
    -------
    y_harmonic : np.ndarray [shape=(n,)]
        audio time series of just the harmonic portion

    See Also
    --------
    hpss : Separate harmonic and percussive components
    percussive : Extract only the percussive component
    librosa.decompose.hpss : HPSS for spectrograms

    Examples
    --------
    >>> # Extract harmonic component
    >>> y, sr = librosa.load(librosa.util.example_audio_file())
    >>> y_harmonic = librosa.effects.harmonic(y)

    >>> # Use a margin > 1.0 for greater harmonic separation
    >>> y_harmonic = librosa.effects.harmonic(y, margin=3.0)

    '''

    # Compute the STFT matrix
    stft = core.stft(y)

    # Remove percussives
    stft_harm = decompose.hpss(stft, **kwargs)[0]

    # Invert the STFTs
    y_harm = util.fix_length(core.istft(stft_harm, dtype=y.dtype), len(y))

    return y_harm

def harmonic ( y , ** kwargs ) : stft = core . stft ( y ) stft_harm = decompose . hpss ( stft , ** kwargs ) [ 0 ] y_harm = util . fix_length ( core . istft ( stft_harm , dtype = y . dtype ) , len ( y ) ) return y_harm

Extract harmonic elements from an audio time - series .


def percussive(y, **kwargs):
    '''Extract percussive elements from an audio time-series.

    Parameters
    ----------
    y : np.ndarray [shape=(n,)]
        audio time series
    kwargs : additional keyword arguments.
        See `librosa.decompose.hpss` for details.

    Returns
    -------
    y_percussive : np.ndarray [shape=(n,)]
        audio time series of just the percussive portion

    See Also
    --------
    hpss : Separate harmonic and percussive components
    harmonic : Extract only the harmonic component
    librosa.decompose.hpss : HPSS for spectrograms

    Examples
    --------
    >>> # Extract percussive component
    >>> y, sr = librosa.load(librosa.util.example_audio_file())
    >>> y_percussive = librosa.effects.percussive(y)

    >>> # Use a margin > 1.0 for greater percussive separation
    >>> y_percussive = librosa.effects.percussive(y, margin=3.0)

    '''

    # Compute the STFT matrix
    stft = core.stft(y)

    # Remove harmonics
    stft_perc = decompose.hpss(stft, **kwargs)[1]

    # Invert the STFT
    y_perc = util.fix_length(core.istft(stft_perc, dtype=y.dtype), len(y))

    return y_perc

def percussive ( y , ** kwargs ) : stft = core . stft ( y ) stft_perc = decompose . hpss ( stft , ** kwargs ) [ 1 ] y_perc = util . fix_length ( core . istft ( stft_perc , dtype = y . dtype ) , len ( y ) ) return y_perc

Extract percussive elements from an audio time - series .


def time_stretch(y, rate):
    '''Time-stretch an audio series by a fixed rate.

    Parameters
    ----------
    y : np.ndarray [shape=(n,)]
        audio time series

    rate : float > 0 [scalar]
        Stretch factor.  If `rate > 1`, then the signal is sped up.

        If `rate < 1`, then the signal is slowed down.

    Returns
    -------
    y_stretch : np.ndarray [shape=(rate * n,)]
        audio time series stretched by the specified rate

    See Also
    --------
    pitch_shift : pitch shifting
    librosa.core.phase_vocoder : spectrogram phase vocoder

    Examples
    --------
    Compress to be twice as fast

    >>> y, sr = librosa.load(librosa.util.example_audio_file())
    >>> y_fast = librosa.effects.time_stretch(y, 2.0)

    Or half the original speed

    >>> y_slow = librosa.effects.time_stretch(y, 0.5)

    '''

    if rate <= 0:
        raise ParameterError('rate must be a positive number')

    # Construct the stft
    stft = core.stft(y)

    # Stretch by phase vocoding
    stft_stretch = core.phase_vocoder(stft, rate)

    # Invert the stft
    y_stretch = core.istft(stft_stretch, dtype=y.dtype)

    return y_stretch

def
time_stretch
(
y
,
rate
)
:
if
rate
<=
0
:
raise
ParameterError
(
'rate must be a positive number'
)
stft
=
core
.
stft
(
y
)
stft_stretch
=
core
.
phase_vocoder
(
stft
,
rate
)
y_stretch
=
core
.
istft
(
stft_stretch
,
dtype
=
y
.
dtype
)
return
y_stretch

Time - stretch an audio series by a fixed rate .


def pitch_shift(y, sr, n_steps, bins_per_octave=12, res_type='kaiser_best'):
    '''Pitch-shift the waveform by `n_steps` half-steps.

    Parameters
    ----------
    y : np.ndarray [shape=(n,)]
        audio time-series

    sr : number > 0 [scalar]
        audio sampling rate of `y`

    n_steps : float [scalar]
        how many (fractional) half-steps to shift `y`

    bins_per_octave : float > 0 [scalar]
        how many steps per octave

    res_type : string
        Resample type.
        Possible options: 'kaiser_best', 'kaiser_fast', and 'scipy', 'polyphase',
        'fft'.
        By default, 'kaiser_best' is used.

        See `core.resample` for more information.

    Returns
    -------
    y_shift : np.ndarray [shape=(n,)]
        The pitch-shifted audio time-series

    See Also
    --------
    time_stretch : time stretching
    librosa.core.phase_vocoder : spectrogram phase vocoder

    Examples
    --------
    Shift up by a major third (four half-steps)

    >>> y, sr = librosa.load(librosa.util.example_audio_file())
    >>> y_third = librosa.effects.pitch_shift(y, sr, n_steps=4)

    Shift down by a tritone (six half-steps)

    >>> y_tritone = librosa.effects.pitch_shift(y, sr, n_steps=-6)

    Shift up by 3 quarter-tones

    >>> y_three_qt = librosa.effects.pitch_shift(y, sr, n_steps=3,
    ...                                          bins_per_octave=24)
    '''

    if bins_per_octave < 1 or not np.issubdtype(type(bins_per_octave), np.integer):
        raise ParameterError('bins_per_octave must be a positive integer.')

    rate = 2.0 ** (-float(n_steps) / bins_per_octave)

    # Stretch in time, then resample
    y_shift = core.resample(time_stretch(y, rate), float(sr) / rate, sr,
                            res_type=res_type)

    # Crop to the same dimension as the input
    return util.fix_length(y_shift, len(y))

Pitch - shift the waveform by n_steps half - steps .


def remix(y, intervals, align_zeros=True):
    '''Remix an audio signal by re-ordering time intervals.

    Parameters
    ----------
    y : np.ndarray [shape=(t,) or (2, t)]
        Audio time series

    intervals : iterable of tuples (start, end)
        An iterable (list-like or generator) where the `i`th item
        `intervals[i]` indicates the start and end (in samples)
        of a slice of `y`.

    align_zeros : boolean
        If `True`, interval boundaries are mapped to the closest
        zero-crossing in `y`.  If `y` is stereo, zero-crossings
        are computed after converting to mono.

    Returns
    -------
    y_remix : np.ndarray [shape=(d,) or (2, d)]
        `y` remixed in the order specified by `intervals`

    Examples
    --------
    Load in the example track and reverse the beats

    >>> y, sr = librosa.load(librosa.util.example_audio_file())

    Compute beats

    >>> _, beat_frames = librosa.beat.beat_track(y=y, sr=sr,
    ...                                          hop_length=512)

    Convert from frames to sample indices

    >>> beat_samples = librosa.frames_to_samples(beat_frames)

    Generate intervals from consecutive events

    >>> intervals = librosa.util.frame(beat_samples, frame_length=2,
    ...                                hop_length=1).T

    Reverse the beat intervals

    >>> y_out = librosa.effects.remix(y, intervals[::-1])
    '''

    # Validate the audio buffer
    util.valid_audio(y, mono=False)

    y_out = []

    if align_zeros:
        y_mono = core.to_mono(y)
        zeros = np.nonzero(core.zero_crossings(y_mono))[-1]
        # Force end-of-signal onto zeros
        zeros = np.append(zeros, [len(y_mono)])

    clip = [slice(None)] * y.ndim

    for interval in intervals:

        if align_zeros:
            interval = zeros[util.match_events(interval, zeros)]

        clip[-1] = slice(interval[0], interval[1])

        y_out.append(y[tuple(clip)])

    return np.concatenate(y_out, axis=-1)

def remix ( y , intervals , align_zeros = True ) : util . valid_audio ( y , mono = False ) y_out = [ ] if align_zeros : y_mono = core . to_mono ( y ) zeros = np . nonzero ( core . zero_crossings ( y_mono ) ) [ - 1 ] zeros = np . append ( zeros , [ len ( y_mono ) ] ) clip = [ slice ( None ) ] * y . ndim for interval in intervals : if align_zeros : interval = zeros [ util . match_events ( interval , zeros ) ] clip [ - 1 ] = slice ( interval [ 0 ] , interval [ 1 ] ) y_out . append ( y [ tuple ( clip ) ] ) return np . concatenate ( y_out , axis = - 1 )

Remix an audio signal by re - ordering time intervals .


def _signal_to_frame_nonsilent(y, frame_length=2048, hop_length=512, top_db=60,
                               ref=np.max):
    '''Frame-wise non-silent indicator for audio input.

    This is a helper function for `trim` and `split`.

    Parameters
    ----------
    y : np.ndarray, shape=(n,) or (2,n)
        Audio signal, mono or stereo

    frame_length : int > 0
        The number of samples per frame

    hop_length : int > 0
        The number of samples between frames

    top_db : number > 0
        The threshold (in decibels) below reference to consider as
        silence

    ref : callable or float
        The reference power

    Returns
    -------
    non_silent : np.ndarray, shape=(m,), dtype=bool
        Indicator of non-silent frames
    '''
    # Convert to mono
    y_mono = core.to_mono(y)

    # Compute the MSE for the signal
    mse = feature.rms(y=y_mono,
                      frame_length=frame_length,
                      hop_length=hop_length)**2

    return (core.power_to_db(mse.squeeze(),
                             ref=ref,
                             top_db=None) > - top_db)

def _signal_to_frame_nonsilent ( y , frame_length = 2048 , hop_length = 512 , top_db = 60 , ref = np . max ) : y_mono = core . to_mono ( y ) mse = feature . rms ( y = y_mono , frame_length = frame_length , hop_length = hop_length ) ** 2 return ( core . power_to_db ( mse . squeeze ( ) , ref = ref , top_db = None ) > - top_db )

Frame - wise non - silent indicator for audio input .


def trim(y, top_db=60, ref=np.max, frame_length=2048, hop_length=512):
    '''Trim leading and trailing silence from an audio signal.

    Parameters
    ----------
    y : np.ndarray, shape=(n,) or (2,n)
        Audio signal, can be mono or stereo

    top_db : number > 0
        The threshold (in decibels) below reference to consider as
        silence

    ref : number or callable
        The reference power.  By default, it uses `np.max` and compares
        to the peak power in the signal.

    frame_length : int > 0
        The number of samples per analysis frame

    hop_length : int > 0
        The number of samples between analysis frames

    Returns
    -------
    y_trimmed : np.ndarray, shape=(m,) or (2, m)
        The trimmed signal

    index : np.ndarray, shape=(2,)
        the interval of `y` corresponding to the non-silent region:
        `y_trimmed = y[index[0]:index[1]]` (for mono) or
        `y_trimmed = y[:, index[0]:index[1]]` (for stereo).

    Examples
    --------
    >>> # Load some audio
    >>> y, sr = librosa.load(librosa.util.example_audio_file())
    >>> # Trim the beginning and ending silence
    >>> yt, index = librosa.effects.trim(y)
    >>> # Print the durations
    >>> print(librosa.get_duration(y), librosa.get_duration(yt))
    61.45886621315193 60.58086167800454
    '''

    non_silent = _signal_to_frame_nonsilent(y,
                                            frame_length=frame_length,
                                            hop_length=hop_length,
                                            ref=ref,
                                            top_db=top_db)

    nonzero = np.flatnonzero(non_silent)

    if nonzero.size > 0:
        # Compute the start and end positions
        # End position goes one frame past the last non-zero
        start = int(core.frames_to_samples(nonzero[0], hop_length))
        end = min(y.shape[-1],
                  int(core.frames_to_samples(nonzero[-1] + 1, hop_length)))
    else:
        # The signal only contains zeros
        start, end = 0, 0

    # Build the mono/stereo index
    full_index = [slice(None)] * y.ndim
    full_index[-1] = slice(start, end)

    return y[tuple(full_index)], np.asarray([start, end])

def trim ( y , top_db = 60 , ref = np . max , frame_length = 2048 , hop_length = 512 ) : non_silent = _signal_to_frame_nonsilent ( y , frame_length = frame_length , hop_length = hop_length , ref = ref , top_db = top_db ) nonzero = np . flatnonzero ( non_silent ) if nonzero . size > 0 : start = int ( core . frames_to_samples ( nonzero [ 0 ] , hop_length ) ) end = min ( y . shape [ - 1 ] , int ( core . frames_to_samples ( nonzero [ - 1 ] + 1 , hop_length ) ) ) else : start , end = 0 , 0 full_index = [ slice ( None ) ] * y . ndim full_index [ - 1 ] = slice ( start , end ) return y [ tuple ( full_index ) ] , np . asarray ( [ start , end ] )

Trim leading and trailing silence from an audio signal .


def split(y, top_db=60, ref=np.max, frame_length=2048, hop_length=512):
    '''Split an audio signal into non-silent intervals.

    Parameters
    ----------
    y : np.ndarray, shape=(n,) or (2, n)
        An audio signal

    top_db : number > 0
        The threshold (in decibels) below reference to consider as
        silence

    ref : number or callable
        The reference power.  By default, it uses `np.max` and compares
        to the peak power in the signal.

    frame_length : int > 0
        The number of samples per analysis frame

    hop_length : int > 0
        The number of samples between analysis frames

    Returns
    -------
    intervals : np.ndarray, shape=(m, 2)
        `intervals[i] == (start_i, end_i)` are the start and end time
        (in samples) of non-silent interval `i`.

    '''

    non_silent = _signal_to_frame_nonsilent(y,
                                            frame_length=frame_length,
                                            hop_length=hop_length,
                                            ref=ref,
                                            top_db=top_db)

    # Interval slicing, adapted from
    # https://stackoverflow.com/questions/2619413/efficiently-finding-the-interval-with-non-zeros-in-scipy-numpy-in-python
    # Find points where the sign flips
    edges = np.flatnonzero(np.diff(non_silent.astype(int)))

    # Pad back the sample lost in the diff
    edges = [edges + 1]

    # If the first frame had high energy, count it
    if non_silent[0]:
        edges.insert(0, [0])

    # Likewise for the last frame
    if non_silent[-1]:
        edges.append([len(non_silent)])

    # Convert from frames to samples
    edges = core.frames_to_samples(np.concatenate(edges),
                                   hop_length=hop_length)

    # Clip to the signal duration
    edges = np.minimum(edges, y.shape[-1])

    # Stack the results back as an ndarray
    return edges.reshape((-1, 2))

def split ( y , top_db = 60 , ref = np . max , frame_length = 2048 , hop_length = 512 ) : non_silent = _signal_to_frame_nonsilent ( y , frame_length = frame_length , hop_length = hop_length , ref = ref , top_db = top_db ) edges = np . flatnonzero ( np . diff ( non_silent . astype ( int ) ) ) edges = [ edges + 1 ] if non_silent [ 0 ] : edges . insert ( 0 , [ 0 ] ) if non_silent [ - 1 ] : edges . append ( [ len ( non_silent ) ] ) edges = core . frames_to_samples ( np . concatenate ( edges ) , hop_length = hop_length ) edges = np . minimum ( edges , y . shape [ - 1 ] ) return edges . reshape ( ( - 1 , 2 ) )

Split an audio signal into non - silent intervals .


def phase_vocoder(D, rate, hop_length=None):
    """Phase vocoder.  Given an STFT matrix D, speed up by a factor of `rate`

    Based on the implementation provided by [1]_.

    .. [1] Ellis, D. P. W. "A phase vocoder in Matlab."
        Columbia University, 2002.
        http://www.ee.columbia.edu/~dpwe/resources/matlab/pvoc/

    Examples
    --------
    >>> # Play at double speed
    >>> y, sr   = librosa.load(librosa.util.example_audio_file())
    >>> D       = librosa.stft(y, n_fft=2048, hop_length=512)
    >>> D_fast  = librosa.phase_vocoder(D, 2.0, hop_length=512)
    >>> y_fast  = librosa.istft(D_fast, hop_length=512)

    >>> # Or play at 1/3 speed
    >>> y, sr   = librosa.load(librosa.util.example_audio_file())
    >>> D       = librosa.stft(y, n_fft=2048, hop_length=512)
    >>> D_slow  = librosa.phase_vocoder(D, 1./3, hop_length=512)
    >>> y_slow  = librosa.istft(D_slow, hop_length=512)

    Parameters
    ----------
    D : np.ndarray [shape=(d, t), dtype=complex]
        STFT matrix

    rate :  float > 0 [scalar]
        Speed-up factor: `rate > 1` is faster, `rate < 1` is slower.

    hop_length : int > 0 [scalar] or None
        The number of samples between successive columns of `D`.

        If None, defaults to `n_fft/4 = (D.shape[0]-1)/2`

    Returns
    -------
    D_stretched : np.ndarray [shape=(d, t / rate), dtype=complex]
        time-stretched STFT
    """

    n_fft = 2 * (D.shape[0] - 1)

    if hop_length is None:
        hop_length = int(n_fft // 4)

    time_steps = np.arange(0, D.shape[1], rate, dtype=np.float)

    # Create an empty output array
    d_stretch = np.zeros((D.shape[0], len(time_steps)), D.dtype, order='F')

    # Expected phase advance in each bin
    phi_advance = np.linspace(0, np.pi * hop_length, D.shape[0])

    # Phase accumulator; initialize to the first sample
    phase_acc = np.angle(D[:, 0])

    # Pad 0 columns to simplify boundary logic
    D = np.pad(D, [(0, 0), (0, 2)], mode='constant')

    for (t, step) in enumerate(time_steps):

        columns = D[:, int(step):int(step + 2)]

        # Weighting for linear magnitude interpolation
        alpha = np.mod(step, 1.0)
        mag = ((1.0 - alpha) * np.abs(columns[:, 0])
               + alpha * np.abs(columns[:, 1]))

        # Store to output array
        d_stretch[:, t] = mag * np.exp(1.j * phase_acc)

        # Compute phase advance
        dphase = (np.angle(columns[:, 1])
                  - np.angle(columns[:, 0])
                  - phi_advance)

        # Wrap to -pi:pi range
        dphase = dphase - 2.0 * np.pi * np.round(dphase / (2.0 * np.pi))

        # Accumulate phase
        phase_acc += phi_advance + dphase

    return d_stretch

def phase_vocoder ( D , rate , hop_length = None ) : n_fft = 2 * ( D . shape [ 0 ] - 1 ) if hop_length is None : hop_length = int ( n_fft // 4 ) time_steps = np . arange ( 0 , D . shape [ 1 ] , rate , dtype = np . float ) d_stretch = np . zeros ( ( D . shape [ 0 ] , len ( time_steps ) ) , D . dtype , order = 'F' ) phi_advance = np . linspace ( 0 , np . pi * hop_length , D . shape [ 0 ] ) phase_acc = np . angle ( D [ : , 0 ] ) D = np . pad ( D , [ ( 0 , 0 ) , ( 0 , 2 ) ] , mode = 'constant' ) for ( t , step ) in enumerate ( time_steps ) : columns = D [ : , int ( step ) : int ( step + 2 ) ] alpha = np . mod ( step , 1.0 ) mag = ( ( 1.0 - alpha ) * np . abs ( columns [ : , 0 ] ) + alpha * np . abs ( columns [ : , 1 ] ) ) d_stretch [ : , t ] = mag * np . exp ( 1.j * phase_acc ) dphase = ( np . angle ( columns [ : , 1 ] ) - np . angle ( columns [ : , 0 ] ) - phi_advance ) dphase = dphase - 2.0 * np . pi * np . round ( dphase / ( 2.0 * np . pi ) ) phase_acc += phi_advance + dphase return d_stretch

Phase vocoder . Given an STFT matrix D speed up by a factor of rate


def amplitude_to_db(S, ref=1.0, amin=1e-5, top_db=80.0):
    '''Convert an amplitude spectrogram to dB-scaled spectrogram.

    This is equivalent to ``power_to_db(S**2)``, but is provided for convenience.

    Parameters
    ----------
    S : np.ndarray
        input amplitude

    ref : scalar or callable
        If scalar, the amplitude `abs(S)` is scaled relative to `ref`:
        `20 * log10(S / ref)`.
        Zeros in the output correspond to positions where `S == ref`.

        If callable, the reference value is computed as `ref(S)`.

    amin : float > 0 [scalar]
        minimum threshold for `S` and `ref`

    top_db : float >= 0 [scalar]
        threshold the output at `top_db` below the peak:
        ``max(20 * log10(S)) - top_db``

    Returns
    -------
    S_db : np.ndarray
        ``S`` measured in dB

    See Also
    --------
    power_to_db, db_to_amplitude

    Notes
    -----
    This function caches at level 30.
    '''

    S = np.asarray(S)

    if np.issubdtype(S.dtype, np.complexfloating):
        warnings.warn('amplitude_to_db was called on complex input so phase '
                      'information will be discarded. To suppress this warning, '
                      'call amplitude_to_db(np.abs(S)) instead.')

    magnitude = np.abs(S)

    if six.callable(ref):
        # User supplied a function to calculate reference power
        ref_value = ref(magnitude)
    else:
        ref_value = np.abs(ref)

    power = np.square(magnitude, out=magnitude)

    return power_to_db(power, ref=ref_value**2, amin=amin**2,
                       top_db=top_db)

Convert an amplitude spectrogram to dB - scaled spectrogram .


def _spectrogram(y=None, S=None, n_fft=2048, hop_length=512, power=1,
                 win_length=None, window='hann', center=True, pad_mode='reflect'):
    '''Helper function to retrieve a magnitude spectrogram.

    This is primarily used in feature extraction functions that can operate on
    either audio time-series or spectrogram input.

    Parameters
    ----------
    y : None or np.ndarray [ndim=1]
        If provided, an audio time series

    S : None or np.ndarray
        Spectrogram input, optional

    n_fft : int > 0
        STFT window size

    hop_length : int > 0
        STFT hop length

    power : float > 0
        Exponent for the magnitude spectrogram,
        e.g., 1 for energy, 2 for power, etc.

    win_length : int <= n_fft [scalar]
        Each frame of audio is windowed by `window()`.
        The window will be of length `win_length` and then padded
        with zeros to match `n_fft`.

        If unspecified, defaults to ``win_length = n_fft``.

    window : string, tuple, number, function, or np.ndarray [shape=(n_fft,)]
        - a window specification (string, tuple, or number);
          see `scipy.signal.get_window`
        - a window function, such as `scipy.signal.hanning`
        - a vector or array of length `n_fft`

        .. see also:: `filters.get_window`

    center : boolean
        - If `True`, the signal `y` is padded so that frame
          `t` is centered at `y[t * hop_length]`.
        - If `False`, then frame `t` begins at `y[t * hop_length]`

    pad_mode : string
        If `center=True`, the padding mode to use at the edges of the signal.
        By default, STFT uses reflection padding.

    Returns
    -------
    S_out : np.ndarray [dtype=np.float32]
        - If `S` is provided as input, then `S_out == S`
        - Else, `S_out = |stft(y, ...)|**power`

    n_fft : int > 0
        - If `S` is provided, then `n_fft` is inferred from `S`
        - Else, copied from input
    '''

    if S is not None:
        # Infer n_fft from spectrogram shape
        n_fft = 2 * (S.shape[0] - 1)
    else:
        # Otherwise, compute a magnitude spectrogram from input
        S = np.abs(stft(y, n_fft=n_fft, hop_length=hop_length,
                        win_length=win_length, center=center,
                        window=window, pad_mode=pad_mode))**power

    return S, n_fft

def _spectrogram ( y = None , S = None , n_fft = 2048 , hop_length = 512 , power = 1 , win_length = None , window = 'hann' , center = True , pad_mode = 'reflect' ) : if S is not None : n_fft = 2 * ( S . shape [ 0 ] - 1 ) else : S = np . abs ( stft ( y , n_fft = n_fft , hop_length = hop_length , win_length = win_length , center = center , window = window , pad_mode = pad_mode ) ) ** power return S , n_fft

Helper function to retrieve a magnitude spectrogram .


def hpss_beats(input_file, output_csv):
    '''HPSS beat tracking

    :parameters:
      - input_file : str
          Path to input audio file (wav, mp3, m4a, flac, etc.)

      - output_file : str
          Path to save beat event timestamps as a CSV file
    '''

    # Load the file
    print('Loading  ', input_file)
    y, sr = librosa.load(input_file)

    # Do HPSS
    print('Harmonic-percussive separation ... ')
    y = librosa.effects.percussive(y)

    # Construct onset envelope from percussive component
    print('Tracking beats on percussive component')
    onset_env = librosa.onset.onset_strength(y=y,
                                             sr=sr,
                                             hop_length=HOP_LENGTH,
                                             n_fft=N_FFT,
                                             aggregate=np.median)

    # Track the beats
    tempo, beats = librosa.beat.beat_track(onset_envelope=onset_env,
                                           sr=sr,
                                           hop_length=HOP_LENGTH)

    beat_times = librosa.frames_to_time(beats,
                                        sr=sr,
                                        hop_length=HOP_LENGTH)

    # Save the output
    print('Saving beats to ', output_csv)
    librosa.output.times_csv(output_csv, beat_times)