From 88042ef634aaac8b350a7c224750e0a74da572d2 Mon Sep 17 00:00:00 2001 From: Paul Brossier Date: Tue, 4 Oct 2016 23:16:14 +0200 Subject: [PATCH] src/synth/sampler.c: refactor interface, add file opening thread and utility functions --- src/synth/sampler.c | 322 +++++++++++++++++++++++++++++++++++++++++++++------- src/synth/sampler.h | 111 +++++++++++++++--- 2 files changed, 379 insertions(+), 54 deletions(-) diff --git a/src/synth/sampler.c b/src/synth/sampler.c index 7df1b5f6..079087b9 100644 --- a/src/synth/sampler.c +++ b/src/synth/sampler.c @@ -26,17 +26,38 @@ #include "io/source.h" #include "synth/sampler.h" +#define HAVE_THREADS 1 +#if 0 +#undef HAVE_THREADS +#endif + +#ifdef HAVE_THREADS +#include +#endif + struct _aubio_sampler_t { uint_t samplerate; uint_t blocksize; aubio_source_t *source; - fvec_t *source_output; - fmat_t *source_output_multi; - char_t *uri; + const char_t *uri; uint_t playing; + uint_t opened; + uint_t loop; + uint_t finished; + uint_t eof; +#ifdef HAVE_THREADS + pthread_t open_thread; // file opening thread + pthread_mutex_t open_mutex; + uint_t waited; // number of frames skipped while opening + const char_t *next_uri; + uint_t open_thread_running; + sint_t available; // number of samples currently available + uint_t started; // source warmed up + uint_t finish; // flag to tell reading thread to exit +#endif }; -aubio_sampler_t *new_aubio_sampler(uint_t samplerate, uint_t blocksize) +aubio_sampler_t *new_aubio_sampler(uint_t blocksize, uint_t samplerate) { aubio_sampler_t *s = AUBIO_NEW(aubio_sampler_t); if ((sint_t)blocksize < 1) { @@ -45,10 +66,20 @@ aubio_sampler_t *new_aubio_sampler(uint_t samplerate, uint_t blocksize) } s->samplerate = samplerate; s->blocksize = blocksize; - s->source_output = new_fvec(blocksize); - s->source_output_multi = new_fmat(4, blocksize); s->source = NULL; s->playing = 0; + s->loop = 0; + s->uri = NULL; + s->finished = 1; + s->eof = 0; + s->opened = 0; + +#ifdef HAVE_THREADS + pthread_mutex_init(&s->open_mutex, 0); + s->waited = 0; + s->open_thread = 0; + s->open_thread_running = 0; +#endif return s; beach: AUBIO_FREE(s); @@ -57,53 +88,228 @@ beach: uint_t aubio_sampler_load( aubio_sampler_t * o, const char_t * uri ) { - if (o->source) del_aubio_source(o->source); + uint_t ret = AUBIO_FAIL; + aubio_source_t *oldsource = o->source, *newsource = NULL; + newsource = new_aubio_source(uri, o->samplerate, o->blocksize); + if (newsource) { + o->source = newsource; + if (oldsource) del_aubio_source(oldsource); + if (o->samplerate == 0) { + o->samplerate = aubio_source_get_samplerate(o->source); + } + o->uri = uri; + o->finished = 0; + o->eof = 0; + o->opened = 1; + ret = AUBIO_OK; + //AUBIO_WRN("sampler: loaded %s\n", o->uri); + } else { + o->source = NULL; + if (oldsource) del_aubio_source(oldsource); + o->playing = 0; + o->uri = NULL; + o->finished = 1; + o->eof = 0; + o->opened = 0; + AUBIO_WRN("sampler: failed loading %s\n", uri); + } + return ret; +} + +#ifdef HAVE_THREADS +static void *aubio_sampler_openfn(void *z) { + aubio_sampler_t *p = z; + uint_t err; + int oldtype; + void *ret; + pthread_setcancelstate(PTHREAD_CANCEL_ASYNCHRONOUS, &oldtype); + pthread_mutex_lock(&p->open_mutex); + p->open_thread_running = 1; + err = aubio_sampler_load(p, p->next_uri); + p->open_thread_running = 0; + pthread_mutex_unlock(&p->open_mutex); + ret = &err; + pthread_exit(ret); +} +#endif - if (o->uri) AUBIO_FREE(o->uri); - o->uri = AUBIO_ARRAY(char_t, strnlen(uri, PATH_MAX)); - strncpy(o->uri, uri, strnlen(uri, PATH_MAX)); +uint_t +aubio_sampler_queue(aubio_sampler_t *o, const char_t *uri) +{ +#ifdef HAVE_THREADS + uint_t ret = AUBIO_OK; + /* open uri in open_thread */ + if (o->open_thread_running) { + // cancel previous open_thread + if (pthread_cancel(o->open_thread)) { + AUBIO_WRN("sampler: cancelling open thread failed\n"); + return AUBIO_FAIL; + } else { + AUBIO_WRN("sampler: previous open of %s cancelled while opening %s\n", + o->next_uri, uri); + } + o->open_thread_running = 0; + } + void *threadret; + if (o->open_thread && pthread_join(o->open_thread, &threadret)) { + AUBIO_WRN("sampler: joining thread failed\n"); + } + if (pthread_mutex_trylock(&o->open_mutex)) { + AUBIO_WRN("sampler: failed locking in queue\n"); + ret = AUBIO_FAIL; + goto lock_failed; + } + o->opened = 0; // while opening + o->started = 0; + o->available = 0; + o->next_uri = uri; + if (pthread_create(&o->open_thread, 0, aubio_sampler_openfn, o) != 0) { + AUBIO_ERR("sampler: failed creating opening thread\n"); + ret = AUBIO_FAIL; + goto thread_create_failed; + } + +thread_create_failed: + pthread_mutex_unlock(&o->open_mutex); +lock_failed: + if (ret == AUBIO_OK) { + //AUBIO_WRN("sampler: queued %s\n", uri); + } else { + AUBIO_ERR("sampler: queueing %s failed\n", uri); + } + return ret; +#else + AUBIO_WRN("sampler: opening %s, not queueing (not compiled with threading)\n", uri); + return aubio_sampler_load(o, uri); +#endif +} - o->source = new_aubio_source(uri, o->samplerate, o->blocksize); - if (o->source) return 0; - AUBIO_ERR("sampler: failed loading %s", uri); - return 1; +void +aubio_sampler_fetch_from_source(aubio_sampler_t *o, fvec_t *output, uint_t *read) { + if (o->opened == 1 && o->source && !o->finished) + aubio_source_do(o->source, output, read); } -void aubio_sampler_do ( aubio_sampler_t * o, const fvec_t * input, fvec_t * output) +void +aubio_sampler_fetch_from_source_multi(aubio_sampler_t *o, fmat_t *output, uint_t *read) { + aubio_source_do_multi(o->source, output, read); +} + +#if 0 +void +aubio_sampler_fetch_from_array(aubio_sampler_t *o, fvec_t *output, uint_t *read) { + // TODO +} +#endif + +uint_t +aubio_sampler_get_samplerate (aubio_sampler_t *o) +{ + return o->samplerate; +} + +uint_t +aubio_sampler_get_opened (aubio_sampler_t *o) +{ + return o->opened; //== 1 ? AUBIO_OK : AUBIO_FAIL; +} + +uint_t +aubio_sampler_get_finished(aubio_sampler_t *o) +{ + return o->finished; +} + +uint_t +aubio_sampler_get_eof (aubio_sampler_t *o) { - uint_t read = 0, i; + return o->eof; +} + +uint_t +aubio_sampler_get_waited_opening (aubio_sampler_t *o, uint_t waited) { +#ifdef HAVE_THREADS if (o->playing) { - aubio_source_do (o->source, o->source_output, &read); - for (i = 0; i < output->length; i++) { - output->data[i] += o->source_output->data[i]; + if (!o->opened) { + o->waited += waited; + } else if (o->waited) { + //AUBIO_WRN("sampler: waited %d frames (%.2fms) while opening %s\n", + // o->waited, 1000.*o->waited/(smpl_t)o->samplerate, o->uri); + uint_t waited = o->waited; + o->waited = 0; + return waited; } - if (read < o->blocksize) o->playing = 0; } - if (input && input != output) { - for (i = 0; i < output->length; i++) { - output->data[i] += input->data[i]; - } +#endif + return 0; +} + +uint_t +aubio_sampler_seek(aubio_sampler_t * o, uint_t pos) +{ + uint_t ret = AUBIO_FAIL; + o->finished = 0; + if (!o->opened) return AUBIO_OK; +#ifdef HAVE_THREADS + if (pthread_mutex_trylock(&o->open_mutex)) { + AUBIO_WRN("sampler: failed locking in seek\n"); + return ret; } +#endif + if (o->source) { + ret = aubio_source_seek(o->source, pos); + } +#ifdef HAVE_THREADS + pthread_mutex_unlock(&o->open_mutex); +#endif + return ret; } -void aubio_sampler_do_multi ( aubio_sampler_t * o, const fmat_t * input, fmat_t * output) +void +aubio_sampler_do_eof (aubio_sampler_t * o) { - uint_t read = 0, i, j; - if (o->playing) { - aubio_source_do_multi (o->source, o->source_output_multi, &read); - for (i = 0; i < output->height; i++) { - for (j = 0; j < output->length; j++) { - output->data[i][j] += o->source_output_multi->data[i][j]; + o->finished = 1; + o->eof = 1; + if (!o->loop) { + o->playing = 0; + } else { + aubio_sampler_seek(o, 0); + } +} + +void aubio_sampler_do ( aubio_sampler_t * o, fvec_t * output, uint_t *read) +{ + o->eof = 0; + if (o->opened == 1 && o->playing) { + aubio_sampler_fetch_from_source(o, output, read); + if (*read < o->blocksize) { + aubio_sampler_do_eof (o); + if (*read > 0) { + // TODO pull (hopsize - read) frames + //memset(... tail , 0) } } - if ( read < o->blocksize ) o->playing = 0; + } else { + fvec_zeros(output); + *read = 0; //output->length; } - if (input && input != output) { - for (i = 0; i < output->height; i++) { - for (j = 0; j < output->length; j++) { - output->data[i][j] += input->data[i][j]; +} + +void aubio_sampler_do_multi ( aubio_sampler_t * o, fmat_t * output, uint_t *read) +{ + o->eof = 0; + if (o->playing) { + aubio_sampler_fetch_from_source_multi(o, output, read); + if (*read < o->blocksize) { + aubio_sampler_do_eof (o); + if (*read > 0) { + // TODO pull (hopsize - read) frames + //memset(... tail , 0) } } + } else { + fmat_zeros(output); + *read = 0; } } @@ -118,9 +324,20 @@ uint_t aubio_sampler_set_playing ( aubio_sampler_t * o, uint_t playing ) return 0; } +uint_t aubio_sampler_get_loop ( aubio_sampler_t * o ) +{ + return o->loop; +} + +uint_t aubio_sampler_set_loop ( aubio_sampler_t * o, uint_t loop ) +{ + o->loop = (loop == 1) ? 1 : 0; + return 0; +} + uint_t aubio_sampler_play ( aubio_sampler_t * o ) { - aubio_source_seek (o->source, 0); + //aubio_source_seek (o->source, 0); return aubio_sampler_set_playing (o, 1); } @@ -129,13 +346,38 @@ uint_t aubio_sampler_stop ( aubio_sampler_t * o ) return aubio_sampler_set_playing (o, 0); } +uint_t aubio_sampler_loop ( aubio_sampler_t * o ) +{ + aubio_sampler_set_loop(o, 1); + aubio_sampler_seek(o, 0); + return aubio_sampler_set_playing (o, 1); +} + +uint_t aubio_sampler_trigger ( aubio_sampler_t * o ) +{ + aubio_sampler_set_loop(o, 0); + aubio_sampler_seek(o, 0); + return aubio_sampler_set_playing (o, 1); +} + void del_aubio_sampler( aubio_sampler_t * o ) { +#ifdef HAVE_THREADS + AUBIO_WRN("sampler: cleaning up\n"); + pthread_mutex_destroy(&o->open_mutex); + if (o->open_thread_running) { + if (pthread_cancel(o->open_thread)) { + AUBIO_WRN("sampler: cancelling open thread failed\n"); + } + } + void *threadret; + if (pthread_join(o->open_thread, &threadret)) { + AUBIO_WRN("sampler: joining open thread failed\n"); + } + pthread_mutex_destroy(&o->open_mutex); +#endif if (o->source) { del_aubio_source(o->source); } - if (o->uri) AUBIO_FREE(o->uri); - del_fvec(o->source_output); - del_fmat(o->source_output_multi); AUBIO_FREE(o); } diff --git a/src/synth/sampler.h b/src/synth/sampler.h index fb1304f3..f326119b 100644 --- a/src/synth/sampler.h +++ b/src/synth/sampler.h @@ -23,13 +23,18 @@ /** \file - Load and play sound files. + Load and play a sound file. This file loads a sample and gets ready to play it. The `_do` function adds the new samples to the input, and write the result as the output. +TODO: + - add _preset_threaded(level) + - add _set_stretch + - add _set_pitch + \example synth/test-sampler.c */ @@ -49,7 +54,7 @@ typedef struct _aubio_sampler_t aubio_sampler_t; \return the newly created ::aubio_sampler_t */ -aubio_sampler_t * new_aubio_sampler(uint_t samplerate, uint_t hop_size); +aubio_sampler_t * new_aubio_sampler(uint_t hop_size, uint_t samplerate); /** load source in sampler @@ -61,33 +66,35 @@ aubio_sampler_t * new_aubio_sampler(uint_t samplerate, uint_t hop_size); */ uint_t aubio_sampler_load( aubio_sampler_t * o, const char_t * uri ); +/** queue source in sampler + + \param o sampler, created by new_aubio_sampler() + \param uri the uri of the source to load + + \return 0 if successfully queued, non-zero otherwise + +*/ +uint_t aubio_sampler_queue(aubio_sampler_t * o, const char_t * uri ); + /** process sampler function \param o sampler, created by new_aubio_sampler() - \param input input of the sampler, to be added to the output \param output output of the sampler -This function adds the new samples from the playing source to the output. - -If `input` is not NULL and different from `output`, then the samples from `input` -are added to the output. +This function get new samples from the playing source into output. */ -void aubio_sampler_do ( aubio_sampler_t * o, const fvec_t * input, fvec_t * output); +void aubio_sampler_do ( aubio_sampler_t * o, fvec_t * output, uint_t *read); /** process sampler function, multiple channels \param o sampler, created by new_aubio_sampler() - \param input input of the sampler, to be added to the output \param output output of the sampler -This function adds the new samples from the playing source to the output. - -If `input` is not NULL and different from `output`, then the samples from `input` -are added to the output. +This function gets new samples from the playing source into output. */ -void aubio_sampler_do_multi ( aubio_sampler_t * o, const fmat_t * input, fmat_t * output); +void aubio_sampler_do_multi ( aubio_sampler_t * o, fmat_t * output, uint_t *read); /** get current playing state @@ -108,6 +115,18 @@ uint_t aubio_sampler_get_playing ( const aubio_sampler_t * o ); */ uint_t aubio_sampler_set_playing ( aubio_sampler_t * o, uint_t playing ); +uint_t aubio_sampler_get_loop(aubio_sampler_t * o); + +/** set current looping state + + \param o sampler, created by new_aubio_sampler() + \param looping 0 for not looping, 1 for looping + + \return 0 if successful, 1 otherwise + +*/ +uint_t aubio_sampler_set_loop(aubio_sampler_t * o, uint_t loop); + /** play sample from start \param o sampler, created by new_aubio_sampler() @@ -117,6 +136,24 @@ uint_t aubio_sampler_set_playing ( aubio_sampler_t * o, uint_t playing ); */ uint_t aubio_sampler_play ( aubio_sampler_t * o ); +/** play sample from start, looping it + + \param o sampler, created by new_aubio_sampler() + + \return 0 if successful, 1 otherwise + +*/ +uint_t aubio_sampler_loop ( aubio_sampler_t * o ); + +/** play sample from start, once + + \param o sampler, created by new_aubio_sampler() + + \return 0 if successful, 1 otherwise + +*/ +uint_t aubio_sampler_trigger ( aubio_sampler_t * o ); + /** stop sample \param o sampler, created by new_aubio_sampler() @@ -126,6 +163,52 @@ uint_t aubio_sampler_play ( aubio_sampler_t * o ); */ uint_t aubio_sampler_stop ( aubio_sampler_t * o ); +/** get end-of-file status + + \param o sampler, created by new_aubio_sampler() + + \return 1 when the eof is being reached, 0 otherwise + +*/ +uint_t aubio_sampler_get_eof(aubio_sampler_t * o); + +/** get end-of-file status + + \param o sampler, created by new_aubio_sampler() + + \return 1 when the eof is being reached, 0 otherwise + +*/ +uint_t aubio_sampler_get_finished (aubio_sampler_t * o); + +/** get samplerate + + \param o sampler, created by new_aubio_sampler() + + \return samplerate of the sampler + +*/ +uint_t aubio_sampler_get_samplerate(aubio_sampler_t * o); + +/** get the number of samples that were set to zero while opening a file + + \param o sampler, created by new_aubio_sampler() + + \return samplerate of the sampler + +*/ +uint_t aubio_sampler_get_waited_opening(aubio_sampler_t * o, uint_t waited); + +/** seek to position + + \param o sampler, created by new_aubio_sampler() + \param pos position to seek to, in samples + + \return 0 if successful, 1 otherwise + +*/ +uint_t aubio_sampler_seek(aubio_sampler_t * o, uint_t pos); + /** destroy ::aubio_sampler_t object \param o sampler, created by new_aubio_sampler() -- 2.11.0