ref: 05ed7f562616cee876d2e4cd397cdb88f600942f
parent: 7b8e51cdcaeebac5bcb54bd158057b396d580fe3
parent: bd183b3e190524d5fbbba2d8e09995a0fa848e42
author: Paul Brossier <piem@piem.org>
date: Thu Jul 2 15:03:56 EDT 2020
Merge branch 'feature/timestretch'
--- a/.circleci/config.yml
+++ b/.circleci/config.yml
@@ -2,7 +2,7 @@
name: Install apt packages
command: |
sudo apt-get update
- sudo apt-get -y install make sox pkg-config libavcodec-dev libavformat-dev libavresample-dev libavutil-dev libsndfile1-dev libsamplerate-dev libvorbis-dev libflac-dev
+ sudo apt-get -y install make sox pkg-config libavcodec-dev libavformat-dev libavresample-dev libavutil-dev libsndfile1-dev libsamplerate-dev librubberband-dev libvorbis-dev libflac-dev
pip-install: &pip-install
name: Install pip dependencies
--- a/.travis.yml
+++ b/.travis.yml
@@ -75,6 +75,7 @@
- libjack-dev
- libasound2-dev
- libfftw3-dev
+ - librubberband-dev
- sox
- lcov
homebrew:
@@ -84,6 +85,8 @@
- libsndfile
- libvorbis
- flac
+ - libsamplerate
+ - rubberband
- lcov
update: true
--- a/azure-pipelines.yml
+++ b/azure-pipelines.yml
@@ -6,7 +6,7 @@
vmImage: 'ubuntu-latest'
steps:
- script: |
- sudo apt install libavformat-dev
+ sudo apt install libavformat-dev librubberband-dev
displayName: 'deps'
- script: |
make
--- /dev/null
+++ b/python/demos/demo_pitchshift.py
@@ -1,0 +1,52 @@
+#! /usr/bin/env python
+
+import sys
+import aubio
+
+if __name__ == '__main__':
+ if len(sys.argv) < 3:
+ print('usage: %s <inputfile> <outputfile> [transpose] [samplerate] [hop_size] [mode]' % sys.argv[0])
+ print('available modes: default, crispness:0, crispness:1, ... crispness:6')
+ sys.exit(1)
+ if len(sys.argv) > 3: transpose = float(sys.argv[3])
+ else: transpose = 12.
+ if len(sys.argv) > 4: samplerate = int(sys.argv[4])
+ else: samplerate = 0
+ if len(sys.argv) > 5: hop_size = int(sys.argv[5])
+ else: hop_size = 64
+ if len(sys.argv) > 6: mode = sys.argv[6]
+ else: mode = "default"
+
+ source_read = aubio.source(sys.argv[1], samplerate, hop_size)
+ if samplerate == 0: samplerate = source_read.samplerate
+ sink_out = aubio.sink(sys.argv[2], samplerate)
+
+ pitchshifter = aubio.pitchshift(mode, 1., hop_size, samplerate)
+ if transpose: pitchshifter.set_transpose(transpose)
+
+ total_frames, read = 0, hop_size
+ transpose_range = 23.9
+ while read:
+ vec, read = source_read()
+ # transpose the samples
+ out = pitchshifter(vec)
+ # position in the file (between 0. and 1.)
+ percent_read = total_frames / float(source_read.duration)
+ # variable transpose rate (in semitones)
+ transpose = 2 * transpose_range * percent_read - transpose_range
+ # set transpose rate
+ pitchshifter.set_transpose(transpose)
+ # print the transposition
+ #print pitchshifter.get_transpose()
+ # write the output
+ sink_out(out, read)
+ total_frames += read
+
+ # end of file, print some results
+ outstr = "wrote %.2fs" % (total_frames / float(samplerate))
+ outstr += " (%d frames in" % total_frames
+ outstr += " %d blocks" % (total_frames // source_read.hop_size)
+ outstr += " at %dHz)" % source_read.samplerate
+ outstr += " from " + source_read.uri
+ outstr += " to " + sink_out.uri
+ print(outstr)
--- a/python/lib/gen_code.py
+++ b/python/lib/gen_code.py
@@ -20,6 +20,7 @@
'ratio': '0.5',
'method': '"default"',
'uri': '"none"',
+ 'transpose': '0.',
}
member_types = {
@@ -83,6 +84,7 @@
'tempo': '1',
'filterbank': 'self->n_filters',
'tss': 'self->buf_size',
+ 'pitchshift': 'self->hop_size',
'dct': 'self->size',
}
@@ -96,6 +98,7 @@
'tempo': 'self->hop_size',
'wavetable': 'self->hop_size',
'tss': 'self->buf_size / 2 + 1',
+ 'pitchshift': 'self->hop_size',
}
def get_name(proto):
@@ -290,6 +293,8 @@
return self.check_valid_uint(p)
if p['type'] == 'char_t*':
return self.check_valid_char(p)
+ if p['type'] == 'smpl_t':
+ return self.check_valid_smpl(p)
else:
print ("ERROR, no idea how to check %s for validity" % p['type'])
@@ -310,6 +315,15 @@
return """
self->{name} = {defval};
if ({name} != NULL) {{
+ self->{name} = {name};
+ }}
+""".format(defval = aubiodefvalue[name], name = name)
+
+ def check_valid_smpl(self, p):
+ name = p['name']
+ return """
+ self->{name} = {defval};
+ if ({name} != 0.) {{
self->{name} = {name};
}}
""".format(defval = aubiodefvalue[name], name = name)
--- a/python/lib/gen_external.py
+++ b/python/lib/gen_external.py
@@ -46,6 +46,7 @@
#'sampler',
'audio_unit',
'spectral_whitening',
+ 'timestretch', # TODO fix parsing of uint_t *read in _do
]
--- a/python/lib/moresetuptools.py
+++ b/python/lib/moresetuptools.py
@@ -78,7 +78,9 @@
print("Info: looking for *optional* additional packages")
packages = ['libavcodec', 'libavformat', 'libavutil',
'libswresample', 'libavresample',
+ 'jack',
'sndfile',
+ 'rubberband',
#'fftw3f',
]
# samplerate only works with float
@@ -100,6 +102,8 @@
ext.define_macros += [('HAVE_SNDFILE', 1)]
if 'samplerate' in ext.libraries:
ext.define_macros += [('HAVE_SAMPLERATE', 1)]
+ if 'rubberband' in ext.libraries:
+ ext.define_macros += [('HAVE_RUBBERBAND', 1)]
if 'fftw3f' in ext.libraries:
ext.define_macros += [('HAVE_FFTW3F', 1)]
ext.define_macros += [('HAVE_FFTW3', 1)]
--- /dev/null
+++ b/python/tests/test_pitchshift.py
@@ -1,0 +1,104 @@
+#! /usr/bin/env python
+
+from numpy.testing import TestCase
+from _tools import parametrize, skipTest
+import numpy as np
+import aubio
+
+class aubio_pitchshift(TestCase):
+
+ def setUp(self):
+ try:
+ self.o = aubio.pitchshift(hop_size = 128)
+ except RuntimeError as e:
+ self.skipTest("creating aubio.pitchshift {}".format(e))
+
+ def test_default_creation(self):
+ self.assertEqual(self.o.get_pitchscale(), 1)
+ self.assertEqual(self.o.get_transpose(), 0)
+
+ def test_on_zeros(self):
+ test_length = self.o.hop_size * 100
+ read = 0
+ # test on zeros
+ vec = aubio.fvec(self.o.hop_size)
+ transpose_range = 24
+ while read < test_length:
+ # transpose the samples
+ out = self.o(vec)
+ self.assertTrue((out == 0).all())
+ # position in the file (between 0. and 1.)
+ percent_read = read / float(test_length)
+ # variable transpose rate (in semitones)
+ transpose = 2 * transpose_range * percent_read - transpose_range
+ # set transpose rate
+ self.o.set_transpose(transpose)
+ read += len(vec)
+
+ def test_on_ones(self):
+ test_length = self.o.hop_size * 100
+ read = 0
+ # test on zeros
+ vec = aubio.fvec(self.o.hop_size) + 1
+ transpose_range = 1.24
+ while read < test_length:
+ # transpose the samples
+ out = self.o(vec)
+ # position in the file (between 0. and 1.)
+ percent_read = read / float(test_length)
+ # variable transpose rate (in semitones)
+ transpose = 2 * transpose_range * percent_read - transpose_range
+ # set transpose rate
+ self.o.set_transpose(transpose)
+ read += len(vec)
+
+ def test_transpose_too_high(self):
+ with self.assertRaises(ValueError):
+ self.o.set_transpose(24.3)
+
+ def test_transpose_too_low(self):
+ with self.assertRaises(ValueError):
+ self.o.set_transpose(-24.3)
+
+class aubio_pitchshift_wrong_params(TestCase):
+
+ def test_wrong_transpose(self):
+ with self.assertRaises(RuntimeError):
+ aubio.pitchshift("default", -123)
+
+class Test_aubio_pitchshift_testruns(object):
+
+ run_args = ['mode', 'pitchscale', 'hop_size', 'samplerate']
+ run_values = [
+ ("default", 1.2, 128, 44100),
+ ("crispness:0", 0.43, 64, 8000),
+ ("crispness:3", 0.53, 256, 8000),
+ ("crispness:3", 1.53, 512, 8000),
+ ("crispness:6", 2.3, 4096, 192000),
+ ]
+
+ @parametrize(run_args, run_values)
+ def test_run_with_params(self, mode, pitchscale, hop_size, samplerate):
+ try:
+ self.o = aubio.pitchshift(mode, pitchscale, hop_size, samplerate)
+ except RuntimeError as e:
+ skipTest("failed creating pitchshift ({})".format(e))
+ test_length = self.o.hop_size * 50
+ read = 0
+ # test on random
+ vec = np.random.rand(self.o.hop_size).astype(aubio.float_type)
+ transpose_range = self.o.get_transpose()
+ while read < test_length:
+ # transpose the samples
+ out = self.o(vec)
+ # position in the file (between 0. and 1.)
+ percent_read = read / float(test_length)
+ # variable transpose rate (in semitones)
+ transpose = transpose_range - 2 * transpose_range * percent_read
+ # set transpose rate
+ self.o.set_transpose(transpose)
+ read += len(vec)
+
+if __name__ == '__main__':
+ from _tools import run_module_suite
+ run_module_suite()
--- a/src/aubio.h
+++ b/src/aubio.h
@@ -220,6 +220,8 @@
#include "pitch/pitchfcomb.h"
#include "pitch/pitchspecacf.h"
#include "tempo/beattracking.h"
+#include "effects/pitchshift.h"
+#include "effects/timestretch.h"
#include "utils/scale.h"
#include "utils/hist.h"
#endif
--- /dev/null
+++ b/src/effects/pitchshift.h
@@ -1,0 +1,127 @@
+/*
+ Copyright (C) 2016 Paul Brossier <piem@aubio.org>
+
+ This file is part of aubio.
+
+ aubio is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ aubio is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with aubio. If not, see <http://www.gnu.org/licenses/>.
+
+*/
+
+#ifndef AUBIO_PITCHSHIFT_H
+#define AUBIO_PITCHSHIFT_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/** \file
+
+ Pitch shifting object
+
+ ::aubio_pitchshift_t can be used to transpose a stream of blocks of frames.
+
+ \example effects/test-pitchshift.c
+
+*/
+
+/** pitch shifting object */
+typedef struct _aubio_pitchshift_t aubio_pitchshift_t;
+
+/** execute pitch shifting on an input signal frame
+
+ \param o pitch shifting object as returned by new_aubio_pitchshift()
+ \param in input signal of size [hop_size]
+ \param out output pitch candidates of size [1]
+
+*/
+void aubio_pitchshift_do (aubio_pitchshift_t * o, const fvec_t * in,
+ fvec_t * out);
+
+/** deletion of the pitch shifting object
+
+ \param o pitch shifting object as returned by new_aubio_pitchshift()
+
+*/
+void del_aubio_pitchshift (aubio_pitchshift_t * o);
+
+/** creation of the pitch shifting object
+
+ \param method set pitch shifting algorithm ("default")
+ \param transpose initial pitch transposition
+ \param hop_size step size between two consecutive analysis instant
+ \param samplerate sampling rate of the signal
+
+ \return newly created ::aubio_pitchshift_t
+
+*/
+aubio_pitchshift_t *new_aubio_pitchshift (const char_t * method,
+ smpl_t transpose, uint_t hop_size, uint_t samplerate);
+
+/** get the latency of the pitch shifting object, in samples
+
+ \param o pitch shifting object as returned by ::new_aubio_pitchshift()
+
+ \return latency of the pitch shifting object in samples
+
+*/
+uint_t aubio_pitchshift_get_latency (aubio_pitchshift_t * o);
+
+/** set the pitch scale of the pitch shifting object
+
+ \param o pitch shifting object as returned by new_aubio_pitchshift()
+ \param pitchscale new pitch scale of the pitch shifting object
+
+ pitchscale is a frequency ratio. It should be in the range [0.25, 4].
+
+ \return 0 if successfull, non-zero otherwise
+
+*/
+uint_t aubio_pitchshift_set_pitchscale (aubio_pitchshift_t * o,
+ smpl_t pitchscale);
+
+/** get the pitchscale of the pitch shifting object
+
+ \param o pitch shifting object as returned by ::new_aubio_pitchshift()
+
+ \return pitchscale of the pitch shifting object
+
+*/
+smpl_t aubio_pitchshift_get_pitchscale (aubio_pitchshift_t * o);
+
+/** set the transposition of the pitch shifting object, in semitones
+
+ \param o pitch shifting object as returned by new_aubio_pitchshift()
+ \param transpose new pitch transposition of the pitch shifting object,
+ expressed in semitones (should be in the range [-24;+24])
+
+ \return 0 if successfull, non-zero otherwise
+
+*/
+uint_t aubio_pitchshift_set_transpose (aubio_pitchshift_t * o,
+ smpl_t transpose);
+
+/** get the transposition of the pitch shifting object, in semitones
+
+ \param o pitch shifting object as returned by ::new_aubio_pitchshift()
+
+ \return transposition of the pitch shifting object, in semitones
+
+*/
+smpl_t aubio_pitchshift_get_transpose (aubio_pitchshift_t * o);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* AUBIO_PITCHSHIFT_H */
--- /dev/null
+++ b/src/effects/pitchshift_dummy.c
@@ -1,0 +1,73 @@
+/*
+ Copyright (C) 2016 Paul Brossier <piem@aubio.org>
+
+ This file is part of aubio.
+
+ aubio is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ aubio is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with aubio. If not, see <http://www.gnu.org/licenses/>.
+
+*/
+
+#include "aubio_priv.h"
+
+#ifndef HAVE_RUBBERBAND
+
+#include "fvec.h"
+#include "effects/pitchshift.h"
+
+// TODO fallback pitch shifting implementation
+
+struct _aubio_pitchshift_t
+{
+ void *dummy;
+};
+
+void aubio_pitchshift_do (aubio_pitchshift_t * o UNUSED, const fvec_t * in UNUSED,
+ fvec_t * out UNUSED) {
+}
+
+void del_aubio_pitchshift (aubio_pitchshift_t * o UNUSED) {
+}
+
+aubio_pitchshift_t *new_aubio_pitchshift (const char_t * method UNUSED,
+ smpl_t pitchscale UNUSED, uint_t hop_size UNUSED, uint_t samplerate UNUSED)
+{
+ AUBIO_ERR ("aubio was not compiled with rubberband\n");
+ return NULL;
+}
+
+uint_t aubio_pitchshift_set_pitchscale (aubio_pitchshift_t * o UNUSED, smpl_t pitchscale UNUSED)
+{
+ return AUBIO_FAIL;
+}
+
+smpl_t aubio_pitchshift_get_pitchscale (aubio_pitchshift_t * o UNUSED)
+{
+ return 1.;
+}
+
+uint_t aubio_pitchshift_set_transpose (aubio_pitchshift_t * o UNUSED, smpl_t transpose UNUSED) {
+ return AUBIO_FAIL;
+}
+
+smpl_t aubio_pitchshift_get_transpose (aubio_pitchshift_t * o UNUSED) {
+ return 0.;
+}
+
+uint_t aubio_pitchshift_get_latency (aubio_pitchshift_t * o UNUSED) {
+ return 0.;
+}
+
+// end of dummy implementation
+
+#endif /* HAVE_RUBBERBAND */
--- /dev/null
+++ b/src/effects/pitchshift_rubberband.c
@@ -1,0 +1,162 @@
+/*
+ Copyright (C) 2016 Paul Brossier <piem@aubio.org>
+
+ This file is part of aubio.
+
+ aubio is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ aubio is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with aubio. If not, see <http://www.gnu.org/licenses/>.
+
+*/
+
+#include "aubio_priv.h"
+
+#ifdef HAVE_RUBBERBAND
+
+#include "fvec.h"
+#include "effects/pitchshift.h"
+
+#include <rubberband/rubberband-c.h>
+
+/** generic pitch shifting structure */
+struct _aubio_pitchshift_t
+{
+ uint_t samplerate; /**< samplerate */
+ uint_t hopsize; /**< hop size */
+ smpl_t pitchscale; /**< pitch scale */
+
+ RubberBandState rb;
+ RubberBandOptions rboptions;
+};
+
+extern RubberBandOptions aubio_get_rubberband_opts(const char_t *mode);
+
+aubio_pitchshift_t *
+new_aubio_pitchshift (const char_t * mode,
+ smpl_t transpose, uint_t hopsize, uint_t samplerate)
+{
+ aubio_pitchshift_t *p = AUBIO_NEW (aubio_pitchshift_t);
+ p->samplerate = samplerate;
+ p->hopsize = hopsize;
+ p->pitchscale = 1.;
+ p->rb = NULL;
+ if ((sint_t)hopsize <= 0) {
+ AUBIO_ERR("pitchshift: hop_size should be >= 0, got %d\n", hopsize);
+ goto beach;
+ }
+ if ((sint_t)samplerate <= 0) {
+ AUBIO_ERR("pitchshift: samplerate should be >= 0, got %d\n", samplerate);
+ goto beach;
+ }
+
+ p->rboptions = aubio_get_rubberband_opts(mode);
+ if (p->rboptions < 0) {
+ AUBIO_ERR("pitchshift: unknown pitch shifting method %s\n", mode);
+ goto beach;
+ }
+
+ //AUBIO_MSG("pitchshift: using pitch shifting method %s\n", mode);
+
+ p->rb = rubberband_new(samplerate, 1, p->rboptions, 1., p->pitchscale);
+ rubberband_set_max_process_size(p->rb, p->hopsize);
+ //rubberband_set_debug_level(p->rb, 10);
+
+ if (aubio_pitchshift_set_transpose(p, transpose)) goto beach;
+
+#if 1
+ // warm up rubber band
+ unsigned int latency = MAX(p->hopsize, rubberband_get_latency(p->rb));
+ int available = rubberband_available(p->rb);
+ fvec_t *zeros = new_fvec(p->hopsize);
+ while (available <= (int)latency) {
+ rubberband_process(p->rb,
+ (const float* const*)&(zeros->data), p->hopsize, 0);
+ available = rubberband_available(p->rb);
+ }
+ del_fvec(zeros);
+#endif
+
+ return p;
+
+beach:
+ del_aubio_pitchshift(p);
+ return NULL;
+}
+
+void
+del_aubio_pitchshift (aubio_pitchshift_t * p)
+{
+ if (p->rb) {
+ rubberband_delete(p->rb);
+ }
+ AUBIO_FREE (p);
+}
+
+uint_t aubio_pitchshift_get_latency (aubio_pitchshift_t * p) {
+ return rubberband_get_latency(p->rb);
+}
+
+uint_t
+aubio_pitchshift_set_pitchscale (aubio_pitchshift_t * p, smpl_t pitchscale)
+{
+ if (pitchscale >= 0.25 && pitchscale <= 4.) {
+ p->pitchscale = pitchscale;
+ rubberband_set_pitch_scale(p->rb, p->pitchscale);
+ return AUBIO_OK;
+ } else {
+ AUBIO_ERR("pitchshift: could not set pitchscale to '%f',"
+ " should be in the range [0.25, 4.].\n", pitchscale);
+ return AUBIO_FAIL;
+ }
+}
+
+smpl_t
+aubio_pitchshift_get_pitchscale (aubio_pitchshift_t * p)
+{
+ return p->pitchscale;
+}
+
+uint_t
+aubio_pitchshift_set_transpose(aubio_pitchshift_t * p, smpl_t transpose)
+{
+ if (transpose >= -24. && transpose <= 24.) {
+ smpl_t pitchscale = POW(2., transpose / 12.);
+ return aubio_pitchshift_set_pitchscale(p, pitchscale);
+ } else {
+ AUBIO_ERR("pitchshift: could not set transpose to '%f',"
+ " should be in the range [-24; 24.].\n", transpose);
+ return AUBIO_FAIL;
+ }
+}
+
+smpl_t
+aubio_pitchshift_get_transpose(aubio_pitchshift_t * p)
+{
+ return 12. * LOG(p->pitchscale) / LOG(2.0);
+}
+
+void
+aubio_pitchshift_do (aubio_pitchshift_t * p, const fvec_t * in, fvec_t * out)
+{
+ // third parameter is always 0 since we are never expecting a final frame
+ rubberband_process(p->rb, (const float* const*)&(in->data), p->hopsize, 0);
+ if (rubberband_available(p->rb) >= (int)p->hopsize) {
+ rubberband_retrieve(p->rb, (float* const*)&(out->data), p->hopsize);
+ } else {
+ AUBIO_WRN("pitchshift: catching up with zeros"
+ ", only %d available, needed: %d, current pitchscale: %f\n",
+ rubberband_available(p->rb), p->hopsize, p->pitchscale);
+ fvec_zeros(out);
+ }
+}
+
+#endif
--- /dev/null
+++ b/src/effects/rubberband_utils.c
@@ -1,0 +1,150 @@
+
+
+#include "aubio_priv.h"
+
+#ifdef HAVE_RUBBERBAND
+
+#include <rubberband/rubberband-c.h>
+
+// check rubberband is 1.8.1, warn if 1.3
+#if !((RUBBERBAND_API_MAJOR_VERSION >= 2) && \
+ (RUBBERBAND_API_MINOR_VERSION >= 5))
+#warning RubberBandOptionDetectorSoft not available, \
+ please upgrade rubberband to version 1.8.1 or higher
+#define RubberBandOptionDetectorSoft 0x00000000
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+char_t** aubio_split_str(const char_t* str, const char_t sep) {
+ char_t** result = 0;
+ uint_t count = 0;
+ char_t input[PATH_MAX];
+ char_t* in_ptr = input;
+ char_t* last_sep = 0;
+ char_t delim[2]; delim[0] = sep; delim[1] = 0;
+
+ strncpy(input, str, PATH_MAX);
+ input[PATH_MAX - 1] = '\0';
+
+ // count number of elements
+ while (*in_ptr) {
+ if (sep == *in_ptr) {
+ count++;
+ last_sep = in_ptr;
+ }
+ in_ptr++;
+ }
+ // add space for trailing token.
+ count += last_sep < (input + strlen(input) - 1);
+ count++;
+
+ result = AUBIO_ARRAY(char_t*, count);
+ if (result) {
+ uint_t idx = 0;
+ char_t* params = strtok(input, delim);
+ while (params) {
+ // make sure we don't got in the wild
+ if (idx >= count)
+ break;
+ *(result + idx++) = strdup(params);
+ params = strtok(0, delim);
+ }
+ // add null string at the end if needed
+ if (idx < count - 1)
+ *(result + idx) = 0;
+ }
+ return result;
+}
+
+RubberBandOptions aubio_get_rubberband_opts(const char_t *mode)
+{
+ RubberBandOptions rboptions = RubberBandOptionProcessRealTime;
+
+ if ( strcmp(mode,"crispness:0") == 0 ) {
+ rboptions |= RubberBandOptionTransientsSmooth;
+ rboptions |= RubberBandOptionWindowLong;
+ rboptions |= RubberBandOptionPhaseIndependent;
+ } else if ( strcmp(mode, "crispness:1") == 0 ) {
+ rboptions |= RubberBandOptionDetectorSoft;
+ rboptions |= RubberBandOptionTransientsSmooth;
+ rboptions |= RubberBandOptionWindowLong;
+ rboptions |= RubberBandOptionPhaseIndependent;
+ } else if ( strcmp(mode, "crispness:2") == 0 ) {
+ rboptions |= RubberBandOptionTransientsSmooth;
+ rboptions |= RubberBandOptionPhaseIndependent;
+ } else if ( strcmp(mode, "crispness:3") == 0 ) {
+ rboptions |= RubberBandOptionTransientsSmooth;
+ } else if ( strcmp(mode, "crispness:4") == 0 ) {
+ // same as "default"
+ } else if ( strcmp(mode, "crispness:5") == 0 ) {
+ rboptions |= RubberBandOptionTransientsCrisp;
+ } else if ( strcmp(mode, "crispness:6") == 0 ) {
+ rboptions |= RubberBandOptionTransientsCrisp;
+ rboptions |= RubberBandOptionWindowShort;
+ rboptions |= RubberBandOptionPhaseIndependent;
+ } else if ( strcmp(mode, "default") == 0 ) {
+ // nothing to do
+ } else {
+ // attempt to parse a list of options, separated with ','
+ char_t **params = aubio_split_str(mode, ':');
+ uint_t i = 0;
+ if (!params || !params[0]) {
+ // memory failure occurred or empty string was passed
+ AUBIO_ERR("rubberband_utils: failed parsing options\n");
+ rboptions = -1;
+ }
+ while (*(params + i) != NULL) {
+ if ( strcmp(params[i], "ProcessOffline" ) == 0 ) {
+ rboptions = RubberBandOptionProcessOffline;
+ // TODO: add wrapper to rb study(smpl_t *input, uint_t write)
+ AUBIO_ERR("rubberband_utils: RubberBandOptionProcessOffline is not available\n");
+ rboptions = -1;
+ }
+ else if ( strcmp(params[i], "ProcessRealTime" ) == 0 ) rboptions |= RubberBandOptionProcessRealTime;
+ else if ( strcmp(params[i], "StretchElastic" ) == 0 ) rboptions |= RubberBandOptionStretchElastic;
+ else if ( strcmp(params[i], "StretchPrecise" ) == 0 ) rboptions |= RubberBandOptionStretchPrecise;
+ else if ( strcmp(params[i], "TransientsCrisp" ) == 0 ) rboptions |= RubberBandOptionTransientsCrisp;
+ else if ( strcmp(params[i], "TransientsMixed" ) == 0 ) rboptions |= RubberBandOptionTransientsMixed;
+ else if ( strcmp(params[i], "TransientsSmooth" ) == 0 ) rboptions |= RubberBandOptionTransientsSmooth;
+ else if ( strcmp(params[i], "DetectorCompound" ) == 0 ) rboptions |= RubberBandOptionDetectorCompound;
+ else if ( strcmp(params[i], "DetectorPercussive" ) == 0 ) rboptions |= RubberBandOptionDetectorPercussive;
+ else if ( strcmp(params[i], "DetectorSoft" ) == 0 ) rboptions |= RubberBandOptionDetectorSoft;
+ else if ( strcmp(params[i], "PhaseLaminar" ) == 0 ) rboptions |= RubberBandOptionPhaseLaminar;
+ else if ( strcmp(params[i], "PhaseIndependent" ) == 0 ) rboptions |= RubberBandOptionPhaseIndependent;
+ else if ( strcmp(params[i], "ThreadingAuto" ) == 0 ) rboptions |= RubberBandOptionThreadingAuto;
+ else if ( strcmp(params[i], "ThreadingNever" ) == 0 ) rboptions |= RubberBandOptionThreadingNever;
+ else if ( strcmp(params[i], "ThreadingAlways" ) == 0 ) rboptions |= RubberBandOptionThreadingAlways;
+ else if ( strcmp(params[i], "WindowStandard" ) == 0 ) rboptions |= RubberBandOptionWindowStandard;
+ else if ( strcmp(params[i], "WindowShort" ) == 0 ) rboptions |= RubberBandOptionWindowShort;
+ else if ( strcmp(params[i], "WindowLong" ) == 0 ) rboptions |= RubberBandOptionWindowLong;
+ else if ( strcmp(params[i], "SmoothingOff" ) == 0 ) rboptions |= RubberBandOptionSmoothingOff;
+ else if ( strcmp(params[i], "SmoothingOn" ) == 0 ) rboptions |= RubberBandOptionSmoothingOn;
+ else if ( strcmp(params[i], "FormantShifted" ) == 0 ) rboptions |= RubberBandOptionFormantShifted;
+ else if ( strcmp(params[i], "FormantPreserved" ) == 0 ) rboptions |= RubberBandOptionFormantPreserved;
+ else if ( strcmp(params[i], "PitchHighSpeed" ) == 0 ) rboptions |= RubberBandOptionPitchHighSpeed;
+ else if ( strcmp(params[i], "PitchHighQuality" ) == 0 ) rboptions |= RubberBandOptionPitchHighQuality;
+ else if ( strcmp(params[i], "PitchHighConsistency" ) == 0 ) rboptions |= RubberBandOptionPitchHighConsistency;
+ else if ( strcmp(params[i], "ChannelsApart" ) == 0 ) rboptions |= RubberBandOptionChannelsApart;
+ else if ( strcmp(params[i], "ChannelsTogether" ) == 0 ) rboptions |= RubberBandOptionChannelsTogether;
+ else {
+ AUBIO_ERR("rubberband_utils: did not understand option '%s', should be one of: "
+ "StretchElastic|StretchPrecise, TransientsCrisp|TransientsMixed|TransientsSmooth, "
+ "DetectorCompound|DetectorPercussive|DetectorSoft, PhaseLaminar|PhaseIndependent, "
+ "ThreadingAuto|ThreadingNever|ThreadingAlways, WindowStandard|WindowLong|WindowShort, "
+ "SmoothingOn|SmoothingOff, FormantShifted|FormantPreserved, "
+ "PitchHighSpeed|PitchHighQuality|PitchHighConsistency, ChannelsApart|ChannelsTogether\n"
+ , params[i]);
+ rboptions = -1;
+ }
+ AUBIO_FREE(params[i]);
+ i++;
+ }
+ AUBIO_FREE(params);
+ }
+ return rboptions;
+}
+
+#endif
--- /dev/null
+++ b/src/effects/timestretch.h
@@ -1,0 +1,198 @@
+/*
+ Copyright (C) 2016 Paul Brossier <piem@aubio.org>
+
+ This file is part of aubio.
+
+ aubio is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ aubio is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with aubio. If not, see <http://www.gnu.org/licenses/>.
+
+*/
+
+#ifndef AUBIO_TIMESTRETCH_H
+#define AUBIO_TIMESTRETCH_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/** \file
+
+ time stretching object
+
+ ::aubio_timestretch_t can be used to open a source file, read samples from
+ it, time-stretch them, and write out the modified samples.
+
+ The time-stretching factor can be changed at any time using
+ aubio_timestretch_set_stretch().
+
+ A transposition can also be applied and changed at any time with
+ aubio_timestretch_set_transpose().
+
+ \example effects/test-timestretch.c
+
+*/
+
+/** time stretching object */
+typedef struct _aubio_timestretch_t aubio_timestretch_t;
+
+/** execute time stretching on an input signal frame
+
+ \param o time stretching object as returned by new_aubio_timestretch()
+ \param out timestretched output of size [hop_size]
+ \param read number of frames actually wrote out
+
+*/
+void aubio_timestretch_do (aubio_timestretch_t * o, fvec_t * out,
+ uint_t * read);
+
+/** deletion of the time stretching object
+
+ \param o time stretching object as returned by new_aubio_timestretch()
+
+*/
+void del_aubio_timestretch (aubio_timestretch_t * o);
+
+/** creation of the time stretching object
+
+ \param method time stretching algorithm ("default")
+ \param stretch initial time stretching factor
+ \param hop_size block size at which the frames should be produced
+ \param samplerate sampling rate of the signal
+
+ \return newly created ::aubio_timestretch_t
+
+*/
+aubio_timestretch_t *new_aubio_timestretch (const char_t * method,
+ smpl_t stretch, uint_t hop_size, uint_t samplerate);
+
+/** push length samples from in to time stretching object
+
+ \param o time stretching object as returned by ::new_aubio_timestretch()
+ \param in input vector of new samples to push to time stretching object
+ \param length number of new samples to push from input vector
+
+ \return number of currently available samples
+
+ */
+sint_t aubio_timestretch_push(aubio_timestretch_t * o, fvec_t *in,
+ uint_t length);
+
+/** get number of currently available samples from time stretching object
+
+ \param o time stretching object as returned by ::new_aubio_timestretch()
+
+ \return number of currently available samples
+
+ */
+sint_t aubio_timestretch_get_available(aubio_timestretch_t * o);
+
+/** get the latency of the time stretching object, in samples
+
+ \param o time stretching object as returned by ::new_aubio_timestretch()
+
+ \return latency of the time stretching object in samples
+
+*/
+uint_t aubio_timestretch_get_latency (aubio_timestretch_t * o);
+
+/** get the samplerate of the time stretching object
+
+ Call after new_aubio_timestretch() was called with 0 to match the original
+ samplerate of the input file.
+
+ \param o time stretching object as returned by new_aubio_timestretch()
+
+ \return samplerate of the time stretching object
+
+ */
+uint_t aubio_timestretch_get_samplerate (aubio_timestretch_t * o);
+
+/** set the stretching ratio of the time stretching object
+
+ \param o time stretching object as returned by new_aubio_timestretch()
+ \param stretch new time stretching ratio of the time stretching object
+ (should be in the range [0.025; 10.])
+
+ \return 0 if successfull, non-zero otherwise
+
+*/
+uint_t aubio_timestretch_set_stretch (aubio_timestretch_t * o, smpl_t stretch);
+
+/** get the transposition of the time stretching object, in semitones
+
+ \param o time stretching object as returned by ::new_aubio_timestretch()
+
+ \return time stretching ratio of the time stretching object, in the range
+ [0.025; 10.]
+
+*/
+smpl_t aubio_timestretch_get_stretch (aubio_timestretch_t * o);
+
+/** set the pitch scale of the time stretching object
+
+ \param o time stretching object as returned by new_aubio_timestretch()
+ \param pitchscale new pitch scale of the time stretching object
+
+ pitchscale is a frequency ratio. It should be in the range [0.25, 4].
+
+ \return 0 if successfull, non-zero otherwise
+
+*/
+uint_t aubio_timestretch_set_pitchscale (aubio_timestretch_t * o,
+ smpl_t pitchscale);
+
+/** get the pitchscale of the time stretching object
+
+ \param o time stretching object as returned by ::new_aubio_timestretch()
+
+ \return pitchscale of the time stretching object
+
+*/
+smpl_t aubio_timestretch_get_pitchscale (aubio_timestretch_t * o);
+
+/** set the transposition of the time stretching object, in semitones
+
+ \param o time stretching object as returned by new_aubio_timestretch()
+
+ \param transpose new pitch transposition of the time stretching object,
+ expressed in semitones (should be in the range [-24;+24])
+
+ \return 0 if successfull, non-zero otherwise
+
+*/
+uint_t aubio_timestretch_set_transpose (aubio_timestretch_t * o,
+ smpl_t transpose);
+
+/** get the transposition of the time stretching object, in semitones
+
+ \param o time stretching object as returned by ::new_aubio_timestretch()
+
+ \return transposition of the time stretching object, in semitones
+
+*/
+smpl_t aubio_timestretch_get_transpose (aubio_timestretch_t * o);
+
+/** reset the time stretching object
+
+ \param o time stretching object as returned by ::new_aubio_timestretch()
+
+ \return 0 on success, non-zero otherwise
+
+*/
+uint_t aubio_timestretch_reset(aubio_timestretch_t * o);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* AUBIO_TIMESTRETCH_H */
--- /dev/null
+++ b/src/effects/timestretch_dummy.c
@@ -1,0 +1,102 @@
+/*
+ Copyright (C) 2016 Paul Brossier <piem@aubio.org>
+
+ This file is part of aubio.
+
+ aubio is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ aubio is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with aubio. If not, see <http://www.gnu.org/licenses/>.
+
+*/
+
+#include "aubio_priv.h"
+
+#ifndef HAVE_RUBBERBAND
+
+#include "fvec.h"
+#include "effects/timestretch.h"
+
+// TODO fallback time stretching implementation
+
+struct _aubio_timestretch_t
+{
+ void *dummy;
+};
+
+void
+aubio_timestretch_do (aubio_timestretch_t * o UNUSED, fvec_t * out UNUSED,
+ uint_t * read UNUSED)
+{
+}
+
+void del_aubio_timestretch (aubio_timestretch_t * o UNUSED) {
+}
+
+aubio_timestretch_t *
+new_aubio_timestretch (const char_t * method UNUSED,
+ smpl_t pitchscale UNUSED, uint_t hop_size UNUSED, uint_t samplerate UNUSED)
+{
+ AUBIO_ERR ("timestretch: aubio was not compiled with rubberband\n");
+ return NULL;
+}
+
+uint_t aubio_timestretch_set_stretch (aubio_timestretch_t * o UNUSED, smpl_t stretch UNUSED)
+{
+ return AUBIO_FAIL;
+}
+
+smpl_t aubio_timestretch_get_stretch (aubio_timestretch_t * o UNUSED)
+{
+ return 1.;
+}
+
+uint_t aubio_timestretch_set_pitchscale (aubio_timestretch_t * o UNUSED, smpl_t pitchscale UNUSED)
+{
+ return AUBIO_FAIL;
+}
+
+uint_t aubio_timestretch_get_samplerate (aubio_timestretch_t * o UNUSED) {
+ return 0;
+}
+
+smpl_t aubio_timestretch_get_pitchscale (aubio_timestretch_t * o UNUSED)
+{
+ return 1.;
+}
+
+uint_t aubio_timestretch_set_transpose (aubio_timestretch_t * o UNUSED, smpl_t transpose UNUSED) {
+ return AUBIO_FAIL;
+}
+
+smpl_t aubio_timestretch_get_transpose (aubio_timestretch_t * o UNUSED) {
+ return 0.;
+}
+
+uint_t aubio_timestretch_get_latency (aubio_timestretch_t * o UNUSED) {
+ return 0.;
+}
+
+uint_t aubio_timestretch_reset(aubio_timestretch_t *o UNUSED) {
+ return AUBIO_FAIL;
+}
+
+sint_t aubio_timestretch_push(aubio_timestretch_t * o UNUSED, fvec_t * in
+ UNUSED, uint_t length UNUSED) {
+ return AUBIO_FAIL;
+}
+
+sint_t aubio_timestretch_get_available(aubio_timestretch_t * o UNUSED) {
+ return AUBIO_FAIL;
+}
+// end of dummy implementation
+
+#endif /* HAVE_RUBBERBAND */
--- /dev/null
+++ b/src/effects/timestretch_rubberband.c
@@ -1,0 +1,261 @@
+/*
+ Copyright (C) 2016 Paul Brossier <piem@aubio.org>
+
+ This file is part of aubio.
+
+ aubio is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ aubio is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with aubio. If not, see <http://www.gnu.org/licenses/>.
+
+*/
+
+#include "aubio_priv.h"
+
+#ifdef HAVE_RUBBERBAND
+
+#include "fvec.h"
+#include "fmat.h"
+#include "io/source.h"
+#include "effects/timestretch.h"
+
+#include <rubberband/rubberband-c.h>
+
+#define MIN_STRETCH_RATIO 0.025
+#define MAX_STRETCH_RATIO 40.
+
+#define HAVE_THREADS 1
+#if 0
+#undef HAVE_THREADS
+#endif
+
+#ifdef HAVE_THREADS
+#include <pthread.h>
+#endif
+
+/** generic time stretching structure */
+struct _aubio_timestretch_t
+{
+ uint_t samplerate; /**< samplerate */
+ uint_t hopsize; /**< hop size */
+ smpl_t stretchratio; /**< time ratio */
+ smpl_t pitchscale; /**< pitch scale */
+
+ RubberBandState rb;
+ RubberBandOptions rboptions;
+};
+
+extern RubberBandOptions aubio_get_rubberband_opts(const char_t *mode);
+
+//static void aubio_timestretch_warmup (aubio_timestretch_t * p);
+
+aubio_timestretch_t *
+new_aubio_timestretch (const char_t * mode, smpl_t stretchratio, uint_t hopsize,
+ uint_t samplerate)
+{
+ aubio_timestretch_t *p = AUBIO_NEW (aubio_timestretch_t);
+ p->hopsize = hopsize;
+ p->pitchscale = 1.;
+
+ if ((sint_t)hopsize <= 0) {
+ AUBIO_ERR("timestretch: hopsize should be > 0, got %d\n", hopsize);
+ goto beach;
+ }
+
+ if ((sint_t)samplerate <= 0) {
+ AUBIO_ERR("timestretch: samplerate should be > 0, got %d\n", samplerate);
+ goto beach;
+ }
+
+ if (stretchratio <= MAX_STRETCH_RATIO && stretchratio >= MIN_STRETCH_RATIO) {
+ p->stretchratio = stretchratio;
+ } else {
+ AUBIO_ERR("timestretch: stretchratio should be in the range [%.3f, %.3f], got %f\n",
+ MIN_STRETCH_RATIO, MAX_STRETCH_RATIO, stretchratio);
+ goto beach;
+ }
+
+ p->rboptions = aubio_get_rubberband_opts(mode);
+ if (p->rboptions < 0) {
+ AUBIO_ERR("timestretch: unknown time stretching method %s\n", mode);
+ goto beach;
+ }
+
+ p->rb = rubberband_new(samplerate, 1, p->rboptions, p->stretchratio, p->pitchscale);
+ if (!p->rb) goto beach;
+
+ p->samplerate = samplerate;
+
+ //aubio_timestretch_warmup(p);
+
+ return p;
+
+beach:
+ del_aubio_timestretch(p);
+ return NULL;
+}
+
+#if 0
+static void
+aubio_timestretch_warmup (aubio_timestretch_t * p)
+{
+ // warm up rubber band
+ //AUBIO_WRN("timestretch: warming-up\n");
+ unsigned int latency = MAX(p->hopsize, rubberband_get_latency(p->rb));
+ fvec_t *input = new_fvec(p->hopsize);
+ while (aubio_timestretch_push(p, input, input->length) < (int)latency) {
+ //sint_t available = aubio_timestretch_get_available(p);
+ //AUBIO_WRN("timestretch: warmup got %d, latency: %d\n", available, latency);
+ }
+ del_fvec(input);
+}
+#endif
+
+void
+del_aubio_timestretch (aubio_timestretch_t * p)
+{
+ if (p->rb) {
+ rubberband_delete(p->rb);
+ }
+ AUBIO_FREE (p);
+}
+
+uint_t
+aubio_timestretch_get_samplerate (aubio_timestretch_t * p)
+{
+ return p->samplerate;
+}
+
+uint_t aubio_timestretch_get_latency (aubio_timestretch_t * p) {
+ return rubberband_get_latency(p->rb);
+}
+
+uint_t
+aubio_timestretch_set_stretch (aubio_timestretch_t * p, smpl_t stretch)
+{
+ if (!p->rb) {
+ AUBIO_ERR("timestretch: could not set stretch ratio,"
+ " rubberband not created\n");
+ return AUBIO_FAIL;
+ }
+ if (stretch >= MIN_STRETCH_RATIO && stretch <= MAX_STRETCH_RATIO) {
+ p->stretchratio = stretch;
+ rubberband_set_time_ratio(p->rb, 1./p->stretchratio);
+ return AUBIO_OK;
+ } else {
+ AUBIO_ERR("timestretch: could not set stretch ratio to '%f',"
+ " should be in the range [%.2f, %.2f].\n", stretch,
+ MIN_STRETCH_RATIO, MAX_STRETCH_RATIO);
+ return AUBIO_FAIL;
+ }
+}
+
+smpl_t
+aubio_timestretch_get_stretch (aubio_timestretch_t * p)
+{
+ return p->stretchratio;
+}
+
+uint_t
+aubio_timestretch_set_pitchscale (aubio_timestretch_t * p, smpl_t pitchscale)
+{
+ if (!p->rb) {
+ AUBIO_ERR("timestretch: could not set pitch scale,"
+ " rubberband not created\n");
+ return AUBIO_FAIL;
+ }
+ if (pitchscale >= 0.0625 && pitchscale <= 4.) {
+ p->pitchscale = pitchscale;
+ rubberband_set_pitch_scale(p->rb, p->pitchscale);
+ return AUBIO_OK;
+ } else {
+ AUBIO_ERR("timestretch: could not set pitchscale to '%f',"
+ " should be in the range [0.0625, 4.].\n", pitchscale);
+ return AUBIO_FAIL;
+ }
+}
+
+smpl_t
+aubio_timestretch_get_pitchscale (aubio_timestretch_t * p)
+{
+ return p->pitchscale;
+}
+
+uint_t
+aubio_timestretch_set_transpose(aubio_timestretch_t * p, smpl_t transpose)
+{
+ if (transpose >= -24. && transpose <= 24.) {
+ smpl_t pitchscale = POW(2., transpose / 12.);
+ return aubio_timestretch_set_pitchscale(p, pitchscale);
+ } else {
+ AUBIO_ERR("timestretch: could not set transpose to '%f',"
+ " should be in the range [-24; 24].\n", transpose);
+ return AUBIO_FAIL;
+ }
+}
+
+smpl_t
+aubio_timestretch_get_transpose(aubio_timestretch_t * p)
+{
+ return 12. * LOG(p->pitchscale) / LOG(2.0);
+}
+
+sint_t
+aubio_timestretch_push(aubio_timestretch_t *p, fvec_t *input, uint_t length)
+{
+ // push new samples to rubberband, return available
+ int available;
+ int eof = (input->length != length) ? 1 : 0;
+ rubberband_process(p->rb, (const float* const*)&(input->data), length, eof);
+ available = rubberband_available(p->rb);
+ //AUBIO_WRN("timestretch: processed %d, %d available, eof: %d\n",
+ // length, available, eof);
+ return available;
+}
+
+sint_t
+aubio_timestretch_get_available(aubio_timestretch_t *p) {
+ return rubberband_available(p->rb);
+}
+
+void
+aubio_timestretch_do(aubio_timestretch_t * p, fvec_t * out, uint_t * read)
+{
+ // now retrieve the samples and write them into out->data
+ int available = rubberband_available(p->rb);
+ if (available >= (int)out->length) {
+ rubberband_retrieve(p->rb, (float* const*)&(out->data), out->length);
+ *read = out->length;
+ } else if (available > 0) {
+ // this occurs each time the end of file is reached
+ //AUBIO_WRN("timestretch: short read\n");
+ rubberband_retrieve(p->rb, (float* const*)&(out->data), available);
+ fvec_t zeros; zeros.length = out->length - available; zeros.data = out->data + available;
+ fvec_zeros(&zeros);
+ *read = available;
+ } else {
+ // this may occur if the previous was a short read available == hopsize
+ fvec_zeros(out);
+ *read = 0;
+ }
+}
+
+uint_t
+aubio_timestretch_reset(aubio_timestretch_t *p)
+{
+ uint_t err = AUBIO_OK;
+ if (p->rb) {
+ rubberband_reset(p->rb);
+ }
+ return err;
+}
+
+#endif
--- a/src/wscript_build
+++ b/src/wscript_build
@@ -6,6 +6,7 @@
uselib += ['INTEL_IPP']
uselib += ['SAMPLERATE']
uselib += ['SNDFILE']
+uselib += ['RUBBERBAND']
uselib += ['AVCODEC']
uselib += ['AVFORMAT']
uselib += ['SWRESAMPLE']
--- /dev/null
+++ b/tests/src/effects/test-pitchshift.c
@@ -1,0 +1,110 @@
+#define AUBIO_UNSTABLE 1
+#include <aubio.h>
+#include "utils_tests.h"
+
+int test_wrong_params(void);
+
+int main (int argc, char **argv)
+{
+ sint_t err = 0;
+
+ if (argc < 3) {
+ PRINT_ERR("not enough arguments, running tests\n");
+ err = test_wrong_params();
+ PRINT_MSG("usage: %s <input_path> <output_path> [transpose] ", argv[0]);
+ PRINT_MSG("[mode] [hop_size] [samplerate]\n");
+ PRINT_MSG(" with [transpose] a number of semi tones in the range "
+ " [-24, 24], and [mode] in 'default', 'crispness:0',"
+ " 'crispness:1', ... 'crispness:6'\n");
+ return err;
+ }
+
+#ifdef HAVE_RUBBERBAND
+ uint_t samplerate = 0;
+ uint_t hop_size = 64;
+ smpl_t transpose = 0.;
+ uint_t n_frames = 0, read = 0;
+
+ char_t *source_path = argv[1];
+ char_t *sink_path = argv[2];
+ char_t *mode = "default";
+
+ transpose = 0.;
+
+ if ( argc >= 4 ) transpose = atof(argv[3]);
+ if ( argc >= 5 ) mode = argv[4];
+ if ( argc >= 6 ) hop_size = atoi(argv[5]);
+ if ( argc >= 7 ) samplerate = atoi(argv[6]);
+
+ fvec_t *vec = new_fvec(hop_size);
+ fvec_t *out = new_fvec(hop_size);
+ if (!vec) { err = 1; goto beach_fvec; }
+
+ aubio_source_t *i = new_aubio_source(source_path, samplerate, hop_size);
+ if (!i) { err = 1; goto beach_source; }
+
+ if (samplerate == 0 ) samplerate = aubio_source_get_samplerate(i);
+
+ aubio_sink_t *o = new_aubio_sink(sink_path, samplerate);
+ if (!o) { err = 1; goto beach_sink; }
+
+ aubio_pitchshift_t *ps =
+ new_aubio_pitchshift(mode, transpose, hop_size, samplerate);
+ if (!ps) { err = 1; goto beach_pitchshift; }
+
+ do {
+ aubio_source_do(i, vec, &read);
+ //aubio_pitchshift_set_transpose(ps, tranpose);
+ aubio_pitchshift_do(ps, vec, out);
+ aubio_sink_do(o, out, read);
+ n_frames += read;
+ } while ( read == hop_size );
+
+ PRINT_MSG("read %d frames at %dHz (%d blocks) from %s\n",
+ n_frames, samplerate, n_frames / hop_size,
+ source_path);
+ PRINT_MSG("wrote to %s with hop_size: %d, samplerate: %d, latency %d\n",
+ sink_path, hop_size, samplerate, aubio_pitchshift_get_latency(ps));
+
+ del_aubio_pitchshift(ps);
+beach_pitchshift:
+ del_aubio_sink(o);
+beach_sink:
+ del_aubio_source(i);
+beach_source:
+ del_fvec(vec);
+ del_fvec(out);
+beach_fvec:
+ aubio_cleanup();
+#else
+ err = 0;
+ PRINT_ERR("aubio was not compiled with rubberband\n");
+#endif
+ return err;
+}
+
+int test_wrong_params(void)
+{
+ const char_t *mode = "default";
+ smpl_t transpose = 0.;
+ uint_t hop_size = 256;
+ uint_t samplerate = 44100;
+
+ if (new_aubio_pitchshift("??", transpose, hop_size, samplerate)) return 1;
+ if (new_aubio_pitchshift(mode, 28., hop_size, samplerate)) return 1;
+ if (new_aubio_pitchshift(mode, transpose, 0, samplerate)) return 1;
+ if (new_aubio_pitchshift(mode, transpose, hop_size, 0)) return 1;
+
+ aubio_pitchshift_t *p = new_aubio_pitchshift(mode, transpose,
+ hop_size, samplerate);
+#ifdef HAVE_RUBBERBAND
+ if (!p) return 1;
+ if (!aubio_pitchshift_set_pitchscale(p, 0.1)) return 1;
+ if (!aubio_pitchshift_set_transpose(p, -30)) return 1;
+ del_aubio_pitchshift(p);
+#else
+ if (p) return 1;
+#endif
+
+ return run_on_default_source_and_sink(main);
+}
--- /dev/null
+++ b/tests/src/effects/test-timestretch.c
@@ -1,0 +1,153 @@
+#define AUBIO_UNSTABLE 1
+#include <aubio.h>
+#include "utils_tests.h"
+
+int test_wrong_params(void);
+
+int main (int argc, char **argv)
+{
+ sint_t err = 0;
+
+ if (argc < 3 || argc >= 9) {
+ PRINT_ERR("wrong number of arguments, running tests\n");
+ err = test_wrong_params();
+ PRINT_MSG("usage: %s <input_path> <output_path> <stretch> [transpose] [mode] [hop_size] [samplerate]\n", argv[0]);
+ PRINT_MSG(" with <stretch> a time stretching ratio in the range [0.025, 10.]\n");
+ PRINT_MSG(" [transpose] a number of semi tones in the range [-24, 24]\n");
+ PRINT_MSG(" and [mode] in 'default', 'crispness:0', ..., 'crispness:6'\n");
+ return err;
+ }
+
+#ifdef HAVE_RUBBERBAND
+ uint_t samplerate = 0; // using source samplerate
+ uint_t hop_size = 64;
+ smpl_t transpose = 0.;
+ smpl_t stretch = 1.;
+ uint_t n_frames = 0, read = 0;
+ uint_t eof = 0, source_read = 0;
+
+ char_t *source_path = argv[1];
+ char_t *sink_path = argv[2];
+ char_t *mode = "default";
+
+ if ( argc >= 4 ) stretch = atof(argv[3]);
+ if ( argc >= 5 ) transpose = atof(argv[4]);
+ if ( argc >= 6 ) mode = argv[5];
+ if ( argc >= 7 ) hop_size = atoi(argv[6]);
+ if ( argc >= 8 ) samplerate = atoi(argv[7]);
+
+ uint_t source_hopsize = 2048;
+ aubio_source_t *s = new_aubio_source(source_path, samplerate, source_hopsize);
+ if (!s) { err = 1; goto beach_source; }
+ if (samplerate == 0) samplerate = aubio_source_get_samplerate(s);
+
+ fvec_t *in = new_fvec(source_hopsize);
+ fvec_t *out = new_fvec(hop_size);
+ if (!out || !in) { err = 1; goto beach_fvec; }
+
+ aubio_timestretch_t *ps = new_aubio_timestretch(mode, stretch, hop_size,
+ samplerate);
+ if (!ps) { err = 1; goto beach_timestretch; }
+ //if (samplerate == 0 ) samplerate = aubio_timestretch_get_samplerate(ps);
+
+ aubio_sink_t *o = new_aubio_sink(sink_path, samplerate);
+ if (!o) { err = 1; goto beach_sink; }
+
+ if (transpose != 0) aubio_timestretch_set_transpose(ps, transpose);
+
+ do {
+ //aubio_timestretch_set_stretch(ps, stretch);
+ //aubio_timestretch_set_transpose(ps, transpose);
+
+ while (aubio_timestretch_get_available(ps) < (sint_t)hop_size && !eof) {
+ aubio_source_do(s, in, &source_read);
+ aubio_timestretch_push(ps, in, source_read);
+ if (source_read < in->length) eof = 1;
+ }
+#if 0
+ if (n_frames == hop_size * 200) {
+ PRINT_MSG("sampler: setting stretch gave %d\n",
+ aubio_timestretch_set_stretch(ps, 2.) );
+ PRINT_MSG("sampler: getting stretch gave %f\n",
+ aubio_timestretch_get_stretch(ps) );
+ PRINT_MSG("sampler: setting transpose gave %d\n",
+ aubio_timestretch_set_transpose(ps, 12.) );
+ PRINT_MSG("sampler: getting transpose gave %f\n",
+ aubio_timestretch_get_transpose(ps) );
+ }
+#endif
+ aubio_timestretch_do(ps, out, &read);
+ aubio_sink_do(o, out, read);
+ n_frames += read;
+ } while ( read == hop_size );
+
+ PRINT_MSG("wrote %d frames at %dHz (%d blocks) from %s written to %s\n",
+ n_frames, samplerate, n_frames / hop_size,
+ source_path, sink_path);
+
+ del_aubio_sink(o);
+beach_sink:
+ del_aubio_timestretch(ps);
+beach_timestretch:
+ del_fvec(out);
+ del_fvec(in);
+beach_fvec:
+ del_aubio_source(s);
+beach_source:
+#else
+ err = 0;
+ PRINT_ERR("aubio was not compiled with rubberband\n");
+#endif
+ return err;
+}
+
+int test_wrong_params(void)
+{
+ const char_t *mode = "default";
+ smpl_t stretch = 1.;
+ uint_t hop_size = 256;
+ uint_t samplerate = 44100;
+
+ if (new_aubio_timestretch("ProcessOffline:?:", stretch, hop_size, samplerate)) return 1;
+ if (new_aubio_timestretch("", stretch, hop_size, samplerate)) return 1;
+ if (new_aubio_timestretch(mode, 41., hop_size, samplerate)) return 1;
+ if (new_aubio_timestretch(mode, stretch, 0, samplerate)) return 1;
+ if (new_aubio_timestretch(mode, stretch, hop_size, 0)) return 1;
+
+ aubio_timestretch_t *p = new_aubio_timestretch(mode, stretch, hop_size,
+ samplerate);
+#ifdef HAVE_RUBBERBAND
+ if (!p) return 1;
+
+ if (aubio_timestretch_get_latency(p) == 0) return 1;
+
+ if (aubio_timestretch_get_samplerate(p) != samplerate) return 1;
+
+ aubio_timestretch_reset(p);
+
+ if (aubio_timestretch_get_transpose(p) != 0) return 1;
+ if (aubio_timestretch_set_transpose(p, 2.)) return 1;
+ if (fabs(aubio_timestretch_get_transpose(p) - 2.) >= 1e-6) return 1;
+ if (!aubio_timestretch_set_transpose(p, 200.)) return 1;
+ if (!aubio_timestretch_set_transpose(p, -200.)) return 1;
+ if (aubio_timestretch_set_transpose(p, 0.)) return 1;
+
+ if (aubio_timestretch_get_pitchscale(p) != 1) return 1;
+ if (aubio_timestretch_set_pitchscale(p, 2.)) return 1;
+ if (fabs(aubio_timestretch_get_pitchscale(p) - 2.) >= 1e-6) return 1;
+ if (!aubio_timestretch_set_pitchscale(p, 0.)) return 1;
+ if (!aubio_timestretch_set_pitchscale(p, 6.)) return 1;
+
+ if (aubio_timestretch_get_stretch(p) != stretch) return 1;
+ if (aubio_timestretch_set_stretch(p, 2.)) return 1;
+ if (fabs(aubio_timestretch_get_stretch(p) - 2.) >= 1e-6) return 1;
+ if (!aubio_timestretch_set_stretch(p, 0.)) return 1;
+ if (!aubio_timestretch_set_stretch(p, 41.)) return 1;
+
+ del_aubio_timestretch(p);
+#else
+ if (p) return 1;
+#endif
+
+ return run_on_default_source_and_sink(main);
+}
--- a/wscript
+++ b/wscript
@@ -81,6 +81,9 @@
add_option_enable_disable(ctx, 'samplerate', default = None,
help_str = 'compile with samplerate (auto)',
help_disable_str = 'disable samplerate')
+ add_option_enable_disable(ctx, 'rubberband', default = None,
+ help_str = 'compile with rubberband (auto)',
+ help_disable_str = 'disable rubberband')
add_option_enable_disable(ctx, 'memcpy', default = True,
help_str = 'use memcpy hacks (default)',
help_disable_str = 'do not use memcpy hacks')
@@ -140,6 +143,7 @@
'sndfile',
'samplerate',
'jack',
+ 'rubberband',
'avcodec',
'blas',
'fftw3',
@@ -415,6 +419,12 @@
ctx.check_cfg(package = 'samplerate',
args = '--cflags --libs samplerate >= 0.0.15',
mandatory = ctx.options.enable_samplerate)
+
+ # check for librubberband
+ if (ctx.options.enable_rubberband != False):
+ ctx.check_cfg(package = 'rubberband', atleast_version = '1.3',
+ args = '--cflags --libs',
+ mandatory = ctx.options.enable_rubberband)
# check for jack
if (ctx.options.enable_jack != False):