Skip to content

Commit

Permalink
Merge 737afaa into 0ebe464
Browse files Browse the repository at this point in the history
  • Loading branch information
marcsarfa committed Jun 6, 2018
2 parents 0ebe464 + 737afaa commit 15ba2e4
Show file tree
Hide file tree
Showing 2 changed files with 89 additions and 1 deletion.
62 changes: 61 additions & 1 deletion pyrubberband/pyrb.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
import soundfile as sf


__all__ = ['time_stretch', 'pitch_shift']
__all__ = ['time_stretch', 'pitch_shift', 'timemap_stretch']

__RUBBERBAND_UTIL = 'rubberband'

Expand Down Expand Up @@ -141,6 +141,66 @@ def time_stretch(y, sr, rate, rbargs=None):
return __rubberband(y, sr, **rbargs)


def timemap_stretch(y, sr, time_map, rbargs=None):
'''Apply a timemap stretch to an audio time series.
This uses the `time` and `timemap` form for rubberband.
Parameters
----------
y : np.ndarray [shape=(n,) or (n, c)]
Audio time series, either single or multichannel
sr : int > 0
Sampling rate of `y`
time_map : list
Each element is a tuple `t` of length 2 which correspond to the input
frame and desired output frame.
If t[1] < t[0] the track will be sped up in this area.
time_map[-1] must correspond to the lengths of the input audio and
output audio.
rbargs
Additional keyword parameters for rubberband
See `rubberband -h` for details.
Returns
-------
y_stretch : np.ndarray
Time-stretched audio
Raises
------
ValueError
if `rate <= 0`
'''

if rbargs is None:
rbargs = dict()

time_stretch = time_map[-1][1]/time_map[-1][0]

rbargs.setdefault('--time', time_stretch)

fs, stretch_file_name = tempfile.mkstemp(suffix='.txt')
os.close(fs)

stretch_file = open(stretch_file_name, 'w')
for t in time_map:
stretch_file.write("%d %d\n" % (t[0], t[1]))
stretch_file.close()

rbargs.setdefault('--timemap', stretch_file_name)

y_stretch = __rubberband(y, sr, **rbargs)
os.unlink(stretch_file_name)

return y_stretch


def pitch_shift(y, sr, n_steps, rbargs=None):
'''Apply a pitch shift to an audio time series.
Expand Down
28 changes: 28 additions & 0 deletions tests/test_pyrb.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,14 @@ def channels(request):
return request.param


@pytest.fixture
def time_map(num_samples, request):
return [(0, 0),
(num_samples//4, num_samples//4),
((3*num_samples)//4, num_samples//2),
(num_samples, (3*num_samples)//4)]


@pytest.fixture
def random_signal(channels, num_samples):
if channels is not None:
Expand Down Expand Up @@ -107,6 +115,26 @@ def test_pitch(sr, num_samples, freq, n_step):
assert np.allclose(s_s / s_s[0], s_f / s_f[0], atol=1e-2)


def test_timemap_stretch(sr, num_samples, freq, time_map):

y = np.cos(2 * np.pi * freq * np.arange(num_samples)/sr)
# Apply time strech
y_s = pyrubberband.timemap_stretch(y, sr, time_map)

assert len(y) * time_map[-1][1] == len(y_s) * time_map[-1][0]

# Make sure the stretched audio signal has the same note as the original
fft = np.abs(np.fft.fft(y))
fft = fft[:len(fft)//2]

fft_s = np.abs(np.fft.fft(y_s))
fft_s = fft_s[:len(fft_s)//2]

assert np.isclose(np.argmax(fft) / len(fft),
np.argmax(fft_s) / len(fft_s),
rtol=1e-1)


@pytest.mark.parametrize(
"cli",
[pytest.mark.xfail('rubberband-missing', raises=RuntimeError),
Expand Down

0 comments on commit 15ba2e4

Please sign in to comment.