From 80b5bbd3d2deadcf448827a9daa043cb9c89d66e Mon Sep 17 00:00:00 2001 From: Paul Brossier Date: Sat, 17 Sep 2016 01:36:06 +0200 Subject: [PATCH] src/effects/pitchshift.h: add a pitch shifter based on rubberband --- src/aubio.h | 1 + src/effects/pitchshift.c | 178 ++++++++++++++++++++++++++++++++++++ src/effects/pitchshift.h | 112 +++++++++++++++++++++++ tests/src/effects/test-pitchshift.c | 76 +++++++++++++++ 4 files changed, 367 insertions(+) create mode 100644 src/effects/pitchshift.c create mode 100644 src/effects/pitchshift.h create mode 100644 tests/src/effects/test-pitchshift.c diff --git a/src/aubio.h b/src/aubio.h index 0d2b416c..99ad323f 100644 --- a/src/aubio.h +++ b/src/aubio.h @@ -207,6 +207,7 @@ extern "C" #include "pitch/pitchfcomb.h" #include "pitch/pitchspecacf.h" #include "tempo/beattracking.h" +#include "effects/pitchshift.h" #include "utils/scale.h" #include "utils/hist.h" #endif diff --git a/src/effects/pitchshift.c b/src/effects/pitchshift.c new file mode 100644 index 00000000..25e6960f --- /dev/null +++ b/src/effects/pitchshift.c @@ -0,0 +1,178 @@ +/* + Copyright (C) 2016 Paul Brossier + + 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 . + +*/ + +#include "config.h" +#include "aubio_priv.h" +#include "fvec.h" +#include "effects/pitchshift.h" + +#ifdef HAVE_RUBBERBAND + +#include "rubberband/rubberband-c.h" + +/** generic pitch shifting structure */ +struct _aubio_pitchshift_t +{ + uint_t samplerate; /**< samplerate */ + uint_t hopsize; /**< hop size */ + smpl_t timeratio; /**< time ratio */ + smpl_t pitchscale; /**< pitch scale */ + + RubberBandState rb; + RubberBandOptions rboptions; +}; + +aubio_pitchshift_t * +new_aubio_pitchshift (const char_t * mode, + smpl_t pitchscale, uint_t hopsize, uint_t samplerate) +{ + aubio_pitchshift_t *p = AUBIO_NEW (aubio_pitchshift_t); + int available = 0; unsigned int latency = 0; + p->samplerate = samplerate; + if (strcmp (mode, "default") != 0) { + AUBIO_ERR ("unknown pitch shifting method %s\n", mode); + goto beach; + } + //p->mode = pitch_type; + p->hopsize = hopsize; + p->timeratio = 1.; + p->pitchscale = pitchscale; + + p->rboptions = RubberBandOptionProcessRealTime || RubberBandOptionPitchHighConsistency; + p->rboptions = p->rboptions || RubberBandOptionTransientsCrisp; + p->rb = rubberband_new(samplerate, 1, p->rboptions, p->timeratio, p->pitchscale); + rubberband_set_max_process_size(p->rb, p->hopsize * 4); + //rubberband_set_debug_level(p->rb, 10); + + latency = MAX(rubberband_get_latency(p->rb), p->hopsize); + + // warm up + 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); +#if 0 + int samples_required = rubberband_get_samples_required(p->rb); + AUBIO_DBG("pitchshift: warmup " + "samples_required: %d, available: %d, hopsize: %d, latency: %d\n", + samples_required, available, p->hopsize, latency); +#endif + } + del_fvec(zeros); + + 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_set_pitchscale (aubio_pitchshift_t * p, smpl_t pitchscale) +{ + if (pitchscale >= 0.0625 && 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 %.2f\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 %.2f\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) +{ + int output = 0; + // this may occur when RubberBandStretcher initialPitchScale is changed + while (rubberband_available(p->rb) <= (int)p->hopsize) { + //AUBIO_WRN("pitchshift: catching up, only %d available\n", rubberband_available(p->rb)); + rubberband_process(p->rb, (const float* const*)&(in->data), 0, output); + } + rubberband_process(p->rb, (const float* const*)&(in->data), p->hopsize, output); + rubberband_retrieve(p->rb, (float* const*)&(out->data), p->hopsize); +} + +#else +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.; +} +#endif /* HAVE_RUBBERBAND */ diff --git a/src/effects/pitchshift.h b/src/effects/pitchshift.h new file mode 100644 index 00000000..48a584b7 --- /dev/null +++ b/src/effects/pitchshift.h @@ -0,0 +1,112 @@ +/* + Copyright (C) 2016 Paul Brossier + + 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 . + +*/ + +#ifndef AUBIO_PITCHSHIFT_H +#define AUBIO_PITCHSHIFT_H + +#ifdef __cplusplus +extern "C" { +#endif + +/** \file + + Pitch shifting object + + This file ... + + \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 + \param buf_size size of the input buffer to analyse + \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 pitchscale, uint_t hop_size, uint_t samplerate); + +/** 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 + + \return 0 if successfull, non-zero otherwise + +*/ +uint_t aubio_pitchshift_set_pitchscale (aubio_pitchshift_t * o, smpl_t pitchscale); + +/** set 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 + + \param o pitch shifting object as returned by new_aubio_pitchshift() + \param transpose new pitch transposition of the pitch shifting object + + \return 0 if successfull, non-zero otherwise + +*/ +uint_t aubio_pitchshift_set_transpose (aubio_pitchshift_t * o, smpl_t transpose); + +/** set the transposition of the pitch shifting object + + \param o pitch shifting object as returned by ::new_aubio_pitchshift() + + \return transposition of the pitch shifting object + +*/ +smpl_t aubio_pitchshift_get_transpose (aubio_pitchshift_t * o); + +#ifdef __cplusplus +} +#endif + +#endif /* AUBIO_PITCHSHIFT_H */ diff --git a/tests/src/effects/test-pitchshift.c b/tests/src/effects/test-pitchshift.c new file mode 100644 index 00000000..46a3db9d --- /dev/null +++ b/tests/src/effects/test-pitchshift.c @@ -0,0 +1,76 @@ +#define AUBIO_UNSTABLE 1 +#include +#include "utils_tests.h" + +int main (int argc, char **argv) +{ + sint_t err = 0; + + if (argc < 3) { + err = 2; + PRINT_ERR("not enough arguments\n"); + PRINT_MSG("usage: %s [samplerate] [hop_size]\n", argv[0]); + return err; + } + +#ifdef HAVE_RUBBERBAND + uint_t samplerate = 0; + uint_t hop_size = 256; + smpl_t pitchscale = 1.; + uint_t n_frames = 0, read = 0; + + char_t *source_path = argv[1]; + char_t *sink_path = argv[2]; + + if ( argc >= 4 ) samplerate = atoi(argv[3]); + if ( argc >= 5 ) hop_size = atoi(argv[4]); + if ( argc >= 6 ) pitchscale = atof(argv[5]); + if ( argc >= 7 ) { + err = 2; + PRINT_ERR("too many arguments\n"); + return err; + } + + 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("default", pitchscale, hop_size, samplerate); + //aubio_pitchshift_set_pitchscale(ps, pitchscale); + + int k = 0; + do { + aubio_source_do(i, vec, &read); + aubio_pitchshift_set_transpose(ps, (float)(k-50) / 100.); + aubio_pitchshift_do(ps, vec, out); + aubio_sink_do(o, out, read); + k ++; + n_frames += read; + } while ( read == hop_size ); + + PRINT_MSG("read %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_pitchshift(ps); + del_aubio_sink(o); +beach_sink: + del_aubio_source(i); +beach_source: + del_fvec(vec); + del_fvec(out); +beach_fvec: +#else /* HAVE_RUBBERBAND */ + err = 3; + PRINT_ERR("aubio was not compiled with rubberband\n"); +#endif /* HAVE_RUBBERBAND */ + return err; +} -- 2.11.0