Index: src/synth/sampler.c
===================================================================
--- src/synth/sampler.c	(revision 33d02426605c9921044f569805734c5449854646)
+++ src/synth/sampler.c	(revision 41b985f1e3468fe8547f44c2ae6b38f2a437a3f3)
@@ -19,4 +19,5 @@
 */
 
+#include <assert.h>
 
 #include "aubio_priv.h"
@@ -24,17 +25,93 @@
 #include "fmat.h"
 #include "io/source.h"
+#include "utils/ringbuffer.h"
 #include "synth/sampler.h"
+
+#define HAVE_THREADS 1
+#define READER_THREAD_ON 1
+#if 0
+#undef HAVE_THREADS
+#endif
+
+#ifdef HAVE_THREADS
+#include <pthread.h>
+#endif
+
+typedef enum {
+  aubio_sampler_reading_from_source,
+  aubio_sampler_reading_from_table,
+  aubio_sampler_n_reading_methods
+} aubio_sampler_reading_method;
+
+
+typedef enum {
+  aubio_sampler_interp_pitchtime,
+  aubio_sampler_interp_quad,
+  aubio_sampler_interp_lin,
+  aubio_sampler_n_interp_methods
+} aubio_sampler_interp_method;
 
 struct _aubio_sampler_t {
   uint_t samplerate;
   uint_t blocksize;
+  // current reading mode (can be a file or an array)
+  uint_t reading_from;
+  // current interpolation mode (can be quadratic, timestretch, ...)
+  uint_t interp;
+  aubio_ringbuffer_t *ring;
+  uint_t perfectloop;
+  uint_t eof_remaining;
+  // reading from a table
+  fvec_t *table;
+  uint_t table_index;
+  // reading from a source
   aubio_source_t *source;
+  const char_t *uri;
+  uint_t playing;
+  uint_t opened;
+  uint_t loop;
+  uint_t finished;              // end of file was reached
+  uint_t eof;                   // end of file is now
+#ifdef HAVE_THREADS
+  // file reading thread
+  pthread_t read_thread;
+  uint_t threaded_read;         // use reading thread?
+  pthread_mutex_t read_mutex;
+  pthread_cond_t read_avail;
+  pthread_cond_t read_request;
+  uint_t source_blocksize;
   fvec_t *source_output;
-  fmat_t *source_output_multi;
-  char_t *uri;
-  uint_t playing;
+  fmat_t *source_moutput;
+  uint_t channels;
+  // file opening thread
+  pthread_t open_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 read_thread_finish;    // flag to tell reading thread to exit
+#endif
 };
 
-aubio_sampler_t *new_aubio_sampler(uint_t samplerate, uint_t blocksize)
+static sint_t aubio_sampler_pull_from_source(aubio_sampler_t *s);
+
+static void aubio_sampler_do_eof(aubio_sampler_t *s);
+
+static void aubio_sampler_read(aubio_sampler_t *s, fvec_t *output, uint_t *read);
+static void aubio_sampler_read_from_source(aubio_sampler_t *s, fvec_t *output, uint_t *read);
+static void aubio_sampler_read_from_table(aubio_sampler_t *s, fvec_t *output, uint_t *read);
+
+#ifdef HAVE_THREADS
+static void *aubio_sampler_openfn(void *p);
+static void *aubio_sampler_readfn(void *p);
+static void aubio_sampler_open_opening_thread(aubio_sampler_t *o);
+static void aubio_sampler_open_reading_thread(aubio_sampler_t *o);
+static void aubio_sampler_close_opening_thread(aubio_sampler_t *o);
+static void aubio_sampler_close_reading_thread(aubio_sampler_t *o);
+#endif
+
+aubio_sampler_t *new_aubio_sampler(uint_t blocksize, uint_t samplerate)
 {
   aubio_sampler_t *s = AUBIO_NEW(aubio_sampler_t);
@@ -45,8 +122,61 @@
   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;
+  s->available = 0;
+
+  s->threaded_read = 0;
+  s->perfectloop = 0;
+#if 0 // naive mode
+  s->source_blocksize = s->blocksize;
+#elif 0 // threaded mode, no ringbuffer
+  s->source_blocksize = s->blocksize;
+  s->threaded_read = 1;
+#elif 0 // unthreaded, with ringbuffer
+  s->source_blocksize = 2048; //32 * s->blocksize;
+  s->perfectloop = 1;
+#elif 1 // threaded with ringhbuffer
+  s->source_blocksize = 2048; //32 * s->blocksize;
+  s->perfectloop = 1;
+  s->threaded_read = 1;
+#endif
+
+  if (s->source_blocksize < s->blocksize) {
+    s->source_blocksize = s->blocksize;
+  }
+  // FIXME: perfectloop fails if source_blocksize > 2048 with source_avcodec
+  //s->source_blocksize = 8192;
+
+  if (s->perfectloop || s->source_blocksize != s->blocksize) {
+    s->ring = new_aubio_ringbuffer(s->source_blocksize * 2, s->blocksize);
+  }
+  if (s->threaded_read || s->perfectloop || s->ring)
+    s->source_output = new_fvec(s->source_blocksize);
+  //s->channels = 1;
+  //s->source_moutput = new_fmat(s->source_blocksize, s->channels);
+
+#ifdef HAVE_THREADS
+  aubio_sampler_open_opening_thread(s);
+
+  if (s->threaded_read) {
+    //AUBIO_WRN("sampler: starting reading thread\n");
+    aubio_sampler_open_reading_thread(s);
+  }
+#endif
+
+#if 0
+  s->reading_from = aubio_sampler_reading_from_table;
+  s->perfectloop = 1;
+  s->threaded_read = 0;
+  s->opened = 1;
+  s->finished = 1;
+  s->table_index = 0;
+#endif
+
   return s;
 beach:
@@ -55,53 +185,544 @@
 }
 
+#ifdef HAVE_THREADS
+void aubio_sampler_open_opening_thread(aubio_sampler_t *s) {
+  pthread_mutex_init(&s->open_mutex, 0);
+  s->waited = 0;
+  s->open_thread = 0;
+  s->open_thread_running = 0;
+}
+
+void aubio_sampler_open_reading_thread(aubio_sampler_t *s) {
+  s->read_thread_finish = 0;
+  pthread_mutex_init(&s->read_mutex, 0);
+  pthread_cond_init (&s->read_avail, 0);
+  pthread_cond_init (&s->read_request, 0);
+  pthread_create(&s->read_thread, 0, aubio_sampler_readfn, s);
+}
+
+void aubio_sampler_close_opening_thread(aubio_sampler_t *o) {
+  // clean up opening thread
+  void *threadret;
+  if (!o->open_thread) return;
+  pthread_mutex_destroy(&o->open_mutex);
+  if (o->open_thread_running) {
+    if (pthread_cancel(o->open_thread)) {
+      AUBIO_WRN("sampler: cancelling file opening thread failed\n");
+    }
+  }
+  if (o->open_thread && pthread_join(o->open_thread, &threadret)) {
+    AUBIO_WRN("sampler: joining file opening thread failed\n");
+  }
+  pthread_mutex_destroy(&o->open_mutex);
+  o->open_thread = 0;
+}
+
+void aubio_sampler_close_reading_thread(aubio_sampler_t *o) {
+  // clean up reading thread
+  void *threadret;
+  if (!o->read_thread) return;
+  o->read_thread_finish = 1;
+  pthread_cond_signal(&o->read_request);
+  if (pthread_cancel(o->read_thread)) {
+    AUBIO_WRN("sampler: cancelling file reading thread failed\n");
+  }
+  if (pthread_join(o->read_thread, &threadret)) {
+    AUBIO_WRN("sampler: joining file reading thread failed\n");
+  }
+  pthread_mutex_destroy(&o->read_mutex);
+  pthread_cond_destroy(&o->read_avail);
+  pthread_cond_destroy(&o->read_request);
+  o->read_thread = 0;
+}
+#endif
+
 uint_t aubio_sampler_load( aubio_sampler_t * o, const char_t * uri )
 {
-  if (o->source) del_aubio_source(o->source);
-
-  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));
-
-  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_do ( aubio_sampler_t * o, const fvec_t * input, fvec_t * output)
-{
-  uint_t read = 0, i;
+  uint_t ret = AUBIO_FAIL;
+  aubio_source_t *oldsource = o->source, *newsource = NULL;
+  newsource = new_aubio_source(uri, o->samplerate, o->source_blocksize);
+  if (newsource) {
+    uint_t duration = aubio_source_get_duration(newsource);
+    if (duration < o->blocksize) {
+      AUBIO_WRN("sampler: %s is %d frames long, but blocksize is %d\n",
+          uri, duration, o->blocksize);
+    }
+    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->eof_remaining = 0;
+    o->opened = 1;
+    ret = AUBIO_OK;
+    AUBIO_WRN("sampler: loaded %s\n", uri);
+    if (o->waited) {
+      AUBIO_WRN("sampler: %.2fms (%d samples) taken to load %s\n", 1000. *
+          o->waited / (smpl_t)o->samplerate, o->waited, 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->eof_remaining = 0;
+    o->opened = 0;
+    AUBIO_WRN("sampler: failed loading %s\n", uri);
+  }
+  if (o->ring) {
+    //AUBIO_WRN("sampler: resetting ringbuffer\n");
+    aubio_ringbuffer_reset(o->ring);
+  }
+  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
+
+uint_t
+aubio_sampler_queue(aubio_sampler_t *o, const char_t *uri)
+{
+#ifdef HAVE_THREADS
+  uint_t ret = AUBIO_OK;
+
+  if (o->reading_from == aubio_sampler_reading_from_table) {
+    o->reading_from = aubio_sampler_reading_from_source;
+    o->opened = 0;
+    o->finished = 1;
+  }
+  /* open uri in open_thread */
+  if (o->open_thread_running) {
+    // cancel previous open_thread
+    if (pthread_cancel(o->open_thread)) {
+      AUBIO_WRN("sampler: failed queuing %s (cancelling existing open thread failed)\n", uri);
+      return AUBIO_FAIL;
+    } else {
+      AUBIO_WRN("sampler: cancelled queuing %s (queuing %s now)\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 queuing %s (locking failed)\n", uri);
+    ret = AUBIO_FAIL;
+    goto lock_failed;
+  }
+  o->opened = 0; // while opening
+  o->started = 0;
+  o->available = 0;
+  o->next_uri = uri;
+  o->waited = 0;
+  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
+}
+
+#ifdef HAVE_THREADS
+
+uint_t aubio_sampler_reading_from_source_ring_fetch(aubio_sampler_t*s);
+
+void *aubio_sampler_readfn(void *z) {
+  aubio_sampler_t *p = z;
+  while(1) {
+    pthread_mutex_lock(&p->read_mutex);
+    if (p->open_thread_running) {
+      //AUBIO_WRN("sampler: readfn(): file is being opened\n");
+      pthread_cond_signal(&p->read_avail);
+      //pthread_cond_wait(&p->read_request, &p->read_mutex);
+    } else if (p->opened && !p->started && !p->finished) {
+      //AUBIO_WRN("sampler: readfn(): file started\n");
+      if (p->ring) {
+        p->available = aubio_sampler_reading_from_source_ring_fetch(p);
+      } else {
+        p->available = aubio_sampler_pull_from_source(p);
+        if (p->available < (sint_t)p->source_blocksize)
+          aubio_sampler_do_eof(p);
+      }
+      pthread_cond_signal(&p->read_avail);
+      if (!p->finished) {
+        pthread_cond_wait(&p->read_request, &p->read_mutex);
+      }
+    } else {
+      //AUBIO_WRN("sampler: readfn(): idle?\n");
+      pthread_cond_signal(&p->read_avail);
+      pthread_cond_wait(&p->read_request, &p->read_mutex);
+      if (p->read_thread_finish) {
+        goto done;
+      }
+    }
+    pthread_mutex_unlock(&p->read_mutex);
+  }
+done:
+  //AUBIO_WRN("sampler: exiting reading thread\n");
+  pthread_mutex_unlock(&p->read_mutex);
+  pthread_exit(NULL);
+}
+#endif
+
+void
+aubio_sampler_read(aubio_sampler_t *s, fvec_t *output, uint_t *read) {
+  if (s->reading_from == aubio_sampler_reading_from_source) {
+    aubio_sampler_read_from_source(s, output, read);
+  } else if (s->reading_from == aubio_sampler_reading_from_table) {
+    aubio_sampler_read_from_table(s, output, read);
+  }
+}
+
+static void
+aubio_sampler_reading_from_source_naive(aubio_sampler_t *s, fvec_t * output,
+    uint_t *read)
+{
+  // directly read from disk
+  //aubio_source_do(s->source, output, read);
+  s->source_output = output;
+  *read = aubio_sampler_pull_from_source(s);
+  if (*read < s->source_blocksize) {
+    //AUBIO_WRN("sampler: calling go_eof in _read_from_source()\n");
+    aubio_sampler_do_eof(s);
+  }
+}
+
+uint_t
+aubio_sampler_reading_from_source_ring_fetch(aubio_sampler_t*s) {
+  // read source_blocksize (> blocksize) at once
+  int ring_avail = aubio_ringbuffer_get_available(s->ring);
+  //if (ring_avail < s->blocksize) {
+  uint_t available = 0;
+  if (ring_avail < (sint_t)s->blocksize) {
+    available = aubio_sampler_pull_from_source(s);
+    if (available > 0) {
+      aubio_ringbuffer_push(s->ring, s->source_output, available);
+    }
+    if (available < s->source_blocksize) {
+      if (ring_avail + available <= s->blocksize) {
+        s->eof_remaining = available + ring_avail;
+        if (s->eof_remaining == 0) s->eof_remaining = s->blocksize;
+        //AUBIO_ERR("sampler: marking special eof got: %d, in ring: %d, %d, eof remaining %d\n",
+        //    available, ring_avail, s->blocksize, s->eof_remaining);
+        if (s->loop) {
+          aubio_sampler_seek(s,0);
+          // read some frames from beginning of source for perfect looping
+          if (s->perfectloop) {
+            available = aubio_sampler_pull_from_source(s);
+            if (available <= 0) {
+              AUBIO_ERR("sampler: perfectloop but s->available = 0 !\n");
+            } else {
+              aubio_ringbuffer_push(s->ring, s->source_output, available);
+            }
+          }
+        }
+      }
+    }
+  }
+  return available;
+}
+
+static void
+aubio_sampler_reading_from_source_ring_pull(aubio_sampler_t *s, fvec_t *output,
+    uint_t *read)
+{
+  // write into output
+  int ring_avail = aubio_ringbuffer_get_available(s->ring);
+  if (ring_avail >= (sint_t)s->blocksize) {
+    //AUBIO_MSG("sampler: pulling %d / %d from ringbuffer\n", s->blocksize, ring_avail);
+    aubio_ringbuffer_pull(s->ring, output, s->blocksize);
+    *read = s->blocksize;
+    if (s->eof_remaining > 0) {
+      if (s->eof_remaining <= s->blocksize) {
+        //AUBIO_WRN("sampler: signaling eof\n");
+        s->eof = 1; // signal eof
+        s->eof_remaining = 0;
+      } else if (s->eof_remaining <= s->source_blocksize) {
+        s->eof_remaining -= s->blocksize;
+      }
+    }
+  } else {
+    //AUBIO_MSG("sampler: last frame, pulling remaining %d left\n", ring_avail);
+    *read = 0;
+    if (ring_avail > 0) {
+      // pull remaining frames in ring buffer
+      aubio_ringbuffer_pull(s->ring, output, ring_avail);
+      *read += ring_avail;
+    }
+    // signal eof
+    aubio_sampler_do_eof(s);
+    // finished playing, reset ringbuffer for next read
+    if (!s->playing)
+      aubio_ringbuffer_reset(s->ring);
+  }
+}
+
+static void
+aubio_sampler_reading_from_source_ring(aubio_sampler_t *s, fvec_t *output,
+    uint_t *read)
+{
+  aubio_sampler_reading_from_source_ring_fetch(s);
+  aubio_sampler_reading_from_source_ring_pull(s, output, read);
+}
+
+#ifdef HAVE_THREADS
+static void
+aubio_sampler_read_from_source_threaded(aubio_sampler_t *s, fvec_t *output,
+    uint_t *read) {
+  // request at least output->length
+  // make sure we have enough samples read from source
+  int available;
+  pthread_mutex_lock(&s->read_mutex);
+  if (!s->opened || s->open_thread_running) {
+    //AUBIO_ERR("sampler: _read_from_source: not opened, signaling read_request\n");
+    pthread_cond_signal(&s->read_request);
+    available = 0;
+  } else if (!s->finished) {
+    pthread_cond_signal(&s->read_request);
+    pthread_cond_wait(&s->read_avail, &s->read_mutex);
+    //AUBIO_ERR("sampler: _read_from_source: %d\n", s->available);
+    available = s->available;
+  } else {
+    //AUBIO_WRN("sampler: _read_from_source: eof\n");
+    pthread_cond_signal(&s->read_request);
+    available = 0;
+  }
+  pthread_mutex_unlock(&s->read_mutex);
+  //AUBIO_WRN("sampler: got %d available in _read_from_source\n", available);
+  // read -> number of samples read
+  if (!s->perfectloop && s->source_blocksize == s->blocksize) {
+    if (available >= (sint_t)s->blocksize) {
+      fvec_copy(s->source_output, output);
+      *read = s->blocksize;
+    } else if (available > 0) {
+      fvec_copy(s->source_output, output);
+      *read = available;
+    } else {
+      fvec_zeros(output);
+      *read = 0;
+    }
+  } else {
+    aubio_sampler_reading_from_source_ring_pull(s, output, read);
+  }
+}
+#endif
+
+void
+aubio_sampler_read_from_source(aubio_sampler_t *s, fvec_t *output, uint_t *read) {
+#ifdef HAVE_THREADS
+  if (s->threaded_read) { // if threaded
+    aubio_sampler_read_from_source_threaded(s, output, read);
+  } else
+#endif
+  {
+    if (s->finished) {
+      *read = 0;
+    }
+    else if (s->source_blocksize == s->blocksize && !s->perfectloop) {
+      aubio_sampler_reading_from_source_naive(s, output, read);
+    } else {
+      aubio_sampler_reading_from_source_ring(s, output, read);
+    }
+#if 1
+    if (s->loop && s->perfectloop && *read != s->blocksize) { // && s->started && !s->finished) {
+      AUBIO_ERR("sampler: perfectloop but read only %d\n", *read);
+    }
+#endif
+  }
+}
+
+void
+aubio_sampler_read_from_table(aubio_sampler_t *s, fvec_t *output, uint_t *read) {
+  *read = 0;
+  if (s->table == NULL) {
+    AUBIO_WRN("sampler: _pull_from_table but table not set %d, %d\n",
+        output->length, *read);
+  } else if (s->playing) {
+    uint_t available = s->table->length - s->table_index;
+    fvec_t tmp;
+    tmp.data = s->table->data + s->table_index;
+    if (available < s->blocksize) {
+      //AUBIO_WRN("sampler: _pull_from_table: table length %d, index: %d, read %d\n",
+      //    s->table->length, s->table_index, *read);
+      tmp.length = available;
+      fvec_t tmpout; tmpout.data = output->data; tmpout.length = available;
+      fvec_copy(&tmp, &tmpout);
+      if (s->loop && s->perfectloop) {
+        uint_t remaining = s->blocksize - available;
+        tmpout.data = output->data + available; tmpout.length = remaining;
+        tmp.data = s->table->data; tmp.length = remaining;
+        fvec_copy(&tmp, &tmpout);
+        s->table_index = remaining;
+        *read = s->blocksize;
+      } else {
+        s->table_index = 0;
+        *read = available;
+      }
+      aubio_sampler_do_eof(s);
+    } else {
+      tmp.length = s->blocksize;
+      fvec_copy(&tmp, output);
+      s->table_index += output->length;
+      *read = s->blocksize;
+    }
+  }
+}
+
+uint_t
+aubio_sampler_set_table(aubio_sampler_t *s, fvec_t *samples) {
+  if (!samples || !s) return AUBIO_FAIL;
+  if (s->reading_from == aubio_sampler_reading_from_source) {
+    //aubio_sampler_close_reading_thread(s);
+  }
+  s->table = samples;
+  //AUBIO_INF("sampler: setting table (%d long)\n", s->table->length);
+  s->table_index = 0;
+  s->reading_from = aubio_sampler_reading_from_table;
+  //s->threaded_read = 0;
+  s->opened = 1;
+  s->finished = 1;
+  return AUBIO_OK;
+}
+
+sint_t
+aubio_sampler_pull_from_source(aubio_sampler_t *s)
+{
+  // pull source_blocksize samples from source, return available frames
+  uint_t source_read = s->source_blocksize;
+  if (s->source == NULL) {
+    AUBIO_ERR("sampler: trying to fetch on NULL source\n");
+    return -1;
+  }
+  aubio_source_do(s->source, s->source_output, &source_read);
+  return source_read;
+}
+
+
+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)
+{
+  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 (read < o->blocksize) o->playing = 0;
-  }
-  if (input && input != output) {
-    for (i = 0; i < output->length; i++) {
-      output->data[i] += input->data[i];
-    }
-  }
-}
-
-void aubio_sampler_do_multi ( aubio_sampler_t * o, const fmat_t * input, fmat_t * output)
-{
-  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];
-      }
-    }
-    if ( read < o->blocksize ) o->playing = 0;
-  }
-  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];
-      }
-    }
+    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;
+    }
+  }
+#endif
+  return 0;
+}
+
+uint_t
+aubio_sampler_seek(aubio_sampler_t * o, uint_t pos)
+{
+  //AUBIO_WRN("sampler: seeking to 0\n");
+  uint_t ret = AUBIO_FAIL;
+  o->finished = 0;
+  if (!o->opened) return AUBIO_OK;
+  if (o->source) {
+    ret = aubio_source_seek(o->source, pos);
+  } else if (o->table && (sint_t)pos >= 0 && pos < o->table->length) {
+    o->table_index = pos < o->table->length ? pos : o->table->length - 1;
+    ret = AUBIO_OK;
+  }
+  return ret;
+}
+
+void
+aubio_sampler_do_eof (aubio_sampler_t * o)
+{
+  //AUBIO_MSG("sampler: calling  _do_eof()\n");
+  o->finished = 1;
+  o->eof = 1;
+  if (!o->loop) {
+    o->playing = 0;
+  } else {
+    if (o->reading_from == aubio_sampler_reading_from_source)
+      aubio_sampler_seek(o, 0);
+    //o->finished = 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_read(o, output, read);
+  } else {
+    fvec_zeros(output);
+    *read = 0;
+  }
+}
+
+void aubio_sampler_do_multi ( aubio_sampler_t * o, fmat_t * output, uint_t *read)
+{
+  o->eof = 0;
+  if (o->opened == 1 && o->playing) {
+    //aubio_sampler_read_multi(o, output, read);
+  } else {
+    fmat_zeros(output);
+    *read = 0;
   }
 }
@@ -118,7 +739,17 @@
 }
 
+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);
   return aubio_sampler_set_playing (o, 1);
 }
@@ -129,12 +760,51 @@
 }
 
+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 )
+{
+  if (o->ring) aubio_ringbuffer_reset(o->ring);
+  aubio_sampler_set_loop(o, 0);
+  aubio_sampler_seek(o, 0);
+  return aubio_sampler_set_playing (o, 1);
+}
+
+uint_t aubio_sampler_set_perfectloop (aubio_sampler_t *s, uint_t perfectloop) {
+  if (!s) return AUBIO_FAIL;
+  s->perfectloop = perfectloop;
+  return AUBIO_OK;
+}
+
+uint_t aubio_sampler_get_perfectloop (aubio_sampler_t *s) {
+  if (!s) return AUBIO_FAIL;
+  return s->perfectloop;
+}
+
 void del_aubio_sampler( aubio_sampler_t * o )
 {
+#ifdef HAVE_THREADS
+  // close opening thread
+  aubio_sampler_close_opening_thread(o);
+  // close reading thread
+  aubio_sampler_close_reading_thread(o);
+#endif
+  //if (o->source_output) {
+  if (o->source_output && (o->threaded_read || o->perfectloop)) {
+    del_fvec(o->source_output);
+  }
+  if (o->source_moutput) {
+    del_fmat(o->source_moutput);
+  }
+  if (o->ring) {
+    del_aubio_ringbuffer(o->ring);
+  }
   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);
 }
