summaryrefslogtreecommitdiff
path: root/modules/portaudio
diff options
context:
space:
mode:
Diffstat (limited to 'modules/portaudio')
-rw-r--r--modules/portaudio/module.mk11
-rw-r--r--modules/portaudio/portaudio.c331
2 files changed, 342 insertions, 0 deletions
diff --git a/modules/portaudio/module.mk b/modules/portaudio/module.mk
new file mode 100644
index 0000000..81e5b80
--- /dev/null
+++ b/modules/portaudio/module.mk
@@ -0,0 +1,11 @@
+#
+# module.mk
+#
+# Copyright (C) 2010 Creytiv.com
+#
+
+MOD := portaudio
+$(MOD)_SRCS += portaudio.c
+$(MOD)_LFLAGS += -lportaudio
+
+include mk/mod.mk
diff --git a/modules/portaudio/portaudio.c b/modules/portaudio/portaudio.c
new file mode 100644
index 0000000..36ca3f1
--- /dev/null
+++ b/modules/portaudio/portaudio.c
@@ -0,0 +1,331 @@
+/**
+ * @file portaudio.c Portaudio sound driver
+ *
+ * Copyright (C) 2010 Creytiv.com
+ */
+#include <stdlib.h>
+#include <string.h>
+#include <portaudio.h>
+#include <re.h>
+#include <rem.h>
+#include <baresip.h>
+
+
+/*
+ * portaudio v19 is required
+ */
+
+struct ausrc_st {
+ struct ausrc *as; /* inheritance */
+ PaStream *stream_rd;
+ ausrc_read_h *rh;
+ void *arg;
+ volatile bool ready;
+ unsigned ch;
+};
+
+struct auplay_st {
+ struct auplay *ap; /* inheritance */
+ PaStream *stream_wr;
+ auplay_write_h *wh;
+ void *arg;
+ volatile bool ready;
+ unsigned ch;
+};
+
+
+static struct ausrc *ausrc;
+static struct auplay *auplay;
+
+
+/*
+ * This routine will be called by the PortAudio engine when audio is needed.
+ * It may called at interrupt level on some machines so don't do anything
+ * that could mess up the system like calling malloc() or free().
+ */
+static int read_callback(const void *inputBuffer, void *outputBuffer,
+ unsigned long frameCount,
+ const PaStreamCallbackTimeInfo *timeInfo,
+ PaStreamCallbackFlags statusFlags, void *userData)
+{
+ struct ausrc_st *st = userData;
+ unsigned sampc;
+
+ (void)outputBuffer;
+ (void)timeInfo;
+ (void)statusFlags;
+
+ if (!st->ready)
+ return paAbort;
+
+ sampc = frameCount * st->ch;
+
+ st->rh(inputBuffer, 2*sampc, st->arg);
+
+ return paContinue;
+}
+
+
+static int write_callback(const void *inputBuffer, void *outputBuffer,
+ unsigned long frameCount,
+ const PaStreamCallbackTimeInfo *timeInfo,
+ PaStreamCallbackFlags statusFlags, void *userData)
+{
+ struct auplay_st *st = userData;
+ unsigned sampc;
+
+ (void)inputBuffer;
+ (void)timeInfo;
+ (void)statusFlags;
+
+ if (!st->ready)
+ return paAbort;
+
+ sampc = frameCount * st->ch;
+
+ st->wh(outputBuffer, 2*sampc, st->arg);
+
+ return paContinue;
+}
+
+
+static int read_stream_open(struct ausrc_st *st, const struct ausrc_prm *prm,
+ uint32_t dev)
+{
+ PaStreamParameters prm_in;
+ PaError err;
+ unsigned long frames_per_buffer = prm->srate * prm->ptime / 1000;
+
+ memset(&prm_in, 0, sizeof(prm_in));
+ prm_in.device = dev;
+ prm_in.channelCount = prm->ch;
+ prm_in.sampleFormat = paInt16;
+ prm_in.suggestedLatency = 0.100;
+
+ st->stream_rd = NULL;
+ err = Pa_OpenStream(&st->stream_rd, &prm_in, NULL, prm->srate,
+ frames_per_buffer, paNoFlag, read_callback, st);
+ if (paNoError != err) {
+ warning("portaudio: read: Pa_OpenStream: %s\n",
+ Pa_GetErrorText(err));
+ return EINVAL;
+ }
+
+ err = Pa_StartStream(st->stream_rd);
+ if (paNoError != err) {
+ warning("portaudio: read: Pa_StartStream: %s\n",
+ Pa_GetErrorText(err));
+ return EINVAL;
+ }
+
+ return 0;
+}
+
+
+static int write_stream_open(struct auplay_st *st,
+ const struct auplay_prm *prm, uint32_t dev)
+{
+ PaStreamParameters prm_out;
+ PaError err;
+ unsigned long frames_per_buffer = prm->srate * prm->ptime / 1000;
+
+ memset(&prm_out, 0, sizeof(prm_out));
+ prm_out.device = dev;
+ prm_out.channelCount = prm->ch;
+ prm_out.sampleFormat = paInt16;
+ prm_out.suggestedLatency = 0.100;
+
+ st->stream_wr = NULL;
+ err = Pa_OpenStream(&st->stream_wr, NULL, &prm_out, prm->srate,
+ frames_per_buffer, paNoFlag, write_callback, st);
+ if (paNoError != err) {
+ warning("portaudio: write: Pa_OpenStream: %s\n",
+ Pa_GetErrorText(err));
+ return EINVAL;
+ }
+
+ err = Pa_StartStream(st->stream_wr);
+ if (paNoError != err) {
+ warning("portaudio: write: Pa_StartStream: %s\n",
+ Pa_GetErrorText(err));
+ return EINVAL;
+ }
+
+ return 0;
+}
+
+
+static void ausrc_destructor(void *arg)
+{
+ struct ausrc_st *st = arg;
+
+ st->ready = false;
+
+ if (st->stream_rd) {
+ Pa_AbortStream(st->stream_rd);
+ Pa_CloseStream(st->stream_rd);
+ }
+
+ mem_deref(st->as);
+}
+
+
+static void auplay_destructor(void *arg)
+{
+ struct auplay_st *st = arg;
+
+ st->ready = false;
+
+ if (st->stream_wr) {
+ Pa_AbortStream(st->stream_wr);
+ Pa_CloseStream(st->stream_wr);
+ }
+
+ mem_deref(st->ap);
+}
+
+
+static int src_alloc(struct ausrc_st **stp, struct ausrc *as,
+ struct media_ctx **ctx,
+ struct ausrc_prm *prm, const char *device,
+ ausrc_read_h *rh, ausrc_error_h *errh, void *arg)
+{
+ struct ausrc_st *st;
+ PaDeviceIndex dev_index;
+ int err;
+
+ (void)ctx;
+ (void)device;
+ (void)errh;
+
+ if (!stp || !as || !prm)
+ return EINVAL;
+
+ if (str_isset(device))
+ dev_index = atoi(device);
+ else
+ dev_index = Pa_GetDefaultInputDevice();
+
+ prm->fmt = AUFMT_S16LE;
+
+ st = mem_zalloc(sizeof(*st), ausrc_destructor);
+ if (!st)
+ return ENOMEM;
+
+ st->as = mem_ref(as);
+ st->rh = rh;
+ st->arg = arg;
+ st->ch = prm->ch;
+
+ st->ready = true;
+
+ err = read_stream_open(st, prm, dev_index);
+ if (err)
+ goto out;
+
+ out:
+ if (err)
+ mem_deref(st);
+ else
+ *stp = st;
+
+ return err;
+}
+
+
+static int play_alloc(struct auplay_st **stp, struct auplay *ap,
+ struct auplay_prm *prm, const char *device,
+ auplay_write_h *wh, void *arg)
+{
+ struct auplay_st *st;
+ PaDeviceIndex dev_index;
+ int err;
+
+ (void)device;
+
+ if (!stp || !ap || !prm)
+ return EINVAL;
+
+ if (str_isset(device))
+ dev_index = atoi(device);
+ else
+ dev_index = Pa_GetDefaultOutputDevice();
+
+ prm->fmt = AUFMT_S16LE;
+
+ st = mem_zalloc(sizeof(*st), auplay_destructor);
+ if (!st)
+ return ENOMEM;
+
+ st->ap = mem_ref(ap);
+ st->wh = wh;
+ st->arg = arg;
+ st->ch = prm->ch;
+
+ st->ready = true;
+
+ err = write_stream_open(st, prm, dev_index);
+ if (err)
+ goto out;
+
+ out:
+ if (err)
+ mem_deref(st);
+ else
+ *stp = st;
+
+ return err;
+}
+
+
+static int pa_init(void)
+{
+ PaError paerr;
+ int i, n, err = 0;
+
+ paerr = Pa_Initialize();
+ if (paNoError != paerr) {
+ warning("portaudio: init: %s\n", Pa_GetErrorText(paerr));
+ return ENODEV;
+ }
+
+ n = Pa_GetDeviceCount();
+
+ info("portaudio: device count is %d\n", n);
+
+ for (i=0; i<n; i++) {
+ const PaDeviceInfo *devinfo;
+
+ devinfo = Pa_GetDeviceInfo(i);
+
+ debug("portaudio: device %d: %s\n", i, devinfo->name);
+ (void)devinfo;
+ }
+
+ if (paNoDevice != Pa_GetDefaultInputDevice())
+ err |= ausrc_register(&ausrc, "portaudio", src_alloc);
+
+ if (paNoDevice != Pa_GetDefaultOutputDevice())
+ err |= auplay_register(&auplay, "portaudio", play_alloc);
+
+ return err;
+}
+
+
+static int pa_close(void)
+{
+ ausrc = mem_deref(ausrc);
+ auplay = mem_deref(auplay);
+
+ Pa_Terminate();
+
+ return 0;
+}
+
+
+EXPORT_SYM const struct mod_export DECL_EXPORTS(portaudio) = {
+ "portaudio",
+ "sound",
+ pa_init,
+ pa_close
+};