source: src/effects/timestretch_rubberband.c @ 8856791

feature/timestretch
Last change on this file since 8856791 was 8856791, checked in by Paul Brossier <piem@piem.org>, 22 months ago

src/effects/timestretch_rubberband.c: add initial pthread support

  • Property mode set to 100644
File size: 10.3 KB
Line 
1/*
2  Copyright (C) 2016 Paul Brossier <piem@aubio.org>
3
4  This file is part of aubio.
5
6  aubio is free software: you can redistribute it and/or modify
7  it under the terms of the GNU General Public License as published by
8  the Free Software Foundation, either version 3 of the License, or
9  (at your option) any later version.
10
11  aubio is distributed in the hope that it will be useful,
12  but WITHOUT ANY WARRANTY; without even the implied warranty of
13  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  GNU General Public License for more details.
15
16  You should have received a copy of the GNU General Public License
17  along with aubio.  If not, see <http://www.gnu.org/licenses/>.
18
19*/
20
21#include "config.h"
22
23#ifdef HAVE_RUBBERBAND
24
25#include "aubio_priv.h"
26#include "fvec.h"
27#include "fmat.h"
28#include "io/source.h"
29#include "effects/timestretch.h"
30
31#include "rubberband/rubberband-c.h"
32
33#define MIN_STRETCH_RATIO 0.025
34#define MAX_STRETCH_RATIO 40.
35
36#define HAVE_THREADS 1
37#if 0
38#undef HAVE_THREADS
39#endif
40
41#ifdef HAVE_THREADS
42#include <pthread.h>
43#endif
44
45/** generic time stretching structure */
46struct _aubio_timestretch_t
47{
48  uint_t samplerate;              /**< samplerate */
49  uint_t hopsize;                 /**< hop size */
50  smpl_t stretchratio;            /**< time ratio */
51  smpl_t pitchscale;              /**< pitch scale */
52
53  aubio_source_t *source;
54  uint_t source_hopsize;          /**< hop size at which the source is read */
55  fvec_t *in;
56  uint_t eof;
57
58  RubberBandState rb;
59  RubberBandOptions rboptions;
60
61#ifdef HAVE_THREADS
62  pthread_t read_thread;
63  pthread_mutex_t read_mutex;
64  pthread_cond_t read_avail;
65  pthread_cond_t read_request;
66  sint_t available;
67#endif
68};
69
70extern RubberBandOptions aubio_get_rubberband_opts(const char_t *mode);
71
72static void aubio_timestretch_warmup (aubio_timestretch_t * p);
73static sint_t aubio_timestretch_fetch(aubio_timestretch_t *p, uint_t fetch);
74#ifdef HAVE_THREADS
75static void *aubio_timestretch_readfn(void *p);
76#endif
77
78aubio_timestretch_t *
79new_aubio_timestretch (const char_t * uri, const char_t * mode,
80    smpl_t stretchratio, uint_t hopsize, uint_t samplerate)
81{
82  aubio_timestretch_t *p = AUBIO_NEW (aubio_timestretch_t);
83  p->samplerate = samplerate;
84  p->hopsize = hopsize;
85  //p->source_hopsize = 2048;
86  p->source_hopsize = hopsize;
87  p->pitchscale = 1.;
88  p->eof = 0;
89
90  p->source = new_aubio_source(uri, samplerate, p->source_hopsize);
91  if (!p->source) goto beach;
92  if (samplerate == 0 ) p->samplerate = aubio_source_get_samplerate(p->source);
93
94  p->in = new_fvec(p->source_hopsize);
95
96  if (stretchratio <= MAX_STRETCH_RATIO && stretchratio >= MIN_STRETCH_RATIO) {
97    p->stretchratio = stretchratio;
98  } else {
99    AUBIO_ERR("timestretch: stretchratio should be in the range [%.3f, %.3f], got %f\n",
100        MIN_STRETCH_RATIO, MAX_STRETCH_RATIO, stretchratio);
101    goto beach;
102  }
103
104  p->rboptions = aubio_get_rubberband_opts(mode);
105  if (p->rboptions < 0) {
106    AUBIO_ERR("timestretch: unknown time stretching method %s\n", mode);
107    goto beach;
108  }
109
110  p->rb = rubberband_new(p->samplerate, 1, p->rboptions, p->stretchratio, p->pitchscale);
111  rubberband_set_max_process_size(p->rb, p->source_hopsize);
112  //rubberband_set_debug_level(p->rb, 10);
113
114#ifdef HAVE_THREADS
115  pthread_mutex_init(&p->read_mutex, 0);
116  pthread_cond_init (&p->read_avail, 0);
117  pthread_cond_init (&p->read_request, 0);
118  pthread_create(&p->read_thread, 0, aubio_timestretch_readfn, p);
119  //AUBIO_DBG("timestretch: new_ waiting for warmup, got %d available\n", p->available);
120  pthread_mutex_lock(&p->read_mutex);
121  pthread_cond_wait(&p->read_avail, &p->read_mutex);
122  pthread_mutex_unlock(&p->read_mutex);
123  //AUBIO_DBG("timestretch: new_ warm up success, got %d available\n", p->available);
124#else
125  aubio_timestretch_warmup(p);
126#endif
127
128  return p;
129
130beach:
131  del_aubio_timestretch(p);
132  return NULL;
133}
134
135#ifdef HAVE_THREADS
136void *
137aubio_timestretch_readfn(void *z)
138{
139  aubio_timestretch_t *p = z;
140  // signal main-thread when we are done
141  //AUBIO_WRN("timestretch: read_thread locking, got %d available\n", p->available);
142  pthread_mutex_lock(&p->read_mutex);
143  aubio_timestretch_warmup(p);
144  //AUBIO_WRN("timestretch: signaling warmup\n");
145  pthread_cond_signal(&p->read_avail);
146  //AUBIO_WRN("timestretch: unlocking in readfn\n");
147  pthread_mutex_unlock(&p->read_mutex);
148  AUBIO_WRN("timestretch: entering readfn loop\n");
149  while(1) { //p->available < (int)p->hopsize && p->eof != 1) {
150    //AUBIO_WRN("timestretch: locking in readfn\n");
151    pthread_mutex_lock(&p->read_mutex);
152    p->available = aubio_timestretch_fetch(p, p->hopsize);
153    //AUBIO_WRN("timestretch: read_thread read %d\n", p->available);
154    // signal main-thread when we are done
155    //AUBIO_WRN("timestretch: signaling new read\n");
156    pthread_cond_signal(&p->read_avail);
157    if (p->eof != 1) {
158      pthread_cond_wait(&p->read_request, &p->read_mutex);
159    }
160    if (p->eof == 1) {
161      AUBIO_WRN("timestretch: read_thread eof reached %d, %d/%d\n", p->available,
162        p->hopsize, p->source_hopsize);
163      pthread_mutex_unlock(&p->read_mutex);
164      break;
165    }
166    //AUBIO_WRN("timestretch: unlocking in readfn\n");
167    pthread_mutex_unlock(&p->read_mutex);
168  }
169#if 1
170  pthread_mutex_lock(&p->read_mutex);
171  //AUBIO_WRN("timestretch: signaling end\n");
172  pthread_cond_signal(&p->read_avail);
173  pthread_mutex_unlock(&p->read_mutex);
174#endif
175  //AUBIO_WRN("timestretch: exiting readfn\n");
176  pthread_exit(NULL);
177}
178#endif
179
180static void
181aubio_timestretch_warmup (aubio_timestretch_t * p)
182{
183  // warm up rubber band
184  unsigned int latency = MAX(p->hopsize, rubberband_get_latency(p->rb));
185#ifdef HAVE_THREADS
186  p->available = aubio_timestretch_fetch(p, latency);
187#else
188  aubio_timestretch_fetch(p, latency);
189#endif
190}
191
192void
193del_aubio_timestretch (aubio_timestretch_t * p)
194{
195#ifdef HAVE_THREADS
196  pthread_mutex_lock(&p->read_mutex);
197  pthread_cond_signal(&p->read_request);
198  //pthread_cond_wait(&p->read_avail, &p->read_mutex);
199  pthread_mutex_unlock(&p->read_mutex);
200#if 1
201  void *threadfn;
202  if ((p->eof == 0) && (pthread_cancel(p->read_thread))) {
203      AUBIO_WRN("timestretch: cancelling thread failed\n");
204  }
205  if (pthread_join(p->read_thread, &threadfn)) {
206      AUBIO_WRN("timestretch: joining thread failed\n");
207  }
208#endif
209  pthread_mutex_destroy(&p->read_mutex);
210  pthread_cond_destroy(&p->read_avail);
211  pthread_cond_destroy(&p->read_request);
212#endif
213  if (p->in) del_fvec(p->in);
214  if (p->source) del_aubio_source(p->source);
215  if (p->rb) {
216    rubberband_delete(p->rb);
217  }
218  AUBIO_FREE (p);
219}
220
221uint_t
222aubio_timestretch_get_samplerate (aubio_timestretch_t * p)
223{
224  return p->samplerate;
225}
226
227uint_t aubio_timestretch_get_latency (aubio_timestretch_t * p) {
228  return rubberband_get_latency(p->rb);
229}
230
231uint_t
232aubio_timestretch_set_stretch (aubio_timestretch_t * p, smpl_t stretch)
233{
234  if (stretch >= MIN_STRETCH_RATIO && stretch <= MAX_STRETCH_RATIO) {
235    p->stretchratio = stretch;
236    rubberband_set_time_ratio(p->rb, 1./p->stretchratio);
237    return AUBIO_OK;
238  } else {
239    AUBIO_WRN("timestretch: could not set stretch ratio to %.2f\n", stretch);
240    return AUBIO_FAIL;
241  }
242}
243
244smpl_t
245aubio_timestretch_get_stretch (aubio_timestretch_t * p)
246{
247  return p->stretchratio;
248}
249
250uint_t
251aubio_timestretch_set_pitchscale (aubio_timestretch_t * p, smpl_t pitchscale)
252{
253  if (pitchscale >= 0.0625  && pitchscale <= 4.) {
254    p->pitchscale = pitchscale;
255    rubberband_set_pitch_scale(p->rb, p->pitchscale);
256    return AUBIO_OK;
257  } else {
258    AUBIO_WRN("timestretch: could not set pitchscale to %.2f\n", pitchscale);
259    return AUBIO_FAIL;
260  }
261}
262
263smpl_t
264aubio_timestretch_get_pitchscale (aubio_timestretch_t * p)
265{
266  return p->pitchscale;
267}
268
269uint_t
270aubio_timestretch_set_transpose(aubio_timestretch_t * p, smpl_t transpose)
271{
272  if (transpose >= -24. && transpose <= 24.) {
273    smpl_t pitchscale = POW(2., transpose / 12.);
274    return aubio_timestretch_set_pitchscale(p, pitchscale);
275  } else {
276    AUBIO_WRN("timestretch: could not set transpose to %.2f\n", transpose);
277    return AUBIO_FAIL;
278  }
279}
280
281smpl_t
282aubio_timestretch_get_transpose(aubio_timestretch_t * p)
283{
284  return 12. * LOG(p->pitchscale) / LOG(2.0);
285}
286
287sint_t
288aubio_timestretch_fetch(aubio_timestretch_t *p, uint_t length)
289{
290  uint_t source_read = p->source_hopsize;
291  // read more samples from source until we have enough available or eof is reached
292  int available = rubberband_available(p->rb);
293  while ((available < (int)length) && (p->eof == 0)) {
294    aubio_source_do(p->source, p->in, &source_read);
295    if (source_read < p->source_hopsize) {
296      p->eof = 1;
297    }
298    rubberband_process(p->rb, (const float* const*)&(p->in->data), source_read, p->eof);
299    available = rubberband_available(p->rb);
300  }
301  return available;
302}
303
304void
305aubio_timestretch_do (aubio_timestretch_t * p, fvec_t * out, uint_t * read)
306{
307#ifndef HAVE_THREADS
308  int available = aubio_timestretch_fetch(p, p->hopsize);
309#else /* HAVE_THREADS */
310  int available;
311  pthread_mutex_lock(&p->read_mutex);
312  if (p->eof != 1) {
313    // signal a read request
314    pthread_cond_signal(&p->read_request);
315    // wait for an available signal
316    pthread_cond_wait(&p->read_avail, &p->read_mutex);
317  } else {
318    available = rubberband_available(p->rb);
319  }
320#endif /* HAVE_THREADS */
321  // now retrieve the samples and write them into out->data
322  if (available >= (int)p->hopsize) {
323    rubberband_retrieve(p->rb, (float* const*)&(out->data), p->hopsize);
324    *read = p->hopsize;
325  } else if (available > 0) {
326    rubberband_retrieve(p->rb, (float* const*)&(out->data), available);
327    *read = available;
328  } else {
329    fvec_zeros(out);
330    *read = 0;
331  }
332#ifdef HAVE_THREADS
333  pthread_mutex_unlock(&p->read_mutex);
334#endif
335}
336
337uint_t
338aubio_timestretch_seek (aubio_timestretch_t *p, uint_t pos)
339{
340  uint_t err = AUBIO_OK;
341#if HAVE_THREADS
342  AUBIO_WRN("timestretch: seek_ waiting for warmup, got %d available\n", p->available);
343  pthread_mutex_lock(&p->read_mutex);
344#endif
345  p->eof = 0;
346  rubberband_reset(p->rb);
347  err = aubio_source_seek(p->source, pos);
348#if HAVE_THREADS
349  p->available = 0;
350  void *threadfn;
351  if ((p->eof == 0) && (pthread_cancel(p->read_thread) == 0)) {
352      AUBIO_WRN("timestretch: cancelling thread failed\n");
353  }
354  if (pthread_join(p->read_thread, &threadfn)) {
355      AUBIO_WRN("timestretch: joining thread failed\n");
356  }
357  pthread_create(&p->read_thread, 0, aubio_timestretch_readfn, p);
358  pthread_cond_wait(&p->read_avail, &p->read_mutex);
359  pthread_mutex_unlock(&p->read_mutex);
360  //AUBIO_WRN("timestretch: seek_ warm up success, got %d available\n", p->available);
361#endif
362  return err;
363}
364
365#endif
Note: See TracBrowser for help on using the repository browser.