summaryrefslogtreecommitdiff
path: root/modules/aufile/aufile.c
diff options
context:
space:
mode:
Diffstat (limited to 'modules/aufile/aufile.c')
-rw-r--r--modules/aufile/aufile.c251
1 files changed, 251 insertions, 0 deletions
diff --git a/modules/aufile/aufile.c b/modules/aufile/aufile.c
new file mode 100644
index 0000000..504816f
--- /dev/null
+++ b/modules/aufile/aufile.c
@@ -0,0 +1,251 @@
+/**
+ * @file aufile.c WAV Audio Source
+ *
+ * Copyright (C) 2015 Creytiv.com
+ */
+
+#define _BSD_SOURCE 1
+#include <pthread.h>
+#include <re.h>
+#include <rem.h>
+#include <baresip.h>
+
+
+/**
+ * @defgroup aufile aufile
+ *
+ * Audio module for using a WAV-file as audio input
+ */
+
+
+struct ausrc_st {
+ struct ausrc *as; /* base class */
+ struct tmr tmr;
+ struct aufile *aufile;
+ struct aubuf *aubuf;
+ uint32_t ptime;
+ size_t sampc;
+ bool run;
+ pthread_t thread;
+ ausrc_read_h *rh;
+ ausrc_error_h *errh;
+ void *arg;
+};
+
+
+static struct ausrc *ausrc;
+
+
+static void destructor(void *arg)
+{
+ struct ausrc_st *st = arg;
+
+ if (st->run) {
+ st->run = false;
+ pthread_join(st->thread, NULL);
+ }
+
+ tmr_cancel(&st->tmr);
+
+ mem_deref(st->aufile);
+ mem_deref(st->aubuf);
+ mem_deref(st->as);
+}
+
+
+static void *play_thread(void *arg)
+{
+ uint64_t now, ts = tmr_jiffies();
+ struct ausrc_st *st = arg;
+ int16_t *sampv;
+
+ sampv = mem_alloc(st->sampc * 2, NULL);
+ if (!sampv)
+ return NULL;
+
+ while (st->run) {
+
+ (void)usleep(4000);
+
+ now = tmr_jiffies();
+
+ if (ts > now)
+ continue;
+
+ aubuf_read_samp(st->aubuf, sampv, st->sampc);
+
+ st->rh(sampv, st->sampc, st->arg);
+
+ ts += st->ptime;
+ }
+
+ mem_deref(sampv);
+
+ info("aufile: player thread exited\n");
+
+ return NULL;
+}
+
+
+static void timeout(void *arg)
+{
+ struct ausrc_st *st = arg;
+
+ tmr_start(&st->tmr, 1000, timeout, st);
+
+ /* check if audio buffer is empty */
+ if (aubuf_cur_size(st->aubuf) < (2 * st->sampc)) {
+
+ info("aufile: end of file\n");
+
+ /* error handler must be called from re_main thread */
+ if (st->errh)
+ st->errh(0, "end of file", st->arg);
+ }
+}
+
+
+static int read_file(struct ausrc_st *st)
+{
+ struct mbuf *mb;
+ int err;
+
+ for (;;) {
+
+ mb = mbuf_alloc(4096);
+ if (!mb)
+ return ENOMEM;
+
+ mb->end = mb->size;
+
+ err = aufile_read(st->aufile, mb->buf, &mb->end);
+ if (err)
+ break;
+
+ if (mb->end == 0) {
+ info("aufile: end of file\n");
+ break;
+ }
+
+ aubuf_append(st->aubuf, mb);
+
+ mb = mem_deref(mb);
+ }
+
+ info("aufile: loaded %zu bytes\n", aubuf_cur_size(st->aubuf));
+
+ mem_deref(mb);
+ return err;
+}
+
+
+static int alloc_handler(struct ausrc_st **stp, struct ausrc *as,
+ struct media_ctx **ctx,
+ struct ausrc_prm *prm, const char *dev,
+ ausrc_read_h *rh, ausrc_error_h *errh, void *arg)
+{
+ struct ausrc_st *st;
+ struct aufile_prm fprm;
+ int err;
+ (void)ctx;
+
+ if (!stp || !as || !prm || !rh)
+ return EINVAL;
+
+ info("aufile: loading input file '%s'\n", dev);
+
+ st = mem_zalloc(sizeof(*st), destructor);
+ if (!st)
+ return ENOMEM;
+
+ st->as = mem_ref(as);
+ st->rh = rh;
+ st->errh = errh;
+ st->arg = arg;
+
+ err = aufile_open(&st->aufile, &fprm, dev, AUFILE_READ);
+ if (err) {
+ warning("aufile: failed to open file '%s' (%m)\n", dev, err);
+ goto out;
+ }
+
+ info("aufile: %s: %u Hz, %d channels\n",
+ dev, fprm.srate, fprm.channels);
+
+ if (fprm.srate != prm->srate) {
+ warning("aufile: input file (%s) must have sample-rate"
+ " %u Hz\n", dev, prm->srate);
+ err = ENODEV;
+ goto out;
+ }
+ if (fprm.channels != prm->ch) {
+ warning("aufile: input file (%s) must have channels = %d\n",
+ dev, prm->ch);
+ err = ENODEV;
+ goto out;
+ }
+ if (fprm.fmt != AUFMT_S16LE) {
+ warning("aufile: input file must have format S16LE\n");
+ err = ENODEV;
+ goto out;
+ }
+
+ st->sampc = prm->srate * prm->ch * prm->ptime / 1000;
+
+ st->ptime = prm->ptime;
+
+ info("aufile: audio ptime=%u sampc=%zu aubuf=[%u:%u]\n",
+ st->ptime, st->sampc,
+ prm->srate * prm->ch * 2,
+ prm->srate * prm->ch * 40);
+
+ /* 1 - inf seconds of audio */
+ err = aubuf_alloc(&st->aubuf,
+ prm->srate * prm->ch * 2,
+ 0);
+ if (err)
+ goto out;
+
+ err = read_file(st);
+ if (err)
+ goto out;
+
+ tmr_start(&st->tmr, 1000, timeout, st);
+
+ st->run = true;
+ err = pthread_create(&st->thread, NULL, play_thread, st);
+ if (err) {
+ st->run = false;
+ goto out;
+ }
+
+ out:
+ if (err)
+ mem_deref(st);
+ else
+ *stp = st;
+
+ return err;
+}
+
+
+static int module_init(void)
+{
+ return ausrc_register(&ausrc, "aufile", alloc_handler);
+}
+
+
+static int module_close(void)
+{
+ ausrc = mem_deref(ausrc);
+
+ return 0;
+}
+
+
+EXPORT_SYM const struct mod_export DECL_EXPORTS(aufile) = {
+ "aufile",
+ "ausrc",
+ module_init,
+ module_close
+};