Skip to content

Commit

Permalink
Merge dbbb932 into 09381cc
Browse files Browse the repository at this point in the history
  • Loading branch information
bmcfee committed May 17, 2020
2 parents 09381cc + dbbb932 commit 5d396fa
Show file tree
Hide file tree
Showing 4 changed files with 87 additions and 15 deletions.
14 changes: 14 additions & 0 deletions .travis.yml
@@ -1,6 +1,8 @@
cache:
apt: True
directories:
- $HOME/env
- $HOME/Library/Caches/Homebrew

language: python

Expand All @@ -22,13 +24,25 @@ jobs:
language: generic
osx_image: xcode11

addons:
apt:
packages:
- libsamplerate0

homebrew:
packages:
- libsamplerate

before_install:
- bash .travis_dependencies.sh
- export PATH="$HOME/env/miniconda-$TRAVIS_OS_NAME$TRAVIS_PYTHON_VERSION/bin:$PATH";
- hash -r
- source activate test-environment
- conda list

before_cache:
- brew cleanup

install:
# install your own package into the environment
# pip install -e rather than setup.py, so that coverage can find the source
Expand Down
5 changes: 0 additions & 5 deletions .travis_dependencies.sh
Expand Up @@ -25,14 +25,9 @@ if [ ! -d "$src" ]; then
# Download miniconda packages
if [ "$TRAVIS_OS_NAME" = "osx" ]; then
wget https://repo.continuum.io/miniconda/Miniconda3-latest-MacOSX-x86_64.sh -O miniconda.sh;
# Platform-specific dependencies
brew update
brew install libsamplerate
fi
if [ "$TRAVIS_OS_NAME" = "linux" ]; then
wget https://repo.continuum.io/miniconda/Miniconda3-latest-Linux-x86_64.sh -O miniconda.sh;
sudo apt update
sudo apt install libsamplerate0
fi
# Install both environments
bash miniconda.sh -b -p $src
Expand Down
69 changes: 60 additions & 9 deletions librosa/feature/utils.py
Expand Up @@ -4,6 +4,7 @@

import numpy as np
import scipy.signal
from numba import jit

from .._cache import cache
from ..util.exceptions import ParameterError
Expand Down Expand Up @@ -135,7 +136,7 @@ def stack_memory(data, n_steps=2, delay=1, **kwargs):
Parameters
----------
data : np.ndarray [shape=(t,) or (d, t)]
data : np.ndarray [shape=(d, t)]
Input data matrix. If `data` is a vector (`data.ndim == 1`),
it will be interpreted as a row matrix and reshaped to `(1, t)`.
Expand Down Expand Up @@ -219,12 +220,19 @@ def stack_memory(data, n_steps=2, delay=1, **kwargs):
if n_steps < 1:
raise ParameterError('n_steps must be a positive integer')

if data.ndim > 2:
raise ParameterError('Input must be at most 2-dimensional. '
'Given data.shape={}'.format(data.shape))

if delay == 0:
raise ParameterError('delay must be a non-zero integer')

data = np.atleast_2d(data)

t = data.shape[1]
t = data.shape[-1]

if t < 1:
raise ParameterError('Cannot stack memory when input data has '
'no columns. Given data.shape={}'.format(data.shape))
kwargs.setdefault('mode', 'constant')

if kwargs['mode'] == 'constant':
Expand All @@ -238,13 +246,56 @@ def stack_memory(data, n_steps=2, delay=1, **kwargs):

data = np.pad(data, [(0, 0), padding], **kwargs)

history = np.vstack([np.roll(data, -i * delay, axis=1) for i in range(n_steps)[::-1]])
# Construct the shape of the target array
shape = list(data.shape)
shape[0] = shape[0] * n_steps
shape[1] = t
shape = tuple(shape)

# Construct the output array to match layout and dtype of input
history = np.empty_like(data, shape=shape)

# Populate the output array
__stack(history, data, n_steps, delay)

return history


@jit(nopython=True, cache=True)
def __stack(history, data, n_steps, delay):
'''Memory-stacking helper function.
Parameters
----------
history : output array (2-dimensional)
data : pre-padded input array (2-dimensional)
n_steps : int > 0, the number of steps to stack
delay : int != 0, the amount of delay between steps
Returns
-------
None
Output is stored directly in the history array
'''
# Dimension of each copy of the data
d = data.shape[0]

# Total number of time-steps to output
t = history.shape[1]

# Trim to original width
if delay > 0:
history = history[:, :t]
for step in range(n_steps):
q = n_steps - 1 - step
# nth block is original shifted left by n*delay steps
history[step * d:(step + 1) * d] = data[:, q*delay:q*delay+t]
else:
history = history[:, -t:]
# Handle the last block separately to avoid -t:0 empty slices
history[-d:, :] = data[:, -t:]

# Make contiguous
return np.asfortranarray(history)
for step in range(n_steps-1):
# nth block is original shifted right by n*delay steps
q = n_steps - 1 - step
history[step * d:(step + 1) * d] = data[:, -t + q*delay:q*delay]
14 changes: 13 additions & 1 deletion tests/test_features.py
Expand Up @@ -69,7 +69,7 @@ def test_delta_badwidthaxis(x, width, axis):
librosa.feature.delta(x, width=width, axis=axis)


@pytest.mark.parametrize("data", [np.random.randn(5), np.random.randn(5, 5), np.remainder(np.arange(10000), 24)])
@pytest.mark.parametrize("data", [np.arange(5.), np.remainder(np.arange(10000), 24)])
@pytest.mark.parametrize("delay", [-4, -2, -1, 1, 2, 4])
@pytest.mark.parametrize("n_steps", [1, 2, 3, 300])
def test_stack_memory(data, n_steps, delay):
Expand Down Expand Up @@ -104,6 +104,18 @@ def test_stack_memory_fail(data, n_steps, delay):
librosa.feature.stack_memory(data, n_steps=n_steps, delay=delay)


@pytest.mark.xfail(raises=librosa.ParameterError)
def test_stack_memory_ndim_toobig():
librosa.feature.stack_memory(np.zeros((2,2,2)), n_steps=3, delay=1)

@pytest.mark.parametrize('data', [np.zeros((2, 0))])
@pytest.mark.xfail(raises=librosa.ParameterError)
@pytest.mark.parametrize('delay', [-2, -1, 1, 2])
@pytest.mark.parametrize('n_steps', [1, 2])
def test_stack_memory_ndim_badshape(data, delay, n_steps):
librosa.feature.stack_memory(data, n_steps=n_steps, delay=delay)


@pytest.fixture(scope="module")
def S_ideal():
# An idealized spectrum with all zero energy except at one DFT band
Expand Down

0 comments on commit 5d396fa

Please sign in to comment.